17f4da474SThomas Mingarelli /* 27f4da474SThomas Mingarelli * HP WatchDog Driver 37f4da474SThomas Mingarelli * based on 47f4da474SThomas Mingarelli * 57f4da474SThomas Mingarelli * SoftDog 0.05: A Software Watchdog Device 67f4da474SThomas Mingarelli * 77f4da474SThomas Mingarelli * (c) Copyright 2007 Hewlett-Packard Development Company, L.P. 87f4da474SThomas Mingarelli * Thomas Mingarelli <thomas.mingarelli@hp.com> 97f4da474SThomas Mingarelli * 107f4da474SThomas Mingarelli * This program is free software; you can redistribute it and/or 117f4da474SThomas Mingarelli * modify it under the terms of the GNU General Public License 127f4da474SThomas Mingarelli * version 2 as published by the Free Software Foundation 137f4da474SThomas Mingarelli * 147f4da474SThomas Mingarelli */ 157f4da474SThomas Mingarelli 167f4da474SThomas Mingarelli #include <linux/device.h> 177f4da474SThomas Mingarelli #include <linux/fs.h> 187f4da474SThomas Mingarelli #include <linux/init.h> 197f4da474SThomas Mingarelli #include <linux/interrupt.h> 207f4da474SThomas Mingarelli #include <linux/io.h> 217f4da474SThomas Mingarelli #include <linux/irq.h> 2247bece87SThomas Mingarelli #include <linux/nmi.h> 237f4da474SThomas Mingarelli #include <linux/kernel.h> 247f4da474SThomas Mingarelli #include <linux/miscdevice.h> 257f4da474SThomas Mingarelli #include <linux/mm.h> 267f4da474SThomas Mingarelli #include <linux/module.h> 277f4da474SThomas Mingarelli #include <linux/kdebug.h> 287f4da474SThomas Mingarelli #include <linux/moduleparam.h> 297f4da474SThomas Mingarelli #include <linux/notifier.h> 307f4da474SThomas Mingarelli #include <linux/pci.h> 317f4da474SThomas Mingarelli #include <linux/pci_ids.h> 327f4da474SThomas Mingarelli #include <linux/reboot.h> 337f4da474SThomas Mingarelli #include <linux/sched.h> 347f4da474SThomas Mingarelli #include <linux/timer.h> 357f4da474SThomas Mingarelli #include <linux/types.h> 367f4da474SThomas Mingarelli #include <linux/uaccess.h> 377f4da474SThomas Mingarelli #include <linux/watchdog.h> 387f4da474SThomas Mingarelli #include <linux/dmi.h> 397f4da474SThomas Mingarelli #include <linux/efi.h> 407f4da474SThomas Mingarelli #include <linux/string.h> 417f4da474SThomas Mingarelli #include <linux/bootmem.h> 427f4da474SThomas Mingarelli #include <linux/slab.h> 437f4da474SThomas Mingarelli #include <asm/desc.h> 4406026413SBernhard Walle #include <asm/cacheflush.h> 457f4da474SThomas Mingarelli 467f4da474SThomas Mingarelli #define PCI_BIOS32_SD_VALUE 0x5F32335F /* "_32_" */ 477f4da474SThomas Mingarelli #define CRU_BIOS_SIGNATURE_VALUE 0x55524324 487f4da474SThomas Mingarelli #define PCI_BIOS32_PARAGRAPH_LEN 16 497f4da474SThomas Mingarelli #define PCI_ROM_BASE1 0x000F0000 507f4da474SThomas Mingarelli #define ROM_SIZE 0x10000 5147bece87SThomas Mingarelli #define HPWDT_VERSION "1.1.1" 527f4da474SThomas Mingarelli 537f4da474SThomas Mingarelli struct bios32_service_dir { 547f4da474SThomas Mingarelli u32 signature; 557f4da474SThomas Mingarelli u32 entry_point; 567f4da474SThomas Mingarelli u8 revision; 577f4da474SThomas Mingarelli u8 length; 587f4da474SThomas Mingarelli u8 checksum; 597f4da474SThomas Mingarelli u8 reserved[5]; 607f4da474SThomas Mingarelli }; 617f4da474SThomas Mingarelli 627f4da474SThomas Mingarelli /* type 212 */ 637f4da474SThomas Mingarelli struct smbios_cru64_info { 647f4da474SThomas Mingarelli u8 type; 657f4da474SThomas Mingarelli u8 byte_length; 667f4da474SThomas Mingarelli u16 handle; 677f4da474SThomas Mingarelli u32 signature; 687f4da474SThomas Mingarelli u64 physical_address; 697f4da474SThomas Mingarelli u32 double_length; 707f4da474SThomas Mingarelli u32 double_offset; 717f4da474SThomas Mingarelli }; 727f4da474SThomas Mingarelli #define SMBIOS_CRU64_INFORMATION 212 737f4da474SThomas Mingarelli 747f4da474SThomas Mingarelli struct cmn_registers { 757f4da474SThomas Mingarelli union { 767f4da474SThomas Mingarelli struct { 777f4da474SThomas Mingarelli u8 ral; 787f4da474SThomas Mingarelli u8 rah; 797f4da474SThomas Mingarelli u16 rea2; 807f4da474SThomas Mingarelli }; 817f4da474SThomas Mingarelli u32 reax; 827f4da474SThomas Mingarelli } u1; 837f4da474SThomas Mingarelli union { 847f4da474SThomas Mingarelli struct { 857f4da474SThomas Mingarelli u8 rbl; 867f4da474SThomas Mingarelli u8 rbh; 877f4da474SThomas Mingarelli u8 reb2l; 887f4da474SThomas Mingarelli u8 reb2h; 897f4da474SThomas Mingarelli }; 907f4da474SThomas Mingarelli u32 rebx; 917f4da474SThomas Mingarelli } u2; 927f4da474SThomas Mingarelli union { 937f4da474SThomas Mingarelli struct { 947f4da474SThomas Mingarelli u8 rcl; 957f4da474SThomas Mingarelli u8 rch; 967f4da474SThomas Mingarelli u16 rec2; 977f4da474SThomas Mingarelli }; 987f4da474SThomas Mingarelli u32 recx; 997f4da474SThomas Mingarelli } u3; 1007f4da474SThomas Mingarelli union { 1017f4da474SThomas Mingarelli struct { 1027f4da474SThomas Mingarelli u8 rdl; 1037f4da474SThomas Mingarelli u8 rdh; 1047f4da474SThomas Mingarelli u16 red2; 1057f4da474SThomas Mingarelli }; 1067f4da474SThomas Mingarelli u32 redx; 1077f4da474SThomas Mingarelli } u4; 1087f4da474SThomas Mingarelli 1097f4da474SThomas Mingarelli u32 resi; 1107f4da474SThomas Mingarelli u32 redi; 1117f4da474SThomas Mingarelli u16 rds; 1127f4da474SThomas Mingarelli u16 res; 1137f4da474SThomas Mingarelli u32 reflags; 1147f4da474SThomas Mingarelli } __attribute__((packed)); 1157f4da474SThomas Mingarelli 1167f4da474SThomas Mingarelli #define DEFAULT_MARGIN 30 1177f4da474SThomas Mingarelli static unsigned int soft_margin = DEFAULT_MARGIN; /* in seconds */ 1187f4da474SThomas Mingarelli static unsigned int reload; /* the computed soft_margin */ 1197f4da474SThomas Mingarelli static int nowayout = WATCHDOG_NOWAYOUT; 1207f4da474SThomas Mingarelli static char expect_release; 1217f4da474SThomas Mingarelli static unsigned long hpwdt_is_open; 122ab4ba3cdSThomas Mingarelli static unsigned int allow_kdump; 12344df7535STom Mingarelli static unsigned int hpwdt_nmi_sourcing; 12444df7535STom Mingarelli static unsigned int priority; /* hpwdt at end of die_notify list */ 1257f4da474SThomas Mingarelli 1267f4da474SThomas Mingarelli static void __iomem *pci_mem_addr; /* the PCI-memory address */ 1277f4da474SThomas Mingarelli static unsigned long __iomem *hpwdt_timer_reg; 1287f4da474SThomas Mingarelli static unsigned long __iomem *hpwdt_timer_con; 1297f4da474SThomas Mingarelli 1307f4da474SThomas Mingarelli static DEFINE_SPINLOCK(rom_lock); 1317f4da474SThomas Mingarelli 1327f4da474SThomas Mingarelli static void *cru_rom_addr; 1337f4da474SThomas Mingarelli 1347f4da474SThomas Mingarelli static struct cmn_registers cmn_regs; 1357f4da474SThomas Mingarelli 1367f4da474SThomas Mingarelli static struct pci_device_id hpwdt_devices[] = { 137d8100c3aSThomas Mingarelli { PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB203) }, 138d8100c3aSThomas Mingarelli { PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3306) }, 1397f4da474SThomas Mingarelli {0}, /* terminate list */ 1407f4da474SThomas Mingarelli }; 1417f4da474SThomas Mingarelli MODULE_DEVICE_TABLE(pci, hpwdt_devices); 1427f4da474SThomas Mingarelli 143143a2e54SWim Van Sebroeck extern asmlinkage void asminline_call(struct cmn_registers *pi86Regs, 144143a2e54SWim Van Sebroeck unsigned long *pRomEntry); 1451f6ef234SLinus Torvalds 1467f4da474SThomas Mingarelli #ifndef CONFIG_X86_64 1477f4da474SThomas Mingarelli /* --32 Bit Bios------------------------------------------------------------ */ 1487f4da474SThomas Mingarelli 1497f4da474SThomas Mingarelli #define HPWDT_ARCH 32 1507f4da474SThomas Mingarelli 1511f6ef234SLinus Torvalds asm(".text \n\t" 1521f6ef234SLinus Torvalds ".align 4 \n" 1531f6ef234SLinus Torvalds "asminline_call: \n\t" 1541f6ef234SLinus Torvalds "pushl %ebp \n\t" 1557f4da474SThomas Mingarelli "movl %esp, %ebp \n\t" 1567f4da474SThomas Mingarelli "pusha \n\t" 1577f4da474SThomas Mingarelli "pushf \n\t" 1587f4da474SThomas Mingarelli "push %es \n\t" 1597f4da474SThomas Mingarelli "push %ds \n\t" 1607f4da474SThomas Mingarelli "pop %es \n\t" 1617f4da474SThomas Mingarelli "movl 8(%ebp),%eax \n\t" 1627f4da474SThomas Mingarelli "movl 4(%eax),%ebx \n\t" 1637f4da474SThomas Mingarelli "movl 8(%eax),%ecx \n\t" 1647f4da474SThomas Mingarelli "movl 12(%eax),%edx \n\t" 1657f4da474SThomas Mingarelli "movl 16(%eax),%esi \n\t" 1667f4da474SThomas Mingarelli "movl 20(%eax),%edi \n\t" 1677f4da474SThomas Mingarelli "movl (%eax),%eax \n\t" 1687f4da474SThomas Mingarelli "push %cs \n\t" 1697f4da474SThomas Mingarelli "call *12(%ebp) \n\t" 1707f4da474SThomas Mingarelli "pushf \n\t" 1717f4da474SThomas Mingarelli "pushl %eax \n\t" 1727f4da474SThomas Mingarelli "movl 8(%ebp),%eax \n\t" 1737f4da474SThomas Mingarelli "movl %ebx,4(%eax) \n\t" 1747f4da474SThomas Mingarelli "movl %ecx,8(%eax) \n\t" 1757f4da474SThomas Mingarelli "movl %edx,12(%eax) \n\t" 1767f4da474SThomas Mingarelli "movl %esi,16(%eax) \n\t" 1777f4da474SThomas Mingarelli "movl %edi,20(%eax) \n\t" 1787f4da474SThomas Mingarelli "movw %ds,24(%eax) \n\t" 1797f4da474SThomas Mingarelli "movw %es,26(%eax) \n\t" 1807f4da474SThomas Mingarelli "popl %ebx \n\t" 1817f4da474SThomas Mingarelli "movl %ebx,(%eax) \n\t" 1827f4da474SThomas Mingarelli "popl %ebx \n\t" 1837f4da474SThomas Mingarelli "movl %ebx,28(%eax) \n\t" 1847f4da474SThomas Mingarelli "pop %es \n\t" 1857f4da474SThomas Mingarelli "popf \n\t" 1867f4da474SThomas Mingarelli "popa \n\t" 1871f6ef234SLinus Torvalds "leave \n\t" 1881f6ef234SLinus Torvalds "ret \n\t" 1891f6ef234SLinus Torvalds ".previous"); 1901f6ef234SLinus Torvalds 1917f4da474SThomas Mingarelli 1927f4da474SThomas Mingarelli /* 1937f4da474SThomas Mingarelli * cru_detect 1947f4da474SThomas Mingarelli * 1957f4da474SThomas Mingarelli * Routine Description: 1967f4da474SThomas Mingarelli * This function uses the 32-bit BIOS Service Directory record to 1977f4da474SThomas Mingarelli * search for a $CRU record. 1987f4da474SThomas Mingarelli * 1997f4da474SThomas Mingarelli * Return Value: 2007f4da474SThomas Mingarelli * 0 : SUCCESS 2017f4da474SThomas Mingarelli * <0 : FAILURE 2027f4da474SThomas Mingarelli */ 2037f4da474SThomas Mingarelli static int __devinit cru_detect(unsigned long map_entry, 2047f4da474SThomas Mingarelli unsigned long map_offset) 2057f4da474SThomas Mingarelli { 2067f4da474SThomas Mingarelli void *bios32_map; 2077f4da474SThomas Mingarelli unsigned long *bios32_entrypoint; 2087f4da474SThomas Mingarelli unsigned long cru_physical_address; 2097f4da474SThomas Mingarelli unsigned long cru_length; 2107f4da474SThomas Mingarelli unsigned long physical_bios_base = 0; 2117f4da474SThomas Mingarelli unsigned long physical_bios_offset = 0; 2127f4da474SThomas Mingarelli int retval = -ENODEV; 2137f4da474SThomas Mingarelli 2147f4da474SThomas Mingarelli bios32_map = ioremap(map_entry, (2 * PAGE_SIZE)); 2157f4da474SThomas Mingarelli 2167f4da474SThomas Mingarelli if (bios32_map == NULL) 2177f4da474SThomas Mingarelli return -ENODEV; 2187f4da474SThomas Mingarelli 2197f4da474SThomas Mingarelli bios32_entrypoint = bios32_map + map_offset; 2207f4da474SThomas Mingarelli 2217f4da474SThomas Mingarelli cmn_regs.u1.reax = CRU_BIOS_SIGNATURE_VALUE; 2227f4da474SThomas Mingarelli 2237f4da474SThomas Mingarelli asminline_call(&cmn_regs, bios32_entrypoint); 2247f4da474SThomas Mingarelli 2257f4da474SThomas Mingarelli if (cmn_regs.u1.ral != 0) { 2267f4da474SThomas Mingarelli printk(KERN_WARNING 2277f4da474SThomas Mingarelli "hpwdt: Call succeeded but with an error: 0x%x\n", 2287f4da474SThomas Mingarelli cmn_regs.u1.ral); 2297f4da474SThomas Mingarelli } else { 2307f4da474SThomas Mingarelli physical_bios_base = cmn_regs.u2.rebx; 2317f4da474SThomas Mingarelli physical_bios_offset = cmn_regs.u4.redx; 2327f4da474SThomas Mingarelli cru_length = cmn_regs.u3.recx; 2337f4da474SThomas Mingarelli cru_physical_address = 2347f4da474SThomas Mingarelli physical_bios_base + physical_bios_offset; 2357f4da474SThomas Mingarelli 2367f4da474SThomas Mingarelli /* If the values look OK, then map it in. */ 2377f4da474SThomas Mingarelli if ((physical_bios_base + physical_bios_offset)) { 2387f4da474SThomas Mingarelli cru_rom_addr = 2397f4da474SThomas Mingarelli ioremap(cru_physical_address, cru_length); 2407f4da474SThomas Mingarelli if (cru_rom_addr) 2417f4da474SThomas Mingarelli retval = 0; 2427f4da474SThomas Mingarelli } 2437f4da474SThomas Mingarelli 2447f4da474SThomas Mingarelli printk(KERN_DEBUG "hpwdt: CRU Base Address: 0x%lx\n", 2457f4da474SThomas Mingarelli physical_bios_base); 2467f4da474SThomas Mingarelli printk(KERN_DEBUG "hpwdt: CRU Offset Address: 0x%lx\n", 2477f4da474SThomas Mingarelli physical_bios_offset); 2487f4da474SThomas Mingarelli printk(KERN_DEBUG "hpwdt: CRU Length: 0x%lx\n", 2497f4da474SThomas Mingarelli cru_length); 2507f4da474SThomas Mingarelli printk(KERN_DEBUG "hpwdt: CRU Mapped Address: 0x%x\n", 2517f4da474SThomas Mingarelli (unsigned int)&cru_rom_addr); 2527f4da474SThomas Mingarelli } 2537f4da474SThomas Mingarelli iounmap(bios32_map); 2547f4da474SThomas Mingarelli return retval; 2557f4da474SThomas Mingarelli } 2567f4da474SThomas Mingarelli 2577f4da474SThomas Mingarelli /* 25830ec910eSRoland Dreier * bios_checksum 25930ec910eSRoland Dreier */ 26030ec910eSRoland Dreier static int __devinit bios_checksum(const char __iomem *ptr, int len) 26130ec910eSRoland Dreier { 26230ec910eSRoland Dreier char sum = 0; 26330ec910eSRoland Dreier int i; 26430ec910eSRoland Dreier 26530ec910eSRoland Dreier /* 26630ec910eSRoland Dreier * calculate checksum of size bytes. This should add up 26730ec910eSRoland Dreier * to zero if we have a valid header. 26830ec910eSRoland Dreier */ 26930ec910eSRoland Dreier for (i = 0; i < len; i++) 27030ec910eSRoland Dreier sum += ptr[i]; 27130ec910eSRoland Dreier 27230ec910eSRoland Dreier return ((sum == 0) && (len > 0)); 27330ec910eSRoland Dreier } 27430ec910eSRoland Dreier 27530ec910eSRoland Dreier /* 2767f4da474SThomas Mingarelli * bios32_present 2777f4da474SThomas Mingarelli * 2787f4da474SThomas Mingarelli * Routine Description: 2797f4da474SThomas Mingarelli * This function finds the 32-bit BIOS Service Directory 2807f4da474SThomas Mingarelli * 2817f4da474SThomas Mingarelli * Return Value: 2827f4da474SThomas Mingarelli * 0 : SUCCESS 2837f4da474SThomas Mingarelli * <0 : FAILURE 2847f4da474SThomas Mingarelli */ 2857f4da474SThomas Mingarelli static int __devinit bios32_present(const char __iomem *p) 2867f4da474SThomas Mingarelli { 2877f4da474SThomas Mingarelli struct bios32_service_dir *bios_32_ptr; 2887f4da474SThomas Mingarelli int length; 2897f4da474SThomas Mingarelli unsigned long map_entry, map_offset; 2907f4da474SThomas Mingarelli 2917f4da474SThomas Mingarelli bios_32_ptr = (struct bios32_service_dir *) p; 2927f4da474SThomas Mingarelli 2937f4da474SThomas Mingarelli /* 2947f4da474SThomas Mingarelli * Search for signature by checking equal to the swizzled value 2957f4da474SThomas Mingarelli * instead of calling another routine to perform a strcmp. 2967f4da474SThomas Mingarelli */ 2977f4da474SThomas Mingarelli if (bios_32_ptr->signature == PCI_BIOS32_SD_VALUE) { 2987f4da474SThomas Mingarelli length = bios_32_ptr->length * PCI_BIOS32_PARAGRAPH_LEN; 2997f4da474SThomas Mingarelli if (bios_checksum(p, length)) { 3007f4da474SThomas Mingarelli /* 3017f4da474SThomas Mingarelli * According to the spec, we're looking for the 3027f4da474SThomas Mingarelli * first 4KB-aligned address below the entrypoint 3037f4da474SThomas Mingarelli * listed in the header. The Service Directory code 3047f4da474SThomas Mingarelli * is guaranteed to occupy no more than 2 4KB pages. 3057f4da474SThomas Mingarelli */ 3067f4da474SThomas Mingarelli map_entry = bios_32_ptr->entry_point & ~(PAGE_SIZE - 1); 3077f4da474SThomas Mingarelli map_offset = bios_32_ptr->entry_point - map_entry; 3087f4da474SThomas Mingarelli 3097f4da474SThomas Mingarelli return cru_detect(map_entry, map_offset); 3107f4da474SThomas Mingarelli } 3117f4da474SThomas Mingarelli } 3127f4da474SThomas Mingarelli return -ENODEV; 3137f4da474SThomas Mingarelli } 3147f4da474SThomas Mingarelli 3157f4da474SThomas Mingarelli static int __devinit detect_cru_service(void) 3167f4da474SThomas Mingarelli { 3177f4da474SThomas Mingarelli char __iomem *p, *q; 3187f4da474SThomas Mingarelli int rc = -1; 3197f4da474SThomas Mingarelli 3207f4da474SThomas Mingarelli /* 3217f4da474SThomas Mingarelli * Search from 0x0f0000 through 0x0fffff, inclusive. 3227f4da474SThomas Mingarelli */ 3237f4da474SThomas Mingarelli p = ioremap(PCI_ROM_BASE1, ROM_SIZE); 3247f4da474SThomas Mingarelli if (p == NULL) 3257f4da474SThomas Mingarelli return -ENOMEM; 3267f4da474SThomas Mingarelli 3277f4da474SThomas Mingarelli for (q = p; q < p + ROM_SIZE; q += 16) { 3287f4da474SThomas Mingarelli rc = bios32_present(q); 3297f4da474SThomas Mingarelli if (!rc) 3307f4da474SThomas Mingarelli break; 3317f4da474SThomas Mingarelli } 3327f4da474SThomas Mingarelli iounmap(p); 3337f4da474SThomas Mingarelli return rc; 3347f4da474SThomas Mingarelli } 3357f4da474SThomas Mingarelli 3367f4da474SThomas Mingarelli #else 3377f4da474SThomas Mingarelli /* --64 Bit Bios------------------------------------------------------------ */ 3387f4da474SThomas Mingarelli 3397f4da474SThomas Mingarelli #define HPWDT_ARCH 64 3407f4da474SThomas Mingarelli 3411f6ef234SLinus Torvalds asm(".text \n\t" 3421f6ef234SLinus Torvalds ".align 4 \n" 3431f6ef234SLinus Torvalds "asminline_call: \n\t" 3441f6ef234SLinus Torvalds "pushq %rbp \n\t" 3457f4da474SThomas Mingarelli "movq %rsp, %rbp \n\t" 3467f4da474SThomas Mingarelli "pushq %rax \n\t" 3477f4da474SThomas Mingarelli "pushq %rbx \n\t" 3487f4da474SThomas Mingarelli "pushq %rdx \n\t" 3497f4da474SThomas Mingarelli "pushq %r12 \n\t" 3507f4da474SThomas Mingarelli "pushq %r9 \n\t" 3517f4da474SThomas Mingarelli "movq %rsi, %r12 \n\t" 3527f4da474SThomas Mingarelli "movq %rdi, %r9 \n\t" 3537f4da474SThomas Mingarelli "movl 4(%r9),%ebx \n\t" 3547f4da474SThomas Mingarelli "movl 8(%r9),%ecx \n\t" 3557f4da474SThomas Mingarelli "movl 12(%r9),%edx \n\t" 3567f4da474SThomas Mingarelli "movl 16(%r9),%esi \n\t" 3577f4da474SThomas Mingarelli "movl 20(%r9),%edi \n\t" 3587f4da474SThomas Mingarelli "movl (%r9),%eax \n\t" 3597f4da474SThomas Mingarelli "call *%r12 \n\t" 3607f4da474SThomas Mingarelli "pushfq \n\t" 3617f4da474SThomas Mingarelli "popq %r12 \n\t" 3627f4da474SThomas Mingarelli "movl %eax, (%r9) \n\t" 3637f4da474SThomas Mingarelli "movl %ebx, 4(%r9) \n\t" 3647f4da474SThomas Mingarelli "movl %ecx, 8(%r9) \n\t" 3657f4da474SThomas Mingarelli "movl %edx, 12(%r9) \n\t" 3667f4da474SThomas Mingarelli "movl %esi, 16(%r9) \n\t" 3677f4da474SThomas Mingarelli "movl %edi, 20(%r9) \n\t" 3687f4da474SThomas Mingarelli "movq %r12, %rax \n\t" 3697f4da474SThomas Mingarelli "movl %eax, 28(%r9) \n\t" 3707f4da474SThomas Mingarelli "popq %r9 \n\t" 3717f4da474SThomas Mingarelli "popq %r12 \n\t" 3727f4da474SThomas Mingarelli "popq %rdx \n\t" 3737f4da474SThomas Mingarelli "popq %rbx \n\t" 3747f4da474SThomas Mingarelli "popq %rax \n\t" 3751f6ef234SLinus Torvalds "leave \n\t" 3761f6ef234SLinus Torvalds "ret \n\t" 3771f6ef234SLinus Torvalds ".previous"); 3787f4da474SThomas Mingarelli 3797f4da474SThomas Mingarelli /* 3807f4da474SThomas Mingarelli * dmi_find_cru 3817f4da474SThomas Mingarelli * 3827f4da474SThomas Mingarelli * Routine Description: 38330ec910eSRoland Dreier * This function checks whether or not a SMBIOS/DMI record is 3847f4da474SThomas Mingarelli * the 64bit CRU info or not 3857f4da474SThomas Mingarelli */ 386e7a19c56SJean Delvare static void __devinit dmi_find_cru(const struct dmi_header *dm, void *dummy) 3877f4da474SThomas Mingarelli { 3887f4da474SThomas Mingarelli struct smbios_cru64_info *smbios_cru64_ptr; 3897f4da474SThomas Mingarelli unsigned long cru_physical_address; 3907f4da474SThomas Mingarelli 3917f4da474SThomas Mingarelli if (dm->type == SMBIOS_CRU64_INFORMATION) { 3927f4da474SThomas Mingarelli smbios_cru64_ptr = (struct smbios_cru64_info *) dm; 3937f4da474SThomas Mingarelli if (smbios_cru64_ptr->signature == CRU_BIOS_SIGNATURE_VALUE) { 3947f4da474SThomas Mingarelli cru_physical_address = 3957f4da474SThomas Mingarelli smbios_cru64_ptr->physical_address + 3967f4da474SThomas Mingarelli smbios_cru64_ptr->double_offset; 3977f4da474SThomas Mingarelli cru_rom_addr = ioremap(cru_physical_address, 3987f4da474SThomas Mingarelli smbios_cru64_ptr->double_length); 39906026413SBernhard Walle set_memory_x((unsigned long)cru_rom_addr & PAGE_MASK, 40006026413SBernhard Walle smbios_cru64_ptr->double_length >> PAGE_SHIFT); 4017f4da474SThomas Mingarelli } 4027f4da474SThomas Mingarelli } 4037f4da474SThomas Mingarelli } 4047f4da474SThomas Mingarelli 4057f4da474SThomas Mingarelli static int __devinit detect_cru_service(void) 4067f4da474SThomas Mingarelli { 4077f4da474SThomas Mingarelli cru_rom_addr = NULL; 4087f4da474SThomas Mingarelli 409e7a19c56SJean Delvare dmi_walk(dmi_find_cru, NULL); 4107f4da474SThomas Mingarelli 4117f4da474SThomas Mingarelli /* if cru_rom_addr has been set then we found a CRU service */ 4127f4da474SThomas Mingarelli return ((cru_rom_addr != NULL) ? 0 : -ENODEV); 4137f4da474SThomas Mingarelli } 4147f4da474SThomas Mingarelli 4157f4da474SThomas Mingarelli /* ------------------------------------------------------------------------- */ 4167f4da474SThomas Mingarelli 4177f4da474SThomas Mingarelli #endif 4187f4da474SThomas Mingarelli 4197f4da474SThomas Mingarelli /* 4207f4da474SThomas Mingarelli * Watchdog operations 4217f4da474SThomas Mingarelli */ 4227f4da474SThomas Mingarelli static void hpwdt_start(void) 4237f4da474SThomas Mingarelli { 4247f4da474SThomas Mingarelli reload = (soft_margin * 1000) / 128; 4257f4da474SThomas Mingarelli iowrite16(reload, hpwdt_timer_reg); 4267f4da474SThomas Mingarelli iowrite16(0x85, hpwdt_timer_con); 4277f4da474SThomas Mingarelli } 4287f4da474SThomas Mingarelli 4297f4da474SThomas Mingarelli static void hpwdt_stop(void) 4307f4da474SThomas Mingarelli { 4317f4da474SThomas Mingarelli unsigned long data; 4327f4da474SThomas Mingarelli 4337f4da474SThomas Mingarelli data = ioread16(hpwdt_timer_con); 4347f4da474SThomas Mingarelli data &= 0xFE; 4357f4da474SThomas Mingarelli iowrite16(data, hpwdt_timer_con); 4367f4da474SThomas Mingarelli } 4377f4da474SThomas Mingarelli 4387f4da474SThomas Mingarelli static void hpwdt_ping(void) 4397f4da474SThomas Mingarelli { 4407f4da474SThomas Mingarelli iowrite16(reload, hpwdt_timer_reg); 4417f4da474SThomas Mingarelli } 4427f4da474SThomas Mingarelli 4437f4da474SThomas Mingarelli static int hpwdt_change_timer(int new_margin) 4447f4da474SThomas Mingarelli { 4457f4da474SThomas Mingarelli /* Arbitrary, can't find the card's limits */ 446*8ba42bd8SThomas Mingarelli if (new_margin < 5 || new_margin > 600) { 4477f4da474SThomas Mingarelli printk(KERN_WARNING 4487f4da474SThomas Mingarelli "hpwdt: New value passed in is invalid: %d seconds.\n", 4497f4da474SThomas Mingarelli new_margin); 4507f4da474SThomas Mingarelli return -EINVAL; 4517f4da474SThomas Mingarelli } 4527f4da474SThomas Mingarelli 4537f4da474SThomas Mingarelli soft_margin = new_margin; 4547f4da474SThomas Mingarelli printk(KERN_DEBUG 4557f4da474SThomas Mingarelli "hpwdt: New timer passed in is %d seconds.\n", 4567f4da474SThomas Mingarelli new_margin); 4577f4da474SThomas Mingarelli reload = (soft_margin * 1000) / 128; 4587f4da474SThomas Mingarelli 4597f4da474SThomas Mingarelli return 0; 4607f4da474SThomas Mingarelli } 4617f4da474SThomas Mingarelli 4627f4da474SThomas Mingarelli /* 463ab4ba3cdSThomas Mingarelli * NMI Handler 464ab4ba3cdSThomas Mingarelli */ 465ab4ba3cdSThomas Mingarelli static int hpwdt_pretimeout(struct notifier_block *nb, unsigned long ulReason, 466ab4ba3cdSThomas Mingarelli void *data) 467ab4ba3cdSThomas Mingarelli { 468ab4ba3cdSThomas Mingarelli unsigned long rom_pl; 469ab4ba3cdSThomas Mingarelli static int die_nmi_called; 470ab4ba3cdSThomas Mingarelli 471ab4ba3cdSThomas Mingarelli if (ulReason != DIE_NMI && ulReason != DIE_NMI_IPI) 472ab4ba3cdSThomas Mingarelli return NOTIFY_OK; 473ab4ba3cdSThomas Mingarelli 47447bece87SThomas Mingarelli if (hpwdt_nmi_sourcing) { 475ab4ba3cdSThomas Mingarelli spin_lock_irqsave(&rom_lock, rom_pl); 476ab4ba3cdSThomas Mingarelli if (!die_nmi_called) 477ab4ba3cdSThomas Mingarelli asminline_call(&cmn_regs, cru_rom_addr); 478ab4ba3cdSThomas Mingarelli die_nmi_called = 1; 479ab4ba3cdSThomas Mingarelli spin_unlock_irqrestore(&rom_lock, rom_pl); 480ab4ba3cdSThomas Mingarelli if (cmn_regs.u1.ral == 0) { 481ab4ba3cdSThomas Mingarelli printk(KERN_WARNING "hpwdt: An NMI occurred, " 482ab4ba3cdSThomas Mingarelli "but unable to determine source.\n"); 483ab4ba3cdSThomas Mingarelli } else { 484ab4ba3cdSThomas Mingarelli if (allow_kdump) 485ab4ba3cdSThomas Mingarelli hpwdt_stop(); 486ab4ba3cdSThomas Mingarelli panic("An NMI occurred, please see the Integrated " 487ab4ba3cdSThomas Mingarelli "Management Log for details.\n"); 488ab4ba3cdSThomas Mingarelli } 48947bece87SThomas Mingarelli } 490290172e7SBernhard Walle return NOTIFY_OK; 491ab4ba3cdSThomas Mingarelli } 492ab4ba3cdSThomas Mingarelli 493ab4ba3cdSThomas Mingarelli /* 4947f4da474SThomas Mingarelli * /dev/watchdog handling 4957f4da474SThomas Mingarelli */ 4967f4da474SThomas Mingarelli static int hpwdt_open(struct inode *inode, struct file *file) 4977f4da474SThomas Mingarelli { 4987f4da474SThomas Mingarelli /* /dev/watchdog can only be opened once */ 4997f4da474SThomas Mingarelli if (test_and_set_bit(0, &hpwdt_is_open)) 5007f4da474SThomas Mingarelli return -EBUSY; 5017f4da474SThomas Mingarelli 5027f4da474SThomas Mingarelli /* Start the watchdog */ 5037f4da474SThomas Mingarelli hpwdt_start(); 5047f4da474SThomas Mingarelli hpwdt_ping(); 5057f4da474SThomas Mingarelli 5067f4da474SThomas Mingarelli return nonseekable_open(inode, file); 5077f4da474SThomas Mingarelli } 5087f4da474SThomas Mingarelli 5097f4da474SThomas Mingarelli static int hpwdt_release(struct inode *inode, struct file *file) 5107f4da474SThomas Mingarelli { 5117f4da474SThomas Mingarelli /* Stop the watchdog */ 5127f4da474SThomas Mingarelli if (expect_release == 42) { 5137f4da474SThomas Mingarelli hpwdt_stop(); 5147f4da474SThomas Mingarelli } else { 5157f4da474SThomas Mingarelli printk(KERN_CRIT 5167f4da474SThomas Mingarelli "hpwdt: Unexpected close, not stopping watchdog!\n"); 5177f4da474SThomas Mingarelli hpwdt_ping(); 5187f4da474SThomas Mingarelli } 5197f4da474SThomas Mingarelli 5207f4da474SThomas Mingarelli expect_release = 0; 5217f4da474SThomas Mingarelli 5227f4da474SThomas Mingarelli /* /dev/watchdog is being closed, make sure it can be re-opened */ 5237f4da474SThomas Mingarelli clear_bit(0, &hpwdt_is_open); 5247f4da474SThomas Mingarelli 5257f4da474SThomas Mingarelli return 0; 5267f4da474SThomas Mingarelli } 5277f4da474SThomas Mingarelli 5287f4da474SThomas Mingarelli static ssize_t hpwdt_write(struct file *file, const char __user *data, 5297f4da474SThomas Mingarelli size_t len, loff_t *ppos) 5307f4da474SThomas Mingarelli { 5317f4da474SThomas Mingarelli /* See if we got the magic character 'V' and reload the timer */ 5327f4da474SThomas Mingarelli if (len) { 5337f4da474SThomas Mingarelli if (!nowayout) { 5347f4da474SThomas Mingarelli size_t i; 5357f4da474SThomas Mingarelli 5367f4da474SThomas Mingarelli /* note: just in case someone wrote the magic character 5377f4da474SThomas Mingarelli * five months ago... */ 5387f4da474SThomas Mingarelli expect_release = 0; 5397f4da474SThomas Mingarelli 5407f4da474SThomas Mingarelli /* scan to see whether or not we got the magic char. */ 5417f4da474SThomas Mingarelli for (i = 0; i != len; i++) { 5427f4da474SThomas Mingarelli char c; 5437f4da474SThomas Mingarelli if (get_user(c, data + i)) 5447f4da474SThomas Mingarelli return -EFAULT; 5457f4da474SThomas Mingarelli if (c == 'V') 5467f4da474SThomas Mingarelli expect_release = 42; 5477f4da474SThomas Mingarelli } 5487f4da474SThomas Mingarelli } 5497f4da474SThomas Mingarelli 5507f4da474SThomas Mingarelli /* someone wrote to us, we should reload the timer */ 5517f4da474SThomas Mingarelli hpwdt_ping(); 5527f4da474SThomas Mingarelli } 5537f4da474SThomas Mingarelli 5547f4da474SThomas Mingarelli return len; 5557f4da474SThomas Mingarelli } 5567f4da474SThomas Mingarelli 55742747d71SWim Van Sebroeck static const struct watchdog_info ident = { 5587f4da474SThomas Mingarelli .options = WDIOF_SETTIMEOUT | 5597f4da474SThomas Mingarelli WDIOF_KEEPALIVEPING | 5607f4da474SThomas Mingarelli WDIOF_MAGICCLOSE, 5617f4da474SThomas Mingarelli .identity = "HP iLO2 HW Watchdog Timer", 5627f4da474SThomas Mingarelli }; 5637f4da474SThomas Mingarelli 5647f4da474SThomas Mingarelli static long hpwdt_ioctl(struct file *file, unsigned int cmd, 5657f4da474SThomas Mingarelli unsigned long arg) 5667f4da474SThomas Mingarelli { 5677f4da474SThomas Mingarelli void __user *argp = (void __user *)arg; 5687f4da474SThomas Mingarelli int __user *p = argp; 5697f4da474SThomas Mingarelli int new_margin; 5707f4da474SThomas Mingarelli int ret = -ENOTTY; 5717f4da474SThomas Mingarelli 5727f4da474SThomas Mingarelli switch (cmd) { 5737f4da474SThomas Mingarelli case WDIOC_GETSUPPORT: 5747f4da474SThomas Mingarelli ret = 0; 5757f4da474SThomas Mingarelli if (copy_to_user(argp, &ident, sizeof(ident))) 5767f4da474SThomas Mingarelli ret = -EFAULT; 5777f4da474SThomas Mingarelli break; 5787f4da474SThomas Mingarelli 5797f4da474SThomas Mingarelli case WDIOC_GETSTATUS: 5807f4da474SThomas Mingarelli case WDIOC_GETBOOTSTATUS: 5817f4da474SThomas Mingarelli ret = put_user(0, p); 5827f4da474SThomas Mingarelli break; 5837f4da474SThomas Mingarelli 5847f4da474SThomas Mingarelli case WDIOC_KEEPALIVE: 5857f4da474SThomas Mingarelli hpwdt_ping(); 5867f4da474SThomas Mingarelli ret = 0; 5877f4da474SThomas Mingarelli break; 5887f4da474SThomas Mingarelli 5897f4da474SThomas Mingarelli case WDIOC_SETTIMEOUT: 5907f4da474SThomas Mingarelli ret = get_user(new_margin, p); 5917f4da474SThomas Mingarelli if (ret) 5927f4da474SThomas Mingarelli break; 5937f4da474SThomas Mingarelli 5947f4da474SThomas Mingarelli ret = hpwdt_change_timer(new_margin); 5957f4da474SThomas Mingarelli if (ret) 5967f4da474SThomas Mingarelli break; 5977f4da474SThomas Mingarelli 5987f4da474SThomas Mingarelli hpwdt_ping(); 5997f4da474SThomas Mingarelli /* Fall */ 6007f4da474SThomas Mingarelli case WDIOC_GETTIMEOUT: 6017f4da474SThomas Mingarelli ret = put_user(soft_margin, p); 6027f4da474SThomas Mingarelli break; 6037f4da474SThomas Mingarelli } 6047f4da474SThomas Mingarelli return ret; 6057f4da474SThomas Mingarelli } 6067f4da474SThomas Mingarelli 6077f4da474SThomas Mingarelli /* 6087f4da474SThomas Mingarelli * Kernel interfaces 6097f4da474SThomas Mingarelli */ 610d5c26a59SWim Van Sebroeck static const struct file_operations hpwdt_fops = { 6117f4da474SThomas Mingarelli .owner = THIS_MODULE, 6127f4da474SThomas Mingarelli .llseek = no_llseek, 6137f4da474SThomas Mingarelli .write = hpwdt_write, 6147f4da474SThomas Mingarelli .unlocked_ioctl = hpwdt_ioctl, 6157f4da474SThomas Mingarelli .open = hpwdt_open, 6167f4da474SThomas Mingarelli .release = hpwdt_release, 6177f4da474SThomas Mingarelli }; 6187f4da474SThomas Mingarelli 6197f4da474SThomas Mingarelli static struct miscdevice hpwdt_miscdev = { 6207f4da474SThomas Mingarelli .minor = WATCHDOG_MINOR, 6217f4da474SThomas Mingarelli .name = "watchdog", 6227f4da474SThomas Mingarelli .fops = &hpwdt_fops, 6237f4da474SThomas Mingarelli }; 6247f4da474SThomas Mingarelli 6257f4da474SThomas Mingarelli static struct notifier_block die_notifier = { 6267f4da474SThomas Mingarelli .notifier_call = hpwdt_pretimeout, 62744df7535STom Mingarelli .priority = 0, 6287f4da474SThomas Mingarelli }; 6297f4da474SThomas Mingarelli 6307f4da474SThomas Mingarelli /* 6317f4da474SThomas Mingarelli * Init & Exit 6327f4da474SThomas Mingarelli */ 6337f4da474SThomas Mingarelli 63447bece87SThomas Mingarelli #ifdef ARCH_HAS_NMI_WATCHDOG 63547bece87SThomas Mingarelli static void __devinit hpwdt_check_nmi_sourcing(struct pci_dev *dev) 63647bece87SThomas Mingarelli { 63747bece87SThomas Mingarelli /* 63847bece87SThomas Mingarelli * If nmi_watchdog is turned off then we can turn on 63947bece87SThomas Mingarelli * our nmi sourcing capability. 64047bece87SThomas Mingarelli */ 64147bece87SThomas Mingarelli if (!nmi_watchdog_active()) 64247bece87SThomas Mingarelli hpwdt_nmi_sourcing = 1; 64347bece87SThomas Mingarelli else 64447bece87SThomas Mingarelli dev_warn(&dev->dev, "NMI sourcing is disabled. To enable this " 64544df7535STom Mingarelli "functionality you must reboot with nmi_watchdog=0 " 64644df7535STom Mingarelli "and load the hpwdt driver with priority=1.\n"); 64747bece87SThomas Mingarelli } 64847bece87SThomas Mingarelli #else 64947bece87SThomas Mingarelli static void __devinit hpwdt_check_nmi_sourcing(struct pci_dev *dev) 65047bece87SThomas Mingarelli { 65147bece87SThomas Mingarelli dev_warn(&dev->dev, "NMI sourcing is disabled. " 65247bece87SThomas Mingarelli "Your kernel does not support a NMI Watchdog.\n"); 65347bece87SThomas Mingarelli } 65447bece87SThomas Mingarelli #endif 65547bece87SThomas Mingarelli 6567f4da474SThomas Mingarelli static int __devinit hpwdt_init_one(struct pci_dev *dev, 6577f4da474SThomas Mingarelli const struct pci_device_id *ent) 6587f4da474SThomas Mingarelli { 6597f4da474SThomas Mingarelli int retval; 6607f4da474SThomas Mingarelli 6617f4da474SThomas Mingarelli /* 66247bece87SThomas Mingarelli * Check if we can do NMI sourcing or not 66347bece87SThomas Mingarelli */ 66447bece87SThomas Mingarelli hpwdt_check_nmi_sourcing(dev); 66547bece87SThomas Mingarelli 66647bece87SThomas Mingarelli /* 6677f4da474SThomas Mingarelli * First let's find out if we are on an iLO2 server. We will 6687f4da474SThomas Mingarelli * not run on a legacy ASM box. 669ab4ba3cdSThomas Mingarelli * So we only support the G5 ProLiant servers and higher. 6707f4da474SThomas Mingarelli */ 6717f4da474SThomas Mingarelli if (dev->subsystem_vendor != PCI_VENDOR_ID_HP) { 6727f4da474SThomas Mingarelli dev_warn(&dev->dev, 6737f4da474SThomas Mingarelli "This server does not have an iLO2 ASIC.\n"); 6747f4da474SThomas Mingarelli return -ENODEV; 6757f4da474SThomas Mingarelli } 6767f4da474SThomas Mingarelli 6777f4da474SThomas Mingarelli if (pci_enable_device(dev)) { 6787f4da474SThomas Mingarelli dev_warn(&dev->dev, 6797f4da474SThomas Mingarelli "Not possible to enable PCI Device: 0x%x:0x%x.\n", 6807f4da474SThomas Mingarelli ent->vendor, ent->device); 6817f4da474SThomas Mingarelli return -ENODEV; 6827f4da474SThomas Mingarelli } 6837f4da474SThomas Mingarelli 6847f4da474SThomas Mingarelli pci_mem_addr = pci_iomap(dev, 1, 0x80); 6857f4da474SThomas Mingarelli if (!pci_mem_addr) { 6867f4da474SThomas Mingarelli dev_warn(&dev->dev, 6877f4da474SThomas Mingarelli "Unable to detect the iLO2 server memory.\n"); 6887f4da474SThomas Mingarelli retval = -ENOMEM; 6897f4da474SThomas Mingarelli goto error_pci_iomap; 6907f4da474SThomas Mingarelli } 6917f4da474SThomas Mingarelli hpwdt_timer_reg = pci_mem_addr + 0x70; 6927f4da474SThomas Mingarelli hpwdt_timer_con = pci_mem_addr + 0x72; 6937f4da474SThomas Mingarelli 6947f4da474SThomas Mingarelli /* Make sure that we have a valid soft_margin */ 6957f4da474SThomas Mingarelli if (hpwdt_change_timer(soft_margin)) 6967f4da474SThomas Mingarelli hpwdt_change_timer(DEFAULT_MARGIN); 6977f4da474SThomas Mingarelli 6987f4da474SThomas Mingarelli /* 6997f4da474SThomas Mingarelli * We need to map the ROM to get the CRU service. 7007f4da474SThomas Mingarelli * For 32 bit Operating Systems we need to go through the 32 Bit 7017f4da474SThomas Mingarelli * BIOS Service Directory 7027f4da474SThomas Mingarelli * For 64 bit Operating Systems we get that service through SMBIOS. 7037f4da474SThomas Mingarelli */ 7047f4da474SThomas Mingarelli retval = detect_cru_service(); 7057f4da474SThomas Mingarelli if (retval < 0) { 7067f4da474SThomas Mingarelli dev_warn(&dev->dev, 7077f4da474SThomas Mingarelli "Unable to detect the %d Bit CRU Service.\n", 7087f4da474SThomas Mingarelli HPWDT_ARCH); 7097f4da474SThomas Mingarelli goto error_get_cru; 7107f4da474SThomas Mingarelli } 7117f4da474SThomas Mingarelli 7127f4da474SThomas Mingarelli /* 7137f4da474SThomas Mingarelli * We know this is the only CRU call we need to make so lets keep as 7147f4da474SThomas Mingarelli * few instructions as possible once the NMI comes in. 7157f4da474SThomas Mingarelli */ 7167f4da474SThomas Mingarelli cmn_regs.u1.rah = 0x0D; 7177f4da474SThomas Mingarelli cmn_regs.u1.ral = 0x02; 7187f4da474SThomas Mingarelli 71944df7535STom Mingarelli /* 72044df7535STom Mingarelli * If the priority is set to 1, then we will be put first on the 72144df7535STom Mingarelli * die notify list to handle a critical NMI. The default is to 72244df7535STom Mingarelli * be last so other users of the NMI signal can function. 72344df7535STom Mingarelli */ 72444df7535STom Mingarelli if (priority) 72544df7535STom Mingarelli die_notifier.priority = 0x7FFFFFFF; 72644df7535STom Mingarelli 7277f4da474SThomas Mingarelli retval = register_die_notifier(&die_notifier); 7287f4da474SThomas Mingarelli if (retval != 0) { 7297f4da474SThomas Mingarelli dev_warn(&dev->dev, 7307f4da474SThomas Mingarelli "Unable to register a die notifier (err=%d).\n", 7317f4da474SThomas Mingarelli retval); 7327f4da474SThomas Mingarelli goto error_die_notifier; 7337f4da474SThomas Mingarelli } 7347f4da474SThomas Mingarelli 7357f4da474SThomas Mingarelli retval = misc_register(&hpwdt_miscdev); 7367f4da474SThomas Mingarelli if (retval < 0) { 7377f4da474SThomas Mingarelli dev_warn(&dev->dev, 7387f4da474SThomas Mingarelli "Unable to register miscdev on minor=%d (err=%d).\n", 7397f4da474SThomas Mingarelli WATCHDOG_MINOR, retval); 7407f4da474SThomas Mingarelli goto error_misc_register; 7417f4da474SThomas Mingarelli } 7427f4da474SThomas Mingarelli 7437f4da474SThomas Mingarelli printk(KERN_INFO 744d8100c3aSThomas Mingarelli "hp Watchdog Timer Driver: %s" 745ab4ba3cdSThomas Mingarelli ", timer margin: %d seconds (nowayout=%d)" 74644df7535STom Mingarelli ", allow kernel dump: %s (default = 0/OFF)" 74744df7535STom Mingarelli ", priority: %s (default = 0/LAST).\n", 748d8100c3aSThomas Mingarelli HPWDT_VERSION, soft_margin, nowayout, 74944df7535STom Mingarelli (allow_kdump == 0) ? "OFF" : "ON", 75044df7535STom Mingarelli (priority == 0) ? "LAST" : "FIRST"); 7517f4da474SThomas Mingarelli 7527f4da474SThomas Mingarelli return 0; 7537f4da474SThomas Mingarelli 7547f4da474SThomas Mingarelli error_misc_register: 7557f4da474SThomas Mingarelli unregister_die_notifier(&die_notifier); 7567f4da474SThomas Mingarelli error_die_notifier: 7577f4da474SThomas Mingarelli if (cru_rom_addr) 7587f4da474SThomas Mingarelli iounmap(cru_rom_addr); 7597f4da474SThomas Mingarelli error_get_cru: 7607f4da474SThomas Mingarelli pci_iounmap(dev, pci_mem_addr); 7617f4da474SThomas Mingarelli error_pci_iomap: 7627f4da474SThomas Mingarelli pci_disable_device(dev); 7637f4da474SThomas Mingarelli return retval; 7647f4da474SThomas Mingarelli } 7657f4da474SThomas Mingarelli 7667f4da474SThomas Mingarelli static void __devexit hpwdt_exit(struct pci_dev *dev) 7677f4da474SThomas Mingarelli { 7687f4da474SThomas Mingarelli if (!nowayout) 7697f4da474SThomas Mingarelli hpwdt_stop(); 7707f4da474SThomas Mingarelli 7717f4da474SThomas Mingarelli misc_deregister(&hpwdt_miscdev); 7727f4da474SThomas Mingarelli unregister_die_notifier(&die_notifier); 7737f4da474SThomas Mingarelli 7747f4da474SThomas Mingarelli if (cru_rom_addr) 7757f4da474SThomas Mingarelli iounmap(cru_rom_addr); 7767f4da474SThomas Mingarelli pci_iounmap(dev, pci_mem_addr); 7777f4da474SThomas Mingarelli pci_disable_device(dev); 7787f4da474SThomas Mingarelli } 7797f4da474SThomas Mingarelli 7807f4da474SThomas Mingarelli static struct pci_driver hpwdt_driver = { 7817f4da474SThomas Mingarelli .name = "hpwdt", 7827f4da474SThomas Mingarelli .id_table = hpwdt_devices, 7837f4da474SThomas Mingarelli .probe = hpwdt_init_one, 7847f4da474SThomas Mingarelli .remove = __devexit_p(hpwdt_exit), 7857f4da474SThomas Mingarelli }; 7867f4da474SThomas Mingarelli 7877f4da474SThomas Mingarelli static void __exit hpwdt_cleanup(void) 7887f4da474SThomas Mingarelli { 7897f4da474SThomas Mingarelli pci_unregister_driver(&hpwdt_driver); 7907f4da474SThomas Mingarelli } 7917f4da474SThomas Mingarelli 7927f4da474SThomas Mingarelli static int __init hpwdt_init(void) 7937f4da474SThomas Mingarelli { 7947f4da474SThomas Mingarelli return pci_register_driver(&hpwdt_driver); 7957f4da474SThomas Mingarelli } 7967f4da474SThomas Mingarelli 7977f4da474SThomas Mingarelli MODULE_AUTHOR("Tom Mingarelli"); 7987f4da474SThomas Mingarelli MODULE_DESCRIPTION("hp watchdog driver"); 7997f4da474SThomas Mingarelli MODULE_LICENSE("GPL"); 800d8100c3aSThomas Mingarelli MODULE_VERSION(HPWDT_VERSION); 8017f4da474SThomas Mingarelli MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 8027f4da474SThomas Mingarelli 8037f4da474SThomas Mingarelli module_param(soft_margin, int, 0); 8047f4da474SThomas Mingarelli MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds"); 8057f4da474SThomas Mingarelli 806ab4ba3cdSThomas Mingarelli module_param(allow_kdump, int, 0); 807ab4ba3cdSThomas Mingarelli MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs"); 808ab4ba3cdSThomas Mingarelli 8097f4da474SThomas Mingarelli module_param(nowayout, int, 0); 8107f4da474SThomas Mingarelli MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 8117f4da474SThomas Mingarelli __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 8127f4da474SThomas Mingarelli 81344df7535STom Mingarelli module_param(priority, int, 0); 81444df7535STom Mingarelli MODULE_PARM_DESC(priority, "The hpwdt driver handles NMIs first or last" 81544df7535STom Mingarelli " (default = 0/Last)\n"); 81644df7535STom Mingarelli 8177f4da474SThomas Mingarelli module_init(hpwdt_init); 8187f4da474SThomas Mingarelli module_exit(hpwdt_cleanup); 819