root/drivers/watchdog/acquirewdt.c

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

DEFINITIONS

This source file includes following definitions.
  1. acq_keepalive
  2. acq_stop
  3. acq_write
  4. acq_ioctl
  5. acq_open
  6. acq_close
  7. acq_probe
  8. acq_remove
  9. acq_shutdown
  10. acq_init
  11. acq_exit

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  *      Acquire Single Board Computer Watchdog Timer driver
   4  *
   5  *      Based on wdt.c. Original copyright messages:
   6  *
   7  *      (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
   8  *                                              All Rights Reserved.
   9  *
  10  *      Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
  11  *      warranty for any of this software. This material is provided
  12  *      "AS-IS" and at no charge.
  13  *
  14  *      (c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
  15  *
  16  *      14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
  17  *          Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
  18  *          Can't add timeout - driver doesn't allow changing value
  19  */
  20 
  21 /*
  22  *      Theory of Operation:
  23  *              The Watch-Dog Timer is provided to ensure that standalone
  24  *              Systems can always recover from catastrophic conditions that
  25  *              caused the CPU to crash. This condition may have occurred by
  26  *              external EMI or a software bug. When the CPU stops working
  27  *              correctly, hardware on the board will either perform a hardware
  28  *              reset (cold boot) or a non-maskable interrupt (NMI) to bring the
  29  *              system back to a known state.
  30  *
  31  *              The Watch-Dog Timer is controlled by two I/O Ports.
  32  *                443 hex       - Read  - Enable or refresh the Watch-Dog Timer
  33  *                043 hex       - Read  - Disable the Watch-Dog Timer
  34  *
  35  *              To enable the Watch-Dog Timer, a read from I/O port 443h must
  36  *              be performed. This will enable and activate the countdown timer
  37  *              which will eventually time out and either reset the CPU or cause
  38  *              an NMI depending on the setting of a jumper. To ensure that this
  39  *              reset condition does not occur, the Watch-Dog Timer must be
  40  *              periodically refreshed by reading the same I/O port 443h.
  41  *              The Watch-Dog Timer is disabled by reading I/O port 043h.
  42  *
  43  *              The Watch-Dog Timer Time-Out Period is set via jumpers.
  44  *              It can be 1, 2, 10, 20, 110 or 220 seconds.
  45  */
  46 
  47 /*
  48  *      Includes, defines, variables, module parameters, ...
  49  */
  50 
  51 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  52 
  53 /* Includes */
  54 #include <linux/module.h>               /* For module specific items */
  55 #include <linux/moduleparam.h>          /* For new moduleparam's */
  56 #include <linux/types.h>                /* For standard types (like size_t) */
  57 #include <linux/errno.h>                /* For the -ENODEV/... values */
  58 #include <linux/kernel.h>               /* For printk/panic/... */
  59 #include <linux/miscdevice.h>           /* For struct miscdevice */
  60 #include <linux/watchdog.h>             /* For the watchdog specific items */
  61 #include <linux/fs.h>                   /* For file operations */
  62 #include <linux/ioport.h>               /* For io-port access */
  63 #include <linux/platform_device.h>      /* For platform_driver framework */
  64 #include <linux/init.h>                 /* For __init/__exit/... */
  65 #include <linux/uaccess.h>              /* For copy_to_user/put_user/... */
  66 #include <linux/io.h>                   /* For inb/outb/... */
  67 
  68 /* Module information */
  69 #define DRV_NAME "acquirewdt"
  70 #define WATCHDOG_NAME "Acquire WDT"
  71 /* There is no way to see what the correct time-out period is */
  72 #define WATCHDOG_HEARTBEAT 0
  73 
  74 /* internal variables */
  75 /* the watchdog platform device */
  76 static struct platform_device *acq_platform_device;
  77 static unsigned long acq_is_open;
  78 static char expect_close;
  79 
  80 /* module parameters */
  81 /* You must set this - there is no sane way to probe for this board. */
  82 static int wdt_stop = 0x43;
  83 module_param(wdt_stop, int, 0);
  84 MODULE_PARM_DESC(wdt_stop, "Acquire WDT 'stop' io port (default 0x43)");
  85 
  86 /* You must set this - there is no sane way to probe for this board. */
  87 static int wdt_start = 0x443;
  88 module_param(wdt_start, int, 0);
  89 MODULE_PARM_DESC(wdt_start, "Acquire WDT 'start' io port (default 0x443)");
  90 
  91 static bool nowayout = WATCHDOG_NOWAYOUT;
  92 module_param(nowayout, bool, 0);
  93 MODULE_PARM_DESC(nowayout,
  94         "Watchdog cannot be stopped once started (default="
  95         __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  96 
  97 /*
  98  *      Watchdog Operations
  99  */
 100 
 101 static void acq_keepalive(void)
 102 {
 103         /* Write a watchdog value */
 104         inb_p(wdt_start);
 105 }
 106 
 107 static void acq_stop(void)
 108 {
 109         /* Turn the card off */
 110         inb_p(wdt_stop);
 111 }
 112 
 113 /*
 114  *      /dev/watchdog handling
 115  */
 116 
 117 static ssize_t acq_write(struct file *file, const char __user *buf,
 118                                                 size_t count, loff_t *ppos)
 119 {
 120         /* See if we got the magic character 'V' and reload the timer */
 121         if (count) {
 122                 if (!nowayout) {
 123                         size_t i;
 124                         /* note: just in case someone wrote the magic character
 125                            five months ago... */
 126                         expect_close = 0;
 127                         /* scan to see whether or not we got the
 128                            magic character */
 129                         for (i = 0; i != count; i++) {
 130                                 char c;
 131                                 if (get_user(c, buf + i))
 132                                         return -EFAULT;
 133                                 if (c == 'V')
 134                                         expect_close = 42;
 135                         }
 136                 }
 137                 /* Well, anyhow someone wrote to us, we should
 138                                 return that favour */
 139                 acq_keepalive();
 140         }
 141         return count;
 142 }
 143 
 144 static long acq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 145 {
 146         int options, retval = -EINVAL;
 147         void __user *argp = (void __user *)arg;
 148         int __user *p = argp;
 149         static const struct watchdog_info ident = {
 150                 .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
 151                 .firmware_version = 1,
 152                 .identity = WATCHDOG_NAME,
 153         };
 154 
 155         switch (cmd) {
 156         case WDIOC_GETSUPPORT:
 157                 return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
 158 
 159         case WDIOC_GETSTATUS:
 160         case WDIOC_GETBOOTSTATUS:
 161                 return put_user(0, p);
 162 
 163         case WDIOC_SETOPTIONS:
 164         {
 165                 if (get_user(options, p))
 166                         return -EFAULT;
 167                 if (options & WDIOS_DISABLECARD) {
 168                         acq_stop();
 169                         retval = 0;
 170                 }
 171                 if (options & WDIOS_ENABLECARD) {
 172                         acq_keepalive();
 173                         retval = 0;
 174                 }
 175                 return retval;
 176         }
 177         case WDIOC_KEEPALIVE:
 178                 acq_keepalive();
 179                 return 0;
 180 
 181         case WDIOC_GETTIMEOUT:
 182                 return put_user(WATCHDOG_HEARTBEAT, p);
 183 
 184         default:
 185                 return -ENOTTY;
 186         }
 187 }
 188 
 189 static int acq_open(struct inode *inode, struct file *file)
 190 {
 191         if (test_and_set_bit(0, &acq_is_open))
 192                 return -EBUSY;
 193 
 194         if (nowayout)
 195                 __module_get(THIS_MODULE);
 196 
 197         /* Activate */
 198         acq_keepalive();
 199         return stream_open(inode, file);
 200 }
 201 
 202 static int acq_close(struct inode *inode, struct file *file)
 203 {
 204         if (expect_close == 42) {
 205                 acq_stop();
 206         } else {
 207                 pr_crit("Unexpected close, not stopping watchdog!\n");
 208                 acq_keepalive();
 209         }
 210         clear_bit(0, &acq_is_open);
 211         expect_close = 0;
 212         return 0;
 213 }
 214 
 215 /*
 216  *      Kernel Interfaces
 217  */
 218 
 219 static const struct file_operations acq_fops = {
 220         .owner          = THIS_MODULE,
 221         .llseek         = no_llseek,
 222         .write          = acq_write,
 223         .unlocked_ioctl = acq_ioctl,
 224         .open           = acq_open,
 225         .release        = acq_close,
 226 };
 227 
 228 static struct miscdevice acq_miscdev = {
 229         .minor  = WATCHDOG_MINOR,
 230         .name   = "watchdog",
 231         .fops   = &acq_fops,
 232 };
 233 
 234 /*
 235  *      Init & exit routines
 236  */
 237 
 238 static int __init acq_probe(struct platform_device *dev)
 239 {
 240         int ret;
 241 
 242         if (wdt_stop != wdt_start) {
 243                 if (!request_region(wdt_stop, 1, WATCHDOG_NAME)) {
 244                         pr_err("I/O address 0x%04x already in use\n", wdt_stop);
 245                         ret = -EIO;
 246                         goto out;
 247                 }
 248         }
 249 
 250         if (!request_region(wdt_start, 1, WATCHDOG_NAME)) {
 251                 pr_err("I/O address 0x%04x already in use\n", wdt_start);
 252                 ret = -EIO;
 253                 goto unreg_stop;
 254         }
 255         ret = misc_register(&acq_miscdev);
 256         if (ret != 0) {
 257                 pr_err("cannot register miscdev on minor=%d (err=%d)\n",
 258                        WATCHDOG_MINOR, ret);
 259                 goto unreg_regions;
 260         }
 261         pr_info("initialized. (nowayout=%d)\n", nowayout);
 262 
 263         return 0;
 264 unreg_regions:
 265         release_region(wdt_start, 1);
 266 unreg_stop:
 267         if (wdt_stop != wdt_start)
 268                 release_region(wdt_stop, 1);
 269 out:
 270         return ret;
 271 }
 272 
 273 static int acq_remove(struct platform_device *dev)
 274 {
 275         misc_deregister(&acq_miscdev);
 276         release_region(wdt_start, 1);
 277         if (wdt_stop != wdt_start)
 278                 release_region(wdt_stop, 1);
 279 
 280         return 0;
 281 }
 282 
 283 static void acq_shutdown(struct platform_device *dev)
 284 {
 285         /* Turn the WDT off if we have a soft shutdown */
 286         acq_stop();
 287 }
 288 
 289 static struct platform_driver acquirewdt_driver = {
 290         .remove         = acq_remove,
 291         .shutdown       = acq_shutdown,
 292         .driver         = {
 293                 .name   = DRV_NAME,
 294         },
 295 };
 296 
 297 static int __init acq_init(void)
 298 {
 299         int err;
 300 
 301         pr_info("WDT driver for Acquire single board computer initialising\n");
 302 
 303         acq_platform_device = platform_device_register_simple(DRV_NAME,
 304                                                                 -1, NULL, 0);
 305         if (IS_ERR(acq_platform_device))
 306                 return PTR_ERR(acq_platform_device);
 307 
 308         err = platform_driver_probe(&acquirewdt_driver, acq_probe);
 309         if (err)
 310                 goto unreg_platform_device;
 311         return 0;
 312 
 313 unreg_platform_device:
 314         platform_device_unregister(acq_platform_device);
 315         return err;
 316 }
 317 
 318 static void __exit acq_exit(void)
 319 {
 320         platform_device_unregister(acq_platform_device);
 321         platform_driver_unregister(&acquirewdt_driver);
 322         pr_info("Watchdog Module Unloaded\n");
 323 }
 324 
 325 module_init(acq_init);
 326 module_exit(acq_exit);
 327 
 328 MODULE_AUTHOR("David Woodhouse");
 329 MODULE_DESCRIPTION("Acquire Inc. Single Board Computer Watchdog Timer driver");
 330 MODULE_LICENSE("GPL");

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