diff options
Diffstat (limited to 'drivers/watchdog/iTCO_wdt.c')
-rw-r--r-- | drivers/watchdog/iTCO_wdt.c | 71 |
1 files changed, 49 insertions, 22 deletions
diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 8da886035374..2c6c2b4ad8bf 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * intel TCO Watchdog Driver | 2 | * intel TCO Watchdog Driver |
3 | * | 3 | * |
4 | * (c) Copyright 2006-2009 Wim Van Sebroeck <wim@iguana.be>. | 4 | * (c) Copyright 2006-2010 Wim Van Sebroeck <wim@iguana.be>. |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or | 6 | * This program is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU General Public License | 7 | * modify it under the terms of the GNU General Public License |
@@ -26,12 +26,15 @@ | |||
26 | * document number 301473-002, 301474-026: 82801F (ICH6) | 26 | * document number 301473-002, 301474-026: 82801F (ICH6) |
27 | * document number 313082-001, 313075-006: 631xESB, 632xESB | 27 | * document number 313082-001, 313075-006: 631xESB, 632xESB |
28 | * document number 307013-003, 307014-024: 82801G (ICH7) | 28 | * document number 307013-003, 307014-024: 82801G (ICH7) |
29 | * document number 322896-001, 322897-001: NM10 | ||
29 | * document number 313056-003, 313057-017: 82801H (ICH8) | 30 | * document number 313056-003, 313057-017: 82801H (ICH8) |
30 | * document number 316972-004, 316973-012: 82801I (ICH9) | 31 | * document number 316972-004, 316973-012: 82801I (ICH9) |
31 | * document number 319973-002, 319974-002: 82801J (ICH10) | 32 | * document number 319973-002, 319974-002: 82801J (ICH10) |
32 | * document number 322169-001, 322170-003: 5 Series, 3400 Series (PCH) | 33 | * document number 322169-001, 322170-003: 5 Series, 3400 Series (PCH) |
33 | * document number 320066-003, 320257-008: EP80597 (IICH) | 34 | * document number 320066-003, 320257-008: EP80597 (IICH) |
34 | * document number TBD : Cougar Point (CPT) | 35 | * document number 324645-001, 324646-001: Cougar Point (CPT) |
36 | * document number TBD : Patsburg (PBG) | ||
37 | * document number TBD : DH89xxCC | ||
35 | */ | 38 | */ |
36 | 39 | ||
37 | /* | 40 | /* |
@@ -40,7 +43,7 @@ | |||
40 | 43 | ||
41 | /* Module and version information */ | 44 | /* Module and version information */ |
42 | #define DRV_NAME "iTCO_wdt" | 45 | #define DRV_NAME "iTCO_wdt" |
43 | #define DRV_VERSION "1.05" | 46 | #define DRV_VERSION "1.06" |
44 | #define PFX DRV_NAME ": " | 47 | #define PFX DRV_NAME ": " |
45 | 48 | ||
46 | /* Includes */ | 49 | /* Includes */ |
@@ -84,6 +87,7 @@ enum iTCO_chipsets { | |||
84 | TCO_ICH7DH, /* ICH7DH */ | 87 | TCO_ICH7DH, /* ICH7DH */ |
85 | TCO_ICH7M, /* ICH7-M & ICH7-U */ | 88 | TCO_ICH7M, /* ICH7-M & ICH7-U */ |
86 | TCO_ICH7MDH, /* ICH7-M DH */ | 89 | TCO_ICH7MDH, /* ICH7-M DH */ |
90 | TCO_NM10, /* NM10 */ | ||
87 | TCO_ICH8, /* ICH8 & ICH8R */ | 91 | TCO_ICH8, /* ICH8 & ICH8R */ |
88 | TCO_ICH8DH, /* ICH8DH */ | 92 | TCO_ICH8DH, /* ICH8DH */ |
89 | TCO_ICH8DO, /* ICH8DO */ | 93 | TCO_ICH8DO, /* ICH8DO */ |
@@ -146,6 +150,9 @@ enum iTCO_chipsets { | |||
146 | TCO_CPT29, /* Cougar Point */ | 150 | TCO_CPT29, /* Cougar Point */ |
147 | TCO_CPT30, /* Cougar Point */ | 151 | TCO_CPT30, /* Cougar Point */ |
148 | TCO_CPT31, /* Cougar Point */ | 152 | TCO_CPT31, /* Cougar Point */ |
153 | TCO_PBG1, /* Patsburg */ | ||
154 | TCO_PBG2, /* Patsburg */ | ||
155 | TCO_DH89XXCC, /* DH89xxCC */ | ||
149 | }; | 156 | }; |
150 | 157 | ||
151 | static struct { | 158 | static struct { |
@@ -171,6 +178,7 @@ static struct { | |||
171 | {"ICH7DH", 2}, | 178 | {"ICH7DH", 2}, |
172 | {"ICH7-M or ICH7-U", 2}, | 179 | {"ICH7-M or ICH7-U", 2}, |
173 | {"ICH7-M DH", 2}, | 180 | {"ICH7-M DH", 2}, |
181 | {"NM10", 2}, | ||
174 | {"ICH8 or ICH8R", 2}, | 182 | {"ICH8 or ICH8R", 2}, |
175 | {"ICH8DH", 2}, | 183 | {"ICH8DH", 2}, |
176 | {"ICH8DO", 2}, | 184 | {"ICH8DO", 2}, |
@@ -233,6 +241,9 @@ static struct { | |||
233 | {"Cougar Point", 2}, | 241 | {"Cougar Point", 2}, |
234 | {"Cougar Point", 2}, | 242 | {"Cougar Point", 2}, |
235 | {"Cougar Point", 2}, | 243 | {"Cougar Point", 2}, |
244 | {"Patsburg", 2}, | ||
245 | {"Patsburg", 2}, | ||
246 | {"DH89xxCC", 2}, | ||
236 | {NULL, 0} | 247 | {NULL, 0} |
237 | }; | 248 | }; |
238 | 249 | ||
@@ -286,6 +297,7 @@ static struct pci_device_id iTCO_wdt_pci_tbl[] = { | |||
286 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_30, TCO_ICH7DH)}, | 297 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_30, TCO_ICH7DH)}, |
287 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_1, TCO_ICH7M)}, | 298 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_1, TCO_ICH7M)}, |
288 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_31, TCO_ICH7MDH)}, | 299 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_31, TCO_ICH7MDH)}, |
300 | { ITCO_PCI_DEVICE(0x27bc, TCO_NM10)}, | ||
289 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_0, TCO_ICH8)}, | 301 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_0, TCO_ICH8)}, |
290 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_2, TCO_ICH8DH)}, | 302 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_2, TCO_ICH8DH)}, |
291 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_3, TCO_ICH8DO)}, | 303 | { ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_3, TCO_ICH8DO)}, |
@@ -348,6 +360,9 @@ static struct pci_device_id iTCO_wdt_pci_tbl[] = { | |||
348 | { ITCO_PCI_DEVICE(0x1c5d, TCO_CPT29)}, | 360 | { ITCO_PCI_DEVICE(0x1c5d, TCO_CPT29)}, |
349 | { ITCO_PCI_DEVICE(0x1c5e, TCO_CPT30)}, | 361 | { ITCO_PCI_DEVICE(0x1c5e, TCO_CPT30)}, |
350 | { ITCO_PCI_DEVICE(0x1c5f, TCO_CPT31)}, | 362 | { ITCO_PCI_DEVICE(0x1c5f, TCO_CPT31)}, |
363 | { ITCO_PCI_DEVICE(0x1d40, TCO_PBG1)}, | ||
364 | { ITCO_PCI_DEVICE(0x1d41, TCO_PBG2)}, | ||
365 | { ITCO_PCI_DEVICE(0x2310, TCO_DH89XXCC)}, | ||
351 | { 0, }, /* End of list */ | 366 | { 0, }, /* End of list */ |
352 | }; | 367 | }; |
353 | MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl); | 368 | MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl); |
@@ -374,7 +389,7 @@ static char expect_release; | |||
374 | static struct { /* this is private data for the iTCO_wdt device */ | 389 | static struct { /* this is private data for the iTCO_wdt device */ |
375 | /* TCO version/generation */ | 390 | /* TCO version/generation */ |
376 | unsigned int iTCO_version; | 391 | unsigned int iTCO_version; |
377 | /* The cards ACPIBASE address (TCOBASE = ACPIBASE+0x60) */ | 392 | /* The device's ACPIBASE address (TCOBASE = ACPIBASE+0x60) */ |
378 | unsigned long ACPIBASE; | 393 | unsigned long ACPIBASE; |
379 | /* NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2)*/ | 394 | /* NO_REBOOT flag is Memory-Mapped GCS register bit 5 (TCO version 2)*/ |
380 | unsigned long __iomem *gcs; | 395 | unsigned long __iomem *gcs; |
@@ -391,8 +406,8 @@ static struct platform_device *iTCO_wdt_platform_device; | |||
391 | #define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */ | 406 | #define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */ |
392 | static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ | 407 | static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ |
393 | module_param(heartbeat, int, 0); | 408 | module_param(heartbeat, int, 0); |
394 | MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. " | 409 | MODULE_PARM_DESC(heartbeat, "Watchdog timeout in seconds. " |
395 | "(2<heartbeat<39 (TCO v1) or 613 (TCO v2), default=" | 410 | "5..76 (TCO v1) or 3..614 (TCO v2), default=" |
396 | __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); | 411 | __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); |
397 | 412 | ||
398 | static int nowayout = WATCHDOG_NOWAYOUT; | 413 | static int nowayout = WATCHDOG_NOWAYOUT; |
@@ -467,7 +482,7 @@ static int iTCO_wdt_start(void) | |||
467 | if (iTCO_wdt_unset_NO_REBOOT_bit()) { | 482 | if (iTCO_wdt_unset_NO_REBOOT_bit()) { |
468 | spin_unlock(&iTCO_wdt_private.io_lock); | 483 | spin_unlock(&iTCO_wdt_private.io_lock); |
469 | printk(KERN_ERR PFX "failed to reset NO_REBOOT flag, " | 484 | printk(KERN_ERR PFX "failed to reset NO_REBOOT flag, " |
470 | "reboot disabled by hardware\n"); | 485 | "reboot disabled by hardware/BIOS\n"); |
471 | return -EIO; | 486 | return -EIO; |
472 | } | 487 | } |
473 | 488 | ||
@@ -523,8 +538,13 @@ static int iTCO_wdt_keepalive(void) | |||
523 | /* Reload the timer by writing to the TCO Timer Counter register */ | 538 | /* Reload the timer by writing to the TCO Timer Counter register */ |
524 | if (iTCO_wdt_private.iTCO_version == 2) | 539 | if (iTCO_wdt_private.iTCO_version == 2) |
525 | outw(0x01, TCO_RLD); | 540 | outw(0x01, TCO_RLD); |
526 | else if (iTCO_wdt_private.iTCO_version == 1) | 541 | else if (iTCO_wdt_private.iTCO_version == 1) { |
542 | /* Reset the timeout status bit so that the timer | ||
543 | * needs to count down twice again before rebooting */ | ||
544 | outw(0x0008, TCO1_STS); /* write 1 to clear bit */ | ||
545 | |||
527 | outb(0x01, TCO_RLD); | 546 | outb(0x01, TCO_RLD); |
547 | } | ||
528 | 548 | ||
529 | spin_unlock(&iTCO_wdt_private.io_lock); | 549 | spin_unlock(&iTCO_wdt_private.io_lock); |
530 | return 0; | 550 | return 0; |
@@ -537,6 +557,11 @@ static int iTCO_wdt_set_heartbeat(int t) | |||
537 | unsigned int tmrval; | 557 | unsigned int tmrval; |
538 | 558 | ||
539 | tmrval = seconds_to_ticks(t); | 559 | tmrval = seconds_to_ticks(t); |
560 | |||
561 | /* For TCO v1 the timer counts down twice before rebooting */ | ||
562 | if (iTCO_wdt_private.iTCO_version == 1) | ||
563 | tmrval /= 2; | ||
564 | |||
540 | /* from the specs: */ | 565 | /* from the specs: */ |
541 | /* "Values of 0h-3h are ignored and should not be attempted" */ | 566 | /* "Values of 0h-3h are ignored and should not be attempted" */ |
542 | if (tmrval < 0x04) | 567 | if (tmrval < 0x04) |
@@ -593,6 +618,8 @@ static int iTCO_wdt_get_timeleft(int *time_left) | |||
593 | spin_lock(&iTCO_wdt_private.io_lock); | 618 | spin_lock(&iTCO_wdt_private.io_lock); |
594 | val8 = inb(TCO_RLD); | 619 | val8 = inb(TCO_RLD); |
595 | val8 &= 0x3f; | 620 | val8 &= 0x3f; |
621 | if (!(inw(TCO1_STS) & 0x0008)) | ||
622 | val8 += (inb(TCOv1_TMR) & 0x3f); | ||
596 | spin_unlock(&iTCO_wdt_private.io_lock); | 623 | spin_unlock(&iTCO_wdt_private.io_lock); |
597 | 624 | ||
598 | *time_left = (val8 * 6) / 10; | 625 | *time_left = (val8 * 6) / 10; |
@@ -769,8 +796,8 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev, | |||
769 | base_address &= 0x0000ff80; | 796 | base_address &= 0x0000ff80; |
770 | if (base_address == 0x00000000) { | 797 | if (base_address == 0x00000000) { |
771 | /* Something's wrong here, ACPIBASE has to be set */ | 798 | /* Something's wrong here, ACPIBASE has to be set */ |
772 | printk(KERN_ERR PFX "failed to get TCOBASE address\n"); | 799 | printk(KERN_ERR PFX "failed to get TCOBASE address, " |
773 | pci_dev_put(pdev); | 800 | "device disabled by hardware/BIOS\n"); |
774 | return -ENODEV; | 801 | return -ENODEV; |
775 | } | 802 | } |
776 | iTCO_wdt_private.iTCO_version = | 803 | iTCO_wdt_private.iTCO_version = |
@@ -785,7 +812,8 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev, | |||
785 | if (iTCO_wdt_private.iTCO_version == 2) { | 812 | if (iTCO_wdt_private.iTCO_version == 2) { |
786 | pci_read_config_dword(pdev, 0xf0, &base_address); | 813 | pci_read_config_dword(pdev, 0xf0, &base_address); |
787 | if ((base_address & 1) == 0) { | 814 | if ((base_address & 1) == 0) { |
788 | printk(KERN_ERR PFX "RCBA is disabled by hardware\n"); | 815 | printk(KERN_ERR PFX "RCBA is disabled by hardware" |
816 | "/BIOS, device disabled\n"); | ||
789 | ret = -ENODEV; | 817 | ret = -ENODEV; |
790 | goto out; | 818 | goto out; |
791 | } | 819 | } |
@@ -796,7 +824,7 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev, | |||
796 | /* Check chipset's NO_REBOOT bit */ | 824 | /* Check chipset's NO_REBOOT bit */ |
797 | if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) { | 825 | if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) { |
798 | printk(KERN_INFO PFX "unable to reset NO_REBOOT flag, " | 826 | printk(KERN_INFO PFX "unable to reset NO_REBOOT flag, " |
799 | "platform may have disabled it\n"); | 827 | "device disabled by hardware/BIOS\n"); |
800 | ret = -ENODEV; /* Cannot reset NO_REBOOT bit */ | 828 | ret = -ENODEV; /* Cannot reset NO_REBOOT bit */ |
801 | goto out_unmap; | 829 | goto out_unmap; |
802 | } | 830 | } |
@@ -807,7 +835,8 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev, | |||
807 | /* The TCO logic uses the TCO_EN bit in the SMI_EN register */ | 835 | /* The TCO logic uses the TCO_EN bit in the SMI_EN register */ |
808 | if (!request_region(SMI_EN, 4, "iTCO_wdt")) { | 836 | if (!request_region(SMI_EN, 4, "iTCO_wdt")) { |
809 | printk(KERN_ERR PFX | 837 | printk(KERN_ERR PFX |
810 | "I/O address 0x%04lx already in use\n", SMI_EN); | 838 | "I/O address 0x%04lx already in use, " |
839 | "device disabled\n", SMI_EN); | ||
811 | ret = -EIO; | 840 | ret = -EIO; |
812 | goto out_unmap; | 841 | goto out_unmap; |
813 | } | 842 | } |
@@ -819,8 +848,8 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev, | |||
819 | /* The TCO I/O registers reside in a 32-byte range pointed to | 848 | /* The TCO I/O registers reside in a 32-byte range pointed to |
820 | by the TCOBASE value */ | 849 | by the TCOBASE value */ |
821 | if (!request_region(TCOBASE, 0x20, "iTCO_wdt")) { | 850 | if (!request_region(TCOBASE, 0x20, "iTCO_wdt")) { |
822 | printk(KERN_ERR PFX "I/O address 0x%04lx already in use\n", | 851 | printk(KERN_ERR PFX "I/O address 0x%04lx already in use " |
823 | TCOBASE); | 852 | "device disabled\n", TCOBASE); |
824 | ret = -EIO; | 853 | ret = -EIO; |
825 | goto unreg_smi_en; | 854 | goto unreg_smi_en; |
826 | } | 855 | } |
@@ -832,9 +861,9 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev, | |||
832 | TCOBASE); | 861 | TCOBASE); |
833 | 862 | ||
834 | /* Clear out the (probably old) status */ | 863 | /* Clear out the (probably old) status */ |
835 | outb(8, TCO1_STS); /* Clear the Time Out Status bit */ | 864 | outw(0x0008, TCO1_STS); /* Clear the Time Out Status bit */ |
836 | outb(2, TCO2_STS); /* Clear SECOND_TO_STS bit */ | 865 | outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */ |
837 | outb(4, TCO2_STS); /* Clear BOOT_STS bit */ | 866 | outw(0x0004, TCO2_STS); /* Clear BOOT_STS bit */ |
838 | 867 | ||
839 | /* Make sure the watchdog is not running */ | 868 | /* Make sure the watchdog is not running */ |
840 | iTCO_wdt_stop(); | 869 | iTCO_wdt_stop(); |
@@ -844,8 +873,7 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev, | |||
844 | if (iTCO_wdt_set_heartbeat(heartbeat)) { | 873 | if (iTCO_wdt_set_heartbeat(heartbeat)) { |
845 | iTCO_wdt_set_heartbeat(WATCHDOG_HEARTBEAT); | 874 | iTCO_wdt_set_heartbeat(WATCHDOG_HEARTBEAT); |
846 | printk(KERN_INFO PFX | 875 | printk(KERN_INFO PFX |
847 | "heartbeat value must be 2 < heartbeat < 39 (TCO v1) " | 876 | "timeout value out of range, using %d\n", heartbeat); |
848 | "or 613 (TCO v2), using %d\n", heartbeat); | ||
849 | } | 877 | } |
850 | 878 | ||
851 | ret = misc_register(&iTCO_wdt_miscdev); | 879 | ret = misc_register(&iTCO_wdt_miscdev); |
@@ -869,7 +897,6 @@ out_unmap: | |||
869 | if (iTCO_wdt_private.iTCO_version == 2) | 897 | if (iTCO_wdt_private.iTCO_version == 2) |
870 | iounmap(iTCO_wdt_private.gcs); | 898 | iounmap(iTCO_wdt_private.gcs); |
871 | out: | 899 | out: |
872 | pci_dev_put(iTCO_wdt_private.pdev); | ||
873 | iTCO_wdt_private.ACPIBASE = 0; | 900 | iTCO_wdt_private.ACPIBASE = 0; |
874 | return ret; | 901 | return ret; |
875 | } | 902 | } |
@@ -910,7 +937,7 @@ static int __devinit iTCO_wdt_probe(struct platform_device *dev) | |||
910 | } | 937 | } |
911 | 938 | ||
912 | if (!found) | 939 | if (!found) |
913 | printk(KERN_INFO PFX "No card detected\n"); | 940 | printk(KERN_INFO PFX "No device detected.\n"); |
914 | 941 | ||
915 | return ret; | 942 | return ret; |
916 | } | 943 | } |