root/drivers/watchdog/wafer5823wdt.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. wafwdt_ping
  2. wafwdt_start
  3. wafwdt_stop
  4. wafwdt_write
  5. wafwdt_ioctl
  6. wafwdt_open
  7. wafwdt_close
  8. wafwdt_notify_sys
  9. wafwdt_init
  10. wafwdt_exit

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

/* [<][>][^][v][top][bottom][index][help] */