diff options
Diffstat (limited to 'drivers/char/watchdog/pcwd.c')
-rw-r--r-- | drivers/char/watchdog/pcwd.c | 450 |
1 files changed, 232 insertions, 218 deletions
diff --git a/drivers/char/watchdog/pcwd.c b/drivers/char/watchdog/pcwd.c index 37c9e13ad3ac..8d6b249ad66b 100644 --- a/drivers/char/watchdog/pcwd.c +++ b/drivers/char/watchdog/pcwd.c | |||
@@ -49,29 +49,37 @@ | |||
49 | * More info available at http://www.berkprod.com/ or http://www.pcwatchdog.com/ | 49 | * More info available at http://www.berkprod.com/ or http://www.pcwatchdog.com/ |
50 | */ | 50 | */ |
51 | 51 | ||
52 | #include <linux/module.h> | 52 | #include <linux/config.h> /* For CONFIG_WATCHDOG_NOWAYOUT/... */ |
53 | #include <linux/moduleparam.h> | 53 | #include <linux/module.h> /* For module specific items */ |
54 | #include <linux/types.h> | 54 | #include <linux/moduleparam.h> /* For new moduleparam's */ |
55 | #include <linux/timer.h> | 55 | #include <linux/types.h> /* For standard types (like size_t) */ |
56 | #include <linux/jiffies.h> | 56 | #include <linux/errno.h> /* For the -ENODEV/... values */ |
57 | #include <linux/config.h> | 57 | #include <linux/kernel.h> /* For printk/panic/... */ |
58 | #include <linux/wait.h> | 58 | #include <linux/delay.h> /* For mdelay function */ |
59 | #include <linux/slab.h> | 59 | #include <linux/timer.h> /* For timer related operations */ |
60 | #include <linux/ioport.h> | 60 | #include <linux/jiffies.h> /* For jiffies stuff */ |
61 | #include <linux/delay.h> | 61 | #include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */ |
62 | #include <linux/fs.h> | 62 | #include <linux/watchdog.h> /* For the watchdog specific items */ |
63 | #include <linux/miscdevice.h> | 63 | #include <linux/notifier.h> /* For notifier support */ |
64 | #include <linux/watchdog.h> | 64 | #include <linux/reboot.h> /* For reboot_notifier stuff */ |
65 | #include <linux/notifier.h> | 65 | #include <linux/init.h> /* For __init/__exit/... */ |
66 | #include <linux/init.h> | 66 | #include <linux/fs.h> /* For file operations */ |
67 | #include <linux/spinlock.h> | 67 | #include <linux/ioport.h> /* For io-port access */ |
68 | #include <linux/reboot.h> | 68 | #include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */ |
69 | #include <linux/sched.h> /* TASK_INTERRUPTIBLE, set_current_state() and friends */ | 69 | #include <linux/sched.h> /* TASK_INTERRUPTIBLE, set_current_state() and friends */ |
70 | #include <asm/uaccess.h> | 70 | #include <linux/slab.h> /* For kmalloc */ |
71 | #include <asm/io.h> | ||
72 | 71 | ||
73 | #define WD_VER "1.16 (06/12/2004)" | 72 | #include <asm/uaccess.h> /* For copy_to_user/put_user/... */ |
74 | #define PFX "pcwd: " | 73 | #include <asm/io.h> /* For inb/outb/... */ |
74 | |||
75 | /* Module and version information */ | ||
76 | #define WATCHDOG_VERSION "1.16" | ||
77 | #define WATCHDOG_DATE "03 Jan 2006" | ||
78 | #define WATCHDOG_DRIVER_NAME "ISA-PC Watchdog" | ||
79 | #define WATCHDOG_NAME "pcwd" | ||
80 | #define PFX WATCHDOG_NAME ": " | ||
81 | #define DRIVER_VERSION WATCHDOG_DRIVER_NAME " driver, v" WATCHDOG_VERSION " (" WATCHDOG_DATE ")\n" | ||
82 | #define WD_VER WATCHDOG_VERSION " (" WATCHDOG_DATE ")" | ||
75 | 83 | ||
76 | /* | 84 | /* |
77 | * It should be noted that PCWD_REVISION_B was removed because A and B | 85 | * It should be noted that PCWD_REVISION_B was removed because A and B |
@@ -85,36 +93,38 @@ | |||
85 | 93 | ||
86 | /* | 94 | /* |
87 | * These are the defines that describe the control status bits for the | 95 | * These are the defines that describe the control status bits for the |
88 | * PC Watchdog card, revision A. | 96 | * PCI-PC Watchdog card. |
89 | */ | 97 | */ |
98 | /* Port 1 : Control Status #1 for the PC Watchdog card, revision A. */ | ||
90 | #define WD_WDRST 0x01 /* Previously reset state */ | 99 | #define WD_WDRST 0x01 /* Previously reset state */ |
91 | #define WD_T110 0x02 /* Temperature overheat sense */ | 100 | #define WD_T110 0x02 /* Temperature overheat sense */ |
92 | #define WD_HRTBT 0x04 /* Heartbeat sense */ | 101 | #define WD_HRTBT 0x04 /* Heartbeat sense */ |
93 | #define WD_RLY2 0x08 /* External relay triggered */ | 102 | #define WD_RLY2 0x08 /* External relay triggered */ |
94 | #define WD_SRLY2 0x80 /* Software external relay triggered */ | 103 | #define WD_SRLY2 0x80 /* Software external relay triggered */ |
95 | 104 | /* Port 1 : Control Status #1 for the PC Watchdog card, revision C. */ | |
96 | /* | ||
97 | * These are the defines that describe the control status bits for the | ||
98 | * PC Watchdog card, revision C. | ||
99 | */ | ||
100 | #define WD_REVC_WTRP 0x01 /* Watchdog Trip status */ | 105 | #define WD_REVC_WTRP 0x01 /* Watchdog Trip status */ |
101 | #define WD_REVC_HRBT 0x02 /* Watchdog Heartbeat */ | 106 | #define WD_REVC_HRBT 0x02 /* Watchdog Heartbeat */ |
102 | #define WD_REVC_TTRP 0x04 /* Temperature Trip status */ | 107 | #define WD_REVC_TTRP 0x04 /* Temperature Trip status */ |
108 | /* Port 2 : Control Status #2 */ | ||
109 | #define WD_WDIS 0x10 /* Watchdog Disabled */ | ||
110 | #define WD_ENTP 0x20 /* Watchdog Enable Temperature Trip */ | ||
111 | #define WD_SSEL 0x40 /* Watchdog Switch Select (1:SW1 <-> 0:SW2) */ | ||
112 | #define WD_WCMD 0x80 /* Watchdog Command Mode */ | ||
103 | 113 | ||
104 | /* max. time we give an ISA watchdog card to process a command */ | 114 | /* max. time we give an ISA watchdog card to process a command */ |
105 | /* 500ms for each 4 bit response (according to spec.) */ | 115 | /* 500ms for each 4 bit response (according to spec.) */ |
106 | #define ISA_COMMAND_TIMEOUT 1000 | 116 | #define ISA_COMMAND_TIMEOUT 1000 |
107 | 117 | ||
108 | /* Watchdog's internal commands */ | 118 | /* Watchdog's internal commands */ |
109 | #define CMD_ISA_IDLE 0x00 | 119 | #define CMD_ISA_IDLE 0x00 |
110 | #define CMD_ISA_VERSION_INTEGER 0x01 | 120 | #define CMD_ISA_VERSION_INTEGER 0x01 |
111 | #define CMD_ISA_VERSION_TENTH 0x02 | 121 | #define CMD_ISA_VERSION_TENTH 0x02 |
112 | #define CMD_ISA_VERSION_HUNDRETH 0x03 | 122 | #define CMD_ISA_VERSION_HUNDRETH 0x03 |
113 | #define CMD_ISA_VERSION_MINOR 0x04 | 123 | #define CMD_ISA_VERSION_MINOR 0x04 |
114 | #define CMD_ISA_SWITCH_SETTINGS 0x05 | 124 | #define CMD_ISA_SWITCH_SETTINGS 0x05 |
115 | #define CMD_ISA_DELAY_TIME_2SECS 0x0A | 125 | #define CMD_ISA_DELAY_TIME_2SECS 0x0A |
116 | #define CMD_ISA_DELAY_TIME_4SECS 0x0B | 126 | #define CMD_ISA_DELAY_TIME_4SECS 0x0B |
117 | #define CMD_ISA_DELAY_TIME_8SECS 0x0C | 127 | #define CMD_ISA_DELAY_TIME_8SECS 0x0C |
118 | 128 | ||
119 | /* | 129 | /* |
120 | * We are using an kernel timer to do the pinging of the watchdog | 130 | * We are using an kernel timer to do the pinging of the watchdog |
@@ -130,15 +140,17 @@ static int cards_found; | |||
130 | /* internal variables */ | 140 | /* internal variables */ |
131 | static atomic_t open_allowed = ATOMIC_INIT(1); | 141 | static atomic_t open_allowed = ATOMIC_INIT(1); |
132 | static char expect_close; | 142 | static char expect_close; |
133 | static struct timer_list timer; | ||
134 | static unsigned long next_heartbeat; | ||
135 | static int temp_panic; | 143 | static int temp_panic; |
136 | static int revision; /* The card's revision */ | 144 | static struct { /* this is private data for each ISA-PC watchdog card */ |
137 | static int supports_temp; /* Wether or not the card has a temperature device */ | 145 | int revision; /* The card's revision */ |
138 | static int command_mode; /* Wether or not the card is in command mode */ | 146 | int supports_temp; /* Wether or not the card has a temperature device */ |
139 | static int initial_status; /* The card's boot status */ | 147 | int command_mode; /* Wether or not the card is in command mode */ |
140 | static int current_readport; /* The cards I/O address */ | 148 | int boot_status; /* The card's boot status */ |
141 | static spinlock_t io_lock; | 149 | int io_addr; /* The cards I/O address */ |
150 | spinlock_t io_lock; /* the lock for io operations */ | ||
151 | struct timer_list timer; /* The timer that pings the watchdog */ | ||
152 | unsigned long next_heartbeat; /* the next_heartbeat for the timer */ | ||
153 | } pcwd_private; | ||
142 | 154 | ||
143 | /* module parameters */ | 155 | /* module parameters */ |
144 | #define WATCHDOG_HEARTBEAT 60 /* 60 sec default heartbeat */ | 156 | #define WATCHDOG_HEARTBEAT 60 /* 60 sec default heartbeat */ |
@@ -161,14 +173,14 @@ static int send_isa_command(int cmd) | |||
161 | int port0, last_port0; /* Double read for stabilising */ | 173 | int port0, last_port0; /* Double read for stabilising */ |
162 | 174 | ||
163 | /* The WCMD bit must be 1 and the command is only 4 bits in size */ | 175 | /* The WCMD bit must be 1 and the command is only 4 bits in size */ |
164 | control_status = (cmd & 0x0F) | 0x80; | 176 | control_status = (cmd & 0x0F) | WD_WCMD; |
165 | outb_p(control_status, current_readport + 2); | 177 | outb_p(control_status, pcwd_private.io_addr + 2); |
166 | udelay(ISA_COMMAND_TIMEOUT); | 178 | udelay(ISA_COMMAND_TIMEOUT); |
167 | 179 | ||
168 | port0 = inb_p(current_readport); | 180 | port0 = inb_p(pcwd_private.io_addr); |
169 | for (i = 0; i < 25; ++i) { | 181 | for (i = 0; i < 25; ++i) { |
170 | last_port0 = port0; | 182 | last_port0 = port0; |
171 | port0 = inb_p(current_readport); | 183 | port0 = inb_p(pcwd_private.io_addr); |
172 | 184 | ||
173 | if (port0 == last_port0) | 185 | if (port0 == last_port0) |
174 | break; /* Data is stable */ | 186 | break; /* Data is stable */ |
@@ -184,7 +196,7 @@ static int set_command_mode(void) | |||
184 | int i, found=0, count=0; | 196 | int i, found=0, count=0; |
185 | 197 | ||
186 | /* Set the card into command mode */ | 198 | /* Set the card into command mode */ |
187 | spin_lock(&io_lock); | 199 | spin_lock(&pcwd_private.io_lock); |
188 | while ((!found) && (count < 3)) { | 200 | while ((!found) && (count < 3)) { |
189 | i = send_isa_command(CMD_ISA_IDLE); | 201 | i = send_isa_command(CMD_ISA_IDLE); |
190 | 202 | ||
@@ -192,15 +204,15 @@ static int set_command_mode(void) | |||
192 | found = 1; | 204 | found = 1; |
193 | else if (i == 0xF3) { | 205 | else if (i == 0xF3) { |
194 | /* Card does not like what we've done to it */ | 206 | /* Card does not like what we've done to it */ |
195 | outb_p(0x00, current_readport + 2); | 207 | outb_p(0x00, pcwd_private.io_addr + 2); |
196 | udelay(1200); /* Spec says wait 1ms */ | 208 | udelay(1200); /* Spec says wait 1ms */ |
197 | outb_p(0x00, current_readport + 2); | 209 | outb_p(0x00, pcwd_private.io_addr + 2); |
198 | udelay(ISA_COMMAND_TIMEOUT); | 210 | udelay(ISA_COMMAND_TIMEOUT); |
199 | } | 211 | } |
200 | count++; | 212 | count++; |
201 | } | 213 | } |
202 | spin_unlock(&io_lock); | 214 | spin_unlock(&pcwd_private.io_lock); |
203 | command_mode = found; | 215 | pcwd_private.command_mode = found; |
204 | 216 | ||
205 | return(found); | 217 | return(found); |
206 | } | 218 | } |
@@ -208,12 +220,95 @@ static int set_command_mode(void) | |||
208 | static void unset_command_mode(void) | 220 | static void unset_command_mode(void) |
209 | { | 221 | { |
210 | /* Set the card into normal mode */ | 222 | /* Set the card into normal mode */ |
211 | spin_lock(&io_lock); | 223 | spin_lock(&pcwd_private.io_lock); |
212 | outb_p(0x00, current_readport + 2); | 224 | outb_p(0x00, pcwd_private.io_addr + 2); |
213 | udelay(ISA_COMMAND_TIMEOUT); | 225 | udelay(ISA_COMMAND_TIMEOUT); |
214 | spin_unlock(&io_lock); | 226 | spin_unlock(&pcwd_private.io_lock); |
227 | |||
228 | pcwd_private.command_mode = 0; | ||
229 | } | ||
230 | |||
231 | static inline void pcwd_check_temperature_support(void) | ||
232 | { | ||
233 | if (inb(pcwd_private.io_addr) != 0xF0) | ||
234 | pcwd_private.supports_temp = 1; | ||
235 | } | ||
236 | |||
237 | static inline char *get_firmware(void) | ||
238 | { | ||
239 | int one, ten, hund, minor; | ||
240 | char *ret; | ||
241 | |||
242 | ret = kmalloc(6, GFP_KERNEL); | ||
243 | if(ret == NULL) | ||
244 | return NULL; | ||
245 | |||
246 | if (set_command_mode()) { | ||
247 | one = send_isa_command(CMD_ISA_VERSION_INTEGER); | ||
248 | ten = send_isa_command(CMD_ISA_VERSION_TENTH); | ||
249 | hund = send_isa_command(CMD_ISA_VERSION_HUNDRETH); | ||
250 | minor = send_isa_command(CMD_ISA_VERSION_MINOR); | ||
251 | sprintf(ret, "%c.%c%c%c", one, ten, hund, minor); | ||
252 | } | ||
253 | else | ||
254 | sprintf(ret, "ERROR"); | ||
255 | |||
256 | unset_command_mode(); | ||
257 | return(ret); | ||
258 | } | ||
259 | |||
260 | static inline int pcwd_get_option_switches(void) | ||
261 | { | ||
262 | int option_switches=0; | ||
263 | |||
264 | if (set_command_mode()) { | ||
265 | /* Get switch settings */ | ||
266 | option_switches = send_isa_command(CMD_ISA_SWITCH_SETTINGS); | ||
267 | } | ||
268 | |||
269 | unset_command_mode(); | ||
270 | return(option_switches); | ||
271 | } | ||
272 | |||
273 | static void pcwd_show_card_info(void) | ||
274 | { | ||
275 | char *firmware; | ||
276 | int option_switches; | ||
277 | |||
278 | /* Get some extra info from the hardware (in command/debug/diag mode) */ | ||
279 | 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); | ||
281 | else if (pcwd_private.revision == PCWD_REVISION_C) { | ||
282 | firmware = get_firmware(); | ||
283 | printk(KERN_INFO PFX "ISA-PC Watchdog (REV.C) detected at port 0x%04x (Firmware version: %s)\n", | ||
284 | pcwd_private.io_addr, firmware); | ||
285 | kfree(firmware); | ||
286 | option_switches = pcwd_get_option_switches(); | ||
287 | printk(KERN_INFO PFX "Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n", | ||
288 | option_switches, | ||
289 | ((option_switches & 0x10) ? "ON" : "OFF"), | ||
290 | ((option_switches & 0x08) ? "ON" : "OFF")); | ||
291 | |||
292 | /* Reprogram internal heartbeat to 2 seconds */ | ||
293 | if (set_command_mode()) { | ||
294 | send_isa_command(CMD_ISA_DELAY_TIME_2SECS); | ||
295 | unset_command_mode(); | ||
296 | } | ||
297 | } | ||
298 | |||
299 | if (pcwd_private.supports_temp) | ||
300 | printk(KERN_INFO PFX "Temperature Option Detected\n"); | ||
301 | |||
302 | if (pcwd_private.boot_status & WDIOF_CARDRESET) | ||
303 | printk(KERN_INFO PFX "Previous reboot was caused by the card\n"); | ||
304 | |||
305 | if (pcwd_private.boot_status & WDIOF_OVERHEAT) { | ||
306 | printk(KERN_EMERG PFX "Card senses a CPU Overheat. Panicking!\n"); | ||
307 | printk(KERN_EMERG PFX "CPU Overheat\n"); | ||
308 | } | ||
215 | 309 | ||
216 | command_mode = 0; | 310 | if (pcwd_private.boot_status == 0) |
311 | printk(KERN_INFO PFX "No previous trip detected - Cold boot or reset\n"); | ||
217 | } | 312 | } |
218 | 313 | ||
219 | static void pcwd_timer_ping(unsigned long data) | 314 | static void pcwd_timer_ping(unsigned long data) |
@@ -222,25 +317,25 @@ static void pcwd_timer_ping(unsigned long data) | |||
222 | 317 | ||
223 | /* If we got a heartbeat pulse within the WDT_INTERVAL | 318 | /* If we got a heartbeat pulse within the WDT_INTERVAL |
224 | * we agree to ping the WDT */ | 319 | * we agree to ping the WDT */ |
225 | if(time_before(jiffies, next_heartbeat)) { | 320 | if(time_before(jiffies, pcwd_private.next_heartbeat)) { |
226 | /* Ping the watchdog */ | 321 | /* Ping the watchdog */ |
227 | spin_lock(&io_lock); | 322 | spin_lock(&pcwd_private.io_lock); |
228 | if (revision == PCWD_REVISION_A) { | 323 | if (pcwd_private.revision == PCWD_REVISION_A) { |
229 | /* Rev A cards are reset by setting the WD_WDRST bit in register 1 */ | 324 | /* Rev A cards are reset by setting the WD_WDRST bit in register 1 */ |
230 | wdrst_stat = inb_p(current_readport); | 325 | wdrst_stat = inb_p(pcwd_private.io_addr); |
231 | wdrst_stat &= 0x0F; | 326 | wdrst_stat &= 0x0F; |
232 | wdrst_stat |= WD_WDRST; | 327 | wdrst_stat |= WD_WDRST; |
233 | 328 | ||
234 | outb_p(wdrst_stat, current_readport + 1); | 329 | outb_p(wdrst_stat, pcwd_private.io_addr + 1); |
235 | } else { | 330 | } else { |
236 | /* Re-trigger watchdog by writing to port 0 */ | 331 | /* Re-trigger watchdog by writing to port 0 */ |
237 | outb_p(0x00, current_readport); | 332 | outb_p(0x00, pcwd_private.io_addr); |
238 | } | 333 | } |
239 | 334 | ||
240 | /* Re-set the timer interval */ | 335 | /* Re-set the timer interval */ |
241 | mod_timer(&timer, jiffies + WDT_INTERVAL); | 336 | mod_timer(&pcwd_private.timer, jiffies + WDT_INTERVAL); |
242 | 337 | ||
243 | spin_unlock(&io_lock); | 338 | spin_unlock(&pcwd_private.io_lock); |
244 | } else { | 339 | } else { |
245 | printk(KERN_WARNING PFX "Heartbeat lost! Will not ping the watchdog\n"); | 340 | printk(KERN_WARNING PFX "Heartbeat lost! Will not ping the watchdog\n"); |
246 | } | 341 | } |
@@ -250,19 +345,19 @@ static int pcwd_start(void) | |||
250 | { | 345 | { |
251 | int stat_reg; | 346 | int stat_reg; |
252 | 347 | ||
253 | next_heartbeat = jiffies + (heartbeat * HZ); | 348 | pcwd_private.next_heartbeat = jiffies + (heartbeat * HZ); |
254 | 349 | ||
255 | /* Start the timer */ | 350 | /* Start the timer */ |
256 | mod_timer(&timer, jiffies + WDT_INTERVAL); | 351 | mod_timer(&pcwd_private.timer, jiffies + WDT_INTERVAL); |
257 | 352 | ||
258 | /* Enable the port */ | 353 | /* Enable the port */ |
259 | if (revision == PCWD_REVISION_C) { | 354 | if (pcwd_private.revision == PCWD_REVISION_C) { |
260 | spin_lock(&io_lock); | 355 | spin_lock(&pcwd_private.io_lock); |
261 | outb_p(0x00, current_readport + 3); | 356 | outb_p(0x00, pcwd_private.io_addr + 3); |
262 | udelay(ISA_COMMAND_TIMEOUT); | 357 | udelay(ISA_COMMAND_TIMEOUT); |
263 | stat_reg = inb_p(current_readport + 2); | 358 | stat_reg = inb_p(pcwd_private.io_addr + 2); |
264 | spin_unlock(&io_lock); | 359 | spin_unlock(&pcwd_private.io_lock); |
265 | if (stat_reg & 0x10) { | 360 | if (stat_reg & WD_WDIS) { |
266 | printk(KERN_INFO PFX "Could not start watchdog\n"); | 361 | printk(KERN_INFO PFX "Could not start watchdog\n"); |
267 | return -EIO; | 362 | return -EIO; |
268 | } | 363 | } |
@@ -275,18 +370,18 @@ static int pcwd_stop(void) | |||
275 | int stat_reg; | 370 | int stat_reg; |
276 | 371 | ||
277 | /* Stop the timer */ | 372 | /* Stop the timer */ |
278 | del_timer(&timer); | 373 | del_timer(&pcwd_private.timer); |
279 | 374 | ||
280 | /* Disable the board */ | 375 | /* Disable the board */ |
281 | if (revision == PCWD_REVISION_C) { | 376 | if (pcwd_private.revision == PCWD_REVISION_C) { |
282 | spin_lock(&io_lock); | 377 | spin_lock(&pcwd_private.io_lock); |
283 | outb_p(0xA5, current_readport + 3); | 378 | outb_p(0xA5, pcwd_private.io_addr + 3); |
284 | udelay(ISA_COMMAND_TIMEOUT); | 379 | udelay(ISA_COMMAND_TIMEOUT); |
285 | outb_p(0xA5, current_readport + 3); | 380 | outb_p(0xA5, pcwd_private.io_addr + 3); |
286 | udelay(ISA_COMMAND_TIMEOUT); | 381 | udelay(ISA_COMMAND_TIMEOUT); |
287 | stat_reg = inb_p(current_readport + 2); | 382 | stat_reg = inb_p(pcwd_private.io_addr + 2); |
288 | spin_unlock(&io_lock); | 383 | spin_unlock(&pcwd_private.io_lock); |
289 | if ((stat_reg & 0x10) == 0) { | 384 | if ((stat_reg & WD_WDIS) == 0) { |
290 | printk(KERN_INFO PFX "Could not stop watchdog\n"); | 385 | printk(KERN_INFO PFX "Could not stop watchdog\n"); |
291 | return -EIO; | 386 | return -EIO; |
292 | } | 387 | } |
@@ -297,7 +392,7 @@ static int pcwd_stop(void) | |||
297 | static int pcwd_keepalive(void) | 392 | static int pcwd_keepalive(void) |
298 | { | 393 | { |
299 | /* user land ping */ | 394 | /* user land ping */ |
300 | next_heartbeat = jiffies + (heartbeat * HZ); | 395 | pcwd_private.next_heartbeat = jiffies + (heartbeat * HZ); |
301 | return 0; | 396 | return 0; |
302 | } | 397 | } |
303 | 398 | ||
@@ -315,23 +410,23 @@ static int pcwd_get_status(int *status) | |||
315 | int card_status; | 410 | int card_status; |
316 | 411 | ||
317 | *status=0; | 412 | *status=0; |
318 | spin_lock(&io_lock); | 413 | spin_lock(&pcwd_private.io_lock); |
319 | if (revision == PCWD_REVISION_A) | 414 | if (pcwd_private.revision == PCWD_REVISION_A) |
320 | /* Rev A cards return status information from | 415 | /* Rev A cards return status information from |
321 | * the base register, which is used for the | 416 | * the base register, which is used for the |
322 | * temperature in other cards. */ | 417 | * temperature in other cards. */ |
323 | card_status = inb(current_readport); | 418 | card_status = inb(pcwd_private.io_addr); |
324 | else { | 419 | else { |
325 | /* Rev C cards return card status in the base | 420 | /* Rev C cards return card status in the base |
326 | * address + 1 register. And use different bits | 421 | * address + 1 register. And use different bits |
327 | * to indicate a card initiated reset, and an | 422 | * to indicate a card initiated reset, and an |
328 | * over-temperature condition. And the reboot | 423 | * over-temperature condition. And the reboot |
329 | * status can be reset. */ | 424 | * status can be reset. */ |
330 | card_status = inb(current_readport + 1); | 425 | card_status = inb(pcwd_private.io_addr + 1); |
331 | } | 426 | } |
332 | spin_unlock(&io_lock); | 427 | spin_unlock(&pcwd_private.io_lock); |
333 | 428 | ||
334 | if (revision == PCWD_REVISION_A) { | 429 | if (pcwd_private.revision == PCWD_REVISION_A) { |
335 | if (card_status & WD_WDRST) | 430 | if (card_status & WD_WDRST) |
336 | *status |= WDIOF_CARDRESET; | 431 | *status |= WDIOF_CARDRESET; |
337 | 432 | ||
@@ -360,10 +455,10 @@ static int pcwd_get_status(int *status) | |||
360 | 455 | ||
361 | static int pcwd_clear_status(void) | 456 | static int pcwd_clear_status(void) |
362 | { | 457 | { |
363 | if (revision == PCWD_REVISION_C) { | 458 | if (pcwd_private.revision == PCWD_REVISION_C) { |
364 | spin_lock(&io_lock); | 459 | spin_lock(&pcwd_private.io_lock); |
365 | outb_p(0x00, current_readport + 1); /* clear reset status */ | 460 | outb_p(0x00, pcwd_private.io_addr + 1); /* clear reset status */ |
366 | spin_unlock(&io_lock); | 461 | spin_unlock(&pcwd_private.io_lock); |
367 | } | 462 | } |
368 | return 0; | 463 | return 0; |
369 | } | 464 | } |
@@ -371,20 +466,20 @@ static int pcwd_clear_status(void) | |||
371 | static int pcwd_get_temperature(int *temperature) | 466 | static int pcwd_get_temperature(int *temperature) |
372 | { | 467 | { |
373 | /* check that port 0 gives temperature info and no command results */ | 468 | /* check that port 0 gives temperature info and no command results */ |
374 | if (command_mode) | 469 | if (pcwd_private.command_mode) |
375 | return -1; | 470 | return -1; |
376 | 471 | ||
377 | *temperature = 0; | 472 | *temperature = 0; |
378 | if (!supports_temp) | 473 | if (!pcwd_private.supports_temp) |
379 | return -ENODEV; | 474 | return -ENODEV; |
380 | 475 | ||
381 | /* | 476 | /* |
382 | * Convert celsius to fahrenheit, since this was | 477 | * Convert celsius to fahrenheit, since this was |
383 | * the decided 'standard' for this return value. | 478 | * the decided 'standard' for this return value. |
384 | */ | 479 | */ |
385 | spin_lock(&io_lock); | 480 | spin_lock(&pcwd_private.io_lock); |
386 | *temperature = ((inb(current_readport)) * 9 / 5) + 32; | 481 | *temperature = ((inb(pcwd_private.io_addr)) * 9 / 5) + 32; |
387 | spin_unlock(&io_lock); | 482 | spin_unlock(&pcwd_private.io_lock); |
388 | 483 | ||
389 | return 0; | 484 | return 0; |
390 | } | 485 | } |
@@ -425,7 +520,7 @@ static int pcwd_ioctl(struct inode *inode, struct file *file, | |||
425 | return put_user(status, argp); | 520 | return put_user(status, argp); |
426 | 521 | ||
427 | case WDIOC_GETBOOTSTATUS: | 522 | case WDIOC_GETBOOTSTATUS: |
428 | return put_user(initial_status, argp); | 523 | return put_user(pcwd_private.boot_status, argp); |
429 | 524 | ||
430 | case WDIOC_GETTEMP: | 525 | case WDIOC_GETTEMP: |
431 | if (pcwd_get_temperature(&temperature)) | 526 | if (pcwd_get_temperature(&temperature)) |
@@ -434,7 +529,7 @@ static int pcwd_ioctl(struct inode *inode, struct file *file, | |||
434 | return put_user(temperature, argp); | 529 | return put_user(temperature, argp); |
435 | 530 | ||
436 | case WDIOC_SETOPTIONS: | 531 | case WDIOC_SETOPTIONS: |
437 | if (revision == PCWD_REVISION_C) | 532 | if (pcwd_private.revision == PCWD_REVISION_C) |
438 | { | 533 | { |
439 | if(copy_from_user(&rv, argp, sizeof(int))) | 534 | if(copy_from_user(&rv, argp, sizeof(int))) |
440 | return -EFAULT; | 535 | return -EFAULT; |
@@ -550,7 +645,7 @@ static ssize_t pcwd_temp_read(struct file *file, char __user *buf, size_t count, | |||
550 | 645 | ||
551 | static int pcwd_temp_open(struct inode *inode, struct file *file) | 646 | static int pcwd_temp_open(struct inode *inode, struct file *file) |
552 | { | 647 | { |
553 | if (!supports_temp) | 648 | if (!pcwd_private.supports_temp) |
554 | return -ENODEV; | 649 | return -ENODEV; |
555 | 650 | ||
556 | return nonseekable_open(inode, file); | 651 | return nonseekable_open(inode, file); |
@@ -616,68 +711,24 @@ static struct notifier_block pcwd_notifier = { | |||
616 | * Init & exit routines | 711 | * Init & exit routines |
617 | */ | 712 | */ |
618 | 713 | ||
619 | static inline void get_support(void) | ||
620 | { | ||
621 | if (inb(current_readport) != 0xF0) | ||
622 | supports_temp = 1; | ||
623 | } | ||
624 | |||
625 | static inline int get_revision(void) | 714 | static inline int get_revision(void) |
626 | { | 715 | { |
627 | int r = PCWD_REVISION_C; | 716 | int r = PCWD_REVISION_C; |
628 | 717 | ||
629 | spin_lock(&io_lock); | 718 | spin_lock(&pcwd_private.io_lock); |
630 | /* REV A cards use only 2 io ports; test | 719 | /* REV A cards use only 2 io ports; test |
631 | * presumes a floating bus reads as 0xff. */ | 720 | * presumes a floating bus reads as 0xff. */ |
632 | if ((inb(current_readport + 2) == 0xFF) || | 721 | if ((inb(pcwd_private.io_addr + 2) == 0xFF) || |
633 | (inb(current_readport + 3) == 0xFF)) | 722 | (inb(pcwd_private.io_addr + 3) == 0xFF)) |
634 | r=PCWD_REVISION_A; | 723 | r=PCWD_REVISION_A; |
635 | spin_unlock(&io_lock); | 724 | spin_unlock(&pcwd_private.io_lock); |
636 | 725 | ||
637 | return r; | 726 | return r; |
638 | } | 727 | } |
639 | 728 | ||
640 | static inline char *get_firmware(void) | ||
641 | { | ||
642 | int one, ten, hund, minor; | ||
643 | char *ret; | ||
644 | |||
645 | ret = kmalloc(6, GFP_KERNEL); | ||
646 | if(ret == NULL) | ||
647 | return NULL; | ||
648 | |||
649 | if (set_command_mode()) { | ||
650 | one = send_isa_command(CMD_ISA_VERSION_INTEGER); | ||
651 | ten = send_isa_command(CMD_ISA_VERSION_TENTH); | ||
652 | hund = send_isa_command(CMD_ISA_VERSION_HUNDRETH); | ||
653 | minor = send_isa_command(CMD_ISA_VERSION_MINOR); | ||
654 | sprintf(ret, "%c.%c%c%c", one, ten, hund, minor); | ||
655 | } | ||
656 | else | ||
657 | sprintf(ret, "ERROR"); | ||
658 | |||
659 | unset_command_mode(); | ||
660 | return(ret); | ||
661 | } | ||
662 | |||
663 | static inline int get_option_switches(void) | ||
664 | { | ||
665 | int rv=0; | ||
666 | |||
667 | if (set_command_mode()) { | ||
668 | /* Get switch settings */ | ||
669 | rv = send_isa_command(CMD_ISA_SWITCH_SETTINGS); | ||
670 | } | ||
671 | |||
672 | unset_command_mode(); | ||
673 | return(rv); | ||
674 | } | ||
675 | |||
676 | static int __devinit pcwatchdog_init(int base_addr) | 729 | static int __devinit pcwatchdog_init(int base_addr) |
677 | { | 730 | { |
678 | int ret; | 731 | int ret; |
679 | char *firmware; | ||
680 | int option_switches; | ||
681 | 732 | ||
682 | cards_found++; | 733 | cards_found++; |
683 | if (cards_found == 1) | 734 | if (cards_found == 1) |
@@ -692,104 +743,66 @@ static int __devinit pcwatchdog_init(int base_addr) | |||
692 | printk(KERN_ERR PFX "No I/O-Address for card detected\n"); | 743 | printk(KERN_ERR PFX "No I/O-Address for card detected\n"); |
693 | return -ENODEV; | 744 | return -ENODEV; |
694 | } | 745 | } |
695 | current_readport = base_addr; | 746 | pcwd_private.io_addr = base_addr; |
696 | 747 | ||
697 | /* Check card's revision */ | 748 | /* Check card's revision */ |
698 | revision = get_revision(); | 749 | pcwd_private.revision = get_revision(); |
699 | 750 | ||
700 | if (!request_region(current_readport, (revision == PCWD_REVISION_A) ? 2 : 4, "PCWD")) { | 751 | if (!request_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4, "PCWD")) { |
701 | printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", | 752 | printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", |
702 | current_readport); | 753 | pcwd_private.io_addr); |
703 | current_readport = 0x0000; | 754 | pcwd_private.io_addr = 0x0000; |
704 | return -EIO; | 755 | return -EIO; |
705 | } | 756 | } |
706 | 757 | ||
707 | /* Initial variables */ | 758 | /* Initial variables */ |
708 | supports_temp = 0; | 759 | pcwd_private.supports_temp = 0; |
709 | temp_panic = 0; | 760 | temp_panic = 0; |
710 | initial_status = 0x0000; | 761 | pcwd_private.boot_status = 0x0000; |
711 | 762 | ||
712 | /* get the boot_status */ | 763 | /* get the boot_status */ |
713 | pcwd_get_status(&initial_status); | 764 | pcwd_get_status(&pcwd_private.boot_status); |
714 | 765 | ||
715 | /* clear the "card caused reboot" flag */ | 766 | /* clear the "card caused reboot" flag */ |
716 | pcwd_clear_status(); | 767 | pcwd_clear_status(); |
717 | 768 | ||
718 | init_timer(&timer); | 769 | init_timer(&pcwd_private.timer); |
719 | timer.function = pcwd_timer_ping; | 770 | pcwd_private.timer.function = pcwd_timer_ping; |
720 | timer.data = 0; | 771 | pcwd_private.timer.data = 0; |
721 | 772 | ||
722 | /* Disable the board */ | 773 | /* Disable the board */ |
723 | pcwd_stop(); | 774 | pcwd_stop(); |
724 | 775 | ||
725 | /* Check whether or not the card supports the temperature device */ | 776 | /* Check whether or not the card supports the temperature device */ |
726 | get_support(); | 777 | pcwd_check_temperature_support(); |
727 | |||
728 | /* Get some extra info from the hardware (in command/debug/diag mode) */ | ||
729 | if (revision == PCWD_REVISION_A) | ||
730 | printk(KERN_INFO PFX "ISA-PC Watchdog (REV.A) detected at port 0x%04x\n", current_readport); | ||
731 | else if (revision == PCWD_REVISION_C) { | ||
732 | firmware = get_firmware(); | ||
733 | printk(KERN_INFO PFX "ISA-PC Watchdog (REV.C) detected at port 0x%04x (Firmware version: %s)\n", | ||
734 | current_readport, firmware); | ||
735 | kfree(firmware); | ||
736 | option_switches = get_option_switches(); | ||
737 | printk(KERN_INFO PFX "Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n", | ||
738 | option_switches, | ||
739 | ((option_switches & 0x10) ? "ON" : "OFF"), | ||
740 | ((option_switches & 0x08) ? "ON" : "OFF")); | ||
741 | |||
742 | /* Reprogram internal heartbeat to 2 seconds */ | ||
743 | if (set_command_mode()) { | ||
744 | send_isa_command(CMD_ISA_DELAY_TIME_2SECS); | ||
745 | unset_command_mode(); | ||
746 | } | ||
747 | } else { | ||
748 | /* Should NEVER happen, unless get_revision() fails. */ | ||
749 | printk(KERN_INFO PFX "Unable to get revision\n"); | ||
750 | release_region(current_readport, (revision == PCWD_REVISION_A) ? 2 : 4); | ||
751 | current_readport = 0x0000; | ||
752 | return -1; | ||
753 | } | ||
754 | 778 | ||
755 | if (supports_temp) | 779 | /* Show info about the card itself */ |
756 | printk(KERN_INFO PFX "Temperature Option Detected\n"); | 780 | pcwd_show_card_info(); |
757 | |||
758 | if (initial_status & WDIOF_CARDRESET) | ||
759 | printk(KERN_INFO PFX "Previous reboot was caused by the card\n"); | ||
760 | |||
761 | if (initial_status & WDIOF_OVERHEAT) { | ||
762 | printk(KERN_EMERG PFX "Card senses a CPU Overheat. Panicking!\n"); | ||
763 | printk(KERN_EMERG PFX "CPU Overheat\n"); | ||
764 | } | ||
765 | |||
766 | if (initial_status == 0) | ||
767 | printk(KERN_INFO PFX "No previous trip detected - Cold boot or reset\n"); | ||
768 | 781 | ||
769 | /* Check that the heartbeat value is within it's range ; if not reset to the default */ | 782 | /* Check that the heartbeat value is within it's range ; if not reset to the default */ |
770 | if (pcwd_set_heartbeat(heartbeat)) { | 783 | if (pcwd_set_heartbeat(heartbeat)) { |
771 | pcwd_set_heartbeat(WATCHDOG_HEARTBEAT); | 784 | pcwd_set_heartbeat(WATCHDOG_HEARTBEAT); |
772 | printk(KERN_INFO PFX "heartbeat value must be 2<=heartbeat<=7200, using %d\n", | 785 | printk(KERN_INFO PFX "heartbeat value must be 2<=heartbeat<=7200, using %d\n", |
773 | WATCHDOG_HEARTBEAT); | 786 | WATCHDOG_HEARTBEAT); |
774 | } | 787 | } |
775 | 788 | ||
776 | ret = register_reboot_notifier(&pcwd_notifier); | 789 | ret = register_reboot_notifier(&pcwd_notifier); |
777 | if (ret) { | 790 | if (ret) { |
778 | printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", | 791 | printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", |
779 | ret); | 792 | ret); |
780 | release_region(current_readport, (revision == PCWD_REVISION_A) ? 2 : 4); | 793 | release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4); |
781 | current_readport = 0x0000; | 794 | pcwd_private.io_addr = 0x0000; |
782 | return ret; | 795 | return ret; |
783 | } | 796 | } |
784 | 797 | ||
785 | if (supports_temp) { | 798 | if (pcwd_private.supports_temp) { |
786 | ret = misc_register(&temp_miscdev); | 799 | ret = misc_register(&temp_miscdev); |
787 | if (ret) { | 800 | if (ret) { |
788 | printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", | 801 | printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", |
789 | TEMP_MINOR, ret); | 802 | TEMP_MINOR, ret); |
790 | unregister_reboot_notifier(&pcwd_notifier); | 803 | unregister_reboot_notifier(&pcwd_notifier); |
791 | release_region(current_readport, (revision == PCWD_REVISION_A) ? 2 : 4); | 804 | release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4); |
792 | current_readport = 0x0000; | 805 | pcwd_private.io_addr = 0x0000; |
793 | return ret; | 806 | return ret; |
794 | } | 807 | } |
795 | } | 808 | } |
@@ -798,11 +811,11 @@ static int __devinit pcwatchdog_init(int base_addr) | |||
798 | if (ret) { | 811 | if (ret) { |
799 | printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", | 812 | printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", |
800 | WATCHDOG_MINOR, ret); | 813 | WATCHDOG_MINOR, ret); |
801 | if (supports_temp) | 814 | if (pcwd_private.supports_temp) |
802 | misc_deregister(&temp_miscdev); | 815 | misc_deregister(&temp_miscdev); |
803 | unregister_reboot_notifier(&pcwd_notifier); | 816 | unregister_reboot_notifier(&pcwd_notifier); |
804 | release_region(current_readport, (revision == PCWD_REVISION_A) ? 2 : 4); | 817 | release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4); |
805 | current_readport = 0x0000; | 818 | pcwd_private.io_addr = 0x0000; |
806 | return ret; | 819 | return ret; |
807 | } | 820 | } |
808 | 821 | ||
@@ -820,11 +833,12 @@ static void __devexit pcwatchdog_exit(void) | |||
820 | 833 | ||
821 | /* Deregister */ | 834 | /* Deregister */ |
822 | misc_deregister(&pcwd_miscdev); | 835 | misc_deregister(&pcwd_miscdev); |
823 | if (supports_temp) | 836 | if (pcwd_private.supports_temp) |
824 | misc_deregister(&temp_miscdev); | 837 | misc_deregister(&temp_miscdev); |
825 | unregister_reboot_notifier(&pcwd_notifier); | 838 | unregister_reboot_notifier(&pcwd_notifier); |
826 | release_region(current_readport, (revision == PCWD_REVISION_A) ? 2 : 4); | 839 | release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4); |
827 | current_readport = 0x0000; | 840 | pcwd_private.io_addr = 0x0000; |
841 | cards_found--; | ||
828 | } | 842 | } |
829 | 843 | ||
830 | /* | 844 | /* |
@@ -887,7 +901,7 @@ static int __init pcwd_init_module(void) | |||
887 | { | 901 | { |
888 | int i, found = 0; | 902 | int i, found = 0; |
889 | 903 | ||
890 | spin_lock_init(&io_lock); | 904 | spin_lock_init(&pcwd_private.io_lock); |
891 | 905 | ||
892 | for (i = 0; pcwd_ioports[i] != 0; i++) { | 906 | for (i = 0; pcwd_ioports[i] != 0; i++) { |
893 | if (pcwd_checkcard(pcwd_ioports[i])) { | 907 | if (pcwd_checkcard(pcwd_ioports[i])) { |
@@ -906,7 +920,7 @@ static int __init pcwd_init_module(void) | |||
906 | 920 | ||
907 | static void __exit pcwd_cleanup_module(void) | 921 | static void __exit pcwd_cleanup_module(void) |
908 | { | 922 | { |
909 | if (current_readport) | 923 | if (pcwd_private.io_addr) |
910 | pcwatchdog_exit(); | 924 | pcwatchdog_exit(); |
911 | return; | 925 | return; |
912 | } | 926 | } |