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 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0031
0032 #include <linux/module.h>
0033 #include <linux/types.h>
0034 #include <linux/miscdevice.h>
0035 #include <linux/watchdog.h>
0036 #include <linux/ioport.h>
0037 #include <linux/fs.h>
0038 #include <linux/init.h>
0039 #include <linux/spinlock.h>
0040 #include <linux/moduleparam.h>
0041 #include <linux/platform_device.h>
0042 #include <linux/io.h>
0043 #include <linux/uaccess.h>
0044
0045
0046 static struct platform_device *ibwdt_platform_device;
0047 static unsigned long ibwdt_is_open;
0048 static DEFINE_SPINLOCK(ibwdt_lock);
0049 static char expect_close;
0050
0051
0052 #define DRV_NAME "ib700wdt"
0053
0054
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 #define WDT_STOP 0x441
0091 #define WDT_START 0x443
0092
0093
0094 #define WATCHDOG_TIMEOUT 30
0095 static int timeout = WATCHDOG_TIMEOUT;
0096 module_param(timeout, int, 0);
0097 MODULE_PARM_DESC(timeout,
0098 "Watchdog timeout in seconds. 0<= timeout <=30, default="
0099 __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
0100
0101 static bool nowayout = WATCHDOG_NOWAYOUT;
0102 module_param(nowayout, bool, 0);
0103 MODULE_PARM_DESC(nowayout,
0104 "Watchdog cannot be stopped once started (default="
0105 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0106
0107
0108
0109
0110
0111
0112 static void ibwdt_ping(void)
0113 {
0114 int wd_margin = 15 - ((timeout + 1) / 2);
0115
0116 spin_lock(&ibwdt_lock);
0117
0118
0119 outb_p(wd_margin, WDT_START);
0120
0121 spin_unlock(&ibwdt_lock);
0122 }
0123
0124 static void ibwdt_disable(void)
0125 {
0126 spin_lock(&ibwdt_lock);
0127 outb_p(0, WDT_STOP);
0128 spin_unlock(&ibwdt_lock);
0129 }
0130
0131 static int ibwdt_set_heartbeat(int t)
0132 {
0133 if (t < 0 || t > 30)
0134 return -EINVAL;
0135
0136 timeout = t;
0137 return 0;
0138 }
0139
0140
0141
0142
0143
0144 static ssize_t ibwdt_write(struct file *file, const char __user *buf,
0145 size_t count, loff_t *ppos)
0146 {
0147 if (count) {
0148 if (!nowayout) {
0149 size_t i;
0150
0151
0152 expect_close = 0;
0153
0154 for (i = 0; i != count; i++) {
0155 char c;
0156 if (get_user(c, buf + i))
0157 return -EFAULT;
0158 if (c == 'V')
0159 expect_close = 42;
0160 }
0161 }
0162 ibwdt_ping();
0163 }
0164 return count;
0165 }
0166
0167 static long ibwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
0168 {
0169 int new_margin;
0170 void __user *argp = (void __user *)arg;
0171 int __user *p = argp;
0172
0173 static const struct watchdog_info ident = {
0174 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT
0175 | WDIOF_MAGICCLOSE,
0176 .firmware_version = 1,
0177 .identity = "IB700 WDT",
0178 };
0179
0180 switch (cmd) {
0181 case WDIOC_GETSUPPORT:
0182 if (copy_to_user(argp, &ident, sizeof(ident)))
0183 return -EFAULT;
0184 break;
0185
0186 case WDIOC_GETSTATUS:
0187 case WDIOC_GETBOOTSTATUS:
0188 return put_user(0, p);
0189
0190 case WDIOC_SETOPTIONS:
0191 {
0192 int options, retval = -EINVAL;
0193
0194 if (get_user(options, p))
0195 return -EFAULT;
0196
0197 if (options & WDIOS_DISABLECARD) {
0198 ibwdt_disable();
0199 retval = 0;
0200 }
0201 if (options & WDIOS_ENABLECARD) {
0202 ibwdt_ping();
0203 retval = 0;
0204 }
0205 return retval;
0206 }
0207 case WDIOC_KEEPALIVE:
0208 ibwdt_ping();
0209 break;
0210
0211 case WDIOC_SETTIMEOUT:
0212 if (get_user(new_margin, p))
0213 return -EFAULT;
0214 if (ibwdt_set_heartbeat(new_margin))
0215 return -EINVAL;
0216 ibwdt_ping();
0217 fallthrough;
0218
0219 case WDIOC_GETTIMEOUT:
0220 return put_user(timeout, p);
0221
0222 default:
0223 return -ENOTTY;
0224 }
0225 return 0;
0226 }
0227
0228 static int ibwdt_open(struct inode *inode, struct file *file)
0229 {
0230 if (test_and_set_bit(0, &ibwdt_is_open))
0231 return -EBUSY;
0232 if (nowayout)
0233 __module_get(THIS_MODULE);
0234
0235
0236 ibwdt_ping();
0237 return stream_open(inode, file);
0238 }
0239
0240 static int ibwdt_close(struct inode *inode, struct file *file)
0241 {
0242 if (expect_close == 42) {
0243 ibwdt_disable();
0244 } else {
0245 pr_crit("WDT device closed unexpectedly. WDT will not stop!\n");
0246 ibwdt_ping();
0247 }
0248 clear_bit(0, &ibwdt_is_open);
0249 expect_close = 0;
0250 return 0;
0251 }
0252
0253
0254
0255
0256
0257 static const struct file_operations ibwdt_fops = {
0258 .owner = THIS_MODULE,
0259 .llseek = no_llseek,
0260 .write = ibwdt_write,
0261 .unlocked_ioctl = ibwdt_ioctl,
0262 .compat_ioctl = compat_ptr_ioctl,
0263 .open = ibwdt_open,
0264 .release = ibwdt_close,
0265 };
0266
0267 static struct miscdevice ibwdt_miscdev = {
0268 .minor = WATCHDOG_MINOR,
0269 .name = "watchdog",
0270 .fops = &ibwdt_fops,
0271 };
0272
0273
0274
0275
0276
0277 static int __init ibwdt_probe(struct platform_device *dev)
0278 {
0279 int res;
0280
0281 #if WDT_START != WDT_STOP
0282 if (!request_region(WDT_STOP, 1, "IB700 WDT")) {
0283 pr_err("STOP method I/O %X is not available\n", WDT_STOP);
0284 res = -EIO;
0285 goto out_nostopreg;
0286 }
0287 #endif
0288
0289 if (!request_region(WDT_START, 1, "IB700 WDT")) {
0290 pr_err("START method I/O %X is not available\n", WDT_START);
0291 res = -EIO;
0292 goto out_nostartreg;
0293 }
0294
0295
0296
0297 if (ibwdt_set_heartbeat(timeout)) {
0298 ibwdt_set_heartbeat(WATCHDOG_TIMEOUT);
0299 pr_info("timeout value must be 0<=x<=30, using %d\n", timeout);
0300 }
0301
0302 res = misc_register(&ibwdt_miscdev);
0303 if (res) {
0304 pr_err("failed to register misc device\n");
0305 goto out_nomisc;
0306 }
0307 return 0;
0308
0309 out_nomisc:
0310 release_region(WDT_START, 1);
0311 out_nostartreg:
0312 #if WDT_START != WDT_STOP
0313 release_region(WDT_STOP, 1);
0314 #endif
0315 out_nostopreg:
0316 return res;
0317 }
0318
0319 static int ibwdt_remove(struct platform_device *dev)
0320 {
0321 misc_deregister(&ibwdt_miscdev);
0322 release_region(WDT_START, 1);
0323 #if WDT_START != WDT_STOP
0324 release_region(WDT_STOP, 1);
0325 #endif
0326 return 0;
0327 }
0328
0329 static void ibwdt_shutdown(struct platform_device *dev)
0330 {
0331
0332 ibwdt_disable();
0333 }
0334
0335 static struct platform_driver ibwdt_driver = {
0336 .remove = ibwdt_remove,
0337 .shutdown = ibwdt_shutdown,
0338 .driver = {
0339 .name = DRV_NAME,
0340 },
0341 };
0342
0343 static int __init ibwdt_init(void)
0344 {
0345 int err;
0346
0347 pr_info("WDT driver for IB700 single board computer initialising\n");
0348
0349 ibwdt_platform_device = platform_device_register_simple(DRV_NAME,
0350 -1, NULL, 0);
0351 if (IS_ERR(ibwdt_platform_device))
0352 return PTR_ERR(ibwdt_platform_device);
0353
0354 err = platform_driver_probe(&ibwdt_driver, ibwdt_probe);
0355 if (err)
0356 goto unreg_platform_device;
0357
0358 return 0;
0359
0360 unreg_platform_device:
0361 platform_device_unregister(ibwdt_platform_device);
0362 return err;
0363 }
0364
0365 static void __exit ibwdt_exit(void)
0366 {
0367 platform_device_unregister(ibwdt_platform_device);
0368 platform_driver_unregister(&ibwdt_driver);
0369 pr_info("Watchdog Module Unloaded\n");
0370 }
0371
0372 module_init(ibwdt_init);
0373 module_exit(ibwdt_exit);
0374
0375 MODULE_AUTHOR("Charles Howes <chowes@vsol.net>");
0376 MODULE_DESCRIPTION("IB700 SBC watchdog driver");
0377 MODULE_LICENSE("GPL");
0378
0379