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); |