diff options
-rw-r--r-- | drivers/char/watchdog/pcwd_pci.c | 175 |
1 files changed, 108 insertions, 67 deletions
diff --git a/drivers/char/watchdog/pcwd_pci.c b/drivers/char/watchdog/pcwd_pci.c index 5a80adbf8032..0cb62e39e58f 100644 --- a/drivers/char/watchdog/pcwd_pci.c +++ b/drivers/char/watchdog/pcwd_pci.c | |||
@@ -50,8 +50,8 @@ | |||
50 | #include <asm/io.h> /* For inb/outb/... */ | 50 | #include <asm/io.h> /* For inb/outb/... */ |
51 | 51 | ||
52 | /* Module and version information */ | 52 | /* Module and version information */ |
53 | #define WATCHDOG_VERSION "1.01" | 53 | #define WATCHDOG_VERSION "1.02" |
54 | #define WATCHDOG_DATE "02 Sep 2005" | 54 | #define WATCHDOG_DATE "03 Sep 2005" |
55 | #define WATCHDOG_DRIVER_NAME "PCI-PC Watchdog" | 55 | #define WATCHDOG_DRIVER_NAME "PCI-PC Watchdog" |
56 | #define WATCHDOG_NAME "pcwd_pci" | 56 | #define WATCHDOG_NAME "pcwd_pci" |
57 | #define PFX WATCHDOG_NAME ": " | 57 | #define PFX WATCHDOG_NAME ": " |
@@ -70,19 +70,30 @@ | |||
70 | * These are the defines that describe the control status bits for the | 70 | * These are the defines that describe the control status bits for the |
71 | * PCI-PC Watchdog card. | 71 | * PCI-PC Watchdog card. |
72 | */ | 72 | */ |
73 | #define WD_PCI_WTRP 0x01 /* Watchdog Trip status */ | 73 | /* Port 1 : Control Status #1 */ |
74 | #define WD_PCI_HRBT 0x02 /* Watchdog Heartbeat */ | 74 | #define WD_PCI_WTRP 0x01 /* Watchdog Trip status */ |
75 | #define WD_PCI_TTRP 0x04 /* Temperature Trip status */ | 75 | #define WD_PCI_HRBT 0x02 /* Watchdog Heartbeat */ |
76 | #define WD_PCI_TTRP 0x04 /* Temperature Trip status */ | ||
77 | #define WD_PCI_RL2A 0x08 /* Relay 2 Active */ | ||
78 | #define WD_PCI_RL1A 0x10 /* Relay 1 Active */ | ||
79 | #define WD_PCI_R2DS 0x40 /* Relay 2 Disable Temperature-trip/reset */ | ||
80 | #define WD_PCI_RLY2 0x80 /* Activate Relay 2 on the board */ | ||
81 | /* Port 2 : Control Status #2 */ | ||
82 | #define WD_PCI_WDIS 0x10 /* Watchdog Disable */ | ||
83 | #define WD_PCI_ENTP 0x20 /* Enable Temperature Trip Reset */ | ||
84 | #define WD_PCI_WRSP 0x40 /* Watchdog wrote response */ | ||
85 | #define WD_PCI_PCMD 0x80 /* PC has sent command */ | ||
76 | 86 | ||
77 | /* according to documentation max. time to process a command for the pci | 87 | /* according to documentation max. time to process a command for the pci |
78 | * watchdog card is 100 ms, so we give it 150 ms to do it's job */ | 88 | * watchdog card is 100 ms, so we give it 150 ms to do it's job */ |
79 | #define PCI_COMMAND_TIMEOUT 150 | 89 | #define PCI_COMMAND_TIMEOUT 150 |
80 | 90 | ||
81 | /* Watchdog's internal commands */ | 91 | /* Watchdog's internal commands */ |
82 | #define CMD_GET_STATUS 0x04 | 92 | #define CMD_GET_STATUS 0x04 |
83 | #define CMD_GET_FIRMWARE_VERSION 0x08 | 93 | #define CMD_GET_FIRMWARE_VERSION 0x08 |
84 | #define CMD_READ_WATCHDOG_TIMEOUT 0x18 | 94 | #define CMD_READ_WATCHDOG_TIMEOUT 0x18 |
85 | #define CMD_WRITE_WATCHDOG_TIMEOUT 0x19 | 95 | #define CMD_WRITE_WATCHDOG_TIMEOUT 0x19 |
96 | #define CMD_GET_CLEAR_RESET_COUNT 0x84 | ||
86 | 97 | ||
87 | /* We can only use 1 card due to the /dev/watchdog restriction */ | 98 | /* We can only use 1 card due to the /dev/watchdog restriction */ |
88 | static int cards_found; | 99 | static int cards_found; |
@@ -91,12 +102,12 @@ static int cards_found; | |||
91 | static int temp_panic; | 102 | static int temp_panic; |
92 | static unsigned long is_active; | 103 | static unsigned long is_active; |
93 | static char expect_release; | 104 | static char expect_release; |
94 | static struct { | 105 | static struct { /* this is private data for each PCI-PC watchdog card */ |
95 | int supports_temp; /* Wether or not the card has a temperature device */ | 106 | int supports_temp; /* Wether or not the card has a temperature device */ |
96 | int boot_status; /* The card's boot status */ | 107 | int boot_status; /* The card's boot status */ |
97 | unsigned long io_addr; /* The cards I/O address */ | 108 | unsigned long io_addr; /* The cards I/O address */ |
98 | spinlock_t io_lock; | 109 | spinlock_t io_lock; /* the lock for io operations */ |
99 | struct pci_dev *pdev; | 110 | struct pci_dev *pdev; /* the PCI-device */ |
100 | } pcipcwd_private; | 111 | } pcipcwd_private; |
101 | 112 | ||
102 | /* module parameters */ | 113 | /* module parameters */ |
@@ -131,10 +142,10 @@ static int send_command(int cmd, int *msb, int *lsb) | |||
131 | /* wait till the pci card processed the command, signaled by | 142 | /* wait till the pci card processed the command, signaled by |
132 | * the WRSP bit in port 2 and give it a max. timeout of | 143 | * the WRSP bit in port 2 and give it a max. timeout of |
133 | * PCI_COMMAND_TIMEOUT to process */ | 144 | * PCI_COMMAND_TIMEOUT to process */ |
134 | got_response = inb_p(pcipcwd_private.io_addr + 2) & 0x40; | 145 | got_response = inb_p(pcipcwd_private.io_addr + 2) & WD_PCI_WRSP; |
135 | for (count = 0; (count < PCI_COMMAND_TIMEOUT) && (!got_response); count++) { | 146 | for (count = 0; (count < PCI_COMMAND_TIMEOUT) && (!got_response); count++) { |
136 | mdelay(1); | 147 | mdelay(1); |
137 | got_response = inb_p(pcipcwd_private.io_addr + 2) & 0x40; | 148 | got_response = inb_p(pcipcwd_private.io_addr + 2) & WD_PCI_WRSP; |
138 | } | 149 | } |
139 | 150 | ||
140 | if (got_response) { | 151 | if (got_response) { |
@@ -150,6 +161,55 @@ static int send_command(int cmd, int *msb, int *lsb) | |||
150 | return got_response; | 161 | return got_response; |
151 | } | 162 | } |
152 | 163 | ||
164 | static inline void pcipcwd_check_temperature_support(void) | ||
165 | { | ||
166 | if (inb_p(pcipcwd_private.io_addr) != 0xF0) | ||
167 | pcipcwd_private.supports_temp = 1; | ||
168 | } | ||
169 | |||
170 | static int pcipcwd_get_option_switches(void) | ||
171 | { | ||
172 | int option_switches; | ||
173 | |||
174 | option_switches = inb_p(pcipcwd_private.io_addr + 3); | ||
175 | return option_switches; | ||
176 | } | ||
177 | |||
178 | static void pcipcwd_show_card_info(void) | ||
179 | { | ||
180 | int got_fw_rev, fw_rev_major, fw_rev_minor; | ||
181 | char fw_ver_str[20]; /* The cards firmware version */ | ||
182 | int option_switches; | ||
183 | |||
184 | got_fw_rev = send_command(CMD_GET_FIRMWARE_VERSION, &fw_rev_major, &fw_rev_minor); | ||
185 | if (got_fw_rev) { | ||
186 | sprintf(fw_ver_str, "%u.%02u", fw_rev_major, fw_rev_minor); | ||
187 | } else { | ||
188 | sprintf(fw_ver_str, "<card no answer>"); | ||
189 | } | ||
190 | |||
191 | /* Get switch settings */ | ||
192 | option_switches = pcipcwd_get_option_switches(); | ||
193 | |||
194 | printk(KERN_INFO PFX "Found card at port 0x%04x (Firmware: %s) %s temp option\n", | ||
195 | (int) pcipcwd_private.io_addr, fw_ver_str, | ||
196 | (pcipcwd_private.supports_temp ? "with" : "without")); | ||
197 | |||
198 | printk(KERN_INFO PFX "Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n", | ||
199 | option_switches, | ||
200 | ((option_switches & 0x10) ? "ON" : "OFF"), | ||
201 | ((option_switches & 0x08) ? "ON" : "OFF")); | ||
202 | |||
203 | if (pcipcwd_private.boot_status & WDIOF_CARDRESET) | ||
204 | printk(KERN_INFO PFX "Previous reset was caused by the Watchdog card\n"); | ||
205 | |||
206 | if (pcipcwd_private.boot_status & WDIOF_OVERHEAT) | ||
207 | printk(KERN_INFO PFX "Card sensed a CPU Overheat\n"); | ||
208 | |||
209 | if (pcipcwd_private.boot_status == 0) | ||
210 | printk(KERN_INFO PFX "No previous trip detected - Cold boot or reset\n"); | ||
211 | } | ||
212 | |||
153 | static int pcipcwd_start(void) | 213 | static int pcipcwd_start(void) |
154 | { | 214 | { |
155 | int stat_reg; | 215 | int stat_reg; |
@@ -161,7 +221,7 @@ static int pcipcwd_start(void) | |||
161 | stat_reg = inb_p(pcipcwd_private.io_addr + 2); | 221 | stat_reg = inb_p(pcipcwd_private.io_addr + 2); |
162 | spin_unlock(&pcipcwd_private.io_lock); | 222 | spin_unlock(&pcipcwd_private.io_lock); |
163 | 223 | ||
164 | if (stat_reg & 0x10) { | 224 | if (stat_reg & WD_PCI_WDIS) { |
165 | printk(KERN_ERR PFX "Card timer not enabled\n"); | 225 | printk(KERN_ERR PFX "Card timer not enabled\n"); |
166 | return -1; | 226 | return -1; |
167 | } | 227 | } |
@@ -183,7 +243,7 @@ static int pcipcwd_stop(void) | |||
183 | stat_reg = inb_p(pcipcwd_private.io_addr + 2); | 243 | stat_reg = inb_p(pcipcwd_private.io_addr + 2); |
184 | spin_unlock(&pcipcwd_private.io_lock); | 244 | spin_unlock(&pcipcwd_private.io_lock); |
185 | 245 | ||
186 | if (!(stat_reg & 0x10)) { | 246 | if (!(stat_reg & WD_PCI_WDIS)) { |
187 | printk(KERN_ERR PFX "Card did not acknowledge disable attempt\n"); | 247 | printk(KERN_ERR PFX "Card did not acknowledge disable attempt\n"); |
188 | return -1; | 248 | return -1; |
189 | } | 249 | } |
@@ -194,7 +254,7 @@ static int pcipcwd_stop(void) | |||
194 | static int pcipcwd_keepalive(void) | 254 | static int pcipcwd_keepalive(void) |
195 | { | 255 | { |
196 | /* Re-trigger watchdog by writing to port 0 */ | 256 | /* Re-trigger watchdog by writing to port 0 */ |
197 | outb_p(0x42, pcipcwd_private.io_addr); | 257 | outb_p(0x42, pcipcwd_private.io_addr); /* send out any data */ |
198 | return 0; | 258 | return 0; |
199 | } | 259 | } |
200 | 260 | ||
@@ -215,13 +275,13 @@ static int pcipcwd_set_heartbeat(int t) | |||
215 | 275 | ||
216 | static int pcipcwd_get_status(int *status) | 276 | static int pcipcwd_get_status(int *status) |
217 | { | 277 | { |
218 | int new_status; | 278 | int control_status; |
219 | 279 | ||
220 | *status=0; | 280 | *status=0; |
221 | new_status = inb_p(pcipcwd_private.io_addr + 1); | 281 | control_status = inb_p(pcipcwd_private.io_addr + 1); |
222 | if (new_status & WD_PCI_WTRP) | 282 | if (control_status & WD_PCI_WTRP) |
223 | *status |= WDIOF_CARDRESET; | 283 | *status |= WDIOF_CARDRESET; |
224 | if (new_status & WD_PCI_TTRP) { | 284 | if (control_status & WD_PCI_TTRP) { |
225 | *status |= WDIOF_OVERHEAT; | 285 | *status |= WDIOF_OVERHEAT; |
226 | if (temp_panic) | 286 | if (temp_panic) |
227 | panic(PFX "Temperature overheat trip!\n"); | 287 | panic(PFX "Temperature overheat trip!\n"); |
@@ -232,7 +292,20 @@ static int pcipcwd_get_status(int *status) | |||
232 | 292 | ||
233 | static int pcipcwd_clear_status(void) | 293 | static int pcipcwd_clear_status(void) |
234 | { | 294 | { |
235 | outb_p(0x01, pcipcwd_private.io_addr + 1); | 295 | int control_status; |
296 | int msb; | ||
297 | int reset_counter; | ||
298 | |||
299 | control_status = inb_p(pcipcwd_private.io_addr + 1); | ||
300 | |||
301 | /* clear trip status & LED and keep mode of relay 2 */ | ||
302 | outb_p((control_status & WD_PCI_R2DS) | WD_PCI_WTRP, pcipcwd_private.io_addr + 1); | ||
303 | |||
304 | /* clear reset counter */ | ||
305 | msb=0; | ||
306 | reset_counter=0xff; | ||
307 | send_command(CMD_GET_CLEAR_RESET_COUNT, &msb, &reset_counter); | ||
308 | |||
236 | return 0; | 309 | return 0; |
237 | } | 310 | } |
238 | 311 | ||
@@ -242,11 +315,13 @@ static int pcipcwd_get_temperature(int *temperature) | |||
242 | if (!pcipcwd_private.supports_temp) | 315 | if (!pcipcwd_private.supports_temp) |
243 | return -ENODEV; | 316 | return -ENODEV; |
244 | 317 | ||
318 | *temperature = inb_p(pcipcwd_private.io_addr); | ||
319 | |||
245 | /* | 320 | /* |
246 | * Convert celsius to fahrenheit, since this was | 321 | * Convert celsius to fahrenheit, since this was |
247 | * the decided 'standard' for this return value. | 322 | * the decided 'standard' for this return value. |
248 | */ | 323 | */ |
249 | *temperature = ((inb_p(pcipcwd_private.io_addr)) * 9 / 5) + 32; | 324 | *temperature = (*temperature * 9 / 5) + 32; |
250 | 325 | ||
251 | return 0; | 326 | return 0; |
252 | } | 327 | } |
@@ -256,7 +331,7 @@ static int pcipcwd_get_temperature(int *temperature) | |||
256 | */ | 331 | */ |
257 | 332 | ||
258 | static ssize_t pcipcwd_write(struct file *file, const char __user *data, | 333 | static ssize_t pcipcwd_write(struct file *file, const char __user *data, |
259 | size_t len, loff_t *ppos) | 334 | size_t len, loff_t *ppos) |
260 | { | 335 | { |
261 | /* See if we got the magic character 'V' and reload the timer */ | 336 | /* See if we got the magic character 'V' and reload the timer */ |
262 | if (len) { | 337 | if (len) { |
@@ -381,8 +456,9 @@ static int pcipcwd_ioctl(struct inode *inode, struct file *file, | |||
381 | static int pcipcwd_open(struct inode *inode, struct file *file) | 456 | static int pcipcwd_open(struct inode *inode, struct file *file) |
382 | { | 457 | { |
383 | /* /dev/watchdog can only be opened once */ | 458 | /* /dev/watchdog can only be opened once */ |
384 | if (test_and_set_bit(0, &is_active)) | 459 | if (test_and_set_bit(0, &is_active)) { |
385 | return -EBUSY; | 460 | return -EBUSY; |
461 | } | ||
386 | 462 | ||
387 | /* Activate */ | 463 | /* Activate */ |
388 | pcipcwd_start(); | 464 | pcipcwd_start(); |
@@ -492,19 +568,10 @@ static struct notifier_block pcipcwd_notifier = { | |||
492 | * Init & exit routines | 568 | * Init & exit routines |
493 | */ | 569 | */ |
494 | 570 | ||
495 | static inline void check_temperature_support(void) | ||
496 | { | ||
497 | if (inb_p(pcipcwd_private.io_addr) != 0xF0) | ||
498 | pcipcwd_private.supports_temp = 1; | ||
499 | } | ||
500 | |||
501 | static int __devinit pcipcwd_card_init(struct pci_dev *pdev, | 571 | static int __devinit pcipcwd_card_init(struct pci_dev *pdev, |
502 | const struct pci_device_id *ent) | 572 | const struct pci_device_id *ent) |
503 | { | 573 | { |
504 | int ret = -EIO; | 574 | int ret = -EIO; |
505 | int got_fw_rev, fw_rev_major, fw_rev_minor; | ||
506 | char fw_ver_str[20]; | ||
507 | char option_switches; | ||
508 | 575 | ||
509 | cards_found++; | 576 | cards_found++; |
510 | if (cards_found == 1) | 577 | if (cards_found == 1) |
@@ -546,36 +613,10 @@ static int __devinit pcipcwd_card_init(struct pci_dev *pdev, | |||
546 | pcipcwd_stop(); | 613 | pcipcwd_stop(); |
547 | 614 | ||
548 | /* Check whether or not the card supports the temperature device */ | 615 | /* Check whether or not the card supports the temperature device */ |
549 | check_temperature_support(); | 616 | pcipcwd_check_temperature_support(); |
550 | |||
551 | /* Get the Firmware Version */ | ||
552 | got_fw_rev = send_command(CMD_GET_FIRMWARE_VERSION, &fw_rev_major, &fw_rev_minor); | ||
553 | if (got_fw_rev) { | ||
554 | sprintf(fw_ver_str, "%u.%02u", fw_rev_major, fw_rev_minor); | ||
555 | } else { | ||
556 | sprintf(fw_ver_str, "<card no answer>"); | ||
557 | } | ||
558 | 617 | ||
559 | /* Get switch settings */ | 618 | /* Show info about the card itself */ |
560 | option_switches = inb_p(pcipcwd_private.io_addr + 3); | 619 | pcipcwd_show_card_info(); |
561 | |||
562 | printk(KERN_INFO PFX "Found card at port 0x%04x (Firmware: %s) %s temp option\n", | ||
563 | (int) pcipcwd_private.io_addr, fw_ver_str, | ||
564 | (pcipcwd_private.supports_temp ? "with" : "without")); | ||
565 | |||
566 | printk(KERN_INFO PFX "Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n", | ||
567 | option_switches, | ||
568 | ((option_switches & 0x10) ? "ON" : "OFF"), | ||
569 | ((option_switches & 0x08) ? "ON" : "OFF")); | ||
570 | |||
571 | if (pcipcwd_private.boot_status & WDIOF_CARDRESET) | ||
572 | printk(KERN_INFO PFX "Previous reset was caused by the Watchdog card\n"); | ||
573 | |||
574 | if (pcipcwd_private.boot_status & WDIOF_OVERHEAT) | ||
575 | printk(KERN_INFO PFX "Card sensed a CPU Overheat\n"); | ||
576 | |||
577 | if (pcipcwd_private.boot_status == 0) | ||
578 | printk(KERN_INFO PFX "No previous trip detected - Cold boot or reset\n"); | ||
579 | 620 | ||
580 | /* Check that the heartbeat value is within it's range ; if not reset to the default */ | 621 | /* Check that the heartbeat value is within it's range ; if not reset to the default */ |
581 | if (pcipcwd_set_heartbeat(heartbeat)) { | 622 | if (pcipcwd_set_heartbeat(heartbeat)) { |
@@ -656,7 +697,7 @@ static struct pci_driver pcipcwd_driver = { | |||
656 | 697 | ||
657 | static int __init pcipcwd_init_module(void) | 698 | static int __init pcipcwd_init_module(void) |
658 | { | 699 | { |
659 | spin_lock_init (&pcipcwd_private.io_lock); | 700 | spin_lock_init(&pcipcwd_private.io_lock); |
660 | 701 | ||
661 | return pci_register_driver(&pcipcwd_driver); | 702 | return pci_register_driver(&pcipcwd_driver); |
662 | } | 703 | } |