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