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/io.h> 20a52e6d18Sdann frazier #include <linux/bitops.h> 217f4da474SThomas Mingarelli #include <linux/kernel.h> 227f4da474SThomas Mingarelli #include <linux/miscdevice.h> 237f4da474SThomas Mingarelli #include <linux/module.h> 247f4da474SThomas Mingarelli #include <linux/moduleparam.h> 257f4da474SThomas Mingarelli #include <linux/pci.h> 267f4da474SThomas Mingarelli #include <linux/pci_ids.h> 277f4da474SThomas Mingarelli #include <linux/types.h> 287f4da474SThomas Mingarelli #include <linux/uaccess.h> 297f4da474SThomas Mingarelli #include <linux/watchdog.h> 307f4da474SThomas Mingarelli #include <linux/dmi.h> 31a52e6d18Sdann frazier #include <linux/spinlock.h> 32923410d0Sdann frazier #include <linux/nmi.h> 33923410d0Sdann frazier #include <linux/kdebug.h> 34923410d0Sdann frazier #include <linux/notifier.h> 3506026413SBernhard Walle #include <asm/cacheflush.h> 367f4da474SThomas Mingarelli 37923410d0Sdann frazier #define HPWDT_VERSION "1.1.1" 38*e802e32dSdann frazier #define SECS_TO_TICKS(secs) ((secs) * 1000 / 128) 39923410d0Sdann frazier #define DEFAULT_MARGIN 30 40923410d0Sdann frazier 41923410d0Sdann frazier static unsigned int soft_margin = DEFAULT_MARGIN; /* in seconds */ 42923410d0Sdann frazier static unsigned int reload; /* the computed soft_margin */ 43923410d0Sdann frazier static int nowayout = WATCHDOG_NOWAYOUT; 44923410d0Sdann frazier static char expect_release; 45923410d0Sdann frazier static unsigned long hpwdt_is_open; 46923410d0Sdann frazier 47923410d0Sdann frazier static void __iomem *pci_mem_addr; /* the PCI-memory address */ 48923410d0Sdann frazier static unsigned long __iomem *hpwdt_timer_reg; 49923410d0Sdann frazier static unsigned long __iomem *hpwdt_timer_con; 50923410d0Sdann frazier 51923410d0Sdann frazier static struct pci_device_id hpwdt_devices[] = { 5236e3ff44Sdann frazier { PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB203) }, /* iLO2 */ 5336e3ff44Sdann frazier { PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3306) }, /* iLO3 */ 54923410d0Sdann frazier {0}, /* terminate list */ 55923410d0Sdann frazier }; 56923410d0Sdann frazier MODULE_DEVICE_TABLE(pci, hpwdt_devices); 57923410d0Sdann frazier 587f4da474SThomas Mingarelli #define PCI_BIOS32_SD_VALUE 0x5F32335F /* "_32_" */ 597f4da474SThomas Mingarelli #define CRU_BIOS_SIGNATURE_VALUE 0x55524324 607f4da474SThomas Mingarelli #define PCI_BIOS32_PARAGRAPH_LEN 16 617f4da474SThomas Mingarelli #define PCI_ROM_BASE1 0x000F0000 627f4da474SThomas Mingarelli #define ROM_SIZE 0x10000 637f4da474SThomas Mingarelli 647f4da474SThomas Mingarelli struct bios32_service_dir { 657f4da474SThomas Mingarelli u32 signature; 667f4da474SThomas Mingarelli u32 entry_point; 677f4da474SThomas Mingarelli u8 revision; 687f4da474SThomas Mingarelli u8 length; 697f4da474SThomas Mingarelli u8 checksum; 707f4da474SThomas Mingarelli u8 reserved[5]; 717f4da474SThomas Mingarelli }; 727f4da474SThomas Mingarelli 737f4da474SThomas Mingarelli /* type 212 */ 747f4da474SThomas Mingarelli struct smbios_cru64_info { 757f4da474SThomas Mingarelli u8 type; 767f4da474SThomas Mingarelli u8 byte_length; 777f4da474SThomas Mingarelli u16 handle; 787f4da474SThomas Mingarelli u32 signature; 797f4da474SThomas Mingarelli u64 physical_address; 807f4da474SThomas Mingarelli u32 double_length; 817f4da474SThomas Mingarelli u32 double_offset; 827f4da474SThomas Mingarelli }; 837f4da474SThomas Mingarelli #define SMBIOS_CRU64_INFORMATION 212 847f4da474SThomas Mingarelli 857f4da474SThomas Mingarelli struct cmn_registers { 867f4da474SThomas Mingarelli union { 877f4da474SThomas Mingarelli struct { 887f4da474SThomas Mingarelli u8 ral; 897f4da474SThomas Mingarelli u8 rah; 907f4da474SThomas Mingarelli u16 rea2; 917f4da474SThomas Mingarelli }; 927f4da474SThomas Mingarelli u32 reax; 937f4da474SThomas Mingarelli } u1; 947f4da474SThomas Mingarelli union { 957f4da474SThomas Mingarelli struct { 967f4da474SThomas Mingarelli u8 rbl; 977f4da474SThomas Mingarelli u8 rbh; 987f4da474SThomas Mingarelli u8 reb2l; 997f4da474SThomas Mingarelli u8 reb2h; 1007f4da474SThomas Mingarelli }; 1017f4da474SThomas Mingarelli u32 rebx; 1027f4da474SThomas Mingarelli } u2; 1037f4da474SThomas Mingarelli union { 1047f4da474SThomas Mingarelli struct { 1057f4da474SThomas Mingarelli u8 rcl; 1067f4da474SThomas Mingarelli u8 rch; 1077f4da474SThomas Mingarelli u16 rec2; 1087f4da474SThomas Mingarelli }; 1097f4da474SThomas Mingarelli u32 recx; 1107f4da474SThomas Mingarelli } u3; 1117f4da474SThomas Mingarelli union { 1127f4da474SThomas Mingarelli struct { 1137f4da474SThomas Mingarelli u8 rdl; 1147f4da474SThomas Mingarelli u8 rdh; 1157f4da474SThomas Mingarelli u16 red2; 1167f4da474SThomas Mingarelli }; 1177f4da474SThomas Mingarelli u32 redx; 1187f4da474SThomas Mingarelli } u4; 1197f4da474SThomas Mingarelli 1207f4da474SThomas Mingarelli u32 resi; 1217f4da474SThomas Mingarelli u32 redi; 1227f4da474SThomas Mingarelli u16 rds; 1237f4da474SThomas Mingarelli u16 res; 1247f4da474SThomas Mingarelli u32 reflags; 1257f4da474SThomas Mingarelli } __attribute__((packed)); 1267f4da474SThomas Mingarelli 12744df7535STom Mingarelli static unsigned int hpwdt_nmi_sourcing; 128923410d0Sdann frazier static unsigned int allow_kdump; 12944df7535STom Mingarelli static unsigned int priority; /* hpwdt at end of die_notify list */ 1307f4da474SThomas Mingarelli static DEFINE_SPINLOCK(rom_lock); 1317f4da474SThomas Mingarelli static void *cru_rom_addr; 1327f4da474SThomas Mingarelli static struct cmn_registers cmn_regs; 1337f4da474SThomas Mingarelli 134143a2e54SWim Van Sebroeck extern asmlinkage void asminline_call(struct cmn_registers *pi86Regs, 135143a2e54SWim Van Sebroeck unsigned long *pRomEntry); 1361f6ef234SLinus Torvalds 1376b7f3d53Sdann frazier #ifdef CONFIG_X86_32 1387f4da474SThomas Mingarelli /* --32 Bit Bios------------------------------------------------------------ */ 1397f4da474SThomas Mingarelli 1407f4da474SThomas Mingarelli #define HPWDT_ARCH 32 1417f4da474SThomas Mingarelli 1421f6ef234SLinus Torvalds asm(".text \n\t" 1431f6ef234SLinus Torvalds ".align 4 \n" 1441f6ef234SLinus Torvalds "asminline_call: \n\t" 1451f6ef234SLinus Torvalds "pushl %ebp \n\t" 1467f4da474SThomas Mingarelli "movl %esp, %ebp \n\t" 1477f4da474SThomas Mingarelli "pusha \n\t" 1487f4da474SThomas Mingarelli "pushf \n\t" 1497f4da474SThomas Mingarelli "push %es \n\t" 1507f4da474SThomas Mingarelli "push %ds \n\t" 1517f4da474SThomas Mingarelli "pop %es \n\t" 1527f4da474SThomas Mingarelli "movl 8(%ebp),%eax \n\t" 1537f4da474SThomas Mingarelli "movl 4(%eax),%ebx \n\t" 1547f4da474SThomas Mingarelli "movl 8(%eax),%ecx \n\t" 1557f4da474SThomas Mingarelli "movl 12(%eax),%edx \n\t" 1567f4da474SThomas Mingarelli "movl 16(%eax),%esi \n\t" 1577f4da474SThomas Mingarelli "movl 20(%eax),%edi \n\t" 1587f4da474SThomas Mingarelli "movl (%eax),%eax \n\t" 1597f4da474SThomas Mingarelli "push %cs \n\t" 1607f4da474SThomas Mingarelli "call *12(%ebp) \n\t" 1617f4da474SThomas Mingarelli "pushf \n\t" 1627f4da474SThomas Mingarelli "pushl %eax \n\t" 1637f4da474SThomas Mingarelli "movl 8(%ebp),%eax \n\t" 1647f4da474SThomas Mingarelli "movl %ebx,4(%eax) \n\t" 1657f4da474SThomas Mingarelli "movl %ecx,8(%eax) \n\t" 1667f4da474SThomas Mingarelli "movl %edx,12(%eax) \n\t" 1677f4da474SThomas Mingarelli "movl %esi,16(%eax) \n\t" 1687f4da474SThomas Mingarelli "movl %edi,20(%eax) \n\t" 1697f4da474SThomas Mingarelli "movw %ds,24(%eax) \n\t" 1707f4da474SThomas Mingarelli "movw %es,26(%eax) \n\t" 1717f4da474SThomas Mingarelli "popl %ebx \n\t" 1727f4da474SThomas Mingarelli "movl %ebx,(%eax) \n\t" 1737f4da474SThomas Mingarelli "popl %ebx \n\t" 1747f4da474SThomas Mingarelli "movl %ebx,28(%eax) \n\t" 1757f4da474SThomas Mingarelli "pop %es \n\t" 1767f4da474SThomas Mingarelli "popf \n\t" 1777f4da474SThomas Mingarelli "popa \n\t" 1781f6ef234SLinus Torvalds "leave \n\t" 1791f6ef234SLinus Torvalds "ret \n\t" 1801f6ef234SLinus Torvalds ".previous"); 1811f6ef234SLinus Torvalds 1827f4da474SThomas Mingarelli 1837f4da474SThomas Mingarelli /* 1847f4da474SThomas Mingarelli * cru_detect 1857f4da474SThomas Mingarelli * 1867f4da474SThomas Mingarelli * Routine Description: 1877f4da474SThomas Mingarelli * This function uses the 32-bit BIOS Service Directory record to 1887f4da474SThomas Mingarelli * search for a $CRU record. 1897f4da474SThomas Mingarelli * 1907f4da474SThomas Mingarelli * Return Value: 1917f4da474SThomas Mingarelli * 0 : SUCCESS 1927f4da474SThomas Mingarelli * <0 : FAILURE 1937f4da474SThomas Mingarelli */ 1947f4da474SThomas Mingarelli static int __devinit cru_detect(unsigned long map_entry, 1957f4da474SThomas Mingarelli unsigned long map_offset) 1967f4da474SThomas Mingarelli { 1977f4da474SThomas Mingarelli void *bios32_map; 1987f4da474SThomas Mingarelli unsigned long *bios32_entrypoint; 1997f4da474SThomas Mingarelli unsigned long cru_physical_address; 2007f4da474SThomas Mingarelli unsigned long cru_length; 2017f4da474SThomas Mingarelli unsigned long physical_bios_base = 0; 2027f4da474SThomas Mingarelli unsigned long physical_bios_offset = 0; 2037f4da474SThomas Mingarelli int retval = -ENODEV; 2047f4da474SThomas Mingarelli 2057f4da474SThomas Mingarelli bios32_map = ioremap(map_entry, (2 * PAGE_SIZE)); 2067f4da474SThomas Mingarelli 2077f4da474SThomas Mingarelli if (bios32_map == NULL) 2087f4da474SThomas Mingarelli return -ENODEV; 2097f4da474SThomas Mingarelli 2107f4da474SThomas Mingarelli bios32_entrypoint = bios32_map + map_offset; 2117f4da474SThomas Mingarelli 2127f4da474SThomas Mingarelli cmn_regs.u1.reax = CRU_BIOS_SIGNATURE_VALUE; 2137f4da474SThomas Mingarelli 2147f4da474SThomas Mingarelli asminline_call(&cmn_regs, bios32_entrypoint); 2157f4da474SThomas Mingarelli 2167f4da474SThomas Mingarelli if (cmn_regs.u1.ral != 0) { 2177f4da474SThomas Mingarelli printk(KERN_WARNING 2187f4da474SThomas Mingarelli "hpwdt: Call succeeded but with an error: 0x%x\n", 2197f4da474SThomas Mingarelli cmn_regs.u1.ral); 2207f4da474SThomas Mingarelli } else { 2217f4da474SThomas Mingarelli physical_bios_base = cmn_regs.u2.rebx; 2227f4da474SThomas Mingarelli physical_bios_offset = cmn_regs.u4.redx; 2237f4da474SThomas Mingarelli cru_length = cmn_regs.u3.recx; 2247f4da474SThomas Mingarelli cru_physical_address = 2257f4da474SThomas Mingarelli physical_bios_base + physical_bios_offset; 2267f4da474SThomas Mingarelli 2277f4da474SThomas Mingarelli /* If the values look OK, then map it in. */ 2287f4da474SThomas Mingarelli if ((physical_bios_base + physical_bios_offset)) { 2297f4da474SThomas Mingarelli cru_rom_addr = 2307f4da474SThomas Mingarelli ioremap(cru_physical_address, cru_length); 2317f4da474SThomas Mingarelli if (cru_rom_addr) 2327f4da474SThomas Mingarelli retval = 0; 2337f4da474SThomas Mingarelli } 2347f4da474SThomas Mingarelli 2357f4da474SThomas Mingarelli printk(KERN_DEBUG "hpwdt: CRU Base Address: 0x%lx\n", 2367f4da474SThomas Mingarelli physical_bios_base); 2377f4da474SThomas Mingarelli printk(KERN_DEBUG "hpwdt: CRU Offset Address: 0x%lx\n", 2387f4da474SThomas Mingarelli physical_bios_offset); 2397f4da474SThomas Mingarelli printk(KERN_DEBUG "hpwdt: CRU Length: 0x%lx\n", 2407f4da474SThomas Mingarelli cru_length); 241adb23631SKulikov Vasiliy printk(KERN_DEBUG "hpwdt: CRU Mapped Address: %p\n", 242adb23631SKulikov Vasiliy &cru_rom_addr); 2437f4da474SThomas Mingarelli } 2447f4da474SThomas Mingarelli iounmap(bios32_map); 2457f4da474SThomas Mingarelli return retval; 2467f4da474SThomas Mingarelli } 2477f4da474SThomas Mingarelli 2487f4da474SThomas Mingarelli /* 24930ec910eSRoland Dreier * bios_checksum 25030ec910eSRoland Dreier */ 25130ec910eSRoland Dreier static int __devinit bios_checksum(const char __iomem *ptr, int len) 25230ec910eSRoland Dreier { 25330ec910eSRoland Dreier char sum = 0; 25430ec910eSRoland Dreier int i; 25530ec910eSRoland Dreier 25630ec910eSRoland Dreier /* 25730ec910eSRoland Dreier * calculate checksum of size bytes. This should add up 25830ec910eSRoland Dreier * to zero if we have a valid header. 25930ec910eSRoland Dreier */ 26030ec910eSRoland Dreier for (i = 0; i < len; i++) 26130ec910eSRoland Dreier sum += ptr[i]; 26230ec910eSRoland Dreier 26330ec910eSRoland Dreier return ((sum == 0) && (len > 0)); 26430ec910eSRoland Dreier } 26530ec910eSRoland Dreier 26630ec910eSRoland Dreier /* 2677f4da474SThomas Mingarelli * bios32_present 2687f4da474SThomas Mingarelli * 2697f4da474SThomas Mingarelli * Routine Description: 2707f4da474SThomas Mingarelli * This function finds the 32-bit BIOS Service Directory 2717f4da474SThomas Mingarelli * 2727f4da474SThomas Mingarelli * Return Value: 2737f4da474SThomas Mingarelli * 0 : SUCCESS 2747f4da474SThomas Mingarelli * <0 : FAILURE 2757f4da474SThomas Mingarelli */ 2767f4da474SThomas Mingarelli static int __devinit bios32_present(const char __iomem *p) 2777f4da474SThomas Mingarelli { 2787f4da474SThomas Mingarelli struct bios32_service_dir *bios_32_ptr; 2797f4da474SThomas Mingarelli int length; 2807f4da474SThomas Mingarelli unsigned long map_entry, map_offset; 2817f4da474SThomas Mingarelli 2827f4da474SThomas Mingarelli bios_32_ptr = (struct bios32_service_dir *) p; 2837f4da474SThomas Mingarelli 2847f4da474SThomas Mingarelli /* 2857f4da474SThomas Mingarelli * Search for signature by checking equal to the swizzled value 2867f4da474SThomas Mingarelli * instead of calling another routine to perform a strcmp. 2877f4da474SThomas Mingarelli */ 2887f4da474SThomas Mingarelli if (bios_32_ptr->signature == PCI_BIOS32_SD_VALUE) { 2897f4da474SThomas Mingarelli length = bios_32_ptr->length * PCI_BIOS32_PARAGRAPH_LEN; 2907f4da474SThomas Mingarelli if (bios_checksum(p, length)) { 2917f4da474SThomas Mingarelli /* 2927f4da474SThomas Mingarelli * According to the spec, we're looking for the 2937f4da474SThomas Mingarelli * first 4KB-aligned address below the entrypoint 2947f4da474SThomas Mingarelli * listed in the header. The Service Directory code 2957f4da474SThomas Mingarelli * is guaranteed to occupy no more than 2 4KB pages. 2967f4da474SThomas Mingarelli */ 2977f4da474SThomas Mingarelli map_entry = bios_32_ptr->entry_point & ~(PAGE_SIZE - 1); 2987f4da474SThomas Mingarelli map_offset = bios_32_ptr->entry_point - map_entry; 2997f4da474SThomas Mingarelli 3007f4da474SThomas Mingarelli return cru_detect(map_entry, map_offset); 3017f4da474SThomas Mingarelli } 3027f4da474SThomas Mingarelli } 3037f4da474SThomas Mingarelli return -ENODEV; 3047f4da474SThomas Mingarelli } 3057f4da474SThomas Mingarelli 3067f4da474SThomas Mingarelli static int __devinit detect_cru_service(void) 3077f4da474SThomas Mingarelli { 3087f4da474SThomas Mingarelli char __iomem *p, *q; 3097f4da474SThomas Mingarelli int rc = -1; 3107f4da474SThomas Mingarelli 3117f4da474SThomas Mingarelli /* 3127f4da474SThomas Mingarelli * Search from 0x0f0000 through 0x0fffff, inclusive. 3137f4da474SThomas Mingarelli */ 3147f4da474SThomas Mingarelli p = ioremap(PCI_ROM_BASE1, ROM_SIZE); 3157f4da474SThomas Mingarelli if (p == NULL) 3167f4da474SThomas Mingarelli return -ENOMEM; 3177f4da474SThomas Mingarelli 3187f4da474SThomas Mingarelli for (q = p; q < p + ROM_SIZE; q += 16) { 3197f4da474SThomas Mingarelli rc = bios32_present(q); 3207f4da474SThomas Mingarelli if (!rc) 3217f4da474SThomas Mingarelli break; 3227f4da474SThomas Mingarelli } 3237f4da474SThomas Mingarelli iounmap(p); 3247f4da474SThomas Mingarelli return rc; 3257f4da474SThomas Mingarelli } 3266b7f3d53Sdann frazier /* ------------------------------------------------------------------------- */ 3276b7f3d53Sdann frazier #endif /* CONFIG_X86_32 */ 3286b7f3d53Sdann frazier #ifdef CONFIG_X86_64 3297f4da474SThomas Mingarelli /* --64 Bit Bios------------------------------------------------------------ */ 3307f4da474SThomas Mingarelli 3317f4da474SThomas Mingarelli #define HPWDT_ARCH 64 3327f4da474SThomas Mingarelli 3331f6ef234SLinus Torvalds asm(".text \n\t" 3341f6ef234SLinus Torvalds ".align 4 \n" 3351f6ef234SLinus Torvalds "asminline_call: \n\t" 3361f6ef234SLinus Torvalds "pushq %rbp \n\t" 3377f4da474SThomas Mingarelli "movq %rsp, %rbp \n\t" 3387f4da474SThomas Mingarelli "pushq %rax \n\t" 3397f4da474SThomas Mingarelli "pushq %rbx \n\t" 3407f4da474SThomas Mingarelli "pushq %rdx \n\t" 3417f4da474SThomas Mingarelli "pushq %r12 \n\t" 3427f4da474SThomas Mingarelli "pushq %r9 \n\t" 3437f4da474SThomas Mingarelli "movq %rsi, %r12 \n\t" 3447f4da474SThomas Mingarelli "movq %rdi, %r9 \n\t" 3457f4da474SThomas Mingarelli "movl 4(%r9),%ebx \n\t" 3467f4da474SThomas Mingarelli "movl 8(%r9),%ecx \n\t" 3477f4da474SThomas Mingarelli "movl 12(%r9),%edx \n\t" 3487f4da474SThomas Mingarelli "movl 16(%r9),%esi \n\t" 3497f4da474SThomas Mingarelli "movl 20(%r9),%edi \n\t" 3507f4da474SThomas Mingarelli "movl (%r9),%eax \n\t" 3517f4da474SThomas Mingarelli "call *%r12 \n\t" 3527f4da474SThomas Mingarelli "pushfq \n\t" 3537f4da474SThomas Mingarelli "popq %r12 \n\t" 3547f4da474SThomas Mingarelli "movl %eax, (%r9) \n\t" 3557f4da474SThomas Mingarelli "movl %ebx, 4(%r9) \n\t" 3567f4da474SThomas Mingarelli "movl %ecx, 8(%r9) \n\t" 3577f4da474SThomas Mingarelli "movl %edx, 12(%r9) \n\t" 3587f4da474SThomas Mingarelli "movl %esi, 16(%r9) \n\t" 3597f4da474SThomas Mingarelli "movl %edi, 20(%r9) \n\t" 3607f4da474SThomas Mingarelli "movq %r12, %rax \n\t" 3617f4da474SThomas Mingarelli "movl %eax, 28(%r9) \n\t" 3627f4da474SThomas Mingarelli "popq %r9 \n\t" 3637f4da474SThomas Mingarelli "popq %r12 \n\t" 3647f4da474SThomas Mingarelli "popq %rdx \n\t" 3657f4da474SThomas Mingarelli "popq %rbx \n\t" 3667f4da474SThomas Mingarelli "popq %rax \n\t" 3671f6ef234SLinus Torvalds "leave \n\t" 3681f6ef234SLinus Torvalds "ret \n\t" 3691f6ef234SLinus Torvalds ".previous"); 3707f4da474SThomas Mingarelli 3717f4da474SThomas Mingarelli /* 3727f4da474SThomas Mingarelli * dmi_find_cru 3737f4da474SThomas Mingarelli * 3747f4da474SThomas Mingarelli * Routine Description: 37530ec910eSRoland Dreier * This function checks whether or not a SMBIOS/DMI record is 3767f4da474SThomas Mingarelli * the 64bit CRU info or not 3777f4da474SThomas Mingarelli */ 378e7a19c56SJean Delvare static void __devinit dmi_find_cru(const struct dmi_header *dm, void *dummy) 3797f4da474SThomas Mingarelli { 3807f4da474SThomas Mingarelli struct smbios_cru64_info *smbios_cru64_ptr; 3817f4da474SThomas Mingarelli unsigned long cru_physical_address; 3827f4da474SThomas Mingarelli 3837f4da474SThomas Mingarelli if (dm->type == SMBIOS_CRU64_INFORMATION) { 3847f4da474SThomas Mingarelli smbios_cru64_ptr = (struct smbios_cru64_info *) dm; 3857f4da474SThomas Mingarelli if (smbios_cru64_ptr->signature == CRU_BIOS_SIGNATURE_VALUE) { 3867f4da474SThomas Mingarelli cru_physical_address = 3877f4da474SThomas Mingarelli smbios_cru64_ptr->physical_address + 3887f4da474SThomas Mingarelli smbios_cru64_ptr->double_offset; 3897f4da474SThomas Mingarelli cru_rom_addr = ioremap(cru_physical_address, 3907f4da474SThomas Mingarelli smbios_cru64_ptr->double_length); 39106026413SBernhard Walle set_memory_x((unsigned long)cru_rom_addr & PAGE_MASK, 39206026413SBernhard Walle smbios_cru64_ptr->double_length >> PAGE_SHIFT); 3937f4da474SThomas Mingarelli } 3947f4da474SThomas Mingarelli } 3957f4da474SThomas Mingarelli } 3967f4da474SThomas Mingarelli 3977f4da474SThomas Mingarelli static int __devinit detect_cru_service(void) 3987f4da474SThomas Mingarelli { 3997f4da474SThomas Mingarelli cru_rom_addr = NULL; 4007f4da474SThomas Mingarelli 401e7a19c56SJean Delvare dmi_walk(dmi_find_cru, NULL); 4027f4da474SThomas Mingarelli 4037f4da474SThomas Mingarelli /* if cru_rom_addr has been set then we found a CRU service */ 4047f4da474SThomas Mingarelli return ((cru_rom_addr != NULL) ? 0 : -ENODEV); 4057f4da474SThomas Mingarelli } 4067f4da474SThomas Mingarelli /* ------------------------------------------------------------------------- */ 4076b7f3d53Sdann frazier #endif /* CONFIG_X86_64 */ 4087f4da474SThomas Mingarelli 4097f4da474SThomas Mingarelli /* 4107f4da474SThomas Mingarelli * Watchdog operations 4117f4da474SThomas Mingarelli */ 4127f4da474SThomas Mingarelli static void hpwdt_start(void) 4137f4da474SThomas Mingarelli { 414*e802e32dSdann frazier reload = SECS_TO_TICKS(soft_margin); 4157f4da474SThomas Mingarelli iowrite16(reload, hpwdt_timer_reg); 4167f4da474SThomas Mingarelli iowrite16(0x85, hpwdt_timer_con); 4177f4da474SThomas Mingarelli } 4187f4da474SThomas Mingarelli 4197f4da474SThomas Mingarelli static void hpwdt_stop(void) 4207f4da474SThomas Mingarelli { 4217f4da474SThomas Mingarelli unsigned long data; 4227f4da474SThomas Mingarelli 4237f4da474SThomas Mingarelli data = ioread16(hpwdt_timer_con); 4247f4da474SThomas Mingarelli data &= 0xFE; 4257f4da474SThomas Mingarelli iowrite16(data, hpwdt_timer_con); 4267f4da474SThomas Mingarelli } 4277f4da474SThomas Mingarelli 4287f4da474SThomas Mingarelli static void hpwdt_ping(void) 4297f4da474SThomas Mingarelli { 4307f4da474SThomas Mingarelli iowrite16(reload, hpwdt_timer_reg); 4317f4da474SThomas Mingarelli } 4327f4da474SThomas Mingarelli 4337f4da474SThomas Mingarelli static int hpwdt_change_timer(int new_margin) 4347f4da474SThomas Mingarelli { 4357f4da474SThomas Mingarelli /* Arbitrary, can't find the card's limits */ 4368ba42bd8SThomas Mingarelli if (new_margin < 5 || new_margin > 600) { 4377f4da474SThomas Mingarelli printk(KERN_WARNING 4387f4da474SThomas Mingarelli "hpwdt: New value passed in is invalid: %d seconds.\n", 4397f4da474SThomas Mingarelli new_margin); 4407f4da474SThomas Mingarelli return -EINVAL; 4417f4da474SThomas Mingarelli } 4427f4da474SThomas Mingarelli 4437f4da474SThomas Mingarelli soft_margin = new_margin; 4447f4da474SThomas Mingarelli printk(KERN_DEBUG 4457f4da474SThomas Mingarelli "hpwdt: New timer passed in is %d seconds.\n", 4467f4da474SThomas Mingarelli new_margin); 447*e802e32dSdann frazier reload = SECS_TO_TICKS(soft_margin); 4487f4da474SThomas Mingarelli 4497f4da474SThomas Mingarelli return 0; 4507f4da474SThomas Mingarelli } 4517f4da474SThomas Mingarelli 4527f4da474SThomas Mingarelli /* 453ab4ba3cdSThomas Mingarelli * NMI Handler 454ab4ba3cdSThomas Mingarelli */ 455ab4ba3cdSThomas Mingarelli static int hpwdt_pretimeout(struct notifier_block *nb, unsigned long ulReason, 456ab4ba3cdSThomas Mingarelli void *data) 457ab4ba3cdSThomas Mingarelli { 458ab4ba3cdSThomas Mingarelli unsigned long rom_pl; 459ab4ba3cdSThomas Mingarelli static int die_nmi_called; 460ab4ba3cdSThomas Mingarelli 461ab4ba3cdSThomas Mingarelli if (ulReason != DIE_NMI && ulReason != DIE_NMI_IPI) 462ab4ba3cdSThomas Mingarelli return NOTIFY_OK; 463ab4ba3cdSThomas Mingarelli 46447bece87SThomas Mingarelli if (hpwdt_nmi_sourcing) { 465ab4ba3cdSThomas Mingarelli spin_lock_irqsave(&rom_lock, rom_pl); 466ab4ba3cdSThomas Mingarelli if (!die_nmi_called) 467ab4ba3cdSThomas Mingarelli asminline_call(&cmn_regs, cru_rom_addr); 468ab4ba3cdSThomas Mingarelli die_nmi_called = 1; 469ab4ba3cdSThomas Mingarelli spin_unlock_irqrestore(&rom_lock, rom_pl); 470ab4ba3cdSThomas Mingarelli if (cmn_regs.u1.ral == 0) { 471ab4ba3cdSThomas Mingarelli printk(KERN_WARNING "hpwdt: An NMI occurred, " 472ab4ba3cdSThomas Mingarelli "but unable to determine source.\n"); 473ab4ba3cdSThomas Mingarelli } else { 474ab4ba3cdSThomas Mingarelli if (allow_kdump) 475ab4ba3cdSThomas Mingarelli hpwdt_stop(); 476ab4ba3cdSThomas Mingarelli panic("An NMI occurred, please see the Integrated " 477ab4ba3cdSThomas Mingarelli "Management Log for details.\n"); 478ab4ba3cdSThomas Mingarelli } 47947bece87SThomas Mingarelli } 480290172e7SBernhard Walle return NOTIFY_OK; 481ab4ba3cdSThomas Mingarelli } 482ab4ba3cdSThomas Mingarelli 483ab4ba3cdSThomas Mingarelli /* 4847f4da474SThomas Mingarelli * /dev/watchdog handling 4857f4da474SThomas Mingarelli */ 4867f4da474SThomas Mingarelli static int hpwdt_open(struct inode *inode, struct file *file) 4877f4da474SThomas Mingarelli { 4887f4da474SThomas Mingarelli /* /dev/watchdog can only be opened once */ 4897f4da474SThomas Mingarelli if (test_and_set_bit(0, &hpwdt_is_open)) 4907f4da474SThomas Mingarelli return -EBUSY; 4917f4da474SThomas Mingarelli 4927f4da474SThomas Mingarelli /* Start the watchdog */ 4937f4da474SThomas Mingarelli hpwdt_start(); 4947f4da474SThomas Mingarelli hpwdt_ping(); 4957f4da474SThomas Mingarelli 4967f4da474SThomas Mingarelli return nonseekable_open(inode, file); 4977f4da474SThomas Mingarelli } 4987f4da474SThomas Mingarelli 4997f4da474SThomas Mingarelli static int hpwdt_release(struct inode *inode, struct file *file) 5007f4da474SThomas Mingarelli { 5017f4da474SThomas Mingarelli /* Stop the watchdog */ 5027f4da474SThomas Mingarelli if (expect_release == 42) { 5037f4da474SThomas Mingarelli hpwdt_stop(); 5047f4da474SThomas Mingarelli } else { 5057f4da474SThomas Mingarelli printk(KERN_CRIT 5067f4da474SThomas Mingarelli "hpwdt: Unexpected close, not stopping watchdog!\n"); 5077f4da474SThomas Mingarelli hpwdt_ping(); 5087f4da474SThomas Mingarelli } 5097f4da474SThomas Mingarelli 5107f4da474SThomas Mingarelli expect_release = 0; 5117f4da474SThomas Mingarelli 5127f4da474SThomas Mingarelli /* /dev/watchdog is being closed, make sure it can be re-opened */ 5137f4da474SThomas Mingarelli clear_bit(0, &hpwdt_is_open); 5147f4da474SThomas Mingarelli 5157f4da474SThomas Mingarelli return 0; 5167f4da474SThomas Mingarelli } 5177f4da474SThomas Mingarelli 5187f4da474SThomas Mingarelli static ssize_t hpwdt_write(struct file *file, const char __user *data, 5197f4da474SThomas Mingarelli size_t len, loff_t *ppos) 5207f4da474SThomas Mingarelli { 5217f4da474SThomas Mingarelli /* See if we got the magic character 'V' and reload the timer */ 5227f4da474SThomas Mingarelli if (len) { 5237f4da474SThomas Mingarelli if (!nowayout) { 5247f4da474SThomas Mingarelli size_t i; 5257f4da474SThomas Mingarelli 5267f4da474SThomas Mingarelli /* note: just in case someone wrote the magic character 5277f4da474SThomas Mingarelli * five months ago... */ 5287f4da474SThomas Mingarelli expect_release = 0; 5297f4da474SThomas Mingarelli 5307f4da474SThomas Mingarelli /* scan to see whether or not we got the magic char. */ 5317f4da474SThomas Mingarelli for (i = 0; i != len; i++) { 5327f4da474SThomas Mingarelli char c; 5337f4da474SThomas Mingarelli if (get_user(c, data + i)) 5347f4da474SThomas Mingarelli return -EFAULT; 5357f4da474SThomas Mingarelli if (c == 'V') 5367f4da474SThomas Mingarelli expect_release = 42; 5377f4da474SThomas Mingarelli } 5387f4da474SThomas Mingarelli } 5397f4da474SThomas Mingarelli 5407f4da474SThomas Mingarelli /* someone wrote to us, we should reload the timer */ 5417f4da474SThomas Mingarelli hpwdt_ping(); 5427f4da474SThomas Mingarelli } 5437f4da474SThomas Mingarelli 5447f4da474SThomas Mingarelli return len; 5457f4da474SThomas Mingarelli } 5467f4da474SThomas Mingarelli 54742747d71SWim Van Sebroeck static const struct watchdog_info ident = { 5487f4da474SThomas Mingarelli .options = WDIOF_SETTIMEOUT | 5497f4da474SThomas Mingarelli WDIOF_KEEPALIVEPING | 5507f4da474SThomas Mingarelli WDIOF_MAGICCLOSE, 55136e3ff44Sdann frazier .identity = "HP iLO2+ HW Watchdog Timer", 5527f4da474SThomas Mingarelli }; 5537f4da474SThomas Mingarelli 5547f4da474SThomas Mingarelli static long hpwdt_ioctl(struct file *file, unsigned int cmd, 5557f4da474SThomas Mingarelli unsigned long arg) 5567f4da474SThomas Mingarelli { 5577f4da474SThomas Mingarelli void __user *argp = (void __user *)arg; 5587f4da474SThomas Mingarelli int __user *p = argp; 5597f4da474SThomas Mingarelli int new_margin; 5607f4da474SThomas Mingarelli int ret = -ENOTTY; 5617f4da474SThomas Mingarelli 5627f4da474SThomas Mingarelli switch (cmd) { 5637f4da474SThomas Mingarelli case WDIOC_GETSUPPORT: 5647f4da474SThomas Mingarelli ret = 0; 5657f4da474SThomas Mingarelli if (copy_to_user(argp, &ident, sizeof(ident))) 5667f4da474SThomas Mingarelli ret = -EFAULT; 5677f4da474SThomas Mingarelli break; 5687f4da474SThomas Mingarelli 5697f4da474SThomas Mingarelli case WDIOC_GETSTATUS: 5707f4da474SThomas Mingarelli case WDIOC_GETBOOTSTATUS: 5717f4da474SThomas Mingarelli ret = put_user(0, p); 5727f4da474SThomas Mingarelli break; 5737f4da474SThomas Mingarelli 5747f4da474SThomas Mingarelli case WDIOC_KEEPALIVE: 5757f4da474SThomas Mingarelli hpwdt_ping(); 5767f4da474SThomas Mingarelli ret = 0; 5777f4da474SThomas Mingarelli break; 5787f4da474SThomas Mingarelli 5797f4da474SThomas Mingarelli case WDIOC_SETTIMEOUT: 5807f4da474SThomas Mingarelli ret = get_user(new_margin, p); 5817f4da474SThomas Mingarelli if (ret) 5827f4da474SThomas Mingarelli break; 5837f4da474SThomas Mingarelli 5847f4da474SThomas Mingarelli ret = hpwdt_change_timer(new_margin); 5857f4da474SThomas Mingarelli if (ret) 5867f4da474SThomas Mingarelli break; 5877f4da474SThomas Mingarelli 5887f4da474SThomas Mingarelli hpwdt_ping(); 5897f4da474SThomas Mingarelli /* Fall */ 5907f4da474SThomas Mingarelli case WDIOC_GETTIMEOUT: 5917f4da474SThomas Mingarelli ret = put_user(soft_margin, p); 5927f4da474SThomas Mingarelli break; 5937f4da474SThomas Mingarelli } 5947f4da474SThomas Mingarelli return ret; 5957f4da474SThomas Mingarelli } 5967f4da474SThomas Mingarelli 5977f4da474SThomas Mingarelli /* 5987f4da474SThomas Mingarelli * Kernel interfaces 5997f4da474SThomas Mingarelli */ 600d5c26a59SWim Van Sebroeck static const struct file_operations hpwdt_fops = { 6017f4da474SThomas Mingarelli .owner = THIS_MODULE, 6027f4da474SThomas Mingarelli .llseek = no_llseek, 6037f4da474SThomas Mingarelli .write = hpwdt_write, 6047f4da474SThomas Mingarelli .unlocked_ioctl = hpwdt_ioctl, 6057f4da474SThomas Mingarelli .open = hpwdt_open, 6067f4da474SThomas Mingarelli .release = hpwdt_release, 6077f4da474SThomas Mingarelli }; 6087f4da474SThomas Mingarelli 6097f4da474SThomas Mingarelli static struct miscdevice hpwdt_miscdev = { 6107f4da474SThomas Mingarelli .minor = WATCHDOG_MINOR, 6117f4da474SThomas Mingarelli .name = "watchdog", 6127f4da474SThomas Mingarelli .fops = &hpwdt_fops, 6137f4da474SThomas Mingarelli }; 6147f4da474SThomas Mingarelli 6157f4da474SThomas Mingarelli static struct notifier_block die_notifier = { 6167f4da474SThomas Mingarelli .notifier_call = hpwdt_pretimeout, 61744df7535STom Mingarelli .priority = 0, 6187f4da474SThomas Mingarelli }; 6197f4da474SThomas Mingarelli 6207f4da474SThomas Mingarelli /* 6217f4da474SThomas Mingarelli * Init & Exit 6227f4da474SThomas Mingarelli */ 6237f4da474SThomas Mingarelli 62447bece87SThomas Mingarelli #ifdef ARCH_HAS_NMI_WATCHDOG 62547bece87SThomas Mingarelli static void __devinit hpwdt_check_nmi_sourcing(struct pci_dev *dev) 62647bece87SThomas Mingarelli { 62747bece87SThomas Mingarelli /* 62847bece87SThomas Mingarelli * If nmi_watchdog is turned off then we can turn on 62947bece87SThomas Mingarelli * our nmi sourcing capability. 63047bece87SThomas Mingarelli */ 63147bece87SThomas Mingarelli if (!nmi_watchdog_active()) 63247bece87SThomas Mingarelli hpwdt_nmi_sourcing = 1; 63347bece87SThomas Mingarelli else 63447bece87SThomas Mingarelli dev_warn(&dev->dev, "NMI sourcing is disabled. To enable this " 63544df7535STom Mingarelli "functionality you must reboot with nmi_watchdog=0 " 63644df7535STom Mingarelli "and load the hpwdt driver with priority=1.\n"); 63747bece87SThomas Mingarelli } 63847bece87SThomas Mingarelli #else 63947bece87SThomas Mingarelli static void __devinit hpwdt_check_nmi_sourcing(struct pci_dev *dev) 64047bece87SThomas Mingarelli { 64147bece87SThomas Mingarelli dev_warn(&dev->dev, "NMI sourcing is disabled. " 64247bece87SThomas Mingarelli "Your kernel does not support a NMI Watchdog.\n"); 64347bece87SThomas Mingarelli } 64447bece87SThomas Mingarelli #endif 64547bece87SThomas Mingarelli 6467f4da474SThomas Mingarelli static int __devinit hpwdt_init_one(struct pci_dev *dev, 6477f4da474SThomas Mingarelli const struct pci_device_id *ent) 6487f4da474SThomas Mingarelli { 6497f4da474SThomas Mingarelli int retval; 6507f4da474SThomas Mingarelli 6517f4da474SThomas Mingarelli /* 65247bece87SThomas Mingarelli * Check if we can do NMI sourcing or not 65347bece87SThomas Mingarelli */ 65447bece87SThomas Mingarelli hpwdt_check_nmi_sourcing(dev); 65547bece87SThomas Mingarelli 65647bece87SThomas Mingarelli /* 65736e3ff44Sdann frazier * First let's find out if we are on an iLO2+ server. We will 6587f4da474SThomas Mingarelli * not run on a legacy ASM box. 659ab4ba3cdSThomas Mingarelli * So we only support the G5 ProLiant servers and higher. 6607f4da474SThomas Mingarelli */ 6617f4da474SThomas Mingarelli if (dev->subsystem_vendor != PCI_VENDOR_ID_HP) { 6627f4da474SThomas Mingarelli dev_warn(&dev->dev, 66336e3ff44Sdann frazier "This server does not have an iLO2+ ASIC.\n"); 6647f4da474SThomas Mingarelli return -ENODEV; 6657f4da474SThomas Mingarelli } 6667f4da474SThomas Mingarelli 6677f4da474SThomas Mingarelli if (pci_enable_device(dev)) { 6687f4da474SThomas Mingarelli dev_warn(&dev->dev, 6697f4da474SThomas Mingarelli "Not possible to enable PCI Device: 0x%x:0x%x.\n", 6707f4da474SThomas Mingarelli ent->vendor, ent->device); 6717f4da474SThomas Mingarelli return -ENODEV; 6727f4da474SThomas Mingarelli } 6737f4da474SThomas Mingarelli 6747f4da474SThomas Mingarelli pci_mem_addr = pci_iomap(dev, 1, 0x80); 6757f4da474SThomas Mingarelli if (!pci_mem_addr) { 6767f4da474SThomas Mingarelli dev_warn(&dev->dev, 67736e3ff44Sdann frazier "Unable to detect the iLO2+ server memory.\n"); 6787f4da474SThomas Mingarelli retval = -ENOMEM; 6797f4da474SThomas Mingarelli goto error_pci_iomap; 6807f4da474SThomas Mingarelli } 6817f4da474SThomas Mingarelli hpwdt_timer_reg = pci_mem_addr + 0x70; 6827f4da474SThomas Mingarelli hpwdt_timer_con = pci_mem_addr + 0x72; 6837f4da474SThomas Mingarelli 6847f4da474SThomas Mingarelli /* Make sure that we have a valid soft_margin */ 6857f4da474SThomas Mingarelli if (hpwdt_change_timer(soft_margin)) 6867f4da474SThomas Mingarelli hpwdt_change_timer(DEFAULT_MARGIN); 6877f4da474SThomas Mingarelli 6887f4da474SThomas Mingarelli /* 6897f4da474SThomas Mingarelli * We need to map the ROM to get the CRU service. 6907f4da474SThomas Mingarelli * For 32 bit Operating Systems we need to go through the 32 Bit 6917f4da474SThomas Mingarelli * BIOS Service Directory 6927f4da474SThomas Mingarelli * For 64 bit Operating Systems we get that service through SMBIOS. 6937f4da474SThomas Mingarelli */ 6947f4da474SThomas Mingarelli retval = detect_cru_service(); 6957f4da474SThomas Mingarelli if (retval < 0) { 6967f4da474SThomas Mingarelli dev_warn(&dev->dev, 6977f4da474SThomas Mingarelli "Unable to detect the %d Bit CRU Service.\n", 6987f4da474SThomas Mingarelli HPWDT_ARCH); 6997f4da474SThomas Mingarelli goto error_get_cru; 7007f4da474SThomas Mingarelli } 7017f4da474SThomas Mingarelli 7027f4da474SThomas Mingarelli /* 7037f4da474SThomas Mingarelli * We know this is the only CRU call we need to make so lets keep as 7047f4da474SThomas Mingarelli * few instructions as possible once the NMI comes in. 7057f4da474SThomas Mingarelli */ 7067f4da474SThomas Mingarelli cmn_regs.u1.rah = 0x0D; 7077f4da474SThomas Mingarelli cmn_regs.u1.ral = 0x02; 7087f4da474SThomas Mingarelli 70944df7535STom Mingarelli /* 71044df7535STom Mingarelli * If the priority is set to 1, then we will be put first on the 71144df7535STom Mingarelli * die notify list to handle a critical NMI. The default is to 71244df7535STom Mingarelli * be last so other users of the NMI signal can function. 71344df7535STom Mingarelli */ 71444df7535STom Mingarelli if (priority) 71544df7535STom Mingarelli die_notifier.priority = 0x7FFFFFFF; 71644df7535STom Mingarelli 7177f4da474SThomas Mingarelli retval = register_die_notifier(&die_notifier); 7187f4da474SThomas Mingarelli if (retval != 0) { 7197f4da474SThomas Mingarelli dev_warn(&dev->dev, 7207f4da474SThomas Mingarelli "Unable to register a die notifier (err=%d).\n", 7217f4da474SThomas Mingarelli retval); 7227f4da474SThomas Mingarelli goto error_die_notifier; 7237f4da474SThomas Mingarelli } 7247f4da474SThomas Mingarelli 7257f4da474SThomas Mingarelli retval = misc_register(&hpwdt_miscdev); 7267f4da474SThomas Mingarelli if (retval < 0) { 7277f4da474SThomas Mingarelli dev_warn(&dev->dev, 7287f4da474SThomas Mingarelli "Unable to register miscdev on minor=%d (err=%d).\n", 7297f4da474SThomas Mingarelli WATCHDOG_MINOR, retval); 7307f4da474SThomas Mingarelli goto error_misc_register; 7317f4da474SThomas Mingarelli } 7327f4da474SThomas Mingarelli 7337f4da474SThomas Mingarelli printk(KERN_INFO 734d8100c3aSThomas Mingarelli "hp Watchdog Timer Driver: %s" 735ab4ba3cdSThomas Mingarelli ", timer margin: %d seconds (nowayout=%d)" 73644df7535STom Mingarelli ", allow kernel dump: %s (default = 0/OFF)" 73744df7535STom Mingarelli ", priority: %s (default = 0/LAST).\n", 738d8100c3aSThomas Mingarelli HPWDT_VERSION, soft_margin, nowayout, 73944df7535STom Mingarelli (allow_kdump == 0) ? "OFF" : "ON", 74044df7535STom Mingarelli (priority == 0) ? "LAST" : "FIRST"); 7417f4da474SThomas Mingarelli 7427f4da474SThomas Mingarelli return 0; 7437f4da474SThomas Mingarelli 7447f4da474SThomas Mingarelli error_misc_register: 7457f4da474SThomas Mingarelli unregister_die_notifier(&die_notifier); 7467f4da474SThomas Mingarelli error_die_notifier: 7477f4da474SThomas Mingarelli if (cru_rom_addr) 7487f4da474SThomas Mingarelli iounmap(cru_rom_addr); 7497f4da474SThomas Mingarelli error_get_cru: 7507f4da474SThomas Mingarelli pci_iounmap(dev, pci_mem_addr); 7517f4da474SThomas Mingarelli error_pci_iomap: 7527f4da474SThomas Mingarelli pci_disable_device(dev); 7537f4da474SThomas Mingarelli return retval; 7547f4da474SThomas Mingarelli } 7557f4da474SThomas Mingarelli 7567f4da474SThomas Mingarelli static void __devexit hpwdt_exit(struct pci_dev *dev) 7577f4da474SThomas Mingarelli { 7587f4da474SThomas Mingarelli if (!nowayout) 7597f4da474SThomas Mingarelli hpwdt_stop(); 7607f4da474SThomas Mingarelli 7617f4da474SThomas Mingarelli misc_deregister(&hpwdt_miscdev); 7627f4da474SThomas Mingarelli unregister_die_notifier(&die_notifier); 7637f4da474SThomas Mingarelli 7647f4da474SThomas Mingarelli if (cru_rom_addr) 7657f4da474SThomas Mingarelli iounmap(cru_rom_addr); 7667f4da474SThomas Mingarelli pci_iounmap(dev, pci_mem_addr); 7677f4da474SThomas Mingarelli pci_disable_device(dev); 7687f4da474SThomas Mingarelli } 7697f4da474SThomas Mingarelli 7707f4da474SThomas Mingarelli static struct pci_driver hpwdt_driver = { 7717f4da474SThomas Mingarelli .name = "hpwdt", 7727f4da474SThomas Mingarelli .id_table = hpwdt_devices, 7737f4da474SThomas Mingarelli .probe = hpwdt_init_one, 7747f4da474SThomas Mingarelli .remove = __devexit_p(hpwdt_exit), 7757f4da474SThomas Mingarelli }; 7767f4da474SThomas Mingarelli 7777f4da474SThomas Mingarelli static void __exit hpwdt_cleanup(void) 7787f4da474SThomas Mingarelli { 7797f4da474SThomas Mingarelli pci_unregister_driver(&hpwdt_driver); 7807f4da474SThomas Mingarelli } 7817f4da474SThomas Mingarelli 7827f4da474SThomas Mingarelli static int __init hpwdt_init(void) 7837f4da474SThomas Mingarelli { 7847f4da474SThomas Mingarelli return pci_register_driver(&hpwdt_driver); 7857f4da474SThomas Mingarelli } 7867f4da474SThomas Mingarelli 7877f4da474SThomas Mingarelli MODULE_AUTHOR("Tom Mingarelli"); 7887f4da474SThomas Mingarelli MODULE_DESCRIPTION("hp watchdog driver"); 7897f4da474SThomas Mingarelli MODULE_LICENSE("GPL"); 790d8100c3aSThomas Mingarelli MODULE_VERSION(HPWDT_VERSION); 7917f4da474SThomas Mingarelli MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 7927f4da474SThomas Mingarelli 7937f4da474SThomas Mingarelli module_param(soft_margin, int, 0); 7947f4da474SThomas Mingarelli MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds"); 7957f4da474SThomas Mingarelli 7967f4da474SThomas Mingarelli module_param(nowayout, int, 0); 7977f4da474SThomas Mingarelli MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 7987f4da474SThomas Mingarelli __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 7997f4da474SThomas Mingarelli 800550d299eSdann frazier module_param(allow_kdump, int, 0); 801550d299eSdann frazier MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs"); 802550d299eSdann frazier 80344df7535STom Mingarelli module_param(priority, int, 0); 80444df7535STom Mingarelli MODULE_PARM_DESC(priority, "The hpwdt driver handles NMIs first or last" 80544df7535STom Mingarelli " (default = 0/Last)\n"); 80644df7535STom Mingarelli 8077f4da474SThomas Mingarelli module_init(hpwdt_init); 8087f4da474SThomas Mingarelli module_exit(hpwdt_cleanup); 809