Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  *  IB700 Single Board Computer WDT driver
0004  *
0005  *  (c) Copyright 2001 Charles Howes <chowes@vsol.net>
0006  *
0007  *  Based on advantechwdt.c which is based on acquirewdt.c which
0008  *  is based on wdt.c.
0009  *
0010  *  (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
0011  *
0012  *  Based on acquirewdt.c which is based on wdt.c.
0013  *  Original copyright messages:
0014  *
0015  *  (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
0016  *                      All Rights Reserved.
0017  *
0018  *  Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
0019  *  warranty for any of this software. This material is provided
0020  *  "AS-IS" and at no charge.
0021  *
0022  *  (c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
0023  *
0024  *  14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
0025  *       Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
0026  *       Added timeout module option to override default
0027  *
0028  */
0029 
0030 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0031 
0032 #include <linux/module.h>
0033 #include <linux/types.h>
0034 #include <linux/miscdevice.h>
0035 #include <linux/watchdog.h>
0036 #include <linux/ioport.h>
0037 #include <linux/fs.h>
0038 #include <linux/init.h>
0039 #include <linux/spinlock.h>
0040 #include <linux/moduleparam.h>
0041 #include <linux/platform_device.h>
0042 #include <linux/io.h>
0043 #include <linux/uaccess.h>
0044 
0045 
0046 static struct platform_device *ibwdt_platform_device;
0047 static unsigned long ibwdt_is_open;
0048 static DEFINE_SPINLOCK(ibwdt_lock);
0049 static char expect_close;
0050 
0051 /* Module information */
0052 #define DRV_NAME "ib700wdt"
0053 
0054 /*
0055  *
0056  * Watchdog Timer Configuration
0057  *
0058  * The function of the watchdog timer is to reset the system
0059  * automatically and is defined at I/O port 0443H.  To enable the
0060  * watchdog timer and allow the system to reset, write I/O port 0443H.
0061  * To disable the timer, write I/O port 0441H for the system to stop the
0062  * watchdog function.  The timer has a tolerance of 20% for its
0063  * intervals.
0064  *
0065  * The following describes how the timer should be programmed.
0066  *
0067  * Enabling Watchdog:
0068  * MOV AX,000FH (Choose the values from 0 to F)
0069  * MOV DX,0443H
0070  * OUT DX,AX
0071  *
0072  * Disabling Watchdog:
0073  * MOV AX,000FH (Any value is fine.)
0074  * MOV DX,0441H
0075  * OUT DX,AX
0076  *
0077  * Watchdog timer control table:
0078  * Level   Value  Time/sec | Level Value Time/sec
0079  *   1       F       0     |   9     7      16
0080  *   2       E       2     |   10    6      18
0081  *   3       D       4     |   11    5      20
0082  *   4       C       6     |   12    4      22
0083  *   5       B       8     |   13    3      24
0084  *   6       A       10    |   14    2      26
0085  *   7       9       12    |   15    1      28
0086  *   8       8       14    |   16    0      30
0087  *
0088  */
0089 
0090 #define WDT_STOP 0x441
0091 #define WDT_START 0x443
0092 
0093 /* Default timeout */
0094 #define WATCHDOG_TIMEOUT 30     /* 30 seconds +/- 20% */
0095 static int timeout = WATCHDOG_TIMEOUT;  /* in seconds */
0096 module_param(timeout, int, 0);
0097 MODULE_PARM_DESC(timeout,
0098     "Watchdog timeout in seconds. 0<= timeout <=30, default="
0099         __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
0100 
0101 static bool nowayout = WATCHDOG_NOWAYOUT;
0102 module_param(nowayout, bool, 0);
0103 MODULE_PARM_DESC(nowayout,
0104         "Watchdog cannot be stopped once started (default="
0105                 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0106 
0107 
0108 /*
0109  *  Watchdog Operations
0110  */
0111 
0112 static void ibwdt_ping(void)
0113 {
0114     int wd_margin = 15 - ((timeout + 1) / 2);
0115 
0116     spin_lock(&ibwdt_lock);
0117 
0118     /* Write a watchdog value */
0119     outb_p(wd_margin, WDT_START);
0120 
0121     spin_unlock(&ibwdt_lock);
0122 }
0123 
0124 static void ibwdt_disable(void)
0125 {
0126     spin_lock(&ibwdt_lock);
0127     outb_p(0, WDT_STOP);
0128     spin_unlock(&ibwdt_lock);
0129 }
0130 
0131 static int ibwdt_set_heartbeat(int t)
0132 {
0133     if (t < 0 || t > 30)
0134         return -EINVAL;
0135 
0136     timeout = t;
0137     return 0;
0138 }
0139 
0140 /*
0141  *  /dev/watchdog handling
0142  */
0143 
0144 static ssize_t ibwdt_write(struct file *file, const char __user *buf,
0145                         size_t count, loff_t *ppos)
0146 {
0147     if (count) {
0148         if (!nowayout) {
0149             size_t i;
0150 
0151             /* In case it was set long ago */
0152             expect_close = 0;
0153 
0154             for (i = 0; i != count; i++) {
0155                 char c;
0156                 if (get_user(c, buf + i))
0157                     return -EFAULT;
0158                 if (c == 'V')
0159                     expect_close = 42;
0160             }
0161         }
0162         ibwdt_ping();
0163     }
0164     return count;
0165 }
0166 
0167 static long ibwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
0168 {
0169     int new_margin;
0170     void __user *argp = (void __user *)arg;
0171     int __user *p = argp;
0172 
0173     static const struct watchdog_info ident = {
0174         .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT
0175                             | WDIOF_MAGICCLOSE,
0176         .firmware_version = 1,
0177         .identity = "IB700 WDT",
0178     };
0179 
0180     switch (cmd) {
0181     case WDIOC_GETSUPPORT:
0182         if (copy_to_user(argp, &ident, sizeof(ident)))
0183             return -EFAULT;
0184         break;
0185 
0186     case WDIOC_GETSTATUS:
0187     case WDIOC_GETBOOTSTATUS:
0188         return put_user(0, p);
0189 
0190     case WDIOC_SETOPTIONS:
0191     {
0192         int options, retval = -EINVAL;
0193 
0194         if (get_user(options, p))
0195             return -EFAULT;
0196 
0197         if (options & WDIOS_DISABLECARD) {
0198             ibwdt_disable();
0199             retval = 0;
0200         }
0201         if (options & WDIOS_ENABLECARD) {
0202             ibwdt_ping();
0203             retval = 0;
0204         }
0205         return retval;
0206     }
0207     case WDIOC_KEEPALIVE:
0208         ibwdt_ping();
0209         break;
0210 
0211     case WDIOC_SETTIMEOUT:
0212         if (get_user(new_margin, p))
0213             return -EFAULT;
0214         if (ibwdt_set_heartbeat(new_margin))
0215             return -EINVAL;
0216         ibwdt_ping();
0217         fallthrough;
0218 
0219     case WDIOC_GETTIMEOUT:
0220         return put_user(timeout, p);
0221 
0222     default:
0223         return -ENOTTY;
0224     }
0225     return 0;
0226 }
0227 
0228 static int ibwdt_open(struct inode *inode, struct file *file)
0229 {
0230     if (test_and_set_bit(0, &ibwdt_is_open))
0231         return -EBUSY;
0232     if (nowayout)
0233         __module_get(THIS_MODULE);
0234 
0235     /* Activate */
0236     ibwdt_ping();
0237     return stream_open(inode, file);
0238 }
0239 
0240 static int ibwdt_close(struct inode *inode, struct file *file)
0241 {
0242     if (expect_close == 42) {
0243         ibwdt_disable();
0244     } else {
0245         pr_crit("WDT device closed unexpectedly.  WDT will not stop!\n");
0246         ibwdt_ping();
0247     }
0248     clear_bit(0, &ibwdt_is_open);
0249     expect_close = 0;
0250     return 0;
0251 }
0252 
0253 /*
0254  *  Kernel Interfaces
0255  */
0256 
0257 static const struct file_operations ibwdt_fops = {
0258     .owner      = THIS_MODULE,
0259     .llseek     = no_llseek,
0260     .write      = ibwdt_write,
0261     .unlocked_ioctl = ibwdt_ioctl,
0262     .compat_ioctl   = compat_ptr_ioctl,
0263     .open       = ibwdt_open,
0264     .release    = ibwdt_close,
0265 };
0266 
0267 static struct miscdevice ibwdt_miscdev = {
0268     .minor = WATCHDOG_MINOR,
0269     .name = "watchdog",
0270     .fops = &ibwdt_fops,
0271 };
0272 
0273 /*
0274  *  Init & exit routines
0275  */
0276 
0277 static int __init ibwdt_probe(struct platform_device *dev)
0278 {
0279     int res;
0280 
0281 #if WDT_START != WDT_STOP
0282     if (!request_region(WDT_STOP, 1, "IB700 WDT")) {
0283         pr_err("STOP method I/O %X is not available\n", WDT_STOP);
0284         res = -EIO;
0285         goto out_nostopreg;
0286     }
0287 #endif
0288 
0289     if (!request_region(WDT_START, 1, "IB700 WDT")) {
0290         pr_err("START method I/O %X is not available\n", WDT_START);
0291         res = -EIO;
0292         goto out_nostartreg;
0293     }
0294 
0295     /* Check that the heartbeat value is within it's range ;
0296      * if not reset to the default */
0297     if (ibwdt_set_heartbeat(timeout)) {
0298         ibwdt_set_heartbeat(WATCHDOG_TIMEOUT);
0299         pr_info("timeout value must be 0<=x<=30, using %d\n", timeout);
0300     }
0301 
0302     res = misc_register(&ibwdt_miscdev);
0303     if (res) {
0304         pr_err("failed to register misc device\n");
0305         goto out_nomisc;
0306     }
0307     return 0;
0308 
0309 out_nomisc:
0310     release_region(WDT_START, 1);
0311 out_nostartreg:
0312 #if WDT_START != WDT_STOP
0313     release_region(WDT_STOP, 1);
0314 #endif
0315 out_nostopreg:
0316     return res;
0317 }
0318 
0319 static int ibwdt_remove(struct platform_device *dev)
0320 {
0321     misc_deregister(&ibwdt_miscdev);
0322     release_region(WDT_START, 1);
0323 #if WDT_START != WDT_STOP
0324     release_region(WDT_STOP, 1);
0325 #endif
0326     return 0;
0327 }
0328 
0329 static void ibwdt_shutdown(struct platform_device *dev)
0330 {
0331     /* Turn the WDT off if we have a soft shutdown */
0332     ibwdt_disable();
0333 }
0334 
0335 static struct platform_driver ibwdt_driver = {
0336     .remove     = ibwdt_remove,
0337     .shutdown   = ibwdt_shutdown,
0338     .driver     = {
0339         .name   = DRV_NAME,
0340     },
0341 };
0342 
0343 static int __init ibwdt_init(void)
0344 {
0345     int err;
0346 
0347     pr_info("WDT driver for IB700 single board computer initialising\n");
0348 
0349     ibwdt_platform_device = platform_device_register_simple(DRV_NAME,
0350                                 -1, NULL, 0);
0351     if (IS_ERR(ibwdt_platform_device))
0352         return PTR_ERR(ibwdt_platform_device);
0353 
0354     err = platform_driver_probe(&ibwdt_driver, ibwdt_probe);
0355     if (err)
0356         goto unreg_platform_device;
0357 
0358     return 0;
0359 
0360 unreg_platform_device:
0361     platform_device_unregister(ibwdt_platform_device);
0362     return err;
0363 }
0364 
0365 static void __exit ibwdt_exit(void)
0366 {
0367     platform_device_unregister(ibwdt_platform_device);
0368     platform_driver_unregister(&ibwdt_driver);
0369     pr_info("Watchdog Module Unloaded\n");
0370 }
0371 
0372 module_init(ibwdt_init);
0373 module_exit(ibwdt_exit);
0374 
0375 MODULE_AUTHOR("Charles Howes <chowes@vsol.net>");
0376 MODULE_DESCRIPTION("IB700 SBC watchdog driver");
0377 MODULE_LICENSE("GPL");
0378 
0379 /* end of ib700wdt.c */