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