linux/arch/mn10300/kernel/mn10300-watchdog.c
David Howells 2f2a2132ff Separate out the proc- and unit-specific header directories from the general
MN10300 arch headers and place them instead in the same directories as contain
the .c files for the processor and unit implementations.

This permits the symlinks include/asm/proc and include/asm/unit to be
dispensed with.  This does, however, require that #include <asm/proc/xxx.h> be
converted to #include <proc/xxx.h> and similarly for asm/unit -> unit.

Signed-off-by: David Howells <dhowells@redhat.com>
2009-04-10 14:33:48 +01:00

185 lines
4.3 KiB
C

/* MN10300 Watchdog timer
*
* Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
* - Derived from arch/i386/kernel/nmi.c
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <linux/nmi.h>
#include <asm/processor.h>
#include <asm/system.h>
#include <asm/atomic.h>
#include <asm/intctl-regs.h>
#include <asm/rtc-regs.h>
#include <asm/div64.h>
#include <asm/smp.h>
#include <asm/gdb-stub.h>
#include <proc/clock.h>
static DEFINE_SPINLOCK(watchdog_print_lock);
static unsigned int watchdog;
static unsigned int watchdog_hz = 1;
unsigned int watchdog_alert_counter;
EXPORT_SYMBOL(touch_nmi_watchdog);
/*
* the best way to detect whether a CPU has a 'hard lockup' problem
* is to check its timer makes IRQ counts. If they are not
* changing then that CPU has some problem.
*
* as these watchdog NMI IRQs are generated on every CPU, we only
* have to check the current processor.
*
* since NMIs dont listen to _any_ locks, we have to be extremely
* careful not to rely on unsafe variables. The printk might lock
* up though, so we have to break up any console locks first ...
* [when there will be more tty-related locks, break them up
* here too!]
*/
static unsigned int last_irq_sums[NR_CPUS];
int __init check_watchdog(void)
{
irq_cpustat_t tmp[1];
printk(KERN_INFO "Testing Watchdog... ");
memcpy(tmp, irq_stat, sizeof(tmp));
local_irq_enable();
mdelay((10 * 1000) / watchdog_hz); /* wait 10 ticks */
local_irq_disable();
if (nmi_count(0) - tmp[0].__nmi_count <= 5) {
printk(KERN_WARNING "CPU#%d: Watchdog appears to be stuck!\n",
0);
return -1;
}
printk(KERN_INFO "OK.\n");
/* now that we know it works we can reduce NMI frequency to
* something more reasonable; makes a difference in some configs
*/
watchdog_hz = 1;
return 0;
}
static int __init setup_watchdog(char *str)
{
unsigned tmp;
int opt;
u8 ctr;
get_option(&str, &opt);
if (opt != 1)
return 0;
watchdog = opt;
if (watchdog) {
set_intr_stub(EXCEP_WDT, watchdog_handler);
ctr = WDCTR_WDCK_65536th;
WDCTR = WDCTR_WDRST | ctr;
WDCTR = ctr;
tmp = WDCTR;
tmp = __muldiv64u(1 << (16 + ctr * 2), 1000000, MN10300_WDCLK);
tmp = 1000000000 / tmp;
watchdog_hz = (tmp + 500) / 1000;
}
return 1;
}
__setup("watchdog=", setup_watchdog);
void __init watchdog_go(void)
{
u8 wdt;
if (watchdog) {
printk(KERN_INFO "Watchdog: running at %uHz\n", watchdog_hz);
wdt = WDCTR & ~WDCTR_WDCNE;
WDCTR = wdt | WDCTR_WDRST;
wdt = WDCTR;
WDCTR = wdt | WDCTR_WDCNE;
wdt = WDCTR;
check_watchdog();
}
}
asmlinkage
void watchdog_interrupt(struct pt_regs *regs, enum exception_code excep)
{
/*
* Since current-> is always on the stack, and we always switch
* the stack NMI-atomically, it's safe to use smp_processor_id().
*/
int sum, cpu = smp_processor_id();
int irq = NMIIRQ;
u8 wdt, tmp;
wdt = WDCTR & ~WDCTR_WDCNE;
WDCTR = wdt;
tmp = WDCTR;
NMICR = NMICR_WDIF;
nmi_count(cpu)++;
kstat_incr_irqs_this_cpu(irq, irq_to_desc(irq));
sum = irq_stat[cpu].__irq_count;
if (last_irq_sums[cpu] == sum) {
/*
* Ayiee, looks like this CPU is stuck ...
* wait a few IRQs (5 seconds) before doing the oops ...
*/
watchdog_alert_counter++;
if (watchdog_alert_counter == 5 * watchdog_hz) {
spin_lock(&watchdog_print_lock);
/*
* We are in trouble anyway, lets at least try
* to get a message out.
*/
bust_spinlocks(1);
printk(KERN_ERR
"NMI Watchdog detected LOCKUP on CPU%d,"
" pc %08lx, registers:\n",
cpu, regs->pc);
show_registers(regs);
printk("console shuts up ...\n");
console_silent();
spin_unlock(&watchdog_print_lock);
bust_spinlocks(0);
#ifdef CONFIG_GDBSTUB
if (gdbstub_busy)
gdbstub_exception(regs, excep);
else
gdbstub_intercept(regs, excep);
#endif
do_exit(SIGSEGV);
}
} else {
last_irq_sums[cpu] = sum;
watchdog_alert_counter = 0;
}
WDCTR = wdt | WDCTR_WDRST;
tmp = WDCTR;
WDCTR = wdt | WDCTR_WDCNE;
tmp = WDCTR;
}