1b7e04f8cSWim Van Sebroeck /* 2b7e04f8cSWim Van Sebroeck * W83877F Computer Watchdog Timer driver 3b7e04f8cSWim Van Sebroeck * 4b7e04f8cSWim Van Sebroeck * Based on acquirewdt.c by Alan Cox, 5b7e04f8cSWim Van Sebroeck * and sbc60xxwdt.c by Jakob Oestergaard <jakob@unthought.net> 6b7e04f8cSWim Van Sebroeck * 7b7e04f8cSWim Van Sebroeck * This program is free software; you can redistribute it and/or 8b7e04f8cSWim Van Sebroeck * modify it under the terms of the GNU General Public License 9b7e04f8cSWim Van Sebroeck * as published by the Free Software Foundation; either version 10b7e04f8cSWim Van Sebroeck * 2 of the License, or (at your option) any later version. 11b7e04f8cSWim Van Sebroeck * 12b7e04f8cSWim Van Sebroeck * The authors do NOT admit liability nor provide warranty for 13b7e04f8cSWim Van Sebroeck * any of this software. This material is provided "AS-IS" in 14b7e04f8cSWim Van Sebroeck * the hope that it may be useful for others. 15b7e04f8cSWim Van Sebroeck * 16b7e04f8cSWim Van Sebroeck * (c) Copyright 2001 Scott Jennings <linuxdrivers@oro.net> 17b7e04f8cSWim Van Sebroeck * 18b7e04f8cSWim Van Sebroeck * 4/19 - 2001 [Initial revision] 19b7e04f8cSWim Van Sebroeck * 9/27 - 2001 Added spinlocking 20b7e04f8cSWim Van Sebroeck * 4/12 - 2002 [rob@osinvestor.com] Eliminate extra comments 21b7e04f8cSWim Van Sebroeck * Eliminate fop_read 22b7e04f8cSWim Van Sebroeck * Eliminate extra spin_unlock 23b7e04f8cSWim Van Sebroeck * Added KERN_* tags to printks 24b7e04f8cSWim Van Sebroeck * add CONFIG_WATCHDOG_NOWAYOUT support 25b7e04f8cSWim Van Sebroeck * fix possible wdt_is_open race 26c1cfd1a2SAlan Cox * changed watchdog_info to correctly reflect what 27c1cfd1a2SAlan Cox * the driver offers 28c1cfd1a2SAlan Cox * added WDIOC_GETSTATUS, WDIOC_GETBOOTSTATUS, 29c1cfd1a2SAlan Cox * WDIOC_SETTIMEOUT, 30b7e04f8cSWim Van Sebroeck * WDIOC_GETTIMEOUT, and WDIOC_SETOPTIONS ioctls 31b7e04f8cSWim Van Sebroeck * 09/8 - 2003 [wim@iguana.be] cleanup of trailing spaces 32b7e04f8cSWim Van Sebroeck * added extra printk's for startup problems 33b7e04f8cSWim Van Sebroeck * use module_param 34c1cfd1a2SAlan Cox * made timeout (the emulated heartbeat) a 35c1cfd1a2SAlan Cox * module_param 36b7e04f8cSWim Van Sebroeck * made the keepalive ping an internal subroutine 37b7e04f8cSWim Van Sebroeck * 38b7e04f8cSWim Van Sebroeck * This WDT driver is different from most other Linux WDT 39b7e04f8cSWim Van Sebroeck * drivers in that the driver will ping the watchdog by itself, 40b7e04f8cSWim Van Sebroeck * because this particular WDT has a very short timeout (1.6 41b7e04f8cSWim Van Sebroeck * seconds) and it would be insane to count on any userspace 42b7e04f8cSWim Van Sebroeck * daemon always getting scheduled within that time frame. 43b7e04f8cSWim Van Sebroeck */ 44b7e04f8cSWim Van Sebroeck 4527c766aaSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 4627c766aaSJoe Perches 47b7e04f8cSWim Van Sebroeck #include <linux/module.h> 48b7e04f8cSWim Van Sebroeck #include <linux/moduleparam.h> 49b7e04f8cSWim Van Sebroeck #include <linux/types.h> 50b7e04f8cSWim Van Sebroeck #include <linux/timer.h> 51b7e04f8cSWim Van Sebroeck #include <linux/jiffies.h> 52b7e04f8cSWim Van Sebroeck #include <linux/miscdevice.h> 53b7e04f8cSWim Van Sebroeck #include <linux/watchdog.h> 54b7e04f8cSWim Van Sebroeck #include <linux/fs.h> 55b7e04f8cSWim Van Sebroeck #include <linux/ioport.h> 56b7e04f8cSWim Van Sebroeck #include <linux/notifier.h> 57b7e04f8cSWim Van Sebroeck #include <linux/reboot.h> 58b7e04f8cSWim Van Sebroeck #include <linux/init.h> 59c1cfd1a2SAlan Cox #include <linux/io.h> 60c1cfd1a2SAlan Cox #include <linux/uaccess.h> 61b7e04f8cSWim Van Sebroeck #include <asm/system.h> 62b7e04f8cSWim Van Sebroeck 63b7e04f8cSWim Van Sebroeck #define OUR_NAME "w83877f_wdt" 64b7e04f8cSWim Van Sebroeck 65b7e04f8cSWim Van Sebroeck #define ENABLE_W83877F_PORT 0x3F0 66b7e04f8cSWim Van Sebroeck #define ENABLE_W83877F 0x87 67b7e04f8cSWim Van Sebroeck #define DISABLE_W83877F 0xAA 68b7e04f8cSWim Van Sebroeck #define WDT_PING 0x443 69b7e04f8cSWim Van Sebroeck #define WDT_REGISTER 0x14 70b7e04f8cSWim Van Sebroeck #define WDT_ENABLE 0x9C 71b7e04f8cSWim Van Sebroeck #define WDT_DISABLE 0x8C 72b7e04f8cSWim Van Sebroeck 73b7e04f8cSWim Van Sebroeck /* 74b7e04f8cSWim Van Sebroeck * The W83877F seems to be fixed at 1.6s timeout (at least on the 75b7e04f8cSWim Van Sebroeck * EMACS PC-104 board I'm using). If we reset the watchdog every 76b7e04f8cSWim Van Sebroeck * ~250ms we should be safe. */ 77b7e04f8cSWim Van Sebroeck 78b7e04f8cSWim Van Sebroeck #define WDT_INTERVAL (HZ/4+1) 79b7e04f8cSWim Van Sebroeck 80b7e04f8cSWim Van Sebroeck /* 81b7e04f8cSWim Van Sebroeck * We must not require too good response from the userspace daemon. 82b7e04f8cSWim Van Sebroeck * Here we require the userspace daemon to send us a heartbeat 83b7e04f8cSWim Van Sebroeck * char to /dev/watchdog every 30 seconds. 84b7e04f8cSWim Van Sebroeck */ 85b7e04f8cSWim Van Sebroeck 86b7e04f8cSWim Van Sebroeck #define WATCHDOG_TIMEOUT 30 /* 30 sec default timeout */ 87c1cfd1a2SAlan Cox /* in seconds, will be multiplied by HZ to get seconds to wait for a ping */ 88c1cfd1a2SAlan Cox static int timeout = WATCHDOG_TIMEOUT; 89b7e04f8cSWim Van Sebroeck module_param(timeout, int, 0); 90c1cfd1a2SAlan Cox MODULE_PARM_DESC(timeout, 91c1cfd1a2SAlan Cox "Watchdog timeout in seconds. (1<=timeout<=3600, default=" 92c1cfd1a2SAlan Cox __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); 93b7e04f8cSWim Van Sebroeck 94b7e04f8cSWim Van Sebroeck 95*86a1e189SWim Van Sebroeck static bool nowayout = WATCHDOG_NOWAYOUT; 96*86a1e189SWim Van Sebroeck module_param(nowayout, bool, 0); 97c1cfd1a2SAlan Cox MODULE_PARM_DESC(nowayout, 98c1cfd1a2SAlan Cox "Watchdog cannot be stopped once started (default=" 99c1cfd1a2SAlan Cox __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 100b7e04f8cSWim Van Sebroeck 101b7e04f8cSWim Van Sebroeck static void wdt_timer_ping(unsigned long); 102b7e04f8cSWim Van Sebroeck static DEFINE_TIMER(timer, wdt_timer_ping, 0, 0); 103b7e04f8cSWim Van Sebroeck static unsigned long next_heartbeat; 104b7e04f8cSWim Van Sebroeck static unsigned long wdt_is_open; 105b7e04f8cSWim Van Sebroeck static char wdt_expect_close; 106c7dfd0ccSAlexey Dobriyan static DEFINE_SPINLOCK(wdt_spinlock); 107b7e04f8cSWim Van Sebroeck 108b7e04f8cSWim Van Sebroeck /* 109b7e04f8cSWim Van Sebroeck * Whack the dog 110b7e04f8cSWim Van Sebroeck */ 111b7e04f8cSWim Van Sebroeck 112b7e04f8cSWim Van Sebroeck static void wdt_timer_ping(unsigned long data) 113b7e04f8cSWim Van Sebroeck { 114b7e04f8cSWim Van Sebroeck /* If we got a heartbeat pulse within the WDT_US_INTERVAL 115b7e04f8cSWim Van Sebroeck * we agree to ping the WDT 116b7e04f8cSWim Van Sebroeck */ 117c1cfd1a2SAlan Cox if (time_before(jiffies, next_heartbeat)) { 118b7e04f8cSWim Van Sebroeck /* Ping the WDT */ 119b7e04f8cSWim Van Sebroeck spin_lock(&wdt_spinlock); 120b7e04f8cSWim Van Sebroeck 121b7e04f8cSWim Van Sebroeck /* Ping the WDT by reading from WDT_PING */ 122b7e04f8cSWim Van Sebroeck inb_p(WDT_PING); 123b7e04f8cSWim Van Sebroeck 124b7e04f8cSWim Van Sebroeck /* Re-set the timer interval */ 125b7e04f8cSWim Van Sebroeck mod_timer(&timer, jiffies + WDT_INTERVAL); 126b7e04f8cSWim Van Sebroeck 127b7e04f8cSWim Van Sebroeck spin_unlock(&wdt_spinlock); 128b7e04f8cSWim Van Sebroeck 129c1cfd1a2SAlan Cox } else 13027c766aaSJoe Perches pr_warn("Heartbeat lost! Will not ping the watchdog\n"); 131b7e04f8cSWim Van Sebroeck } 132b7e04f8cSWim Van Sebroeck 133b7e04f8cSWim Van Sebroeck /* 134b7e04f8cSWim Van Sebroeck * Utility routines 135b7e04f8cSWim Van Sebroeck */ 136b7e04f8cSWim Van Sebroeck 137b7e04f8cSWim Van Sebroeck static void wdt_change(int writeval) 138b7e04f8cSWim Van Sebroeck { 139b7e04f8cSWim Van Sebroeck unsigned long flags; 140b7e04f8cSWim Van Sebroeck spin_lock_irqsave(&wdt_spinlock, flags); 141b7e04f8cSWim Van Sebroeck 142b7e04f8cSWim Van Sebroeck /* buy some time */ 143b7e04f8cSWim Van Sebroeck inb_p(WDT_PING); 144b7e04f8cSWim Van Sebroeck 145b7e04f8cSWim Van Sebroeck /* make W83877F available */ 146b7e04f8cSWim Van Sebroeck outb_p(ENABLE_W83877F, ENABLE_W83877F_PORT); 147b7e04f8cSWim Van Sebroeck outb_p(ENABLE_W83877F, ENABLE_W83877F_PORT); 148b7e04f8cSWim Van Sebroeck 149b7e04f8cSWim Van Sebroeck /* enable watchdog */ 150b7e04f8cSWim Van Sebroeck outb_p(WDT_REGISTER, ENABLE_W83877F_PORT); 151b7e04f8cSWim Van Sebroeck outb_p(writeval, ENABLE_W83877F_PORT+1); 152b7e04f8cSWim Van Sebroeck 153b7e04f8cSWim Van Sebroeck /* lock the W8387FF away */ 154b7e04f8cSWim Van Sebroeck outb_p(DISABLE_W83877F, ENABLE_W83877F_PORT); 155b7e04f8cSWim Van Sebroeck 156b7e04f8cSWim Van Sebroeck spin_unlock_irqrestore(&wdt_spinlock, flags); 157b7e04f8cSWim Van Sebroeck } 158b7e04f8cSWim Van Sebroeck 159b7e04f8cSWim Van Sebroeck static void wdt_startup(void) 160b7e04f8cSWim Van Sebroeck { 161b7e04f8cSWim Van Sebroeck next_heartbeat = jiffies + (timeout * HZ); 162b7e04f8cSWim Van Sebroeck 163b7e04f8cSWim Van Sebroeck /* Start the timer */ 164b7e04f8cSWim Van Sebroeck mod_timer(&timer, jiffies + WDT_INTERVAL); 165b7e04f8cSWim Van Sebroeck 166b7e04f8cSWim Van Sebroeck wdt_change(WDT_ENABLE); 167b7e04f8cSWim Van Sebroeck 16827c766aaSJoe Perches pr_info("Watchdog timer is now enabled\n"); 169b7e04f8cSWim Van Sebroeck } 170b7e04f8cSWim Van Sebroeck 171b7e04f8cSWim Van Sebroeck static void wdt_turnoff(void) 172b7e04f8cSWim Van Sebroeck { 173b7e04f8cSWim Van Sebroeck /* Stop the timer */ 174b7e04f8cSWim Van Sebroeck del_timer(&timer); 175b7e04f8cSWim Van Sebroeck 176b7e04f8cSWim Van Sebroeck wdt_change(WDT_DISABLE); 177b7e04f8cSWim Van Sebroeck 17827c766aaSJoe Perches pr_info("Watchdog timer is now disabled...\n"); 179b7e04f8cSWim Van Sebroeck } 180b7e04f8cSWim Van Sebroeck 181b7e04f8cSWim Van Sebroeck static void wdt_keepalive(void) 182b7e04f8cSWim Van Sebroeck { 183b7e04f8cSWim Van Sebroeck /* user land ping */ 184b7e04f8cSWim Van Sebroeck next_heartbeat = jiffies + (timeout * HZ); 185b7e04f8cSWim Van Sebroeck } 186b7e04f8cSWim Van Sebroeck 187b7e04f8cSWim Van Sebroeck /* 188b7e04f8cSWim Van Sebroeck * /dev/watchdog handling 189b7e04f8cSWim Van Sebroeck */ 190b7e04f8cSWim Van Sebroeck 191c1cfd1a2SAlan Cox static ssize_t fop_write(struct file *file, const char __user *buf, 192c1cfd1a2SAlan Cox size_t count, loff_t *ppos) 193b7e04f8cSWim Van Sebroeck { 194b7e04f8cSWim Van Sebroeck /* See if we got the magic character 'V' and reload the timer */ 195c1cfd1a2SAlan Cox if (count) { 196c1cfd1a2SAlan Cox if (!nowayout) { 197b7e04f8cSWim Van Sebroeck size_t ofs; 198b7e04f8cSWim Van Sebroeck 199c1cfd1a2SAlan Cox /* note: just in case someone wrote the magic 200c1cfd1a2SAlan Cox character five months ago... */ 201b7e04f8cSWim Van Sebroeck wdt_expect_close = 0; 202b7e04f8cSWim Van Sebroeck 203c1cfd1a2SAlan Cox /* scan to see whether or not we got the 204c1cfd1a2SAlan Cox magic character */ 205c1cfd1a2SAlan Cox for (ofs = 0; ofs != count; ofs++) { 206b7e04f8cSWim Van Sebroeck char c; 207b7e04f8cSWim Van Sebroeck if (get_user(c, buf + ofs)) 208b7e04f8cSWim Van Sebroeck return -EFAULT; 209b7e04f8cSWim Van Sebroeck if (c == 'V') 210b7e04f8cSWim Van Sebroeck wdt_expect_close = 42; 211b7e04f8cSWim Van Sebroeck } 212b7e04f8cSWim Van Sebroeck } 213b7e04f8cSWim Van Sebroeck 214b7e04f8cSWim Van Sebroeck /* someone wrote to us, we should restart timer */ 215b7e04f8cSWim Van Sebroeck wdt_keepalive(); 216b7e04f8cSWim Van Sebroeck } 217b7e04f8cSWim Van Sebroeck return count; 218b7e04f8cSWim Van Sebroeck } 219b7e04f8cSWim Van Sebroeck 220b7e04f8cSWim Van Sebroeck static int fop_open(struct inode *inode, struct file *file) 221b7e04f8cSWim Van Sebroeck { 222b7e04f8cSWim Van Sebroeck /* Just in case we're already talking to someone... */ 223b7e04f8cSWim Van Sebroeck if (test_and_set_bit(0, &wdt_is_open)) 224b7e04f8cSWim Van Sebroeck return -EBUSY; 225b7e04f8cSWim Van Sebroeck 226b7e04f8cSWim Van Sebroeck /* Good, fire up the show */ 227b7e04f8cSWim Van Sebroeck wdt_startup(); 228b7e04f8cSWim Van Sebroeck return nonseekable_open(inode, file); 229b7e04f8cSWim Van Sebroeck } 230b7e04f8cSWim Van Sebroeck 231b7e04f8cSWim Van Sebroeck static int fop_close(struct inode *inode, struct file *file) 232b7e04f8cSWim Van Sebroeck { 233b7e04f8cSWim Van Sebroeck if (wdt_expect_close == 42) 234b7e04f8cSWim Van Sebroeck wdt_turnoff(); 235b7e04f8cSWim Van Sebroeck else { 236b7e04f8cSWim Van Sebroeck del_timer(&timer); 23727c766aaSJoe Perches pr_crit("device file closed unexpectedly. Will not stop the WDT!\n"); 238b7e04f8cSWim Van Sebroeck } 239b7e04f8cSWim Van Sebroeck clear_bit(0, &wdt_is_open); 240b7e04f8cSWim Van Sebroeck wdt_expect_close = 0; 241b7e04f8cSWim Van Sebroeck return 0; 242b7e04f8cSWim Van Sebroeck } 243b7e04f8cSWim Van Sebroeck 244c1cfd1a2SAlan Cox static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 245b7e04f8cSWim Van Sebroeck { 246b7e04f8cSWim Van Sebroeck void __user *argp = (void __user *)arg; 247b7e04f8cSWim Van Sebroeck int __user *p = argp; 248c1cfd1a2SAlan Cox static const struct watchdog_info ident = { 249c1cfd1a2SAlan Cox .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT 250c1cfd1a2SAlan Cox | WDIOF_MAGICCLOSE, 251b7e04f8cSWim Van Sebroeck .firmware_version = 1, 252b7e04f8cSWim Van Sebroeck .identity = "W83877F", 253b7e04f8cSWim Van Sebroeck }; 254b7e04f8cSWim Van Sebroeck 255c1cfd1a2SAlan Cox switch (cmd) { 256b7e04f8cSWim Van Sebroeck case WDIOC_GETSUPPORT: 257b7e04f8cSWim Van Sebroeck return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; 258b7e04f8cSWim Van Sebroeck case WDIOC_GETSTATUS: 259b7e04f8cSWim Van Sebroeck case WDIOC_GETBOOTSTATUS: 260b7e04f8cSWim Van Sebroeck return put_user(0, p); 261b7e04f8cSWim Van Sebroeck case WDIOC_SETOPTIONS: 262b7e04f8cSWim Van Sebroeck { 263b7e04f8cSWim Van Sebroeck int new_options, retval = -EINVAL; 264b7e04f8cSWim Van Sebroeck 265b7e04f8cSWim Van Sebroeck if (get_user(new_options, p)) 266b7e04f8cSWim Van Sebroeck return -EFAULT; 267b7e04f8cSWim Van Sebroeck 268b7e04f8cSWim Van Sebroeck if (new_options & WDIOS_DISABLECARD) { 269b7e04f8cSWim Van Sebroeck wdt_turnoff(); 270b7e04f8cSWim Van Sebroeck retval = 0; 271b7e04f8cSWim Van Sebroeck } 272b7e04f8cSWim Van Sebroeck 273b7e04f8cSWim Van Sebroeck if (new_options & WDIOS_ENABLECARD) { 274b7e04f8cSWim Van Sebroeck wdt_startup(); 275b7e04f8cSWim Van Sebroeck retval = 0; 276b7e04f8cSWim Van Sebroeck } 277b7e04f8cSWim Van Sebroeck 278b7e04f8cSWim Van Sebroeck return retval; 279b7e04f8cSWim Van Sebroeck } 2800c06090cSWim Van Sebroeck case WDIOC_KEEPALIVE: 2810c06090cSWim Van Sebroeck wdt_keepalive(); 2820c06090cSWim Van Sebroeck return 0; 283b7e04f8cSWim Van Sebroeck case WDIOC_SETTIMEOUT: 284b7e04f8cSWim Van Sebroeck { 285b7e04f8cSWim Van Sebroeck int new_timeout; 286b7e04f8cSWim Van Sebroeck 287b7e04f8cSWim Van Sebroeck if (get_user(new_timeout, p)) 288b7e04f8cSWim Van Sebroeck return -EFAULT; 289b7e04f8cSWim Van Sebroeck 290c1cfd1a2SAlan Cox /* arbitrary upper limit */ 291c1cfd1a2SAlan Cox if (new_timeout < 1 || new_timeout > 3600) 292b7e04f8cSWim Van Sebroeck return -EINVAL; 293b7e04f8cSWim Van Sebroeck 294b7e04f8cSWim Van Sebroeck timeout = new_timeout; 295b7e04f8cSWim Van Sebroeck wdt_keepalive(); 296b7e04f8cSWim Van Sebroeck /* Fall through */ 297b7e04f8cSWim Van Sebroeck } 298b7e04f8cSWim Van Sebroeck case WDIOC_GETTIMEOUT: 299b7e04f8cSWim Van Sebroeck return put_user(timeout, p); 3000c06090cSWim Van Sebroeck default: 3010c06090cSWim Van Sebroeck return -ENOTTY; 302b7e04f8cSWim Van Sebroeck } 303b7e04f8cSWim Van Sebroeck } 304b7e04f8cSWim Van Sebroeck 305b7e04f8cSWim Van Sebroeck static const struct file_operations wdt_fops = { 306b7e04f8cSWim Van Sebroeck .owner = THIS_MODULE, 307b7e04f8cSWim Van Sebroeck .llseek = no_llseek, 308b7e04f8cSWim Van Sebroeck .write = fop_write, 309b7e04f8cSWim Van Sebroeck .open = fop_open, 310b7e04f8cSWim Van Sebroeck .release = fop_close, 311c1cfd1a2SAlan Cox .unlocked_ioctl = fop_ioctl, 312b7e04f8cSWim Van Sebroeck }; 313b7e04f8cSWim Van Sebroeck 314b7e04f8cSWim Van Sebroeck static struct miscdevice wdt_miscdev = { 315b7e04f8cSWim Van Sebroeck .minor = WATCHDOG_MINOR, 316b7e04f8cSWim Van Sebroeck .name = "watchdog", 317b7e04f8cSWim Van Sebroeck .fops = &wdt_fops, 318b7e04f8cSWim Van Sebroeck }; 319b7e04f8cSWim Van Sebroeck 320b7e04f8cSWim Van Sebroeck /* 321b7e04f8cSWim Van Sebroeck * Notifier for system down 322b7e04f8cSWim Van Sebroeck */ 323b7e04f8cSWim Van Sebroeck 324b7e04f8cSWim Van Sebroeck static int wdt_notify_sys(struct notifier_block *this, unsigned long code, 325b7e04f8cSWim Van Sebroeck void *unused) 326b7e04f8cSWim Van Sebroeck { 327b7e04f8cSWim Van Sebroeck if (code == SYS_DOWN || code == SYS_HALT) 328b7e04f8cSWim Van Sebroeck wdt_turnoff(); 329b7e04f8cSWim Van Sebroeck return NOTIFY_DONE; 330b7e04f8cSWim Van Sebroeck } 331b7e04f8cSWim Van Sebroeck 332b7e04f8cSWim Van Sebroeck /* 333b7e04f8cSWim Van Sebroeck * The WDT needs to learn about soft shutdowns in order to 334b7e04f8cSWim Van Sebroeck * turn the timebomb registers off. 335b7e04f8cSWim Van Sebroeck */ 336b7e04f8cSWim Van Sebroeck 337c1cfd1a2SAlan Cox static struct notifier_block wdt_notifier = { 338b7e04f8cSWim Van Sebroeck .notifier_call = wdt_notify_sys, 339b7e04f8cSWim Van Sebroeck }; 340b7e04f8cSWim Van Sebroeck 341b7e04f8cSWim Van Sebroeck static void __exit w83877f_wdt_unload(void) 342b7e04f8cSWim Van Sebroeck { 343b7e04f8cSWim Van Sebroeck wdt_turnoff(); 344b7e04f8cSWim Van Sebroeck 345b7e04f8cSWim Van Sebroeck /* Deregister */ 346b7e04f8cSWim Van Sebroeck misc_deregister(&wdt_miscdev); 347b7e04f8cSWim Van Sebroeck 348b7e04f8cSWim Van Sebroeck unregister_reboot_notifier(&wdt_notifier); 349b7e04f8cSWim Van Sebroeck release_region(WDT_PING, 1); 350b7e04f8cSWim Van Sebroeck release_region(ENABLE_W83877F_PORT, 2); 351b7e04f8cSWim Van Sebroeck } 352b7e04f8cSWim Van Sebroeck 353b7e04f8cSWim Van Sebroeck static int __init w83877f_wdt_init(void) 354b7e04f8cSWim Van Sebroeck { 355b7e04f8cSWim Van Sebroeck int rc = -EBUSY; 356b7e04f8cSWim Van Sebroeck 357c1cfd1a2SAlan Cox if (timeout < 1 || timeout > 3600) { /* arbitrary upper limit */ 358b7e04f8cSWim Van Sebroeck timeout = WATCHDOG_TIMEOUT; 35927c766aaSJoe Perches pr_info("timeout value must be 1 <= x <= 3600, using %d\n", 360b7e04f8cSWim Van Sebroeck timeout); 361b7e04f8cSWim Van Sebroeck } 362b7e04f8cSWim Van Sebroeck 363c1cfd1a2SAlan Cox if (!request_region(ENABLE_W83877F_PORT, 2, "W83877F WDT")) { 36427c766aaSJoe Perches pr_err("I/O address 0x%04x already in use\n", 365b7e04f8cSWim Van Sebroeck ENABLE_W83877F_PORT); 366b7e04f8cSWim Van Sebroeck rc = -EIO; 367b7e04f8cSWim Van Sebroeck goto err_out; 368b7e04f8cSWim Van Sebroeck } 369b7e04f8cSWim Van Sebroeck 370c1cfd1a2SAlan Cox if (!request_region(WDT_PING, 1, "W8387FF WDT")) { 37127c766aaSJoe Perches pr_err("I/O address 0x%04x already in use\n", WDT_PING); 372b7e04f8cSWim Van Sebroeck rc = -EIO; 373b7e04f8cSWim Van Sebroeck goto err_out_region1; 374b7e04f8cSWim Van Sebroeck } 375b7e04f8cSWim Van Sebroeck 376b7e04f8cSWim Van Sebroeck rc = register_reboot_notifier(&wdt_notifier); 377c1cfd1a2SAlan Cox if (rc) { 37827c766aaSJoe Perches pr_err("cannot register reboot notifier (err=%d)\n", rc); 379c6cb13aeSWim Van Sebroeck goto err_out_region2; 380c6cb13aeSWim Van Sebroeck } 381c6cb13aeSWim Van Sebroeck 382c6cb13aeSWim Van Sebroeck rc = misc_register(&wdt_miscdev); 383c1cfd1a2SAlan Cox if (rc) { 38427c766aaSJoe Perches pr_err("cannot register miscdev on minor=%d (err=%d)\n", 385c6cb13aeSWim Van Sebroeck wdt_miscdev.minor, rc); 386c6cb13aeSWim Van Sebroeck goto err_out_reboot; 387b7e04f8cSWim Van Sebroeck } 388b7e04f8cSWim Van Sebroeck 38927c766aaSJoe Perches pr_info("WDT driver for W83877F initialised. timeout=%d sec (nowayout=%d)\n", 390b7e04f8cSWim Van Sebroeck timeout, nowayout); 391b7e04f8cSWim Van Sebroeck 392b7e04f8cSWim Van Sebroeck return 0; 393b7e04f8cSWim Van Sebroeck 394c6cb13aeSWim Van Sebroeck err_out_reboot: 395c6cb13aeSWim Van Sebroeck unregister_reboot_notifier(&wdt_notifier); 396b7e04f8cSWim Van Sebroeck err_out_region2: 397b7e04f8cSWim Van Sebroeck release_region(WDT_PING, 1); 398b7e04f8cSWim Van Sebroeck err_out_region1: 399b7e04f8cSWim Van Sebroeck release_region(ENABLE_W83877F_PORT, 2); 400b7e04f8cSWim Van Sebroeck err_out: 401b7e04f8cSWim Van Sebroeck return rc; 402b7e04f8cSWim Van Sebroeck } 403b7e04f8cSWim Van Sebroeck 404b7e04f8cSWim Van Sebroeck module_init(w83877f_wdt_init); 405b7e04f8cSWim Van Sebroeck module_exit(w83877f_wdt_unload); 406b7e04f8cSWim Van Sebroeck 407b7e04f8cSWim Van Sebroeck MODULE_AUTHOR("Scott and Bill Jennings"); 408b7e04f8cSWim Van Sebroeck MODULE_DESCRIPTION("Driver for watchdog timer in w83877f chip"); 409b7e04f8cSWim Van Sebroeck MODULE_LICENSE("GPL"); 410b7e04f8cSWim Van Sebroeck MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 411