Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/blackfin/kernel/nmi.c
10817 views
1
/*
2
* Blackfin nmi_watchdog Driver
3
*
4
* Originally based on bfin_wdt.c
5
* Copyright 2010-2010 Analog Devices Inc.
6
* Graff Yang <[email protected]>
7
*
8
* Enter bugs at http://blackfin.uclinux.org/
9
*
10
* Licensed under the GPL-2 or later.
11
*/
12
13
#include <linux/bitops.h>
14
#include <linux/hardirq.h>
15
#include <linux/syscore_ops.h>
16
#include <linux/pm.h>
17
#include <linux/nmi.h>
18
#include <linux/smp.h>
19
#include <linux/timer.h>
20
#include <asm/blackfin.h>
21
#include <asm/atomic.h>
22
#include <asm/cacheflush.h>
23
#include <asm/bfin_watchdog.h>
24
25
#define DRV_NAME "nmi-wdt"
26
27
#define NMI_WDT_TIMEOUT 5 /* 5 seconds */
28
#define NMI_CHECK_TIMEOUT (4 * HZ) /* 4 seconds in jiffies */
29
static int nmi_wdt_cpu = 1;
30
31
static unsigned int timeout = NMI_WDT_TIMEOUT;
32
static int nmi_active;
33
34
static unsigned short wdoga_ctl;
35
static unsigned int wdoga_cnt;
36
static struct corelock_slot saved_corelock;
37
static atomic_t nmi_touched[NR_CPUS];
38
static struct timer_list ntimer;
39
40
enum {
41
COREA_ENTER_NMI = 0,
42
COREA_EXIT_NMI,
43
COREB_EXIT_NMI,
44
45
NMI_EVENT_NR,
46
};
47
static unsigned long nmi_event __attribute__ ((__section__(".l2.bss")));
48
49
/* we are in nmi, non-atomic bit ops is safe */
50
static inline void set_nmi_event(int event)
51
{
52
__set_bit(event, &nmi_event);
53
}
54
55
static inline void wait_nmi_event(int event)
56
{
57
while (!test_bit(event, &nmi_event))
58
barrier();
59
__clear_bit(event, &nmi_event);
60
}
61
62
static inline void send_corea_nmi(void)
63
{
64
wdoga_ctl = bfin_read_WDOGA_CTL();
65
wdoga_cnt = bfin_read_WDOGA_CNT();
66
67
bfin_write_WDOGA_CTL(WDEN_DISABLE);
68
bfin_write_WDOGA_CNT(0);
69
bfin_write_WDOGA_CTL(WDEN_ENABLE | ICTL_NMI);
70
}
71
72
static inline void restore_corea_nmi(void)
73
{
74
bfin_write_WDOGA_CTL(WDEN_DISABLE);
75
bfin_write_WDOGA_CTL(WDOG_EXPIRED | WDEN_DISABLE | ICTL_NONE);
76
77
bfin_write_WDOGA_CNT(wdoga_cnt);
78
bfin_write_WDOGA_CTL(wdoga_ctl);
79
}
80
81
static inline void save_corelock(void)
82
{
83
saved_corelock = corelock;
84
corelock.lock = 0;
85
}
86
87
static inline void restore_corelock(void)
88
{
89
corelock = saved_corelock;
90
}
91
92
93
static inline void nmi_wdt_keepalive(void)
94
{
95
bfin_write_WDOGB_STAT(0);
96
}
97
98
static inline void nmi_wdt_stop(void)
99
{
100
bfin_write_WDOGB_CTL(WDEN_DISABLE);
101
}
102
103
/* before calling this function, you must stop the WDT */
104
static inline void nmi_wdt_clear(void)
105
{
106
/* clear TRO bit, disable event generation */
107
bfin_write_WDOGB_CTL(WDOG_EXPIRED | WDEN_DISABLE | ICTL_NONE);
108
}
109
110
static inline void nmi_wdt_start(void)
111
{
112
bfin_write_WDOGB_CTL(WDEN_ENABLE | ICTL_NMI);
113
}
114
115
static inline int nmi_wdt_running(void)
116
{
117
return ((bfin_read_WDOGB_CTL() & WDEN_MASK) != WDEN_DISABLE);
118
}
119
120
static inline int nmi_wdt_set_timeout(unsigned long t)
121
{
122
u32 cnt, max_t, sclk;
123
int run;
124
125
sclk = get_sclk();
126
max_t = -1 / sclk;
127
cnt = t * sclk;
128
if (t > max_t) {
129
pr_warning("NMI: timeout value is too large\n");
130
return -EINVAL;
131
}
132
133
run = nmi_wdt_running();
134
nmi_wdt_stop();
135
bfin_write_WDOGB_CNT(cnt);
136
if (run)
137
nmi_wdt_start();
138
139
timeout = t;
140
141
return 0;
142
}
143
144
int check_nmi_wdt_touched(void)
145
{
146
unsigned int this_cpu = smp_processor_id();
147
unsigned int cpu;
148
cpumask_t mask;
149
150
cpumask_copy(&mask, cpu_online_mask);
151
if (!atomic_read(&nmi_touched[this_cpu]))
152
return 0;
153
154
atomic_set(&nmi_touched[this_cpu], 0);
155
156
cpumask_clear_cpu(this_cpu, &mask);
157
for_each_cpu(cpu, &mask) {
158
invalidate_dcache_range((unsigned long)(&nmi_touched[cpu]),
159
(unsigned long)(&nmi_touched[cpu]));
160
if (!atomic_read(&nmi_touched[cpu]))
161
return 0;
162
atomic_set(&nmi_touched[cpu], 0);
163
}
164
165
return 1;
166
}
167
168
static void nmi_wdt_timer(unsigned long data)
169
{
170
if (check_nmi_wdt_touched())
171
nmi_wdt_keepalive();
172
173
mod_timer(&ntimer, jiffies + NMI_CHECK_TIMEOUT);
174
}
175
176
static int __init init_nmi_wdt(void)
177
{
178
nmi_wdt_set_timeout(timeout);
179
nmi_wdt_start();
180
nmi_active = true;
181
182
init_timer(&ntimer);
183
ntimer.function = nmi_wdt_timer;
184
ntimer.expires = jiffies + NMI_CHECK_TIMEOUT;
185
add_timer(&ntimer);
186
187
pr_info("nmi_wdt: initialized: timeout=%d sec\n", timeout);
188
return 0;
189
}
190
device_initcall(init_nmi_wdt);
191
192
void touch_nmi_watchdog(void)
193
{
194
atomic_set(&nmi_touched[smp_processor_id()], 1);
195
}
196
197
/* Suspend/resume support */
198
#ifdef CONFIG_PM
199
static int nmi_wdt_suspend(void)
200
{
201
nmi_wdt_stop();
202
return 0;
203
}
204
205
static void nmi_wdt_resume(void)
206
{
207
if (nmi_active)
208
nmi_wdt_start();
209
}
210
211
static struct syscore_ops nmi_syscore_ops = {
212
.resume = nmi_wdt_resume,
213
.suspend = nmi_wdt_suspend,
214
};
215
216
static int __init init_nmi_wdt_syscore(void)
217
{
218
if (nmi_active)
219
register_syscore_ops(&nmi_syscore_ops);
220
221
return 0;
222
}
223
late_initcall(init_nmi_wdt_syscore);
224
225
#endif /* CONFIG_PM */
226
227
228
asmlinkage notrace void do_nmi(struct pt_regs *fp)
229
{
230
unsigned int cpu = smp_processor_id();
231
nmi_enter();
232
233
cpu_pda[cpu].__nmi_count += 1;
234
235
if (cpu == nmi_wdt_cpu) {
236
/* CoreB goes here first */
237
238
/* reload the WDOG_STAT */
239
nmi_wdt_keepalive();
240
241
/* clear nmi interrupt for CoreB */
242
nmi_wdt_stop();
243
nmi_wdt_clear();
244
245
/* trigger NMI interrupt of CoreA */
246
send_corea_nmi();
247
248
/* waiting CoreB to enter NMI */
249
wait_nmi_event(COREA_ENTER_NMI);
250
251
/* recover WDOGA's settings */
252
restore_corea_nmi();
253
254
save_corelock();
255
256
/* corelock is save/cleared, CoreA is dummping messages */
257
258
wait_nmi_event(COREA_EXIT_NMI);
259
} else {
260
/* OK, CoreA entered NMI */
261
set_nmi_event(COREA_ENTER_NMI);
262
}
263
264
pr_emerg("\nNMI Watchdog detected LOCKUP, dump for CPU %d\n", cpu);
265
dump_bfin_process(fp);
266
dump_bfin_mem(fp);
267
show_regs(fp);
268
dump_bfin_trace_buffer();
269
show_stack(current, (unsigned long *)fp);
270
271
if (cpu == nmi_wdt_cpu) {
272
pr_emerg("This fault is not recoverable, sorry!\n");
273
274
/* CoreA dump finished, restore the corelock */
275
restore_corelock();
276
277
set_nmi_event(COREB_EXIT_NMI);
278
} else {
279
/* CoreB dump finished, notice the CoreA we are done */
280
set_nmi_event(COREA_EXIT_NMI);
281
282
/* synchronize with CoreA */
283
wait_nmi_event(COREB_EXIT_NMI);
284
}
285
286
nmi_exit();
287
}
288
289