Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
mn10300-watchdog.c
Go to the documentation of this file.
1 /* MN10300 Watchdog timer
2  *
3  * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
4  * Written by David Howells ([email protected])
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 
29 static DEFINE_SPINLOCK(watchdog_print_lock);
30 static unsigned int watchdog;
31 static unsigned int watchdog_hz = 1;
33 
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  */
47 static unsigned int last_irq_sums[NR_CPUS];
48 
50 {
51  irq_cpustat_t tmp[1];
52 
53  printk(KERN_INFO "Testing Watchdog... ");
54 
55  memcpy(tmp, irq_stat, sizeof(tmp));
57  mdelay((10 * 1000) / watchdog_hz); /* wait 10 ticks */
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 
76 static 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 
104 void __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
121 static 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 
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 
145  kstat_incr_irqs_this_cpu(irq, irq_to_desc(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  */
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);
170  "NMI Watchdog detected LOCKUP on CPU%d,"
171  " pc %08lx, registers:\n",
172  cpu, regs->pc);
173 #ifdef CONFIG_SMP
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;
198  }
199  }
200 
201  WDCTR = wdt | WDCTR_WDRST;
202  tmp = WDCTR;
203  WDCTR = wdt | WDCTR_WDCNE;
204  tmp = WDCTR;
205 }