diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-13 20:58:54 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-13 20:58:54 -0400 |
| commit | 7ec3fa1f4a5c4f9cc3d72983bfd8ac87cae356ab (patch) | |
| tree | cd9dc3433846e59559666b6f5c022a27fc036607 | |
| parent | 9a459f6812fc26e0eb24bbe9c388000e23f67f6b (diff) | |
| parent | 86ded1f35df32ad795cfc8cc1bdaeffbcaec0d5f (diff) | |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog
* git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog:
watchdog: hpwdt (12/12): Make NMI decoding a compile-time option
watchdog: hpwdt (11/12): move NMI-decoding init and exit to seperate functions
watchdog: hpwdt (10/12): Use "decoding" instead of "sourcing"
watchdog: hpwdt (9/12): hpwdt_pretimeout reorganization
watchdog: hpwdt (8/12): implement WDIOC_GETTIMELEFT
watchdog: hpwdt (7/12): allow full range of timer values supported by hardware
watchdog: hpwdt (6/12): Introduce SECS_TO_TICKS() macro
watchdog: hpwdt (5/12): Make x86 assembly ifdef guard more strict
watchdog: hpwdt (4/12): Despecificate driver from iLO2
watchdog: hpwdt (3/12): Group NMI sourcing specific items together
watchdog: hpwdt (2/12): Group options that affect watchdog behavior together
watchdog: hpwdt (1/12): clean-up include-files.
| -rw-r--r-- | drivers/watchdog/Kconfig | 19 | ||||
| -rw-r--r-- | drivers/watchdog/hpwdt.c | 306 |
2 files changed, 186 insertions, 139 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 4d2992aadfb7..b036677df8c4 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
| @@ -574,16 +574,21 @@ config IT87_WDT | |||
| 574 | be called it87_wdt. | 574 | be called it87_wdt. |
| 575 | 575 | ||
| 576 | config HP_WATCHDOG | 576 | config HP_WATCHDOG |
| 577 | tristate "HP Proliant iLO 2 Hardware Watchdog Timer" | 577 | tristate "HP Proliant iLO2+ Hardware Watchdog Timer" |
| 578 | depends on X86 | 578 | depends on X86 |
| 579 | help | 579 | help |
| 580 | A software monitoring watchdog and NMI sourcing driver. This driver | 580 | A software monitoring watchdog and NMI sourcing driver. This driver |
| 581 | will detect lockups and provide stack trace. Also, when an NMI | 581 | will detect lockups and provide a stack trace. This is a driver that |
| 582 | occurs this driver will make the necessary BIOS calls to log | 582 | will only load on a HP ProLiant system with a minimum of iLO2 support. |
| 583 | the cause of the NMI. This is a driver that will only load on a | 583 | To compile this driver as a module, choose M here: the module will be |
| 584 | HP ProLiant system with a minimum of iLO2 support. | 584 | called hpwdt. |
| 585 | To compile this driver as a module, choose M here: the | 585 | |
| 586 | module will be called hpwdt. | 586 | config HPWDT_NMI_DECODING |
| 587 | bool "NMI decoding support for the HP ProLiant iLO2+ Hardware Watchdog Timer" | ||
| 588 | depends on HP_WATCHDOG | ||
| 589 | help | ||
| 590 | When an NMI occurs this feature will make the necessary BIOS calls to | ||
| 591 | log the cause of the NMI. | ||
| 587 | 592 | ||
| 588 | config SC1200_WDT | 593 | config SC1200_WDT |
| 589 | tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog" | 594 | tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog" |
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index fd312fc8940e..3d77116e4634 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c | |||
| @@ -16,38 +16,55 @@ | |||
| 16 | #include <linux/device.h> | 16 | #include <linux/device.h> |
| 17 | #include <linux/fs.h> | 17 | #include <linux/fs.h> |
| 18 | #include <linux/init.h> | 18 | #include <linux/init.h> |
| 19 | #include <linux/interrupt.h> | ||
| 20 | #include <linux/io.h> | 19 | #include <linux/io.h> |
| 21 | #include <linux/irq.h> | 20 | #include <linux/bitops.h> |
| 22 | #include <linux/nmi.h> | ||
| 23 | #include <linux/kernel.h> | 21 | #include <linux/kernel.h> |
| 24 | #include <linux/miscdevice.h> | 22 | #include <linux/miscdevice.h> |
| 25 | #include <linux/mm.h> | ||
| 26 | #include <linux/module.h> | 23 | #include <linux/module.h> |
| 27 | #include <linux/kdebug.h> | ||
| 28 | #include <linux/moduleparam.h> | 24 | #include <linux/moduleparam.h> |
| 29 | #include <linux/notifier.h> | ||
| 30 | #include <linux/pci.h> | 25 | #include <linux/pci.h> |
| 31 | #include <linux/pci_ids.h> | 26 | #include <linux/pci_ids.h> |
| 32 | #include <linux/reboot.h> | ||
| 33 | #include <linux/sched.h> | ||
| 34 | #include <linux/timer.h> | ||
| 35 | #include <linux/types.h> | 27 | #include <linux/types.h> |
| 36 | #include <linux/uaccess.h> | 28 | #include <linux/uaccess.h> |
| 37 | #include <linux/watchdog.h> | 29 | #include <linux/watchdog.h> |
| 30 | #ifdef CONFIG_HPWDT_NMI_DECODING | ||
| 38 | #include <linux/dmi.h> | 31 | #include <linux/dmi.h> |
| 39 | #include <linux/efi.h> | 32 | #include <linux/spinlock.h> |
| 40 | #include <linux/string.h> | 33 | #include <linux/nmi.h> |
| 41 | #include <linux/bootmem.h> | 34 | #include <linux/kdebug.h> |
| 42 | #include <asm/desc.h> | 35 | #include <linux/notifier.h> |
| 43 | #include <asm/cacheflush.h> | 36 | #include <asm/cacheflush.h> |
| 37 | #endif /* CONFIG_HPWDT_NMI_DECODING */ | ||
| 38 | |||
| 39 | #define HPWDT_VERSION "1.2.0" | ||
| 40 | #define SECS_TO_TICKS(secs) ((secs) * 1000 / 128) | ||
| 41 | #define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000) | ||
| 42 | #define HPWDT_MAX_TIMER TICKS_TO_SECS(65535) | ||
| 43 | #define DEFAULT_MARGIN 30 | ||
| 44 | |||
| 45 | static unsigned int soft_margin = DEFAULT_MARGIN; /* in seconds */ | ||
| 46 | static unsigned int reload; /* the computed soft_margin */ | ||
| 47 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
| 48 | static char expect_release; | ||
| 49 | static unsigned long hpwdt_is_open; | ||
| 50 | |||
| 51 | static void __iomem *pci_mem_addr; /* the PCI-memory address */ | ||
| 52 | static unsigned long __iomem *hpwdt_timer_reg; | ||
| 53 | static unsigned long __iomem *hpwdt_timer_con; | ||
| 44 | 54 | ||
| 55 | static struct pci_device_id hpwdt_devices[] = { | ||
| 56 | { PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB203) }, /* iLO2 */ | ||
| 57 | { PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3306) }, /* iLO3 */ | ||
| 58 | {0}, /* terminate list */ | ||
| 59 | }; | ||
| 60 | MODULE_DEVICE_TABLE(pci, hpwdt_devices); | ||
| 61 | |||
| 62 | #ifdef CONFIG_HPWDT_NMI_DECODING | ||
| 45 | #define PCI_BIOS32_SD_VALUE 0x5F32335F /* "_32_" */ | 63 | #define PCI_BIOS32_SD_VALUE 0x5F32335F /* "_32_" */ |
| 46 | #define CRU_BIOS_SIGNATURE_VALUE 0x55524324 | 64 | #define CRU_BIOS_SIGNATURE_VALUE 0x55524324 |
| 47 | #define PCI_BIOS32_PARAGRAPH_LEN 16 | 65 | #define PCI_BIOS32_PARAGRAPH_LEN 16 |
| 48 | #define PCI_ROM_BASE1 0x000F0000 | 66 | #define PCI_ROM_BASE1 0x000F0000 |
| 49 | #define ROM_SIZE 0x10000 | 67 | #define ROM_SIZE 0x10000 |
| 50 | #define HPWDT_VERSION "1.1.1" | ||
| 51 | 68 | ||
| 52 | struct bios32_service_dir { | 69 | struct bios32_service_dir { |
| 53 | u32 signature; | 70 | u32 signature; |
| @@ -112,37 +129,17 @@ struct cmn_registers { | |||
| 112 | u32 reflags; | 129 | u32 reflags; |
| 113 | } __attribute__((packed)); | 130 | } __attribute__((packed)); |
| 114 | 131 | ||
| 115 | #define DEFAULT_MARGIN 30 | 132 | static unsigned int hpwdt_nmi_decoding; |
| 116 | static unsigned int soft_margin = DEFAULT_MARGIN; /* in seconds */ | ||
| 117 | static unsigned int reload; /* the computed soft_margin */ | ||
| 118 | static int nowayout = WATCHDOG_NOWAYOUT; | ||
| 119 | static char expect_release; | ||
| 120 | static unsigned long hpwdt_is_open; | ||
| 121 | static unsigned int allow_kdump; | 133 | static unsigned int allow_kdump; |
| 122 | static unsigned int hpwdt_nmi_sourcing; | ||
| 123 | static unsigned int priority; /* hpwdt at end of die_notify list */ | 134 | static unsigned int priority; /* hpwdt at end of die_notify list */ |
| 124 | |||
| 125 | static void __iomem *pci_mem_addr; /* the PCI-memory address */ | ||
| 126 | static unsigned long __iomem *hpwdt_timer_reg; | ||
| 127 | static unsigned long __iomem *hpwdt_timer_con; | ||
| 128 | |||
| 129 | static DEFINE_SPINLOCK(rom_lock); | 135 | static DEFINE_SPINLOCK(rom_lock); |
| 130 | |||
| 131 | static void *cru_rom_addr; | 136 | static void *cru_rom_addr; |
| 132 | |||
| 133 | static struct cmn_registers cmn_regs; | 137 | static struct cmn_registers cmn_regs; |
| 134 | 138 | ||
| 135 | static struct pci_device_id hpwdt_devices[] = { | ||
| 136 | { PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB203) }, | ||
| 137 | { PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3306) }, | ||
| 138 | {0}, /* terminate list */ | ||
| 139 | }; | ||
| 140 | MODULE_DEVICE_TABLE(pci, hpwdt_devices); | ||
| 141 | |||
| 142 | extern asmlinkage void asminline_call(struct cmn_registers *pi86Regs, | 139 | extern asmlinkage void asminline_call(struct cmn_registers *pi86Regs, |
| 143 | unsigned long *pRomEntry); | 140 | unsigned long *pRomEntry); |
| 144 | 141 | ||
| 145 | #ifndef CONFIG_X86_64 | 142 | #ifdef CONFIG_X86_32 |
| 146 | /* --32 Bit Bios------------------------------------------------------------ */ | 143 | /* --32 Bit Bios------------------------------------------------------------ */ |
| 147 | 144 | ||
| 148 | #define HPWDT_ARCH 32 | 145 | #define HPWDT_ARCH 32 |
| @@ -331,8 +328,9 @@ static int __devinit detect_cru_service(void) | |||
| 331 | iounmap(p); | 328 | iounmap(p); |
| 332 | return rc; | 329 | return rc; |
| 333 | } | 330 | } |
| 334 | 331 | /* ------------------------------------------------------------------------- */ | |
| 335 | #else | 332 | #endif /* CONFIG_X86_32 */ |
| 333 | #ifdef CONFIG_X86_64 | ||
| 336 | /* --64 Bit Bios------------------------------------------------------------ */ | 334 | /* --64 Bit Bios------------------------------------------------------------ */ |
| 337 | 335 | ||
| 338 | #define HPWDT_ARCH 64 | 336 | #define HPWDT_ARCH 64 |
| @@ -410,17 +408,16 @@ static int __devinit detect_cru_service(void) | |||
| 410 | /* if cru_rom_addr has been set then we found a CRU service */ | 408 | /* if cru_rom_addr has been set then we found a CRU service */ |
| 411 | return ((cru_rom_addr != NULL) ? 0 : -ENODEV); | 409 | return ((cru_rom_addr != NULL) ? 0 : -ENODEV); |
| 412 | } | 410 | } |
| 413 | |||
| 414 | /* ------------------------------------------------------------------------- */ | 411 | /* ------------------------------------------------------------------------- */ |
| 415 | 412 | #endif /* CONFIG_X86_64 */ | |
| 416 | #endif | 413 | #endif /* CONFIG_HPWDT_NMI_DECODING */ |
| 417 | 414 | ||
| 418 | /* | 415 | /* |
| 419 | * Watchdog operations | 416 | * Watchdog operations |
| 420 | */ | 417 | */ |
| 421 | static void hpwdt_start(void) | 418 | static void hpwdt_start(void) |
| 422 | { | 419 | { |
| 423 | reload = (soft_margin * 1000) / 128; | 420 | reload = SECS_TO_TICKS(soft_margin); |
| 424 | iowrite16(reload, hpwdt_timer_reg); | 421 | iowrite16(reload, hpwdt_timer_reg); |
| 425 | iowrite16(0x85, hpwdt_timer_con); | 422 | iowrite16(0x85, hpwdt_timer_con); |
| 426 | } | 423 | } |
| @@ -441,8 +438,7 @@ static void hpwdt_ping(void) | |||
| 441 | 438 | ||
| 442 | static int hpwdt_change_timer(int new_margin) | 439 | static int hpwdt_change_timer(int new_margin) |
| 443 | { | 440 | { |
| 444 | /* Arbitrary, can't find the card's limits */ | 441 | if (new_margin < 1 || new_margin > HPWDT_MAX_TIMER) { |
| 445 | if (new_margin < 5 || new_margin > 600) { | ||
| 446 | printk(KERN_WARNING | 442 | printk(KERN_WARNING |
| 447 | "hpwdt: New value passed in is invalid: %d seconds.\n", | 443 | "hpwdt: New value passed in is invalid: %d seconds.\n", |
| 448 | new_margin); | 444 | new_margin); |
| @@ -453,11 +449,17 @@ static int hpwdt_change_timer(int new_margin) | |||
| 453 | printk(KERN_DEBUG | 449 | printk(KERN_DEBUG |
| 454 | "hpwdt: New timer passed in is %d seconds.\n", | 450 | "hpwdt: New timer passed in is %d seconds.\n", |
| 455 | new_margin); | 451 | new_margin); |
| 456 | reload = (soft_margin * 1000) / 128; | 452 | reload = SECS_TO_TICKS(soft_margin); |
| 457 | 453 | ||
| 458 | return 0; | 454 | return 0; |
| 459 | } | 455 | } |
| 460 | 456 | ||
| 457 | static int hpwdt_time_left(void) | ||
| 458 | { | ||
| 459 | return TICKS_TO_SECS(ioread16(hpwdt_timer_reg)); | ||
| 460 | } | ||
| 461 | |||
| 462 | #ifdef CONFIG_HPWDT_NMI_DECODING | ||
| 461 | /* | 463 | /* |
| 462 | * NMI Handler | 464 | * NMI Handler |
| 463 | */ | 465 | */ |
| @@ -468,26 +470,29 @@ static int hpwdt_pretimeout(struct notifier_block *nb, unsigned long ulReason, | |||
| 468 | static int die_nmi_called; | 470 | static int die_nmi_called; |
| 469 | 471 | ||
| 470 | if (ulReason != DIE_NMI && ulReason != DIE_NMI_IPI) | 472 | if (ulReason != DIE_NMI && ulReason != DIE_NMI_IPI) |
| 471 | return NOTIFY_OK; | 473 | goto out; |
| 472 | 474 | ||
| 473 | if (hpwdt_nmi_sourcing) { | 475 | if (!hpwdt_nmi_decoding) |
| 474 | spin_lock_irqsave(&rom_lock, rom_pl); | 476 | goto out; |
| 475 | if (!die_nmi_called) | 477 | |
| 476 | asminline_call(&cmn_regs, cru_rom_addr); | 478 | spin_lock_irqsave(&rom_lock, rom_pl); |
| 477 | die_nmi_called = 1; | 479 | if (!die_nmi_called) |
| 478 | spin_unlock_irqrestore(&rom_lock, rom_pl); | 480 | asminline_call(&cmn_regs, cru_rom_addr); |
| 479 | if (cmn_regs.u1.ral == 0) { | 481 | die_nmi_called = 1; |
| 480 | printk(KERN_WARNING "hpwdt: An NMI occurred, " | 482 | spin_unlock_irqrestore(&rom_lock, rom_pl); |
| 481 | "but unable to determine source.\n"); | 483 | if (cmn_regs.u1.ral == 0) { |
| 482 | } else { | 484 | printk(KERN_WARNING "hpwdt: An NMI occurred, " |
| 483 | if (allow_kdump) | 485 | "but unable to determine source.\n"); |
| 484 | hpwdt_stop(); | 486 | } else { |
| 485 | panic("An NMI occurred, please see the Integrated " | 487 | if (allow_kdump) |
| 486 | "Management Log for details.\n"); | 488 | hpwdt_stop(); |
| 487 | } | 489 | panic("An NMI occurred, please see the Integrated " |
| 490 | "Management Log for details.\n"); | ||
| 488 | } | 491 | } |
| 492 | out: | ||
| 489 | return NOTIFY_OK; | 493 | return NOTIFY_OK; |
| 490 | } | 494 | } |
| 495 | #endif /* CONFIG_HPWDT_NMI_DECODING */ | ||
| 491 | 496 | ||
| 492 | /* | 497 | /* |
| 493 | * /dev/watchdog handling | 498 | * /dev/watchdog handling |
| @@ -557,7 +562,7 @@ static const struct watchdog_info ident = { | |||
| 557 | .options = WDIOF_SETTIMEOUT | | 562 | .options = WDIOF_SETTIMEOUT | |
| 558 | WDIOF_KEEPALIVEPING | | 563 | WDIOF_KEEPALIVEPING | |
| 559 | WDIOF_MAGICCLOSE, | 564 | WDIOF_MAGICCLOSE, |
| 560 | .identity = "HP iLO2 HW Watchdog Timer", | 565 | .identity = "HP iLO2+ HW Watchdog Timer", |
| 561 | }; | 566 | }; |
| 562 | 567 | ||
| 563 | static long hpwdt_ioctl(struct file *file, unsigned int cmd, | 568 | static long hpwdt_ioctl(struct file *file, unsigned int cmd, |
| @@ -599,6 +604,10 @@ static long hpwdt_ioctl(struct file *file, unsigned int cmd, | |||
| 599 | case WDIOC_GETTIMEOUT: | 604 | case WDIOC_GETTIMEOUT: |
| 600 | ret = put_user(soft_margin, p); | 605 | ret = put_user(soft_margin, p); |
| 601 | break; | 606 | break; |
| 607 | |||
| 608 | case WDIOC_GETTIMELEFT: | ||
| 609 | ret = put_user(hpwdt_time_left(), p); | ||
| 610 | break; | ||
| 602 | } | 611 | } |
| 603 | return ret; | 612 | return ret; |
| 604 | } | 613 | } |
| @@ -621,80 +630,45 @@ static struct miscdevice hpwdt_miscdev = { | |||
| 621 | .fops = &hpwdt_fops, | 630 | .fops = &hpwdt_fops, |
| 622 | }; | 631 | }; |
| 623 | 632 | ||
| 633 | #ifdef CONFIG_HPWDT_NMI_DECODING | ||
| 624 | static struct notifier_block die_notifier = { | 634 | static struct notifier_block die_notifier = { |
| 625 | .notifier_call = hpwdt_pretimeout, | 635 | .notifier_call = hpwdt_pretimeout, |
| 626 | .priority = 0, | 636 | .priority = 0, |
| 627 | }; | 637 | }; |
| 638 | #endif /* CONFIG_HPWDT_NMI_DECODING */ | ||
| 628 | 639 | ||
| 629 | /* | 640 | /* |
| 630 | * Init & Exit | 641 | * Init & Exit |
| 631 | */ | 642 | */ |
| 632 | 643 | ||
| 644 | #ifdef CONFIG_HPWDT_NMI_DECODING | ||
| 633 | #ifdef ARCH_HAS_NMI_WATCHDOG | 645 | #ifdef ARCH_HAS_NMI_WATCHDOG |
| 634 | static void __devinit hpwdt_check_nmi_sourcing(struct pci_dev *dev) | 646 | static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev) |
| 635 | { | 647 | { |
| 636 | /* | 648 | /* |
| 637 | * If nmi_watchdog is turned off then we can turn on | 649 | * If nmi_watchdog is turned off then we can turn on |
| 638 | * our nmi sourcing capability. | 650 | * our nmi decoding capability. |
| 639 | */ | 651 | */ |
| 640 | if (!nmi_watchdog_active()) | 652 | if (!nmi_watchdog_active()) |
| 641 | hpwdt_nmi_sourcing = 1; | 653 | hpwdt_nmi_decoding = 1; |
| 642 | else | 654 | else |
| 643 | dev_warn(&dev->dev, "NMI sourcing is disabled. To enable this " | 655 | dev_warn(&dev->dev, "NMI decoding is disabled. To enable this " |
| 644 | "functionality you must reboot with nmi_watchdog=0 " | 656 | "functionality you must reboot with nmi_watchdog=0 " |
| 645 | "and load the hpwdt driver with priority=1.\n"); | 657 | "and load the hpwdt driver with priority=1.\n"); |
| 646 | } | 658 | } |
| 647 | #else | 659 | #else |
| 648 | static void __devinit hpwdt_check_nmi_sourcing(struct pci_dev *dev) | 660 | static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev) |
| 649 | { | 661 | { |
| 650 | dev_warn(&dev->dev, "NMI sourcing is disabled. " | 662 | dev_warn(&dev->dev, "NMI decoding is disabled. " |
| 651 | "Your kernel does not support a NMI Watchdog.\n"); | 663 | "Your kernel does not support a NMI Watchdog.\n"); |
| 652 | } | 664 | } |
| 653 | #endif | 665 | #endif /* ARCH_HAS_NMI_WATCHDOG */ |
| 654 | 666 | ||
| 655 | static int __devinit hpwdt_init_one(struct pci_dev *dev, | 667 | static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev) |
| 656 | const struct pci_device_id *ent) | ||
| 657 | { | 668 | { |
| 658 | int retval; | 669 | int retval; |
| 659 | 670 | ||
| 660 | /* | 671 | /* |
| 661 | * Check if we can do NMI sourcing or not | ||
| 662 | */ | ||
| 663 | hpwdt_check_nmi_sourcing(dev); | ||
| 664 | |||
| 665 | /* | ||
| 666 | * First let's find out if we are on an iLO2 server. We will | ||
| 667 | * not run on a legacy ASM box. | ||
| 668 | * So we only support the G5 ProLiant servers and higher. | ||
| 669 | */ | ||
| 670 | if (dev->subsystem_vendor != PCI_VENDOR_ID_HP) { | ||
| 671 | dev_warn(&dev->dev, | ||
| 672 | "This server does not have an iLO2 ASIC.\n"); | ||
| 673 | return -ENODEV; | ||
| 674 | } | ||
| 675 | |||
| 676 | if (pci_enable_device(dev)) { | ||
| 677 | dev_warn(&dev->dev, | ||
| 678 | "Not possible to enable PCI Device: 0x%x:0x%x.\n", | ||
| 679 | ent->vendor, ent->device); | ||
| 680 | return -ENODEV; | ||
| 681 | } | ||
| 682 | |||
| 683 | pci_mem_addr = pci_iomap(dev, 1, 0x80); | ||
| 684 | if (!pci_mem_addr) { | ||
| 685 | dev_warn(&dev->dev, | ||
| 686 | "Unable to detect the iLO2 server memory.\n"); | ||
| 687 | retval = -ENOMEM; | ||
| 688 | goto error_pci_iomap; | ||
| 689 | } | ||
| 690 | hpwdt_timer_reg = pci_mem_addr + 0x70; | ||
| 691 | hpwdt_timer_con = pci_mem_addr + 0x72; | ||
| 692 | |||
| 693 | /* Make sure that we have a valid soft_margin */ | ||
| 694 | if (hpwdt_change_timer(soft_margin)) | ||
| 695 | hpwdt_change_timer(DEFAULT_MARGIN); | ||
| 696 | |||
| 697 | /* | ||
| 698 | * We need to map the ROM to get the CRU service. | 672 | * We need to map the ROM to get the CRU service. |
| 699 | * For 32 bit Operating Systems we need to go through the 32 Bit | 673 | * For 32 bit Operating Systems we need to go through the 32 Bit |
| 700 | * BIOS Service Directory | 674 | * BIOS Service Directory |
| @@ -705,7 +679,7 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev, | |||
| 705 | dev_warn(&dev->dev, | 679 | dev_warn(&dev->dev, |
| 706 | "Unable to detect the %d Bit CRU Service.\n", | 680 | "Unable to detect the %d Bit CRU Service.\n", |
| 707 | HPWDT_ARCH); | 681 | HPWDT_ARCH); |
| 708 | goto error_get_cru; | 682 | return retval; |
| 709 | } | 683 | } |
| 710 | 684 | ||
| 711 | /* | 685 | /* |
| @@ -728,9 +702,87 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev, | |||
| 728 | dev_warn(&dev->dev, | 702 | dev_warn(&dev->dev, |
| 729 | "Unable to register a die notifier (err=%d).\n", | 703 | "Unable to register a die notifier (err=%d).\n", |
| 730 | retval); | 704 | retval); |
| 731 | goto error_die_notifier; | 705 | if (cru_rom_addr) |
| 706 | iounmap(cru_rom_addr); | ||
| 732 | } | 707 | } |
| 733 | 708 | ||
| 709 | dev_info(&dev->dev, | ||
| 710 | "HP Watchdog Timer Driver: NMI decoding initialized" | ||
| 711 | ", allow kernel dump: %s (default = 0/OFF)" | ||
| 712 | ", priority: %s (default = 0/LAST).\n", | ||
| 713 | (allow_kdump == 0) ? "OFF" : "ON", | ||
| 714 | (priority == 0) ? "LAST" : "FIRST"); | ||
| 715 | return 0; | ||
| 716 | } | ||
| 717 | |||
| 718 | static void __devexit hpwdt_exit_nmi_decoding(void) | ||
| 719 | { | ||
| 720 | unregister_die_notifier(&die_notifier); | ||
| 721 | if (cru_rom_addr) | ||
| 722 | iounmap(cru_rom_addr); | ||
| 723 | } | ||
| 724 | #else /* !CONFIG_HPWDT_NMI_DECODING */ | ||
| 725 | static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev) | ||
| 726 | { | ||
| 727 | } | ||
| 728 | |||
| 729 | static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev) | ||
| 730 | { | ||
| 731 | return 0; | ||
| 732 | } | ||
| 733 | |||
| 734 | static void __devexit hpwdt_exit_nmi_decoding(void) | ||
| 735 | { | ||
| 736 | } | ||
| 737 | #endif /* CONFIG_HPWDT_NMI_DECODING */ | ||
| 738 | |||
| 739 | static int __devinit hpwdt_init_one(struct pci_dev *dev, | ||
| 740 | const struct pci_device_id *ent) | ||
| 741 | { | ||
| 742 | int retval; | ||
| 743 | |||
| 744 | /* | ||
| 745 | * Check if we can do NMI decoding or not | ||
| 746 | */ | ||
| 747 | hpwdt_check_nmi_decoding(dev); | ||
| 748 | |||
| 749 | /* | ||
| 750 | * First let's find out if we are on an iLO2+ server. We will | ||
| 751 | * not run on a legacy ASM box. | ||
| 752 | * So we only support the G5 ProLiant servers and higher. | ||
| 753 | */ | ||
| 754 | if (dev->subsystem_vendor != PCI_VENDOR_ID_HP) { | ||
| 755 | dev_warn(&dev->dev, | ||
| 756 | "This server does not have an iLO2+ ASIC.\n"); | ||
| 757 | return -ENODEV; | ||
| 758 | } | ||
| 759 | |||
| 760 | if (pci_enable_device(dev)) { | ||
| 761 | dev_warn(&dev->dev, | ||
| 762 | "Not possible to enable PCI Device: 0x%x:0x%x.\n", | ||
| 763 | ent->vendor, ent->device); | ||
| 764 | return -ENODEV; | ||
| 765 | } | ||
| 766 | |||
| 767 | pci_mem_addr = pci_iomap(dev, 1, 0x80); | ||
| 768 | if (!pci_mem_addr) { | ||
| 769 | dev_warn(&dev->dev, | ||
| 770 | "Unable to detect the iLO2+ server memory.\n"); | ||
| 771 | retval = -ENOMEM; | ||
| 772 | goto error_pci_iomap; | ||
| 773 | } | ||
| 774 | hpwdt_timer_reg = pci_mem_addr + 0x70; | ||
| 775 | hpwdt_timer_con = pci_mem_addr + 0x72; | ||
| 776 | |||
| 777 | /* Make sure that we have a valid soft_margin */ | ||
| 778 | if (hpwdt_change_timer(soft_margin)) | ||
| 779 | hpwdt_change_timer(DEFAULT_MARGIN); | ||
| 780 | |||
| 781 | /* Initialize NMI Decoding functionality */ | ||
| 782 | retval = hpwdt_init_nmi_decoding(dev); | ||
| 783 | if (retval != 0) | ||
| 784 | goto error_init_nmi_decoding; | ||
| 785 | |||
| 734 | retval = misc_register(&hpwdt_miscdev); | 786 | retval = misc_register(&hpwdt_miscdev); |
| 735 | if (retval < 0) { | 787 | if (retval < 0) { |
| 736 | dev_warn(&dev->dev, | 788 | dev_warn(&dev->dev, |
| @@ -739,23 +791,14 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev, | |||
| 739 | goto error_misc_register; | 791 | goto error_misc_register; |
| 740 | } | 792 | } |
| 741 | 793 | ||
| 742 | printk(KERN_INFO | 794 | dev_info(&dev->dev, "HP Watchdog Timer Driver: %s" |
| 743 | "hp Watchdog Timer Driver: %s" | 795 | ", timer margin: %d seconds (nowayout=%d).\n", |
| 744 | ", timer margin: %d seconds (nowayout=%d)" | 796 | HPWDT_VERSION, soft_margin, nowayout); |
| 745 | ", allow kernel dump: %s (default = 0/OFF)" | ||
| 746 | ", priority: %s (default = 0/LAST).\n", | ||
| 747 | HPWDT_VERSION, soft_margin, nowayout, | ||
| 748 | (allow_kdump == 0) ? "OFF" : "ON", | ||
| 749 | (priority == 0) ? "LAST" : "FIRST"); | ||
| 750 | |||
| 751 | return 0; | 797 | return 0; |
| 752 | 798 | ||
| 753 | error_misc_register: | 799 | error_misc_register: |
| 754 | unregister_die_notifier(&die_notifier); | 800 | hpwdt_exit_nmi_decoding(); |
| 755 | error_die_notifier: | 801 | error_init_nmi_decoding: |
| 756 | if (cru_rom_addr) | ||
| 757 | iounmap(cru_rom_addr); | ||
| 758 | error_get_cru: | ||
| 759 | pci_iounmap(dev, pci_mem_addr); | 802 | pci_iounmap(dev, pci_mem_addr); |
| 760 | error_pci_iomap: | 803 | error_pci_iomap: |
| 761 | pci_disable_device(dev); | 804 | pci_disable_device(dev); |
| @@ -768,10 +811,7 @@ static void __devexit hpwdt_exit(struct pci_dev *dev) | |||
| 768 | hpwdt_stop(); | 811 | hpwdt_stop(); |
| 769 | 812 | ||
| 770 | misc_deregister(&hpwdt_miscdev); | 813 | misc_deregister(&hpwdt_miscdev); |
| 771 | unregister_die_notifier(&die_notifier); | 814 | hpwdt_exit_nmi_decoding(); |
| 772 | |||
| 773 | if (cru_rom_addr) | ||
| 774 | iounmap(cru_rom_addr); | ||
| 775 | pci_iounmap(dev, pci_mem_addr); | 815 | pci_iounmap(dev, pci_mem_addr); |
| 776 | pci_disable_device(dev); | 816 | pci_disable_device(dev); |
| 777 | } | 817 | } |
| @@ -802,16 +842,18 @@ MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | |||
| 802 | module_param(soft_margin, int, 0); | 842 | module_param(soft_margin, int, 0); |
| 803 | MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds"); | 843 | MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds"); |
| 804 | 844 | ||
| 805 | module_param(allow_kdump, int, 0); | ||
| 806 | MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs"); | ||
| 807 | |||
| 808 | module_param(nowayout, int, 0); | 845 | module_param(nowayout, int, 0); |
| 809 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" | 846 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" |
| 810 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); | 847 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
| 811 | 848 | ||
| 849 | #ifdef CONFIG_HPWDT_NMI_DECODING | ||
| 850 | module_param(allow_kdump, int, 0); | ||
| 851 | MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs"); | ||
| 852 | |||
| 812 | module_param(priority, int, 0); | 853 | module_param(priority, int, 0); |
| 813 | MODULE_PARM_DESC(priority, "The hpwdt driver handles NMIs first or last" | 854 | MODULE_PARM_DESC(priority, "The hpwdt driver handles NMIs first or last" |
| 814 | " (default = 0/Last)\n"); | 855 | " (default = 0/Last)\n"); |
| 856 | #endif /* !CONFIG_HPWDT_NMI_DECODING */ | ||
| 815 | 857 | ||
| 816 | module_init(hpwdt_init); | 858 | module_init(hpwdt_init); |
| 817 | module_exit(hpwdt_cleanup); | 859 | module_exit(hpwdt_cleanup); |
