Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  IndyDog 0.3 A Hardware Watchdog Device for SGI IP22
0004  *
0005  *  (c) Copyright 2002 Guido Guenther <agx@sigxcpu.org>,
0006  *                      All Rights Reserved.
0007  *
0008  *  based on softdog.c by Alan Cox <alan@lxorguk.ukuu.org.uk>
0009  */
0010 
0011 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0012 
0013 #include <linux/module.h>
0014 #include <linux/moduleparam.h>
0015 #include <linux/types.h>
0016 #include <linux/kernel.h>
0017 #include <linux/fs.h>
0018 #include <linux/mm.h>
0019 #include <linux/miscdevice.h>
0020 #include <linux/watchdog.h>
0021 #include <linux/notifier.h>
0022 #include <linux/reboot.h>
0023 #include <linux/init.h>
0024 #include <linux/uaccess.h>
0025 #include <asm/sgi/mc.h>
0026 
0027 static unsigned long indydog_alive;
0028 static DEFINE_SPINLOCK(indydog_lock);
0029 
0030 #define WATCHDOG_TIMEOUT 30     /* 30 sec default timeout */
0031 
0032 static bool nowayout = WATCHDOG_NOWAYOUT;
0033 module_param(nowayout, bool, 0);
0034 MODULE_PARM_DESC(nowayout,
0035         "Watchdog cannot be stopped once started (default="
0036                 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0037 
0038 static void indydog_start(void)
0039 {
0040     spin_lock(&indydog_lock);
0041     sgimc->cpuctrl0 |= SGIMC_CCTRL0_WDOG;
0042     spin_unlock(&indydog_lock);
0043 }
0044 
0045 static void indydog_stop(void)
0046 {
0047     spin_lock(&indydog_lock);
0048     sgimc->cpuctrl0 &= ~SGIMC_CCTRL0_WDOG;
0049     spin_unlock(&indydog_lock);
0050 
0051     pr_info("Stopped watchdog timer\n");
0052 }
0053 
0054 static void indydog_ping(void)
0055 {
0056     sgimc->watchdogt = 0;
0057 }
0058 
0059 /*
0060  *  Allow only one person to hold it open
0061  */
0062 static int indydog_open(struct inode *inode, struct file *file)
0063 {
0064     if (test_and_set_bit(0, &indydog_alive))
0065         return -EBUSY;
0066 
0067     if (nowayout)
0068         __module_get(THIS_MODULE);
0069 
0070     /* Activate timer */
0071     indydog_start();
0072     indydog_ping();
0073 
0074     pr_info("Started watchdog timer\n");
0075 
0076     return stream_open(inode, file);
0077 }
0078 
0079 static int indydog_release(struct inode *inode, struct file *file)
0080 {
0081     /* Shut off the timer.
0082      * Lock it in if it's a module and we defined ...NOWAYOUT */
0083     if (!nowayout)
0084         indydog_stop();     /* Turn the WDT off */
0085     clear_bit(0, &indydog_alive);
0086     return 0;
0087 }
0088 
0089 static ssize_t indydog_write(struct file *file, const char *data,
0090                         size_t len, loff_t *ppos)
0091 {
0092     /* Refresh the timer. */
0093     if (len)
0094         indydog_ping();
0095     return len;
0096 }
0097 
0098 static long indydog_ioctl(struct file *file, unsigned int cmd,
0099                             unsigned long arg)
0100 {
0101     int options, retval = -EINVAL;
0102     static const struct watchdog_info ident = {
0103         .options        = WDIOF_KEEPALIVEPING,
0104         .firmware_version   = 0,
0105         .identity       = "Hardware Watchdog for SGI IP22",
0106     };
0107 
0108     switch (cmd) {
0109     case WDIOC_GETSUPPORT:
0110         if (copy_to_user((struct watchdog_info *)arg,
0111                  &ident, sizeof(ident)))
0112             return -EFAULT;
0113         return 0;
0114     case WDIOC_GETSTATUS:
0115     case WDIOC_GETBOOTSTATUS:
0116         return put_user(0, (int *)arg);
0117     case WDIOC_SETOPTIONS:
0118     {
0119         if (get_user(options, (int *)arg))
0120             return -EFAULT;
0121         if (options & WDIOS_DISABLECARD) {
0122             indydog_stop();
0123             retval = 0;
0124         }
0125         if (options & WDIOS_ENABLECARD) {
0126             indydog_start();
0127             retval = 0;
0128         }
0129         return retval;
0130     }
0131     case WDIOC_KEEPALIVE:
0132         indydog_ping();
0133         return 0;
0134     case WDIOC_GETTIMEOUT:
0135         return put_user(WATCHDOG_TIMEOUT, (int *)arg);
0136     default:
0137         return -ENOTTY;
0138     }
0139 }
0140 
0141 static int indydog_notify_sys(struct notifier_block *this,
0142                     unsigned long code, void *unused)
0143 {
0144     if (code == SYS_DOWN || code == SYS_HALT)
0145         indydog_stop();     /* Turn the WDT off */
0146 
0147     return NOTIFY_DONE;
0148 }
0149 
0150 static const struct file_operations indydog_fops = {
0151     .owner      = THIS_MODULE,
0152     .llseek     = no_llseek,
0153     .write      = indydog_write,
0154     .unlocked_ioctl = indydog_ioctl,
0155     .compat_ioctl   = compat_ptr_ioctl,
0156     .open       = indydog_open,
0157     .release    = indydog_release,
0158 };
0159 
0160 static struct miscdevice indydog_miscdev = {
0161     .minor      = WATCHDOG_MINOR,
0162     .name       = "watchdog",
0163     .fops       = &indydog_fops,
0164 };
0165 
0166 static struct notifier_block indydog_notifier = {
0167     .notifier_call = indydog_notify_sys,
0168 };
0169 
0170 static int __init watchdog_init(void)
0171 {
0172     int ret;
0173 
0174     ret = register_reboot_notifier(&indydog_notifier);
0175     if (ret) {
0176         pr_err("cannot register reboot notifier (err=%d)\n", ret);
0177         return ret;
0178     }
0179 
0180     ret = misc_register(&indydog_miscdev);
0181     if (ret) {
0182         pr_err("cannot register miscdev on minor=%d (err=%d)\n",
0183                WATCHDOG_MINOR, ret);
0184         unregister_reboot_notifier(&indydog_notifier);
0185         return ret;
0186     }
0187 
0188     pr_info("Hardware Watchdog Timer for SGI IP22: 0.3\n");
0189 
0190     return 0;
0191 }
0192 
0193 static void __exit watchdog_exit(void)
0194 {
0195     misc_deregister(&indydog_miscdev);
0196     unregister_reboot_notifier(&indydog_notifier);
0197 }
0198 
0199 module_init(watchdog_init);
0200 module_exit(watchdog_exit);
0201 
0202 MODULE_AUTHOR("Guido Guenther <agx@sigxcpu.org>");
0203 MODULE_DESCRIPTION("Hardware Watchdog Device for SGI IP22");
0204 MODULE_LICENSE("GPL");