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/init.h> 217f4da474SThomas Mingarelli #include <linux/io.h> 22a52e6d18Sdann frazier #include <linux/bitops.h> 237f4da474SThomas Mingarelli #include <linux/kernel.h> 247f4da474SThomas Mingarelli #include <linux/miscdevice.h> 257f4da474SThomas Mingarelli #include <linux/module.h> 267f4da474SThomas Mingarelli #include <linux/moduleparam.h> 277f4da474SThomas Mingarelli #include <linux/pci.h> 287f4da474SThomas Mingarelli #include <linux/pci_ids.h> 297f4da474SThomas Mingarelli #include <linux/types.h> 307f4da474SThomas Mingarelli #include <linux/uaccess.h> 317f4da474SThomas Mingarelli #include <linux/watchdog.h> 3286ded1f3Sdann frazier #ifdef CONFIG_HPWDT_NMI_DECODING 337f4da474SThomas Mingarelli #include <linux/dmi.h> 34a52e6d18Sdann frazier #include <linux/spinlock.h> 35923410d0Sdann frazier #include <linux/nmi.h> 36923410d0Sdann frazier #include <linux/kdebug.h> 37923410d0Sdann frazier #include <linux/notifier.h> 3806026413SBernhard Walle #include <asm/cacheflush.h> 3986ded1f3Sdann frazier #endif /* CONFIG_HPWDT_NMI_DECODING */ 40d48b0e17SIngo Molnar #include <asm/nmi.h> 417f4da474SThomas Mingarelli 425efc7a62SThomas Mingarelli #define HPWDT_VERSION "1.3.0" 43e802e32dSdann frazier #define SECS_TO_TICKS(secs) ((secs) * 1000 / 128) 446f681c2eSdann frazier #define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000) 456f681c2eSdann frazier #define HPWDT_MAX_TIMER TICKS_TO_SECS(65535) 46923410d0Sdann frazier #define DEFAULT_MARGIN 30 47923410d0Sdann frazier 48923410d0Sdann frazier static unsigned int soft_margin = DEFAULT_MARGIN; /* in seconds */ 49923410d0Sdann frazier static unsigned int reload; /* the computed soft_margin */ 5086a1e189SWim Van Sebroeck static bool nowayout = WATCHDOG_NOWAYOUT; 51923410d0Sdann frazier static char expect_release; 52923410d0Sdann frazier static unsigned long hpwdt_is_open; 53923410d0Sdann frazier 54923410d0Sdann frazier static void __iomem *pci_mem_addr; /* the PCI-memory address */ 55923410d0Sdann frazier static unsigned long __iomem *hpwdt_timer_reg; 56923410d0Sdann frazier static unsigned long __iomem *hpwdt_timer_con; 57923410d0Sdann frazier 584562f539SWim Van Sebroeck static DEFINE_PCI_DEVICE_TABLE(hpwdt_devices) = { 5936e3ff44Sdann frazier { PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB203) }, /* iLO2 */ 6036e3ff44Sdann frazier { PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3306) }, /* iLO3 */ 61923410d0Sdann frazier {0}, /* terminate list */ 62923410d0Sdann frazier }; 63923410d0Sdann frazier MODULE_DEVICE_TABLE(pci, hpwdt_devices); 64923410d0Sdann frazier 6586ded1f3Sdann frazier #ifdef CONFIG_HPWDT_NMI_DECODING 667f4da474SThomas Mingarelli #define PCI_BIOS32_SD_VALUE 0x5F32335F /* "_32_" */ 677f4da474SThomas Mingarelli #define CRU_BIOS_SIGNATURE_VALUE 0x55524324 687f4da474SThomas Mingarelli #define PCI_BIOS32_PARAGRAPH_LEN 16 697f4da474SThomas Mingarelli #define PCI_ROM_BASE1 0x000F0000 707f4da474SThomas Mingarelli #define ROM_SIZE 0x10000 717f4da474SThomas Mingarelli 727f4da474SThomas Mingarelli struct bios32_service_dir { 737f4da474SThomas Mingarelli u32 signature; 747f4da474SThomas Mingarelli u32 entry_point; 757f4da474SThomas Mingarelli u8 revision; 767f4da474SThomas Mingarelli u8 length; 777f4da474SThomas Mingarelli u8 checksum; 787f4da474SThomas Mingarelli u8 reserved[5]; 797f4da474SThomas Mingarelli }; 807f4da474SThomas Mingarelli 817f4da474SThomas Mingarelli /* type 212 */ 827f4da474SThomas Mingarelli struct smbios_cru64_info { 837f4da474SThomas Mingarelli u8 type; 847f4da474SThomas Mingarelli u8 byte_length; 857f4da474SThomas Mingarelli u16 handle; 867f4da474SThomas Mingarelli u32 signature; 877f4da474SThomas Mingarelli u64 physical_address; 887f4da474SThomas Mingarelli u32 double_length; 897f4da474SThomas Mingarelli u32 double_offset; 907f4da474SThomas Mingarelli }; 917f4da474SThomas Mingarelli #define SMBIOS_CRU64_INFORMATION 212 927f4da474SThomas Mingarelli 935efc7a62SThomas Mingarelli /* type 219 */ 945efc7a62SThomas Mingarelli struct smbios_proliant_info { 955efc7a62SThomas Mingarelli u8 type; 965efc7a62SThomas Mingarelli u8 byte_length; 975efc7a62SThomas Mingarelli u16 handle; 985efc7a62SThomas Mingarelli u32 power_features; 995efc7a62SThomas Mingarelli u32 omega_features; 1005efc7a62SThomas Mingarelli u32 reserved; 1015efc7a62SThomas Mingarelli u32 misc_features; 1025efc7a62SThomas Mingarelli }; 1035efc7a62SThomas Mingarelli #define SMBIOS_ICRU_INFORMATION 219 1045efc7a62SThomas Mingarelli 1055efc7a62SThomas Mingarelli 1067f4da474SThomas Mingarelli struct cmn_registers { 1077f4da474SThomas Mingarelli union { 1087f4da474SThomas Mingarelli struct { 1097f4da474SThomas Mingarelli u8 ral; 1107f4da474SThomas Mingarelli u8 rah; 1117f4da474SThomas Mingarelli u16 rea2; 1127f4da474SThomas Mingarelli }; 1137f4da474SThomas Mingarelli u32 reax; 1147f4da474SThomas Mingarelli } u1; 1157f4da474SThomas Mingarelli union { 1167f4da474SThomas Mingarelli struct { 1177f4da474SThomas Mingarelli u8 rbl; 1187f4da474SThomas Mingarelli u8 rbh; 1197f4da474SThomas Mingarelli u8 reb2l; 1207f4da474SThomas Mingarelli u8 reb2h; 1217f4da474SThomas Mingarelli }; 1227f4da474SThomas Mingarelli u32 rebx; 1237f4da474SThomas Mingarelli } u2; 1247f4da474SThomas Mingarelli union { 1257f4da474SThomas Mingarelli struct { 1267f4da474SThomas Mingarelli u8 rcl; 1277f4da474SThomas Mingarelli u8 rch; 1287f4da474SThomas Mingarelli u16 rec2; 1297f4da474SThomas Mingarelli }; 1307f4da474SThomas Mingarelli u32 recx; 1317f4da474SThomas Mingarelli } u3; 1327f4da474SThomas Mingarelli union { 1337f4da474SThomas Mingarelli struct { 1347f4da474SThomas Mingarelli u8 rdl; 1357f4da474SThomas Mingarelli u8 rdh; 1367f4da474SThomas Mingarelli u16 red2; 1377f4da474SThomas Mingarelli }; 1387f4da474SThomas Mingarelli u32 redx; 1397f4da474SThomas Mingarelli } u4; 1407f4da474SThomas Mingarelli 1417f4da474SThomas Mingarelli u32 resi; 1427f4da474SThomas Mingarelli u32 redi; 1437f4da474SThomas Mingarelli u16 rds; 1447f4da474SThomas Mingarelli u16 res; 1457f4da474SThomas Mingarelli u32 reflags; 1467f4da474SThomas Mingarelli } __attribute__((packed)); 1477f4da474SThomas Mingarelli 14834572b29Sdann frazier static unsigned int hpwdt_nmi_decoding; 149a089361cSMingarelli, Thomas static unsigned int allow_kdump = 1; 1505efc7a62SThomas Mingarelli static unsigned int is_icru; 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" 1641f6ef234SLinus Torvalds ".align 4 \n" 1651f6ef234SLinus Torvalds "asminline_call: \n\t" 1661f6ef234SLinus Torvalds "pushl %ebp \n\t" 1677f4da474SThomas Mingarelli "movl %esp, %ebp \n\t" 1687f4da474SThomas Mingarelli "pusha \n\t" 1697f4da474SThomas Mingarelli "pushf \n\t" 1707f4da474SThomas Mingarelli "push %es \n\t" 1717f4da474SThomas Mingarelli "push %ds \n\t" 1727f4da474SThomas Mingarelli "pop %es \n\t" 1737f4da474SThomas Mingarelli "movl 8(%ebp),%eax \n\t" 1747f4da474SThomas Mingarelli "movl 4(%eax),%ebx \n\t" 1757f4da474SThomas Mingarelli "movl 8(%eax),%ecx \n\t" 1767f4da474SThomas Mingarelli "movl 12(%eax),%edx \n\t" 1777f4da474SThomas Mingarelli "movl 16(%eax),%esi \n\t" 1787f4da474SThomas Mingarelli "movl 20(%eax),%edi \n\t" 1797f4da474SThomas Mingarelli "movl (%eax),%eax \n\t" 1807f4da474SThomas Mingarelli "push %cs \n\t" 1817f4da474SThomas Mingarelli "call *12(%ebp) \n\t" 1827f4da474SThomas Mingarelli "pushf \n\t" 1837f4da474SThomas Mingarelli "pushl %eax \n\t" 1847f4da474SThomas Mingarelli "movl 8(%ebp),%eax \n\t" 1857f4da474SThomas Mingarelli "movl %ebx,4(%eax) \n\t" 1867f4da474SThomas Mingarelli "movl %ecx,8(%eax) \n\t" 1877f4da474SThomas Mingarelli "movl %edx,12(%eax) \n\t" 1887f4da474SThomas Mingarelli "movl %esi,16(%eax) \n\t" 1897f4da474SThomas Mingarelli "movl %edi,20(%eax) \n\t" 1907f4da474SThomas Mingarelli "movw %ds,24(%eax) \n\t" 1917f4da474SThomas Mingarelli "movw %es,26(%eax) \n\t" 1927f4da474SThomas Mingarelli "popl %ebx \n\t" 1937f4da474SThomas Mingarelli "movl %ebx,(%eax) \n\t" 1947f4da474SThomas Mingarelli "popl %ebx \n\t" 1957f4da474SThomas Mingarelli "movl %ebx,28(%eax) \n\t" 1967f4da474SThomas Mingarelli "pop %es \n\t" 1977f4da474SThomas Mingarelli "popf \n\t" 1987f4da474SThomas Mingarelli "popa \n\t" 1991f6ef234SLinus Torvalds "leave \n\t" 2001f6ef234SLinus Torvalds "ret \n\t" 2011f6ef234SLinus Torvalds ".previous"); 2021f6ef234SLinus Torvalds 2037f4da474SThomas Mingarelli 2047f4da474SThomas Mingarelli /* 2057f4da474SThomas Mingarelli * cru_detect 2067f4da474SThomas Mingarelli * 2077f4da474SThomas Mingarelli * Routine Description: 2087f4da474SThomas Mingarelli * This function uses the 32-bit BIOS Service Directory record to 2097f4da474SThomas Mingarelli * search for a $CRU record. 2107f4da474SThomas Mingarelli * 2117f4da474SThomas Mingarelli * Return Value: 2127f4da474SThomas Mingarelli * 0 : SUCCESS 2137f4da474SThomas Mingarelli * <0 : FAILURE 2147f4da474SThomas Mingarelli */ 2157f4da474SThomas Mingarelli static int __devinit cru_detect(unsigned long map_entry, 2167f4da474SThomas Mingarelli unsigned long map_offset) 2177f4da474SThomas Mingarelli { 2187f4da474SThomas Mingarelli void *bios32_map; 2197f4da474SThomas Mingarelli unsigned long *bios32_entrypoint; 2207f4da474SThomas Mingarelli unsigned long cru_physical_address; 2217f4da474SThomas Mingarelli unsigned long cru_length; 2227f4da474SThomas Mingarelli unsigned long physical_bios_base = 0; 2237f4da474SThomas Mingarelli unsigned long physical_bios_offset = 0; 2247f4da474SThomas Mingarelli int retval = -ENODEV; 2257f4da474SThomas Mingarelli 2267f4da474SThomas Mingarelli bios32_map = ioremap(map_entry, (2 * PAGE_SIZE)); 2277f4da474SThomas Mingarelli 2287f4da474SThomas Mingarelli if (bios32_map == NULL) 2297f4da474SThomas Mingarelli return -ENODEV; 2307f4da474SThomas Mingarelli 2317f4da474SThomas Mingarelli bios32_entrypoint = bios32_map + map_offset; 2327f4da474SThomas Mingarelli 2337f4da474SThomas Mingarelli cmn_regs.u1.reax = CRU_BIOS_SIGNATURE_VALUE; 2347f4da474SThomas Mingarelli 23597d2a10dSMaxim Uvarov set_memory_x((unsigned long)bios32_map, 2); 2367f4da474SThomas Mingarelli asminline_call(&cmn_regs, bios32_entrypoint); 2377f4da474SThomas Mingarelli 2387f4da474SThomas Mingarelli if (cmn_regs.u1.ral != 0) { 23927c766aaSJoe Perches pr_warn("Call succeeded but with an error: 0x%x\n", 2407f4da474SThomas Mingarelli cmn_regs.u1.ral); 2417f4da474SThomas Mingarelli } else { 2427f4da474SThomas Mingarelli physical_bios_base = cmn_regs.u2.rebx; 2437f4da474SThomas Mingarelli physical_bios_offset = cmn_regs.u4.redx; 2447f4da474SThomas Mingarelli cru_length = cmn_regs.u3.recx; 2457f4da474SThomas Mingarelli cru_physical_address = 2467f4da474SThomas Mingarelli physical_bios_base + physical_bios_offset; 2477f4da474SThomas Mingarelli 2487f4da474SThomas Mingarelli /* If the values look OK, then map it in. */ 2497f4da474SThomas Mingarelli if ((physical_bios_base + physical_bios_offset)) { 2507f4da474SThomas Mingarelli cru_rom_addr = 2517f4da474SThomas Mingarelli ioremap(cru_physical_address, cru_length); 252e67d668eSMingarelli, Thomas if (cru_rom_addr) { 25397d2a10dSMaxim Uvarov set_memory_x((unsigned long)cru_rom_addr & PAGE_MASK, 25497d2a10dSMaxim Uvarov (cru_length + PAGE_SIZE - 1) >> PAGE_SHIFT); 2557f4da474SThomas Mingarelli retval = 0; 2567f4da474SThomas Mingarelli } 257e67d668eSMingarelli, Thomas } 2587f4da474SThomas Mingarelli 25927c766aaSJoe Perches pr_debug("CRU Base Address: 0x%lx\n", physical_bios_base); 26027c766aaSJoe Perches pr_debug("CRU Offset Address: 0x%lx\n", physical_bios_offset); 26127c766aaSJoe Perches pr_debug("CRU Length: 0x%lx\n", cru_length); 26227c766aaSJoe Perches pr_debug("CRU Mapped Address: %p\n", &cru_rom_addr); 2637f4da474SThomas Mingarelli } 2647f4da474SThomas Mingarelli iounmap(bios32_map); 2657f4da474SThomas Mingarelli return retval; 2667f4da474SThomas Mingarelli } 2677f4da474SThomas Mingarelli 2687f4da474SThomas Mingarelli /* 26930ec910eSRoland Dreier * bios_checksum 27030ec910eSRoland Dreier */ 27130ec910eSRoland Dreier static int __devinit bios_checksum(const char __iomem *ptr, int len) 27230ec910eSRoland Dreier { 27330ec910eSRoland Dreier char sum = 0; 27430ec910eSRoland Dreier int i; 27530ec910eSRoland Dreier 27630ec910eSRoland Dreier /* 27730ec910eSRoland Dreier * calculate checksum of size bytes. This should add up 27830ec910eSRoland Dreier * to zero if we have a valid header. 27930ec910eSRoland Dreier */ 28030ec910eSRoland Dreier for (i = 0; i < len; i++) 28130ec910eSRoland Dreier sum += ptr[i]; 28230ec910eSRoland Dreier 28330ec910eSRoland Dreier return ((sum == 0) && (len > 0)); 28430ec910eSRoland Dreier } 28530ec910eSRoland Dreier 28630ec910eSRoland Dreier /* 2877f4da474SThomas Mingarelli * bios32_present 2887f4da474SThomas Mingarelli * 2897f4da474SThomas Mingarelli * Routine Description: 2907f4da474SThomas Mingarelli * This function finds the 32-bit BIOS Service Directory 2917f4da474SThomas Mingarelli * 2927f4da474SThomas Mingarelli * Return Value: 2937f4da474SThomas Mingarelli * 0 : SUCCESS 2947f4da474SThomas Mingarelli * <0 : FAILURE 2957f4da474SThomas Mingarelli */ 2967f4da474SThomas Mingarelli static int __devinit bios32_present(const char __iomem *p) 2977f4da474SThomas Mingarelli { 2987f4da474SThomas Mingarelli struct bios32_service_dir *bios_32_ptr; 2997f4da474SThomas Mingarelli int length; 3007f4da474SThomas Mingarelli unsigned long map_entry, map_offset; 3017f4da474SThomas Mingarelli 3027f4da474SThomas Mingarelli bios_32_ptr = (struct bios32_service_dir *) p; 3037f4da474SThomas Mingarelli 3047f4da474SThomas Mingarelli /* 3057f4da474SThomas Mingarelli * Search for signature by checking equal to the swizzled value 3067f4da474SThomas Mingarelli * instead of calling another routine to perform a strcmp. 3077f4da474SThomas Mingarelli */ 3087f4da474SThomas Mingarelli if (bios_32_ptr->signature == PCI_BIOS32_SD_VALUE) { 3097f4da474SThomas Mingarelli length = bios_32_ptr->length * PCI_BIOS32_PARAGRAPH_LEN; 3107f4da474SThomas Mingarelli if (bios_checksum(p, length)) { 3117f4da474SThomas Mingarelli /* 3127f4da474SThomas Mingarelli * According to the spec, we're looking for the 3137f4da474SThomas Mingarelli * first 4KB-aligned address below the entrypoint 3147f4da474SThomas Mingarelli * listed in the header. The Service Directory code 3157f4da474SThomas Mingarelli * is guaranteed to occupy no more than 2 4KB pages. 3167f4da474SThomas Mingarelli */ 3177f4da474SThomas Mingarelli map_entry = bios_32_ptr->entry_point & ~(PAGE_SIZE - 1); 3187f4da474SThomas Mingarelli map_offset = bios_32_ptr->entry_point - map_entry; 3197f4da474SThomas Mingarelli 3207f4da474SThomas Mingarelli return cru_detect(map_entry, map_offset); 3217f4da474SThomas Mingarelli } 3227f4da474SThomas Mingarelli } 3237f4da474SThomas Mingarelli return -ENODEV; 3247f4da474SThomas Mingarelli } 3257f4da474SThomas Mingarelli 3267f4da474SThomas Mingarelli static int __devinit detect_cru_service(void) 3277f4da474SThomas Mingarelli { 3287f4da474SThomas Mingarelli char __iomem *p, *q; 3297f4da474SThomas Mingarelli int rc = -1; 3307f4da474SThomas Mingarelli 3317f4da474SThomas Mingarelli /* 3327f4da474SThomas Mingarelli * Search from 0x0f0000 through 0x0fffff, inclusive. 3337f4da474SThomas Mingarelli */ 3347f4da474SThomas Mingarelli p = ioremap(PCI_ROM_BASE1, ROM_SIZE); 3357f4da474SThomas Mingarelli if (p == NULL) 3367f4da474SThomas Mingarelli return -ENOMEM; 3377f4da474SThomas Mingarelli 3387f4da474SThomas Mingarelli for (q = p; q < p + ROM_SIZE; q += 16) { 3397f4da474SThomas Mingarelli rc = bios32_present(q); 3407f4da474SThomas Mingarelli if (!rc) 3417f4da474SThomas Mingarelli break; 3427f4da474SThomas Mingarelli } 3437f4da474SThomas Mingarelli iounmap(p); 3447f4da474SThomas Mingarelli return rc; 3457f4da474SThomas Mingarelli } 3466b7f3d53Sdann frazier /* ------------------------------------------------------------------------- */ 3476b7f3d53Sdann frazier #endif /* CONFIG_X86_32 */ 3486b7f3d53Sdann frazier #ifdef CONFIG_X86_64 3497f4da474SThomas Mingarelli /* --64 Bit Bios------------------------------------------------------------ */ 3507f4da474SThomas Mingarelli 3517f4da474SThomas Mingarelli #define HPWDT_ARCH 64 3527f4da474SThomas Mingarelli 3531f6ef234SLinus Torvalds asm(".text \n\t" 3541f6ef234SLinus Torvalds ".align 4 \n" 3551f6ef234SLinus Torvalds "asminline_call: \n\t" 3561f6ef234SLinus Torvalds "pushq %rbp \n\t" 3577f4da474SThomas Mingarelli "movq %rsp, %rbp \n\t" 3587f4da474SThomas Mingarelli "pushq %rax \n\t" 3597f4da474SThomas Mingarelli "pushq %rbx \n\t" 3607f4da474SThomas Mingarelli "pushq %rdx \n\t" 3617f4da474SThomas Mingarelli "pushq %r12 \n\t" 3627f4da474SThomas Mingarelli "pushq %r9 \n\t" 3637f4da474SThomas Mingarelli "movq %rsi, %r12 \n\t" 3647f4da474SThomas Mingarelli "movq %rdi, %r9 \n\t" 3657f4da474SThomas Mingarelli "movl 4(%r9),%ebx \n\t" 3667f4da474SThomas Mingarelli "movl 8(%r9),%ecx \n\t" 3677f4da474SThomas Mingarelli "movl 12(%r9),%edx \n\t" 3687f4da474SThomas Mingarelli "movl 16(%r9),%esi \n\t" 3697f4da474SThomas Mingarelli "movl 20(%r9),%edi \n\t" 3707f4da474SThomas Mingarelli "movl (%r9),%eax \n\t" 3717f4da474SThomas Mingarelli "call *%r12 \n\t" 3727f4da474SThomas Mingarelli "pushfq \n\t" 3737f4da474SThomas Mingarelli "popq %r12 \n\t" 3747f4da474SThomas Mingarelli "movl %eax, (%r9) \n\t" 3757f4da474SThomas Mingarelli "movl %ebx, 4(%r9) \n\t" 3767f4da474SThomas Mingarelli "movl %ecx, 8(%r9) \n\t" 3777f4da474SThomas Mingarelli "movl %edx, 12(%r9) \n\t" 3787f4da474SThomas Mingarelli "movl %esi, 16(%r9) \n\t" 3797f4da474SThomas Mingarelli "movl %edi, 20(%r9) \n\t" 3807f4da474SThomas Mingarelli "movq %r12, %rax \n\t" 3817f4da474SThomas Mingarelli "movl %eax, 28(%r9) \n\t" 3827f4da474SThomas Mingarelli "popq %r9 \n\t" 3837f4da474SThomas Mingarelli "popq %r12 \n\t" 3847f4da474SThomas Mingarelli "popq %rdx \n\t" 3857f4da474SThomas Mingarelli "popq %rbx \n\t" 3867f4da474SThomas Mingarelli "popq %rax \n\t" 3871f6ef234SLinus Torvalds "leave \n\t" 3881f6ef234SLinus Torvalds "ret \n\t" 3891f6ef234SLinus Torvalds ".previous"); 3907f4da474SThomas Mingarelli 3917f4da474SThomas Mingarelli /* 3927f4da474SThomas Mingarelli * dmi_find_cru 3937f4da474SThomas Mingarelli * 3947f4da474SThomas Mingarelli * Routine Description: 39530ec910eSRoland Dreier * This function checks whether or not a SMBIOS/DMI record is 3967f4da474SThomas Mingarelli * the 64bit CRU info or not 3977f4da474SThomas Mingarelli */ 398e7a19c56SJean Delvare static void __devinit dmi_find_cru(const struct dmi_header *dm, void *dummy) 3997f4da474SThomas Mingarelli { 4007f4da474SThomas Mingarelli struct smbios_cru64_info *smbios_cru64_ptr; 4017f4da474SThomas Mingarelli unsigned long cru_physical_address; 4027f4da474SThomas Mingarelli 4037f4da474SThomas Mingarelli if (dm->type == SMBIOS_CRU64_INFORMATION) { 4047f4da474SThomas Mingarelli smbios_cru64_ptr = (struct smbios_cru64_info *) dm; 4057f4da474SThomas Mingarelli if (smbios_cru64_ptr->signature == CRU_BIOS_SIGNATURE_VALUE) { 4067f4da474SThomas Mingarelli cru_physical_address = 4077f4da474SThomas Mingarelli smbios_cru64_ptr->physical_address + 4087f4da474SThomas Mingarelli smbios_cru64_ptr->double_offset; 4097f4da474SThomas Mingarelli cru_rom_addr = ioremap(cru_physical_address, 4107f4da474SThomas Mingarelli smbios_cru64_ptr->double_length); 41106026413SBernhard Walle set_memory_x((unsigned long)cru_rom_addr & PAGE_MASK, 41206026413SBernhard Walle smbios_cru64_ptr->double_length >> PAGE_SHIFT); 4137f4da474SThomas Mingarelli } 4147f4da474SThomas Mingarelli } 4157f4da474SThomas Mingarelli } 4167f4da474SThomas Mingarelli 4177f4da474SThomas Mingarelli static int __devinit detect_cru_service(void) 4187f4da474SThomas Mingarelli { 4197f4da474SThomas Mingarelli cru_rom_addr = NULL; 4207f4da474SThomas Mingarelli 421e7a19c56SJean Delvare dmi_walk(dmi_find_cru, NULL); 4227f4da474SThomas Mingarelli 4237f4da474SThomas Mingarelli /* if cru_rom_addr has been set then we found a CRU service */ 4247f4da474SThomas Mingarelli return ((cru_rom_addr != NULL) ? 0 : -ENODEV); 4257f4da474SThomas Mingarelli } 4267f4da474SThomas Mingarelli /* ------------------------------------------------------------------------- */ 4276b7f3d53Sdann frazier #endif /* CONFIG_X86_64 */ 42886ded1f3Sdann frazier #endif /* CONFIG_HPWDT_NMI_DECODING */ 4297f4da474SThomas Mingarelli 4307f4da474SThomas Mingarelli /* 4317f4da474SThomas Mingarelli * Watchdog operations 4327f4da474SThomas Mingarelli */ 4337f4da474SThomas Mingarelli static void hpwdt_start(void) 4347f4da474SThomas Mingarelli { 435e802e32dSdann frazier reload = SECS_TO_TICKS(soft_margin); 4367f4da474SThomas Mingarelli iowrite16(reload, hpwdt_timer_reg); 437d08c9a33SMingarelli, Thomas iowrite8(0x85, hpwdt_timer_con); 4387f4da474SThomas Mingarelli } 4397f4da474SThomas Mingarelli 4407f4da474SThomas Mingarelli static void hpwdt_stop(void) 4417f4da474SThomas Mingarelli { 4427f4da474SThomas Mingarelli unsigned long data; 4437f4da474SThomas Mingarelli 444d08c9a33SMingarelli, Thomas data = ioread8(hpwdt_timer_con); 4457f4da474SThomas Mingarelli data &= 0xFE; 446d08c9a33SMingarelli, Thomas iowrite8(data, hpwdt_timer_con); 4477f4da474SThomas Mingarelli } 4487f4da474SThomas Mingarelli 4497f4da474SThomas Mingarelli static void hpwdt_ping(void) 4507f4da474SThomas Mingarelli { 4517f4da474SThomas Mingarelli iowrite16(reload, hpwdt_timer_reg); 4527f4da474SThomas Mingarelli } 4537f4da474SThomas Mingarelli 4547f4da474SThomas Mingarelli static int hpwdt_change_timer(int new_margin) 4557f4da474SThomas Mingarelli { 4566f681c2eSdann frazier if (new_margin < 1 || new_margin > HPWDT_MAX_TIMER) { 45727c766aaSJoe Perches pr_warn("New value passed in is invalid: %d seconds\n", 4587f4da474SThomas Mingarelli new_margin); 4597f4da474SThomas Mingarelli return -EINVAL; 4607f4da474SThomas Mingarelli } 4617f4da474SThomas Mingarelli 4627f4da474SThomas Mingarelli soft_margin = new_margin; 46327c766aaSJoe Perches pr_debug("New timer passed in is %d seconds\n", new_margin); 464e802e32dSdann frazier reload = SECS_TO_TICKS(soft_margin); 4657f4da474SThomas Mingarelli 4667f4da474SThomas Mingarelli return 0; 4677f4da474SThomas Mingarelli } 4687f4da474SThomas Mingarelli 469aae67f36Sdann frazier static int hpwdt_time_left(void) 470aae67f36Sdann frazier { 471aae67f36Sdann frazier return TICKS_TO_SECS(ioread16(hpwdt_timer_reg)); 472aae67f36Sdann frazier } 473aae67f36Sdann frazier 47486ded1f3Sdann frazier #ifdef CONFIG_HPWDT_NMI_DECODING 4757f4da474SThomas Mingarelli /* 476ab4ba3cdSThomas Mingarelli * NMI Handler 477ab4ba3cdSThomas Mingarelli */ 4789c48f1c6SDon Zickus static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs) 479ab4ba3cdSThomas Mingarelli { 480ab4ba3cdSThomas Mingarelli unsigned long rom_pl; 481ab4ba3cdSThomas Mingarelli static int die_nmi_called; 482ab4ba3cdSThomas Mingarelli 48334572b29Sdann frazier if (!hpwdt_nmi_decoding) 484243066baSdann frazier goto out; 485243066baSdann frazier 486ab4ba3cdSThomas Mingarelli spin_lock_irqsave(&rom_lock, rom_pl); 4875efc7a62SThomas Mingarelli if (!die_nmi_called && !is_icru) 488ab4ba3cdSThomas Mingarelli asminline_call(&cmn_regs, cru_rom_addr); 489ab4ba3cdSThomas Mingarelli die_nmi_called = 1; 490ab4ba3cdSThomas Mingarelli spin_unlock_irqrestore(&rom_lock, rom_pl); 4915efc7a62SThomas Mingarelli 492ab4ba3cdSThomas Mingarelli if (allow_kdump) 493ab4ba3cdSThomas Mingarelli hpwdt_stop(); 494dbc018ecSNaga Chumbalkar 495dbc018ecSNaga Chumbalkar if (!is_icru) { 496dbc018ecSNaga Chumbalkar if (cmn_regs.u1.ral == 0) { 497dbc018ecSNaga Chumbalkar panic("An NMI occurred, " 498dbc018ecSNaga Chumbalkar "but unable to determine source.\n"); 499dbc018ecSNaga Chumbalkar } 500dbc018ecSNaga Chumbalkar } 501ab4ba3cdSThomas Mingarelli panic("An NMI occurred, please see the Integrated " 502ab4ba3cdSThomas Mingarelli "Management Log for details.\n"); 5035efc7a62SThomas Mingarelli 504243066baSdann frazier out: 5059c48f1c6SDon Zickus return NMI_DONE; 506ab4ba3cdSThomas Mingarelli } 50786ded1f3Sdann frazier #endif /* CONFIG_HPWDT_NMI_DECODING */ 508ab4ba3cdSThomas Mingarelli 509ab4ba3cdSThomas Mingarelli /* 5107f4da474SThomas Mingarelli * /dev/watchdog handling 5117f4da474SThomas Mingarelli */ 5127f4da474SThomas Mingarelli static int hpwdt_open(struct inode *inode, struct file *file) 5137f4da474SThomas Mingarelli { 5147f4da474SThomas Mingarelli /* /dev/watchdog can only be opened once */ 5157f4da474SThomas Mingarelli if (test_and_set_bit(0, &hpwdt_is_open)) 5167f4da474SThomas Mingarelli return -EBUSY; 5177f4da474SThomas Mingarelli 5187f4da474SThomas Mingarelli /* Start the watchdog */ 5197f4da474SThomas Mingarelli hpwdt_start(); 5207f4da474SThomas Mingarelli hpwdt_ping(); 5217f4da474SThomas Mingarelli 5227f4da474SThomas Mingarelli return nonseekable_open(inode, file); 5237f4da474SThomas Mingarelli } 5247f4da474SThomas Mingarelli 5257f4da474SThomas Mingarelli static int hpwdt_release(struct inode *inode, struct file *file) 5267f4da474SThomas Mingarelli { 5277f4da474SThomas Mingarelli /* Stop the watchdog */ 5287f4da474SThomas Mingarelli if (expect_release == 42) { 5297f4da474SThomas Mingarelli hpwdt_stop(); 5307f4da474SThomas Mingarelli } else { 53127c766aaSJoe Perches pr_crit("Unexpected close, not stopping watchdog!\n"); 5327f4da474SThomas Mingarelli hpwdt_ping(); 5337f4da474SThomas Mingarelli } 5347f4da474SThomas Mingarelli 5357f4da474SThomas Mingarelli expect_release = 0; 5367f4da474SThomas Mingarelli 5377f4da474SThomas Mingarelli /* /dev/watchdog is being closed, make sure it can be re-opened */ 5387f4da474SThomas Mingarelli clear_bit(0, &hpwdt_is_open); 5397f4da474SThomas Mingarelli 5407f4da474SThomas Mingarelli return 0; 5417f4da474SThomas Mingarelli } 5427f4da474SThomas Mingarelli 5437f4da474SThomas Mingarelli static ssize_t hpwdt_write(struct file *file, const char __user *data, 5447f4da474SThomas Mingarelli size_t len, loff_t *ppos) 5457f4da474SThomas Mingarelli { 5467f4da474SThomas Mingarelli /* See if we got the magic character 'V' and reload the timer */ 5477f4da474SThomas Mingarelli if (len) { 5487f4da474SThomas Mingarelli if (!nowayout) { 5497f4da474SThomas Mingarelli size_t i; 5507f4da474SThomas Mingarelli 5517f4da474SThomas Mingarelli /* note: just in case someone wrote the magic character 5527f4da474SThomas Mingarelli * five months ago... */ 5537f4da474SThomas Mingarelli expect_release = 0; 5547f4da474SThomas Mingarelli 5557f4da474SThomas Mingarelli /* scan to see whether or not we got the magic char. */ 5567f4da474SThomas Mingarelli for (i = 0; i != len; i++) { 5577f4da474SThomas Mingarelli char c; 5587f4da474SThomas Mingarelli if (get_user(c, data + i)) 5597f4da474SThomas Mingarelli return -EFAULT; 5607f4da474SThomas Mingarelli if (c == 'V') 5617f4da474SThomas Mingarelli expect_release = 42; 5627f4da474SThomas Mingarelli } 5637f4da474SThomas Mingarelli } 5647f4da474SThomas Mingarelli 5657f4da474SThomas Mingarelli /* someone wrote to us, we should reload the timer */ 5667f4da474SThomas Mingarelli hpwdt_ping(); 5677f4da474SThomas Mingarelli } 5687f4da474SThomas Mingarelli 5697f4da474SThomas Mingarelli return len; 5707f4da474SThomas Mingarelli } 5717f4da474SThomas Mingarelli 57242747d71SWim Van Sebroeck static const struct watchdog_info ident = { 5737f4da474SThomas Mingarelli .options = WDIOF_SETTIMEOUT | 5747f4da474SThomas Mingarelli WDIOF_KEEPALIVEPING | 5757f4da474SThomas Mingarelli WDIOF_MAGICCLOSE, 57636e3ff44Sdann frazier .identity = "HP iLO2+ HW Watchdog Timer", 5777f4da474SThomas Mingarelli }; 5787f4da474SThomas Mingarelli 5797f4da474SThomas Mingarelli static long hpwdt_ioctl(struct file *file, unsigned int cmd, 5807f4da474SThomas Mingarelli unsigned long arg) 5817f4da474SThomas Mingarelli { 5827f4da474SThomas Mingarelli void __user *argp = (void __user *)arg; 5837f4da474SThomas Mingarelli int __user *p = argp; 5847f4da474SThomas Mingarelli int new_margin; 5857f4da474SThomas Mingarelli int ret = -ENOTTY; 5867f4da474SThomas Mingarelli 5877f4da474SThomas Mingarelli switch (cmd) { 5887f4da474SThomas Mingarelli case WDIOC_GETSUPPORT: 5897f4da474SThomas Mingarelli ret = 0; 5907f4da474SThomas Mingarelli if (copy_to_user(argp, &ident, sizeof(ident))) 5917f4da474SThomas Mingarelli ret = -EFAULT; 5927f4da474SThomas Mingarelli break; 5937f4da474SThomas Mingarelli 5947f4da474SThomas Mingarelli case WDIOC_GETSTATUS: 5957f4da474SThomas Mingarelli case WDIOC_GETBOOTSTATUS: 5967f4da474SThomas Mingarelli ret = put_user(0, p); 5977f4da474SThomas Mingarelli break; 5987f4da474SThomas Mingarelli 5997f4da474SThomas Mingarelli case WDIOC_KEEPALIVE: 6007f4da474SThomas Mingarelli hpwdt_ping(); 6017f4da474SThomas Mingarelli ret = 0; 6027f4da474SThomas Mingarelli break; 6037f4da474SThomas Mingarelli 6047f4da474SThomas Mingarelli case WDIOC_SETTIMEOUT: 6057f4da474SThomas Mingarelli ret = get_user(new_margin, p); 6067f4da474SThomas Mingarelli if (ret) 6077f4da474SThomas Mingarelli break; 6087f4da474SThomas Mingarelli 6097f4da474SThomas Mingarelli ret = hpwdt_change_timer(new_margin); 6107f4da474SThomas Mingarelli if (ret) 6117f4da474SThomas Mingarelli break; 6127f4da474SThomas Mingarelli 6137f4da474SThomas Mingarelli hpwdt_ping(); 6147f4da474SThomas Mingarelli /* Fall */ 6157f4da474SThomas Mingarelli case WDIOC_GETTIMEOUT: 6167f4da474SThomas Mingarelli ret = put_user(soft_margin, p); 6177f4da474SThomas Mingarelli break; 618aae67f36Sdann frazier 619aae67f36Sdann frazier case WDIOC_GETTIMELEFT: 620aae67f36Sdann frazier ret = put_user(hpwdt_time_left(), p); 621aae67f36Sdann frazier break; 6227f4da474SThomas Mingarelli } 6237f4da474SThomas Mingarelli return ret; 6247f4da474SThomas Mingarelli } 6257f4da474SThomas Mingarelli 6267f4da474SThomas Mingarelli /* 6277f4da474SThomas Mingarelli * Kernel interfaces 6287f4da474SThomas Mingarelli */ 629d5c26a59SWim Van Sebroeck static const struct file_operations hpwdt_fops = { 6307f4da474SThomas Mingarelli .owner = THIS_MODULE, 6317f4da474SThomas Mingarelli .llseek = no_llseek, 6327f4da474SThomas Mingarelli .write = hpwdt_write, 6337f4da474SThomas Mingarelli .unlocked_ioctl = hpwdt_ioctl, 6347f4da474SThomas Mingarelli .open = hpwdt_open, 6357f4da474SThomas Mingarelli .release = hpwdt_release, 6367f4da474SThomas Mingarelli }; 6377f4da474SThomas Mingarelli 6387f4da474SThomas Mingarelli static struct miscdevice hpwdt_miscdev = { 6397f4da474SThomas Mingarelli .minor = WATCHDOG_MINOR, 6407f4da474SThomas Mingarelli .name = "watchdog", 6417f4da474SThomas Mingarelli .fops = &hpwdt_fops, 6427f4da474SThomas Mingarelli }; 6437f4da474SThomas Mingarelli 6447f4da474SThomas Mingarelli /* 6457f4da474SThomas Mingarelli * Init & Exit 6467f4da474SThomas Mingarelli */ 6477f4da474SThomas Mingarelli 64886ded1f3Sdann frazier #ifdef CONFIG_HPWDT_NMI_DECODING 6494a7863ccSDon Zickus #ifdef CONFIG_X86_LOCAL_APIC 65034572b29Sdann frazier static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev) 65147bece87SThomas Mingarelli { 65247bece87SThomas Mingarelli /* 65347bece87SThomas Mingarelli * If nmi_watchdog is turned off then we can turn on 65434572b29Sdann frazier * our nmi decoding capability. 65547bece87SThomas Mingarelli */ 65634572b29Sdann frazier hpwdt_nmi_decoding = 1; 65747bece87SThomas Mingarelli } 65847bece87SThomas Mingarelli #else 65934572b29Sdann frazier static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev) 66047bece87SThomas Mingarelli { 66134572b29Sdann frazier dev_warn(&dev->dev, "NMI decoding is disabled. " 66247bece87SThomas Mingarelli "Your kernel does not support a NMI Watchdog.\n"); 66347bece87SThomas Mingarelli } 6644a7863ccSDon Zickus #endif /* CONFIG_X86_LOCAL_APIC */ 6652ec7ed67Sdann frazier 6665efc7a62SThomas Mingarelli /* 6675efc7a62SThomas Mingarelli * dmi_find_icru 6685efc7a62SThomas Mingarelli * 6695efc7a62SThomas Mingarelli * Routine Description: 6705efc7a62SThomas Mingarelli * This function checks whether or not we are on an iCRU-based server. 6715efc7a62SThomas Mingarelli * This check is independent of architecture and needs to be made for 6725efc7a62SThomas Mingarelli * any ProLiant system. 6735efc7a62SThomas Mingarelli */ 6745efc7a62SThomas Mingarelli static void __devinit dmi_find_icru(const struct dmi_header *dm, void *dummy) 6755efc7a62SThomas Mingarelli { 6765efc7a62SThomas Mingarelli struct smbios_proliant_info *smbios_proliant_ptr; 6775efc7a62SThomas Mingarelli 6785efc7a62SThomas Mingarelli if (dm->type == SMBIOS_ICRU_INFORMATION) { 6795efc7a62SThomas Mingarelli smbios_proliant_ptr = (struct smbios_proliant_info *) dm; 6805efc7a62SThomas Mingarelli if (smbios_proliant_ptr->misc_features & 0x01) 6815efc7a62SThomas Mingarelli is_icru = 1; 6825efc7a62SThomas Mingarelli } 6835efc7a62SThomas Mingarelli } 6845efc7a62SThomas Mingarelli 6852ec7ed67Sdann frazier static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev) 6862ec7ed67Sdann frazier { 6872ec7ed67Sdann frazier int retval; 6882ec7ed67Sdann frazier 6892ec7ed67Sdann frazier /* 6905efc7a62SThomas Mingarelli * On typical CRU-based systems we need to map that service in 6915efc7a62SThomas Mingarelli * the BIOS. For 32 bit Operating Systems we need to go through 6925efc7a62SThomas Mingarelli * the 32 Bit BIOS Service Directory. For 64 bit Operating 6935efc7a62SThomas Mingarelli * Systems we get that service through SMBIOS. 6945efc7a62SThomas Mingarelli * 6955efc7a62SThomas Mingarelli * On systems that support the new iCRU service all we need to 6965efc7a62SThomas Mingarelli * do is call dmi_walk to get the supported flag value and skip 6975efc7a62SThomas Mingarelli * the old cru detect code. 6985efc7a62SThomas Mingarelli */ 6995efc7a62SThomas Mingarelli dmi_walk(dmi_find_icru, NULL); 7005efc7a62SThomas Mingarelli if (!is_icru) { 7015efc7a62SThomas Mingarelli 7025efc7a62SThomas Mingarelli /* 7032ec7ed67Sdann frazier * We need to map the ROM to get the CRU service. 7042ec7ed67Sdann frazier * For 32 bit Operating Systems we need to go through the 32 Bit 7052ec7ed67Sdann frazier * BIOS Service Directory 7062ec7ed67Sdann frazier * For 64 bit Operating Systems we get that service through SMBIOS. 7072ec7ed67Sdann frazier */ 7082ec7ed67Sdann frazier retval = detect_cru_service(); 7092ec7ed67Sdann frazier if (retval < 0) { 7102ec7ed67Sdann frazier dev_warn(&dev->dev, 7112ec7ed67Sdann frazier "Unable to detect the %d Bit CRU Service.\n", 7122ec7ed67Sdann frazier HPWDT_ARCH); 7132ec7ed67Sdann frazier return retval; 7142ec7ed67Sdann frazier } 7152ec7ed67Sdann frazier 7162ec7ed67Sdann frazier /* 7172ec7ed67Sdann frazier * We know this is the only CRU call we need to make so lets keep as 7182ec7ed67Sdann frazier * few instructions as possible once the NMI comes in. 7192ec7ed67Sdann frazier */ 7202ec7ed67Sdann frazier cmn_regs.u1.rah = 0x0D; 7212ec7ed67Sdann frazier cmn_regs.u1.ral = 0x02; 7225efc7a62SThomas Mingarelli } 7232ec7ed67Sdann frazier 7242ec7ed67Sdann frazier /* 72509ee1014SDon Zickus * Only one function can register for NMI_UNKNOWN 7262ec7ed67Sdann frazier */ 72709ee1014SDon Zickus retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, 0, "hpwdt"); 728553222f3SDon Zickus if (retval) 729553222f3SDon Zickus goto error; 730553222f3SDon Zickus retval = register_nmi_handler(NMI_SERR, hpwdt_pretimeout, 0, "hpwdt"); 731553222f3SDon Zickus if (retval) 732553222f3SDon Zickus goto error1; 733553222f3SDon Zickus retval = register_nmi_handler(NMI_IO_CHECK, hpwdt_pretimeout, 0, "hpwdt"); 734553222f3SDon Zickus if (retval) 735553222f3SDon Zickus goto error2; 7362ec7ed67Sdann frazier 7372ec7ed67Sdann frazier dev_info(&dev->dev, 7382ec7ed67Sdann frazier "HP Watchdog Timer Driver: NMI decoding initialized" 73909ee1014SDon Zickus ", allow kernel dump: %s (default = 0/OFF)\n", 74009ee1014SDon Zickus (allow_kdump == 0) ? "OFF" : "ON"); 7412ec7ed67Sdann frazier return 0; 742553222f3SDon Zickus 743553222f3SDon Zickus error2: 744553222f3SDon Zickus unregister_nmi_handler(NMI_SERR, "hpwdt"); 745553222f3SDon Zickus error1: 746553222f3SDon Zickus unregister_nmi_handler(NMI_UNKNOWN, "hpwdt"); 747553222f3SDon Zickus error: 7482ec7ed67Sdann frazier dev_warn(&dev->dev, 7492ec7ed67Sdann frazier "Unable to register a die notifier (err=%d).\n", 7502ec7ed67Sdann frazier retval); 7512ec7ed67Sdann frazier if (cru_rom_addr) 7522ec7ed67Sdann frazier iounmap(cru_rom_addr); 753553222f3SDon Zickus return retval; 7542ec7ed67Sdann frazier } 7552ec7ed67Sdann frazier 756b77b7088SAxel Lin static void hpwdt_exit_nmi_decoding(void) 7572ec7ed67Sdann frazier { 7589c48f1c6SDon Zickus unregister_nmi_handler(NMI_UNKNOWN, "hpwdt"); 759a089361cSMingarelli, Thomas unregister_nmi_handler(NMI_SERR, "hpwdt"); 760a089361cSMingarelli, Thomas unregister_nmi_handler(NMI_IO_CHECK, "hpwdt"); 7612ec7ed67Sdann frazier if (cru_rom_addr) 7622ec7ed67Sdann frazier iounmap(cru_rom_addr); 7632ec7ed67Sdann frazier } 76486ded1f3Sdann frazier #else /* !CONFIG_HPWDT_NMI_DECODING */ 76586ded1f3Sdann frazier static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev) 76686ded1f3Sdann frazier { 76786ded1f3Sdann frazier } 76886ded1f3Sdann frazier 76986ded1f3Sdann frazier static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev) 77086ded1f3Sdann frazier { 77186ded1f3Sdann frazier return 0; 77286ded1f3Sdann frazier } 77386ded1f3Sdann frazier 774b77b7088SAxel Lin static void hpwdt_exit_nmi_decoding(void) 77586ded1f3Sdann frazier { 77686ded1f3Sdann frazier } 77786ded1f3Sdann frazier #endif /* CONFIG_HPWDT_NMI_DECODING */ 77847bece87SThomas Mingarelli 7797f4da474SThomas Mingarelli static int __devinit hpwdt_init_one(struct pci_dev *dev, 7807f4da474SThomas Mingarelli const struct pci_device_id *ent) 7817f4da474SThomas Mingarelli { 7827f4da474SThomas Mingarelli int retval; 7837f4da474SThomas Mingarelli 7847f4da474SThomas Mingarelli /* 78534572b29Sdann frazier * Check if we can do NMI decoding or not 78647bece87SThomas Mingarelli */ 78734572b29Sdann frazier hpwdt_check_nmi_decoding(dev); 78847bece87SThomas Mingarelli 78947bece87SThomas Mingarelli /* 79036e3ff44Sdann frazier * First let's find out if we are on an iLO2+ server. We will 7917f4da474SThomas Mingarelli * not run on a legacy ASM box. 792ab4ba3cdSThomas Mingarelli * So we only support the G5 ProLiant servers and higher. 7937f4da474SThomas Mingarelli */ 7947f4da474SThomas Mingarelli if (dev->subsystem_vendor != PCI_VENDOR_ID_HP) { 7957f4da474SThomas Mingarelli dev_warn(&dev->dev, 79636e3ff44Sdann frazier "This server does not have an iLO2+ ASIC.\n"); 7977f4da474SThomas Mingarelli return -ENODEV; 7987f4da474SThomas Mingarelli } 7997f4da474SThomas Mingarelli 8007f4da474SThomas Mingarelli if (pci_enable_device(dev)) { 8017f4da474SThomas Mingarelli dev_warn(&dev->dev, 8027f4da474SThomas Mingarelli "Not possible to enable PCI Device: 0x%x:0x%x.\n", 8037f4da474SThomas Mingarelli ent->vendor, ent->device); 8047f4da474SThomas Mingarelli return -ENODEV; 8057f4da474SThomas Mingarelli } 8067f4da474SThomas Mingarelli 8077f4da474SThomas Mingarelli pci_mem_addr = pci_iomap(dev, 1, 0x80); 8087f4da474SThomas Mingarelli if (!pci_mem_addr) { 8097f4da474SThomas Mingarelli dev_warn(&dev->dev, 81036e3ff44Sdann frazier "Unable to detect the iLO2+ server memory.\n"); 8117f4da474SThomas Mingarelli retval = -ENOMEM; 8127f4da474SThomas Mingarelli goto error_pci_iomap; 8137f4da474SThomas Mingarelli } 8147f4da474SThomas Mingarelli hpwdt_timer_reg = pci_mem_addr + 0x70; 8157f4da474SThomas Mingarelli hpwdt_timer_con = pci_mem_addr + 0x72; 8167f4da474SThomas Mingarelli 817308b135eSToshi Kani /* Make sure that timer is disabled until /dev/watchdog is opened */ 818308b135eSToshi Kani hpwdt_stop(); 819308b135eSToshi Kani 8207f4da474SThomas Mingarelli /* Make sure that we have a valid soft_margin */ 8217f4da474SThomas Mingarelli if (hpwdt_change_timer(soft_margin)) 8227f4da474SThomas Mingarelli hpwdt_change_timer(DEFAULT_MARGIN); 8237f4da474SThomas Mingarelli 8242ec7ed67Sdann frazier /* Initialize NMI Decoding functionality */ 8252ec7ed67Sdann frazier retval = hpwdt_init_nmi_decoding(dev); 8262ec7ed67Sdann frazier if (retval != 0) 8272ec7ed67Sdann frazier goto error_init_nmi_decoding; 8287f4da474SThomas Mingarelli 8297f4da474SThomas Mingarelli retval = misc_register(&hpwdt_miscdev); 8307f4da474SThomas Mingarelli if (retval < 0) { 8317f4da474SThomas Mingarelli dev_warn(&dev->dev, 8327f4da474SThomas Mingarelli "Unable to register miscdev on minor=%d (err=%d).\n", 8337f4da474SThomas Mingarelli WATCHDOG_MINOR, retval); 8347f4da474SThomas Mingarelli goto error_misc_register; 8357f4da474SThomas Mingarelli } 8367f4da474SThomas Mingarelli 8372ec7ed67Sdann frazier dev_info(&dev->dev, "HP Watchdog Timer Driver: %s" 8382ec7ed67Sdann frazier ", timer margin: %d seconds (nowayout=%d).\n", 8392ec7ed67Sdann frazier HPWDT_VERSION, soft_margin, nowayout); 8407f4da474SThomas Mingarelli return 0; 8417f4da474SThomas Mingarelli 8427f4da474SThomas Mingarelli error_misc_register: 8432ec7ed67Sdann frazier hpwdt_exit_nmi_decoding(); 8442ec7ed67Sdann frazier error_init_nmi_decoding: 8457f4da474SThomas Mingarelli pci_iounmap(dev, pci_mem_addr); 8467f4da474SThomas Mingarelli error_pci_iomap: 8477f4da474SThomas Mingarelli pci_disable_device(dev); 8487f4da474SThomas Mingarelli return retval; 8497f4da474SThomas Mingarelli } 8507f4da474SThomas Mingarelli 8517f4da474SThomas Mingarelli static void __devexit hpwdt_exit(struct pci_dev *dev) 8527f4da474SThomas Mingarelli { 8537f4da474SThomas Mingarelli if (!nowayout) 8547f4da474SThomas Mingarelli hpwdt_stop(); 8557f4da474SThomas Mingarelli 8567f4da474SThomas Mingarelli misc_deregister(&hpwdt_miscdev); 8572ec7ed67Sdann frazier hpwdt_exit_nmi_decoding(); 8587f4da474SThomas Mingarelli pci_iounmap(dev, pci_mem_addr); 8597f4da474SThomas Mingarelli pci_disable_device(dev); 8607f4da474SThomas Mingarelli } 8617f4da474SThomas Mingarelli 8627f4da474SThomas Mingarelli static struct pci_driver hpwdt_driver = { 8637f4da474SThomas Mingarelli .name = "hpwdt", 8647f4da474SThomas Mingarelli .id_table = hpwdt_devices, 8657f4da474SThomas Mingarelli .probe = hpwdt_init_one, 866*82268714SBill Pemberton .remove = hpwdt_exit, 8677f4da474SThomas Mingarelli }; 8687f4da474SThomas Mingarelli 8697f4da474SThomas Mingarelli MODULE_AUTHOR("Tom Mingarelli"); 8707f4da474SThomas Mingarelli MODULE_DESCRIPTION("hp watchdog driver"); 8717f4da474SThomas Mingarelli MODULE_LICENSE("GPL"); 872d8100c3aSThomas Mingarelli MODULE_VERSION(HPWDT_VERSION); 8737f4da474SThomas Mingarelli MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 8747f4da474SThomas Mingarelli 8757f4da474SThomas Mingarelli module_param(soft_margin, int, 0); 8767f4da474SThomas Mingarelli MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds"); 8777f4da474SThomas Mingarelli 87886a1e189SWim Van Sebroeck module_param(nowayout, bool, 0); 8797f4da474SThomas Mingarelli MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 8807f4da474SThomas Mingarelli __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 8817f4da474SThomas Mingarelli 88286ded1f3Sdann frazier #ifdef CONFIG_HPWDT_NMI_DECODING 883550d299eSdann frazier module_param(allow_kdump, int, 0); 884550d299eSdann frazier MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs"); 88586ded1f3Sdann frazier #endif /* !CONFIG_HPWDT_NMI_DECODING */ 88644df7535STom Mingarelli 8875ce9c371SWim Van Sebroeck module_pci_driver(hpwdt_driver); 888