54 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
56 #include <linux/module.h>
58 #include <linux/types.h>
59 #include <linux/errno.h>
60 #include <linux/kernel.h>
65 #include <linux/watchdog.h>
66 #include <linux/reboot.h>
76 #define WATCHDOG_VERSION "1.20"
77 #define WATCHDOG_DATE "18 Feb 2007"
78 #define WATCHDOG_DRIVER_NAME "ISA-PC Watchdog"
79 #define WATCHDOG_NAME "pcwd"
80 #define DRIVER_VERSION WATCHDOG_DRIVER_NAME " driver, v" WATCHDOG_VERSION "\n"
89 #define PCWD_REVISION_A 1
90 #define PCWD_REVISION_C 2
98 #define PCWD_ISA_NR_CARDS 3
99 static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 };
106 #define WD_WDRST 0x01
108 #define WD_HRTBT 0x04
110 #define WD_SRLY2 0x80
112 #define WD_REVC_WTRP 0x01
113 #define WD_REVC_HRBT 0x02
114 #define WD_REVC_TTRP 0x04
115 #define WD_REVC_RL2A 0x08
117 #define WD_REVC_RL1A 0x10
118 #define WD_REVC_R2DS 0x40
119 #define WD_REVC_RLY2 0x80
129 #define ISA_COMMAND_TIMEOUT 1000
132 #define CMD_ISA_IDLE 0x00
133 #define CMD_ISA_VERSION_INTEGER 0x01
134 #define CMD_ISA_VERSION_TENTH 0x02
135 #define CMD_ISA_VERSION_HUNDRETH 0x03
136 #define CMD_ISA_VERSION_MINOR 0x04
137 #define CMD_ISA_SWITCH_SETTINGS 0x05
138 #define CMD_ISA_RESET_PC 0x06
139 #define CMD_ISA_ARM_0 0x07
140 #define CMD_ISA_ARM_30 0x08
141 #define CMD_ISA_ARM_60 0x09
142 #define CMD_ISA_DELAY_TIME_2SECS 0x0A
143 #define CMD_ISA_DELAY_TIME_4SECS 0x0B
144 #define CMD_ISA_DELAY_TIME_8SECS 0x0C
145 #define CMD_ISA_RESET_RELAYS 0x0D
148 static const int heartbeat_tbl[] = {
165 #define WDT_INTERVAL (HZ/2+1)
168 static int cards_found;
171 static unsigned long open_allowed;
173 static int temp_panic;
197 "Debug level: 0=Quiet, 1=Verbose, 2=Debug (default=0)");
200 #define WATCHDOG_HEARTBEAT 0
204 "(2 <= heartbeat <= 7200 or 0=delay-time from dip-switches, default="
210 "Watchdog cannot be stopped once started (default="
217 static int send_isa_command(
int cmd)
221 int port0, last_port0;
224 pr_debug(
"sending following data cmd=0x%02x\n", cmd);
227 control_status = (cmd & 0x0F) |
WD_WCMD;
228 outb_p(control_status, pcwd_private.io_addr + 2);
231 port0 =
inb_p(pcwd_private.io_addr);
232 for (i = 0; i < 25; ++
i) {
234 port0 =
inb_p(pcwd_private.io_addr);
236 if (port0 == last_port0)
243 pr_debug(
"received following data for cmd=0x%02x: port0=0x%02x last_port0=0x%02x\n",
244 cmd, port0, last_port0);
249 static int set_command_mode(
void)
251 int i, found = 0,
count = 0;
254 spin_lock(&pcwd_private.io_lock);
255 while ((!found) && (
count < 3)) {
260 else if (i == 0xF3) {
262 outb_p(0x00, pcwd_private.io_addr + 2);
264 outb_p(0x00, pcwd_private.io_addr + 2);
269 spin_unlock(&pcwd_private.io_lock);
270 pcwd_private.command_mode = found;
273 pr_debug(
"command_mode=%d\n", pcwd_private.command_mode);
278 static void unset_command_mode(
void)
281 spin_lock(&pcwd_private.io_lock);
282 outb_p(0x00, pcwd_private.io_addr + 2);
284 spin_unlock(&pcwd_private.io_lock);
286 pcwd_private.command_mode = 0;
289 pr_debug(
"command_mode=%d\n", pcwd_private.command_mode);
292 static inline void pcwd_check_temperature_support(
void)
294 if (
inb(pcwd_private.io_addr) != 0xF0)
295 pcwd_private.supports_temp = 1;
298 static inline void pcwd_get_firmware(
void)
302 strcpy(pcwd_private.fw_ver_str,
"ERROR");
304 if (set_command_mode()) {
309 sprintf(pcwd_private.fw_ver_str,
"%c.%c%c%c",
310 one, ten, hund, minor);
312 unset_command_mode();
317 static inline int pcwd_get_option_switches(
void)
319 int option_switches = 0;
321 if (set_command_mode()) {
326 unset_command_mode();
327 return option_switches;
330 static void pcwd_show_card_info(
void)
336 pr_info(
"ISA-PC Watchdog (REV.A) detected at port 0x%04x\n",
337 pcwd_private.io_addr);
340 pr_info(
"ISA-PC Watchdog (REV.C) detected at port 0x%04x (Firmware version: %s)\n",
341 pcwd_private.io_addr, pcwd_private.fw_ver_str);
342 option_switches = pcwd_get_option_switches();
343 pr_info(
"Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n",
345 ((option_switches & 0x10) ?
"ON" :
"OFF"),
346 ((option_switches & 0x08) ?
"ON" :
"OFF"));
349 if (set_command_mode()) {
351 unset_command_mode();
355 if (pcwd_private.supports_temp)
356 pr_info(
"Temperature Option Detected\n");
359 pr_info(
"Previous reboot was caused by the card\n");
362 pr_emerg(
"Card senses a CPU Overheat. Panicking!\n");
366 if (pcwd_private.boot_status == 0)
367 pr_info(
"No previous trip detected - Cold boot or reset\n");
370 static void pcwd_timer_ping(
unsigned long data)
376 if (
time_before(jiffies, pcwd_private.next_heartbeat)) {
378 spin_lock(&pcwd_private.io_lock);
382 wdrst_stat =
inb_p(pcwd_private.io_addr);
386 outb_p(wdrst_stat, pcwd_private.io_addr + 1);
389 outb_p(0x00, pcwd_private.io_addr);
395 spin_unlock(&pcwd_private.io_lock);
397 pr_warn(
"Heartbeat lost! Will not ping the watchdog\n");
401 static int pcwd_start(
void)
412 spin_lock(&pcwd_private.io_lock);
413 outb_p(0x00, pcwd_private.io_addr + 3);
415 stat_reg =
inb_p(pcwd_private.io_addr + 2);
416 spin_unlock(&pcwd_private.io_lock);
418 pr_info(
"Could not start watchdog\n");
429 static int pcwd_stop(
void)
438 spin_lock(&pcwd_private.io_lock);
439 outb_p(0xA5, pcwd_private.io_addr + 3);
441 outb_p(0xA5, pcwd_private.io_addr + 3);
443 stat_reg =
inb_p(pcwd_private.io_addr + 2);
444 spin_unlock(&pcwd_private.io_lock);
445 if ((stat_reg &
WD_WDIS) == 0) {
446 pr_info(
"Could not stop watchdog\n");
457 static int pcwd_keepalive(
void)
463 pr_debug(
"Watchdog keepalive signal send\n");
468 static int pcwd_set_heartbeat(
int t)
470 if (t < 2 || t > 7200)
481 static int pcwd_get_status(
int *
status)
486 spin_lock(&pcwd_private.io_lock);
491 control_status =
inb(pcwd_private.io_addr);
498 control_status =
inb(pcwd_private.io_addr + 1);
500 spin_unlock(&pcwd_private.io_lock);
506 if (control_status &
WD_T110) {
509 pr_info(
"Temperature overheat trip!\n");
520 pr_info(
"Temperature overheat trip!\n");
529 static int pcwd_clear_status(
void)
534 spin_lock(&pcwd_private.io_lock);
537 pr_info(
"clearing watchdog trip status\n");
539 control_status =
inb_p(pcwd_private.io_addr + 1);
542 pr_debug(
"status was: 0x%02x\n", control_status);
549 pcwd_private.io_addr + 1);
551 spin_unlock(&pcwd_private.io_lock);
559 if (pcwd_private.command_mode)
563 if (!pcwd_private.supports_temp)
570 spin_lock(&pcwd_private.io_lock);
571 *temperature = ((
inb(pcwd_private.io_addr)) * 9 / 5) + 32;
572 spin_unlock(&pcwd_private.io_lock);
575 pr_debug(
"temperature is: %d F\n", *temperature);
585 static long pcwd_ioctl(
struct file *
file,
unsigned int cmd,
unsigned long arg)
598 .firmware_version = 1,
609 pcwd_get_status(&status);
613 return put_user(pcwd_private.boot_status, argp);
616 if (pcwd_get_temperature(&temperature))
627 status = pcwd_stop();
632 status = pcwd_start();
649 if (pcwd_set_heartbeat(new_heartbeat))
665 static ssize_t pcwd_write(
struct file *file,
const char __user *
buf,
size_t len,
675 for (i = 0; i != len; i++) {
689 static int pcwd_open(
struct inode *
inode,
struct file *file)
701 static int pcwd_close(
struct inode *inode,
struct file *file)
703 if (expect_close == 42)
706 pr_crit(
"Unexpected close, not stopping watchdog!\n");
718 static ssize_t pcwd_temp_read(
struct file *file,
char __user *buf,
size_t count,
723 if (pcwd_get_temperature(&temperature))
732 static int pcwd_temp_open(
struct inode *inode,
struct file *file)
734 if (!pcwd_private.supports_temp)
740 static int pcwd_temp_close(
struct inode *inode,
struct file *file)
753 .unlocked_ioctl = pcwd_ioctl,
755 .release = pcwd_close,
767 .read = pcwd_temp_read,
768 .open = pcwd_temp_open,
769 .release = pcwd_temp_close,
774 .name =
"temperature",
775 .fops = &pcwd_temp_fops,
782 static inline int get_revision(
void)
786 spin_lock(&pcwd_private.io_lock);
789 if ((
inb(pcwd_private.io_addr + 2) == 0xFF) ||
790 (
inb(pcwd_private.io_addr + 3) == 0xFF))
792 spin_unlock(&pcwd_private.io_lock);
806 int base_addr = pcwd_ioports[
id];
807 int port0, last_port0;
808 int port1, last_port1;
813 pr_debug(
"pcwd_isa_match id=%d\n",
id);
816 pr_info(
"Port 0x%04x unavailable\n", base_addr);
822 port0 =
inb_p(base_addr);
823 port1 =
inb_p(base_addr + 1);
824 if (port0 != 0xff || port1 != 0xff) {
826 for (i = 0; i < 4; ++
i) {
833 port0 =
inb_p(base_addr);
834 port1 =
inb_p(base_addr + 1);
837 if ((port0 ^ last_port0) &
WD_HRTBT ||
849 static int __devinit pcwd_isa_probe(
struct device *dev,
unsigned int id)
854 pr_debug(
"pcwd_isa_probe id=%d\n",
id);
857 if (cards_found == 1)
861 if (cards_found > 1) {
862 pr_err(
"This driver only supports 1 device\n");
866 if (pcwd_ioports[
id] == 0x0000) {
867 pr_err(
"No I/O-Address for card detected\n");
870 pcwd_private.io_addr = pcwd_ioports[
id];
875 pcwd_private.revision = get_revision();
879 pr_err(
"I/O address 0x%04x already in use\n",
880 pcwd_private.io_addr);
882 goto error_request_region;
886 pcwd_private.supports_temp = 0;
888 pcwd_private.boot_status = 0x0000;
891 pcwd_get_status(&pcwd_private.boot_status);
896 setup_timer(&pcwd_private.timer, pcwd_timer_ping, 0);
902 pcwd_check_temperature_support();
905 pcwd_show_card_info();
909 heartbeat = heartbeat_tbl[(pcwd_get_option_switches() & 0x07)];
915 pr_info(
"heartbeat value must be 2 <= heartbeat <= 7200, using %d\n",
919 if (pcwd_private.supports_temp) {
922 pr_err(
"cannot register miscdev on minor=%d (err=%d)\n",
924 goto error_misc_register_temp;
930 pr_err(
"cannot register miscdev on minor=%d (err=%d)\n",
932 goto error_misc_register_watchdog;
935 pr_info(
"initialized. heartbeat=%d sec (nowayout=%d)\n",
940 error_misc_register_watchdog:
941 if (pcwd_private.supports_temp)
943 error_misc_register_temp:
946 error_request_region:
947 pcwd_private.io_addr = 0x0000;
952 static int __devexit pcwd_isa_remove(
struct device *dev,
unsigned int id)
955 pr_debug(
"pcwd_isa_remove id=%d\n",
id);
957 if (!pcwd_private.io_addr)
966 if (pcwd_private.supports_temp)
970 pcwd_private.io_addr = 0x0000;
976 static void pcwd_isa_shutdown(
struct device *dev,
unsigned int id)
979 pr_debug(
"pcwd_isa_shutdown id=%d\n",
id);
985 .match = pcwd_isa_match,
986 .probe = pcwd_isa_probe,
988 .shutdown = pcwd_isa_shutdown,
995 static int __init pcwd_init_module(
void)
1000 static void __exit pcwd_cleanup_module(
void)
1003 pr_info(
"Watchdog Module Unloaded\n");