0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0042
0043 #include <linux/module.h>
0044 #include <linux/moduleparam.h>
0045 #include <linux/types.h>
0046 #include <linux/timer.h>
0047 #include <linux/jiffies.h>
0048 #include <linux/miscdevice.h>
0049 #include <linux/watchdog.h>
0050 #include <linux/fs.h>
0051 #include <linux/ioport.h>
0052 #include <linux/notifier.h>
0053 #include <linux/reboot.h>
0054 #include <linux/init.h>
0055 #include <linux/io.h>
0056 #include <linux/uaccess.h>
0057
0058 #define OUR_NAME "w83877f_wdt"
0059
0060 #define ENABLE_W83877F_PORT 0x3F0
0061 #define ENABLE_W83877F 0x87
0062 #define DISABLE_W83877F 0xAA
0063 #define WDT_PING 0x443
0064 #define WDT_REGISTER 0x14
0065 #define WDT_ENABLE 0x9C
0066 #define WDT_DISABLE 0x8C
0067
0068
0069
0070
0071
0072
0073 #define WDT_INTERVAL (HZ/4+1)
0074
0075
0076
0077
0078
0079
0080
0081 #define WATCHDOG_TIMEOUT 30
0082
0083 static int timeout = WATCHDOG_TIMEOUT;
0084 module_param(timeout, int, 0);
0085 MODULE_PARM_DESC(timeout,
0086 "Watchdog timeout in seconds. (1<=timeout<=3600, default="
0087 __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
0088
0089
0090 static bool nowayout = WATCHDOG_NOWAYOUT;
0091 module_param(nowayout, bool, 0);
0092 MODULE_PARM_DESC(nowayout,
0093 "Watchdog cannot be stopped once started (default="
0094 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0095
0096 static void wdt_timer_ping(struct timer_list *);
0097 static DEFINE_TIMER(timer, wdt_timer_ping);
0098 static unsigned long next_heartbeat;
0099 static unsigned long wdt_is_open;
0100 static char wdt_expect_close;
0101 static DEFINE_SPINLOCK(wdt_spinlock);
0102
0103
0104
0105
0106
0107 static void wdt_timer_ping(struct timer_list *unused)
0108 {
0109
0110
0111
0112 if (time_before(jiffies, next_heartbeat)) {
0113
0114 spin_lock(&wdt_spinlock);
0115
0116
0117 inb_p(WDT_PING);
0118
0119
0120 mod_timer(&timer, jiffies + WDT_INTERVAL);
0121
0122 spin_unlock(&wdt_spinlock);
0123
0124 } else
0125 pr_warn("Heartbeat lost! Will not ping the watchdog\n");
0126 }
0127
0128
0129
0130
0131
0132 static void wdt_change(int writeval)
0133 {
0134 unsigned long flags;
0135 spin_lock_irqsave(&wdt_spinlock, flags);
0136
0137
0138 inb_p(WDT_PING);
0139
0140
0141 outb_p(ENABLE_W83877F, ENABLE_W83877F_PORT);
0142 outb_p(ENABLE_W83877F, ENABLE_W83877F_PORT);
0143
0144
0145 outb_p(WDT_REGISTER, ENABLE_W83877F_PORT);
0146 outb_p(writeval, ENABLE_W83877F_PORT+1);
0147
0148
0149 outb_p(DISABLE_W83877F, ENABLE_W83877F_PORT);
0150
0151 spin_unlock_irqrestore(&wdt_spinlock, flags);
0152 }
0153
0154 static void wdt_startup(void)
0155 {
0156 next_heartbeat = jiffies + (timeout * HZ);
0157
0158
0159 mod_timer(&timer, jiffies + WDT_INTERVAL);
0160
0161 wdt_change(WDT_ENABLE);
0162
0163 pr_info("Watchdog timer is now enabled\n");
0164 }
0165
0166 static void wdt_turnoff(void)
0167 {
0168
0169 del_timer_sync(&timer);
0170
0171 wdt_change(WDT_DISABLE);
0172
0173 pr_info("Watchdog timer is now disabled...\n");
0174 }
0175
0176 static void wdt_keepalive(void)
0177 {
0178
0179 next_heartbeat = jiffies + (timeout * HZ);
0180 }
0181
0182
0183
0184
0185
0186 static ssize_t fop_write(struct file *file, const char __user *buf,
0187 size_t count, loff_t *ppos)
0188 {
0189
0190 if (count) {
0191 if (!nowayout) {
0192 size_t ofs;
0193
0194
0195
0196 wdt_expect_close = 0;
0197
0198
0199
0200 for (ofs = 0; ofs != count; ofs++) {
0201 char c;
0202 if (get_user(c, buf + ofs))
0203 return -EFAULT;
0204 if (c == 'V')
0205 wdt_expect_close = 42;
0206 }
0207 }
0208
0209
0210 wdt_keepalive();
0211 }
0212 return count;
0213 }
0214
0215 static int fop_open(struct inode *inode, struct file *file)
0216 {
0217
0218 if (test_and_set_bit(0, &wdt_is_open))
0219 return -EBUSY;
0220
0221
0222 wdt_startup();
0223 return stream_open(inode, file);
0224 }
0225
0226 static int fop_close(struct inode *inode, struct file *file)
0227 {
0228 if (wdt_expect_close == 42)
0229 wdt_turnoff();
0230 else {
0231 del_timer(&timer);
0232 pr_crit("device file closed unexpectedly. Will not stop the WDT!\n");
0233 }
0234 clear_bit(0, &wdt_is_open);
0235 wdt_expect_close = 0;
0236 return 0;
0237 }
0238
0239 static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
0240 {
0241 void __user *argp = (void __user *)arg;
0242 int __user *p = argp;
0243 static const struct watchdog_info ident = {
0244 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT
0245 | WDIOF_MAGICCLOSE,
0246 .firmware_version = 1,
0247 .identity = "W83877F",
0248 };
0249
0250 switch (cmd) {
0251 case WDIOC_GETSUPPORT:
0252 return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
0253 case WDIOC_GETSTATUS:
0254 case WDIOC_GETBOOTSTATUS:
0255 return put_user(0, p);
0256 case WDIOC_SETOPTIONS:
0257 {
0258 int new_options, retval = -EINVAL;
0259
0260 if (get_user(new_options, p))
0261 return -EFAULT;
0262
0263 if (new_options & WDIOS_DISABLECARD) {
0264 wdt_turnoff();
0265 retval = 0;
0266 }
0267
0268 if (new_options & WDIOS_ENABLECARD) {
0269 wdt_startup();
0270 retval = 0;
0271 }
0272
0273 return retval;
0274 }
0275 case WDIOC_KEEPALIVE:
0276 wdt_keepalive();
0277 return 0;
0278 case WDIOC_SETTIMEOUT:
0279 {
0280 int new_timeout;
0281
0282 if (get_user(new_timeout, p))
0283 return -EFAULT;
0284
0285
0286 if (new_timeout < 1 || new_timeout > 3600)
0287 return -EINVAL;
0288
0289 timeout = new_timeout;
0290 wdt_keepalive();
0291 }
0292 fallthrough;
0293 case WDIOC_GETTIMEOUT:
0294 return put_user(timeout, p);
0295 default:
0296 return -ENOTTY;
0297 }
0298 }
0299
0300 static const struct file_operations wdt_fops = {
0301 .owner = THIS_MODULE,
0302 .llseek = no_llseek,
0303 .write = fop_write,
0304 .open = fop_open,
0305 .release = fop_close,
0306 .unlocked_ioctl = fop_ioctl,
0307 .compat_ioctl = compat_ptr_ioctl,
0308 };
0309
0310 static struct miscdevice wdt_miscdev = {
0311 .minor = WATCHDOG_MINOR,
0312 .name = "watchdog",
0313 .fops = &wdt_fops,
0314 };
0315
0316
0317
0318
0319
0320 static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
0321 void *unused)
0322 {
0323 if (code == SYS_DOWN || code == SYS_HALT)
0324 wdt_turnoff();
0325 return NOTIFY_DONE;
0326 }
0327
0328
0329
0330
0331
0332
0333 static struct notifier_block wdt_notifier = {
0334 .notifier_call = wdt_notify_sys,
0335 };
0336
0337 static void __exit w83877f_wdt_unload(void)
0338 {
0339 wdt_turnoff();
0340
0341
0342 misc_deregister(&wdt_miscdev);
0343
0344 unregister_reboot_notifier(&wdt_notifier);
0345 release_region(WDT_PING, 1);
0346 release_region(ENABLE_W83877F_PORT, 2);
0347 }
0348
0349 static int __init w83877f_wdt_init(void)
0350 {
0351 int rc = -EBUSY;
0352
0353 if (timeout < 1 || timeout > 3600) {
0354 timeout = WATCHDOG_TIMEOUT;
0355 pr_info("timeout value must be 1 <= x <= 3600, using %d\n",
0356 timeout);
0357 }
0358
0359 if (!request_region(ENABLE_W83877F_PORT, 2, "W83877F WDT")) {
0360 pr_err("I/O address 0x%04x already in use\n",
0361 ENABLE_W83877F_PORT);
0362 rc = -EIO;
0363 goto err_out;
0364 }
0365
0366 if (!request_region(WDT_PING, 1, "W8387FF WDT")) {
0367 pr_err("I/O address 0x%04x already in use\n", WDT_PING);
0368 rc = -EIO;
0369 goto err_out_region1;
0370 }
0371
0372 rc = register_reboot_notifier(&wdt_notifier);
0373 if (rc) {
0374 pr_err("cannot register reboot notifier (err=%d)\n", rc);
0375 goto err_out_region2;
0376 }
0377
0378 rc = misc_register(&wdt_miscdev);
0379 if (rc) {
0380 pr_err("cannot register miscdev on minor=%d (err=%d)\n",
0381 wdt_miscdev.minor, rc);
0382 goto err_out_reboot;
0383 }
0384
0385 pr_info("WDT driver for W83877F initialised. timeout=%d sec (nowayout=%d)\n",
0386 timeout, nowayout);
0387
0388 return 0;
0389
0390 err_out_reboot:
0391 unregister_reboot_notifier(&wdt_notifier);
0392 err_out_region2:
0393 release_region(WDT_PING, 1);
0394 err_out_region1:
0395 release_region(ENABLE_W83877F_PORT, 2);
0396 err_out:
0397 return rc;
0398 }
0399
0400 module_init(w83877f_wdt_init);
0401 module_exit(w83877f_wdt_unload);
0402
0403 MODULE_AUTHOR("Scott and Bill Jennings");
0404 MODULE_DESCRIPTION("Driver for watchdog timer in w83877f chip");
0405 MODULE_LICENSE("GPL");