Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  *  ICP Wafer 5823 Single Board Computer WDT driver
0004  *  http://www.icpamerica.com/wafer_5823.php
0005  *  May also work on other similar models
0006  *
0007  *  (c) Copyright 2002 Justin Cormack <justin@street-vision.com>
0008  *
0009  *  Release 0.02
0010  *
0011  *  Based on advantechwdt.c which is based on wdt.c.
0012  *  Original copyright messages:
0013  *
0014  *  (c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>,
0015  *                      All Rights Reserved.
0016  *
0017  *  Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
0018  *  warranty for any of this software. This material is provided
0019  *  "AS-IS" and at no charge.
0020  *
0021  *  (c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
0022  *
0023  */
0024 
0025 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0026 
0027 #include <linux/module.h>
0028 #include <linux/moduleparam.h>
0029 #include <linux/miscdevice.h>
0030 #include <linux/watchdog.h>
0031 #include <linux/fs.h>
0032 #include <linux/ioport.h>
0033 #include <linux/notifier.h>
0034 #include <linux/reboot.h>
0035 #include <linux/init.h>
0036 #include <linux/spinlock.h>
0037 #include <linux/io.h>
0038 #include <linux/uaccess.h>
0039 
0040 #define WATCHDOG_NAME "Wafer 5823 WDT"
0041 #define PFX WATCHDOG_NAME ": "
0042 #define WD_TIMO 60          /* 60 sec default timeout */
0043 
0044 static unsigned long wafwdt_is_open;
0045 static char expect_close;
0046 static DEFINE_SPINLOCK(wafwdt_lock);
0047 
0048 /*
0049  *  You must set these - there is no sane way to probe for this board.
0050  *
0051  *  To enable, write the timeout value in seconds (1 to 255) to I/O
0052  *  port WDT_START, then read the port to start the watchdog. To pat
0053  *  the dog, read port WDT_STOP to stop the timer, then read WDT_START
0054  *  to restart it again.
0055  */
0056 
0057 static int wdt_stop = 0x843;
0058 static int wdt_start = 0x443;
0059 
0060 static int timeout = WD_TIMO;  /* in seconds */
0061 module_param(timeout, int, 0);
0062 MODULE_PARM_DESC(timeout,
0063         "Watchdog timeout in seconds. 1 <= timeout <= 255, default="
0064                 __MODULE_STRING(WD_TIMO) ".");
0065 
0066 static bool nowayout = WATCHDOG_NOWAYOUT;
0067 module_param(nowayout, bool, 0);
0068 MODULE_PARM_DESC(nowayout,
0069         "Watchdog cannot be stopped once started (default="
0070                 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0071 
0072 static void wafwdt_ping(void)
0073 {
0074     /* pat watchdog */
0075     spin_lock(&wafwdt_lock);
0076     inb_p(wdt_stop);
0077     inb_p(wdt_start);
0078     spin_unlock(&wafwdt_lock);
0079 }
0080 
0081 static void wafwdt_start(void)
0082 {
0083     /* start up watchdog */
0084     outb_p(timeout, wdt_start);
0085     inb_p(wdt_start);
0086 }
0087 
0088 static void wafwdt_stop(void)
0089 {
0090     /* stop watchdog */
0091     inb_p(wdt_stop);
0092 }
0093 
0094 static ssize_t wafwdt_write(struct file *file, const char __user *buf,
0095                         size_t count, loff_t *ppos)
0096 {
0097     /* See if we got the magic character 'V' and reload the timer */
0098     if (count) {
0099         if (!nowayout) {
0100             size_t i;
0101 
0102             /* In case it was set long ago */
0103             expect_close = 0;
0104 
0105             /* scan to see whether or not we got the magic
0106                character */
0107             for (i = 0; i != count; i++) {
0108                 char c;
0109                 if (get_user(c, buf + i))
0110                     return -EFAULT;
0111                 if (c == 'V')
0112                     expect_close = 42;
0113             }
0114         }
0115         /* Well, anyhow someone wrote to us, we should
0116            return that favour */
0117         wafwdt_ping();
0118     }
0119     return count;
0120 }
0121 
0122 static long wafwdt_ioctl(struct file *file, unsigned int cmd,
0123                             unsigned long arg)
0124 {
0125     int new_timeout;
0126     void __user *argp = (void __user *)arg;
0127     int __user *p = argp;
0128     static const struct watchdog_info ident = {
0129         .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
0130                             WDIOF_MAGICCLOSE,
0131         .firmware_version = 1,
0132         .identity = "Wafer 5823 WDT",
0133     };
0134 
0135     switch (cmd) {
0136     case WDIOC_GETSUPPORT:
0137         if (copy_to_user(argp, &ident, sizeof(ident)))
0138             return -EFAULT;
0139         break;
0140 
0141     case WDIOC_GETSTATUS:
0142     case WDIOC_GETBOOTSTATUS:
0143         return put_user(0, p);
0144 
0145     case WDIOC_SETOPTIONS:
0146     {
0147         int options, retval = -EINVAL;
0148 
0149         if (get_user(options, p))
0150             return -EFAULT;
0151 
0152         if (options & WDIOS_DISABLECARD) {
0153             wafwdt_stop();
0154             retval = 0;
0155         }
0156 
0157         if (options & WDIOS_ENABLECARD) {
0158             wafwdt_start();
0159             retval = 0;
0160         }
0161 
0162         return retval;
0163     }
0164 
0165     case WDIOC_KEEPALIVE:
0166         wafwdt_ping();
0167         break;
0168 
0169     case WDIOC_SETTIMEOUT:
0170         if (get_user(new_timeout, p))
0171             return -EFAULT;
0172         if ((new_timeout < 1) || (new_timeout > 255))
0173             return -EINVAL;
0174         timeout = new_timeout;
0175         wafwdt_stop();
0176         wafwdt_start();
0177         fallthrough;
0178     case WDIOC_GETTIMEOUT:
0179         return put_user(timeout, p);
0180 
0181     default:
0182         return -ENOTTY;
0183     }
0184     return 0;
0185 }
0186 
0187 static int wafwdt_open(struct inode *inode, struct file *file)
0188 {
0189     if (test_and_set_bit(0, &wafwdt_is_open))
0190         return -EBUSY;
0191 
0192     /*
0193      *      Activate
0194      */
0195     wafwdt_start();
0196     return stream_open(inode, file);
0197 }
0198 
0199 static int wafwdt_close(struct inode *inode, struct file *file)
0200 {
0201     if (expect_close == 42)
0202         wafwdt_stop();
0203     else {
0204         pr_crit("WDT device closed unexpectedly.  WDT will not stop!\n");
0205         wafwdt_ping();
0206     }
0207     clear_bit(0, &wafwdt_is_open);
0208     expect_close = 0;
0209     return 0;
0210 }
0211 
0212 /*
0213  *  Notifier for system down
0214  */
0215 
0216 static int wafwdt_notify_sys(struct notifier_block *this, unsigned long code,
0217                                 void *unused)
0218 {
0219     if (code == SYS_DOWN || code == SYS_HALT)
0220         wafwdt_stop();
0221     return NOTIFY_DONE;
0222 }
0223 
0224 /*
0225  *  Kernel Interfaces
0226  */
0227 
0228 static const struct file_operations wafwdt_fops = {
0229     .owner      = THIS_MODULE,
0230     .llseek     = no_llseek,
0231     .write      = wafwdt_write,
0232     .unlocked_ioctl = wafwdt_ioctl,
0233     .compat_ioctl   = compat_ptr_ioctl,
0234     .open       = wafwdt_open,
0235     .release    = wafwdt_close,
0236 };
0237 
0238 static struct miscdevice wafwdt_miscdev = {
0239     .minor  = WATCHDOG_MINOR,
0240     .name   = "watchdog",
0241     .fops   = &wafwdt_fops,
0242 };
0243 
0244 /*
0245  *  The WDT needs to learn about soft shutdowns in order to
0246  *  turn the timebomb registers off.
0247  */
0248 
0249 static struct notifier_block wafwdt_notifier = {
0250     .notifier_call = wafwdt_notify_sys,
0251 };
0252 
0253 static int __init wafwdt_init(void)
0254 {
0255     int ret;
0256 
0257     pr_info("WDT driver for Wafer 5823 single board computer initialising\n");
0258 
0259     if (timeout < 1 || timeout > 255) {
0260         timeout = WD_TIMO;
0261         pr_info("timeout value must be 1 <= x <= 255, using %d\n",
0262             timeout);
0263     }
0264 
0265     if (wdt_stop != wdt_start) {
0266         if (!request_region(wdt_stop, 1, "Wafer 5823 WDT")) {
0267             pr_err("I/O address 0x%04x already in use\n", wdt_stop);
0268             ret = -EIO;
0269             goto error;
0270         }
0271     }
0272 
0273     if (!request_region(wdt_start, 1, "Wafer 5823 WDT")) {
0274         pr_err("I/O address 0x%04x already in use\n", wdt_start);
0275         ret = -EIO;
0276         goto error2;
0277     }
0278 
0279     ret = register_reboot_notifier(&wafwdt_notifier);
0280     if (ret != 0) {
0281         pr_err("cannot register reboot notifier (err=%d)\n", ret);
0282         goto error3;
0283     }
0284 
0285     ret = misc_register(&wafwdt_miscdev);
0286     if (ret != 0) {
0287         pr_err("cannot register miscdev on minor=%d (err=%d)\n",
0288                WATCHDOG_MINOR, ret);
0289         goto error4;
0290     }
0291 
0292     pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
0293         timeout, nowayout);
0294 
0295     return ret;
0296 error4:
0297     unregister_reboot_notifier(&wafwdt_notifier);
0298 error3:
0299     release_region(wdt_start, 1);
0300 error2:
0301     if (wdt_stop != wdt_start)
0302         release_region(wdt_stop, 1);
0303 error:
0304     return ret;
0305 }
0306 
0307 static void __exit wafwdt_exit(void)
0308 {
0309     misc_deregister(&wafwdt_miscdev);
0310     unregister_reboot_notifier(&wafwdt_notifier);
0311     if (wdt_stop != wdt_start)
0312         release_region(wdt_stop, 1);
0313     release_region(wdt_start, 1);
0314 }
0315 
0316 module_init(wafwdt_init);
0317 module_exit(wafwdt_exit);
0318 
0319 MODULE_AUTHOR("Justin Cormack");
0320 MODULE_DESCRIPTION("ICP Wafer 5823 Single Board Computer WDT driver");
0321 MODULE_LICENSE("GPL");
0322 
0323 /* end of wafer5823wdt.c */