forked from Minki/linux
b920de1b77
Add architecture support for the MN10300/AM33 CPUs produced by MEI to the kernel. This patch also adds board support for the ASB2303 with the ASB2308 daughter board, and the ASB2305. The only processor supported is the MN103E010, which is an AM33v2 core plus on-chip devices. [akpm@linux-foundation.org: nuke cvs control strings] Signed-off-by: Masakazu Urade <urade.masakazu@jp.panasonic.com> Signed-off-by: Koichi Yasutake <yasutake.koichi@jp.panasonic.com> Signed-off-by: David Howells <dhowells@redhat.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
184 lines
4.3 KiB
C
184 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 <asm/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();
|
|
u8 wdt, tmp;
|
|
|
|
wdt = WDCTR & ~WDCTR_WDCNE;
|
|
WDCTR = wdt;
|
|
tmp = WDCTR;
|
|
NMICR = NMICR_WDIF;
|
|
|
|
nmi_count(cpu)++;
|
|
kstat_this_cpu.irqs[NMIIRQ]++;
|
|
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;
|
|
}
|