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 <linux/atomic.h> 22#include <asm/intctl-regs.h> 23#include <asm/rtc-regs.h> 24#include <asm/div64.h> 25#include <asm/smp.h> 26#include <asm/gdb-stub.h> 27#include <proc/clock.h> 28 29static DEFINE_SPINLOCK(watchdog_print_lock); 30static unsigned int watchdog; 31static unsigned int watchdog_hz = 1; 32unsigned int watchdog_alert_counter[NR_CPUS]; 33 34EXPORT_SYMBOL(touch_nmi_watchdog); 35 36/* 37 * the best way to detect whether a CPU has a 'hard lockup' problem 38 * is to check its timer makes IRQ counts. If they are not 39 * changing then that CPU has some problem. 40 * 41 * since NMIs dont listen to _any_ locks, we have to be extremely 42 * careful not to rely on unsafe variables. The printk might lock 43 * up though, so we have to break up any console locks first ... 44 * [when there will be more tty-related locks, break them up 45 * here too!] 46 */ 47static unsigned int last_irq_sums[NR_CPUS]; 48 49int __init check_watchdog(void) 50{ 51 irq_cpustat_t tmp[1]; 52 53 printk(KERN_INFO "Testing Watchdog... "); 54 55 memcpy(tmp, irq_stat, sizeof(tmp)); 56 local_irq_enable(); 57 mdelay((10 * 1000) / watchdog_hz); /* wait 10 ticks */ 58 local_irq_disable(); 59 60 if (nmi_count(0) - tmp[0].__nmi_count <= 5) { 61 printk(KERN_WARNING "CPU#%d: Watchdog appears to be stuck!\n", 62 0); 63 return -1; 64 } 65 66 printk(KERN_INFO "OK.\n"); 67 68 /* now that we know it works we can reduce NMI frequency to something 69 * more reasonable; makes a difference in some configs 70 */ 71 watchdog_hz = 1; 72 73 return 0; 74} 75 76static int __init setup_watchdog(char *str) 77{ 78 unsigned tmp; 79 int opt; 80 u8 ctr; 81 82 get_option(&str, &opt); 83 if (opt != 1) 84 return 0; 85 86 watchdog = opt; 87 if (watchdog) { 88 set_intr_stub(EXCEP_WDT, watchdog_handler); 89 ctr = WDCTR_WDCK_65536th; 90 WDCTR = WDCTR_WDRST | ctr; 91 WDCTR = ctr; 92 tmp = WDCTR; 93 94 tmp = __muldiv64u(1 << (16 + ctr * 2), 1000000, MN10300_WDCLK); 95 tmp = 1000000000 / tmp; 96 watchdog_hz = (tmp + 500) / 1000; 97 } 98 99 return 1; 100} 101 102__setup("watchdog=", setup_watchdog); 103 104void __init watchdog_go(void) 105{ 106 u8 wdt; 107 108 if (watchdog) { 109 printk(KERN_INFO "Watchdog: running at %uHz\n", watchdog_hz); 110 wdt = WDCTR & ~WDCTR_WDCNE; 111 WDCTR = wdt | WDCTR_WDRST; 112 wdt = WDCTR; 113 WDCTR = wdt | WDCTR_WDCNE; 114 wdt = WDCTR; 115 116 check_watchdog(); 117 } 118} 119 120#ifdef CONFIG_SMP 121static void watchdog_dump_register(void *dummy) 122{ 123 printk(KERN_ERR "--- Register Dump (CPU%d) ---\n", CPUID); 124 show_registers(current_frame()); 125} 126#endif 127 128asmlinkage 129void watchdog_interrupt(struct pt_regs *regs, enum exception_code excep) 130{ 131 /* 132 * Since current-> is always on the stack, and we always switch 133 * the stack NMI-atomically, it's safe to use smp_processor_id(). 134 */ 135 int sum, cpu; 136 int irq = NMIIRQ; 137 u8 wdt, tmp; 138 139 wdt = WDCTR & ~WDCTR_WDCNE; 140 WDCTR = wdt; 141 tmp = WDCTR; 142 NMICR = NMICR_WDIF; 143 144 nmi_count(smp_processor_id())++; 145 kstat_incr_irq_this_cpu(irq); 146 147 for_each_online_cpu(cpu) { 148 149 sum = irq_stat[cpu].__irq_count; 150 151 if ((last_irq_sums[cpu] == sum) 152#if defined(CONFIG_GDBSTUB) && defined(CONFIG_SMP) 153 && !(CHK_GDBSTUB_BUSY() 154 || atomic_read(&cpu_doing_single_step)) 155#endif 156 ) { 157 /* 158 * Ayiee, looks like this CPU is stuck ... 159 * wait a few IRQs (5 seconds) before doing the oops ... 160 */ 161 watchdog_alert_counter[cpu]++; 162 if (watchdog_alert_counter[cpu] == 5 * watchdog_hz) { 163 spin_lock(&watchdog_print_lock); 164 /* 165 * We are in trouble anyway, lets at least try 166 * to get a message out. 167 */ 168 bust_spinlocks(1); 169 printk(KERN_ERR 170 "NMI Watchdog detected LOCKUP on CPU%d," 171 " pc %08lx, registers:\n", 172 cpu, regs->pc); 173#ifdef CONFIG_SMP 174 printk(KERN_ERR 175 "--- Register Dump (CPU%d) ---\n", 176 CPUID); 177#endif 178 show_registers(regs); 179#ifdef CONFIG_SMP 180 smp_nmi_call_function(watchdog_dump_register, 181 NULL, 1); 182#endif 183 printk(KERN_NOTICE "console shuts up ...\n"); 184 console_silent(); 185 spin_unlock(&watchdog_print_lock); 186 bust_spinlocks(0); 187#ifdef CONFIG_GDBSTUB 188 if (CHK_GDBSTUB_BUSY_AND_ACTIVE()) 189 gdbstub_exception(regs, excep); 190 else 191 gdbstub_intercept(regs, excep); 192#endif 193 do_exit(SIGSEGV); 194 } 195 } else { 196 last_irq_sums[cpu] = sum; 197 watchdog_alert_counter[cpu] = 0; 198 } 199 } 200 201 WDCTR = wdt | WDCTR_WDRST; 202 tmp = WDCTR; 203 WDCTR = wdt | WDCTR_WDCNE; 204 tmp = WDCTR; 205} 206