xref: /linux/drivers/watchdog/hpwdt.c (revision e802e32d2b42ce1384baf4e150f444477729aad2)
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