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
0046
0047
0048
0049
0050
0051 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0052
0053 #include <linux/module.h>
0054 #include <linux/moduleparam.h>
0055 #include <linux/types.h>
0056 #include <linux/timer.h>
0057 #include <linux/miscdevice.h>
0058 #include <linux/watchdog.h>
0059 #include <linux/fs.h>
0060 #include <linux/ioport.h>
0061 #include <linux/notifier.h>
0062 #include <linux/reboot.h>
0063 #include <linux/init.h>
0064 #include <linux/jiffies.h>
0065 #include <linux/io.h>
0066 #include <linux/uaccess.h>
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079 #define WDT_INTERVAL (HZ/4+1)
0080
0081
0082
0083
0084
0085
0086
0087 #define WATCHDOG_TIMEOUT 30
0088
0089 static int timeout = WATCHDOG_TIMEOUT;
0090 module_param(timeout, int, 0);
0091 MODULE_PARM_DESC(timeout,
0092 "Watchdog timeout in seconds. (1 <= timeout <= 3600, default="
0093 __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
0094
0095 static bool nowayout = WATCHDOG_NOWAYOUT;
0096 module_param(nowayout, bool, 0);
0097 MODULE_PARM_DESC(nowayout,
0098 "Watchdog cannot be stopped once started (default="
0099 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0100
0101
0102
0103
0104 #define MMCR_BASE 0xfffef000
0105 #define OFFS_WDTMRCTL 0xCB0
0106
0107
0108 #define WDT_EXP_SEL_01 0x0001
0109 #define WDT_EXP_SEL_02 0x0002
0110 #define WDT_EXP_SEL_03 0x0004
0111 #define WDT_EXP_SEL_04 0x0008
0112 #define WDT_EXP_SEL_05 0x0010
0113 #define WDT_EXP_SEL_06 0x0020
0114 #define WDT_EXP_SEL_07 0x0040
0115 #define WDT_EXP_SEL_08 0x0080
0116 #define WDT_IRQ_FLG 0x1000
0117 #define WDT_WRST_ENB 0x4000
0118 #define WDT_ENB 0x8000
0119
0120 static __u16 __iomem *wdtmrctl;
0121
0122 static void wdt_timer_ping(struct timer_list *);
0123 static DEFINE_TIMER(timer, wdt_timer_ping);
0124 static unsigned long next_heartbeat;
0125 static unsigned long wdt_is_open;
0126 static char wdt_expect_close;
0127 static DEFINE_SPINLOCK(wdt_spinlock);
0128
0129
0130
0131
0132
0133 static void wdt_timer_ping(struct timer_list *unused)
0134 {
0135
0136
0137
0138 if (time_before(jiffies, next_heartbeat)) {
0139
0140 spin_lock(&wdt_spinlock);
0141 writew(0xAAAA, wdtmrctl);
0142 writew(0x5555, wdtmrctl);
0143 spin_unlock(&wdt_spinlock);
0144
0145
0146 mod_timer(&timer, jiffies + WDT_INTERVAL);
0147 } else
0148 pr_warn("Heartbeat lost! Will not ping the watchdog\n");
0149 }
0150
0151
0152
0153
0154
0155 static void wdt_config(int writeval)
0156 {
0157 unsigned long flags;
0158
0159
0160 spin_lock_irqsave(&wdt_spinlock, flags);
0161 readw(wdtmrctl);
0162 writew(0xAAAA, wdtmrctl);
0163 writew(0x5555, wdtmrctl);
0164
0165 writew(0x3333, wdtmrctl);
0166 writew(0xCCCC, wdtmrctl);
0167
0168 writew(writeval, wdtmrctl);
0169 spin_unlock_irqrestore(&wdt_spinlock, flags);
0170 }
0171
0172 static int wdt_startup(void)
0173 {
0174 next_heartbeat = jiffies + (timeout * HZ);
0175
0176
0177 mod_timer(&timer, jiffies + WDT_INTERVAL);
0178
0179
0180 wdt_config(WDT_ENB | WDT_WRST_ENB | WDT_EXP_SEL_04);
0181
0182 pr_info("Watchdog timer is now enabled\n");
0183 return 0;
0184 }
0185
0186 static int wdt_turnoff(void)
0187 {
0188
0189 del_timer_sync(&timer);
0190
0191
0192 wdt_config(0);
0193
0194 pr_info("Watchdog timer is now disabled...\n");
0195 return 0;
0196 }
0197
0198 static int wdt_keepalive(void)
0199 {
0200
0201 next_heartbeat = jiffies + (timeout * HZ);
0202 return 0;
0203 }
0204
0205 static int wdt_set_heartbeat(int t)
0206 {
0207 if ((t < 1) || (t > 3600))
0208 return -EINVAL;
0209
0210 timeout = t;
0211 return 0;
0212 }
0213
0214
0215
0216
0217
0218 static ssize_t fop_write(struct file *file, const char __user *buf,
0219 size_t count, loff_t *ppos)
0220 {
0221
0222 if (count) {
0223 if (!nowayout) {
0224 size_t ofs;
0225
0226
0227
0228 wdt_expect_close = 0;
0229
0230
0231 for (ofs = 0; ofs != count; ofs++) {
0232 char c;
0233 if (get_user(c, buf + ofs))
0234 return -EFAULT;
0235 if (c == 'V')
0236 wdt_expect_close = 42;
0237 }
0238 }
0239
0240
0241
0242 wdt_keepalive();
0243 }
0244 return count;
0245 }
0246
0247 static int fop_open(struct inode *inode, struct file *file)
0248 {
0249
0250 if (test_and_set_bit(0, &wdt_is_open))
0251 return -EBUSY;
0252 if (nowayout)
0253 __module_get(THIS_MODULE);
0254
0255
0256 wdt_startup();
0257 return stream_open(inode, file);
0258 }
0259
0260 static int fop_close(struct inode *inode, struct file *file)
0261 {
0262 if (wdt_expect_close == 42)
0263 wdt_turnoff();
0264 else {
0265 pr_crit("Unexpected close, not stopping watchdog!\n");
0266 wdt_keepalive();
0267 }
0268 clear_bit(0, &wdt_is_open);
0269 wdt_expect_close = 0;
0270 return 0;
0271 }
0272
0273 static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
0274 {
0275 void __user *argp = (void __user *)arg;
0276 int __user *p = argp;
0277 static const struct watchdog_info ident = {
0278 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT
0279 | WDIOF_MAGICCLOSE,
0280 .firmware_version = 1,
0281 .identity = "SC520",
0282 };
0283
0284 switch (cmd) {
0285 case WDIOC_GETSUPPORT:
0286 return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
0287 case WDIOC_GETSTATUS:
0288 case WDIOC_GETBOOTSTATUS:
0289 return put_user(0, p);
0290 case WDIOC_SETOPTIONS:
0291 {
0292 int new_options, retval = -EINVAL;
0293
0294 if (get_user(new_options, p))
0295 return -EFAULT;
0296
0297 if (new_options & WDIOS_DISABLECARD) {
0298 wdt_turnoff();
0299 retval = 0;
0300 }
0301
0302 if (new_options & WDIOS_ENABLECARD) {
0303 wdt_startup();
0304 retval = 0;
0305 }
0306
0307 return retval;
0308 }
0309 case WDIOC_KEEPALIVE:
0310 wdt_keepalive();
0311 return 0;
0312 case WDIOC_SETTIMEOUT:
0313 {
0314 int new_timeout;
0315
0316 if (get_user(new_timeout, p))
0317 return -EFAULT;
0318
0319 if (wdt_set_heartbeat(new_timeout))
0320 return -EINVAL;
0321
0322 wdt_keepalive();
0323 }
0324 fallthrough;
0325 case WDIOC_GETTIMEOUT:
0326 return put_user(timeout, p);
0327 default:
0328 return -ENOTTY;
0329 }
0330 }
0331
0332 static const struct file_operations wdt_fops = {
0333 .owner = THIS_MODULE,
0334 .llseek = no_llseek,
0335 .write = fop_write,
0336 .open = fop_open,
0337 .release = fop_close,
0338 .unlocked_ioctl = fop_ioctl,
0339 .compat_ioctl = compat_ptr_ioctl,
0340 };
0341
0342 static struct miscdevice wdt_miscdev = {
0343 .minor = WATCHDOG_MINOR,
0344 .name = "watchdog",
0345 .fops = &wdt_fops,
0346 };
0347
0348
0349
0350
0351
0352 static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
0353 void *unused)
0354 {
0355 if (code == SYS_DOWN || code == SYS_HALT)
0356 wdt_turnoff();
0357 return NOTIFY_DONE;
0358 }
0359
0360
0361
0362
0363
0364
0365 static struct notifier_block wdt_notifier = {
0366 .notifier_call = wdt_notify_sys,
0367 };
0368
0369 static void __exit sc520_wdt_unload(void)
0370 {
0371 if (!nowayout)
0372 wdt_turnoff();
0373
0374
0375 misc_deregister(&wdt_miscdev);
0376 unregister_reboot_notifier(&wdt_notifier);
0377 iounmap(wdtmrctl);
0378 }
0379
0380 static int __init sc520_wdt_init(void)
0381 {
0382 int rc = -EBUSY;
0383
0384
0385
0386 if (wdt_set_heartbeat(timeout)) {
0387 wdt_set_heartbeat(WATCHDOG_TIMEOUT);
0388 pr_info("timeout value must be 1 <= timeout <= 3600, using %d\n",
0389 WATCHDOG_TIMEOUT);
0390 }
0391
0392 wdtmrctl = ioremap(MMCR_BASE + OFFS_WDTMRCTL, 2);
0393 if (!wdtmrctl) {
0394 pr_err("Unable to remap memory\n");
0395 rc = -ENOMEM;
0396 goto err_out_region2;
0397 }
0398
0399 rc = register_reboot_notifier(&wdt_notifier);
0400 if (rc) {
0401 pr_err("cannot register reboot notifier (err=%d)\n", rc);
0402 goto err_out_ioremap;
0403 }
0404
0405 rc = misc_register(&wdt_miscdev);
0406 if (rc) {
0407 pr_err("cannot register miscdev on minor=%d (err=%d)\n",
0408 WATCHDOG_MINOR, rc);
0409 goto err_out_notifier;
0410 }
0411
0412 pr_info("WDT driver for SC520 initialised. timeout=%d sec (nowayout=%d)\n",
0413 timeout, nowayout);
0414
0415 return 0;
0416
0417 err_out_notifier:
0418 unregister_reboot_notifier(&wdt_notifier);
0419 err_out_ioremap:
0420 iounmap(wdtmrctl);
0421 err_out_region2:
0422 return rc;
0423 }
0424
0425 module_init(sc520_wdt_init);
0426 module_exit(sc520_wdt_unload);
0427
0428 MODULE_AUTHOR("Scott and Bill Jennings");
0429 MODULE_DESCRIPTION(
0430 "Driver for watchdog timer in AMD \"Elan\" SC520 uProcessor");
0431 MODULE_LICENSE("GPL");