1/* MN10300 Watchdog timer 2 * 3 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. 4 * Written by David Howells (dhowells@redhat.com) 5 * - Derived from arch/i386/kernel/nmi.c 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public Licence 9 * as published by the Free Software Foundation; either version 10 * 2 of the Licence, or (at your option) any later version. 11 */ 12#include <linux/module.h> 13#include <linux/sched.h> 14#include <linux/kernel.h> 15#include <linux/init.h> 16#include <linux/delay.h> 17#include <linux/interrupt.h> 18#include <linux/kernel_stat.h> 19#include <linux/nmi.h> 20#include <asm/processor.h> 21#include <asm/system.h> 22#include <asm/atomic.h> 23#include <asm/intctl-regs.h> 24#include <asm/rtc-regs.h> 25#include <asm/div64.h> 26#include <asm/smp.h> 27#include <asm/gdb-stub.h> 28#include <proc/clock.h> 29 30static DEFINE_SPINLOCK(watchdog_print_lock); 31static unsigned int watchdog; 32static unsigned int watchdog_hz = 1; 33unsigned int watchdog_alert_counter; 34 35EXPORT_SYMBOL(touch_nmi_watchdog); 36 37/* 38 * the best way to detect whether a CPU has a 'hard lockup' problem 39 * is to check its timer makes IRQ counts. If they are not 40 * changing then that CPU has some problem. 41 * 42 * as these watchdog NMI IRQs are generated on every CPU, we only 43 * have to check the current processor. 44 * 45 * since NMIs dont listen to _any_ locks, we have to be extremely 46 * careful not to rely on unsafe variables. The printk might lock 47 * up though, so we have to break up any console locks first ... 48 * [when there will be more tty-related locks, break them up 49 * here too!] 50 */ 51static unsigned int last_irq_sums[NR_CPUS]; 52 53int __init check_watchdog(void) 54{ 55 irq_cpustat_t tmp[1]; 56 57 printk(KERN_INFO "Testing Watchdog... "); 58 59 memcpy(tmp, irq_stat, sizeof(tmp)); 60 local_irq_enable(); 61 mdelay((10 * 1000) / watchdog_hz); /* wait 10 ticks */ 62 local_irq_disable(); 63 64 if (nmi_count(0) - tmp[0].__nmi_count <= 5) { 65 printk(KERN_WARNING "CPU#%d: Watchdog appears to be stuck!\n", 66 0); 67 return -1; 68 } 69 70 printk(KERN_INFO "OK.\n"); 71 72 /* now that we know it works we can reduce NMI frequency to 73 * something more reasonable; makes a difference in some configs 74 */ 75 watchdog_hz = 1; 76 77 return 0; 78} 79 80static int __init setup_watchdog(char *str) 81{ 82 unsigned tmp; 83 int opt; 84 u8 ctr; 85 86 get_option(&str, &opt); 87 if (opt != 1) 88 return 0; 89 90 watchdog = opt; 91 if (watchdog) { 92 set_intr_stub(EXCEP_WDT, watchdog_handler); 93 ctr = WDCTR_WDCK_65536th; 94 WDCTR = WDCTR_WDRST | ctr; 95 WDCTR = ctr; 96 tmp = WDCTR; 97 98 tmp = __muldiv64u(1 << (16 + ctr * 2), 1000000, MN10300_WDCLK); 99 tmp = 1000000000 / tmp; 100 watchdog_hz = (tmp + 500) / 1000; 101 } 102 103 return 1; 104} 105 106__setup("watchdog=", setup_watchdog); 107 108void __init watchdog_go(void) 109{ 110 u8 wdt; 111 112 if (watchdog) { 113 printk(KERN_INFO "Watchdog: running at %uHz\n", watchdog_hz); 114 wdt = WDCTR & ~WDCTR_WDCNE; 115 WDCTR = wdt | WDCTR_WDRST; 116 wdt = WDCTR; 117 WDCTR = wdt | WDCTR_WDCNE; 118 wdt = WDCTR; 119 120 check_watchdog(); 121 } 122} 123 124asmlinkage 125void watchdog_interrupt(struct pt_regs *regs, enum exception_code excep) 126{ 127 128 /* 129 * Since current-> is always on the stack, and we always switch 130 * the stack NMI-atomically, it's safe to use smp_processor_id(). 131 */ 132 int sum, cpu = smp_processor_id(); 133 int irq = NMIIRQ; 134 u8 wdt, tmp; 135 136 wdt = WDCTR & ~WDCTR_WDCNE; 137 WDCTR = wdt; 138 tmp = WDCTR; 139 NMICR = NMICR_WDIF; 140 141 nmi_count(cpu)++; 142 kstat_incr_irqs_this_cpu(irq, irq_to_desc(irq)); 143 sum = irq_stat[cpu].__irq_count; 144 145 if (last_irq_sums[cpu] == sum) { 146 /* 147 * Ayiee, looks like this CPU is stuck ... 148 * wait a few IRQs (5 seconds) before doing the oops ... 149 */ 150 watchdog_alert_counter++; 151 if (watchdog_alert_counter == 5 * watchdog_hz) { 152 spin_lock(&watchdog_print_lock); 153 /* 154 * We are in trouble anyway, lets at least try 155 * to get a message out. 156 */ 157 bust_spinlocks(1); 158 printk(KERN_ERR 159 "NMI Watchdog detected LOCKUP on CPU%d," 160 " pc %08lx, registers:\n", 161 cpu, regs->pc); 162 show_registers(regs); 163 printk("console shuts up ...\n"); 164 console_silent(); 165 spin_unlock(&watchdog_print_lock); 166 bust_spinlocks(0); 167#ifdef CONFIG_GDBSTUB 168 if (gdbstub_busy) 169 gdbstub_exception(regs, excep); 170 else 171 gdbstub_intercept(regs, excep); 172#endif 173 do_exit(SIGSEGV); 174 } 175 } else { 176 last_irq_sums[cpu] = sum; 177 watchdog_alert_counter = 0; 178 } 179 180 WDCTR = wdt | WDCTR_WDRST; 181 tmp = WDCTR; 182 WDCTR = wdt | WDCTR_WDCNE; 183 tmp = WDCTR; 184} 185