0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0014
0015 #include <linux/module.h>
0016 #include <linux/moduleparam.h>
0017 #include <linux/types.h>
0018 #include <linux/kernel.h>
0019 #include <linux/fs.h>
0020 #include <linux/miscdevice.h>
0021 #include <linux/init.h>
0022 #include <linux/ioport.h>
0023 #include <linux/watchdog.h>
0024 #include <linux/notifier.h>
0025 #include <linux/reboot.h>
0026 #include <linux/uaccess.h>
0027 #include <linux/io.h>
0028
0029
0030 #define WATCHDOG_VERSION "1.00"
0031 #define WATCHDOG_NAME "W83977F WDT"
0032
0033 #define IO_INDEX_PORT 0x3F0
0034 #define IO_DATA_PORT (IO_INDEX_PORT+1)
0035
0036 #define UNLOCK_DATA 0x87
0037 #define LOCK_DATA 0xAA
0038 #define DEVICE_REGISTER 0x07
0039
0040 #define DEFAULT_TIMEOUT 45
0041
0042 static int timeout = DEFAULT_TIMEOUT;
0043 static int timeoutW;
0044 static unsigned long timer_alive;
0045 static int testmode;
0046 static char expect_close;
0047 static DEFINE_SPINLOCK(spinlock);
0048
0049 module_param(timeout, int, 0);
0050 MODULE_PARM_DESC(timeout,
0051 "Watchdog timeout in seconds (15..7635), default="
0052 __MODULE_STRING(DEFAULT_TIMEOUT) ")");
0053 module_param(testmode, int, 0);
0054 MODULE_PARM_DESC(testmode, "Watchdog testmode (1 = no reboot), default=0");
0055
0056 static bool nowayout = WATCHDOG_NOWAYOUT;
0057 module_param(nowayout, bool, 0);
0058 MODULE_PARM_DESC(nowayout,
0059 "Watchdog cannot be stopped once started (default="
0060 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0061
0062
0063
0064
0065
0066 static int wdt_start(void)
0067 {
0068 unsigned long flags;
0069
0070 spin_lock_irqsave(&spinlock, flags);
0071
0072
0073 outb_p(UNLOCK_DATA, IO_INDEX_PORT);
0074 outb_p(UNLOCK_DATA, IO_INDEX_PORT);
0075
0076
0077
0078
0079
0080
0081
0082 outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
0083 outb_p(0x08, IO_DATA_PORT);
0084 outb_p(0xF2, IO_INDEX_PORT);
0085 outb_p(timeoutW, IO_DATA_PORT);
0086 outb_p(0xF3, IO_INDEX_PORT);
0087 outb_p(0x08, IO_DATA_PORT);
0088 outb_p(0xF4, IO_INDEX_PORT);
0089 outb_p(0x00, IO_DATA_PORT);
0090
0091
0092 outb_p(0x30, IO_INDEX_PORT);
0093 outb_p(0x01, IO_DATA_PORT);
0094
0095
0096
0097
0098
0099
0100
0101
0102 outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
0103 outb_p(0x07, IO_DATA_PORT);
0104 if (!testmode) {
0105 unsigned pin_map;
0106
0107 outb_p(0xE6, IO_INDEX_PORT);
0108 outb_p(0x0A, IO_DATA_PORT);
0109 outb_p(0x2C, IO_INDEX_PORT);
0110 pin_map = inb_p(IO_DATA_PORT);
0111 pin_map |= 0x10;
0112 pin_map &= ~(0x20);
0113 outb_p(0x2C, IO_INDEX_PORT);
0114 outb_p(pin_map, IO_DATA_PORT);
0115 }
0116 outb_p(0xE3, IO_INDEX_PORT);
0117 outb_p(0x08, IO_DATA_PORT);
0118
0119
0120 outb_p(0x30, IO_INDEX_PORT);
0121 outb_p(0x01, IO_DATA_PORT);
0122
0123
0124 outb_p(LOCK_DATA, IO_INDEX_PORT);
0125
0126 spin_unlock_irqrestore(&spinlock, flags);
0127
0128 pr_info("activated\n");
0129
0130 return 0;
0131 }
0132
0133
0134
0135
0136
0137 static int wdt_stop(void)
0138 {
0139 unsigned long flags;
0140
0141 spin_lock_irqsave(&spinlock, flags);
0142
0143
0144 outb_p(UNLOCK_DATA, IO_INDEX_PORT);
0145 outb_p(UNLOCK_DATA, IO_INDEX_PORT);
0146
0147
0148
0149
0150
0151
0152
0153 outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
0154 outb_p(0x08, IO_DATA_PORT);
0155 outb_p(0xF2, IO_INDEX_PORT);
0156 outb_p(0xFF, IO_DATA_PORT);
0157 outb_p(0xF3, IO_INDEX_PORT);
0158 outb_p(0x00, IO_DATA_PORT);
0159 outb_p(0xF4, IO_INDEX_PORT);
0160 outb_p(0x00, IO_DATA_PORT);
0161 outb_p(0xF2, IO_INDEX_PORT);
0162 outb_p(0x00, IO_DATA_PORT);
0163
0164
0165
0166
0167
0168 outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
0169 outb_p(0x07, IO_DATA_PORT);
0170 if (!testmode) {
0171 outb_p(0xE6, IO_INDEX_PORT);
0172 outb_p(0x01, IO_DATA_PORT);
0173 }
0174 outb_p(0xE3, IO_INDEX_PORT);
0175 outb_p(0x01, IO_DATA_PORT);
0176
0177
0178 outb_p(LOCK_DATA, IO_INDEX_PORT);
0179
0180 spin_unlock_irqrestore(&spinlock, flags);
0181
0182 pr_info("shutdown\n");
0183
0184 return 0;
0185 }
0186
0187
0188
0189
0190
0191
0192 static int wdt_keepalive(void)
0193 {
0194 unsigned long flags;
0195
0196 spin_lock_irqsave(&spinlock, flags);
0197
0198
0199 outb_p(UNLOCK_DATA, IO_INDEX_PORT);
0200 outb_p(UNLOCK_DATA, IO_INDEX_PORT);
0201
0202
0203 outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
0204 outb_p(0x08, IO_DATA_PORT);
0205 outb_p(0xF2, IO_INDEX_PORT);
0206 outb_p(timeoutW, IO_DATA_PORT);
0207
0208
0209 outb_p(LOCK_DATA, IO_INDEX_PORT);
0210
0211 spin_unlock_irqrestore(&spinlock, flags);
0212
0213 return 0;
0214 }
0215
0216
0217
0218
0219
0220 static int wdt_set_timeout(int t)
0221 {
0222 unsigned int tmrval;
0223
0224
0225
0226
0227
0228
0229
0230
0231 if (t < 15)
0232 return -EINVAL;
0233
0234 tmrval = ((t + 15) + 29) / 30;
0235
0236 if (tmrval > 255)
0237 return -EINVAL;
0238
0239
0240
0241
0242
0243 timeoutW = tmrval;
0244 timeout = (timeoutW * 30) - 15;
0245 return 0;
0246 }
0247
0248
0249
0250
0251
0252 static int wdt_get_status(int *status)
0253 {
0254 int new_status;
0255 unsigned long flags;
0256
0257 spin_lock_irqsave(&spinlock, flags);
0258
0259
0260 outb_p(UNLOCK_DATA, IO_INDEX_PORT);
0261 outb_p(UNLOCK_DATA, IO_INDEX_PORT);
0262
0263
0264 outb_p(DEVICE_REGISTER, IO_INDEX_PORT);
0265 outb_p(0x08, IO_DATA_PORT);
0266 outb_p(0xF4, IO_INDEX_PORT);
0267 new_status = inb_p(IO_DATA_PORT);
0268
0269
0270 outb_p(LOCK_DATA, IO_INDEX_PORT);
0271
0272 spin_unlock_irqrestore(&spinlock, flags);
0273
0274 *status = 0;
0275 if (new_status & 1)
0276 *status |= WDIOF_CARDRESET;
0277
0278 return 0;
0279 }
0280
0281
0282
0283
0284
0285
0286 static int wdt_open(struct inode *inode, struct file *file)
0287 {
0288
0289 if (test_and_set_bit(0, &timer_alive))
0290 return -EBUSY;
0291
0292 if (nowayout)
0293 __module_get(THIS_MODULE);
0294
0295 wdt_start();
0296 return stream_open(inode, file);
0297 }
0298
0299 static int wdt_release(struct inode *inode, struct file *file)
0300 {
0301
0302
0303
0304
0305 if (expect_close == 42) {
0306 wdt_stop();
0307 clear_bit(0, &timer_alive);
0308 } else {
0309 wdt_keepalive();
0310 pr_crit("unexpected close, not stopping watchdog!\n");
0311 }
0312 expect_close = 0;
0313 return 0;
0314 }
0315
0316
0317
0318
0319
0320
0321
0322
0323
0324
0325
0326
0327 static ssize_t wdt_write(struct file *file, const char __user *buf,
0328 size_t count, loff_t *ppos)
0329 {
0330
0331 if (count) {
0332 if (!nowayout) {
0333 size_t ofs;
0334
0335
0336
0337 expect_close = 0;
0338
0339
0340
0341 for (ofs = 0; ofs != count; ofs++) {
0342 char c;
0343 if (get_user(c, buf + ofs))
0344 return -EFAULT;
0345 if (c == 'V')
0346 expect_close = 42;
0347 }
0348 }
0349
0350
0351 wdt_keepalive();
0352 }
0353 return count;
0354 }
0355
0356
0357
0358
0359
0360
0361
0362
0363
0364
0365
0366
0367 static const struct watchdog_info ident = {
0368 .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
0369 .firmware_version = 1,
0370 .identity = WATCHDOG_NAME,
0371 };
0372
0373 static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
0374 {
0375 int status;
0376 int new_options, retval = -EINVAL;
0377 int new_timeout;
0378 union {
0379 struct watchdog_info __user *ident;
0380 int __user *i;
0381 } uarg;
0382
0383 uarg.i = (int __user *)arg;
0384
0385 switch (cmd) {
0386 case WDIOC_GETSUPPORT:
0387 return copy_to_user(uarg.ident, &ident,
0388 sizeof(ident)) ? -EFAULT : 0;
0389
0390 case WDIOC_GETSTATUS:
0391 wdt_get_status(&status);
0392 return put_user(status, uarg.i);
0393
0394 case WDIOC_GETBOOTSTATUS:
0395 return put_user(0, uarg.i);
0396
0397 case WDIOC_SETOPTIONS:
0398 if (get_user(new_options, uarg.i))
0399 return -EFAULT;
0400
0401 if (new_options & WDIOS_DISABLECARD) {
0402 wdt_stop();
0403 retval = 0;
0404 }
0405
0406 if (new_options & WDIOS_ENABLECARD) {
0407 wdt_start();
0408 retval = 0;
0409 }
0410
0411 return retval;
0412
0413 case WDIOC_KEEPALIVE:
0414 wdt_keepalive();
0415 return 0;
0416
0417 case WDIOC_SETTIMEOUT:
0418 if (get_user(new_timeout, uarg.i))
0419 return -EFAULT;
0420
0421 if (wdt_set_timeout(new_timeout))
0422 return -EINVAL;
0423
0424 wdt_keepalive();
0425 fallthrough;
0426
0427 case WDIOC_GETTIMEOUT:
0428 return put_user(timeout, uarg.i);
0429
0430 default:
0431 return -ENOTTY;
0432
0433 }
0434 }
0435
0436 static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
0437 void *unused)
0438 {
0439 if (code == SYS_DOWN || code == SYS_HALT)
0440 wdt_stop();
0441 return NOTIFY_DONE;
0442 }
0443
0444 static const struct file_operations wdt_fops = {
0445 .owner = THIS_MODULE,
0446 .llseek = no_llseek,
0447 .write = wdt_write,
0448 .unlocked_ioctl = wdt_ioctl,
0449 .compat_ioctl = compat_ptr_ioctl,
0450 .open = wdt_open,
0451 .release = wdt_release,
0452 };
0453
0454 static struct miscdevice wdt_miscdev = {
0455 .minor = WATCHDOG_MINOR,
0456 .name = "watchdog",
0457 .fops = &wdt_fops,
0458 };
0459
0460 static struct notifier_block wdt_notifier = {
0461 .notifier_call = wdt_notify_sys,
0462 };
0463
0464 static int __init w83977f_wdt_init(void)
0465 {
0466 int rc;
0467
0468 pr_info("driver v%s\n", WATCHDOG_VERSION);
0469
0470
0471
0472
0473
0474 if (wdt_set_timeout(timeout)) {
0475 wdt_set_timeout(DEFAULT_TIMEOUT);
0476 pr_info("timeout value must be 15 <= timeout <= 7635, using %d\n",
0477 DEFAULT_TIMEOUT);
0478 }
0479
0480 if (!request_region(IO_INDEX_PORT, 2, WATCHDOG_NAME)) {
0481 pr_err("I/O address 0x%04x already in use\n", IO_INDEX_PORT);
0482 rc = -EIO;
0483 goto err_out;
0484 }
0485
0486 rc = register_reboot_notifier(&wdt_notifier);
0487 if (rc) {
0488 pr_err("cannot register reboot notifier (err=%d)\n", rc);
0489 goto err_out_region;
0490 }
0491
0492 rc = misc_register(&wdt_miscdev);
0493 if (rc) {
0494 pr_err("cannot register miscdev on minor=%d (err=%d)\n",
0495 wdt_miscdev.minor, rc);
0496 goto err_out_reboot;
0497 }
0498
0499 pr_info("initialized. timeout=%d sec (nowayout=%d testmode=%d)\n",
0500 timeout, nowayout, testmode);
0501
0502 return 0;
0503
0504 err_out_reboot:
0505 unregister_reboot_notifier(&wdt_notifier);
0506 err_out_region:
0507 release_region(IO_INDEX_PORT, 2);
0508 err_out:
0509 return rc;
0510 }
0511
0512 static void __exit w83977f_wdt_exit(void)
0513 {
0514 wdt_stop();
0515 misc_deregister(&wdt_miscdev);
0516 unregister_reboot_notifier(&wdt_notifier);
0517 release_region(IO_INDEX_PORT, 2);
0518 }
0519
0520 module_init(w83977f_wdt_init);
0521 module_exit(w83977f_wdt_exit);
0522
0523 MODULE_AUTHOR("Jose Goncalves <jose.goncalves@inov.pt>");
0524 MODULE_DESCRIPTION("Driver for watchdog timer in W83977F I/O chip");
0525 MODULE_LICENSE("GPL");