2008-02-08 15:19:31 +03:00
/* 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 ( ) ;
2009-01-15 02:43:54 +03:00
int irq = NMIIRQ ;
2008-02-08 15:19:31 +03:00
u8 wdt , tmp ;
wdt = WDCTR & ~ WDCTR_WDCNE ;
WDCTR = wdt ;
tmp = WDCTR ;
NMICR = NMICR_WDIF ;
nmi_count ( cpu ) + + ;
2009-01-15 02:43:54 +03:00
kstat_incr_irqs_this_cpu ( irq , irq_to_desc ( irq ) ) ;
2008-02-08 15:19:31 +03:00
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 ;
}