1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
40
41#include <linux/module.h>
42#include <linux/types.h>
43#include <linux/miscdevice.h>
44#include <linux/watchdog.h>
45#include <linux/ioport.h>
46#include <linux/delay.h>
47#include <linux/notifier.h>
48#include <linux/fs.h>
49#include <linux/reboot.h>
50#include <linux/init.h>
51#include <linux/spinlock.h>
52#include <linux/moduleparam.h>
53#include <linux/io.h>
54#include <linux/uaccess.h>
55
56
57static unsigned long sbc8360_is_open;
58static char expect_close;
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126static int wd_times[64][2] = {
127 {0, 1},
128 {1, 1},
129 {2, 1},
130 {3, 1},
131 {4, 1},
132 {5, 1},
133 {6, 1},
134 {7, 1},
135 {8, 1},
136 {9, 1},
137 {0xA, 1},
138 {0xB, 1},
139 {0xC, 1},
140 {0xD, 1},
141 {0xE, 1},
142 {0xF, 1},
143 {0, 2},
144 {1, 2},
145 {2, 2},
146 {3, 2},
147 {4, 2},
148 {5, 2},
149 {6, 2},
150 {7, 2},
151 {8, 2},
152 {9, 2},
153 {0xA, 2},
154 {0xB, 2},
155 {0xC, 2},
156 {0xD, 2},
157 {0xE, 2},
158 {0xF, 2},
159 {0, 3},
160 {1, 3},
161 {2, 3},
162 {3, 3},
163 {4, 3},
164 {5, 3},
165 {6, 3},
166 {7, 3},
167 {8, 3},
168 {9, 3},
169 {0xA, 3},
170 {0xB, 3},
171 {0xC, 3},
172 {0xD, 3},
173 {0xE, 3},
174 {0xF, 3},
175 {0, 4},
176 {1, 4},
177 {2, 4},
178 {3, 4},
179 {4, 4},
180 {5, 4},
181 {6, 4},
182 {7, 4},
183 {8, 4},
184 {9, 4},
185 {0xA, 4},
186 {0xB, 4},
187 {0xC, 4},
188 {0xD, 4},
189 {0xE, 4},
190 {0xF, 4}
191};
192
193#define SBC8360_ENABLE 0x120
194#define SBC8360_BASETIME 0x121
195
196static int timeout = 27;
197static int wd_margin = 0xB;
198static int wd_multiplier = 2;
199static bool nowayout = WATCHDOG_NOWAYOUT;
200
201module_param(timeout, int, 0);
202MODULE_PARM_DESC(timeout, "Index into timeout table (0-63) (default=27 (60s))");
203module_param(nowayout, bool, 0);
204MODULE_PARM_DESC(nowayout,
205 "Watchdog cannot be stopped once started (default="
206 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
207
208
209
210
211
212
213static void sbc8360_activate(void)
214{
215
216 outb(0x0A, SBC8360_ENABLE);
217 msleep_interruptible(100);
218 outb(0x0B, SBC8360_ENABLE);
219 msleep_interruptible(100);
220
221 outb(wd_multiplier, SBC8360_ENABLE);
222 msleep_interruptible(100);
223
224}
225
226
227static void sbc8360_ping(void)
228{
229
230 outb(wd_margin, SBC8360_BASETIME);
231}
232
233
234static void sbc8360_stop(void)
235{
236
237 outb(0, SBC8360_ENABLE);
238}
239
240
241static ssize_t sbc8360_write(struct file *file, const char __user *buf,
242 size_t count, loff_t *ppos)
243{
244 if (count) {
245 if (!nowayout) {
246 size_t i;
247
248
249 expect_close = 0;
250
251 for (i = 0; i != count; i++) {
252 char c;
253 if (get_user(c, buf + i))
254 return -EFAULT;
255 if (c == 'V')
256 expect_close = 42;
257 }
258 }
259 sbc8360_ping();
260 }
261 return count;
262}
263
264static int sbc8360_open(struct inode *inode, struct file *file)
265{
266 if (test_and_set_bit(0, &sbc8360_is_open))
267 return -EBUSY;
268 if (nowayout)
269 __module_get(THIS_MODULE);
270
271
272 sbc8360_activate();
273 sbc8360_ping();
274 return nonseekable_open(inode, file);
275}
276
277static int sbc8360_close(struct inode *inode, struct file *file)
278{
279 if (expect_close == 42)
280 sbc8360_stop();
281 else
282 pr_crit("SBC8360 device closed unexpectedly. SBC8360 will not stop!\n");
283
284 clear_bit(0, &sbc8360_is_open);
285 expect_close = 0;
286 return 0;
287}
288
289
290
291
292
293static int sbc8360_notify_sys(struct notifier_block *this, unsigned long code,
294 void *unused)
295{
296 if (code == SYS_DOWN || code == SYS_HALT)
297 sbc8360_stop();
298
299 return NOTIFY_DONE;
300}
301
302
303
304
305
306static const struct file_operations sbc8360_fops = {
307 .owner = THIS_MODULE,
308 .llseek = no_llseek,
309 .write = sbc8360_write,
310 .open = sbc8360_open,
311 .release = sbc8360_close,
312};
313
314static struct miscdevice sbc8360_miscdev = {
315 .minor = WATCHDOG_MINOR,
316 .name = "watchdog",
317 .fops = &sbc8360_fops,
318};
319
320
321
322
323
324
325static struct notifier_block sbc8360_notifier = {
326 .notifier_call = sbc8360_notify_sys,
327};
328
329static int __init sbc8360_init(void)
330{
331 int res;
332 unsigned long int mseconds = 60000;
333
334 if (timeout < 0 || timeout > 63) {
335 pr_err("Invalid timeout index (must be 0-63)\n");
336 res = -EINVAL;
337 goto out;
338 }
339
340 if (!request_region(SBC8360_ENABLE, 1, "SBC8360")) {
341 pr_err("ENABLE method I/O %X is not available\n",
342 SBC8360_ENABLE);
343 res = -EIO;
344 goto out;
345 }
346 if (!request_region(SBC8360_BASETIME, 1, "SBC8360")) {
347 pr_err("BASETIME method I/O %X is not available\n",
348 SBC8360_BASETIME);
349 res = -EIO;
350 goto out_nobasetimereg;
351 }
352
353 res = register_reboot_notifier(&sbc8360_notifier);
354 if (res) {
355 pr_err("Failed to register reboot notifier\n");
356 goto out_noreboot;
357 }
358
359 res = misc_register(&sbc8360_miscdev);
360 if (res) {
361 pr_err("failed to register misc device\n");
362 goto out_nomisc;
363 }
364
365 wd_margin = wd_times[timeout][0];
366 wd_multiplier = wd_times[timeout][1];
367
368 if (wd_multiplier == 1)
369 mseconds = (wd_margin + 1) * 500;
370 else if (wd_multiplier == 2)
371 mseconds = (wd_margin + 1) * 5000;
372 else if (wd_multiplier == 3)
373 mseconds = (wd_margin + 1) * 50000;
374 else if (wd_multiplier == 4)
375 mseconds = (wd_margin + 1) * 100000;
376
377
378 pr_info("Timeout set at %ld ms\n", mseconds);
379
380 return 0;
381
382out_nomisc:
383 unregister_reboot_notifier(&sbc8360_notifier);
384out_noreboot:
385 release_region(SBC8360_BASETIME, 1);
386out_nobasetimereg:
387 release_region(SBC8360_ENABLE, 1);
388out:
389 return res;
390}
391
392static void __exit sbc8360_exit(void)
393{
394 misc_deregister(&sbc8360_miscdev);
395 unregister_reboot_notifier(&sbc8360_notifier);
396 release_region(SBC8360_ENABLE, 1);
397 release_region(SBC8360_BASETIME, 1);
398}
399
400module_init(sbc8360_init);
401module_exit(sbc8360_exit);
402
403MODULE_AUTHOR("Ian E. Morgan <imorgan@webcon.ca>");
404MODULE_DESCRIPTION("SBC8360 watchdog driver");
405MODULE_LICENSE("GPL");
406MODULE_VERSION("1.01");
407
408
409