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
0042
0043
0044 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0045
0046 #include <linux/interrupt.h>
0047 #include <linux/module.h>
0048 #include <linux/moduleparam.h>
0049 #include <linux/types.h>
0050 #include <linux/miscdevice.h>
0051 #include <linux/watchdog.h>
0052 #include <linux/fs.h>
0053 #include <linux/ioport.h>
0054 #include <linux/notifier.h>
0055 #include <linux/reboot.h>
0056 #include <linux/init.h>
0057 #include <linux/io.h>
0058 #include <linux/uaccess.h>
0059
0060
0061 static unsigned long eurwdt_is_open;
0062 static int eurwdt_timeout;
0063 static char eur_expect_close;
0064 static DEFINE_SPINLOCK(eurwdt_lock);
0065
0066
0067
0068
0069
0070 static int io = 0x3f0;
0071 static int irq = 10;
0072 static char *ev = "int";
0073
0074 #define WDT_TIMEOUT 60
0075
0076 static bool nowayout = WATCHDOG_NOWAYOUT;
0077 module_param(nowayout, bool, 0);
0078 MODULE_PARM_DESC(nowayout,
0079 "Watchdog cannot be stopped once started (default="
0080 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0081
0082
0083
0084
0085
0086 #define WDT_CTRL_REG 0x30
0087 #define WDT_OUTPIN_CFG 0xe2
0088 #define WDT_EVENT_INT 0x00
0089 #define WDT_EVENT_REBOOT 0x08
0090 #define WDT_UNIT_SEL 0xf1
0091 #define WDT_UNIT_SECS 0x80
0092 #define WDT_TIMEOUT_VAL 0xf2
0093 #define WDT_TIMER_CFG 0xf3
0094
0095
0096 module_param_hw(io, int, ioport, 0);
0097 MODULE_PARM_DESC(io, "Eurotech WDT io port (default=0x3f0)");
0098 module_param_hw(irq, int, irq, 0);
0099 MODULE_PARM_DESC(irq, "Eurotech WDT irq (default=10)");
0100 module_param(ev, charp, 0);
0101 MODULE_PARM_DESC(ev, "Eurotech WDT event type (default is `int')");
0102
0103
0104
0105
0106
0107
0108 static inline void eurwdt_write_reg(u8 index, u8 data)
0109 {
0110 outb(index, io);
0111 outb(data, io+1);
0112 }
0113
0114 static inline void eurwdt_lock_chip(void)
0115 {
0116 outb(0xaa, io);
0117 }
0118
0119 static inline void eurwdt_unlock_chip(void)
0120 {
0121 outb(0x55, io);
0122 eurwdt_write_reg(0x07, 0x08);
0123 }
0124
0125 static inline void eurwdt_set_timeout(int timeout)
0126 {
0127 eurwdt_write_reg(WDT_TIMEOUT_VAL, (u8) timeout);
0128 }
0129
0130 static inline void eurwdt_disable_timer(void)
0131 {
0132 eurwdt_set_timeout(0);
0133 }
0134
0135 static void eurwdt_activate_timer(void)
0136 {
0137 eurwdt_disable_timer();
0138 eurwdt_write_reg(WDT_CTRL_REG, 0x01);
0139 eurwdt_write_reg(WDT_OUTPIN_CFG,
0140 !strcmp("int", ev) ? WDT_EVENT_INT : WDT_EVENT_REBOOT);
0141
0142
0143 if (irq == 2 || irq > 15 || irq < 0) {
0144 pr_err("invalid irq number\n");
0145 irq = 0;
0146 }
0147 if (irq == 0)
0148 pr_info("interrupt disabled\n");
0149
0150 eurwdt_write_reg(WDT_TIMER_CFG, irq << 4);
0151
0152 eurwdt_write_reg(WDT_UNIT_SEL, WDT_UNIT_SECS);
0153 eurwdt_set_timeout(0);
0154 }
0155
0156
0157
0158
0159
0160
0161 static irqreturn_t eurwdt_interrupt(int irq, void *dev_id)
0162 {
0163 pr_crit("timeout WDT timeout\n");
0164
0165 #ifdef ONLY_TESTING
0166 pr_crit("Would Reboot\n");
0167 #else
0168 pr_crit("Initiating system reboot\n");
0169 emergency_restart();
0170 #endif
0171 return IRQ_HANDLED;
0172 }
0173
0174
0175
0176
0177
0178
0179
0180
0181 static void eurwdt_ping(void)
0182 {
0183
0184 eurwdt_set_timeout(eurwdt_timeout);
0185 }
0186
0187
0188
0189
0190
0191
0192
0193
0194
0195
0196
0197
0198 static ssize_t eurwdt_write(struct file *file, const char __user *buf,
0199 size_t count, loff_t *ppos)
0200 {
0201 if (count) {
0202 if (!nowayout) {
0203 size_t i;
0204
0205 eur_expect_close = 0;
0206
0207 for (i = 0; i != count; i++) {
0208 char c;
0209 if (get_user(c, buf + i))
0210 return -EFAULT;
0211 if (c == 'V')
0212 eur_expect_close = 42;
0213 }
0214 }
0215 spin_lock(&eurwdt_lock);
0216 eurwdt_ping();
0217 spin_unlock(&eurwdt_lock);
0218 }
0219 return count;
0220 }
0221
0222
0223
0224
0225
0226
0227
0228
0229
0230
0231
0232 static long eurwdt_ioctl(struct file *file,
0233 unsigned int cmd, unsigned long arg)
0234 {
0235 void __user *argp = (void __user *)arg;
0236 int __user *p = argp;
0237 static const struct watchdog_info ident = {
0238 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT
0239 | WDIOF_MAGICCLOSE,
0240 .firmware_version = 1,
0241 .identity = "WDT Eurotech CPU-1220/1410",
0242 };
0243
0244 int time;
0245 int options, retval = -EINVAL;
0246
0247 switch (cmd) {
0248 case WDIOC_GETSUPPORT:
0249 return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
0250
0251 case WDIOC_GETSTATUS:
0252 case WDIOC_GETBOOTSTATUS:
0253 return put_user(0, p);
0254
0255 case WDIOC_SETOPTIONS:
0256 if (get_user(options, p))
0257 return -EFAULT;
0258 spin_lock(&eurwdt_lock);
0259 if (options & WDIOS_DISABLECARD) {
0260 eurwdt_disable_timer();
0261 retval = 0;
0262 }
0263 if (options & WDIOS_ENABLECARD) {
0264 eurwdt_activate_timer();
0265 eurwdt_ping();
0266 retval = 0;
0267 }
0268 spin_unlock(&eurwdt_lock);
0269 return retval;
0270
0271 case WDIOC_KEEPALIVE:
0272 spin_lock(&eurwdt_lock);
0273 eurwdt_ping();
0274 spin_unlock(&eurwdt_lock);
0275 return 0;
0276
0277 case WDIOC_SETTIMEOUT:
0278 if (copy_from_user(&time, p, sizeof(int)))
0279 return -EFAULT;
0280
0281
0282 if (time < 0 || time > 255)
0283 return -EINVAL;
0284
0285 spin_lock(&eurwdt_lock);
0286 eurwdt_timeout = time;
0287 eurwdt_set_timeout(time);
0288 spin_unlock(&eurwdt_lock);
0289 fallthrough;
0290
0291 case WDIOC_GETTIMEOUT:
0292 return put_user(eurwdt_timeout, p);
0293
0294 default:
0295 return -ENOTTY;
0296 }
0297 }
0298
0299
0300
0301
0302
0303
0304
0305
0306
0307
0308 static int eurwdt_open(struct inode *inode, struct file *file)
0309 {
0310 if (test_and_set_bit(0, &eurwdt_is_open))
0311 return -EBUSY;
0312 eurwdt_timeout = WDT_TIMEOUT;
0313
0314 eurwdt_activate_timer();
0315 return stream_open(inode, file);
0316 }
0317
0318
0319
0320
0321
0322
0323
0324
0325
0326
0327
0328
0329
0330 static int eurwdt_release(struct inode *inode, struct file *file)
0331 {
0332 if (eur_expect_close == 42)
0333 eurwdt_disable_timer();
0334 else {
0335 pr_crit("Unexpected close, not stopping watchdog!\n");
0336 eurwdt_ping();
0337 }
0338 clear_bit(0, &eurwdt_is_open);
0339 eur_expect_close = 0;
0340 return 0;
0341 }
0342
0343
0344
0345
0346
0347
0348
0349
0350
0351
0352
0353
0354
0355 static int eurwdt_notify_sys(struct notifier_block *this, unsigned long code,
0356 void *unused)
0357 {
0358 if (code == SYS_DOWN || code == SYS_HALT)
0359 eurwdt_disable_timer();
0360
0361 return NOTIFY_DONE;
0362 }
0363
0364
0365
0366
0367
0368
0369 static const struct file_operations eurwdt_fops = {
0370 .owner = THIS_MODULE,
0371 .llseek = no_llseek,
0372 .write = eurwdt_write,
0373 .unlocked_ioctl = eurwdt_ioctl,
0374 .compat_ioctl = compat_ptr_ioctl,
0375 .open = eurwdt_open,
0376 .release = eurwdt_release,
0377 };
0378
0379 static struct miscdevice eurwdt_miscdev = {
0380 .minor = WATCHDOG_MINOR,
0381 .name = "watchdog",
0382 .fops = &eurwdt_fops,
0383 };
0384
0385
0386
0387
0388
0389
0390 static struct notifier_block eurwdt_notifier = {
0391 .notifier_call = eurwdt_notify_sys,
0392 };
0393
0394
0395
0396
0397
0398
0399
0400
0401
0402
0403
0404 static void __exit eurwdt_exit(void)
0405 {
0406 eurwdt_lock_chip();
0407
0408 misc_deregister(&eurwdt_miscdev);
0409
0410 unregister_reboot_notifier(&eurwdt_notifier);
0411 release_region(io, 2);
0412 free_irq(irq, NULL);
0413 }
0414
0415
0416
0417
0418
0419
0420
0421
0422
0423 static int __init eurwdt_init(void)
0424 {
0425 int ret;
0426
0427 ret = request_irq(irq, eurwdt_interrupt, 0, "eurwdt", NULL);
0428 if (ret) {
0429 pr_err("IRQ %d is not free\n", irq);
0430 goto out;
0431 }
0432
0433 if (!request_region(io, 2, "eurwdt")) {
0434 pr_err("IO %X is not free\n", io);
0435 ret = -EBUSY;
0436 goto outirq;
0437 }
0438
0439 ret = register_reboot_notifier(&eurwdt_notifier);
0440 if (ret) {
0441 pr_err("can't register reboot notifier (err=%d)\n", ret);
0442 goto outreg;
0443 }
0444
0445 ret = misc_register(&eurwdt_miscdev);
0446 if (ret) {
0447 pr_err("can't misc_register on minor=%d\n", WATCHDOG_MINOR);
0448 goto outreboot;
0449 }
0450
0451 eurwdt_unlock_chip();
0452
0453 ret = 0;
0454 pr_info("Eurotech WDT driver 0.01 at %X (Interrupt %d) - timeout event: %s\n",
0455 io, irq, (!strcmp("int", ev) ? "int" : "reboot"));
0456
0457 out:
0458 return ret;
0459
0460 outreboot:
0461 unregister_reboot_notifier(&eurwdt_notifier);
0462
0463 outreg:
0464 release_region(io, 2);
0465
0466 outirq:
0467 free_irq(irq, NULL);
0468 goto out;
0469 }
0470
0471 module_init(eurwdt_init);
0472 module_exit(eurwdt_exit);
0473
0474 MODULE_AUTHOR("Rodolfo Giometti");
0475 MODULE_DESCRIPTION("Driver for Eurotech CPU-1220/1410 on board watchdog");
0476 MODULE_LICENSE("GPL");