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 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0028
0029 #include <linux/module.h>
0030 #include <linux/moduleparam.h>
0031 #include <linux/types.h>
0032 #include <linux/miscdevice.h>
0033 #include <linux/watchdog.h>
0034 #include <linux/fs.h>
0035 #include <linux/ioport.h>
0036 #include <linux/platform_device.h>
0037 #include <linux/init.h>
0038 #include <linux/io.h>
0039 #include <linux/uaccess.h>
0040
0041
0042 #define DRV_NAME "advantechwdt"
0043 #define WATCHDOG_NAME "Advantech WDT"
0044 #define WATCHDOG_TIMEOUT 60
0045
0046
0047 static struct platform_device *advwdt_platform_device;
0048 static unsigned long advwdt_is_open;
0049 static char adv_expect_close;
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062 static int wdt_stop = 0x443;
0063 module_param(wdt_stop, int, 0);
0064 MODULE_PARM_DESC(wdt_stop, "Advantech WDT 'stop' io port (default 0x443)");
0065
0066 static int wdt_start = 0x443;
0067 module_param(wdt_start, int, 0);
0068 MODULE_PARM_DESC(wdt_start, "Advantech WDT 'start' io port (default 0x443)");
0069
0070 static int timeout = WATCHDOG_TIMEOUT;
0071 module_param(timeout, int, 0);
0072 MODULE_PARM_DESC(timeout,
0073 "Watchdog timeout in seconds. 1<= timeout <=63, default="
0074 __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
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 static void advwdt_ping(void)
0087 {
0088
0089 outb_p(timeout, wdt_start);
0090 }
0091
0092 static void advwdt_disable(void)
0093 {
0094 inb_p(wdt_stop);
0095 }
0096
0097 static int advwdt_set_heartbeat(int t)
0098 {
0099 if (t < 1 || t > 63)
0100 return -EINVAL;
0101 timeout = t;
0102 return 0;
0103 }
0104
0105
0106
0107
0108
0109 static ssize_t advwdt_write(struct file *file, const char __user *buf,
0110 size_t count, loff_t *ppos)
0111 {
0112 if (count) {
0113 if (!nowayout) {
0114 size_t i;
0115
0116 adv_expect_close = 0;
0117
0118 for (i = 0; i != count; i++) {
0119 char c;
0120 if (get_user(c, buf + i))
0121 return -EFAULT;
0122 if (c == 'V')
0123 adv_expect_close = 42;
0124 }
0125 }
0126 advwdt_ping();
0127 }
0128 return count;
0129 }
0130
0131 static long advwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
0132 {
0133 int new_timeout;
0134 void __user *argp = (void __user *)arg;
0135 int __user *p = argp;
0136 static const struct watchdog_info ident = {
0137 .options = WDIOF_KEEPALIVEPING |
0138 WDIOF_SETTIMEOUT |
0139 WDIOF_MAGICCLOSE,
0140 .firmware_version = 1,
0141 .identity = WATCHDOG_NAME,
0142 };
0143
0144 switch (cmd) {
0145 case WDIOC_GETSUPPORT:
0146 if (copy_to_user(argp, &ident, sizeof(ident)))
0147 return -EFAULT;
0148 break;
0149
0150 case WDIOC_GETSTATUS:
0151 case WDIOC_GETBOOTSTATUS:
0152 return put_user(0, p);
0153
0154 case WDIOC_SETOPTIONS:
0155 {
0156 int options, retval = -EINVAL;
0157
0158 if (get_user(options, p))
0159 return -EFAULT;
0160 if (options & WDIOS_DISABLECARD) {
0161 advwdt_disable();
0162 retval = 0;
0163 }
0164 if (options & WDIOS_ENABLECARD) {
0165 advwdt_ping();
0166 retval = 0;
0167 }
0168 return retval;
0169 }
0170 case WDIOC_KEEPALIVE:
0171 advwdt_ping();
0172 break;
0173
0174 case WDIOC_SETTIMEOUT:
0175 if (get_user(new_timeout, p))
0176 return -EFAULT;
0177 if (advwdt_set_heartbeat(new_timeout))
0178 return -EINVAL;
0179 advwdt_ping();
0180 fallthrough;
0181 case WDIOC_GETTIMEOUT:
0182 return put_user(timeout, p);
0183 default:
0184 return -ENOTTY;
0185 }
0186 return 0;
0187 }
0188
0189 static int advwdt_open(struct inode *inode, struct file *file)
0190 {
0191 if (test_and_set_bit(0, &advwdt_is_open))
0192 return -EBUSY;
0193
0194
0195
0196
0197 advwdt_ping();
0198 return stream_open(inode, file);
0199 }
0200
0201 static int advwdt_close(struct inode *inode, struct file *file)
0202 {
0203 if (adv_expect_close == 42) {
0204 advwdt_disable();
0205 } else {
0206 pr_crit("Unexpected close, not stopping watchdog!\n");
0207 advwdt_ping();
0208 }
0209 clear_bit(0, &advwdt_is_open);
0210 adv_expect_close = 0;
0211 return 0;
0212 }
0213
0214
0215
0216
0217
0218 static const struct file_operations advwdt_fops = {
0219 .owner = THIS_MODULE,
0220 .llseek = no_llseek,
0221 .write = advwdt_write,
0222 .unlocked_ioctl = advwdt_ioctl,
0223 .compat_ioctl = compat_ptr_ioctl,
0224 .open = advwdt_open,
0225 .release = advwdt_close,
0226 };
0227
0228 static struct miscdevice advwdt_miscdev = {
0229 .minor = WATCHDOG_MINOR,
0230 .name = "watchdog",
0231 .fops = &advwdt_fops,
0232 };
0233
0234
0235
0236
0237
0238 static int __init advwdt_probe(struct platform_device *dev)
0239 {
0240 int ret;
0241
0242 if (wdt_stop != wdt_start) {
0243 if (!request_region(wdt_stop, 1, WATCHDOG_NAME)) {
0244 pr_err("I/O address 0x%04x already in use\n",
0245 wdt_stop);
0246 ret = -EIO;
0247 goto out;
0248 }
0249 }
0250
0251 if (!request_region(wdt_start, 1, WATCHDOG_NAME)) {
0252 pr_err("I/O address 0x%04x already in use\n", wdt_start);
0253 ret = -EIO;
0254 goto unreg_stop;
0255 }
0256
0257
0258
0259 if (advwdt_set_heartbeat(timeout)) {
0260 advwdt_set_heartbeat(WATCHDOG_TIMEOUT);
0261 pr_info("timeout value must be 1<=x<=63, using %d\n", timeout);
0262 }
0263
0264 ret = misc_register(&advwdt_miscdev);
0265 if (ret != 0) {
0266 pr_err("cannot register miscdev on minor=%d (err=%d)\n",
0267 WATCHDOG_MINOR, ret);
0268 goto unreg_regions;
0269 }
0270 pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
0271 timeout, nowayout);
0272 out:
0273 return ret;
0274 unreg_regions:
0275 release_region(wdt_start, 1);
0276 unreg_stop:
0277 if (wdt_stop != wdt_start)
0278 release_region(wdt_stop, 1);
0279 goto out;
0280 }
0281
0282 static int advwdt_remove(struct platform_device *dev)
0283 {
0284 misc_deregister(&advwdt_miscdev);
0285 release_region(wdt_start, 1);
0286 if (wdt_stop != wdt_start)
0287 release_region(wdt_stop, 1);
0288
0289 return 0;
0290 }
0291
0292 static void advwdt_shutdown(struct platform_device *dev)
0293 {
0294
0295 advwdt_disable();
0296 }
0297
0298 static struct platform_driver advwdt_driver = {
0299 .remove = advwdt_remove,
0300 .shutdown = advwdt_shutdown,
0301 .driver = {
0302 .name = DRV_NAME,
0303 },
0304 };
0305
0306 static int __init advwdt_init(void)
0307 {
0308 int err;
0309
0310 pr_info("WDT driver for Advantech single board computer initialising\n");
0311
0312 advwdt_platform_device = platform_device_register_simple(DRV_NAME,
0313 -1, NULL, 0);
0314 if (IS_ERR(advwdt_platform_device))
0315 return PTR_ERR(advwdt_platform_device);
0316
0317 err = platform_driver_probe(&advwdt_driver, advwdt_probe);
0318 if (err)
0319 goto unreg_platform_device;
0320
0321 return 0;
0322
0323 unreg_platform_device:
0324 platform_device_unregister(advwdt_platform_device);
0325 return err;
0326 }
0327
0328 static void __exit advwdt_exit(void)
0329 {
0330 platform_device_unregister(advwdt_platform_device);
0331 platform_driver_unregister(&advwdt_driver);
0332 pr_info("Watchdog Module Unloaded\n");
0333 }
0334
0335 module_init(advwdt_init);
0336 module_exit(advwdt_exit);
0337
0338 MODULE_LICENSE("GPL");
0339 MODULE_AUTHOR("Marek Michalkiewicz <marekm@linux.org.pl>");
0340 MODULE_DESCRIPTION("Advantech Single Board Computer WDT driver");