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 <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