1/* 2 * National Semiconductor PC87307/PC97307 (ala SC1200) WDT driver 3 * (c) Copyright 2002 Zwane Mwaikambo <zwane@commfireservices.com>, 4 * All Rights Reserved. 5 * Based on wdt.c and wdt977.c by Alan Cox and Woody Suwalski respectively. 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 10 * 2 of the License, or (at your option) any later version. 11 * 12 * The author(s) of this software shall not be held liable for damages 13 * of any nature resulting due to the use of this software. This 14 * software is provided AS-IS with no warranties. 15 * 16 * Changelog: 17 * 20020220 Zwane Mwaikambo Code based on datasheet, no hardware. 18 * 20020221 Zwane Mwaikambo Cleanups as suggested by Jeff Garzik and Alan Cox. 19 * 20020222 Zwane Mwaikambo Added probing. 20 * 20020225 Zwane Mwaikambo Added ISAPNP support. 21 * 20020412 Rob Radez Broke out start/stop functions 22 * <rob@osinvestor.com> Return proper status instead of temperature warning 23 * Add WDIOC_GETBOOTSTATUS and WDIOC_SETOPTIONS ioctls 24 * Fix CONFIG_WATCHDOG_NOWAYOUT 25 * 20020530 Joel Becker Add Matt Domsch's nowayout module option 26 * 20030116 Adam Belay Updated to the latest pnp code 27 * 28 */ 29 30#include <linux/module.h> 31#include <linux/moduleparam.h> 32#include <linux/miscdevice.h> 33#include <linux/watchdog.h> 34#include <linux/ioport.h> 35#include <linux/spinlock.h> 36#include <linux/notifier.h> 37#include <linux/reboot.h> 38#include <linux/init.h> 39#include <linux/pnp.h> 40#include <linux/fs.h> 41 42#include <asm/semaphore.h> 43#include <asm/io.h> 44#include <asm/uaccess.h> 45 46#define SC1200_MODULE_VER "build 20020303" 47#define SC1200_MODULE_NAME "sc1200wdt" 48#define PFX SC1200_MODULE_NAME ": " 49 50#define MAX_TIMEOUT 255 /* 255 minutes */ 51#define PMIR (io) /* Power Management Index Register */ 52#define PMDR (io+1) /* Power Management Data Register */ 53 54/* Data Register indexes */ 55#define FER1 0x00 /* Function enable register 1 */ 56#define FER2 0x01 /* Function enable register 2 */ 57#define PMC1 0x02 /* Power Management Ctrl 1 */ 58#define PMC2 0x03 /* Power Management Ctrl 2 */ 59#define PMC3 0x04 /* Power Management Ctrl 3 */ 60#define WDTO 0x05 /* Watchdog timeout register */ 61#define WDCF 0x06 /* Watchdog config register */ 62#define WDST 0x07 /* Watchdog status register */ 63 64/* WDCF bitfields - which devices assert WDO */ 65#define KBC_IRQ 0x01 /* Keyboard Controller */ 66#define MSE_IRQ 0x02 /* Mouse */ 67#define UART1_IRQ 0x03 /* Serial0 */ 68#define UART2_IRQ 0x04 /* Serial1 */ 69/* 5 -7 are reserved */ 70 71static char banner[] __initdata = KERN_INFO PFX SC1200_MODULE_VER; 72static int timeout = 1; 73static int io = -1; 74static int io_len = 2; /* for non plug and play */ 75static struct semaphore open_sem; 76static char expect_close; 77static spinlock_t sc1200wdt_lock; /* io port access serialisation */ 78 79#if defined CONFIG_PNP 80static int isapnp = 1; 81static struct pnp_dev *wdt_dev; 82 83module_param(isapnp, int, 0); 84MODULE_PARM_DESC(isapnp, "When set to 0 driver ISA PnP support will be disabled"); 85#endif 86 87module_param(io, int, 0); 88MODULE_PARM_DESC(io, "io port"); 89module_param(timeout, int, 0); 90MODULE_PARM_DESC(timeout, "range is 0-255 minutes, default is 1"); 91 92static int nowayout = WATCHDOG_NOWAYOUT; 93module_param(nowayout, int, 0); 94MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 95 96 97 98/* Read from Data Register */ 99static inline void sc1200wdt_read_data(unsigned char index, unsigned char *data) 100{ 101 spin_lock(&sc1200wdt_lock); 102 outb_p(index, PMIR); 103 *data = inb(PMDR); 104 spin_unlock(&sc1200wdt_lock); 105} 106 107 108/* Write to Data Register */ 109static inline void sc1200wdt_write_data(unsigned char index, unsigned char data) 110{ 111 spin_lock(&sc1200wdt_lock); 112 outb_p(index, PMIR); 113 outb(data, PMDR); 114 spin_unlock(&sc1200wdt_lock); 115} 116 117 118static void sc1200wdt_start(void) 119{ 120 unsigned char reg; 121 122 sc1200wdt_read_data(WDCF, ®); 123 /* assert WDO when any of the following interrupts are triggered too */ 124 reg |= (KBC_IRQ | MSE_IRQ | UART1_IRQ | UART2_IRQ); 125 sc1200wdt_write_data(WDCF, reg); 126 /* set the timeout and get the ball rolling */ 127 sc1200wdt_write_data(WDTO, timeout); 128} 129 130 131static void sc1200wdt_stop(void) 132{ 133 sc1200wdt_write_data(WDTO, 0); 134} 135 136 137/* This returns the status of the WDO signal, inactive high. */ 138static inline int sc1200wdt_status(void) 139{ 140 unsigned char ret; 141 142 sc1200wdt_read_data(WDST, &ret); 143 /* If the bit is inactive, the watchdog is enabled, so return 144 * KEEPALIVEPING which is a bit of a kludge because there's nothing 145 * else for enabled/disabled status 146 */ 147 return (ret & 0x01) ? 0 : WDIOF_KEEPALIVEPING; /* bits 1 - 7 are undefined */ 148} 149 150 151static int sc1200wdt_open(struct inode *inode, struct file *file) 152{ 153 nonseekable_open(inode, file); 154 155 /* allow one at a time */ 156 if (down_trylock(&open_sem)) 157 return -EBUSY; 158 159 if (timeout > MAX_TIMEOUT) 160 timeout = MAX_TIMEOUT; 161 162 sc1200wdt_start(); 163 printk(KERN_INFO PFX "Watchdog enabled, timeout = %d min(s)", timeout); 164 165 return 0; 166} 167 168 169static int sc1200wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) 170{ 171 int new_timeout; 172 void __user *argp = (void __user *)arg; 173 int __user *p = argp; 174 static struct watchdog_info ident = { 175 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, 176 .firmware_version = 0, 177 .identity = "PC87307/PC97307", 178 }; 179 180 switch (cmd) { 181 default: 182 return -ENOTTY; 183 184 case WDIOC_GETSUPPORT: 185 if (copy_to_user(argp, &ident, sizeof ident)) 186 return -EFAULT; 187 return 0; 188 189 case WDIOC_GETSTATUS: 190 return put_user(sc1200wdt_status(), p); 191 192 case WDIOC_GETBOOTSTATUS: 193 return put_user(0, p); 194 195 case WDIOC_KEEPALIVE: 196 sc1200wdt_write_data(WDTO, timeout); 197 return 0; 198 199 case WDIOC_SETTIMEOUT: 200 if (get_user(new_timeout, p)) 201 return -EFAULT; 202 203 /* the API states this is given in secs */ 204 new_timeout /= 60; 205 if (new_timeout < 0 || new_timeout > MAX_TIMEOUT) 206 return -EINVAL; 207 208 timeout = new_timeout; 209 sc1200wdt_write_data(WDTO, timeout); 210 /* fall through and return the new timeout */ 211 212 case WDIOC_GETTIMEOUT: 213 return put_user(timeout * 60, p); 214 215 case WDIOC_SETOPTIONS: 216 { 217 int options, retval = -EINVAL; 218 219 if (get_user(options, p)) 220 return -EFAULT; 221 222 if (options & WDIOS_DISABLECARD) { 223 sc1200wdt_stop(); 224 retval = 0; 225 } 226 227 if (options & WDIOS_ENABLECARD) { 228 sc1200wdt_start(); 229 retval = 0; 230 } 231 232 return retval; 233 } 234 } 235} 236 237 238static int sc1200wdt_release(struct inode *inode, struct file *file) 239{ 240 if (expect_close == 42) { 241 sc1200wdt_stop(); 242 printk(KERN_INFO PFX "Watchdog disabled\n"); 243 } else { 244 sc1200wdt_write_data(WDTO, timeout); 245 printk(KERN_CRIT PFX "Unexpected close!, timeout = %d min(s)\n", timeout); 246 } 247 up(&open_sem); 248 expect_close = 0; 249 250 return 0; 251} 252 253 254static ssize_t sc1200wdt_write(struct file *file, const char __user *data, size_t len, loff_t *ppos) 255{ 256 if (len) { 257 if (!nowayout) { 258 size_t i; 259 260 expect_close = 0; 261 262 for (i = 0; i != len; i++) { 263 char c; 264 265 if (get_user(c, data+i)) 266 return -EFAULT; 267 if (c == 'V') 268 expect_close = 42; 269 } 270 } 271 272 sc1200wdt_write_data(WDTO, timeout); 273 return len; 274 } 275 276 return 0; 277} 278 279 280static int sc1200wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused) 281{ 282 if (code == SYS_DOWN || code == SYS_HALT) 283 sc1200wdt_stop(); 284 285 return NOTIFY_DONE; 286} 287 288 289static struct notifier_block sc1200wdt_notifier = 290{ 291 .notifier_call = sc1200wdt_notify_sys, 292}; 293 294static const struct file_operations sc1200wdt_fops = 295{ 296 .owner = THIS_MODULE, 297 .llseek = no_llseek, 298 .write = sc1200wdt_write, 299 .ioctl = sc1200wdt_ioctl, 300 .open = sc1200wdt_open, 301 .release = sc1200wdt_release, 302}; 303 304static struct miscdevice sc1200wdt_miscdev = 305{ 306 .minor = WATCHDOG_MINOR, 307 .name = "watchdog", 308 .fops = &sc1200wdt_fops, 309}; 310 311 312static int __init sc1200wdt_probe(void) 313{ 314 /* The probe works by reading the PMC3 register's default value of 0x0e 315 * there is one caveat, if the device disables the parallel port or any 316 * of the UARTs we won't be able to detect it. 317 * Nb. This could be done with accuracy by reading the SID registers, but 318 * we don't have access to those io regions. 319 */ 320 321 unsigned char reg; 322 323 sc1200wdt_read_data(PMC3, ®); 324 reg &= 0x0f; /* we don't want the UART busy bits */ 325 return (reg == 0x0e) ? 0 : -ENODEV; 326} 327 328 329#if defined CONFIG_PNP 330 331static struct pnp_device_id scl200wdt_pnp_devices[] = { 332 /* National Semiconductor PC87307/PC97307 watchdog component */ 333 {.id = "NSC0800", .driver_data = 0}, 334 {.id = ""}, 335}; 336 337static int scl200wdt_pnp_probe(struct pnp_dev * dev, const struct pnp_device_id *dev_id) 338{ 339 /* this driver only supports one card at a time */ 340 if (wdt_dev || !isapnp) 341 return -EBUSY; 342 343 wdt_dev = dev; 344 io = pnp_port_start(wdt_dev, 0); 345 io_len = pnp_port_len(wdt_dev, 0); 346 347 if (!request_region(io, io_len, SC1200_MODULE_NAME)) { 348 printk(KERN_ERR PFX "Unable to register IO port %#x\n", io); 349 return -EBUSY; 350 } 351 352 printk(KERN_INFO "scl200wdt: PnP device found at io port %#x/%d\n", io, io_len); 353 return 0; 354} 355 356static void scl200wdt_pnp_remove(struct pnp_dev * dev) 357{ 358 if (wdt_dev){ 359 release_region(io, io_len); 360 wdt_dev = NULL; 361 } 362} 363 364static struct pnp_driver scl200wdt_pnp_driver = { 365 .name = "scl200wdt", 366 .id_table = scl200wdt_pnp_devices, 367 .probe = scl200wdt_pnp_probe, 368 .remove = scl200wdt_pnp_remove, 369}; 370 371#endif /* CONFIG_PNP */ 372 373 374static int __init sc1200wdt_init(void) 375{ 376 int ret; 377 378 printk("%s\n", banner); 379 380 spin_lock_init(&sc1200wdt_lock); 381 sema_init(&open_sem, 1); 382 383#if defined CONFIG_PNP 384 if (isapnp) { 385 ret = pnp_register_driver(&scl200wdt_pnp_driver); 386 if (ret) 387 goto out_clean; 388 } 389#endif 390 391 if (io == -1) { 392 printk(KERN_ERR PFX "io parameter must be specified\n"); 393 ret = -EINVAL; 394 goto out_pnp; 395 } 396 397#if defined CONFIG_PNP 398 /* now that the user has specified an IO port and we haven't detected 399 * any devices, disable pnp support */ 400 isapnp = 0; 401 pnp_unregister_driver(&scl200wdt_pnp_driver); 402#endif 403 404 if (!request_region(io, io_len, SC1200_MODULE_NAME)) { 405 printk(KERN_ERR PFX "Unable to register IO port %#x\n", io); 406 ret = -EBUSY; 407 goto out_pnp; 408 } 409 410 ret = sc1200wdt_probe(); 411 if (ret) 412 goto out_io; 413 414 ret = register_reboot_notifier(&sc1200wdt_notifier); 415 if (ret) { 416 printk(KERN_ERR PFX "Unable to register reboot notifier err = %d\n", ret); 417 goto out_io; 418 } 419 420 ret = misc_register(&sc1200wdt_miscdev); 421 if (ret) { 422 printk(KERN_ERR PFX "Unable to register miscdev on minor %d\n", WATCHDOG_MINOR); 423 goto out_rbt; 424 } 425 426 /* ret = 0 */ 427 428out_clean: 429 return ret; 430 431out_rbt: 432 unregister_reboot_notifier(&sc1200wdt_notifier); 433 434out_io: 435 release_region(io, io_len); 436 437out_pnp: 438#if defined CONFIG_PNP 439 if (isapnp) 440 pnp_unregister_driver(&scl200wdt_pnp_driver); 441#endif 442 goto out_clean; 443} 444 445 446static void __exit sc1200wdt_exit(void) 447{ 448 misc_deregister(&sc1200wdt_miscdev); 449 unregister_reboot_notifier(&sc1200wdt_notifier); 450 451#if defined CONFIG_PNP 452 if(isapnp) 453 pnp_unregister_driver(&scl200wdt_pnp_driver); 454 else 455#endif 456 release_region(io, io_len); 457} 458 459module_init(sc1200wdt_init); 460module_exit(sc1200wdt_exit); 461 462MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>"); 463MODULE_DESCRIPTION("Driver for National Semiconductor PC87307/PC97307 watchdog component"); 464MODULE_LICENSE("GPL"); 465MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 466