1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Wdt977 0.04: A Watchdog Device for Netwinder W83977AF chip |
4 | * |
5 | * (c) Copyright 1998 Rebel.com (Woody Suwalski <woody@netwinder.org>) |
6 | * |
7 | * ----------------------- |
8 | * |
9 | * ----------------------- |
10 | * 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com> |
11 | * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT |
12 | * 19-Dec-2001 Woody Suwalski: Netwinder fixes, ioctl interface |
13 | * 06-Jan-2002 Woody Suwalski: For compatibility, convert all timeouts |
14 | * from minutes to seconds. |
15 | * 07-Jul-2003 Daniele Bellucci: Audit return code of misc_register in |
16 | * nwwatchdog_init. |
17 | * 25-Oct-2005 Woody Suwalski: Convert addresses to #defs, add spinlocks |
18 | * remove limitiation to be used on |
19 | * Netwinders only |
20 | */ |
21 | |
22 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
23 | |
24 | #include <linux/module.h> |
25 | #include <linux/moduleparam.h> |
26 | #include <linux/types.h> |
27 | #include <linux/kernel.h> |
28 | #include <linux/fs.h> |
29 | #include <linux/miscdevice.h> |
30 | #include <linux/init.h> |
31 | #include <linux/ioport.h> |
32 | #include <linux/watchdog.h> |
33 | #include <linux/notifier.h> |
34 | #include <linux/reboot.h> |
35 | #include <linux/io.h> |
36 | #include <linux/uaccess.h> |
37 | |
38 | #include <asm/mach-types.h> |
39 | |
40 | #define WATCHDOG_VERSION "0.04" |
41 | #define WATCHDOG_NAME "Wdt977" |
42 | |
43 | #define IO_INDEX_PORT 0x370 /* on some systems it can be 0x3F0 */ |
44 | #define IO_DATA_PORT (IO_INDEX_PORT + 1) |
45 | |
46 | #define UNLOCK_DATA 0x87 |
47 | #define LOCK_DATA 0xAA |
48 | #define DEVICE_REGISTER 0x07 |
49 | |
50 | |
51 | #define DEFAULT_TIMEOUT 60 /* default timeout in seconds */ |
52 | |
53 | static int timeout = DEFAULT_TIMEOUT; |
54 | static int timeoutM; /* timeout in minutes */ |
55 | static unsigned long timer_alive; |
56 | static int testmode; |
57 | static char expect_close; |
58 | static DEFINE_SPINLOCK(spinlock); |
59 | |
60 | module_param(timeout, int, 0); |
61 | MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (60..15300, default=" |
62 | __MODULE_STRING(DEFAULT_TIMEOUT) ")" ); |
63 | module_param(testmode, int, 0); |
64 | MODULE_PARM_DESC(testmode, "Watchdog testmode (1 = no reboot), default=0" ); |
65 | |
66 | static bool nowayout = WATCHDOG_NOWAYOUT; |
67 | module_param(nowayout, bool, 0); |
68 | MODULE_PARM_DESC(nowayout, |
69 | "Watchdog cannot be stopped once started (default=" |
70 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")" ); |
71 | |
72 | /* |
73 | * Start the watchdog |
74 | */ |
75 | |
76 | static int wdt977_start(void) |
77 | { |
78 | unsigned long flags; |
79 | |
80 | spin_lock_irqsave(&spinlock, flags); |
81 | |
82 | /* unlock the SuperIO chip */ |
83 | outb_p(UNLOCK_DATA, IO_INDEX_PORT); |
84 | outb_p(UNLOCK_DATA, IO_INDEX_PORT); |
85 | |
86 | /* select device Aux2 (device=8) and set watchdog regs F2, F3 and F4 |
87 | * F2 has the timeout in minutes |
88 | * F3 could be set to the POWER LED blink (with GP17 set to PowerLed) |
89 | * at timeout, and to reset timer on kbd/mouse activity (not impl.) |
90 | * F4 is used to just clear the TIMEOUT'ed state (bit 0) |
91 | */ |
92 | outb_p(DEVICE_REGISTER, IO_INDEX_PORT); |
93 | outb_p(value: 0x08, IO_DATA_PORT); |
94 | outb_p(value: 0xF2, IO_INDEX_PORT); |
95 | outb_p(value: timeoutM, IO_DATA_PORT); |
96 | outb_p(value: 0xF3, IO_INDEX_PORT); |
97 | outb_p(value: 0x00, IO_DATA_PORT); /* another setting is 0E for |
98 | kbd/mouse/LED */ |
99 | outb_p(value: 0xF4, IO_INDEX_PORT); |
100 | outb_p(value: 0x00, IO_DATA_PORT); |
101 | |
102 | /* At last select device Aux1 (dev=7) and set GP16 as a |
103 | * watchdog output. In test mode watch the bit 1 on F4 to |
104 | * indicate "triggered" |
105 | */ |
106 | if (!testmode) { |
107 | outb_p(DEVICE_REGISTER, IO_INDEX_PORT); |
108 | outb_p(value: 0x07, IO_DATA_PORT); |
109 | outb_p(value: 0xE6, IO_INDEX_PORT); |
110 | outb_p(value: 0x08, IO_DATA_PORT); |
111 | } |
112 | |
113 | /* lock the SuperIO chip */ |
114 | outb_p(LOCK_DATA, IO_INDEX_PORT); |
115 | |
116 | spin_unlock_irqrestore(lock: &spinlock, flags); |
117 | pr_info("activated\n" ); |
118 | |
119 | return 0; |
120 | } |
121 | |
122 | /* |
123 | * Stop the watchdog |
124 | */ |
125 | |
126 | static int wdt977_stop(void) |
127 | { |
128 | unsigned long flags; |
129 | spin_lock_irqsave(&spinlock, flags); |
130 | |
131 | /* unlock the SuperIO chip */ |
132 | outb_p(UNLOCK_DATA, IO_INDEX_PORT); |
133 | outb_p(UNLOCK_DATA, IO_INDEX_PORT); |
134 | |
135 | /* select device Aux2 (device=8) and set watchdog regs F2,F3 and F4 |
136 | * F3 is reset to its default state |
137 | * F4 can clear the TIMEOUT'ed state (bit 0) - back to default |
138 | * We can not use GP17 as a PowerLed, as we use its usage as a RedLed |
139 | */ |
140 | outb_p(DEVICE_REGISTER, IO_INDEX_PORT); |
141 | outb_p(value: 0x08, IO_DATA_PORT); |
142 | outb_p(value: 0xF2, IO_INDEX_PORT); |
143 | outb_p(value: 0xFF, IO_DATA_PORT); |
144 | outb_p(value: 0xF3, IO_INDEX_PORT); |
145 | outb_p(value: 0x00, IO_DATA_PORT); |
146 | outb_p(value: 0xF4, IO_INDEX_PORT); |
147 | outb_p(value: 0x00, IO_DATA_PORT); |
148 | outb_p(value: 0xF2, IO_INDEX_PORT); |
149 | outb_p(value: 0x00, IO_DATA_PORT); |
150 | |
151 | /* at last select device Aux1 (dev=7) and set |
152 | GP16 as a watchdog output */ |
153 | outb_p(DEVICE_REGISTER, IO_INDEX_PORT); |
154 | outb_p(value: 0x07, IO_DATA_PORT); |
155 | outb_p(value: 0xE6, IO_INDEX_PORT); |
156 | outb_p(value: 0x08, IO_DATA_PORT); |
157 | |
158 | /* lock the SuperIO chip */ |
159 | outb_p(LOCK_DATA, IO_INDEX_PORT); |
160 | |
161 | spin_unlock_irqrestore(lock: &spinlock, flags); |
162 | pr_info("shutdown\n" ); |
163 | |
164 | return 0; |
165 | } |
166 | |
167 | /* |
168 | * Send a keepalive ping to the watchdog |
169 | * This is done by simply re-writing the timeout to reg. 0xF2 |
170 | */ |
171 | |
172 | static int wdt977_keepalive(void) |
173 | { |
174 | unsigned long flags; |
175 | spin_lock_irqsave(&spinlock, flags); |
176 | |
177 | /* unlock the SuperIO chip */ |
178 | outb_p(UNLOCK_DATA, IO_INDEX_PORT); |
179 | outb_p(UNLOCK_DATA, IO_INDEX_PORT); |
180 | |
181 | /* select device Aux2 (device=8) and kicks watchdog reg F2 */ |
182 | /* F2 has the timeout in minutes */ |
183 | outb_p(DEVICE_REGISTER, IO_INDEX_PORT); |
184 | outb_p(value: 0x08, IO_DATA_PORT); |
185 | outb_p(value: 0xF2, IO_INDEX_PORT); |
186 | outb_p(value: timeoutM, IO_DATA_PORT); |
187 | |
188 | /* lock the SuperIO chip */ |
189 | outb_p(LOCK_DATA, IO_INDEX_PORT); |
190 | spin_unlock_irqrestore(lock: &spinlock, flags); |
191 | |
192 | return 0; |
193 | } |
194 | |
195 | /* |
196 | * Set the watchdog timeout value |
197 | */ |
198 | |
199 | static int wdt977_set_timeout(int t) |
200 | { |
201 | int tmrval; |
202 | |
203 | /* convert seconds to minutes, rounding up */ |
204 | tmrval = (t + 59) / 60; |
205 | |
206 | if (machine_is_netwinder()) { |
207 | /* we have a hw bug somewhere, so each 977 minute is actually |
208 | * only 30sec. This limits the max timeout to half of device |
209 | * max of 255 minutes... |
210 | */ |
211 | tmrval += tmrval; |
212 | } |
213 | |
214 | if (tmrval < 1 || tmrval > 255) |
215 | return -EINVAL; |
216 | |
217 | /* timeout is the timeout in seconds, timeoutM is |
218 | the timeout in minutes) */ |
219 | timeout = t; |
220 | timeoutM = tmrval; |
221 | return 0; |
222 | } |
223 | |
224 | /* |
225 | * Get the watchdog status |
226 | */ |
227 | |
228 | static int wdt977_get_status(int *status) |
229 | { |
230 | int new_status; |
231 | unsigned long flags; |
232 | |
233 | spin_lock_irqsave(&spinlock, flags); |
234 | |
235 | /* unlock the SuperIO chip */ |
236 | outb_p(UNLOCK_DATA, IO_INDEX_PORT); |
237 | outb_p(UNLOCK_DATA, IO_INDEX_PORT); |
238 | |
239 | /* select device Aux2 (device=8) and read watchdog reg F4 */ |
240 | outb_p(DEVICE_REGISTER, IO_INDEX_PORT); |
241 | outb_p(value: 0x08, IO_DATA_PORT); |
242 | outb_p(value: 0xF4, IO_INDEX_PORT); |
243 | new_status = inb_p(IO_DATA_PORT); |
244 | |
245 | /* lock the SuperIO chip */ |
246 | outb_p(LOCK_DATA, IO_INDEX_PORT); |
247 | |
248 | spin_unlock_irqrestore(lock: &spinlock, flags); |
249 | |
250 | *status = 0; |
251 | if (new_status & 1) |
252 | *status |= WDIOF_CARDRESET; |
253 | |
254 | return 0; |
255 | } |
256 | |
257 | |
258 | /* |
259 | * /dev/watchdog handling |
260 | */ |
261 | |
262 | static int wdt977_open(struct inode *inode, struct file *file) |
263 | { |
264 | /* If the watchdog is alive we don't need to start it again */ |
265 | if (test_and_set_bit(nr: 0, addr: &timer_alive)) |
266 | return -EBUSY; |
267 | |
268 | if (nowayout) |
269 | __module_get(THIS_MODULE); |
270 | |
271 | wdt977_start(); |
272 | return stream_open(inode, filp: file); |
273 | } |
274 | |
275 | static int wdt977_release(struct inode *inode, struct file *file) |
276 | { |
277 | /* |
278 | * Shut off the timer. |
279 | * Lock it in if it's a module and we set nowayout |
280 | */ |
281 | if (expect_close == 42) { |
282 | wdt977_stop(); |
283 | clear_bit(nr: 0, addr: &timer_alive); |
284 | } else { |
285 | wdt977_keepalive(); |
286 | pr_crit("Unexpected close, not stopping watchdog!\n" ); |
287 | } |
288 | expect_close = 0; |
289 | return 0; |
290 | } |
291 | |
292 | |
293 | /* |
294 | * wdt977_write: |
295 | * @file: file handle to the watchdog |
296 | * @buf: buffer to write (unused as data does not matter here |
297 | * @count: count of bytes |
298 | * @ppos: pointer to the position to write. No seeks allowed |
299 | * |
300 | * A write to a watchdog device is defined as a keepalive signal. Any |
301 | * write of data will do, as we we don't define content meaning. |
302 | */ |
303 | |
304 | static ssize_t wdt977_write(struct file *file, const char __user *buf, |
305 | size_t count, loff_t *ppos) |
306 | { |
307 | if (count) { |
308 | if (!nowayout) { |
309 | size_t i; |
310 | |
311 | /* In case it was set long ago */ |
312 | expect_close = 0; |
313 | |
314 | for (i = 0; i != count; i++) { |
315 | char c; |
316 | if (get_user(c, buf + i)) |
317 | return -EFAULT; |
318 | if (c == 'V') |
319 | expect_close = 42; |
320 | } |
321 | } |
322 | |
323 | /* someone wrote to us, we should restart timer */ |
324 | wdt977_keepalive(); |
325 | } |
326 | return count; |
327 | } |
328 | |
329 | static const struct watchdog_info ident = { |
330 | .options = WDIOF_SETTIMEOUT | |
331 | WDIOF_MAGICCLOSE | |
332 | WDIOF_KEEPALIVEPING, |
333 | .firmware_version = 1, |
334 | .identity = WATCHDOG_NAME, |
335 | }; |
336 | |
337 | /* |
338 | * wdt977_ioctl: |
339 | * @inode: inode of the device |
340 | * @file: file handle to the device |
341 | * @cmd: watchdog command |
342 | * @arg: argument pointer |
343 | * |
344 | * The watchdog API defines a common set of functions for all watchdogs |
345 | * according to their available features. |
346 | */ |
347 | |
348 | static long wdt977_ioctl(struct file *file, unsigned int cmd, |
349 | unsigned long arg) |
350 | { |
351 | int status; |
352 | int new_options, retval = -EINVAL; |
353 | int new_timeout; |
354 | union { |
355 | struct watchdog_info __user *ident; |
356 | int __user *i; |
357 | } uarg; |
358 | |
359 | uarg.i = (int __user *)arg; |
360 | |
361 | switch (cmd) { |
362 | case WDIOC_GETSUPPORT: |
363 | return copy_to_user(to: uarg.ident, from: &ident, |
364 | n: sizeof(ident)) ? -EFAULT : 0; |
365 | |
366 | case WDIOC_GETSTATUS: |
367 | wdt977_get_status(status: &status); |
368 | return put_user(status, uarg.i); |
369 | |
370 | case WDIOC_GETBOOTSTATUS: |
371 | return put_user(0, uarg.i); |
372 | |
373 | case WDIOC_SETOPTIONS: |
374 | if (get_user(new_options, uarg.i)) |
375 | return -EFAULT; |
376 | |
377 | if (new_options & WDIOS_DISABLECARD) { |
378 | wdt977_stop(); |
379 | retval = 0; |
380 | } |
381 | |
382 | if (new_options & WDIOS_ENABLECARD) { |
383 | wdt977_start(); |
384 | retval = 0; |
385 | } |
386 | |
387 | return retval; |
388 | |
389 | case WDIOC_KEEPALIVE: |
390 | wdt977_keepalive(); |
391 | return 0; |
392 | |
393 | case WDIOC_SETTIMEOUT: |
394 | if (get_user(new_timeout, uarg.i)) |
395 | return -EFAULT; |
396 | |
397 | if (wdt977_set_timeout(t: new_timeout)) |
398 | return -EINVAL; |
399 | |
400 | wdt977_keepalive(); |
401 | fallthrough; |
402 | |
403 | case WDIOC_GETTIMEOUT: |
404 | return put_user(timeout, uarg.i); |
405 | |
406 | default: |
407 | return -ENOTTY; |
408 | |
409 | } |
410 | } |
411 | |
412 | static int wdt977_notify_sys(struct notifier_block *this, unsigned long code, |
413 | void *unused) |
414 | { |
415 | if (code == SYS_DOWN || code == SYS_HALT) |
416 | wdt977_stop(); |
417 | return NOTIFY_DONE; |
418 | } |
419 | |
420 | static const struct file_operations wdt977_fops = { |
421 | .owner = THIS_MODULE, |
422 | .llseek = no_llseek, |
423 | .write = wdt977_write, |
424 | .unlocked_ioctl = wdt977_ioctl, |
425 | .compat_ioctl = compat_ptr_ioctl, |
426 | .open = wdt977_open, |
427 | .release = wdt977_release, |
428 | }; |
429 | |
430 | static struct miscdevice wdt977_miscdev = { |
431 | .minor = WATCHDOG_MINOR, |
432 | .name = "watchdog" , |
433 | .fops = &wdt977_fops, |
434 | }; |
435 | |
436 | static struct notifier_block wdt977_notifier = { |
437 | .notifier_call = wdt977_notify_sys, |
438 | }; |
439 | |
440 | static int __init wd977_init(void) |
441 | { |
442 | int rc; |
443 | |
444 | pr_info("driver v%s\n" , WATCHDOG_VERSION); |
445 | |
446 | /* Check that the timeout value is within its range; |
447 | if not reset to the default */ |
448 | if (wdt977_set_timeout(t: timeout)) { |
449 | wdt977_set_timeout(DEFAULT_TIMEOUT); |
450 | pr_info("timeout value must be 60 < timeout < 15300, using %d\n" , |
451 | DEFAULT_TIMEOUT); |
452 | } |
453 | |
454 | /* on Netwinder the IOports are already reserved by |
455 | * arch/arm/mach-footbridge/netwinder-hw.c |
456 | */ |
457 | if (!machine_is_netwinder()) { |
458 | if (!request_region(IO_INDEX_PORT, 2, WATCHDOG_NAME)) { |
459 | pr_err("I/O address 0x%04x already in use\n" , |
460 | IO_INDEX_PORT); |
461 | rc = -EIO; |
462 | goto err_out; |
463 | } |
464 | } |
465 | |
466 | rc = register_reboot_notifier(&wdt977_notifier); |
467 | if (rc) { |
468 | pr_err("cannot register reboot notifier (err=%d)\n" , rc); |
469 | goto err_out_region; |
470 | } |
471 | |
472 | rc = misc_register(misc: &wdt977_miscdev); |
473 | if (rc) { |
474 | pr_err("cannot register miscdev on minor=%d (err=%d)\n" , |
475 | wdt977_miscdev.minor, rc); |
476 | goto err_out_reboot; |
477 | } |
478 | |
479 | pr_info("initialized. timeout=%d sec (nowayout=%d, testmode=%i)\n" , |
480 | timeout, nowayout, testmode); |
481 | |
482 | return 0; |
483 | |
484 | err_out_reboot: |
485 | unregister_reboot_notifier(&wdt977_notifier); |
486 | err_out_region: |
487 | if (!machine_is_netwinder()) |
488 | release_region(IO_INDEX_PORT, 2); |
489 | err_out: |
490 | return rc; |
491 | } |
492 | |
493 | static void __exit wd977_exit(void) |
494 | { |
495 | wdt977_stop(); |
496 | misc_deregister(misc: &wdt977_miscdev); |
497 | unregister_reboot_notifier(&wdt977_notifier); |
498 | release_region(IO_INDEX_PORT, 2); |
499 | } |
500 | |
501 | module_init(wd977_init); |
502 | module_exit(wd977_exit); |
503 | |
504 | MODULE_AUTHOR("Woody Suwalski <woodys@xandros.com>" ); |
505 | MODULE_DESCRIPTION("W83977AF Watchdog driver" ); |
506 | MODULE_LICENSE("GPL" ); |
507 | |