0001
0002
0003
0004
0005
0006
0007
0008
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
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
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
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
0082
0083 if (!nowayout)
0084 indydog_stop();
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
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();
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");