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