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 1627c766aaSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1727c766aaSJoe Perches 187f4da474SThomas Mingarelli #include <linux/device.h> 197f4da474SThomas Mingarelli #include <linux/fs.h> 207f4da474SThomas Mingarelli #include <linux/io.h> 21a52e6d18Sdann frazier #include <linux/bitops.h> 227f4da474SThomas Mingarelli #include <linux/kernel.h> 237f4da474SThomas Mingarelli #include <linux/miscdevice.h> 247f4da474SThomas Mingarelli #include <linux/module.h> 257f4da474SThomas Mingarelli #include <linux/moduleparam.h> 267f4da474SThomas Mingarelli #include <linux/pci.h> 277f4da474SThomas Mingarelli #include <linux/pci_ids.h> 287f4da474SThomas Mingarelli #include <linux/types.h> 297f4da474SThomas Mingarelli #include <linux/uaccess.h> 307f4da474SThomas Mingarelli #include <linux/watchdog.h> 3186ded1f3Sdann frazier #ifdef CONFIG_HPWDT_NMI_DECODING 327f4da474SThomas Mingarelli #include <linux/dmi.h> 33a52e6d18Sdann frazier #include <linux/spinlock.h> 34923410d0Sdann frazier #include <linux/nmi.h> 35923410d0Sdann frazier #include <linux/kdebug.h> 36923410d0Sdann frazier #include <linux/notifier.h> 3706026413SBernhard Walle #include <asm/cacheflush.h> 3886ded1f3Sdann frazier #endif /* CONFIG_HPWDT_NMI_DECODING */ 39d48b0e17SIngo Molnar #include <asm/nmi.h> 407f4da474SThomas Mingarelli 417bb5be94SThomas Mingarelli #define HPWDT_VERSION "1.3.3" 42e802e32dSdann frazier #define SECS_TO_TICKS(secs) ((secs) * 1000 / 128) 436f681c2eSdann frazier #define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000) 446f681c2eSdann frazier #define HPWDT_MAX_TIMER TICKS_TO_SECS(65535) 45923410d0Sdann frazier #define DEFAULT_MARGIN 30 46923410d0Sdann frazier 47923410d0Sdann frazier static unsigned int soft_margin = DEFAULT_MARGIN; /* in seconds */ 48923410d0Sdann frazier static unsigned int reload; /* the computed soft_margin */ 4986a1e189SWim Van Sebroeck static bool nowayout = WATCHDOG_NOWAYOUT; 50923410d0Sdann frazier static char expect_release; 51923410d0Sdann frazier static unsigned long hpwdt_is_open; 52923410d0Sdann frazier 53923410d0Sdann frazier static void __iomem *pci_mem_addr; /* the PCI-memory address */ 54923410d0Sdann frazier static unsigned long __iomem *hpwdt_timer_reg; 55923410d0Sdann frazier static unsigned long __iomem *hpwdt_timer_con; 56923410d0Sdann frazier 57bc17f9dcSJingoo Han static const struct pci_device_id hpwdt_devices[] = { 5836e3ff44Sdann frazier { PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB203) }, /* iLO2 */ 5936e3ff44Sdann frazier { PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3306) }, /* iLO3 */ 60923410d0Sdann frazier {0}, /* terminate list */ 61923410d0Sdann frazier }; 62923410d0Sdann frazier MODULE_DEVICE_TABLE(pci, hpwdt_devices); 63923410d0Sdann frazier 6486ded1f3Sdann frazier #ifdef CONFIG_HPWDT_NMI_DECODING 657f4da474SThomas Mingarelli #define PCI_BIOS32_SD_VALUE 0x5F32335F /* "_32_" */ 667f4da474SThomas Mingarelli #define CRU_BIOS_SIGNATURE_VALUE 0x55524324 677f4da474SThomas Mingarelli #define PCI_BIOS32_PARAGRAPH_LEN 16 687f4da474SThomas Mingarelli #define PCI_ROM_BASE1 0x000F0000 697f4da474SThomas Mingarelli #define ROM_SIZE 0x10000 707f4da474SThomas Mingarelli 717f4da474SThomas Mingarelli struct bios32_service_dir { 727f4da474SThomas Mingarelli u32 signature; 737f4da474SThomas Mingarelli u32 entry_point; 747f4da474SThomas Mingarelli u8 revision; 757f4da474SThomas Mingarelli u8 length; 767f4da474SThomas Mingarelli u8 checksum; 777f4da474SThomas Mingarelli u8 reserved[5]; 787f4da474SThomas Mingarelli }; 797f4da474SThomas Mingarelli 807f4da474SThomas Mingarelli /* type 212 */ 817f4da474SThomas Mingarelli struct smbios_cru64_info { 827f4da474SThomas Mingarelli u8 type; 837f4da474SThomas Mingarelli u8 byte_length; 847f4da474SThomas Mingarelli u16 handle; 857f4da474SThomas Mingarelli u32 signature; 867f4da474SThomas Mingarelli u64 physical_address; 877f4da474SThomas Mingarelli u32 double_length; 887f4da474SThomas Mingarelli u32 double_offset; 897f4da474SThomas Mingarelli }; 907f4da474SThomas Mingarelli #define SMBIOS_CRU64_INFORMATION 212 917f4da474SThomas Mingarelli 925efc7a62SThomas Mingarelli /* type 219 */ 935efc7a62SThomas Mingarelli struct smbios_proliant_info { 945efc7a62SThomas Mingarelli u8 type; 955efc7a62SThomas Mingarelli u8 byte_length; 965efc7a62SThomas Mingarelli u16 handle; 975efc7a62SThomas Mingarelli u32 power_features; 985efc7a62SThomas Mingarelli u32 omega_features; 995efc7a62SThomas Mingarelli u32 reserved; 1005efc7a62SThomas Mingarelli u32 misc_features; 1015efc7a62SThomas Mingarelli }; 1025efc7a62SThomas Mingarelli #define SMBIOS_ICRU_INFORMATION 219 1035efc7a62SThomas Mingarelli 1045efc7a62SThomas Mingarelli 1057f4da474SThomas Mingarelli struct cmn_registers { 1067f4da474SThomas Mingarelli union { 1077f4da474SThomas Mingarelli struct { 1087f4da474SThomas Mingarelli u8 ral; 1097f4da474SThomas Mingarelli u8 rah; 1107f4da474SThomas Mingarelli u16 rea2; 1117f4da474SThomas Mingarelli }; 1127f4da474SThomas Mingarelli u32 reax; 1137f4da474SThomas Mingarelli } u1; 1147f4da474SThomas Mingarelli union { 1157f4da474SThomas Mingarelli struct { 1167f4da474SThomas Mingarelli u8 rbl; 1177f4da474SThomas Mingarelli u8 rbh; 1187f4da474SThomas Mingarelli u8 reb2l; 1197f4da474SThomas Mingarelli u8 reb2h; 1207f4da474SThomas Mingarelli }; 1217f4da474SThomas Mingarelli u32 rebx; 1227f4da474SThomas Mingarelli } u2; 1237f4da474SThomas Mingarelli union { 1247f4da474SThomas Mingarelli struct { 1257f4da474SThomas Mingarelli u8 rcl; 1267f4da474SThomas Mingarelli u8 rch; 1277f4da474SThomas Mingarelli u16 rec2; 1287f4da474SThomas Mingarelli }; 1297f4da474SThomas Mingarelli u32 recx; 1307f4da474SThomas Mingarelli } u3; 1317f4da474SThomas Mingarelli union { 1327f4da474SThomas Mingarelli struct { 1337f4da474SThomas Mingarelli u8 rdl; 1347f4da474SThomas Mingarelli u8 rdh; 1357f4da474SThomas Mingarelli u16 red2; 1367f4da474SThomas Mingarelli }; 1377f4da474SThomas Mingarelli u32 redx; 1387f4da474SThomas Mingarelli } u4; 1397f4da474SThomas Mingarelli 1407f4da474SThomas Mingarelli u32 resi; 1417f4da474SThomas Mingarelli u32 redi; 1427f4da474SThomas Mingarelli u16 rds; 1437f4da474SThomas Mingarelli u16 res; 1447f4da474SThomas Mingarelli u32 reflags; 1457f4da474SThomas Mingarelli } __attribute__((packed)); 1467f4da474SThomas Mingarelli 14734572b29Sdann frazier static unsigned int hpwdt_nmi_decoding; 148a089361cSMingarelli, Thomas static unsigned int allow_kdump = 1; 1495efc7a62SThomas Mingarelli static unsigned int is_icru; 150cce78da7SMingarelli, Thomas static unsigned int is_uefi; 1517f4da474SThomas Mingarelli static DEFINE_SPINLOCK(rom_lock); 1527f4da474SThomas Mingarelli static void *cru_rom_addr; 1537f4da474SThomas Mingarelli static struct cmn_registers cmn_regs; 1547f4da474SThomas Mingarelli 155143a2e54SWim Van Sebroeck extern asmlinkage void asminline_call(struct cmn_registers *pi86Regs, 156143a2e54SWim Van Sebroeck unsigned long *pRomEntry); 1571f6ef234SLinus Torvalds 1586b7f3d53Sdann frazier #ifdef CONFIG_X86_32 1597f4da474SThomas Mingarelli /* --32 Bit Bios------------------------------------------------------------ */ 1607f4da474SThomas Mingarelli 1617f4da474SThomas Mingarelli #define HPWDT_ARCH 32 1627f4da474SThomas Mingarelli 1631f6ef234SLinus Torvalds asm(".text \n\t" 164a6b08887SAndi Kleen ".align 4 \n\t" 165a6b08887SAndi Kleen ".globl asminline_call \n" 1661f6ef234SLinus Torvalds "asminline_call: \n\t" 1671f6ef234SLinus Torvalds "pushl %ebp \n\t" 1687f4da474SThomas Mingarelli "movl %esp, %ebp \n\t" 1697f4da474SThomas Mingarelli "pusha \n\t" 1707f4da474SThomas Mingarelli "pushf \n\t" 1717f4da474SThomas Mingarelli "push %es \n\t" 1727f4da474SThomas Mingarelli "push %ds \n\t" 1737f4da474SThomas Mingarelli "pop %es \n\t" 1747f4da474SThomas Mingarelli "movl 8(%ebp),%eax \n\t" 1757f4da474SThomas Mingarelli "movl 4(%eax),%ebx \n\t" 1767f4da474SThomas Mingarelli "movl 8(%eax),%ecx \n\t" 1777f4da474SThomas Mingarelli "movl 12(%eax),%edx \n\t" 1787f4da474SThomas Mingarelli "movl 16(%eax),%esi \n\t" 1797f4da474SThomas Mingarelli "movl 20(%eax),%edi \n\t" 1807f4da474SThomas Mingarelli "movl (%eax),%eax \n\t" 1817f4da474SThomas Mingarelli "push %cs \n\t" 1827f4da474SThomas Mingarelli "call *12(%ebp) \n\t" 1837f4da474SThomas Mingarelli "pushf \n\t" 1847f4da474SThomas Mingarelli "pushl %eax \n\t" 1857f4da474SThomas Mingarelli "movl 8(%ebp),%eax \n\t" 1867f4da474SThomas Mingarelli "movl %ebx,4(%eax) \n\t" 1877f4da474SThomas Mingarelli "movl %ecx,8(%eax) \n\t" 1887f4da474SThomas Mingarelli "movl %edx,12(%eax) \n\t" 1897f4da474SThomas Mingarelli "movl %esi,16(%eax) \n\t" 1907f4da474SThomas Mingarelli "movl %edi,20(%eax) \n\t" 1917f4da474SThomas Mingarelli "movw %ds,24(%eax) \n\t" 1927f4da474SThomas Mingarelli "movw %es,26(%eax) \n\t" 1937f4da474SThomas Mingarelli "popl %ebx \n\t" 1947f4da474SThomas Mingarelli "movl %ebx,(%eax) \n\t" 1957f4da474SThomas Mingarelli "popl %ebx \n\t" 1967f4da474SThomas Mingarelli "movl %ebx,28(%eax) \n\t" 1977f4da474SThomas Mingarelli "pop %es \n\t" 1987f4da474SThomas Mingarelli "popf \n\t" 1997f4da474SThomas Mingarelli "popa \n\t" 2001f6ef234SLinus Torvalds "leave \n\t" 2011f6ef234SLinus Torvalds "ret \n\t" 2021f6ef234SLinus Torvalds ".previous"); 2031f6ef234SLinus Torvalds 2047f4da474SThomas Mingarelli 2057f4da474SThomas Mingarelli /* 2067f4da474SThomas Mingarelli * cru_detect 2077f4da474SThomas Mingarelli * 2087f4da474SThomas Mingarelli * Routine Description: 2097f4da474SThomas Mingarelli * This function uses the 32-bit BIOS Service Directory record to 2107f4da474SThomas Mingarelli * search for a $CRU record. 2117f4da474SThomas Mingarelli * 2127f4da474SThomas Mingarelli * Return Value: 2137f4da474SThomas Mingarelli * 0 : SUCCESS 2147f4da474SThomas Mingarelli * <0 : FAILURE 2157f4da474SThomas Mingarelli */ 2162d991a16SBill Pemberton static int cru_detect(unsigned long map_entry, 2177f4da474SThomas Mingarelli unsigned long map_offset) 2187f4da474SThomas Mingarelli { 2197f4da474SThomas Mingarelli void *bios32_map; 2207f4da474SThomas Mingarelli unsigned long *bios32_entrypoint; 2217f4da474SThomas Mingarelli unsigned long cru_physical_address; 2227f4da474SThomas Mingarelli unsigned long cru_length; 2237f4da474SThomas Mingarelli unsigned long physical_bios_base = 0; 2247f4da474SThomas Mingarelli unsigned long physical_bios_offset = 0; 2257f4da474SThomas Mingarelli int retval = -ENODEV; 2267f4da474SThomas Mingarelli 2277f4da474SThomas Mingarelli bios32_map = ioremap(map_entry, (2 * PAGE_SIZE)); 2287f4da474SThomas Mingarelli 2297f4da474SThomas Mingarelli if (bios32_map == NULL) 2307f4da474SThomas Mingarelli return -ENODEV; 2317f4da474SThomas Mingarelli 2327f4da474SThomas Mingarelli bios32_entrypoint = bios32_map + map_offset; 2337f4da474SThomas Mingarelli 2347f4da474SThomas Mingarelli cmn_regs.u1.reax = CRU_BIOS_SIGNATURE_VALUE; 2357f4da474SThomas Mingarelli 23697d2a10dSMaxim Uvarov set_memory_x((unsigned long)bios32_map, 2); 2377f4da474SThomas Mingarelli asminline_call(&cmn_regs, bios32_entrypoint); 2387f4da474SThomas Mingarelli 2397f4da474SThomas Mingarelli if (cmn_regs.u1.ral != 0) { 24027c766aaSJoe Perches pr_warn("Call succeeded but with an error: 0x%x\n", 2417f4da474SThomas Mingarelli cmn_regs.u1.ral); 2427f4da474SThomas Mingarelli } else { 2437f4da474SThomas Mingarelli physical_bios_base = cmn_regs.u2.rebx; 2447f4da474SThomas Mingarelli physical_bios_offset = cmn_regs.u4.redx; 2457f4da474SThomas Mingarelli cru_length = cmn_regs.u3.recx; 2467f4da474SThomas Mingarelli cru_physical_address = 2477f4da474SThomas Mingarelli physical_bios_base + physical_bios_offset; 2487f4da474SThomas Mingarelli 2497f4da474SThomas Mingarelli /* If the values look OK, then map it in. */ 2507f4da474SThomas Mingarelli if ((physical_bios_base + physical_bios_offset)) { 2517f4da474SThomas Mingarelli cru_rom_addr = 2527f4da474SThomas Mingarelli ioremap(cru_physical_address, cru_length); 253e67d668eSMingarelli, Thomas if (cru_rom_addr) { 25497d2a10dSMaxim Uvarov set_memory_x((unsigned long)cru_rom_addr & PAGE_MASK, 25597d2a10dSMaxim Uvarov (cru_length + PAGE_SIZE - 1) >> PAGE_SHIFT); 2567f4da474SThomas Mingarelli retval = 0; 2577f4da474SThomas Mingarelli } 258e67d668eSMingarelli, Thomas } 2597f4da474SThomas Mingarelli 26027c766aaSJoe Perches pr_debug("CRU Base Address: 0x%lx\n", physical_bios_base); 26127c766aaSJoe Perches pr_debug("CRU Offset Address: 0x%lx\n", physical_bios_offset); 26227c766aaSJoe Perches pr_debug("CRU Length: 0x%lx\n", cru_length); 26327c766aaSJoe Perches pr_debug("CRU Mapped Address: %p\n", &cru_rom_addr); 2647f4da474SThomas Mingarelli } 2657f4da474SThomas Mingarelli iounmap(bios32_map); 2667f4da474SThomas Mingarelli return retval; 2677f4da474SThomas Mingarelli } 2687f4da474SThomas Mingarelli 2697f4da474SThomas Mingarelli /* 27030ec910eSRoland Dreier * bios_checksum 27130ec910eSRoland Dreier */ 2722d991a16SBill Pemberton static int bios_checksum(const char __iomem *ptr, int len) 27330ec910eSRoland Dreier { 27430ec910eSRoland Dreier char sum = 0; 27530ec910eSRoland Dreier int i; 27630ec910eSRoland Dreier 27730ec910eSRoland Dreier /* 27830ec910eSRoland Dreier * calculate checksum of size bytes. This should add up 27930ec910eSRoland Dreier * to zero if we have a valid header. 28030ec910eSRoland Dreier */ 28130ec910eSRoland Dreier for (i = 0; i < len; i++) 28230ec910eSRoland Dreier sum += ptr[i]; 28330ec910eSRoland Dreier 28430ec910eSRoland Dreier return ((sum == 0) && (len > 0)); 28530ec910eSRoland Dreier } 28630ec910eSRoland Dreier 28730ec910eSRoland Dreier /* 2887f4da474SThomas Mingarelli * bios32_present 2897f4da474SThomas Mingarelli * 2907f4da474SThomas Mingarelli * Routine Description: 2917f4da474SThomas Mingarelli * This function finds the 32-bit BIOS Service Directory 2927f4da474SThomas Mingarelli * 2937f4da474SThomas Mingarelli * Return Value: 2947f4da474SThomas Mingarelli * 0 : SUCCESS 2957f4da474SThomas Mingarelli * <0 : FAILURE 2967f4da474SThomas Mingarelli */ 2972d991a16SBill Pemberton static int bios32_present(const char __iomem *p) 2987f4da474SThomas Mingarelli { 2997f4da474SThomas Mingarelli struct bios32_service_dir *bios_32_ptr; 3007f4da474SThomas Mingarelli int length; 3017f4da474SThomas Mingarelli unsigned long map_entry, map_offset; 3027f4da474SThomas Mingarelli 3037f4da474SThomas Mingarelli bios_32_ptr = (struct bios32_service_dir *) p; 3047f4da474SThomas Mingarelli 3057f4da474SThomas Mingarelli /* 3067f4da474SThomas Mingarelli * Search for signature by checking equal to the swizzled value 3077f4da474SThomas Mingarelli * instead of calling another routine to perform a strcmp. 3087f4da474SThomas Mingarelli */ 3097f4da474SThomas Mingarelli if (bios_32_ptr->signature == PCI_BIOS32_SD_VALUE) { 3107f4da474SThomas Mingarelli length = bios_32_ptr->length * PCI_BIOS32_PARAGRAPH_LEN; 3117f4da474SThomas Mingarelli if (bios_checksum(p, length)) { 3127f4da474SThomas Mingarelli /* 3137f4da474SThomas Mingarelli * According to the spec, we're looking for the 3147f4da474SThomas Mingarelli * first 4KB-aligned address below the entrypoint 3157f4da474SThomas Mingarelli * listed in the header. The Service Directory code 3167f4da474SThomas Mingarelli * is guaranteed to occupy no more than 2 4KB pages. 3177f4da474SThomas Mingarelli */ 3187f4da474SThomas Mingarelli map_entry = bios_32_ptr->entry_point & ~(PAGE_SIZE - 1); 3197f4da474SThomas Mingarelli map_offset = bios_32_ptr->entry_point - map_entry; 3207f4da474SThomas Mingarelli 3217f4da474SThomas Mingarelli return cru_detect(map_entry, map_offset); 3227f4da474SThomas Mingarelli } 3237f4da474SThomas Mingarelli } 3247f4da474SThomas Mingarelli return -ENODEV; 3257f4da474SThomas Mingarelli } 3267f4da474SThomas Mingarelli 3272d991a16SBill Pemberton static int detect_cru_service(void) 3287f4da474SThomas Mingarelli { 3297f4da474SThomas Mingarelli char __iomem *p, *q; 3307f4da474SThomas Mingarelli int rc = -1; 3317f4da474SThomas Mingarelli 3327f4da474SThomas Mingarelli /* 3337f4da474SThomas Mingarelli * Search from 0x0f0000 through 0x0fffff, inclusive. 3347f4da474SThomas Mingarelli */ 3357f4da474SThomas Mingarelli p = ioremap(PCI_ROM_BASE1, ROM_SIZE); 3367f4da474SThomas Mingarelli if (p == NULL) 3377f4da474SThomas Mingarelli return -ENOMEM; 3387f4da474SThomas Mingarelli 3397f4da474SThomas Mingarelli for (q = p; q < p + ROM_SIZE; q += 16) { 3407f4da474SThomas Mingarelli rc = bios32_present(q); 3417f4da474SThomas Mingarelli if (!rc) 3427f4da474SThomas Mingarelli break; 3437f4da474SThomas Mingarelli } 3447f4da474SThomas Mingarelli iounmap(p); 3457f4da474SThomas Mingarelli return rc; 3467f4da474SThomas Mingarelli } 3476b7f3d53Sdann frazier /* ------------------------------------------------------------------------- */ 3486b7f3d53Sdann frazier #endif /* CONFIG_X86_32 */ 3496b7f3d53Sdann frazier #ifdef CONFIG_X86_64 3507f4da474SThomas Mingarelli /* --64 Bit Bios------------------------------------------------------------ */ 3517f4da474SThomas Mingarelli 3527f4da474SThomas Mingarelli #define HPWDT_ARCH 64 3537f4da474SThomas Mingarelli 3541f6ef234SLinus Torvalds asm(".text \n\t" 355a6b08887SAndi Kleen ".align 4 \n\t" 356a6b08887SAndi Kleen ".globl asminline_call \n" 3571f6ef234SLinus Torvalds "asminline_call: \n\t" 3581f6ef234SLinus Torvalds "pushq %rbp \n\t" 3597f4da474SThomas Mingarelli "movq %rsp, %rbp \n\t" 3607f4da474SThomas Mingarelli "pushq %rax \n\t" 3617f4da474SThomas Mingarelli "pushq %rbx \n\t" 3627f4da474SThomas Mingarelli "pushq %rdx \n\t" 3637f4da474SThomas Mingarelli "pushq %r12 \n\t" 3647f4da474SThomas Mingarelli "pushq %r9 \n\t" 3657f4da474SThomas Mingarelli "movq %rsi, %r12 \n\t" 3667f4da474SThomas Mingarelli "movq %rdi, %r9 \n\t" 3677f4da474SThomas Mingarelli "movl 4(%r9),%ebx \n\t" 3687f4da474SThomas Mingarelli "movl 8(%r9),%ecx \n\t" 3697f4da474SThomas Mingarelli "movl 12(%r9),%edx \n\t" 3707f4da474SThomas Mingarelli "movl 16(%r9),%esi \n\t" 3717f4da474SThomas Mingarelli "movl 20(%r9),%edi \n\t" 3727f4da474SThomas Mingarelli "movl (%r9),%eax \n\t" 3737f4da474SThomas Mingarelli "call *%r12 \n\t" 3747f4da474SThomas Mingarelli "pushfq \n\t" 3757f4da474SThomas Mingarelli "popq %r12 \n\t" 3767f4da474SThomas Mingarelli "movl %eax, (%r9) \n\t" 3777f4da474SThomas Mingarelli "movl %ebx, 4(%r9) \n\t" 3787f4da474SThomas Mingarelli "movl %ecx, 8(%r9) \n\t" 3797f4da474SThomas Mingarelli "movl %edx, 12(%r9) \n\t" 3807f4da474SThomas Mingarelli "movl %esi, 16(%r9) \n\t" 3817f4da474SThomas Mingarelli "movl %edi, 20(%r9) \n\t" 3827f4da474SThomas Mingarelli "movq %r12, %rax \n\t" 3837f4da474SThomas Mingarelli "movl %eax, 28(%r9) \n\t" 3847f4da474SThomas Mingarelli "popq %r9 \n\t" 3857f4da474SThomas Mingarelli "popq %r12 \n\t" 3867f4da474SThomas Mingarelli "popq %rdx \n\t" 3877f4da474SThomas Mingarelli "popq %rbx \n\t" 3887f4da474SThomas Mingarelli "popq %rax \n\t" 3891f6ef234SLinus Torvalds "leave \n\t" 3901f6ef234SLinus Torvalds "ret \n\t" 3911f6ef234SLinus Torvalds ".previous"); 3927f4da474SThomas Mingarelli 3937f4da474SThomas Mingarelli /* 3947f4da474SThomas Mingarelli * dmi_find_cru 3957f4da474SThomas Mingarelli * 3967f4da474SThomas Mingarelli * Routine Description: 39730ec910eSRoland Dreier * This function checks whether or not a SMBIOS/DMI record is 3987f4da474SThomas Mingarelli * the 64bit CRU info or not 3997f4da474SThomas Mingarelli */ 4002d991a16SBill Pemberton static void dmi_find_cru(const struct dmi_header *dm, void *dummy) 4017f4da474SThomas Mingarelli { 4027f4da474SThomas Mingarelli struct smbios_cru64_info *smbios_cru64_ptr; 4037f4da474SThomas Mingarelli unsigned long cru_physical_address; 4047f4da474SThomas Mingarelli 4057f4da474SThomas Mingarelli if (dm->type == SMBIOS_CRU64_INFORMATION) { 4067f4da474SThomas Mingarelli smbios_cru64_ptr = (struct smbios_cru64_info *) dm; 4077f4da474SThomas Mingarelli if (smbios_cru64_ptr->signature == CRU_BIOS_SIGNATURE_VALUE) { 4087f4da474SThomas Mingarelli cru_physical_address = 4097f4da474SThomas Mingarelli smbios_cru64_ptr->physical_address + 4107f4da474SThomas Mingarelli smbios_cru64_ptr->double_offset; 4117f4da474SThomas Mingarelli cru_rom_addr = ioremap(cru_physical_address, 4127f4da474SThomas Mingarelli smbios_cru64_ptr->double_length); 41306026413SBernhard Walle set_memory_x((unsigned long)cru_rom_addr & PAGE_MASK, 41406026413SBernhard Walle smbios_cru64_ptr->double_length >> PAGE_SHIFT); 4157f4da474SThomas Mingarelli } 4167f4da474SThomas Mingarelli } 4177f4da474SThomas Mingarelli } 4187f4da474SThomas Mingarelli 4192d991a16SBill Pemberton static int detect_cru_service(void) 4207f4da474SThomas Mingarelli { 4217f4da474SThomas Mingarelli cru_rom_addr = NULL; 4227f4da474SThomas Mingarelli 423e7a19c56SJean Delvare dmi_walk(dmi_find_cru, NULL); 4247f4da474SThomas Mingarelli 4257f4da474SThomas Mingarelli /* if cru_rom_addr has been set then we found a CRU service */ 4267f4da474SThomas Mingarelli return ((cru_rom_addr != NULL) ? 0 : -ENODEV); 4277f4da474SThomas Mingarelli } 4287f4da474SThomas Mingarelli /* ------------------------------------------------------------------------- */ 4296b7f3d53Sdann frazier #endif /* CONFIG_X86_64 */ 43086ded1f3Sdann frazier #endif /* CONFIG_HPWDT_NMI_DECODING */ 4317f4da474SThomas Mingarelli 4327f4da474SThomas Mingarelli /* 4337f4da474SThomas Mingarelli * Watchdog operations 4347f4da474SThomas Mingarelli */ 4357f4da474SThomas Mingarelli static void hpwdt_start(void) 4367f4da474SThomas Mingarelli { 437e802e32dSdann frazier reload = SECS_TO_TICKS(soft_margin); 4387f4da474SThomas Mingarelli iowrite16(reload, hpwdt_timer_reg); 439d08c9a33SMingarelli, Thomas iowrite8(0x85, hpwdt_timer_con); 4407f4da474SThomas Mingarelli } 4417f4da474SThomas Mingarelli 4427f4da474SThomas Mingarelli static void hpwdt_stop(void) 4437f4da474SThomas Mingarelli { 4447f4da474SThomas Mingarelli unsigned long data; 4457f4da474SThomas Mingarelli 446d08c9a33SMingarelli, Thomas data = ioread8(hpwdt_timer_con); 4477f4da474SThomas Mingarelli data &= 0xFE; 448d08c9a33SMingarelli, Thomas iowrite8(data, hpwdt_timer_con); 4497f4da474SThomas Mingarelli } 4507f4da474SThomas Mingarelli 4517f4da474SThomas Mingarelli static void hpwdt_ping(void) 4527f4da474SThomas Mingarelli { 4537f4da474SThomas Mingarelli iowrite16(reload, hpwdt_timer_reg); 4547f4da474SThomas Mingarelli } 4557f4da474SThomas Mingarelli 4567f4da474SThomas Mingarelli static int hpwdt_change_timer(int new_margin) 4577f4da474SThomas Mingarelli { 4586f681c2eSdann frazier if (new_margin < 1 || new_margin > HPWDT_MAX_TIMER) { 45927c766aaSJoe Perches pr_warn("New value passed in is invalid: %d seconds\n", 4607f4da474SThomas Mingarelli new_margin); 4617f4da474SThomas Mingarelli return -EINVAL; 4627f4da474SThomas Mingarelli } 4637f4da474SThomas Mingarelli 4647f4da474SThomas Mingarelli soft_margin = new_margin; 46527c766aaSJoe Perches pr_debug("New timer passed in is %d seconds\n", new_margin); 466e802e32dSdann frazier reload = SECS_TO_TICKS(soft_margin); 4677f4da474SThomas Mingarelli 4687f4da474SThomas Mingarelli return 0; 4697f4da474SThomas Mingarelli } 4707f4da474SThomas Mingarelli 471aae67f36Sdann frazier static int hpwdt_time_left(void) 472aae67f36Sdann frazier { 473aae67f36Sdann frazier return TICKS_TO_SECS(ioread16(hpwdt_timer_reg)); 474aae67f36Sdann frazier } 475aae67f36Sdann frazier 47686ded1f3Sdann frazier #ifdef CONFIG_HPWDT_NMI_DECODING 4777f4da474SThomas Mingarelli /* 478ab4ba3cdSThomas Mingarelli * NMI Handler 479ab4ba3cdSThomas Mingarelli */ 4809c48f1c6SDon Zickus static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs) 481ab4ba3cdSThomas Mingarelli { 482ab4ba3cdSThomas Mingarelli unsigned long rom_pl; 483ab4ba3cdSThomas Mingarelli static int die_nmi_called; 484ab4ba3cdSThomas Mingarelli 48534572b29Sdann frazier if (!hpwdt_nmi_decoding) 486243066baSdann frazier goto out; 487243066baSdann frazier 488ab4ba3cdSThomas Mingarelli spin_lock_irqsave(&rom_lock, rom_pl); 489cce78da7SMingarelli, Thomas if (!die_nmi_called && !is_icru && !is_uefi) 490ab4ba3cdSThomas Mingarelli asminline_call(&cmn_regs, cru_rom_addr); 491ab4ba3cdSThomas Mingarelli die_nmi_called = 1; 492ab4ba3cdSThomas Mingarelli spin_unlock_irqrestore(&rom_lock, rom_pl); 4935efc7a62SThomas Mingarelli 494ab4ba3cdSThomas Mingarelli if (allow_kdump) 495ab4ba3cdSThomas Mingarelli hpwdt_stop(); 496dbc018ecSNaga Chumbalkar 497cce78da7SMingarelli, Thomas if (!is_icru && !is_uefi) { 498dbc018ecSNaga Chumbalkar if (cmn_regs.u1.ral == 0) { 499dbc018ecSNaga Chumbalkar panic("An NMI occurred, " 500dbc018ecSNaga Chumbalkar "but unable to determine source.\n"); 501dbc018ecSNaga Chumbalkar } 502dbc018ecSNaga Chumbalkar } 5037bb5be94SThomas Mingarelli panic("An NMI occurred. Depending on your system the reason " 5047bb5be94SThomas Mingarelli "for the NMI is logged in any one of the following " 5057bb5be94SThomas Mingarelli "resources:\n" 5067bb5be94SThomas Mingarelli "1. Integrated Management Log (IML)\n" 5077bb5be94SThomas Mingarelli "2. OA Syslog\n" 5087bb5be94SThomas Mingarelli "3. OA Forward Progress Log\n" 5097bb5be94SThomas Mingarelli "4. iLO Event Log"); 5105efc7a62SThomas Mingarelli 511243066baSdann frazier out: 5129c48f1c6SDon Zickus return NMI_DONE; 513ab4ba3cdSThomas Mingarelli } 51486ded1f3Sdann frazier #endif /* CONFIG_HPWDT_NMI_DECODING */ 515ab4ba3cdSThomas Mingarelli 516ab4ba3cdSThomas Mingarelli /* 5177f4da474SThomas Mingarelli * /dev/watchdog handling 5187f4da474SThomas Mingarelli */ 5197f4da474SThomas Mingarelli static int hpwdt_open(struct inode *inode, struct file *file) 5207f4da474SThomas Mingarelli { 5217f4da474SThomas Mingarelli /* /dev/watchdog can only be opened once */ 5227f4da474SThomas Mingarelli if (test_and_set_bit(0, &hpwdt_is_open)) 5237f4da474SThomas Mingarelli return -EBUSY; 5247f4da474SThomas Mingarelli 5257f4da474SThomas Mingarelli /* Start the watchdog */ 5267f4da474SThomas Mingarelli hpwdt_start(); 5277f4da474SThomas Mingarelli hpwdt_ping(); 5287f4da474SThomas Mingarelli 5297f4da474SThomas Mingarelli return nonseekable_open(inode, file); 5307f4da474SThomas Mingarelli } 5317f4da474SThomas Mingarelli 5327f4da474SThomas Mingarelli static int hpwdt_release(struct inode *inode, struct file *file) 5337f4da474SThomas Mingarelli { 5347f4da474SThomas Mingarelli /* Stop the watchdog */ 5357f4da474SThomas Mingarelli if (expect_release == 42) { 5367f4da474SThomas Mingarelli hpwdt_stop(); 5377f4da474SThomas Mingarelli } else { 53827c766aaSJoe Perches pr_crit("Unexpected close, not stopping watchdog!\n"); 5397f4da474SThomas Mingarelli hpwdt_ping(); 5407f4da474SThomas Mingarelli } 5417f4da474SThomas Mingarelli 5427f4da474SThomas Mingarelli expect_release = 0; 5437f4da474SThomas Mingarelli 5447f4da474SThomas Mingarelli /* /dev/watchdog is being closed, make sure it can be re-opened */ 5457f4da474SThomas Mingarelli clear_bit(0, &hpwdt_is_open); 5467f4da474SThomas Mingarelli 5477f4da474SThomas Mingarelli return 0; 5487f4da474SThomas Mingarelli } 5497f4da474SThomas Mingarelli 5507f4da474SThomas Mingarelli static ssize_t hpwdt_write(struct file *file, const char __user *data, 5517f4da474SThomas Mingarelli size_t len, loff_t *ppos) 5527f4da474SThomas Mingarelli { 5537f4da474SThomas Mingarelli /* See if we got the magic character 'V' and reload the timer */ 5547f4da474SThomas Mingarelli if (len) { 5557f4da474SThomas Mingarelli if (!nowayout) { 5567f4da474SThomas Mingarelli size_t i; 5577f4da474SThomas Mingarelli 5587f4da474SThomas Mingarelli /* note: just in case someone wrote the magic character 5597f4da474SThomas Mingarelli * five months ago... */ 5607f4da474SThomas Mingarelli expect_release = 0; 5617f4da474SThomas Mingarelli 5627f4da474SThomas Mingarelli /* scan to see whether or not we got the magic char. */ 5637f4da474SThomas Mingarelli for (i = 0; i != len; i++) { 5647f4da474SThomas Mingarelli char c; 5657f4da474SThomas Mingarelli if (get_user(c, data + i)) 5667f4da474SThomas Mingarelli return -EFAULT; 5677f4da474SThomas Mingarelli if (c == 'V') 5687f4da474SThomas Mingarelli expect_release = 42; 5697f4da474SThomas Mingarelli } 5707f4da474SThomas Mingarelli } 5717f4da474SThomas Mingarelli 5727f4da474SThomas Mingarelli /* someone wrote to us, we should reload the timer */ 5737f4da474SThomas Mingarelli hpwdt_ping(); 5747f4da474SThomas Mingarelli } 5757f4da474SThomas Mingarelli 5767f4da474SThomas Mingarelli return len; 5777f4da474SThomas Mingarelli } 5787f4da474SThomas Mingarelli 57942747d71SWim Van Sebroeck static const struct watchdog_info ident = { 5807f4da474SThomas Mingarelli .options = WDIOF_SETTIMEOUT | 5817f4da474SThomas Mingarelli WDIOF_KEEPALIVEPING | 5827f4da474SThomas Mingarelli WDIOF_MAGICCLOSE, 58336e3ff44Sdann frazier .identity = "HP iLO2+ HW Watchdog Timer", 5847f4da474SThomas Mingarelli }; 5857f4da474SThomas Mingarelli 5867f4da474SThomas Mingarelli static long hpwdt_ioctl(struct file *file, unsigned int cmd, 5877f4da474SThomas Mingarelli unsigned long arg) 5887f4da474SThomas Mingarelli { 5897f4da474SThomas Mingarelli void __user *argp = (void __user *)arg; 5907f4da474SThomas Mingarelli int __user *p = argp; 591*46c80b20SJean Delvare int new_margin, options; 5927f4da474SThomas Mingarelli int ret = -ENOTTY; 5937f4da474SThomas Mingarelli 5947f4da474SThomas Mingarelli switch (cmd) { 5957f4da474SThomas Mingarelli case WDIOC_GETSUPPORT: 5967f4da474SThomas Mingarelli ret = 0; 5977f4da474SThomas Mingarelli if (copy_to_user(argp, &ident, sizeof(ident))) 5987f4da474SThomas Mingarelli ret = -EFAULT; 5997f4da474SThomas Mingarelli break; 6007f4da474SThomas Mingarelli 6017f4da474SThomas Mingarelli case WDIOC_GETSTATUS: 6027f4da474SThomas Mingarelli case WDIOC_GETBOOTSTATUS: 6037f4da474SThomas Mingarelli ret = put_user(0, p); 6047f4da474SThomas Mingarelli break; 6057f4da474SThomas Mingarelli 6067f4da474SThomas Mingarelli case WDIOC_KEEPALIVE: 6077f4da474SThomas Mingarelli hpwdt_ping(); 6087f4da474SThomas Mingarelli ret = 0; 6097f4da474SThomas Mingarelli break; 6107f4da474SThomas Mingarelli 611*46c80b20SJean Delvare case WDIOC_SETOPTIONS: 612*46c80b20SJean Delvare ret = get_user(options, p); 613*46c80b20SJean Delvare if (ret) 614*46c80b20SJean Delvare break; 615*46c80b20SJean Delvare 616*46c80b20SJean Delvare if (options & WDIOS_DISABLECARD) 617*46c80b20SJean Delvare hpwdt_stop(); 618*46c80b20SJean Delvare 619*46c80b20SJean Delvare if (options & WDIOS_ENABLECARD) { 620*46c80b20SJean Delvare hpwdt_start(); 621*46c80b20SJean Delvare hpwdt_ping(); 622*46c80b20SJean Delvare } 623*46c80b20SJean Delvare break; 624*46c80b20SJean Delvare 6257f4da474SThomas Mingarelli case WDIOC_SETTIMEOUT: 6267f4da474SThomas Mingarelli ret = get_user(new_margin, p); 6277f4da474SThomas Mingarelli if (ret) 6287f4da474SThomas Mingarelli break; 6297f4da474SThomas Mingarelli 6307f4da474SThomas Mingarelli ret = hpwdt_change_timer(new_margin); 6317f4da474SThomas Mingarelli if (ret) 6327f4da474SThomas Mingarelli break; 6337f4da474SThomas Mingarelli 6347f4da474SThomas Mingarelli hpwdt_ping(); 6357f4da474SThomas Mingarelli /* Fall */ 6367f4da474SThomas Mingarelli case WDIOC_GETTIMEOUT: 6377f4da474SThomas Mingarelli ret = put_user(soft_margin, p); 6387f4da474SThomas Mingarelli break; 639aae67f36Sdann frazier 640aae67f36Sdann frazier case WDIOC_GETTIMELEFT: 641aae67f36Sdann frazier ret = put_user(hpwdt_time_left(), p); 642aae67f36Sdann frazier break; 6437f4da474SThomas Mingarelli } 6447f4da474SThomas Mingarelli return ret; 6457f4da474SThomas Mingarelli } 6467f4da474SThomas Mingarelli 6477f4da474SThomas Mingarelli /* 6487f4da474SThomas Mingarelli * Kernel interfaces 6497f4da474SThomas Mingarelli */ 650d5c26a59SWim Van Sebroeck static const struct file_operations hpwdt_fops = { 6517f4da474SThomas Mingarelli .owner = THIS_MODULE, 6527f4da474SThomas Mingarelli .llseek = no_llseek, 6537f4da474SThomas Mingarelli .write = hpwdt_write, 6547f4da474SThomas Mingarelli .unlocked_ioctl = hpwdt_ioctl, 6557f4da474SThomas Mingarelli .open = hpwdt_open, 6567f4da474SThomas Mingarelli .release = hpwdt_release, 6577f4da474SThomas Mingarelli }; 6587f4da474SThomas Mingarelli 6597f4da474SThomas Mingarelli static struct miscdevice hpwdt_miscdev = { 6607f4da474SThomas Mingarelli .minor = WATCHDOG_MINOR, 6617f4da474SThomas Mingarelli .name = "watchdog", 6627f4da474SThomas Mingarelli .fops = &hpwdt_fops, 6637f4da474SThomas Mingarelli }; 6647f4da474SThomas Mingarelli 6657f4da474SThomas Mingarelli /* 6667f4da474SThomas Mingarelli * Init & Exit 6677f4da474SThomas Mingarelli */ 6687f4da474SThomas Mingarelli 66986ded1f3Sdann frazier #ifdef CONFIG_HPWDT_NMI_DECODING 6704a7863ccSDon Zickus #ifdef CONFIG_X86_LOCAL_APIC 6712d991a16SBill Pemberton static void hpwdt_check_nmi_decoding(struct pci_dev *dev) 67247bece87SThomas Mingarelli { 67347bece87SThomas Mingarelli /* 67447bece87SThomas Mingarelli * If nmi_watchdog is turned off then we can turn on 67534572b29Sdann frazier * our nmi decoding capability. 67647bece87SThomas Mingarelli */ 67734572b29Sdann frazier hpwdt_nmi_decoding = 1; 67847bece87SThomas Mingarelli } 67947bece87SThomas Mingarelli #else 6802d991a16SBill Pemberton static void hpwdt_check_nmi_decoding(struct pci_dev *dev) 68147bece87SThomas Mingarelli { 68234572b29Sdann frazier dev_warn(&dev->dev, "NMI decoding is disabled. " 68347bece87SThomas Mingarelli "Your kernel does not support a NMI Watchdog.\n"); 68447bece87SThomas Mingarelli } 6854a7863ccSDon Zickus #endif /* CONFIG_X86_LOCAL_APIC */ 6862ec7ed67Sdann frazier 6875efc7a62SThomas Mingarelli /* 6885efc7a62SThomas Mingarelli * dmi_find_icru 6895efc7a62SThomas Mingarelli * 6905efc7a62SThomas Mingarelli * Routine Description: 6915efc7a62SThomas Mingarelli * This function checks whether or not we are on an iCRU-based server. 6925efc7a62SThomas Mingarelli * This check is independent of architecture and needs to be made for 6935efc7a62SThomas Mingarelli * any ProLiant system. 6945efc7a62SThomas Mingarelli */ 6952d991a16SBill Pemberton static void dmi_find_icru(const struct dmi_header *dm, void *dummy) 6965efc7a62SThomas Mingarelli { 6975efc7a62SThomas Mingarelli struct smbios_proliant_info *smbios_proliant_ptr; 6985efc7a62SThomas Mingarelli 6995efc7a62SThomas Mingarelli if (dm->type == SMBIOS_ICRU_INFORMATION) { 7005efc7a62SThomas Mingarelli smbios_proliant_ptr = (struct smbios_proliant_info *) dm; 7015efc7a62SThomas Mingarelli if (smbios_proliant_ptr->misc_features & 0x01) 7025efc7a62SThomas Mingarelli is_icru = 1; 703cce78da7SMingarelli, Thomas if (smbios_proliant_ptr->misc_features & 0x408) 704cce78da7SMingarelli, Thomas is_uefi = 1; 7055efc7a62SThomas Mingarelli } 7065efc7a62SThomas Mingarelli } 7075efc7a62SThomas Mingarelli 7082d991a16SBill Pemberton static int hpwdt_init_nmi_decoding(struct pci_dev *dev) 7092ec7ed67Sdann frazier { 7102ec7ed67Sdann frazier int retval; 7112ec7ed67Sdann frazier 7122ec7ed67Sdann frazier /* 7135efc7a62SThomas Mingarelli * On typical CRU-based systems we need to map that service in 7145efc7a62SThomas Mingarelli * the BIOS. For 32 bit Operating Systems we need to go through 7155efc7a62SThomas Mingarelli * the 32 Bit BIOS Service Directory. For 64 bit Operating 7165efc7a62SThomas Mingarelli * Systems we get that service through SMBIOS. 7175efc7a62SThomas Mingarelli * 7185efc7a62SThomas Mingarelli * On systems that support the new iCRU service all we need to 7195efc7a62SThomas Mingarelli * do is call dmi_walk to get the supported flag value and skip 7205efc7a62SThomas Mingarelli * the old cru detect code. 7215efc7a62SThomas Mingarelli */ 7225efc7a62SThomas Mingarelli dmi_walk(dmi_find_icru, NULL); 723cce78da7SMingarelli, Thomas if (!is_icru && !is_uefi) { 7245efc7a62SThomas Mingarelli 7255efc7a62SThomas Mingarelli /* 7262ec7ed67Sdann frazier * We need to map the ROM to get the CRU service. 7272ec7ed67Sdann frazier * For 32 bit Operating Systems we need to go through the 32 Bit 7282ec7ed67Sdann frazier * BIOS Service Directory 7292ec7ed67Sdann frazier * For 64 bit Operating Systems we get that service through SMBIOS. 7302ec7ed67Sdann frazier */ 7312ec7ed67Sdann frazier retval = detect_cru_service(); 7322ec7ed67Sdann frazier if (retval < 0) { 7332ec7ed67Sdann frazier dev_warn(&dev->dev, 7342ec7ed67Sdann frazier "Unable to detect the %d Bit CRU Service.\n", 7352ec7ed67Sdann frazier HPWDT_ARCH); 7362ec7ed67Sdann frazier return retval; 7372ec7ed67Sdann frazier } 7382ec7ed67Sdann frazier 7392ec7ed67Sdann frazier /* 7402ec7ed67Sdann frazier * We know this is the only CRU call we need to make so lets keep as 7412ec7ed67Sdann frazier * few instructions as possible once the NMI comes in. 7422ec7ed67Sdann frazier */ 7432ec7ed67Sdann frazier cmn_regs.u1.rah = 0x0D; 7442ec7ed67Sdann frazier cmn_regs.u1.ral = 0x02; 7455efc7a62SThomas Mingarelli } 7462ec7ed67Sdann frazier 7472ec7ed67Sdann frazier /* 74809ee1014SDon Zickus * Only one function can register for NMI_UNKNOWN 7492ec7ed67Sdann frazier */ 75009ee1014SDon Zickus retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, 0, "hpwdt"); 751553222f3SDon Zickus if (retval) 752553222f3SDon Zickus goto error; 753553222f3SDon Zickus retval = register_nmi_handler(NMI_SERR, hpwdt_pretimeout, 0, "hpwdt"); 754553222f3SDon Zickus if (retval) 755553222f3SDon Zickus goto error1; 756553222f3SDon Zickus retval = register_nmi_handler(NMI_IO_CHECK, hpwdt_pretimeout, 0, "hpwdt"); 757553222f3SDon Zickus if (retval) 758553222f3SDon Zickus goto error2; 7592ec7ed67Sdann frazier 7602ec7ed67Sdann frazier dev_info(&dev->dev, 7612ec7ed67Sdann frazier "HP Watchdog Timer Driver: NMI decoding initialized" 762b91b5be5SMasanari Iida ", allow kernel dump: %s (default = 1/ON)\n", 76309ee1014SDon Zickus (allow_kdump == 0) ? "OFF" : "ON"); 7642ec7ed67Sdann frazier return 0; 765553222f3SDon Zickus 766553222f3SDon Zickus error2: 767553222f3SDon Zickus unregister_nmi_handler(NMI_SERR, "hpwdt"); 768553222f3SDon Zickus error1: 769553222f3SDon Zickus unregister_nmi_handler(NMI_UNKNOWN, "hpwdt"); 770553222f3SDon Zickus error: 7712ec7ed67Sdann frazier dev_warn(&dev->dev, 7722ec7ed67Sdann frazier "Unable to register a die notifier (err=%d).\n", 7732ec7ed67Sdann frazier retval); 7742ec7ed67Sdann frazier if (cru_rom_addr) 7752ec7ed67Sdann frazier iounmap(cru_rom_addr); 776553222f3SDon Zickus return retval; 7772ec7ed67Sdann frazier } 7782ec7ed67Sdann frazier 779b77b7088SAxel Lin static void hpwdt_exit_nmi_decoding(void) 7802ec7ed67Sdann frazier { 7819c48f1c6SDon Zickus unregister_nmi_handler(NMI_UNKNOWN, "hpwdt"); 782a089361cSMingarelli, Thomas unregister_nmi_handler(NMI_SERR, "hpwdt"); 783a089361cSMingarelli, Thomas unregister_nmi_handler(NMI_IO_CHECK, "hpwdt"); 7842ec7ed67Sdann frazier if (cru_rom_addr) 7852ec7ed67Sdann frazier iounmap(cru_rom_addr); 7862ec7ed67Sdann frazier } 78786ded1f3Sdann frazier #else /* !CONFIG_HPWDT_NMI_DECODING */ 7882d991a16SBill Pemberton static void hpwdt_check_nmi_decoding(struct pci_dev *dev) 78986ded1f3Sdann frazier { 79086ded1f3Sdann frazier } 79186ded1f3Sdann frazier 7922d991a16SBill Pemberton static int hpwdt_init_nmi_decoding(struct pci_dev *dev) 79386ded1f3Sdann frazier { 79486ded1f3Sdann frazier return 0; 79586ded1f3Sdann frazier } 79686ded1f3Sdann frazier 797b77b7088SAxel Lin static void hpwdt_exit_nmi_decoding(void) 79886ded1f3Sdann frazier { 79986ded1f3Sdann frazier } 80086ded1f3Sdann frazier #endif /* CONFIG_HPWDT_NMI_DECODING */ 80147bece87SThomas Mingarelli 8022d991a16SBill Pemberton static int hpwdt_init_one(struct pci_dev *dev, 8037f4da474SThomas Mingarelli const struct pci_device_id *ent) 8047f4da474SThomas Mingarelli { 8057f4da474SThomas Mingarelli int retval; 8067f4da474SThomas Mingarelli 8077f4da474SThomas Mingarelli /* 80834572b29Sdann frazier * Check if we can do NMI decoding or not 80947bece87SThomas Mingarelli */ 81034572b29Sdann frazier hpwdt_check_nmi_decoding(dev); 81147bece87SThomas Mingarelli 81247bece87SThomas Mingarelli /* 81336e3ff44Sdann frazier * First let's find out if we are on an iLO2+ server. We will 8147f4da474SThomas Mingarelli * not run on a legacy ASM box. 815ab4ba3cdSThomas Mingarelli * So we only support the G5 ProLiant servers and higher. 8167f4da474SThomas Mingarelli */ 8177f4da474SThomas Mingarelli if (dev->subsystem_vendor != PCI_VENDOR_ID_HP) { 8187f4da474SThomas Mingarelli dev_warn(&dev->dev, 81936e3ff44Sdann frazier "This server does not have an iLO2+ ASIC.\n"); 8207f4da474SThomas Mingarelli return -ENODEV; 8217f4da474SThomas Mingarelli } 8227f4da474SThomas Mingarelli 8230821f20dSMingarelli, Thomas /* 8240821f20dSMingarelli, Thomas * Ignore all auxilary iLO devices with the following PCI ID 8250821f20dSMingarelli, Thomas */ 8260821f20dSMingarelli, Thomas if (dev->subsystem_device == 0x1979) 8270821f20dSMingarelli, Thomas return -ENODEV; 8280821f20dSMingarelli, Thomas 8297f4da474SThomas Mingarelli if (pci_enable_device(dev)) { 8307f4da474SThomas Mingarelli dev_warn(&dev->dev, 8317f4da474SThomas Mingarelli "Not possible to enable PCI Device: 0x%x:0x%x.\n", 8327f4da474SThomas Mingarelli ent->vendor, ent->device); 8337f4da474SThomas Mingarelli return -ENODEV; 8347f4da474SThomas Mingarelli } 8357f4da474SThomas Mingarelli 8367f4da474SThomas Mingarelli pci_mem_addr = pci_iomap(dev, 1, 0x80); 8377f4da474SThomas Mingarelli if (!pci_mem_addr) { 8387f4da474SThomas Mingarelli dev_warn(&dev->dev, 83936e3ff44Sdann frazier "Unable to detect the iLO2+ server memory.\n"); 8407f4da474SThomas Mingarelli retval = -ENOMEM; 8417f4da474SThomas Mingarelli goto error_pci_iomap; 8427f4da474SThomas Mingarelli } 8437f4da474SThomas Mingarelli hpwdt_timer_reg = pci_mem_addr + 0x70; 8447f4da474SThomas Mingarelli hpwdt_timer_con = pci_mem_addr + 0x72; 8457f4da474SThomas Mingarelli 846308b135eSToshi Kani /* Make sure that timer is disabled until /dev/watchdog is opened */ 847308b135eSToshi Kani hpwdt_stop(); 848308b135eSToshi Kani 8497f4da474SThomas Mingarelli /* Make sure that we have a valid soft_margin */ 8507f4da474SThomas Mingarelli if (hpwdt_change_timer(soft_margin)) 8517f4da474SThomas Mingarelli hpwdt_change_timer(DEFAULT_MARGIN); 8527f4da474SThomas Mingarelli 8532ec7ed67Sdann frazier /* Initialize NMI Decoding functionality */ 8542ec7ed67Sdann frazier retval = hpwdt_init_nmi_decoding(dev); 8552ec7ed67Sdann frazier if (retval != 0) 8562ec7ed67Sdann frazier goto error_init_nmi_decoding; 8577f4da474SThomas Mingarelli 8587f4da474SThomas Mingarelli retval = misc_register(&hpwdt_miscdev); 8597f4da474SThomas Mingarelli if (retval < 0) { 8607f4da474SThomas Mingarelli dev_warn(&dev->dev, 8617f4da474SThomas Mingarelli "Unable to register miscdev on minor=%d (err=%d).\n", 8627f4da474SThomas Mingarelli WATCHDOG_MINOR, retval); 8637f4da474SThomas Mingarelli goto error_misc_register; 8647f4da474SThomas Mingarelli } 8657f4da474SThomas Mingarelli 8662ec7ed67Sdann frazier dev_info(&dev->dev, "HP Watchdog Timer Driver: %s" 8672ec7ed67Sdann frazier ", timer margin: %d seconds (nowayout=%d).\n", 8682ec7ed67Sdann frazier HPWDT_VERSION, soft_margin, nowayout); 8697f4da474SThomas Mingarelli return 0; 8707f4da474SThomas Mingarelli 8717f4da474SThomas Mingarelli error_misc_register: 8722ec7ed67Sdann frazier hpwdt_exit_nmi_decoding(); 8732ec7ed67Sdann frazier error_init_nmi_decoding: 8747f4da474SThomas Mingarelli pci_iounmap(dev, pci_mem_addr); 8757f4da474SThomas Mingarelli error_pci_iomap: 8767f4da474SThomas Mingarelli pci_disable_device(dev); 8777f4da474SThomas Mingarelli return retval; 8787f4da474SThomas Mingarelli } 8797f4da474SThomas Mingarelli 8804b12b896SBill Pemberton static void hpwdt_exit(struct pci_dev *dev) 8817f4da474SThomas Mingarelli { 8827f4da474SThomas Mingarelli if (!nowayout) 8837f4da474SThomas Mingarelli hpwdt_stop(); 8847f4da474SThomas Mingarelli 8857f4da474SThomas Mingarelli misc_deregister(&hpwdt_miscdev); 8862ec7ed67Sdann frazier hpwdt_exit_nmi_decoding(); 8877f4da474SThomas Mingarelli pci_iounmap(dev, pci_mem_addr); 8887f4da474SThomas Mingarelli pci_disable_device(dev); 8897f4da474SThomas Mingarelli } 8907f4da474SThomas Mingarelli 8917f4da474SThomas Mingarelli static struct pci_driver hpwdt_driver = { 8927f4da474SThomas Mingarelli .name = "hpwdt", 8937f4da474SThomas Mingarelli .id_table = hpwdt_devices, 8947f4da474SThomas Mingarelli .probe = hpwdt_init_one, 89582268714SBill Pemberton .remove = hpwdt_exit, 8967f4da474SThomas Mingarelli }; 8977f4da474SThomas Mingarelli 8987f4da474SThomas Mingarelli MODULE_AUTHOR("Tom Mingarelli"); 8997f4da474SThomas Mingarelli MODULE_DESCRIPTION("hp watchdog driver"); 9007f4da474SThomas Mingarelli MODULE_LICENSE("GPL"); 901d8100c3aSThomas Mingarelli MODULE_VERSION(HPWDT_VERSION); 9027f4da474SThomas Mingarelli 9037f4da474SThomas Mingarelli module_param(soft_margin, int, 0); 9047f4da474SThomas Mingarelli MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds"); 9057f4da474SThomas Mingarelli 90686a1e189SWim Van Sebroeck module_param(nowayout, bool, 0); 9077f4da474SThomas Mingarelli MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 9087f4da474SThomas Mingarelli __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 9097f4da474SThomas Mingarelli 91086ded1f3Sdann frazier #ifdef CONFIG_HPWDT_NMI_DECODING 911550d299eSdann frazier module_param(allow_kdump, int, 0); 912550d299eSdann frazier MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs"); 91386ded1f3Sdann frazier #endif /* !CONFIG_HPWDT_NMI_DECODING */ 91444df7535STom Mingarelli 9155ce9c371SWim Van Sebroeck module_pci_driver(hpwdt_driver); 916