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
0052
0053
0054
0055 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0056
0057 #include <linux/module.h> /* For module specific items */
0058 #include <linux/moduleparam.h> /* For new moduleparam's */
0059 #include <linux/types.h> /* For standard types (like size_t) */
0060 #include <linux/errno.h> /* For the -ENODEV/... values */
0061 #include <linux/kernel.h> /* For printk/panic/... */
0062 #include <linux/delay.h> /* For mdelay function */
0063 #include <linux/timer.h> /* For timer related operations */
0064 #include <linux/jiffies.h> /* For jiffies stuff */
0065 #include <linux/miscdevice.h> /* For struct miscdevice */
0066 #include <linux/watchdog.h> /* For the watchdog specific items */
0067 #include <linux/reboot.h> /* For kernel_power_off() */
0068 #include <linux/init.h> /* For __init/__exit/... */
0069 #include <linux/fs.h> /* For file operations */
0070 #include <linux/isa.h> /* For isa devices */
0071 #include <linux/ioport.h> /* For io-port access */
0072 #include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */
0073 #include <linux/uaccess.h> /* For copy_to_user/put_user/... */
0074 #include <linux/io.h> /* For inb/outb/... */
0075
0076
0077 #define WATCHDOG_VERSION "1.20"
0078 #define WATCHDOG_DATE "18 Feb 2007"
0079 #define WATCHDOG_DRIVER_NAME "ISA-PC Watchdog"
0080 #define WATCHDOG_NAME "pcwd"
0081 #define DRIVER_VERSION WATCHDOG_DRIVER_NAME " driver, v" WATCHDOG_VERSION "\n"
0082
0083
0084
0085
0086
0087
0088
0089
0090 #define PCWD_REVISION_A 1
0091 #define PCWD_REVISION_C 2
0092
0093
0094
0095
0096
0097
0098
0099 #define PCWD_ISA_NR_CARDS 3
0100 static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 };
0101
0102
0103
0104
0105
0106
0107 #define WD_WDRST 0x01
0108 #define WD_T110 0x02
0109 #define WD_HRTBT 0x04
0110 #define WD_RLY2 0x08
0111 #define WD_SRLY2 0x80
0112
0113 #define WD_REVC_WTRP 0x01
0114 #define WD_REVC_HRBT 0x02
0115 #define WD_REVC_TTRP 0x04
0116 #define WD_REVC_RL2A 0x08
0117
0118 #define WD_REVC_RL1A 0x10
0119 #define WD_REVC_R2DS 0x40
0120 #define WD_REVC_RLY2 0x80
0121
0122 #define WD_WDIS 0x10
0123 #define WD_ENTP 0x20
0124 #define WD_SSEL 0x40
0125
0126 #define WD_WCMD 0x80
0127
0128
0129
0130 #define ISA_COMMAND_TIMEOUT 1000
0131
0132
0133 #define CMD_ISA_IDLE 0x00
0134 #define CMD_ISA_VERSION_INTEGER 0x01
0135 #define CMD_ISA_VERSION_TENTH 0x02
0136 #define CMD_ISA_VERSION_HUNDRETH 0x03
0137 #define CMD_ISA_VERSION_MINOR 0x04
0138 #define CMD_ISA_SWITCH_SETTINGS 0x05
0139 #define CMD_ISA_RESET_PC 0x06
0140 #define CMD_ISA_ARM_0 0x07
0141 #define CMD_ISA_ARM_30 0x08
0142 #define CMD_ISA_ARM_60 0x09
0143 #define CMD_ISA_DELAY_TIME_2SECS 0x0A
0144 #define CMD_ISA_DELAY_TIME_4SECS 0x0B
0145 #define CMD_ISA_DELAY_TIME_8SECS 0x0C
0146 #define CMD_ISA_RESET_RELAYS 0x0D
0147
0148
0149 static const int heartbeat_tbl[] = {
0150 20,
0151 40,
0152 60,
0153 300,
0154 600,
0155 1800,
0156 3600,
0157 7200,
0158 };
0159
0160
0161
0162
0163
0164
0165
0166 #define WDT_INTERVAL (HZ/2+1)
0167
0168
0169 static int cards_found;
0170
0171
0172 static unsigned long open_allowed;
0173 static char expect_close;
0174 static int temp_panic;
0175
0176
0177 static struct {
0178 char fw_ver_str[6];
0179 int revision;
0180 int supports_temp;
0181
0182 int command_mode;
0183
0184 int boot_status;
0185 int io_addr;
0186 spinlock_t io_lock;
0187 struct timer_list timer;
0188 unsigned long next_heartbeat;
0189 } pcwd_private;
0190
0191
0192 #define QUIET 0
0193 #define VERBOSE 1
0194 #define DEBUG 2
0195 static int debug = QUIET;
0196 module_param(debug, int, 0);
0197 MODULE_PARM_DESC(debug,
0198 "Debug level: 0=Quiet, 1=Verbose, 2=Debug (default=0)");
0199
0200
0201 #define WATCHDOG_HEARTBEAT 0
0202 static int heartbeat = WATCHDOG_HEARTBEAT;
0203 module_param(heartbeat, int, 0);
0204 MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. "
0205 "(2 <= heartbeat <= 7200 or 0=delay-time from dip-switches, default="
0206 __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
0207
0208 static bool nowayout = WATCHDOG_NOWAYOUT;
0209 module_param(nowayout, bool, 0);
0210 MODULE_PARM_DESC(nowayout,
0211 "Watchdog cannot be stopped once started (default="
0212 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0213
0214
0215
0216
0217
0218 static int send_isa_command(int cmd)
0219 {
0220 int i;
0221 int control_status;
0222 int port0, last_port0;
0223
0224 if (debug >= DEBUG)
0225 pr_debug("sending following data cmd=0x%02x\n", cmd);
0226
0227
0228 control_status = (cmd & 0x0F) | WD_WCMD;
0229 outb_p(control_status, pcwd_private.io_addr + 2);
0230 udelay(ISA_COMMAND_TIMEOUT);
0231
0232 port0 = inb_p(pcwd_private.io_addr);
0233 for (i = 0; i < 25; ++i) {
0234 last_port0 = port0;
0235 port0 = inb_p(pcwd_private.io_addr);
0236
0237 if (port0 == last_port0)
0238 break;
0239
0240 udelay(250);
0241 }
0242
0243 if (debug >= DEBUG)
0244 pr_debug("received following data for cmd=0x%02x: port0=0x%02x last_port0=0x%02x\n",
0245 cmd, port0, last_port0);
0246
0247 return port0;
0248 }
0249
0250 static int set_command_mode(void)
0251 {
0252 int i, found = 0, count = 0;
0253
0254
0255 spin_lock(&pcwd_private.io_lock);
0256 while ((!found) && (count < 3)) {
0257 i = send_isa_command(CMD_ISA_IDLE);
0258
0259 if (i == 0x00)
0260 found = 1;
0261 else if (i == 0xF3) {
0262
0263 outb_p(0x00, pcwd_private.io_addr + 2);
0264 udelay(1200);
0265 outb_p(0x00, pcwd_private.io_addr + 2);
0266 udelay(ISA_COMMAND_TIMEOUT);
0267 }
0268 count++;
0269 }
0270 spin_unlock(&pcwd_private.io_lock);
0271 pcwd_private.command_mode = found;
0272
0273 if (debug >= DEBUG)
0274 pr_debug("command_mode=%d\n", pcwd_private.command_mode);
0275
0276 return found;
0277 }
0278
0279 static void unset_command_mode(void)
0280 {
0281
0282 spin_lock(&pcwd_private.io_lock);
0283 outb_p(0x00, pcwd_private.io_addr + 2);
0284 udelay(ISA_COMMAND_TIMEOUT);
0285 spin_unlock(&pcwd_private.io_lock);
0286
0287 pcwd_private.command_mode = 0;
0288
0289 if (debug >= DEBUG)
0290 pr_debug("command_mode=%d\n", pcwd_private.command_mode);
0291 }
0292
0293 static inline void pcwd_check_temperature_support(void)
0294 {
0295 if (inb(pcwd_private.io_addr) != 0xF0)
0296 pcwd_private.supports_temp = 1;
0297 }
0298
0299 static inline void pcwd_get_firmware(void)
0300 {
0301 int one, ten, hund, minor;
0302
0303 strcpy(pcwd_private.fw_ver_str, "ERROR");
0304
0305 if (set_command_mode()) {
0306 one = send_isa_command(CMD_ISA_VERSION_INTEGER);
0307 ten = send_isa_command(CMD_ISA_VERSION_TENTH);
0308 hund = send_isa_command(CMD_ISA_VERSION_HUNDRETH);
0309 minor = send_isa_command(CMD_ISA_VERSION_MINOR);
0310 sprintf(pcwd_private.fw_ver_str, "%c.%c%c%c",
0311 one, ten, hund, minor);
0312 }
0313 unset_command_mode();
0314
0315 return;
0316 }
0317
0318 static inline int pcwd_get_option_switches(void)
0319 {
0320 int option_switches = 0;
0321
0322 if (set_command_mode()) {
0323
0324 option_switches = send_isa_command(CMD_ISA_SWITCH_SETTINGS);
0325 }
0326
0327 unset_command_mode();
0328 return option_switches;
0329 }
0330
0331 static void pcwd_show_card_info(void)
0332 {
0333 int option_switches;
0334
0335
0336 if (pcwd_private.revision == PCWD_REVISION_A)
0337 pr_info("ISA-PC Watchdog (REV.A) detected at port 0x%04x\n",
0338 pcwd_private.io_addr);
0339 else if (pcwd_private.revision == PCWD_REVISION_C) {
0340 pcwd_get_firmware();
0341 pr_info("ISA-PC Watchdog (REV.C) detected at port 0x%04x (Firmware version: %s)\n",
0342 pcwd_private.io_addr, pcwd_private.fw_ver_str);
0343 option_switches = pcwd_get_option_switches();
0344 pr_info("Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n",
0345 option_switches,
0346 ((option_switches & 0x10) ? "ON" : "OFF"),
0347 ((option_switches & 0x08) ? "ON" : "OFF"));
0348
0349
0350 if (set_command_mode()) {
0351 send_isa_command(CMD_ISA_DELAY_TIME_2SECS);
0352 unset_command_mode();
0353 }
0354 }
0355
0356 if (pcwd_private.supports_temp)
0357 pr_info("Temperature Option Detected\n");
0358
0359 if (pcwd_private.boot_status & WDIOF_CARDRESET)
0360 pr_info("Previous reboot was caused by the card\n");
0361
0362 if (pcwd_private.boot_status & WDIOF_OVERHEAT) {
0363 pr_emerg("Card senses a CPU Overheat. Panicking!\n");
0364 pr_emerg("CPU Overheat\n");
0365 }
0366
0367 if (pcwd_private.boot_status == 0)
0368 pr_info("No previous trip detected - Cold boot or reset\n");
0369 }
0370
0371 static void pcwd_timer_ping(struct timer_list *unused)
0372 {
0373 int wdrst_stat;
0374
0375
0376
0377 if (time_before(jiffies, pcwd_private.next_heartbeat)) {
0378
0379 spin_lock(&pcwd_private.io_lock);
0380 if (pcwd_private.revision == PCWD_REVISION_A) {
0381
0382
0383 wdrst_stat = inb_p(pcwd_private.io_addr);
0384 wdrst_stat &= 0x0F;
0385 wdrst_stat |= WD_WDRST;
0386
0387 outb_p(wdrst_stat, pcwd_private.io_addr + 1);
0388 } else {
0389
0390 outb_p(0x00, pcwd_private.io_addr);
0391 }
0392
0393
0394 mod_timer(&pcwd_private.timer, jiffies + WDT_INTERVAL);
0395
0396 spin_unlock(&pcwd_private.io_lock);
0397 } else {
0398 pr_warn("Heartbeat lost! Will not ping the watchdog\n");
0399 }
0400 }
0401
0402 static int pcwd_start(void)
0403 {
0404 int stat_reg;
0405
0406 pcwd_private.next_heartbeat = jiffies + (heartbeat * HZ);
0407
0408
0409 mod_timer(&pcwd_private.timer, jiffies + WDT_INTERVAL);
0410
0411
0412 if (pcwd_private.revision == PCWD_REVISION_C) {
0413 spin_lock(&pcwd_private.io_lock);
0414 outb_p(0x00, pcwd_private.io_addr + 3);
0415 udelay(ISA_COMMAND_TIMEOUT);
0416 stat_reg = inb_p(pcwd_private.io_addr + 2);
0417 spin_unlock(&pcwd_private.io_lock);
0418 if (stat_reg & WD_WDIS) {
0419 pr_info("Could not start watchdog\n");
0420 return -EIO;
0421 }
0422 }
0423
0424 if (debug >= VERBOSE)
0425 pr_debug("Watchdog started\n");
0426
0427 return 0;
0428 }
0429
0430 static int pcwd_stop(void)
0431 {
0432 int stat_reg;
0433
0434
0435 del_timer(&pcwd_private.timer);
0436
0437
0438 if (pcwd_private.revision == PCWD_REVISION_C) {
0439 spin_lock(&pcwd_private.io_lock);
0440 outb_p(0xA5, pcwd_private.io_addr + 3);
0441 udelay(ISA_COMMAND_TIMEOUT);
0442 outb_p(0xA5, pcwd_private.io_addr + 3);
0443 udelay(ISA_COMMAND_TIMEOUT);
0444 stat_reg = inb_p(pcwd_private.io_addr + 2);
0445 spin_unlock(&pcwd_private.io_lock);
0446 if ((stat_reg & WD_WDIS) == 0) {
0447 pr_info("Could not stop watchdog\n");
0448 return -EIO;
0449 }
0450 }
0451
0452 if (debug >= VERBOSE)
0453 pr_debug("Watchdog stopped\n");
0454
0455 return 0;
0456 }
0457
0458 static int pcwd_keepalive(void)
0459 {
0460
0461 pcwd_private.next_heartbeat = jiffies + (heartbeat * HZ);
0462
0463 if (debug >= DEBUG)
0464 pr_debug("Watchdog keepalive signal send\n");
0465
0466 return 0;
0467 }
0468
0469 static int pcwd_set_heartbeat(int t)
0470 {
0471 if (t < 2 || t > 7200)
0472 return -EINVAL;
0473
0474 heartbeat = t;
0475
0476 if (debug >= VERBOSE)
0477 pr_debug("New heartbeat: %d\n", heartbeat);
0478
0479 return 0;
0480 }
0481
0482 static int pcwd_get_status(int *status)
0483 {
0484 int control_status;
0485
0486 *status = 0;
0487 spin_lock(&pcwd_private.io_lock);
0488 if (pcwd_private.revision == PCWD_REVISION_A)
0489
0490
0491
0492 control_status = inb(pcwd_private.io_addr);
0493 else {
0494
0495
0496
0497
0498
0499 control_status = inb(pcwd_private.io_addr + 1);
0500 }
0501 spin_unlock(&pcwd_private.io_lock);
0502
0503 if (pcwd_private.revision == PCWD_REVISION_A) {
0504 if (control_status & WD_WDRST)
0505 *status |= WDIOF_CARDRESET;
0506
0507 if (control_status & WD_T110) {
0508 *status |= WDIOF_OVERHEAT;
0509 if (temp_panic) {
0510 pr_info("Temperature overheat trip!\n");
0511 kernel_power_off();
0512 }
0513 }
0514 } else {
0515 if (control_status & WD_REVC_WTRP)
0516 *status |= WDIOF_CARDRESET;
0517
0518 if (control_status & WD_REVC_TTRP) {
0519 *status |= WDIOF_OVERHEAT;
0520 if (temp_panic) {
0521 pr_info("Temperature overheat trip!\n");
0522 kernel_power_off();
0523 }
0524 }
0525 }
0526
0527 return 0;
0528 }
0529
0530 static int pcwd_clear_status(void)
0531 {
0532 int control_status;
0533
0534 if (pcwd_private.revision == PCWD_REVISION_C) {
0535 spin_lock(&pcwd_private.io_lock);
0536
0537 if (debug >= VERBOSE)
0538 pr_info("clearing watchdog trip status\n");
0539
0540 control_status = inb_p(pcwd_private.io_addr + 1);
0541
0542 if (debug >= DEBUG) {
0543 pr_debug("status was: 0x%02x\n", control_status);
0544 pr_debug("sending: 0x%02x\n",
0545 (control_status & WD_REVC_R2DS));
0546 }
0547
0548
0549 outb_p((control_status & WD_REVC_R2DS),
0550 pcwd_private.io_addr + 1);
0551
0552 spin_unlock(&pcwd_private.io_lock);
0553 }
0554 return 0;
0555 }
0556
0557 static int pcwd_get_temperature(int *temperature)
0558 {
0559
0560 if (pcwd_private.command_mode)
0561 return -1;
0562
0563 *temperature = 0;
0564 if (!pcwd_private.supports_temp)
0565 return -ENODEV;
0566
0567
0568
0569
0570
0571 spin_lock(&pcwd_private.io_lock);
0572 *temperature = ((inb(pcwd_private.io_addr)) * 9 / 5) + 32;
0573 spin_unlock(&pcwd_private.io_lock);
0574
0575 if (debug >= DEBUG) {
0576 pr_debug("temperature is: %d F\n", *temperature);
0577 }
0578
0579 return 0;
0580 }
0581
0582
0583
0584
0585
0586 static long pcwd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
0587 {
0588 int rv;
0589 int status;
0590 int temperature;
0591 int new_heartbeat;
0592 int __user *argp = (int __user *)arg;
0593 static const struct watchdog_info ident = {
0594 .options = WDIOF_OVERHEAT |
0595 WDIOF_CARDRESET |
0596 WDIOF_KEEPALIVEPING |
0597 WDIOF_SETTIMEOUT |
0598 WDIOF_MAGICCLOSE,
0599 .firmware_version = 1,
0600 .identity = "PCWD",
0601 };
0602
0603 switch (cmd) {
0604 case WDIOC_GETSUPPORT:
0605 if (copy_to_user(argp, &ident, sizeof(ident)))
0606 return -EFAULT;
0607 return 0;
0608
0609 case WDIOC_GETSTATUS:
0610 pcwd_get_status(&status);
0611 return put_user(status, argp);
0612
0613 case WDIOC_GETBOOTSTATUS:
0614 return put_user(pcwd_private.boot_status, argp);
0615
0616 case WDIOC_GETTEMP:
0617 if (pcwd_get_temperature(&temperature))
0618 return -EFAULT;
0619
0620 return put_user(temperature, argp);
0621
0622 case WDIOC_SETOPTIONS:
0623 if (pcwd_private.revision == PCWD_REVISION_C) {
0624 if (get_user(rv, argp))
0625 return -EFAULT;
0626
0627 if (rv & WDIOS_DISABLECARD) {
0628 status = pcwd_stop();
0629 if (status < 0)
0630 return status;
0631 }
0632 if (rv & WDIOS_ENABLECARD) {
0633 status = pcwd_start();
0634 if (status < 0)
0635 return status;
0636 }
0637 if (rv & WDIOS_TEMPPANIC)
0638 temp_panic = 1;
0639 }
0640 return -EINVAL;
0641
0642 case WDIOC_KEEPALIVE:
0643 pcwd_keepalive();
0644 return 0;
0645
0646 case WDIOC_SETTIMEOUT:
0647 if (get_user(new_heartbeat, argp))
0648 return -EFAULT;
0649
0650 if (pcwd_set_heartbeat(new_heartbeat))
0651 return -EINVAL;
0652
0653 pcwd_keepalive();
0654 fallthrough;
0655
0656 case WDIOC_GETTIMEOUT:
0657 return put_user(heartbeat, argp);
0658
0659 default:
0660 return -ENOTTY;
0661 }
0662
0663 return 0;
0664 }
0665
0666 static ssize_t pcwd_write(struct file *file, const char __user *buf, size_t len,
0667 loff_t *ppos)
0668 {
0669 if (len) {
0670 if (!nowayout) {
0671 size_t i;
0672
0673
0674 expect_close = 0;
0675
0676 for (i = 0; i != len; i++) {
0677 char c;
0678
0679 if (get_user(c, buf + i))
0680 return -EFAULT;
0681 if (c == 'V')
0682 expect_close = 42;
0683 }
0684 }
0685 pcwd_keepalive();
0686 }
0687 return len;
0688 }
0689
0690 static int pcwd_open(struct inode *inode, struct file *file)
0691 {
0692 if (test_and_set_bit(0, &open_allowed))
0693 return -EBUSY;
0694 if (nowayout)
0695 __module_get(THIS_MODULE);
0696
0697 pcwd_start();
0698 pcwd_keepalive();
0699 return stream_open(inode, file);
0700 }
0701
0702 static int pcwd_close(struct inode *inode, struct file *file)
0703 {
0704 if (expect_close == 42)
0705 pcwd_stop();
0706 else {
0707 pr_crit("Unexpected close, not stopping watchdog!\n");
0708 pcwd_keepalive();
0709 }
0710 expect_close = 0;
0711 clear_bit(0, &open_allowed);
0712 return 0;
0713 }
0714
0715
0716
0717
0718
0719 static ssize_t pcwd_temp_read(struct file *file, char __user *buf, size_t count,
0720 loff_t *ppos)
0721 {
0722 int temperature;
0723
0724 if (pcwd_get_temperature(&temperature))
0725 return -EFAULT;
0726
0727 if (copy_to_user(buf, &temperature, 1))
0728 return -EFAULT;
0729
0730 return 1;
0731 }
0732
0733 static int pcwd_temp_open(struct inode *inode, struct file *file)
0734 {
0735 if (!pcwd_private.supports_temp)
0736 return -ENODEV;
0737
0738 return stream_open(inode, file);
0739 }
0740
0741 static int pcwd_temp_close(struct inode *inode, struct file *file)
0742 {
0743 return 0;
0744 }
0745
0746
0747
0748
0749
0750 static const struct file_operations pcwd_fops = {
0751 .owner = THIS_MODULE,
0752 .llseek = no_llseek,
0753 .write = pcwd_write,
0754 .unlocked_ioctl = pcwd_ioctl,
0755 .compat_ioctl = compat_ptr_ioctl,
0756 .open = pcwd_open,
0757 .release = pcwd_close,
0758 };
0759
0760 static struct miscdevice pcwd_miscdev = {
0761 .minor = WATCHDOG_MINOR,
0762 .name = "watchdog",
0763 .fops = &pcwd_fops,
0764 };
0765
0766 static const struct file_operations pcwd_temp_fops = {
0767 .owner = THIS_MODULE,
0768 .llseek = no_llseek,
0769 .read = pcwd_temp_read,
0770 .open = pcwd_temp_open,
0771 .release = pcwd_temp_close,
0772 };
0773
0774 static struct miscdevice temp_miscdev = {
0775 .minor = TEMP_MINOR,
0776 .name = "temperature",
0777 .fops = &pcwd_temp_fops,
0778 };
0779
0780
0781
0782
0783
0784 static inline int get_revision(void)
0785 {
0786 int r = PCWD_REVISION_C;
0787
0788 spin_lock(&pcwd_private.io_lock);
0789
0790
0791 if ((inb(pcwd_private.io_addr + 2) == 0xFF) ||
0792 (inb(pcwd_private.io_addr + 3) == 0xFF))
0793 r = PCWD_REVISION_A;
0794 spin_unlock(&pcwd_private.io_lock);
0795
0796 return r;
0797 }
0798
0799
0800
0801
0802
0803
0804
0805
0806 static int pcwd_isa_match(struct device *dev, unsigned int id)
0807 {
0808 int base_addr = pcwd_ioports[id];
0809 int port0, last_port0;
0810 int port1, last_port1;
0811 int i;
0812 int retval;
0813
0814 if (debug >= DEBUG)
0815 pr_debug("pcwd_isa_match id=%d\n", id);
0816
0817 if (!request_region(base_addr, 4, "PCWD")) {
0818 pr_info("Port 0x%04x unavailable\n", base_addr);
0819 return 0;
0820 }
0821
0822 retval = 0;
0823
0824 port0 = inb_p(base_addr);
0825 port1 = inb_p(base_addr + 1);
0826 if (port0 != 0xff || port1 != 0xff) {
0827
0828 for (i = 0; i < 4; ++i) {
0829
0830 msleep(500);
0831
0832 last_port0 = port0;
0833 last_port1 = port1;
0834
0835 port0 = inb_p(base_addr);
0836 port1 = inb_p(base_addr + 1);
0837
0838
0839 if ((port0 ^ last_port0) & WD_HRTBT ||
0840 (port1 ^ last_port1) & WD_REVC_HRBT) {
0841 retval = 1;
0842 break;
0843 }
0844 }
0845 }
0846 release_region(base_addr, 4);
0847
0848 return retval;
0849 }
0850
0851 static int pcwd_isa_probe(struct device *dev, unsigned int id)
0852 {
0853 int ret;
0854
0855 if (debug >= DEBUG)
0856 pr_debug("pcwd_isa_probe id=%d\n", id);
0857
0858 cards_found++;
0859 if (cards_found == 1)
0860 pr_info("v%s Ken Hollis (kenji@bitgate.com)\n",
0861 WATCHDOG_VERSION);
0862
0863 if (cards_found > 1) {
0864 pr_err("This driver only supports 1 device\n");
0865 return -ENODEV;
0866 }
0867
0868 if (pcwd_ioports[id] == 0x0000) {
0869 pr_err("No I/O-Address for card detected\n");
0870 return -ENODEV;
0871 }
0872 pcwd_private.io_addr = pcwd_ioports[id];
0873
0874 spin_lock_init(&pcwd_private.io_lock);
0875
0876
0877 pcwd_private.revision = get_revision();
0878
0879 if (!request_region(pcwd_private.io_addr,
0880 (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4, "PCWD")) {
0881 pr_err("I/O address 0x%04x already in use\n",
0882 pcwd_private.io_addr);
0883 ret = -EIO;
0884 goto error_request_region;
0885 }
0886
0887
0888 pcwd_private.supports_temp = 0;
0889 temp_panic = 0;
0890 pcwd_private.boot_status = 0x0000;
0891
0892
0893 pcwd_get_status(&pcwd_private.boot_status);
0894
0895
0896 pcwd_clear_status();
0897
0898 timer_setup(&pcwd_private.timer, pcwd_timer_ping, 0);
0899
0900
0901 pcwd_stop();
0902
0903
0904 pcwd_check_temperature_support();
0905
0906
0907 pcwd_show_card_info();
0908
0909
0910 if (heartbeat == 0)
0911 heartbeat = heartbeat_tbl[(pcwd_get_option_switches() & 0x07)];
0912
0913
0914
0915 if (pcwd_set_heartbeat(heartbeat)) {
0916 pcwd_set_heartbeat(WATCHDOG_HEARTBEAT);
0917 pr_info("heartbeat value must be 2 <= heartbeat <= 7200, using %d\n",
0918 WATCHDOG_HEARTBEAT);
0919 }
0920
0921 if (pcwd_private.supports_temp) {
0922 ret = misc_register(&temp_miscdev);
0923 if (ret) {
0924 pr_err("cannot register miscdev on minor=%d (err=%d)\n",
0925 TEMP_MINOR, ret);
0926 goto error_misc_register_temp;
0927 }
0928 }
0929
0930 ret = misc_register(&pcwd_miscdev);
0931 if (ret) {
0932 pr_err("cannot register miscdev on minor=%d (err=%d)\n",
0933 WATCHDOG_MINOR, ret);
0934 goto error_misc_register_watchdog;
0935 }
0936
0937 pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
0938 heartbeat, nowayout);
0939
0940 return 0;
0941
0942 error_misc_register_watchdog:
0943 if (pcwd_private.supports_temp)
0944 misc_deregister(&temp_miscdev);
0945 error_misc_register_temp:
0946 release_region(pcwd_private.io_addr,
0947 (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4);
0948 error_request_region:
0949 pcwd_private.io_addr = 0x0000;
0950 cards_found--;
0951 return ret;
0952 }
0953
0954 static void pcwd_isa_remove(struct device *dev, unsigned int id)
0955 {
0956 if (debug >= DEBUG)
0957 pr_debug("pcwd_isa_remove id=%d\n", id);
0958
0959
0960 if (!nowayout)
0961 pcwd_stop();
0962
0963
0964 misc_deregister(&pcwd_miscdev);
0965 if (pcwd_private.supports_temp)
0966 misc_deregister(&temp_miscdev);
0967 release_region(pcwd_private.io_addr,
0968 (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4);
0969 pcwd_private.io_addr = 0x0000;
0970 cards_found--;
0971 }
0972
0973 static void pcwd_isa_shutdown(struct device *dev, unsigned int id)
0974 {
0975 if (debug >= DEBUG)
0976 pr_debug("pcwd_isa_shutdown id=%d\n", id);
0977
0978 pcwd_stop();
0979 }
0980
0981 static struct isa_driver pcwd_isa_driver = {
0982 .match = pcwd_isa_match,
0983 .probe = pcwd_isa_probe,
0984 .remove = pcwd_isa_remove,
0985 .shutdown = pcwd_isa_shutdown,
0986 .driver = {
0987 .owner = THIS_MODULE,
0988 .name = WATCHDOG_NAME,
0989 },
0990 };
0991
0992 module_isa_driver(pcwd_isa_driver, PCWD_ISA_NR_CARDS);
0993
0994 MODULE_AUTHOR("Ken Hollis <kenji@bitgate.com>, "
0995 "Wim Van Sebroeck <wim@iguana.be>");
0996 MODULE_DESCRIPTION("Berkshire ISA-PC Watchdog driver");
0997 MODULE_VERSION(WATCHDOG_VERSION);
0998 MODULE_LICENSE("GPL");