1 /* 2 * ICP Wafer 5823 Single Board Computer WDT driver 3 * http://www.icpamerica.com/wafer_5823.php 4 * May also work on other similar models 5 * 6 * (c) Copyright 2002 Justin Cormack <justin@street-vision.com> 7 * 8 * Release 0.02 9 * 10 * Based on advantechwdt.c which is based on wdt.c. 11 * Original copyright messages: 12 * 13 * (c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>, 14 * All Rights Reserved. 15 * 16 * This program is free software; you can redistribute it and/or 17 * modify it under the terms of the GNU General Public License 18 * as published by the Free Software Foundation; either version 19 * 2 of the License, or (at your option) any later version. 20 * 21 * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide 22 * warranty for any of this software. This material is provided 23 * "AS-IS" and at no charge. 24 * 25 * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk> 26 * 27 */ 28 29 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 30 31 #include <linux/module.h> 32 #include <linux/moduleparam.h> 33 #include <linux/miscdevice.h> 34 #include <linux/watchdog.h> 35 #include <linux/fs.h> 36 #include <linux/ioport.h> 37 #include <linux/notifier.h> 38 #include <linux/reboot.h> 39 #include <linux/init.h> 40 #include <linux/spinlock.h> 41 #include <linux/io.h> 42 #include <linux/uaccess.h> 43 44 #define WATCHDOG_NAME "Wafer 5823 WDT" 45 #define PFX WATCHDOG_NAME ": " 46 #define WD_TIMO 60 /* 60 sec default timeout */ 47 48 static unsigned long wafwdt_is_open; 49 static char expect_close; 50 static DEFINE_SPINLOCK(wafwdt_lock); 51 52 /* 53 * You must set these - there is no sane way to probe for this board. 54 * 55 * To enable, write the timeout value in seconds (1 to 255) to I/O 56 * port WDT_START, then read the port to start the watchdog. To pat 57 * the dog, read port WDT_STOP to stop the timer, then read WDT_START 58 * to restart it again. 59 */ 60 61 static int wdt_stop = 0x843; 62 static int wdt_start = 0x443; 63 64 static int timeout = WD_TIMO; /* in seconds */ 65 module_param(timeout, int, 0); 66 MODULE_PARM_DESC(timeout, 67 "Watchdog timeout in seconds. 1 <= timeout <= 255, default=" 68 __MODULE_STRING(WD_TIMO) "."); 69 70 static bool nowayout = WATCHDOG_NOWAYOUT; 71 module_param(nowayout, bool, 0); 72 MODULE_PARM_DESC(nowayout, 73 "Watchdog cannot be stopped once started (default=" 74 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 75 76 static void wafwdt_ping(void) 77 { 78 /* pat watchdog */ 79 spin_lock(&wafwdt_lock); 80 inb_p(wdt_stop); 81 inb_p(wdt_start); 82 spin_unlock(&wafwdt_lock); 83 } 84 85 static void wafwdt_start(void) 86 { 87 /* start up watchdog */ 88 outb_p(timeout, wdt_start); 89 inb_p(wdt_start); 90 } 91 92 static void wafwdt_stop(void) 93 { 94 /* stop watchdog */ 95 inb_p(wdt_stop); 96 } 97 98 static ssize_t wafwdt_write(struct file *file, const char __user *buf, 99 size_t count, loff_t *ppos) 100 { 101 /* See if we got the magic character 'V' and reload the timer */ 102 if (count) { 103 if (!nowayout) { 104 size_t i; 105 106 /* In case it was set long ago */ 107 expect_close = 0; 108 109 /* scan to see whether or not we got the magic 110 character */ 111 for (i = 0; i != count; i++) { 112 char c; 113 if (get_user(c, buf + i)) 114 return -EFAULT; 115 if (c == 'V') 116 expect_close = 42; 117 } 118 } 119 /* Well, anyhow someone wrote to us, we should 120 return that favour */ 121 wafwdt_ping(); 122 } 123 return count; 124 } 125 126 static long wafwdt_ioctl(struct file *file, unsigned int cmd, 127 unsigned long arg) 128 { 129 int new_timeout; 130 void __user *argp = (void __user *)arg; 131 int __user *p = argp; 132 static const struct watchdog_info ident = { 133 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | 134 WDIOF_MAGICCLOSE, 135 .firmware_version = 1, 136 .identity = "Wafer 5823 WDT", 137 }; 138 139 switch (cmd) { 140 case WDIOC_GETSUPPORT: 141 if (copy_to_user(argp, &ident, sizeof(ident))) 142 return -EFAULT; 143 break; 144 145 case WDIOC_GETSTATUS: 146 case WDIOC_GETBOOTSTATUS: 147 return put_user(0, p); 148 149 case WDIOC_SETOPTIONS: 150 { 151 int options, retval = -EINVAL; 152 153 if (get_user(options, p)) 154 return -EFAULT; 155 156 if (options & WDIOS_DISABLECARD) { 157 wafwdt_stop(); 158 retval = 0; 159 } 160 161 if (options & WDIOS_ENABLECARD) { 162 wafwdt_start(); 163 retval = 0; 164 } 165 166 return retval; 167 } 168 169 case WDIOC_KEEPALIVE: 170 wafwdt_ping(); 171 break; 172 173 case WDIOC_SETTIMEOUT: 174 if (get_user(new_timeout, p)) 175 return -EFAULT; 176 if ((new_timeout < 1) || (new_timeout > 255)) 177 return -EINVAL; 178 timeout = new_timeout; 179 wafwdt_stop(); 180 wafwdt_start(); 181 /* Fall through */ 182 case WDIOC_GETTIMEOUT: 183 return put_user(timeout, p); 184 185 default: 186 return -ENOTTY; 187 } 188 return 0; 189 } 190 191 static int wafwdt_open(struct inode *inode, struct file *file) 192 { 193 if (test_and_set_bit(0, &wafwdt_is_open)) 194 return -EBUSY; 195 196 /* 197 * Activate 198 */ 199 wafwdt_start(); 200 return nonseekable_open(inode, file); 201 } 202 203 static int wafwdt_close(struct inode *inode, struct file *file) 204 { 205 if (expect_close == 42) 206 wafwdt_stop(); 207 else { 208 pr_crit("WDT device closed unexpectedly. WDT will not stop!\n"); 209 wafwdt_ping(); 210 } 211 clear_bit(0, &wafwdt_is_open); 212 expect_close = 0; 213 return 0; 214 } 215 216 /* 217 * Notifier for system down 218 */ 219 220 static int wafwdt_notify_sys(struct notifier_block *this, unsigned long code, 221 void *unused) 222 { 223 if (code == SYS_DOWN || code == SYS_HALT) 224 wafwdt_stop(); 225 return NOTIFY_DONE; 226 } 227 228 /* 229 * Kernel Interfaces 230 */ 231 232 static const struct file_operations wafwdt_fops = { 233 .owner = THIS_MODULE, 234 .llseek = no_llseek, 235 .write = wafwdt_write, 236 .unlocked_ioctl = wafwdt_ioctl, 237 .open = wafwdt_open, 238 .release = wafwdt_close, 239 }; 240 241 static struct miscdevice wafwdt_miscdev = { 242 .minor = WATCHDOG_MINOR, 243 .name = "watchdog", 244 .fops = &wafwdt_fops, 245 }; 246 247 /* 248 * The WDT needs to learn about soft shutdowns in order to 249 * turn the timebomb registers off. 250 */ 251 252 static struct notifier_block wafwdt_notifier = { 253 .notifier_call = wafwdt_notify_sys, 254 }; 255 256 static int __init wafwdt_init(void) 257 { 258 int ret; 259 260 pr_info("WDT driver for Wafer 5823 single board computer initialising\n"); 261 262 if (timeout < 1 || timeout > 255) { 263 timeout = WD_TIMO; 264 pr_info("timeout value must be 1 <= x <= 255, using %d\n", 265 timeout); 266 } 267 268 if (wdt_stop != wdt_start) { 269 if (!request_region(wdt_stop, 1, "Wafer 5823 WDT")) { 270 pr_err("I/O address 0x%04x already in use\n", wdt_stop); 271 ret = -EIO; 272 goto error; 273 } 274 } 275 276 if (!request_region(wdt_start, 1, "Wafer 5823 WDT")) { 277 pr_err("I/O address 0x%04x already in use\n", wdt_start); 278 ret = -EIO; 279 goto error2; 280 } 281 282 ret = register_reboot_notifier(&wafwdt_notifier); 283 if (ret != 0) { 284 pr_err("cannot register reboot notifier (err=%d)\n", ret); 285 goto error3; 286 } 287 288 ret = misc_register(&wafwdt_miscdev); 289 if (ret != 0) { 290 pr_err("cannot register miscdev on minor=%d (err=%d)\n", 291 WATCHDOG_MINOR, ret); 292 goto error4; 293 } 294 295 pr_info("initialized. timeout=%d sec (nowayout=%d)\n", 296 timeout, nowayout); 297 298 return ret; 299 error4: 300 unregister_reboot_notifier(&wafwdt_notifier); 301 error3: 302 release_region(wdt_start, 1); 303 error2: 304 if (wdt_stop != wdt_start) 305 release_region(wdt_stop, 1); 306 error: 307 return ret; 308 } 309 310 static void __exit wafwdt_exit(void) 311 { 312 misc_deregister(&wafwdt_miscdev); 313 unregister_reboot_notifier(&wafwdt_notifier); 314 if (wdt_stop != wdt_start) 315 release_region(wdt_stop, 1); 316 release_region(wdt_start, 1); 317 } 318 319 module_init(wafwdt_init); 320 module_exit(wafwdt_exit); 321 322 MODULE_AUTHOR("Justin Cormack"); 323 MODULE_DESCRIPTION("ICP Wafer 5823 Single Board Computer WDT driver"); 324 MODULE_LICENSE("GPL"); 325 326 /* end of wafer5823wdt.c */ 327