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