2 * Blackfin nmi_watchdog Driver
4 * Originally based on bfin_wdt.c
5 * Copyright 2010-2010 Analog Devices Inc.
6 * Graff Yang <graf.yang@analog.com>
8 * Enter bugs at http://blackfin.uclinux.org/
10 * Licensed under the GPL-2 or later.
13 #include <linux/bitops.h>
14 #include <linux/hardirq.h>
15 #include <linux/sysdev.h>
17 #include <linux/nmi.h>
18 #include <linux/smp.h>
19 #include <linux/timer.h>
20 #include <asm/blackfin.h>
21 #include <asm/atomic.h>
22 #include <asm/cacheflush.h>
24 /* Bit in WDOG_CTL that indicates watchdog has expired (WDR0) */
25 #define WDOG_EXPIRED 0x8000
27 /* Masks for WDEV field in WDOG_CTL register */
28 #define ICTL_RESET 0x0
34 /* Masks for WDEN field in WDOG_CTL register */
35 #define WDEN_MASK 0x0FF0
36 #define WDEN_ENABLE 0x0000
37 #define WDEN_DISABLE 0x0AD0
39 #define DRV_NAME "nmi-wdt"
41 #define NMI_WDT_TIMEOUT 5 /* 5 seconds */
42 #define NMI_CHECK_TIMEOUT (4 * HZ) /* 4 seconds in jiffies */
43 static int nmi_wdt_cpu = 1;
45 static unsigned int timeout = NMI_WDT_TIMEOUT;
46 static int nmi_active;
48 static unsigned short wdoga_ctl;
49 static unsigned int wdoga_cnt;
50 static struct corelock_slot saved_corelock;
51 static atomic_t nmi_touched[NR_CPUS];
52 static struct timer_list ntimer;
61 static unsigned long nmi_event __attribute__ ((__section__(".l2.bss")));
63 /* we are in nmi, non-atomic bit ops is safe */
64 static inline void set_nmi_event(int event)
66 __set_bit(event, &nmi_event);
69 static inline void wait_nmi_event(int event)
71 while (!test_bit(event, &nmi_event))
73 __clear_bit(event, &nmi_event);
76 static inline void send_corea_nmi(void)
78 wdoga_ctl = bfin_read_WDOGA_CTL();
79 wdoga_cnt = bfin_read_WDOGA_CNT();
81 bfin_write_WDOGA_CTL(WDEN_DISABLE);
82 bfin_write_WDOGA_CNT(0);
83 bfin_write_WDOGA_CTL(WDEN_ENABLE | ICTL_NMI);
86 static inline void restore_corea_nmi(void)
88 bfin_write_WDOGA_CTL(WDEN_DISABLE);
89 bfin_write_WDOGA_CTL(WDOG_EXPIRED | WDEN_DISABLE | ICTL_NONE);
91 bfin_write_WDOGA_CNT(wdoga_cnt);
92 bfin_write_WDOGA_CTL(wdoga_ctl);
95 static inline void save_corelock(void)
97 saved_corelock = corelock;
101 static inline void restore_corelock(void)
103 corelock = saved_corelock;
107 static inline void nmi_wdt_keepalive(void)
109 bfin_write_WDOGB_STAT(0);
112 static inline void nmi_wdt_stop(void)
114 bfin_write_WDOGB_CTL(WDEN_DISABLE);
117 /* before calling this function, you must stop the WDT */
118 static inline void nmi_wdt_clear(void)
120 /* clear TRO bit, disable event generation */
121 bfin_write_WDOGB_CTL(WDOG_EXPIRED | WDEN_DISABLE | ICTL_NONE);
124 static inline void nmi_wdt_start(void)
126 bfin_write_WDOGB_CTL(WDEN_ENABLE | ICTL_NMI);
129 static inline int nmi_wdt_running(void)
131 return ((bfin_read_WDOGB_CTL() & WDEN_MASK) != WDEN_DISABLE);
134 static inline int nmi_wdt_set_timeout(unsigned long t)
136 u32 cnt, max_t, sclk;
143 pr_warning("NMI: timeout value is too large\n");
147 run = nmi_wdt_running();
149 bfin_write_WDOGB_CNT(cnt);
158 int check_nmi_wdt_touched(void)
160 unsigned int this_cpu = smp_processor_id();
163 cpumask_t mask = cpu_online_map;
165 if (!atomic_read(&nmi_touched[this_cpu]))
168 atomic_set(&nmi_touched[this_cpu], 0);
170 cpu_clear(this_cpu, mask);
171 for_each_cpu_mask(cpu, mask) {
172 invalidate_dcache_range((unsigned long)(&nmi_touched[cpu]),
173 (unsigned long)(&nmi_touched[cpu]));
174 if (!atomic_read(&nmi_touched[cpu]))
176 atomic_set(&nmi_touched[cpu], 0);
182 static void nmi_wdt_timer(unsigned long data)
184 if (check_nmi_wdt_touched())
187 mod_timer(&ntimer, jiffies + NMI_CHECK_TIMEOUT);
190 static int __init init_nmi_wdt(void)
192 nmi_wdt_set_timeout(timeout);
197 ntimer.function = nmi_wdt_timer;
198 ntimer.expires = jiffies + NMI_CHECK_TIMEOUT;
201 pr_info("nmi_wdt: initialized: timeout=%d sec\n", timeout);
204 device_initcall(init_nmi_wdt);
206 void touch_nmi_watchdog(void)
208 atomic_set(&nmi_touched[smp_processor_id()], 1);
211 /* Suspend/resume support */
213 static int nmi_wdt_suspend(struct sys_device *dev, pm_message_t state)
219 static int nmi_wdt_resume(struct sys_device *dev)
226 static struct sysdev_class nmi_sysclass = {
228 .resume = nmi_wdt_resume,
229 .suspend = nmi_wdt_suspend,
232 static struct sys_device device_nmi_wdt = {
234 .cls = &nmi_sysclass,
237 static int __init init_nmi_wdt_sysfs(void)
244 error = sysdev_class_register(&nmi_sysclass);
246 error = sysdev_register(&device_nmi_wdt);
249 late_initcall(init_nmi_wdt_sysfs);
251 #endif /* CONFIG_PM */
254 asmlinkage notrace void do_nmi(struct pt_regs *fp)
256 unsigned int cpu = smp_processor_id();
259 cpu_pda[cpu].__nmi_count += 1;
261 if (cpu == nmi_wdt_cpu) {
262 /* CoreB goes here first */
264 /* reload the WDOG_STAT */
267 /* clear nmi interrupt for CoreB */
271 /* trigger NMI interrupt of CoreA */
274 /* waiting CoreB to enter NMI */
275 wait_nmi_event(COREA_ENTER_NMI);
277 /* recover WDOGA's settings */
282 /* corelock is save/cleared, CoreA is dummping messages */
284 wait_nmi_event(COREA_EXIT_NMI);
286 /* OK, CoreA entered NMI */
287 set_nmi_event(COREA_ENTER_NMI);
290 pr_emerg("\nNMI Watchdog detected LOCKUP, dump for CPU %d\n", cpu);
291 dump_bfin_process(fp);
294 dump_bfin_trace_buffer();
295 show_stack(current, (unsigned long *)fp);
297 if (cpu == nmi_wdt_cpu) {
298 pr_emerg("This fault is not recoverable, sorry!\n");
300 /* CoreA dump finished, restore the corelock */
303 set_nmi_event(COREB_EXIT_NMI);
305 /* CoreB dump finished, notice the CoreA we are done */
306 set_nmi_event(COREA_EXIT_NMI);
308 /* synchronize with CoreA */
309 wait_nmi_event(COREB_EXIT_NMI);