Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  SBC EPX C3 0.1  A Hardware Watchdog Device for the Winsystems EPX-C3
0004  *  single board computer
0005  *
0006  *  (c) Copyright 2006 Calin A. Culianu <calin@ajvar.org>, All Rights
0007  *  Reserved.
0008  *
0009  *  based on softdog.c by Alan Cox <alan@lxorguk.ukuu.org.uk>
0010  */
0011 
0012 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0013 
0014 #include <linux/module.h>
0015 #include <linux/moduleparam.h>
0016 #include <linux/types.h>
0017 #include <linux/kernel.h>
0018 #include <linux/fs.h>
0019 #include <linux/mm.h>
0020 #include <linux/miscdevice.h>
0021 #include <linux/watchdog.h>
0022 #include <linux/notifier.h>
0023 #include <linux/reboot.h>
0024 #include <linux/init.h>
0025 #include <linux/ioport.h>
0026 #include <linux/uaccess.h>
0027 #include <linux/io.h>
0028 
0029 static int epx_c3_alive;
0030 
0031 #define WATCHDOG_TIMEOUT 1      /* 1 sec default timeout */
0032 
0033 static bool nowayout = WATCHDOG_NOWAYOUT;
0034 module_param(nowayout, bool, 0);
0035 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
0036                     __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0037 
0038 #define EPXC3_WATCHDOG_CTL_REG 0x1ee /* write 1 to enable, 0 to disable */
0039 #define EPXC3_WATCHDOG_PET_REG 0x1ef /* write anything to pet once enabled */
0040 
0041 static void epx_c3_start(void)
0042 {
0043     outb(1, EPXC3_WATCHDOG_CTL_REG);
0044 }
0045 
0046 static void epx_c3_stop(void)
0047 {
0048 
0049     outb(0, EPXC3_WATCHDOG_CTL_REG);
0050 
0051     pr_info("Stopped watchdog timer\n");
0052 }
0053 
0054 static void epx_c3_pet(void)
0055 {
0056     outb(1, EPXC3_WATCHDOG_PET_REG);
0057 }
0058 
0059 /*
0060  *  Allow only one person to hold it open
0061  */
0062 static int epx_c3_open(struct inode *inode, struct file *file)
0063 {
0064     if (epx_c3_alive)
0065         return -EBUSY;
0066 
0067     if (nowayout)
0068         __module_get(THIS_MODULE);
0069 
0070     /* Activate timer */
0071     epx_c3_start();
0072     epx_c3_pet();
0073 
0074     epx_c3_alive = 1;
0075     pr_info("Started watchdog timer\n");
0076 
0077     return stream_open(inode, file);
0078 }
0079 
0080 static int epx_c3_release(struct inode *inode, struct file *file)
0081 {
0082     /* Shut off the timer.
0083      * Lock it in if it's a module and we defined ...NOWAYOUT */
0084     if (!nowayout)
0085         epx_c3_stop();      /* Turn the WDT off */
0086 
0087     epx_c3_alive = 0;
0088 
0089     return 0;
0090 }
0091 
0092 static ssize_t epx_c3_write(struct file *file, const char __user *data,
0093             size_t len, loff_t *ppos)
0094 {
0095     /* Refresh the timer. */
0096     if (len)
0097         epx_c3_pet();
0098     return len;
0099 }
0100 
0101 static long epx_c3_ioctl(struct file *file, unsigned int cmd,
0102                         unsigned long arg)
0103 {
0104     int options, retval = -EINVAL;
0105     int __user *argp = (void __user *)arg;
0106     static const struct watchdog_info ident = {
0107         .options        = WDIOF_KEEPALIVEPING,
0108         .firmware_version   = 0,
0109         .identity       = "Winsystems EPX-C3 H/W Watchdog",
0110     };
0111 
0112     switch (cmd) {
0113     case WDIOC_GETSUPPORT:
0114         if (copy_to_user(argp, &ident, sizeof(ident)))
0115             return -EFAULT;
0116         return 0;
0117     case WDIOC_GETSTATUS:
0118     case WDIOC_GETBOOTSTATUS:
0119         return put_user(0, argp);
0120     case WDIOC_SETOPTIONS:
0121         if (get_user(options, argp))
0122             return -EFAULT;
0123 
0124         if (options & WDIOS_DISABLECARD) {
0125             epx_c3_stop();
0126             retval = 0;
0127         }
0128 
0129         if (options & WDIOS_ENABLECARD) {
0130             epx_c3_start();
0131             retval = 0;
0132         }
0133 
0134         return retval;
0135     case WDIOC_KEEPALIVE:
0136         epx_c3_pet();
0137         return 0;
0138     case WDIOC_GETTIMEOUT:
0139         return put_user(WATCHDOG_TIMEOUT, argp);
0140     default:
0141         return -ENOTTY;
0142     }
0143 }
0144 
0145 static int epx_c3_notify_sys(struct notifier_block *this, unsigned long code,
0146                 void *unused)
0147 {
0148     if (code == SYS_DOWN || code == SYS_HALT)
0149         epx_c3_stop();      /* Turn the WDT off */
0150 
0151     return NOTIFY_DONE;
0152 }
0153 
0154 static const struct file_operations epx_c3_fops = {
0155     .owner      = THIS_MODULE,
0156     .llseek     = no_llseek,
0157     .write      = epx_c3_write,
0158     .unlocked_ioctl = epx_c3_ioctl,
0159     .compat_ioctl   = compat_ptr_ioctl,
0160     .open       = epx_c3_open,
0161     .release    = epx_c3_release,
0162 };
0163 
0164 static struct miscdevice epx_c3_miscdev = {
0165     .minor      = WATCHDOG_MINOR,
0166     .name       = "watchdog",
0167     .fops       = &epx_c3_fops,
0168 };
0169 
0170 static struct notifier_block epx_c3_notifier = {
0171     .notifier_call = epx_c3_notify_sys,
0172 };
0173 
0174 static int __init watchdog_init(void)
0175 {
0176     int ret;
0177 
0178     if (!request_region(EPXC3_WATCHDOG_CTL_REG, 2, "epxc3_watchdog"))
0179         return -EBUSY;
0180 
0181     ret = register_reboot_notifier(&epx_c3_notifier);
0182     if (ret) {
0183         pr_err("cannot register reboot notifier (err=%d)\n", ret);
0184         goto out;
0185     }
0186 
0187     ret = misc_register(&epx_c3_miscdev);
0188     if (ret) {
0189         pr_err("cannot register miscdev on minor=%d (err=%d)\n",
0190                WATCHDOG_MINOR, ret);
0191         unregister_reboot_notifier(&epx_c3_notifier);
0192         goto out;
0193     }
0194 
0195     pr_info("Hardware Watchdog Timer for Winsystems EPX-C3 SBC: 0.1\n");
0196 
0197     return 0;
0198 
0199 out:
0200     release_region(EPXC3_WATCHDOG_CTL_REG, 2);
0201     return ret;
0202 }
0203 
0204 static void __exit watchdog_exit(void)
0205 {
0206     misc_deregister(&epx_c3_miscdev);
0207     unregister_reboot_notifier(&epx_c3_notifier);
0208     release_region(EPXC3_WATCHDOG_CTL_REG, 2);
0209 }
0210 
0211 module_init(watchdog_init);
0212 module_exit(watchdog_exit);
0213 
0214 MODULE_AUTHOR("Calin A. Culianu <calin@ajvar.org>");
0215 MODULE_DESCRIPTION("Hardware Watchdog Device for Winsystems EPX-C3 SBC.  "
0216     "Note that there is no way to probe for this device -- "
0217     "so only use it if you are *sure* you are running on this specific "
0218     "SBC system from Winsystems!  It writes to IO ports 0x1ee and 0x1ef!");
0219 MODULE_LICENSE("GPL");