1 /* 2 * Eurotech CPU-1220/1410/1420 on board WDT driver 3 * 4 * (c) Copyright 2001 Ascensit <support@ascensit.com> 5 * (c) Copyright 2001 Rodolfo Giometti <giometti@ascensit.com> 6 * (c) Copyright 2002 Rob Radez <rob@osinvestor.com> 7 * 8 * Based on wdt.c. 9 * Original copyright messages: 10 * 11 * (c) Copyright 1996-1997 Alan Cox <alan@redhat.com>, All Rights Reserved. 12 * http://www.redhat.com 13 * 14 * This program is free software; you can redistribute it and/or 15 * modify it under the terms of the GNU General Public License 16 * as published by the Free Software Foundation; either version 17 * 2 of the License, or (at your option) any later version. 18 * 19 * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide 20 * warranty for any of this software. This material is provided 21 * "AS-IS" and at no charge. 22 * 23 * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>* 24 */ 25 26 /* Changelog: 27 * 28 * 2001 - Rodolfo Giometti 29 * Initial release 30 * 31 * 2002/04/25 - Rob Radez 32 * clean up #includes 33 * clean up locking 34 * make __setup param unique 35 * proper options in watchdog_info 36 * add WDIOC_GETSTATUS and WDIOC_SETOPTIONS ioctls 37 * add expect_close support 38 * 39 * 2002.05.30 - Joel Becker <joel.becker@oracle.com> 40 * Added Matt Domsch's nowayout module option. 41 */ 42 43 /* 44 * The eurotech CPU-1220/1410/1420's watchdog is a part 45 * of the on-board SUPER I/O device SMSC FDC 37B782. 46 */ 47 48 #include <linux/interrupt.h> 49 #include <linux/module.h> 50 #include <linux/moduleparam.h> 51 #include <linux/types.h> 52 #include <linux/miscdevice.h> 53 #include <linux/watchdog.h> 54 #include <linux/fs.h> 55 #include <linux/ioport.h> 56 #include <linux/notifier.h> 57 #include <linux/reboot.h> 58 #include <linux/init.h> 59 60 #include <asm/io.h> 61 #include <asm/uaccess.h> 62 #include <asm/system.h> 63 64 static unsigned long eurwdt_is_open; 65 static int eurwdt_timeout; 66 static char eur_expect_close; 67 68 /* 69 * You must set these - there is no sane way to probe for this board. 70 * You can use eurwdt=x,y to set these now. 71 */ 72 73 static int io = 0x3f0; 74 static int irq = 10; 75 static char *ev = "int"; 76 77 #define WDT_TIMEOUT 60 /* 1 minute */ 78 79 static int nowayout = WATCHDOG_NOWAYOUT; 80 module_param(nowayout, int, 0); 81 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 82 83 /* 84 * Some symbolic names 85 */ 86 87 #define WDT_CTRL_REG 0x30 88 #define WDT_OUTPIN_CFG 0xe2 89 #define WDT_EVENT_INT 0x00 90 #define WDT_EVENT_REBOOT 0x08 91 #define WDT_UNIT_SEL 0xf1 92 #define WDT_UNIT_SECS 0x80 93 #define WDT_TIMEOUT_VAL 0xf2 94 #define WDT_TIMER_CFG 0xf3 95 96 97 module_param(io, int, 0); 98 MODULE_PARM_DESC(io, "Eurotech WDT io port (default=0x3f0)"); 99 module_param(irq, int, 0); 100 MODULE_PARM_DESC(irq, "Eurotech WDT irq (default=10)"); 101 module_param(ev, charp, 0); 102 MODULE_PARM_DESC(ev, "Eurotech WDT event type (default is `int')"); 103 104 105 /* 106 * Programming support 107 */ 108 109 static inline void eurwdt_write_reg(u8 index, u8 data) 110 { 111 outb(index, io); 112 outb(data, io+1); 113 } 114 115 static inline void eurwdt_lock_chip(void) 116 { 117 outb(0xaa, io); 118 } 119 120 static inline void eurwdt_unlock_chip(void) 121 { 122 outb(0x55, io); 123 eurwdt_write_reg(0x07, 0x08); /* set the logical device */ 124 } 125 126 static inline void eurwdt_set_timeout(int timeout) 127 { 128 eurwdt_write_reg(WDT_TIMEOUT_VAL, (u8) timeout); 129 } 130 131 static inline void eurwdt_disable_timer(void) 132 { 133 eurwdt_set_timeout(0); 134 } 135 136 static void eurwdt_activate_timer(void) 137 { 138 eurwdt_disable_timer(); 139 eurwdt_write_reg(WDT_CTRL_REG, 0x01); /* activate the WDT */ 140 eurwdt_write_reg(WDT_OUTPIN_CFG, !strcmp("int", ev) ? WDT_EVENT_INT : WDT_EVENT_REBOOT); 141 142 /* Setting interrupt line */ 143 if (irq == 2 || irq > 15 || irq < 0) { 144 printk(KERN_ERR ": invalid irq number\n"); 145 irq = 0; /* if invalid we disable interrupt */ 146 } 147 if (irq == 0) 148 printk(KERN_INFO ": interrupt disabled\n"); 149 150 eurwdt_write_reg(WDT_TIMER_CFG, irq<<4); 151 152 eurwdt_write_reg(WDT_UNIT_SEL, WDT_UNIT_SECS); /* we use seconds */ 153 eurwdt_set_timeout(0); /* the default timeout */ 154 } 155 156 157 /* 158 * Kernel methods. 159 */ 160 161 static irqreturn_t eurwdt_interrupt(int irq, void *dev_id) 162 { 163 printk(KERN_CRIT "timeout WDT timeout\n"); 164 165 #ifdef ONLY_TESTING 166 printk(KERN_CRIT "Would Reboot.\n"); 167 #else 168 printk(KERN_CRIT "Initiating system reboot.\n"); 169 emergency_restart(); 170 #endif 171 return IRQ_HANDLED; 172 } 173 174 175 /** 176 * eurwdt_ping: 177 * 178 * Reload counter one with the watchdog timeout. 179 */ 180 181 static void eurwdt_ping(void) 182 { 183 /* Write the watchdog default value */ 184 eurwdt_set_timeout(eurwdt_timeout); 185 } 186 187 /** 188 * eurwdt_write: 189 * @file: file handle to the watchdog 190 * @buf: buffer to write (unused as data does not matter here 191 * @count: count of bytes 192 * @ppos: pointer to the position to write. No seeks allowed 193 * 194 * A write to a watchdog device is defined as a keepalive signal. Any 195 * write of data will do, as we we don't define content meaning. 196 */ 197 198 static ssize_t eurwdt_write(struct file *file, const char __user *buf, 199 size_t count, loff_t *ppos) 200 { 201 if (count) { 202 if (!nowayout) { 203 size_t i; 204 205 eur_expect_close = 0; 206 207 for (i = 0; i != count; i++) { 208 char c; 209 if(get_user(c, buf+i)) 210 return -EFAULT; 211 if (c == 'V') 212 eur_expect_close = 42; 213 } 214 } 215 eurwdt_ping(); /* the default timeout */ 216 } 217 218 return count; 219 } 220 221 /** 222 * eurwdt_ioctl: 223 * @inode: inode of the device 224 * @file: file handle to the device 225 * @cmd: watchdog command 226 * @arg: argument pointer 227 * 228 * The watchdog API defines a common set of functions for all watchdogs 229 * according to their available features. 230 */ 231 232 static int eurwdt_ioctl(struct inode *inode, struct file *file, 233 unsigned int cmd, unsigned long arg) 234 { 235 void __user *argp = (void __user *)arg; 236 int __user *p = argp; 237 static struct watchdog_info ident = { 238 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, 239 .firmware_version = 1, 240 .identity = "WDT Eurotech CPU-1220/1410", 241 }; 242 243 int time; 244 int options, retval = -EINVAL; 245 246 switch(cmd) { 247 default: 248 return -ENOTTY; 249 250 case WDIOC_GETSUPPORT: 251 return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; 252 253 case WDIOC_GETSTATUS: 254 case WDIOC_GETBOOTSTATUS: 255 return put_user(0, p); 256 257 case WDIOC_KEEPALIVE: 258 eurwdt_ping(); 259 return 0; 260 261 case WDIOC_SETTIMEOUT: 262 if (copy_from_user(&time, p, sizeof(int))) 263 return -EFAULT; 264 265 /* Sanity check */ 266 if (time < 0 || time > 255) 267 return -EINVAL; 268 269 eurwdt_timeout = time; 270 eurwdt_set_timeout(time); 271 /* Fall */ 272 273 case WDIOC_GETTIMEOUT: 274 return put_user(eurwdt_timeout, p); 275 276 case WDIOC_SETOPTIONS: 277 if (get_user(options, p)) 278 return -EFAULT; 279 if (options & WDIOS_DISABLECARD) { 280 eurwdt_disable_timer(); 281 retval = 0; 282 } 283 if (options & WDIOS_ENABLECARD) { 284 eurwdt_activate_timer(); 285 eurwdt_ping(); 286 retval = 0; 287 } 288 return retval; 289 } 290 } 291 292 /** 293 * eurwdt_open: 294 * @inode: inode of device 295 * @file: file handle to device 296 * 297 * The misc device has been opened. The watchdog device is single 298 * open and on opening we load the counter. 299 */ 300 301 static int eurwdt_open(struct inode *inode, struct file *file) 302 { 303 if (test_and_set_bit(0, &eurwdt_is_open)) 304 return -EBUSY; 305 eurwdt_timeout = WDT_TIMEOUT; /* initial timeout */ 306 /* Activate the WDT */ 307 eurwdt_activate_timer(); 308 return nonseekable_open(inode, file); 309 } 310 311 /** 312 * eurwdt_release: 313 * @inode: inode to board 314 * @file: file handle to board 315 * 316 * The watchdog has a configurable API. There is a religious dispute 317 * between people who want their watchdog to be able to shut down and 318 * those who want to be sure if the watchdog manager dies the machine 319 * reboots. In the former case we disable the counters, in the latter 320 * case you have to open it again very soon. 321 */ 322 323 static int eurwdt_release(struct inode *inode, struct file *file) 324 { 325 if (eur_expect_close == 42) { 326 eurwdt_disable_timer(); 327 } else { 328 printk(KERN_CRIT "eurwdt: Unexpected close, not stopping watchdog!\n"); 329 eurwdt_ping(); 330 } 331 clear_bit(0, &eurwdt_is_open); 332 eur_expect_close = 0; 333 return 0; 334 } 335 336 /** 337 * eurwdt_notify_sys: 338 * @this: our notifier block 339 * @code: the event being reported 340 * @unused: unused 341 * 342 * Our notifier is called on system shutdowns. We want to turn the card 343 * off at reboot otherwise the machine will reboot again during memory 344 * test or worse yet during the following fsck. This would suck, in fact 345 * trust me - if it happens it does suck. 346 */ 347 348 static int eurwdt_notify_sys(struct notifier_block *this, unsigned long code, 349 void *unused) 350 { 351 if (code == SYS_DOWN || code == SYS_HALT) { 352 /* Turn the card off */ 353 eurwdt_disable_timer(); 354 } 355 356 return NOTIFY_DONE; 357 } 358 359 /* 360 * Kernel Interfaces 361 */ 362 363 364 static const struct file_operations eurwdt_fops = { 365 .owner = THIS_MODULE, 366 .llseek = no_llseek, 367 .write = eurwdt_write, 368 .ioctl = eurwdt_ioctl, 369 .open = eurwdt_open, 370 .release = eurwdt_release, 371 }; 372 373 static struct miscdevice eurwdt_miscdev = { 374 .minor = WATCHDOG_MINOR, 375 .name = "watchdog", 376 .fops = &eurwdt_fops, 377 }; 378 379 /* 380 * The WDT card needs to learn about soft shutdowns in order to 381 * turn the timebomb registers off. 382 */ 383 384 static struct notifier_block eurwdt_notifier = { 385 .notifier_call = eurwdt_notify_sys, 386 }; 387 388 /** 389 * cleanup_module: 390 * 391 * Unload the watchdog. You cannot do this with any file handles open. 392 * If your watchdog is set to continue ticking on close and you unload 393 * it, well it keeps ticking. We won't get the interrupt but the board 394 * will not touch PC memory so all is fine. You just have to load a new 395 * module in 60 seconds or reboot. 396 */ 397 398 static void __exit eurwdt_exit(void) 399 { 400 eurwdt_lock_chip(); 401 402 misc_deregister(&eurwdt_miscdev); 403 404 unregister_reboot_notifier(&eurwdt_notifier); 405 release_region(io, 2); 406 free_irq(irq, NULL); 407 } 408 409 /** 410 * eurwdt_init: 411 * 412 * Set up the WDT watchdog board. After grabbing the resources 413 * we require we need also to unlock the device. 414 * The open() function will actually kick the board off. 415 */ 416 417 static int __init eurwdt_init(void) 418 { 419 int ret; 420 421 ret = request_irq(irq, eurwdt_interrupt, IRQF_DISABLED, "eurwdt", NULL); 422 if(ret) { 423 printk(KERN_ERR "eurwdt: IRQ %d is not free.\n", irq); 424 goto out; 425 } 426 427 if (!request_region(io, 2, "eurwdt")) { 428 printk(KERN_ERR "eurwdt: IO %X is not free.\n", io); 429 ret = -EBUSY; 430 goto outirq; 431 } 432 433 ret = register_reboot_notifier(&eurwdt_notifier); 434 if (ret) { 435 printk(KERN_ERR "eurwdt: can't register reboot notifier (err=%d)\n", ret); 436 goto outreg; 437 } 438 439 ret = misc_register(&eurwdt_miscdev); 440 if (ret) { 441 printk(KERN_ERR "eurwdt: can't misc_register on minor=%d\n", 442 WATCHDOG_MINOR); 443 goto outreboot; 444 } 445 446 eurwdt_unlock_chip(); 447 448 ret = 0; 449 printk(KERN_INFO "Eurotech WDT driver 0.01 at %X (Interrupt %d)" 450 " - timeout event: %s\n", 451 io, irq, (!strcmp("int", ev) ? "int" : "reboot")); 452 453 out: 454 return ret; 455 456 outreboot: 457 unregister_reboot_notifier(&eurwdt_notifier); 458 459 outreg: 460 release_region(io, 2); 461 462 outirq: 463 free_irq(irq, NULL); 464 goto out; 465 } 466 467 module_init(eurwdt_init); 468 module_exit(eurwdt_exit); 469 470 MODULE_AUTHOR("Rodolfo Giometti"); 471 MODULE_DESCRIPTION("Driver for Eurotech CPU-1220/1410 on board watchdog"); 472 MODULE_LICENSE("GPL"); 473 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 474