linux/arch/mn10300/kernel/mn10300-watchdog.c
<<
>>
Prefs
   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 <asm/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        u8 wdt, tmp;
 134
 135        wdt = WDCTR & ~WDCTR_WDCNE;
 136        WDCTR = wdt;
 137        tmp = WDCTR;
 138        NMICR = NMICR_WDIF;
 139
 140        nmi_count(cpu)++;
 141        kstat_this_cpu.irqs[NMIIRQ]++;
 142        sum = irq_stat[cpu].__irq_count;
 143
 144        if (last_irq_sums[cpu] == sum) {
 145                /*
 146                 * Ayiee, looks like this CPU is stuck ...
 147                 * wait a few IRQs (5 seconds) before doing the oops ...
 148                 */
 149                watchdog_alert_counter++;
 150                if (watchdog_alert_counter == 5 * watchdog_hz) {
 151                        spin_lock(&watchdog_print_lock);
 152                        /*
 153                         * We are in trouble anyway, lets at least try
 154                         * to get a message out.
 155                         */
 156                        bust_spinlocks(1);
 157                        printk(KERN_ERR
 158                               "NMI Watchdog detected LOCKUP on CPU%d,"
 159                               " pc %08lx, registers:\n",
 160                               cpu, regs->pc);
 161                        show_registers(regs);
 162                        printk("console shuts up ...\n");
 163                        console_silent();
 164                        spin_unlock(&watchdog_print_lock);
 165                        bust_spinlocks(0);
 166#ifdef CONFIG_GDBSTUB
 167                        if (gdbstub_busy)
 168                                gdbstub_exception(regs, excep);
 169                        else
 170                                gdbstub_intercept(regs, excep);
 171#endif
 172                        do_exit(SIGSEGV);
 173                }
 174        } else {
 175                last_irq_sums[cpu] = sum;
 176                watchdog_alert_counter = 0;
 177        }
 178
 179        WDCTR = wdt | WDCTR_WDRST;
 180        tmp = WDCTR;
 181        WDCTR = wdt | WDCTR_WDCNE;
 182        tmp = WDCTR;
 183}
 184