diff options
Diffstat (limited to 'drivers/char/watchdog/pcwd.c')
-rw-r--r-- | drivers/char/watchdog/pcwd.c | 137 |
1 files changed, 102 insertions, 35 deletions
diff --git a/drivers/char/watchdog/pcwd.c b/drivers/char/watchdog/pcwd.c index 8d6b249ad66b..6d44ca68312d 100644 --- a/drivers/char/watchdog/pcwd.c +++ b/drivers/char/watchdog/pcwd.c | |||
@@ -66,15 +66,13 @@ | |||
66 | #include <linux/fs.h> /* For file operations */ | 66 | #include <linux/fs.h> /* For file operations */ |
67 | #include <linux/ioport.h> /* For io-port access */ | 67 | #include <linux/ioport.h> /* For io-port access */ |
68 | #include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */ | 68 | #include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */ |
69 | #include <linux/sched.h> /* TASK_INTERRUPTIBLE, set_current_state() and friends */ | ||
70 | #include <linux/slab.h> /* For kmalloc */ | ||
71 | 69 | ||
72 | #include <asm/uaccess.h> /* For copy_to_user/put_user/... */ | 70 | #include <asm/uaccess.h> /* For copy_to_user/put_user/... */ |
73 | #include <asm/io.h> /* For inb/outb/... */ | 71 | #include <asm/io.h> /* For inb/outb/... */ |
74 | 72 | ||
75 | /* Module and version information */ | 73 | /* Module and version information */ |
76 | #define WATCHDOG_VERSION "1.16" | 74 | #define WATCHDOG_VERSION "1.17" |
77 | #define WATCHDOG_DATE "03 Jan 2006" | 75 | #define WATCHDOG_DATE "12 Feb 2006" |
78 | #define WATCHDOG_DRIVER_NAME "ISA-PC Watchdog" | 76 | #define WATCHDOG_DRIVER_NAME "ISA-PC Watchdog" |
79 | #define WATCHDOG_NAME "pcwd" | 77 | #define WATCHDOG_NAME "pcwd" |
80 | #define PFX WATCHDOG_NAME ": " | 78 | #define PFX WATCHDOG_NAME ": " |
@@ -96,15 +94,19 @@ | |||
96 | * PCI-PC Watchdog card. | 94 | * PCI-PC Watchdog card. |
97 | */ | 95 | */ |
98 | /* Port 1 : Control Status #1 for the PC Watchdog card, revision A. */ | 96 | /* Port 1 : Control Status #1 for the PC Watchdog card, revision A. */ |
99 | #define WD_WDRST 0x01 /* Previously reset state */ | 97 | #define WD_WDRST 0x01 /* Previously reset state */ |
100 | #define WD_T110 0x02 /* Temperature overheat sense */ | 98 | #define WD_T110 0x02 /* Temperature overheat sense */ |
101 | #define WD_HRTBT 0x04 /* Heartbeat sense */ | 99 | #define WD_HRTBT 0x04 /* Heartbeat sense */ |
102 | #define WD_RLY2 0x08 /* External relay triggered */ | 100 | #define WD_RLY2 0x08 /* External relay triggered */ |
103 | #define WD_SRLY2 0x80 /* Software external relay triggered */ | 101 | #define WD_SRLY2 0x80 /* Software external relay triggered */ |
104 | /* Port 1 : Control Status #1 for the PC Watchdog card, revision C. */ | 102 | /* Port 1 : Control Status #1 for the PC Watchdog card, revision C. */ |
105 | #define WD_REVC_WTRP 0x01 /* Watchdog Trip status */ | 103 | #define WD_REVC_WTRP 0x01 /* Watchdog Trip status */ |
106 | #define WD_REVC_HRBT 0x02 /* Watchdog Heartbeat */ | 104 | #define WD_REVC_HRBT 0x02 /* Watchdog Heartbeat */ |
107 | #define WD_REVC_TTRP 0x04 /* Temperature Trip status */ | 105 | #define WD_REVC_TTRP 0x04 /* Temperature Trip status */ |
106 | #define WD_REVC_RL2A 0x08 /* Relay 2 activated by on-board processor */ | ||
107 | #define WD_REVC_RL1A 0x10 /* Relay 1 active */ | ||
108 | #define WD_REVC_R2DS 0x40 /* Relay 2 disable */ | ||
109 | #define WD_REVC_RLY2 0x80 /* Relay 2 activated? */ | ||
108 | /* Port 2 : Control Status #2 */ | 110 | /* Port 2 : Control Status #2 */ |
109 | #define WD_WDIS 0x10 /* Watchdog Disabled */ | 111 | #define WD_WDIS 0x10 /* Watchdog Disabled */ |
110 | #define WD_ENTP 0x20 /* Watchdog Enable Temperature Trip */ | 112 | #define WD_ENTP 0x20 /* Watchdog Enable Temperature Trip */ |
@@ -122,9 +124,14 @@ | |||
122 | #define CMD_ISA_VERSION_HUNDRETH 0x03 | 124 | #define CMD_ISA_VERSION_HUNDRETH 0x03 |
123 | #define CMD_ISA_VERSION_MINOR 0x04 | 125 | #define CMD_ISA_VERSION_MINOR 0x04 |
124 | #define CMD_ISA_SWITCH_SETTINGS 0x05 | 126 | #define CMD_ISA_SWITCH_SETTINGS 0x05 |
127 | #define CMD_ISA_RESET_PC 0x06 | ||
128 | #define CMD_ISA_ARM_0 0x07 | ||
129 | #define CMD_ISA_ARM_30 0x08 | ||
130 | #define CMD_ISA_ARM_60 0x09 | ||
125 | #define CMD_ISA_DELAY_TIME_2SECS 0x0A | 131 | #define CMD_ISA_DELAY_TIME_2SECS 0x0A |
126 | #define CMD_ISA_DELAY_TIME_4SECS 0x0B | 132 | #define CMD_ISA_DELAY_TIME_4SECS 0x0B |
127 | #define CMD_ISA_DELAY_TIME_8SECS 0x0C | 133 | #define CMD_ISA_DELAY_TIME_8SECS 0x0C |
134 | #define CMD_ISA_RESET_RELAYS 0x0D | ||
128 | 135 | ||
129 | /* | 136 | /* |
130 | * We are using an kernel timer to do the pinging of the watchdog | 137 | * We are using an kernel timer to do the pinging of the watchdog |
@@ -142,6 +149,7 @@ static atomic_t open_allowed = ATOMIC_INIT(1); | |||
142 | static char expect_close; | 149 | static char expect_close; |
143 | static int temp_panic; | 150 | static int temp_panic; |
144 | static struct { /* this is private data for each ISA-PC watchdog card */ | 151 | static struct { /* this is private data for each ISA-PC watchdog card */ |
152 | char fw_ver_str[6]; /* The cards firmware version */ | ||
145 | int revision; /* The card's revision */ | 153 | int revision; /* The card's revision */ |
146 | int supports_temp; /* Wether or not the card has a temperature device */ | 154 | int supports_temp; /* Wether or not the card has a temperature device */ |
147 | int command_mode; /* Wether or not the card is in command mode */ | 155 | int command_mode; /* Wether or not the card is in command mode */ |
@@ -153,6 +161,13 @@ static struct { /* this is private data for each ISA-PC watchdog card */ | |||
153 | } pcwd_private; | 161 | } pcwd_private; |
154 | 162 | ||
155 | /* module parameters */ | 163 | /* module parameters */ |
164 | #define QUIET 0 /* Default */ | ||
165 | #define VERBOSE 1 /* Verbose */ | ||
166 | #define DEBUG 2 /* print fancy stuff too */ | ||
167 | static int debug = QUIET; | ||
168 | module_param(debug, int, 0); | ||
169 | MODULE_PARM_DESC(debug, "Debug level: 0=Quiet, 1=Verbose, 2=Debug (default=0)"); | ||
170 | |||
156 | #define WATCHDOG_HEARTBEAT 60 /* 60 sec default heartbeat */ | 171 | #define WATCHDOG_HEARTBEAT 60 /* 60 sec default heartbeat */ |
157 | static int heartbeat = WATCHDOG_HEARTBEAT; | 172 | static int heartbeat = WATCHDOG_HEARTBEAT; |
158 | module_param(heartbeat, int, 0); | 173 | module_param(heartbeat, int, 0); |
@@ -172,6 +187,10 @@ static int send_isa_command(int cmd) | |||
172 | int control_status; | 187 | int control_status; |
173 | int port0, last_port0; /* Double read for stabilising */ | 188 | int port0, last_port0; /* Double read for stabilising */ |
174 | 189 | ||
190 | if (debug >= DEBUG) | ||
191 | printk(KERN_DEBUG PFX "sending following data cmd=0x%02x\n", | ||
192 | cmd); | ||
193 | |||
175 | /* The WCMD bit must be 1 and the command is only 4 bits in size */ | 194 | /* The WCMD bit must be 1 and the command is only 4 bits in size */ |
176 | control_status = (cmd & 0x0F) | WD_WCMD; | 195 | control_status = (cmd & 0x0F) | WD_WCMD; |
177 | outb_p(control_status, pcwd_private.io_addr + 2); | 196 | outb_p(control_status, pcwd_private.io_addr + 2); |
@@ -188,6 +207,10 @@ static int send_isa_command(int cmd) | |||
188 | udelay (250); | 207 | udelay (250); |
189 | } | 208 | } |
190 | 209 | ||
210 | if (debug >= DEBUG) | ||
211 | printk(KERN_DEBUG PFX "received following data for cmd=0x%02x: port0=0x%02x last_port0=0x%02x\n", | ||
212 | cmd, port0, last_port0); | ||
213 | |||
191 | return port0; | 214 | return port0; |
192 | } | 215 | } |
193 | 216 | ||
@@ -214,6 +237,10 @@ static int set_command_mode(void) | |||
214 | spin_unlock(&pcwd_private.io_lock); | 237 | spin_unlock(&pcwd_private.io_lock); |
215 | pcwd_private.command_mode = found; | 238 | pcwd_private.command_mode = found; |
216 | 239 | ||
240 | if (debug >= DEBUG) | ||
241 | printk(KERN_DEBUG PFX "command_mode=%d\n", | ||
242 | pcwd_private.command_mode); | ||
243 | |||
217 | return(found); | 244 | return(found); |
218 | } | 245 | } |
219 | 246 | ||
@@ -226,6 +253,10 @@ static void unset_command_mode(void) | |||
226 | spin_unlock(&pcwd_private.io_lock); | 253 | spin_unlock(&pcwd_private.io_lock); |
227 | 254 | ||
228 | pcwd_private.command_mode = 0; | 255 | pcwd_private.command_mode = 0; |
256 | |||
257 | if (debug >= DEBUG) | ||
258 | printk(KERN_DEBUG PFX "command_mode=%d\n", | ||
259 | pcwd_private.command_mode); | ||
229 | } | 260 | } |
230 | 261 | ||
231 | static inline void pcwd_check_temperature_support(void) | 262 | static inline void pcwd_check_temperature_support(void) |
@@ -234,27 +265,22 @@ static inline void pcwd_check_temperature_support(void) | |||
234 | pcwd_private.supports_temp = 1; | 265 | pcwd_private.supports_temp = 1; |
235 | } | 266 | } |
236 | 267 | ||
237 | static inline char *get_firmware(void) | 268 | static inline void pcwd_get_firmware(void) |
238 | { | 269 | { |
239 | int one, ten, hund, minor; | 270 | int one, ten, hund, minor; |
240 | char *ret; | ||
241 | 271 | ||
242 | ret = kmalloc(6, GFP_KERNEL); | 272 | strcpy(pcwd_private.fw_ver_str, "ERROR"); |
243 | if(ret == NULL) | ||
244 | return NULL; | ||
245 | 273 | ||
246 | if (set_command_mode()) { | 274 | if (set_command_mode()) { |
247 | one = send_isa_command(CMD_ISA_VERSION_INTEGER); | 275 | one = send_isa_command(CMD_ISA_VERSION_INTEGER); |
248 | ten = send_isa_command(CMD_ISA_VERSION_TENTH); | 276 | ten = send_isa_command(CMD_ISA_VERSION_TENTH); |
249 | hund = send_isa_command(CMD_ISA_VERSION_HUNDRETH); | 277 | hund = send_isa_command(CMD_ISA_VERSION_HUNDRETH); |
250 | minor = send_isa_command(CMD_ISA_VERSION_MINOR); | 278 | minor = send_isa_command(CMD_ISA_VERSION_MINOR); |
251 | sprintf(ret, "%c.%c%c%c", one, ten, hund, minor); | 279 | sprintf(pcwd_private.fw_ver_str, "%c.%c%c%c", one, ten, hund, minor); |
252 | } | 280 | } |
253 | else | ||
254 | sprintf(ret, "ERROR"); | ||
255 | |||
256 | unset_command_mode(); | 281 | unset_command_mode(); |
257 | return(ret); | 282 | |
283 | return; | ||
258 | } | 284 | } |
259 | 285 | ||
260 | static inline int pcwd_get_option_switches(void) | 286 | static inline int pcwd_get_option_switches(void) |
@@ -272,17 +298,15 @@ static inline int pcwd_get_option_switches(void) | |||
272 | 298 | ||
273 | static void pcwd_show_card_info(void) | 299 | static void pcwd_show_card_info(void) |
274 | { | 300 | { |
275 | char *firmware; | ||
276 | int option_switches; | 301 | int option_switches; |
277 | 302 | ||
278 | /* Get some extra info from the hardware (in command/debug/diag mode) */ | 303 | /* Get some extra info from the hardware (in command/debug/diag mode) */ |
279 | if (pcwd_private.revision == PCWD_REVISION_A) | 304 | if (pcwd_private.revision == PCWD_REVISION_A) |
280 | printk(KERN_INFO PFX "ISA-PC Watchdog (REV.A) detected at port 0x%04x\n", pcwd_private.io_addr); | 305 | printk(KERN_INFO PFX "ISA-PC Watchdog (REV.A) detected at port 0x%04x\n", pcwd_private.io_addr); |
281 | else if (pcwd_private.revision == PCWD_REVISION_C) { | 306 | else if (pcwd_private.revision == PCWD_REVISION_C) { |
282 | firmware = get_firmware(); | 307 | pcwd_get_firmware(); |
283 | printk(KERN_INFO PFX "ISA-PC Watchdog (REV.C) detected at port 0x%04x (Firmware version: %s)\n", | 308 | printk(KERN_INFO PFX "ISA-PC Watchdog (REV.C) detected at port 0x%04x (Firmware version: %s)\n", |
284 | pcwd_private.io_addr, firmware); | 309 | pcwd_private.io_addr, pcwd_private.fw_ver_str); |
285 | kfree(firmware); | ||
286 | option_switches = pcwd_get_option_switches(); | 310 | option_switches = pcwd_get_option_switches(); |
287 | printk(KERN_INFO PFX "Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n", | 311 | printk(KERN_INFO PFX "Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n", |
288 | option_switches, | 312 | option_switches, |
@@ -362,6 +386,10 @@ static int pcwd_start(void) | |||
362 | return -EIO; | 386 | return -EIO; |
363 | } | 387 | } |
364 | } | 388 | } |
389 | |||
390 | if (debug >= VERBOSE) | ||
391 | printk(KERN_DEBUG PFX "Watchdog started\n"); | ||
392 | |||
365 | return 0; | 393 | return 0; |
366 | } | 394 | } |
367 | 395 | ||
@@ -386,6 +414,10 @@ static int pcwd_stop(void) | |||
386 | return -EIO; | 414 | return -EIO; |
387 | } | 415 | } |
388 | } | 416 | } |
417 | |||
418 | if (debug >= VERBOSE) | ||
419 | printk(KERN_DEBUG PFX "Watchdog stopped\n"); | ||
420 | |||
389 | return 0; | 421 | return 0; |
390 | } | 422 | } |
391 | 423 | ||
@@ -393,6 +425,10 @@ static int pcwd_keepalive(void) | |||
393 | { | 425 | { |
394 | /* user land ping */ | 426 | /* user land ping */ |
395 | pcwd_private.next_heartbeat = jiffies + (heartbeat * HZ); | 427 | pcwd_private.next_heartbeat = jiffies + (heartbeat * HZ); |
428 | |||
429 | if (debug >= DEBUG) | ||
430 | printk(KERN_DEBUG PFX "Watchdog keepalive signal send\n"); | ||
431 | |||
396 | return 0; | 432 | return 0; |
397 | } | 433 | } |
398 | 434 | ||
@@ -402,12 +438,17 @@ static int pcwd_set_heartbeat(int t) | |||
402 | return -EINVAL; | 438 | return -EINVAL; |
403 | 439 | ||
404 | heartbeat = t; | 440 | heartbeat = t; |
441 | |||
442 | if (debug >= VERBOSE) | ||
443 | printk(KERN_DEBUG PFX "New heartbeat: %d\n", | ||
444 | heartbeat); | ||
445 | |||
405 | return 0; | 446 | return 0; |
406 | } | 447 | } |
407 | 448 | ||
408 | static int pcwd_get_status(int *status) | 449 | static int pcwd_get_status(int *status) |
409 | { | 450 | { |
410 | int card_status; | 451 | int control_status; |
411 | 452 | ||
412 | *status=0; | 453 | *status=0; |
413 | spin_lock(&pcwd_private.io_lock); | 454 | spin_lock(&pcwd_private.io_lock); |
@@ -415,37 +456,39 @@ static int pcwd_get_status(int *status) | |||
415 | /* Rev A cards return status information from | 456 | /* Rev A cards return status information from |
416 | * the base register, which is used for the | 457 | * the base register, which is used for the |
417 | * temperature in other cards. */ | 458 | * temperature in other cards. */ |
418 | card_status = inb(pcwd_private.io_addr); | 459 | control_status = inb(pcwd_private.io_addr); |
419 | else { | 460 | else { |
420 | /* Rev C cards return card status in the base | 461 | /* Rev C cards return card status in the base |
421 | * address + 1 register. And use different bits | 462 | * address + 1 register. And use different bits |
422 | * to indicate a card initiated reset, and an | 463 | * to indicate a card initiated reset, and an |
423 | * over-temperature condition. And the reboot | 464 | * over-temperature condition. And the reboot |
424 | * status can be reset. */ | 465 | * status can be reset. */ |
425 | card_status = inb(pcwd_private.io_addr + 1); | 466 | control_status = inb(pcwd_private.io_addr + 1); |
426 | } | 467 | } |
427 | spin_unlock(&pcwd_private.io_lock); | 468 | spin_unlock(&pcwd_private.io_lock); |
428 | 469 | ||
429 | if (pcwd_private.revision == PCWD_REVISION_A) { | 470 | if (pcwd_private.revision == PCWD_REVISION_A) { |
430 | if (card_status & WD_WDRST) | 471 | if (control_status & WD_WDRST) |
431 | *status |= WDIOF_CARDRESET; | 472 | *status |= WDIOF_CARDRESET; |
432 | 473 | ||
433 | if (card_status & WD_T110) { | 474 | if (control_status & WD_T110) { |
434 | *status |= WDIOF_OVERHEAT; | 475 | *status |= WDIOF_OVERHEAT; |
435 | if (temp_panic) { | 476 | if (temp_panic) { |
436 | printk (KERN_INFO PFX "Temperature overheat trip!\n"); | 477 | printk (KERN_INFO PFX "Temperature overheat trip!\n"); |
437 | kernel_power_off(); | 478 | kernel_power_off(); |
479 | /* or should we just do a: panic(PFX "Temperature overheat trip!\n"); */ | ||
438 | } | 480 | } |
439 | } | 481 | } |
440 | } else { | 482 | } else { |
441 | if (card_status & WD_REVC_WTRP) | 483 | if (control_status & WD_REVC_WTRP) |
442 | *status |= WDIOF_CARDRESET; | 484 | *status |= WDIOF_CARDRESET; |
443 | 485 | ||
444 | if (card_status & WD_REVC_TTRP) { | 486 | if (control_status & WD_REVC_TTRP) { |
445 | *status |= WDIOF_OVERHEAT; | 487 | *status |= WDIOF_OVERHEAT; |
446 | if (temp_panic) { | 488 | if (temp_panic) { |
447 | printk (KERN_INFO PFX "Temperature overheat trip!\n"); | 489 | printk (KERN_INFO PFX "Temperature overheat trip!\n"); |
448 | kernel_power_off(); | 490 | kernel_power_off(); |
491 | /* or should we just do a: panic(PFX "Temperature overheat trip!\n"); */ | ||
449 | } | 492 | } |
450 | } | 493 | } |
451 | } | 494 | } |
@@ -455,9 +498,25 @@ static int pcwd_get_status(int *status) | |||
455 | 498 | ||
456 | static int pcwd_clear_status(void) | 499 | static int pcwd_clear_status(void) |
457 | { | 500 | { |
501 | int control_status; | ||
502 | |||
458 | if (pcwd_private.revision == PCWD_REVISION_C) { | 503 | if (pcwd_private.revision == PCWD_REVISION_C) { |
459 | spin_lock(&pcwd_private.io_lock); | 504 | spin_lock(&pcwd_private.io_lock); |
460 | outb_p(0x00, pcwd_private.io_addr + 1); /* clear reset status */ | 505 | |
506 | if (debug >= VERBOSE) | ||
507 | printk(KERN_INFO PFX "clearing watchdog trip status\n"); | ||
508 | |||
509 | control_status = inb_p(pcwd_private.io_addr + 1); | ||
510 | |||
511 | if (debug >= DEBUG) { | ||
512 | printk(KERN_DEBUG PFX "status was: 0x%02x\n", control_status); | ||
513 | printk(KERN_DEBUG PFX "sending: 0x%02x\n", | ||
514 | (control_status & WD_REVC_R2DS)); | ||
515 | } | ||
516 | |||
517 | /* clear reset status & Keep Relay 2 disable state as it is */ | ||
518 | outb_p((control_status & WD_REVC_R2DS), pcwd_private.io_addr + 1); | ||
519 | |||
461 | spin_unlock(&pcwd_private.io_lock); | 520 | spin_unlock(&pcwd_private.io_lock); |
462 | } | 521 | } |
463 | return 0; | 522 | return 0; |
@@ -481,6 +540,11 @@ static int pcwd_get_temperature(int *temperature) | |||
481 | *temperature = ((inb(pcwd_private.io_addr)) * 9 / 5) + 32; | 540 | *temperature = ((inb(pcwd_private.io_addr)) * 9 / 5) + 32; |
482 | spin_unlock(&pcwd_private.io_lock); | 541 | spin_unlock(&pcwd_private.io_lock); |
483 | 542 | ||
543 | if (debug >= DEBUG) { | ||
544 | printk(KERN_DEBUG PFX "temperature is: %d F\n", | ||
545 | *temperature); | ||
546 | } | ||
547 | |||
484 | return 0; | 548 | return 0; |
485 | } | 549 | } |
486 | 550 | ||
@@ -599,6 +663,8 @@ static ssize_t pcwd_write(struct file *file, const char __user *buf, size_t len, | |||
599 | static int pcwd_open(struct inode *inode, struct file *file) | 663 | static int pcwd_open(struct inode *inode, struct file *file) |
600 | { | 664 | { |
601 | if (!atomic_dec_and_test(&open_allowed) ) { | 665 | if (!atomic_dec_and_test(&open_allowed) ) { |
666 | if (debug >= VERBOSE) | ||
667 | printk(KERN_ERR PFX "Attempt to open already opened device.\n"); | ||
602 | atomic_inc( &open_allowed ); | 668 | atomic_inc( &open_allowed ); |
603 | return -EBUSY; | 669 | return -EBUSY; |
604 | } | 670 | } |
@@ -922,7 +988,8 @@ static void __exit pcwd_cleanup_module(void) | |||
922 | { | 988 | { |
923 | if (pcwd_private.io_addr) | 989 | if (pcwd_private.io_addr) |
924 | pcwatchdog_exit(); | 990 | pcwatchdog_exit(); |
925 | return; | 991 | |
992 | printk(KERN_INFO PFX "Watchdog Module Unloaded.\n"); | ||
926 | } | 993 | } |
927 | 994 | ||
928 | module_init(pcwd_init_module); | 995 | module_init(pcwd_init_module); |