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 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0030
0031 #include <linux/module.h>
0032 #include <linux/moduleparam.h>
0033 #include <linux/miscdevice.h>
0034 #include <linux/watchdog.h>
0035 #include <linux/ioport.h>
0036 #include <linux/spinlock.h>
0037 #include <linux/notifier.h>
0038 #include <linux/reboot.h>
0039 #include <linux/init.h>
0040 #include <linux/pnp.h>
0041 #include <linux/fs.h>
0042 #include <linux/semaphore.h>
0043 #include <linux/io.h>
0044 #include <linux/uaccess.h>
0045
0046 #define SC1200_MODULE_VER "build 20020303"
0047 #define SC1200_MODULE_NAME "sc1200wdt"
0048
0049 #define MAX_TIMEOUT 255
0050 #define PMIR (io)
0051 #define PMDR (io+1)
0052
0053
0054 #define FER1 0x00
0055 #define FER2 0x01
0056 #define PMC1 0x02
0057 #define PMC2 0x03
0058 #define PMC3 0x04
0059 #define WDTO 0x05
0060 #define WDCF 0x06
0061 #define WDST 0x07
0062
0063
0064 #define KBC_IRQ 0x01
0065 #define MSE_IRQ 0x02
0066 #define UART1_IRQ 0x03
0067 #define UART2_IRQ 0x04
0068
0069
0070 static int timeout = 1;
0071 static int io = -1;
0072 static int io_len = 2;
0073 static unsigned long open_flag;
0074 static char expect_close;
0075 static DEFINE_SPINLOCK(sc1200wdt_lock);
0076
0077 #if defined CONFIG_PNP
0078 static int isapnp = 1;
0079 static struct pnp_dev *wdt_dev;
0080
0081 module_param(isapnp, int, 0);
0082 MODULE_PARM_DESC(isapnp,
0083 "When set to 0 driver ISA PnP support will be disabled");
0084 #endif
0085
0086 module_param_hw(io, int, ioport, 0);
0087 MODULE_PARM_DESC(io, "io port");
0088 module_param(timeout, int, 0);
0089 MODULE_PARM_DESC(timeout, "range is 0-255 minutes, default is 1");
0090
0091 static bool nowayout = WATCHDOG_NOWAYOUT;
0092 module_param(nowayout, bool, 0);
0093 MODULE_PARM_DESC(nowayout,
0094 "Watchdog cannot be stopped once started (default="
0095 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0096
0097
0098
0099
0100 static inline void __sc1200wdt_read_data(unsigned char index,
0101 unsigned char *data)
0102 {
0103 outb_p(index, PMIR);
0104 *data = inb(PMDR);
0105 }
0106
0107 static void sc1200wdt_read_data(unsigned char index, unsigned char *data)
0108 {
0109 spin_lock(&sc1200wdt_lock);
0110 __sc1200wdt_read_data(index, data);
0111 spin_unlock(&sc1200wdt_lock);
0112 }
0113
0114
0115 static inline void __sc1200wdt_write_data(unsigned char index,
0116 unsigned char data)
0117 {
0118 outb_p(index, PMIR);
0119 outb(data, PMDR);
0120 }
0121
0122 static inline void sc1200wdt_write_data(unsigned char index,
0123 unsigned char data)
0124 {
0125 spin_lock(&sc1200wdt_lock);
0126 __sc1200wdt_write_data(index, data);
0127 spin_unlock(&sc1200wdt_lock);
0128 }
0129
0130
0131 static void sc1200wdt_start(void)
0132 {
0133 unsigned char reg;
0134 spin_lock(&sc1200wdt_lock);
0135
0136 __sc1200wdt_read_data(WDCF, ®);
0137
0138 reg |= (KBC_IRQ | MSE_IRQ | UART1_IRQ | UART2_IRQ);
0139 __sc1200wdt_write_data(WDCF, reg);
0140
0141 __sc1200wdt_write_data(WDTO, timeout);
0142
0143 spin_unlock(&sc1200wdt_lock);
0144 }
0145
0146 static void sc1200wdt_stop(void)
0147 {
0148 sc1200wdt_write_data(WDTO, 0);
0149 }
0150
0151
0152 static inline int sc1200wdt_status(void)
0153 {
0154 unsigned char ret;
0155
0156 sc1200wdt_read_data(WDST, &ret);
0157
0158
0159
0160
0161 return (ret & 0x01) ? 0 : WDIOF_KEEPALIVEPING;
0162 }
0163
0164 static int sc1200wdt_open(struct inode *inode, struct file *file)
0165 {
0166
0167 if (test_and_set_bit(0, &open_flag))
0168 return -EBUSY;
0169
0170 if (timeout > MAX_TIMEOUT)
0171 timeout = MAX_TIMEOUT;
0172
0173 sc1200wdt_start();
0174 pr_info("Watchdog enabled, timeout = %d min(s)", timeout);
0175
0176 return stream_open(inode, file);
0177 }
0178
0179
0180 static long sc1200wdt_ioctl(struct file *file, unsigned int cmd,
0181 unsigned long arg)
0182 {
0183 int new_timeout;
0184 void __user *argp = (void __user *)arg;
0185 int __user *p = argp;
0186 static const struct watchdog_info ident = {
0187 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
0188 WDIOF_MAGICCLOSE,
0189 .firmware_version = 0,
0190 .identity = "PC87307/PC97307",
0191 };
0192
0193 switch (cmd) {
0194 case WDIOC_GETSUPPORT:
0195 if (copy_to_user(argp, &ident, sizeof(ident)))
0196 return -EFAULT;
0197 return 0;
0198
0199 case WDIOC_GETSTATUS:
0200 return put_user(sc1200wdt_status(), p);
0201
0202 case WDIOC_GETBOOTSTATUS:
0203 return put_user(0, p);
0204
0205 case WDIOC_SETOPTIONS:
0206 {
0207 int options, retval = -EINVAL;
0208
0209 if (get_user(options, p))
0210 return -EFAULT;
0211
0212 if (options & WDIOS_DISABLECARD) {
0213 sc1200wdt_stop();
0214 retval = 0;
0215 }
0216
0217 if (options & WDIOS_ENABLECARD) {
0218 sc1200wdt_start();
0219 retval = 0;
0220 }
0221
0222 return retval;
0223 }
0224 case WDIOC_KEEPALIVE:
0225 sc1200wdt_write_data(WDTO, timeout);
0226 return 0;
0227
0228 case WDIOC_SETTIMEOUT:
0229 if (get_user(new_timeout, p))
0230 return -EFAULT;
0231
0232 new_timeout /= 60;
0233 if (new_timeout < 0 || new_timeout > MAX_TIMEOUT)
0234 return -EINVAL;
0235 timeout = new_timeout;
0236 sc1200wdt_write_data(WDTO, timeout);
0237 fallthrough;
0238
0239 case WDIOC_GETTIMEOUT:
0240 return put_user(timeout * 60, p);
0241
0242 default:
0243 return -ENOTTY;
0244 }
0245 }
0246
0247
0248 static int sc1200wdt_release(struct inode *inode, struct file *file)
0249 {
0250 if (expect_close == 42) {
0251 sc1200wdt_stop();
0252 pr_info("Watchdog disabled\n");
0253 } else {
0254 sc1200wdt_write_data(WDTO, timeout);
0255 pr_crit("Unexpected close!, timeout = %d min(s)\n", timeout);
0256 }
0257 clear_bit(0, &open_flag);
0258 expect_close = 0;
0259
0260 return 0;
0261 }
0262
0263
0264 static ssize_t sc1200wdt_write(struct file *file, const char __user *data,
0265 size_t len, loff_t *ppos)
0266 {
0267 if (len) {
0268 if (!nowayout) {
0269 size_t i;
0270
0271 expect_close = 0;
0272
0273 for (i = 0; i != len; i++) {
0274 char c;
0275
0276 if (get_user(c, data + i))
0277 return -EFAULT;
0278 if (c == 'V')
0279 expect_close = 42;
0280 }
0281 }
0282
0283 sc1200wdt_write_data(WDTO, timeout);
0284 return len;
0285 }
0286
0287 return 0;
0288 }
0289
0290
0291 static int sc1200wdt_notify_sys(struct notifier_block *this,
0292 unsigned long code, void *unused)
0293 {
0294 if (code == SYS_DOWN || code == SYS_HALT)
0295 sc1200wdt_stop();
0296
0297 return NOTIFY_DONE;
0298 }
0299
0300
0301 static struct notifier_block sc1200wdt_notifier = {
0302 .notifier_call = sc1200wdt_notify_sys,
0303 };
0304
0305 static const struct file_operations sc1200wdt_fops = {
0306 .owner = THIS_MODULE,
0307 .llseek = no_llseek,
0308 .write = sc1200wdt_write,
0309 .unlocked_ioctl = sc1200wdt_ioctl,
0310 .compat_ioctl = compat_ptr_ioctl,
0311 .open = sc1200wdt_open,
0312 .release = sc1200wdt_release,
0313 };
0314
0315 static struct miscdevice sc1200wdt_miscdev = {
0316 .minor = WATCHDOG_MINOR,
0317 .name = "watchdog",
0318 .fops = &sc1200wdt_fops,
0319 };
0320
0321
0322 static int __init sc1200wdt_probe(void)
0323 {
0324
0325
0326
0327
0328
0329
0330
0331 unsigned char reg;
0332
0333 sc1200wdt_read_data(PMC3, ®);
0334 reg &= 0x0f;
0335 return (reg == 0x0e) ? 0 : -ENODEV;
0336 }
0337
0338
0339 #if defined CONFIG_PNP
0340
0341 static const struct pnp_device_id scl200wdt_pnp_devices[] = {
0342
0343 {.id = "NSC0800", .driver_data = 0},
0344 {.id = ""},
0345 };
0346
0347 static int scl200wdt_pnp_probe(struct pnp_dev *dev,
0348 const struct pnp_device_id *dev_id)
0349 {
0350
0351 if (wdt_dev || !isapnp)
0352 return -EBUSY;
0353
0354 wdt_dev = dev;
0355 io = pnp_port_start(wdt_dev, 0);
0356 io_len = pnp_port_len(wdt_dev, 0);
0357
0358 if (!request_region(io, io_len, SC1200_MODULE_NAME)) {
0359 pr_err("Unable to register IO port %#x\n", io);
0360 return -EBUSY;
0361 }
0362
0363 pr_info("PnP device found at io port %#x/%d\n", io, io_len);
0364 return 0;
0365 }
0366
0367 static void scl200wdt_pnp_remove(struct pnp_dev *dev)
0368 {
0369 if (wdt_dev) {
0370 release_region(io, io_len);
0371 wdt_dev = NULL;
0372 }
0373 }
0374
0375 static struct pnp_driver scl200wdt_pnp_driver = {
0376 .name = "scl200wdt",
0377 .id_table = scl200wdt_pnp_devices,
0378 .probe = scl200wdt_pnp_probe,
0379 .remove = scl200wdt_pnp_remove,
0380 };
0381
0382 #endif
0383
0384
0385 static int __init sc1200wdt_init(void)
0386 {
0387 int ret;
0388
0389 pr_info("%s\n", SC1200_MODULE_VER);
0390
0391 #if defined CONFIG_PNP
0392 if (isapnp) {
0393 ret = pnp_register_driver(&scl200wdt_pnp_driver);
0394 if (ret)
0395 goto out_clean;
0396 }
0397 #endif
0398
0399 if (io == -1) {
0400 pr_err("io parameter must be specified\n");
0401 ret = -EINVAL;
0402 goto out_pnp;
0403 }
0404
0405 #if defined CONFIG_PNP
0406
0407
0408 if (isapnp)
0409 pnp_unregister_driver(&scl200wdt_pnp_driver);
0410 isapnp = 0;
0411 #endif
0412
0413 if (!request_region(io, io_len, SC1200_MODULE_NAME)) {
0414 pr_err("Unable to register IO port %#x\n", io);
0415 ret = -EBUSY;
0416 goto out_pnp;
0417 }
0418
0419 ret = sc1200wdt_probe();
0420 if (ret)
0421 goto out_io;
0422
0423 ret = register_reboot_notifier(&sc1200wdt_notifier);
0424 if (ret) {
0425 pr_err("Unable to register reboot notifier err = %d\n", ret);
0426 goto out_io;
0427 }
0428
0429 ret = misc_register(&sc1200wdt_miscdev);
0430 if (ret) {
0431 pr_err("Unable to register miscdev on minor %d\n",
0432 WATCHDOG_MINOR);
0433 goto out_rbt;
0434 }
0435
0436
0437
0438 out_clean:
0439 return ret;
0440
0441 out_rbt:
0442 unregister_reboot_notifier(&sc1200wdt_notifier);
0443
0444 out_io:
0445 release_region(io, io_len);
0446
0447 out_pnp:
0448 #if defined CONFIG_PNP
0449 if (isapnp)
0450 pnp_unregister_driver(&scl200wdt_pnp_driver);
0451 #endif
0452 goto out_clean;
0453 }
0454
0455
0456 static void __exit sc1200wdt_exit(void)
0457 {
0458 misc_deregister(&sc1200wdt_miscdev);
0459 unregister_reboot_notifier(&sc1200wdt_notifier);
0460
0461 #if defined CONFIG_PNP
0462 if (isapnp)
0463 pnp_unregister_driver(&scl200wdt_pnp_driver);
0464 else
0465 #endif
0466 release_region(io, io_len);
0467 }
0468
0469 module_init(sc1200wdt_init);
0470 module_exit(sc1200wdt_exit);
0471
0472 MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>");
0473 MODULE_DESCRIPTION(
0474 "Driver for National Semiconductor PC87307/PC97307 watchdog component");
0475 MODULE_LICENSE("GPL");