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 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0036
0037 #include <linux/module.h>
0038 #include <linux/types.h>
0039 #include <linux/miscdevice.h>
0040 #include <linux/watchdog.h>
0041 #include <linux/ioport.h>
0042 #include <linux/delay.h>
0043 #include <linux/notifier.h>
0044 #include <linux/fs.h>
0045 #include <linux/reboot.h>
0046 #include <linux/init.h>
0047 #include <linux/spinlock.h>
0048 #include <linux/moduleparam.h>
0049 #include <linux/io.h>
0050 #include <linux/uaccess.h>
0051
0052
0053 static unsigned long sbc8360_is_open;
0054 static char expect_close;
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122 static int wd_times[64][2] = {
0123 {0, 1},
0124 {1, 1},
0125 {2, 1},
0126 {3, 1},
0127 {4, 1},
0128 {5, 1},
0129 {6, 1},
0130 {7, 1},
0131 {8, 1},
0132 {9, 1},
0133 {0xA, 1},
0134 {0xB, 1},
0135 {0xC, 1},
0136 {0xD, 1},
0137 {0xE, 1},
0138 {0xF, 1},
0139 {0, 2},
0140 {1, 2},
0141 {2, 2},
0142 {3, 2},
0143 {4, 2},
0144 {5, 2},
0145 {6, 2},
0146 {7, 2},
0147 {8, 2},
0148 {9, 2},
0149 {0xA, 2},
0150 {0xB, 2},
0151 {0xC, 2},
0152 {0xD, 2},
0153 {0xE, 2},
0154 {0xF, 2},
0155 {0, 3},
0156 {1, 3},
0157 {2, 3},
0158 {3, 3},
0159 {4, 3},
0160 {5, 3},
0161 {6, 3},
0162 {7, 3},
0163 {8, 3},
0164 {9, 3},
0165 {0xA, 3},
0166 {0xB, 3},
0167 {0xC, 3},
0168 {0xD, 3},
0169 {0xE, 3},
0170 {0xF, 3},
0171 {0, 4},
0172 {1, 4},
0173 {2, 4},
0174 {3, 4},
0175 {4, 4},
0176 {5, 4},
0177 {6, 4},
0178 {7, 4},
0179 {8, 4},
0180 {9, 4},
0181 {0xA, 4},
0182 {0xB, 4},
0183 {0xC, 4},
0184 {0xD, 4},
0185 {0xE, 4},
0186 {0xF, 4}
0187 };
0188
0189 #define SBC8360_ENABLE 0x120
0190 #define SBC8360_BASETIME 0x121
0191
0192 static int timeout = 27;
0193 static int wd_margin = 0xB;
0194 static int wd_multiplier = 2;
0195 static bool nowayout = WATCHDOG_NOWAYOUT;
0196
0197 module_param(timeout, int, 0);
0198 MODULE_PARM_DESC(timeout, "Index into timeout table (0-63) (default=27 (60s))");
0199 module_param(nowayout, bool, 0);
0200 MODULE_PARM_DESC(nowayout,
0201 "Watchdog cannot be stopped once started (default="
0202 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0203
0204
0205
0206
0207
0208
0209 static void sbc8360_activate(void)
0210 {
0211
0212 outb(0x0A, SBC8360_ENABLE);
0213 msleep_interruptible(100);
0214 outb(0x0B, SBC8360_ENABLE);
0215 msleep_interruptible(100);
0216
0217 outb(wd_multiplier, SBC8360_ENABLE);
0218 msleep_interruptible(100);
0219
0220 }
0221
0222
0223 static void sbc8360_ping(void)
0224 {
0225
0226 outb(wd_margin, SBC8360_BASETIME);
0227 }
0228
0229
0230 static void sbc8360_stop(void)
0231 {
0232
0233 outb(0, SBC8360_ENABLE);
0234 }
0235
0236
0237 static ssize_t sbc8360_write(struct file *file, const char __user *buf,
0238 size_t count, loff_t *ppos)
0239 {
0240 if (count) {
0241 if (!nowayout) {
0242 size_t i;
0243
0244
0245 expect_close = 0;
0246
0247 for (i = 0; i != count; i++) {
0248 char c;
0249 if (get_user(c, buf + i))
0250 return -EFAULT;
0251 if (c == 'V')
0252 expect_close = 42;
0253 }
0254 }
0255 sbc8360_ping();
0256 }
0257 return count;
0258 }
0259
0260 static int sbc8360_open(struct inode *inode, struct file *file)
0261 {
0262 if (test_and_set_bit(0, &sbc8360_is_open))
0263 return -EBUSY;
0264 if (nowayout)
0265 __module_get(THIS_MODULE);
0266
0267
0268 sbc8360_activate();
0269 sbc8360_ping();
0270 return stream_open(inode, file);
0271 }
0272
0273 static int sbc8360_close(struct inode *inode, struct file *file)
0274 {
0275 if (expect_close == 42)
0276 sbc8360_stop();
0277 else
0278 pr_crit("SBC8360 device closed unexpectedly. SBC8360 will not stop!\n");
0279
0280 clear_bit(0, &sbc8360_is_open);
0281 expect_close = 0;
0282 return 0;
0283 }
0284
0285
0286
0287
0288
0289 static int sbc8360_notify_sys(struct notifier_block *this, unsigned long code,
0290 void *unused)
0291 {
0292 if (code == SYS_DOWN || code == SYS_HALT)
0293 sbc8360_stop();
0294
0295 return NOTIFY_DONE;
0296 }
0297
0298
0299
0300
0301
0302 static const struct file_operations sbc8360_fops = {
0303 .owner = THIS_MODULE,
0304 .llseek = no_llseek,
0305 .write = sbc8360_write,
0306 .open = sbc8360_open,
0307 .release = sbc8360_close,
0308 };
0309
0310 static struct miscdevice sbc8360_miscdev = {
0311 .minor = WATCHDOG_MINOR,
0312 .name = "watchdog",
0313 .fops = &sbc8360_fops,
0314 };
0315
0316
0317
0318
0319
0320
0321 static struct notifier_block sbc8360_notifier = {
0322 .notifier_call = sbc8360_notify_sys,
0323 };
0324
0325 static int __init sbc8360_init(void)
0326 {
0327 int res;
0328 unsigned long int mseconds = 60000;
0329
0330 if (timeout < 0 || timeout > 63) {
0331 pr_err("Invalid timeout index (must be 0-63)\n");
0332 res = -EINVAL;
0333 goto out;
0334 }
0335
0336 if (!request_region(SBC8360_ENABLE, 1, "SBC8360")) {
0337 pr_err("ENABLE method I/O %X is not available\n",
0338 SBC8360_ENABLE);
0339 res = -EIO;
0340 goto out;
0341 }
0342 if (!request_region(SBC8360_BASETIME, 1, "SBC8360")) {
0343 pr_err("BASETIME method I/O %X is not available\n",
0344 SBC8360_BASETIME);
0345 res = -EIO;
0346 goto out_nobasetimereg;
0347 }
0348
0349 res = register_reboot_notifier(&sbc8360_notifier);
0350 if (res) {
0351 pr_err("Failed to register reboot notifier\n");
0352 goto out_noreboot;
0353 }
0354
0355 res = misc_register(&sbc8360_miscdev);
0356 if (res) {
0357 pr_err("failed to register misc device\n");
0358 goto out_nomisc;
0359 }
0360
0361 wd_margin = wd_times[timeout][0];
0362 wd_multiplier = wd_times[timeout][1];
0363
0364 if (wd_multiplier == 1)
0365 mseconds = (wd_margin + 1) * 500;
0366 else if (wd_multiplier == 2)
0367 mseconds = (wd_margin + 1) * 5000;
0368 else if (wd_multiplier == 3)
0369 mseconds = (wd_margin + 1) * 50000;
0370 else if (wd_multiplier == 4)
0371 mseconds = (wd_margin + 1) * 100000;
0372
0373
0374 pr_info("Timeout set at %ld ms\n", mseconds);
0375
0376 return 0;
0377
0378 out_nomisc:
0379 unregister_reboot_notifier(&sbc8360_notifier);
0380 out_noreboot:
0381 release_region(SBC8360_BASETIME, 1);
0382 out_nobasetimereg:
0383 release_region(SBC8360_ENABLE, 1);
0384 out:
0385 return res;
0386 }
0387
0388 static void __exit sbc8360_exit(void)
0389 {
0390 misc_deregister(&sbc8360_miscdev);
0391 unregister_reboot_notifier(&sbc8360_notifier);
0392 release_region(SBC8360_ENABLE, 1);
0393 release_region(SBC8360_BASETIME, 1);
0394 }
0395
0396 module_init(sbc8360_init);
0397 module_exit(sbc8360_exit);
0398
0399 MODULE_AUTHOR("Ian E. Morgan <imorgan@webcon.ca>");
0400 MODULE_DESCRIPTION("SBC8360 watchdog driver");
0401 MODULE_LICENSE("GPL");
0402 MODULE_VERSION("1.01");
0403
0404