2  *      Advantech Single Board Computer WDT driver
 
   4  *      (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
 
   6  *      Based on acquirewdt.c which is based on wdt.c.
 
   7  *      Original copyright messages:
 
   9  *      (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
 
  10  *                              http://www.redhat.com
 
  12  *      This program is free software; you can redistribute it and/or
 
  13  *      modify it under the terms of the GNU General Public License
 
  14  *      as published by the Free Software Foundation; either version
 
  15  *      2 of the License, or (at your option) any later version.
 
  17  *      Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
 
  18  *      warranty for any of this software. This material is provided
 
  19  *      "AS-IS" and at no charge.
 
  21  *      (c) Copyright 1995    Alan Cox <alan@redhat.com>
 
  23  *      14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
 
  24  *          Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
 
  26  *      16-Oct-2002 Rob Radez <rob@osinvestor.com>
 
  27  *          Clean up ioctls, clean up init + exit, add expect close support,
 
  28  *          add wdt_start and wdt_stop as parameters.
 
  31 #include <linux/module.h>
 
  32 #include <linux/moduleparam.h>
 
  33 #include <linux/types.h>
 
  34 #include <linux/miscdevice.h>
 
  35 #include <linux/watchdog.h>
 
  37 #include <linux/ioport.h>
 
  38 #include <linux/notifier.h>
 
  39 #include <linux/reboot.h>
 
  40 #include <linux/init.h>
 
  43 #include <asm/uaccess.h>
 
  44 #include <asm/system.h>
 
  46 #define WATCHDOG_NAME "Advantech WDT"
 
  47 #define PFX WATCHDOG_NAME ": "
 
  48 #define WATCHDOG_TIMEOUT 60             /* 60 sec default timeout */
 
  50 static unsigned long advwdt_is_open;
 
  51 static char adv_expect_close;
 
  54  *      You must set these - there is no sane way to probe for this board.
 
  56  *      To enable or restart, write the timeout value in seconds (1 to 63)
 
  57  *      to I/O port wdt_start.  To disable, read I/O port wdt_stop.
 
  58  *      Both are 0x443 for most boards (tested on a PCA-6276VE-00B1), but
 
  59  *      check your manual (at least the PCA-6159 seems to be different -
 
  60  *      the manual says wdt_stop is 0x43, not 0x443).
 
  61  *      (0x43 is also a write-only control register for the 8254 timer!)
 
  64 static int wdt_stop = 0x443;
 
  65 module_param(wdt_stop, int, 0);
 
  66 MODULE_PARM_DESC(wdt_stop, "Advantech WDT 'stop' io port (default 0x443)");
 
  68 static int wdt_start = 0x443;
 
  69 module_param(wdt_start, int, 0);
 
  70 MODULE_PARM_DESC(wdt_start, "Advantech WDT 'start' io port (default 0x443)");
 
  72 static int timeout = WATCHDOG_TIMEOUT;  /* in seconds */
 
  73 module_param(timeout, int, 0);
 
  74 MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. 1<= timeout <=63, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
 
  76 static int nowayout = WATCHDOG_NOWAYOUT;
 
  77 module_param(nowayout, int, 0);
 
  78 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
 
  87         /* Write a watchdog value */
 
  88         outb_p(timeout, wdt_start);
 
  98 advwdt_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
 
 104                         adv_expect_close = 0;
 
 106                         for (i = 0; i != count; i++) {
 
 108                                 if (get_user(c, buf+i))
 
 111                                         adv_expect_close = 42;
 
 120 advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 
 124         void __user *argp = (void __user *)arg;
 
 125         int __user *p = argp;
 
 126         static struct watchdog_info ident = {
 
 127                 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
 
 128                 .firmware_version = 1,
 
 129                 .identity = "Advantech WDT",
 
 133         case WDIOC_GETSUPPORT:
 
 134           if (copy_to_user(argp, &ident, sizeof(ident)))
 
 138         case WDIOC_GETSTATUS:
 
 139         case WDIOC_GETBOOTSTATUS:
 
 140           return put_user(0, p);
 
 142         case WDIOC_KEEPALIVE:
 
 146         case WDIOC_SETTIMEOUT:
 
 147           if (get_user(new_timeout, p))
 
 149           if ((new_timeout < 1) || (new_timeout > 63))
 
 151           timeout = new_timeout;
 
 155         case WDIOC_GETTIMEOUT:
 
 156           return put_user(timeout, p);
 
 158         case WDIOC_SETOPTIONS:
 
 160           int options, retval = -EINVAL;
 
 162           if (get_user(options, p))
 
 165           if (options & WDIOS_DISABLECARD) {
 
 170           if (options & WDIOS_ENABLECARD) {
 
 185 advwdt_open(struct inode *inode, struct file *file)
 
 187         if (test_and_set_bit(0, &advwdt_is_open))
 
 194         return nonseekable_open(inode, file);
 
 198 advwdt_close(struct inode *inode, struct file *file)
 
 200         if (adv_expect_close == 42) {
 
 203                 printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
 
 206         clear_bit(0, &advwdt_is_open);
 
 207         adv_expect_close = 0;
 
 212  *      Notifier for system down
 
 216 advwdt_notify_sys(struct notifier_block *this, unsigned long code,
 
 219         if (code == SYS_DOWN || code == SYS_HALT) {
 
 220                 /* Turn the WDT off */
 
 230 static struct file_operations advwdt_fops = {
 
 231         .owner          = THIS_MODULE,
 
 233         .write          = advwdt_write,
 
 234         .ioctl          = advwdt_ioctl,
 
 236         .release        = advwdt_close,
 
 239 static struct miscdevice advwdt_miscdev = {
 
 240         .minor = WATCHDOG_MINOR,
 
 242         .fops = &advwdt_fops,
 
 246  *      The WDT needs to learn about soft shutdowns in order to
 
 247  *      turn the timebomb registers off.
 
 250 static struct notifier_block advwdt_notifier = {
 
 251         .notifier_call = advwdt_notify_sys,
 
 259         printk(KERN_INFO "WDT driver for Advantech single board computer initialising.\n");
 
 261         if (timeout < 1 || timeout > 63) {
 
 262                 timeout = WATCHDOG_TIMEOUT;
 
 263                 printk (KERN_INFO PFX "timeout value must be 1<=x<=63, using %d\n",
 
 267         if (wdt_stop != wdt_start) {
 
 268                 if (!request_region(wdt_stop, 1, WATCHDOG_NAME)) {
 
 269                         printk (KERN_ERR PFX "I/O address 0x%04x already in use\n",
 
 276         if (!request_region(wdt_start, 1, WATCHDOG_NAME)) {
 
 277                 printk (KERN_ERR PFX "I/O address 0x%04x already in use\n",
 
 283         ret = register_reboot_notifier(&advwdt_notifier);
 
 285                 printk (KERN_ERR PFX "cannot register reboot notifier (err=%d)\n",
 
 290         ret = misc_register(&advwdt_miscdev);
 
 292                 printk (KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
 
 293                         WATCHDOG_MINOR, ret);
 
 297         printk (KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n",
 
 303         unregister_reboot_notifier(&advwdt_notifier);
 
 305         release_region(wdt_start, 1);
 
 307         if (wdt_stop != wdt_start)
 
 308                 release_region(wdt_stop, 1);
 
 315         misc_deregister(&advwdt_miscdev);
 
 316         unregister_reboot_notifier(&advwdt_notifier);
 
 317         if(wdt_stop != wdt_start)
 
 318                 release_region(wdt_stop,1);
 
 319         release_region(wdt_start,1);
 
 322 module_init(advwdt_init);
 
 323 module_exit(advwdt_exit);
 
 325 MODULE_LICENSE("GPL");
 
 326 MODULE_AUTHOR("Marek Michalkiewicz <marekm@linux.org.pl>");
 
 327 MODULE_DESCRIPTION("Advantech Single Board Computer WDT driver");
 
 328 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);