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 <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[NR_CPUS];
  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 * since NMIs dont listen to _any_ locks, we have to be extremely
  43 * careful not to rely on unsafe variables. The printk might lock
  44 * up though, so we have to break up any console locks first ...
  45 * [when there will be more tty-related locks, break them up
  46 *  here too!]
  47 */
  48static unsigned int last_irq_sums[NR_CPUS];
  49
  50int __init check_watchdog(void)
  51{
  52        irq_cpustat_t tmp[1];
  53
  54        printk(KERN_INFO "Testing Watchdog... ");
  55
  56        memcpy(tmp, irq_stat, sizeof(tmp));
  57        local_irq_enable();
  58        mdelay((10 * 1000) / watchdog_hz); /* wait 10 ticks */
  59        local_irq_disable();
  60
  61        if (nmi_count(0) - tmp[0].__nmi_count <= 5) {
  62                printk(KERN_WARNING "CPU#%d: Watchdog appears to be stuck!\n",
  63                       0);
  64                return -1;
  65        }
  66
  67        printk(KERN_INFO "OK.\n");
  68
  69        /* now that we know it works we can reduce NMI frequency to something
  70         * more reasonable; makes a difference in some configs
  71         */
  72        watchdog_hz = 1;
  73
  74        return 0;
  75}
  76
  77static int __init setup_watchdog(char *str)
  78{
  79        unsigned tmp;
  80        int opt;
  81        u8 ctr;
  82
  83        get_option(&str, &opt);
  84        if (opt != 1)
  85                return 0;
  86
  87        watchdog = opt;
  88        if (watchdog) {
  89                set_intr_stub(EXCEP_WDT, watchdog_handler);
  90                ctr = WDCTR_WDCK_65536th;
  91                WDCTR = WDCTR_WDRST | ctr;
  92                WDCTR = ctr;
  93                tmp = WDCTR;
  94
  95                tmp = __muldiv64u(1 << (16 + ctr * 2), 1000000, MN10300_WDCLK);
  96                tmp = 1000000000 / tmp;
  97                watchdog_hz = (tmp + 500) / 1000;
  98        }
  99
 100        return 1;
 101}
 102
 103__setup("watchdog=", setup_watchdog);
 104
 105void __init watchdog_go(void)
 106{
 107        u8 wdt;
 108
 109        if (watchdog) {
 110                printk(KERN_INFO "Watchdog: running at %uHz\n", watchdog_hz);
 111                wdt = WDCTR & ~WDCTR_WDCNE;
 112                WDCTR = wdt | WDCTR_WDRST;
 113                wdt = WDCTR;
 114                WDCTR = wdt | WDCTR_WDCNE;
 115                wdt = WDCTR;
 116
 117                check_watchdog();
 118        }
 119}
 120
 121#ifdef CONFIG_SMP
 122static void watchdog_dump_register(void *dummy)
 123{
 124        printk(KERN_ERR "--- Register Dump (CPU%d) ---\n", CPUID);
 125        show_registers(current_frame());
 126}
 127#endif
 128
 129asmlinkage
 130void watchdog_interrupt(struct pt_regs *regs, enum exception_code excep)
 131{
 132        /*
 133         * Since current-> is always on the stack, and we always switch
 134         * the stack NMI-atomically, it's safe to use smp_processor_id().
 135         */
 136        int sum, cpu;
 137        int irq = NMIIRQ;
 138        u8 wdt, tmp;
 139
 140        wdt = WDCTR & ~WDCTR_WDCNE;
 141        WDCTR = wdt;
 142        tmp = WDCTR;
 143        NMICR = NMICR_WDIF;
 144
 145        nmi_count(smp_processor_id())++;
 146        kstat_incr_irqs_this_cpu(irq, irq_to_desc(irq));
 147
 148        for_each_online_cpu(cpu) {
 149
 150                sum = irq_stat[cpu].__irq_count;
 151
 152                if ((last_irq_sums[cpu] == sum)
 153#if defined(CONFIG_GDBSTUB) && defined(CONFIG_SMP)
 154                        && !(CHK_GDBSTUB_BUSY()
 155                             || atomic_read(&cpu_doing_single_step))
 156#endif
 157                        ) {
 158                        /*
 159                         * Ayiee, looks like this CPU is stuck ...
 160                         * wait a few IRQs (5 seconds) before doing the oops ...
 161                         */
 162                        watchdog_alert_counter[cpu]++;
 163                        if (watchdog_alert_counter[cpu] == 5 * watchdog_hz) {
 164                                spin_lock(&watchdog_print_lock);
 165                                /*
 166                                 * We are in trouble anyway, lets at least try
 167                                 * to get a message out.
 168                                 */
 169                                bust_spinlocks(1);
 170                                printk(KERN_ERR
 171                                       "NMI Watchdog detected LOCKUP on CPU%d,"
 172                                       " pc %08lx, registers:\n",
 173                                       cpu, regs->pc);
 174#ifdef CONFIG_SMP
 175                                printk(KERN_ERR
 176                                       "--- Register Dump (CPU%d) ---\n",
 177                                       CPUID);
 178#endif
 179                                show_registers(regs);
 180#ifdef CONFIG_SMP
 181                                smp_nmi_call_function(watchdog_dump_register,
 182                                        NULL, 1);
 183#endif
 184                                printk(KERN_NOTICE "console shuts up ...\n");
 185                                console_silent();
 186                                spin_unlock(&watchdog_print_lock);
 187                                bust_spinlocks(0);
 188#ifdef CONFIG_GDBSTUB
 189                                if (CHK_GDBSTUB_BUSY_AND_ACTIVE())
 190                                        gdbstub_exception(regs, excep);
 191                                else
 192                                        gdbstub_intercept(regs, excep);
 193#endif
 194                                do_exit(SIGSEGV);
 195                        }
 196                } else {
 197                        last_irq_sums[cpu] = sum;
 198                        watchdog_alert_counter[cpu] = 0;
 199                }
 200        }
 201
 202        WDCTR = wdt | WDCTR_WDRST;
 203        tmp = WDCTR;
 204        WDCTR = wdt | WDCTR_WDCNE;
 205        tmp = WDCTR;
 206}
 207