2  *      SBC EPX C3 0.1  A Hardware Watchdog Device for the Winsystems EPX-C3
 
   3  *      single board computer
 
   5  *      (c) Copyright 2006 Calin A. Culianu <calin@ajvar.org>, All Rights
 
   8  *      This program is free software; you can redistribute it and/or
 
   9  *      modify it under the terms of the GNU General Public License
 
  10  *      as published by the Free Software Foundation; either version
 
  11  *      2 of the License, or (at your option) any later version.
 
  13  *      based on softdog.c by Alan Cox <alan@redhat.com>
 
  16 #include <linux/module.h>
 
  17 #include <linux/moduleparam.h>
 
  18 #include <linux/types.h>
 
  19 #include <linux/kernel.h>
 
  22 #include <linux/miscdevice.h>
 
  23 #include <linux/watchdog.h>
 
  24 #include <linux/notifier.h>
 
  25 #include <linux/reboot.h>
 
  26 #include <linux/init.h>
 
  27 #include <linux/ioport.h>
 
  28 #include <asm/uaccess.h>
 
  31 #define PFX "epx_c3: "
 
  32 static int epx_c3_alive;
 
  34 #define WATCHDOG_TIMEOUT 1              /* 1 sec default timeout */
 
  36 static int nowayout = WATCHDOG_NOWAYOUT;
 
  37 module_param(nowayout, int, 0);
 
  38 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 
  40 #define EPXC3_WATCHDOG_CTL_REG 0x1ee /* write 1 to enable, 0 to disable */
 
  41 #define EPXC3_WATCHDOG_PET_REG 0x1ef /* write anything to pet once enabled */
 
  43 static void epx_c3_start(void)
 
  45         outb(1, EPXC3_WATCHDOG_CTL_REG);
 
  48 static void epx_c3_stop(void)
 
  51         outb(0, EPXC3_WATCHDOG_CTL_REG);
 
  53         printk(KERN_INFO PFX "Stopped watchdog timer.\n");
 
  56 static void epx_c3_pet(void)
 
  58         outb(1, EPXC3_WATCHDOG_PET_REG);
 
  62  *      Allow only one person to hold it open
 
  64 static int epx_c3_open(struct inode *inode, struct file *file)
 
  70                 __module_get(THIS_MODULE);
 
  77         printk(KERN_INFO "Started watchdog timer.\n");
 
  79         return nonseekable_open(inode, file);
 
  82 static int epx_c3_release(struct inode *inode, struct file *file)
 
  84         /* Shut off the timer.
 
  85          * Lock it in if it's a module and we defined ...NOWAYOUT */
 
  87                 epx_c3_stop();          /* Turn the WDT off */
 
  94 static ssize_t epx_c3_write(struct file *file, const char __user *data,
 
  95                         size_t len, loff_t *ppos)
 
  97         /* Refresh the timer. */
 
 103 static int epx_c3_ioctl(struct inode *inode, struct file *file,
 
 104                         unsigned int cmd, unsigned long arg)
 
 106         int options, retval = -EINVAL;
 
 107         int __user *argp = (void __user *)arg;
 
 108         static struct watchdog_info ident = {
 
 109                 .options                = WDIOF_KEEPALIVEPING |
 
 111                 .firmware_version       = 0,
 
 112                 .identity               = "Winsystems EPX-C3 H/W Watchdog",
 
 116         case WDIOC_GETSUPPORT:
 
 117                 if (copy_to_user(argp, &ident, sizeof(ident)))
 
 120         case WDIOC_GETSTATUS:
 
 121         case WDIOC_GETBOOTSTATUS:
 
 122                 return put_user(0, argp);
 
 123         case WDIOC_KEEPALIVE:
 
 126         case WDIOC_GETTIMEOUT:
 
 127                 return put_user(WATCHDOG_TIMEOUT, argp);
 
 128         case WDIOC_SETOPTIONS:
 
 129                 if (get_user(options, argp))
 
 132                 if (options & WDIOS_DISABLECARD) {
 
 137                 if (options & WDIOS_ENABLECARD) {
 
 148 static int epx_c3_notify_sys(struct notifier_block *this, unsigned long code,
 
 151         if (code == SYS_DOWN || code == SYS_HALT)
 
 152                 epx_c3_stop();          /* Turn the WDT off */
 
 157 static const struct file_operations epx_c3_fops = {
 
 158         .owner          = THIS_MODULE,
 
 160         .write          = epx_c3_write,
 
 161         .ioctl          = epx_c3_ioctl,
 
 163         .release        = epx_c3_release,
 
 166 static struct miscdevice epx_c3_miscdev = {
 
 167         .minor          = WATCHDOG_MINOR,
 
 169         .fops           = &epx_c3_fops,
 
 172 static struct notifier_block epx_c3_notifier = {
 
 173         .notifier_call = epx_c3_notify_sys,
 
 176 static const char banner[] __initdata =
 
 177     KERN_INFO PFX "Hardware Watchdog Timer for Winsystems EPX-C3 SBC: 0.1\n";
 
 179 static int __init watchdog_init(void)
 
 183         if (!request_region(EPXC3_WATCHDOG_CTL_REG, 2, "epxc3_watchdog"))
 
 186         ret = register_reboot_notifier(&epx_c3_notifier);
 
 188                 printk(KERN_ERR PFX "cannot register reboot notifier "
 
 193         ret = misc_register(&epx_c3_miscdev);
 
 195                 printk(KERN_ERR PFX "cannot register miscdev on minor=%d "
 
 196                         "(err=%d)\n", WATCHDOG_MINOR, ret);
 
 197                 unregister_reboot_notifier(&epx_c3_notifier);
 
 206         release_region(EPXC3_WATCHDOG_CTL_REG, 2);
 
 210 static void __exit watchdog_exit(void)
 
 212         misc_deregister(&epx_c3_miscdev);
 
 213         unregister_reboot_notifier(&epx_c3_notifier);
 
 214         release_region(EPXC3_WATCHDOG_CTL_REG, 2);
 
 217 module_init(watchdog_init);
 
 218 module_exit(watchdog_exit);
 
 220 MODULE_AUTHOR("Calin A. Culianu <calin@ajvar.org>");
 
 221 MODULE_DESCRIPTION("Hardware Watchdog Device for Winsystems EPX-C3 SBC.  Note that there is no way to probe for this device -- so only use it if you are *sure* you are runnning on this specific SBC system from Winsystems!  It writes to IO ports 0x1ee and 0x1ef!");
 
 222 MODULE_LICENSE("GPL");
 
 223 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);