···238238 all of this lives in the init section and is thrown away after the239239 kernel boots completely.240240241241+config NMI_WATCHDOG242242+ bool "Enable NMI watchdog to help debugging lockup on SMP"243243+ default n244244+ depends on (SMP && !BFIN_SCRATCH_REG_RETN)245245+ help246246+ If any CPU in the system does not execute the period local timer247247+ interrupt for more than 5 seconds, then the NMI handler dumps debug248248+ information. This information can be used to debug the lockup.249249+241250config CPLB_INFO242251 bool "Display the CPLB information"243252 help
···11+/*22+ * Copyright 2010 Analog Devices Inc.33+ *44+ * Licensed under the GPL-255+ */66+77+#ifndef _BFIN_NMI_H_88+#define _BFIN_NMI_H_99+1010+#include <linux/nmi.h>1111+1212+#endif
+1
arch/blackfin/include/asm/smp.h
···2222struct corelock_slot {2323 int lock;2424};2525+extern struct corelock_slot corelock;25262627void smp_icache_flush_range_others(unsigned long start,2728 unsigned long end);
···11+/*22+ * Blackfin nmi_watchdog Driver33+ *44+ * Originally based on bfin_wdt.c55+ * Copyright 2010-2010 Analog Devices Inc.66+ * Graff Yang <graf.yang@analog.com>77+ *88+ * Enter bugs at http://blackfin.uclinux.org/99+ *1010+ * Licensed under the GPL-2 or later.1111+ */1212+1313+#include <linux/bitops.h>1414+#include <linux/hardirq.h>1515+#include <linux/sysdev.h>1616+#include <linux/pm.h>1717+#include <linux/nmi.h>1818+#include <linux/smp.h>1919+#include <linux/timer.h>2020+#include <asm/blackfin.h>2121+#include <asm/atomic.h>2222+#include <asm/cacheflush.h>2323+2424+/* Bit in WDOG_CTL that indicates watchdog has expired (WDR0) */2525+#define WDOG_EXPIRED 0x80002626+2727+/* Masks for WDEV field in WDOG_CTL register */2828+#define ICTL_RESET 0x02929+#define ICTL_NMI 0x23030+#define ICTL_GPI 0x43131+#define ICTL_NONE 0x63232+#define ICTL_MASK 0x63333+3434+/* Masks for WDEN field in WDOG_CTL register */3535+#define WDEN_MASK 0x0FF03636+#define WDEN_ENABLE 0x00003737+#define WDEN_DISABLE 0x0AD03838+3939+#define DRV_NAME "nmi-wdt"4040+4141+#define NMI_WDT_TIMEOUT 5 /* 5 seconds */4242+#define NMI_CHECK_TIMEOUT (4 * HZ) /* 4 seconds in jiffies */4343+static int nmi_wdt_cpu = 1;4444+4545+static unsigned int timeout = NMI_WDT_TIMEOUT;4646+static int nmi_active;4747+4848+static unsigned short wdoga_ctl;4949+static unsigned int wdoga_cnt;5050+static struct corelock_slot saved_corelock;5151+static atomic_t nmi_touched[NR_CPUS];5252+static struct timer_list ntimer;5353+5454+enum {5555+ COREA_ENTER_NMI = 0,5656+ COREA_EXIT_NMI,5757+ COREB_EXIT_NMI,5858+5959+ NMI_EVENT_NR,6060+};6161+static unsigned long nmi_event __attribute__ ((__section__(".l2.bss")));6262+6363+/* we are in nmi, non-atomic bit ops is safe */6464+static inline void set_nmi_event(int event)6565+{6666+ __set_bit(event, &nmi_event);6767+}6868+6969+static inline void wait_nmi_event(int event)7070+{7171+ while (!test_bit(event, &nmi_event))7272+ barrier();7373+ __clear_bit(event, &nmi_event);7474+}7575+7676+static inline void send_corea_nmi(void)7777+{7878+ wdoga_ctl = bfin_read_WDOGA_CTL();7979+ wdoga_cnt = bfin_read_WDOGA_CNT();8080+8181+ bfin_write_WDOGA_CTL(WDEN_DISABLE);8282+ bfin_write_WDOGA_CNT(0);8383+ bfin_write_WDOGA_CTL(WDEN_ENABLE | ICTL_NMI);8484+}8585+8686+static inline void restore_corea_nmi(void)8787+{8888+ bfin_write_WDOGA_CTL(WDEN_DISABLE);8989+ bfin_write_WDOGA_CTL(WDOG_EXPIRED | WDEN_DISABLE | ICTL_NONE);9090+9191+ bfin_write_WDOGA_CNT(wdoga_cnt);9292+ bfin_write_WDOGA_CTL(wdoga_ctl);9393+}9494+9595+static inline void save_corelock(void)9696+{9797+ saved_corelock = corelock;9898+ corelock.lock = 0;9999+}100100+101101+static inline void restore_corelock(void)102102+{103103+ corelock = saved_corelock;104104+}105105+106106+107107+static inline void nmi_wdt_keepalive(void)108108+{109109+ bfin_write_WDOGB_STAT(0);110110+}111111+112112+static inline void nmi_wdt_stop(void)113113+{114114+ bfin_write_WDOGB_CTL(WDEN_DISABLE);115115+}116116+117117+/* before calling this function, you must stop the WDT */118118+static inline void nmi_wdt_clear(void)119119+{120120+ /* clear TRO bit, disable event generation */121121+ bfin_write_WDOGB_CTL(WDOG_EXPIRED | WDEN_DISABLE | ICTL_NONE);122122+}123123+124124+static inline void nmi_wdt_start(void)125125+{126126+ bfin_write_WDOGB_CTL(WDEN_ENABLE | ICTL_NMI);127127+}128128+129129+static inline int nmi_wdt_running(void)130130+{131131+ return ((bfin_read_WDOGB_CTL() & WDEN_MASK) != WDEN_DISABLE);132132+}133133+134134+static inline int nmi_wdt_set_timeout(unsigned long t)135135+{136136+ u32 cnt, max_t, sclk;137137+ int run;138138+139139+ sclk = get_sclk();140140+ max_t = -1 / sclk;141141+ cnt = t * sclk;142142+ if (t > max_t) {143143+ pr_warning("NMI: timeout value is too large\n");144144+ return -EINVAL;145145+ }146146+147147+ run = nmi_wdt_running();148148+ nmi_wdt_stop();149149+ bfin_write_WDOGB_CNT(cnt);150150+ if (run)151151+ nmi_wdt_start();152152+153153+ timeout = t;154154+155155+ return 0;156156+}157157+158158+int check_nmi_wdt_touched(void)159159+{160160+ unsigned int this_cpu = smp_processor_id();161161+ unsigned int cpu;162162+163163+ cpumask_t mask = cpu_online_map;164164+165165+ if (!atomic_read(&nmi_touched[this_cpu]))166166+ return 0;167167+168168+ atomic_set(&nmi_touched[this_cpu], 0);169169+170170+ cpu_clear(this_cpu, mask);171171+ for_each_cpu_mask(cpu, mask) {172172+ invalidate_dcache_range((unsigned long)(&nmi_touched[cpu]),173173+ (unsigned long)(&nmi_touched[cpu]));174174+ if (!atomic_read(&nmi_touched[cpu]))175175+ return 0;176176+ atomic_set(&nmi_touched[cpu], 0);177177+ }178178+179179+ return 1;180180+}181181+182182+static void nmi_wdt_timer(unsigned long data)183183+{184184+ if (check_nmi_wdt_touched())185185+ nmi_wdt_keepalive();186186+187187+ mod_timer(&ntimer, jiffies + NMI_CHECK_TIMEOUT);188188+}189189+190190+static int __init init_nmi_wdt(void)191191+{192192+ nmi_wdt_set_timeout(timeout);193193+ nmi_wdt_start();194194+ nmi_active = true;195195+196196+ init_timer(&ntimer);197197+ ntimer.function = nmi_wdt_timer;198198+ ntimer.expires = jiffies + NMI_CHECK_TIMEOUT;199199+ add_timer(&ntimer);200200+201201+ pr_info("nmi_wdt: initialized: timeout=%d sec\n", timeout);202202+ return 0;203203+}204204+device_initcall(init_nmi_wdt);205205+206206+void touch_nmi_watchdog(void)207207+{208208+ atomic_set(&nmi_touched[smp_processor_id()], 1);209209+}210210+211211+/* Suspend/resume support */212212+#ifdef CONFIG_PM213213+static int nmi_wdt_suspend(struct sys_device *dev, pm_message_t state)214214+{215215+ nmi_wdt_stop();216216+ return 0;217217+}218218+219219+static int nmi_wdt_resume(struct sys_device *dev)220220+{221221+ if (nmi_active)222222+ nmi_wdt_start();223223+ return 0;224224+}225225+226226+static struct sysdev_class nmi_sysclass = {227227+ .name = DRV_NAME,228228+ .resume = nmi_wdt_resume,229229+ .suspend = nmi_wdt_suspend,230230+};231231+232232+static struct sys_device device_nmi_wdt = {233233+ .id = 0,234234+ .cls = &nmi_sysclass,235235+};236236+237237+static int __init init_nmi_wdt_sysfs(void)238238+{239239+ int error;240240+241241+ if (!nmi_active)242242+ return 0;243243+244244+ error = sysdev_class_register(&nmi_sysclass);245245+ if (!error)246246+ error = sysdev_register(&device_nmi_wdt);247247+ return error;248248+}249249+late_initcall(init_nmi_wdt_sysfs);250250+251251+#endif /* CONFIG_PM */252252+253253+254254+asmlinkage notrace void do_nmi(struct pt_regs *fp)255255+{256256+ unsigned int cpu = smp_processor_id();257257+ nmi_enter();258258+259259+ cpu_pda[cpu].__nmi_count += 1;260260+261261+ if (cpu == nmi_wdt_cpu) {262262+ /* CoreB goes here first */263263+264264+ /* reload the WDOG_STAT */265265+ nmi_wdt_keepalive();266266+267267+ /* clear nmi interrupt for CoreB */268268+ nmi_wdt_stop();269269+ nmi_wdt_clear();270270+271271+ /* trigger NMI interrupt of CoreA */272272+ send_corea_nmi();273273+274274+ /* waiting CoreB to enter NMI */275275+ wait_nmi_event(COREA_ENTER_NMI);276276+277277+ /* recover WDOGA's settings */278278+ restore_corea_nmi();279279+280280+ save_corelock();281281+282282+ /* corelock is save/cleared, CoreA is dummping messages */283283+284284+ wait_nmi_event(COREA_EXIT_NMI);285285+ } else {286286+ /* OK, CoreA entered NMI */287287+ set_nmi_event(COREA_ENTER_NMI);288288+ }289289+290290+ pr_emerg("\nNMI Watchdog detected LOCKUP, dump for CPU %d\n", cpu);291291+ dump_bfin_process(fp);292292+ dump_bfin_mem(fp);293293+ show_regs(fp);294294+ dump_bfin_trace_buffer();295295+ show_stack(current, (unsigned long *)fp);296296+297297+ if (cpu == nmi_wdt_cpu) {298298+ pr_emerg("This fault is not recoverable, sorry!\n");299299+300300+ /* CoreA dump finished, restore the corelock */301301+ restore_corelock();302302+303303+ set_nmi_event(COREB_EXIT_NMI);304304+ } else {305305+ /* CoreB dump finished, notice the CoreA we are done */306306+ set_nmi_event(COREA_EXIT_NMI);307307+308308+ /* synchronize with CoreA */309309+ wait_nmi_event(COREB_EXIT_NMI);310310+ }311311+312312+ nmi_exit();313313+}
+4
arch/blackfin/kernel/time-ts.c
···2121#include <asm/blackfin.h>2222#include <asm/time.h>2323#include <asm/gptimers.h>2424+#include <asm/nmi.h>24252526/* Accelerators for sched_clock()2627 * convert from cycles(64bits) => nanoseconds (64bits)···310309311310 smp_mb();312311 evt->event_handler(evt);312312+313313+ touch_nmi_watchdog();314314+313315 return IRQ_HANDLED;314316}315317
+17-1
arch/blackfin/mach-common/interrupt.S
···194194ENDPROC(_evt_ivhw)195195196196/* Interrupt routine for evt2 (NMI).197197- * We don't actually use this, so just return.198197 * For inner circle type details, please see:199198 * http://docs.blackfin.uclinux.org/doku.php?id=linux-kernel:nmi200199 */201200ENTRY(_evt_nmi)201201+#ifndef CONFIG_NMI_WATCHDOG202202.weak _evt_nmi203203+#else204204+ /* Not take account of CPLBs, this handler will not return */205205+ SAVE_ALL_SYS206206+ r0 = sp;207207+ r1 = retn;208208+ [sp + PT_PC] = r1;209209+ trace_buffer_save(p4,r5);210210+211211+ ANOMALY_283_315_WORKAROUND(p4, r5)212212+213213+ SP += -12;214214+ call _do_nmi;215215+ SP += 12;216216+1:217217+ jump 1b;218218+#endif203219 rtn;204220ENDPROC(_evt_nmi)205221