xref: /linux/drivers/watchdog/hpwdt.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27f4da474SThomas Mingarelli /*
3ca22e79fSMingarelli, Thomas  *	HPE WatchDog Driver
47f4da474SThomas Mingarelli  *	based on
57f4da474SThomas Mingarelli  *
67f4da474SThomas Mingarelli  *	SoftDog	0.05:	A Software Watchdog Device
77f4da474SThomas Mingarelli  *
89a46fc4eSJerry Hoemann  *	(c) Copyright 2018 Hewlett Packard Enterprise Development LP
9ca22e79fSMingarelli, Thomas  *	Thomas Mingarelli <thomas.mingarelli@hpe.com>
107f4da474SThomas Mingarelli  */
117f4da474SThomas Mingarelli 
1227c766aaSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1327c766aaSJoe Perches 
147f4da474SThomas Mingarelli #include <linux/device.h>
157f4da474SThomas Mingarelli #include <linux/io.h>
167f4da474SThomas Mingarelli #include <linux/kernel.h>
177f4da474SThomas Mingarelli #include <linux/module.h>
187f4da474SThomas Mingarelli #include <linux/moduleparam.h>
197f4da474SThomas Mingarelli #include <linux/pci.h>
207f4da474SThomas Mingarelli #include <linux/pci_ids.h>
217f4da474SThomas Mingarelli #include <linux/types.h>
227f4da474SThomas Mingarelli #include <linux/watchdog.h>
23ed835d81SJerry Hoemann #ifdef CONFIG_HPWDT_NMI_DECODING
24d48b0e17SIngo Molnar #include <asm/nmi.h>
25ed835d81SJerry Hoemann #endif
26acc195bdSJerry Hoemann #include <linux/crash_dump.h>
277f4da474SThomas Mingarelli 
285674b74eSJerry Hoemann #define HPWDT_VERSION			"2.0.4"
29e802e32dSdann frazier #define SECS_TO_TICKS(secs)		((secs) * 1000 / 128)
306f681c2eSdann frazier #define TICKS_TO_SECS(ticks)		((ticks) * 128 / 1000)
31be3d7f7cSJerry Hoemann #define HPWDT_MAX_TICKS			65535
32be3d7f7cSJerry Hoemann #define HPWDT_MAX_TIMER			TICKS_TO_SECS(HPWDT_MAX_TICKS)
33923410d0Sdann frazier #define DEFAULT_MARGIN			30
340458f403SJerry Hoemann #define PRETIMEOUT_SEC			9
35923410d0Sdann frazier 
36923410d0Sdann frazier static unsigned int soft_margin = DEFAULT_MARGIN;	/* in seconds */
3786a1e189SWim Van Sebroeck static bool nowayout = WATCHDOG_NOWAYOUT;
380458f403SJerry Hoemann static bool pretimeout = IS_ENABLED(CONFIG_HPWDT_NMI_DECODING);
39be3d7f7cSJerry Hoemann static int kdumptimeout = -1;
40923410d0Sdann frazier 
41923410d0Sdann frazier static void __iomem *pci_mem_addr;		/* the PCI-memory address */
42838534e5SJerry Hoemann static unsigned long __iomem *hpwdt_nmistat;
43923410d0Sdann frazier static unsigned long __iomem *hpwdt_timer_reg;
44923410d0Sdann frazier static unsigned long __iomem *hpwdt_timer_con;
45923410d0Sdann frazier 
46bc17f9dcSJingoo Han static const struct pci_device_id hpwdt_devices[] = {
4736e3ff44Sdann frazier 	{ PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB203) },	/* iLO2 */
4836e3ff44Sdann frazier 	{ PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3306) },	/* iLO3 */
49742b80c5SJerry Hoemann 	{ PCI_DEVICE(PCI_VENDOR_ID_HP_3PAR, 0x0389) },	/* PCtrl */
50923410d0Sdann frazier 	{0},			/* terminate list */
51923410d0Sdann frazier };
52923410d0Sdann frazier MODULE_DEVICE_TABLE(pci, hpwdt_devices);
53923410d0Sdann frazier 
5494d6b80cSJerry Hoemann static const struct pci_device_id hpwdt_blacklist[] = {
5594d6b80cSJerry Hoemann 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3306, PCI_VENDOR_ID_HP, 0x1979) }, /* auxilary iLO */
56de2cb0ccSJerry Hoemann 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3306, PCI_VENDOR_ID_HP_3PAR, 0x0289) },  /* CL */
5794d6b80cSJerry Hoemann 	{0},			/* terminate list */
5894d6b80cSJerry Hoemann };
597f4da474SThomas Mingarelli 
60be3d7f7cSJerry Hoemann static struct watchdog_device hpwdt_dev;
617f4da474SThomas Mingarelli /*
627f4da474SThomas Mingarelli  *	Watchdog operations
637f4da474SThomas Mingarelli  */
hpwdt_hw_is_running(void)64bb721d6bSJerry Hoemann static int hpwdt_hw_is_running(void)
65bb721d6bSJerry Hoemann {
66bb721d6bSJerry Hoemann 	return ioread8(hpwdt_timer_con) & 0x01;
67bb721d6bSJerry Hoemann }
68bb721d6bSJerry Hoemann 
hpwdt_start(struct watchdog_device * wdd)69d0a4027fSJerry Hoemann static int hpwdt_start(struct watchdog_device *wdd)
707f4da474SThomas Mingarelli {
710458f403SJerry Hoemann 	int control = 0x81 | (pretimeout ? 0x4 : 0);
72c22d8e38SJerry Hoemann 	int reload = SECS_TO_TICKS(min(wdd->timeout, wdd->max_hw_heartbeat_ms/1000));
73d0a4027fSJerry Hoemann 
74c22d8e38SJerry Hoemann 	dev_dbg(wdd->parent, "start watchdog 0x%08x:0x%08x:0x%02x\n", wdd->timeout, reload, control);
757f4da474SThomas Mingarelli 	iowrite16(reload, hpwdt_timer_reg);
760458f403SJerry Hoemann 	iowrite8(control, hpwdt_timer_con);
77d0a4027fSJerry Hoemann 
78d0a4027fSJerry Hoemann 	return 0;
797f4da474SThomas Mingarelli }
807f4da474SThomas Mingarelli 
hpwdt_stop(void)817f4da474SThomas Mingarelli static void hpwdt_stop(void)
827f4da474SThomas Mingarelli {
837f4da474SThomas Mingarelli 	unsigned long data;
847f4da474SThomas Mingarelli 
85ccfd6921SJerry Hoemann 	pr_debug("stop  watchdog\n");
86ccfd6921SJerry Hoemann 
87d08c9a33SMingarelli, Thomas 	data = ioread8(hpwdt_timer_con);
887f4da474SThomas Mingarelli 	data &= 0xFE;
89d08c9a33SMingarelli, Thomas 	iowrite8(data, hpwdt_timer_con);
907f4da474SThomas Mingarelli }
917f4da474SThomas Mingarelli 
hpwdt_stop_core(struct watchdog_device * wdd)92d0a4027fSJerry Hoemann static int hpwdt_stop_core(struct watchdog_device *wdd)
937f4da474SThomas Mingarelli {
94d0a4027fSJerry Hoemann 	hpwdt_stop();
957f4da474SThomas Mingarelli 
967f4da474SThomas Mingarelli 	return 0;
977f4da474SThomas Mingarelli }
987f4da474SThomas Mingarelli 
hpwdt_ping_ticks(int val)99be3d7f7cSJerry Hoemann static void hpwdt_ping_ticks(int val)
100be3d7f7cSJerry Hoemann {
101be3d7f7cSJerry Hoemann 	val = min(val, HPWDT_MAX_TICKS);
102be3d7f7cSJerry Hoemann 	iowrite16(val, hpwdt_timer_reg);
103be3d7f7cSJerry Hoemann }
104be3d7f7cSJerry Hoemann 
hpwdt_ping(struct watchdog_device * wdd)105d0a4027fSJerry Hoemann static int hpwdt_ping(struct watchdog_device *wdd)
106d0a4027fSJerry Hoemann {
107c22d8e38SJerry Hoemann 	int reload = SECS_TO_TICKS(min(wdd->timeout, wdd->max_hw_heartbeat_ms/1000));
1080458f403SJerry Hoemann 
109c22d8e38SJerry Hoemann 	dev_dbg(wdd->parent, "ping  watchdog 0x%08x:0x%08x\n", wdd->timeout, reload);
110be3d7f7cSJerry Hoemann 	hpwdt_ping_ticks(reload);
1110458f403SJerry Hoemann 
112d0a4027fSJerry Hoemann 	return 0;
113d0a4027fSJerry Hoemann }
114d0a4027fSJerry Hoemann 
hpwdt_gettimeleft(struct watchdog_device * wdd)115d0a4027fSJerry Hoemann static unsigned int hpwdt_gettimeleft(struct watchdog_device *wdd)
116aae67f36Sdann frazier {
117aae67f36Sdann frazier 	return TICKS_TO_SECS(ioread16(hpwdt_timer_reg));
118aae67f36Sdann frazier }
119aae67f36Sdann frazier 
hpwdt_settimeout(struct watchdog_device * wdd,unsigned int val)120d0a4027fSJerry Hoemann static int hpwdt_settimeout(struct watchdog_device *wdd, unsigned int val)
121d0a4027fSJerry Hoemann {
122ccfd6921SJerry Hoemann 	dev_dbg(wdd->parent, "set_timeout = %d\n", val);
123ccfd6921SJerry Hoemann 
124d0a4027fSJerry Hoemann 	wdd->timeout = val;
1250458f403SJerry Hoemann 	if (val <= wdd->pretimeout) {
126ccfd6921SJerry Hoemann 		dev_dbg(wdd->parent, "pretimeout < timeout. Setting to zero\n");
1270458f403SJerry Hoemann 		wdd->pretimeout = 0;
12817f0d1b9SJiapeng Zhong 		pretimeout = false;
1290458f403SJerry Hoemann 		if (watchdog_active(wdd))
1300458f403SJerry Hoemann 			hpwdt_start(wdd);
1310458f403SJerry Hoemann 	}
132d0a4027fSJerry Hoemann 	hpwdt_ping(wdd);
133d0a4027fSJerry Hoemann 
134d0a4027fSJerry Hoemann 	return 0;
135d0a4027fSJerry Hoemann }
136d0a4027fSJerry Hoemann 
137aeebc6baSArnd Bergmann #ifdef CONFIG_HPWDT_NMI_DECODING
hpwdt_set_pretimeout(struct watchdog_device * wdd,unsigned int req)1380458f403SJerry Hoemann static int hpwdt_set_pretimeout(struct watchdog_device *wdd, unsigned int req)
1390458f403SJerry Hoemann {
1400458f403SJerry Hoemann 	unsigned int val = 0;
1410458f403SJerry Hoemann 
142ccfd6921SJerry Hoemann 	dev_dbg(wdd->parent, "set_pretimeout = %d\n", req);
1430458f403SJerry Hoemann 	if (req) {
1440458f403SJerry Hoemann 		val = PRETIMEOUT_SEC;
1450458f403SJerry Hoemann 		if (val >= wdd->timeout)
1460458f403SJerry Hoemann 			return -EINVAL;
1470458f403SJerry Hoemann 	}
1480458f403SJerry Hoemann 
149ccfd6921SJerry Hoemann 	if (val != req)
150ccfd6921SJerry Hoemann 		dev_dbg(wdd->parent, "Rounding pretimeout to: %d\n", val);
151ccfd6921SJerry Hoemann 
1520458f403SJerry Hoemann 	wdd->pretimeout = val;
1530458f403SJerry Hoemann 	pretimeout = !!val;
1540458f403SJerry Hoemann 
1550458f403SJerry Hoemann 	if (watchdog_active(wdd))
1560458f403SJerry Hoemann 		hpwdt_start(wdd);
1570458f403SJerry Hoemann 
1580458f403SJerry Hoemann 	return 0;
1590458f403SJerry Hoemann }
1600458f403SJerry Hoemann 
hpwdt_my_nmi(void)161838534e5SJerry Hoemann static int hpwdt_my_nmi(void)
162838534e5SJerry Hoemann {
163838534e5SJerry Hoemann 	return ioread8(hpwdt_nmistat) & 0x6;
164838534e5SJerry Hoemann }
165838534e5SJerry Hoemann 
1667f4da474SThomas Mingarelli /*
167ab4ba3cdSThomas Mingarelli  *	NMI Handler
168ab4ba3cdSThomas Mingarelli  */
hpwdt_pretimeout(unsigned int ulReason,struct pt_regs * regs)1699c48f1c6SDon Zickus static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs)
170ab4ba3cdSThomas Mingarelli {
171a042229aSJerry Hoemann 	unsigned int mynmi = hpwdt_my_nmi();
172a042229aSJerry Hoemann 	static char panic_msg[] =
173a042229aSJerry Hoemann 		"00: An NMI occurred. Depending on your system the reason "
174a042229aSJerry Hoemann 		"for the NMI is logged in any one of the following resources:\n"
175a042229aSJerry Hoemann 		"1. Integrated Management Log (IML)\n"
176a042229aSJerry Hoemann 		"2. OA Syslog\n"
177a042229aSJerry Hoemann 		"3. OA Forward Progress Log\n"
178a042229aSJerry Hoemann 		"4. iLO Event Log";
179a042229aSJerry Hoemann 
180dced0b3eSJerry Hoemann 	if (ulReason == NMI_UNKNOWN && !mynmi)
181838534e5SJerry Hoemann 		return NMI_DONE;
182838534e5SJerry Hoemann 
183be3d7f7cSJerry Hoemann 	if (kdumptimeout < 0)
184ab4ba3cdSThomas Mingarelli 		hpwdt_stop();
185be3d7f7cSJerry Hoemann 	else if (kdumptimeout == 0)
186be3d7f7cSJerry Hoemann 		;
187be3d7f7cSJerry Hoemann 	else {
188be3d7f7cSJerry Hoemann 		unsigned int val = max((unsigned int)kdumptimeout, hpwdt_dev.timeout);
189be3d7f7cSJerry Hoemann 		hpwdt_ping_ticks(SECS_TO_TICKS(val));
190be3d7f7cSJerry Hoemann 	}
191dbc018ecSNaga Chumbalkar 
192a042229aSJerry Hoemann 	hex_byte_pack(panic_msg, mynmi);
193a042229aSJerry Hoemann 	nmi_panic(regs, panic_msg);
1945efc7a62SThomas Mingarelli 
195abc514c5SHidehiro Kawai 	return NMI_HANDLED;
196ab4ba3cdSThomas Mingarelli }
19786ded1f3Sdann frazier #endif /* CONFIG_HPWDT_NMI_DECODING */
198ab4ba3cdSThomas Mingarelli 
1997f4da474SThomas Mingarelli 
20042747d71SWim Van Sebroeck static const struct watchdog_info ident = {
2010458f403SJerry Hoemann 	.options = WDIOF_PRETIMEOUT    |
2020458f403SJerry Hoemann 		   WDIOF_SETTIMEOUT    |
2037f4da474SThomas Mingarelli 		   WDIOF_KEEPALIVEPING |
2047f4da474SThomas Mingarelli 		   WDIOF_MAGICCLOSE,
205ca22e79fSMingarelli, Thomas 	.identity = "HPE iLO2+ HW Watchdog Timer",
2067f4da474SThomas Mingarelli };
2077f4da474SThomas Mingarelli 
2087f4da474SThomas Mingarelli /*
2097f4da474SThomas Mingarelli  *	Kernel interfaces
2107f4da474SThomas Mingarelli  */
211d0a4027fSJerry Hoemann 
212d0a4027fSJerry Hoemann static const struct watchdog_ops hpwdt_ops = {
2137f4da474SThomas Mingarelli 	.owner		= THIS_MODULE,
214d0a4027fSJerry Hoemann 	.start		= hpwdt_start,
215d0a4027fSJerry Hoemann 	.stop		= hpwdt_stop_core,
216d0a4027fSJerry Hoemann 	.ping		= hpwdt_ping,
217d0a4027fSJerry Hoemann 	.set_timeout	= hpwdt_settimeout,
218d0a4027fSJerry Hoemann 	.get_timeleft	= hpwdt_gettimeleft,
2190458f403SJerry Hoemann #ifdef CONFIG_HPWDT_NMI_DECODING
2200458f403SJerry Hoemann 	.set_pretimeout	= hpwdt_set_pretimeout,
2210458f403SJerry Hoemann #endif
2227f4da474SThomas Mingarelli };
2237f4da474SThomas Mingarelli 
224d0a4027fSJerry Hoemann static struct watchdog_device hpwdt_dev = {
225d0a4027fSJerry Hoemann 	.info		= &ident,
226d0a4027fSJerry Hoemann 	.ops		= &hpwdt_ops,
227d0a4027fSJerry Hoemann 	.min_timeout	= 1,
228d0a4027fSJerry Hoemann 	.timeout	= DEFAULT_MARGIN,
2290458f403SJerry Hoemann 	.pretimeout	= PRETIMEOUT_SEC,
230c22d8e38SJerry Hoemann 	.max_hw_heartbeat_ms	= HPWDT_MAX_TIMER * 1000,
2317f4da474SThomas Mingarelli };
2327f4da474SThomas Mingarelli 
233d0a4027fSJerry Hoemann 
2347f4da474SThomas Mingarelli /*
2357f4da474SThomas Mingarelli  *	Init & Exit
2367f4da474SThomas Mingarelli  */
2377f4da474SThomas Mingarelli 
hpwdt_init_nmi_decoding(struct pci_dev * dev)2382d991a16SBill Pemberton static int hpwdt_init_nmi_decoding(struct pci_dev *dev)
2392ec7ed67Sdann frazier {
2402b3d89b4SJerry Hoemann #ifdef CONFIG_HPWDT_NMI_DECODING
2412ec7ed67Sdann frazier 	int retval;
2422ec7ed67Sdann frazier 	/*
24309ee1014SDon Zickus 	 * Only one function can register for NMI_UNKNOWN
2442ec7ed67Sdann frazier 	 */
24509ee1014SDon Zickus 	retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, 0, "hpwdt");
246553222f3SDon Zickus 	if (retval)
247553222f3SDon Zickus 		goto error;
248553222f3SDon Zickus 	retval = register_nmi_handler(NMI_SERR, hpwdt_pretimeout, 0, "hpwdt");
249553222f3SDon Zickus 	if (retval)
250553222f3SDon Zickus 		goto error1;
251553222f3SDon Zickus 	retval = register_nmi_handler(NMI_IO_CHECK, hpwdt_pretimeout, 0, "hpwdt");
252553222f3SDon Zickus 	if (retval)
253553222f3SDon Zickus 		goto error2;
2542ec7ed67Sdann frazier 
2552ec7ed67Sdann frazier 	dev_info(&dev->dev,
256703fc3dfSJerry Hoemann 		"HPE Watchdog Timer Driver: NMI decoding initialized\n");
257703fc3dfSJerry Hoemann 
2582ec7ed67Sdann frazier 	return 0;
259553222f3SDon Zickus 
260553222f3SDon Zickus error2:
261553222f3SDon Zickus 	unregister_nmi_handler(NMI_SERR, "hpwdt");
262553222f3SDon Zickus error1:
263553222f3SDon Zickus 	unregister_nmi_handler(NMI_UNKNOWN, "hpwdt");
264553222f3SDon Zickus error:
2652ec7ed67Sdann frazier 	dev_warn(&dev->dev,
2662ec7ed67Sdann frazier 		"Unable to register a die notifier (err=%d).\n",
2672ec7ed67Sdann frazier 		retval);
268553222f3SDon Zickus 	return retval;
2692b3d89b4SJerry Hoemann #endif	/* CONFIG_HPWDT_NMI_DECODING */
27086ded1f3Sdann frazier 	return 0;
27186ded1f3Sdann frazier }
27286ded1f3Sdann frazier 
hpwdt_exit_nmi_decoding(void)273b77b7088SAxel Lin static void hpwdt_exit_nmi_decoding(void)
27486ded1f3Sdann frazier {
2752b3d89b4SJerry Hoemann #ifdef CONFIG_HPWDT_NMI_DECODING
2762b3d89b4SJerry Hoemann 	unregister_nmi_handler(NMI_UNKNOWN, "hpwdt");
2772b3d89b4SJerry Hoemann 	unregister_nmi_handler(NMI_SERR, "hpwdt");
2782b3d89b4SJerry Hoemann 	unregister_nmi_handler(NMI_IO_CHECK, "hpwdt");
2792b3d89b4SJerry Hoemann #endif
28086ded1f3Sdann frazier }
28147bece87SThomas Mingarelli 
hpwdt_init_one(struct pci_dev * dev,const struct pci_device_id * ent)2822d991a16SBill Pemberton static int hpwdt_init_one(struct pci_dev *dev,
2837f4da474SThomas Mingarelli 					const struct pci_device_id *ent)
2847f4da474SThomas Mingarelli {
2857f4da474SThomas Mingarelli 	int retval;
2867f4da474SThomas Mingarelli 
2877f4da474SThomas Mingarelli 	/*
28836e3ff44Sdann frazier 	 * First let's find out if we are on an iLO2+ server. We will
2897f4da474SThomas Mingarelli 	 * not run on a legacy ASM box.
290ab4ba3cdSThomas Mingarelli 	 * So we only support the G5 ProLiant servers and higher.
2917f4da474SThomas Mingarelli 	 */
292fc113d54SBrian Boylston 	if (dev->subsystem_vendor != PCI_VENDOR_ID_HP &&
293fc113d54SBrian Boylston 	    dev->subsystem_vendor != PCI_VENDOR_ID_HP_3PAR) {
2947f4da474SThomas Mingarelli 		dev_warn(&dev->dev,
29536e3ff44Sdann frazier 			"This server does not have an iLO2+ ASIC.\n");
2967f4da474SThomas Mingarelli 		return -ENODEV;
2977f4da474SThomas Mingarelli 	}
2987f4da474SThomas Mingarelli 
29994d6b80cSJerry Hoemann 	if (pci_match_id(hpwdt_blacklist, dev)) {
30094d6b80cSJerry Hoemann 		dev_dbg(&dev->dev, "Not supported on this device\n");
3010821f20dSMingarelli, Thomas 		return -ENODEV;
30294d6b80cSJerry Hoemann 	}
3030821f20dSMingarelli, Thomas 
3047f4da474SThomas Mingarelli 	if (pci_enable_device(dev)) {
3057f4da474SThomas Mingarelli 		dev_warn(&dev->dev,
3067f4da474SThomas Mingarelli 			"Not possible to enable PCI Device: 0x%x:0x%x.\n",
3077f4da474SThomas Mingarelli 			ent->vendor, ent->device);
3087f4da474SThomas Mingarelli 		return -ENODEV;
3097f4da474SThomas Mingarelli 	}
3107f4da474SThomas Mingarelli 
3117f4da474SThomas Mingarelli 	pci_mem_addr = pci_iomap(dev, 1, 0x80);
3127f4da474SThomas Mingarelli 	if (!pci_mem_addr) {
3137f4da474SThomas Mingarelli 		dev_warn(&dev->dev,
31436e3ff44Sdann frazier 			"Unable to detect the iLO2+ server memory.\n");
3157f4da474SThomas Mingarelli 		retval = -ENOMEM;
3167f4da474SThomas Mingarelli 		goto error_pci_iomap;
3177f4da474SThomas Mingarelli 	}
318838534e5SJerry Hoemann 	hpwdt_nmistat	= pci_mem_addr + 0x6e;
3197f4da474SThomas Mingarelli 	hpwdt_timer_reg = pci_mem_addr + 0x70;
3207f4da474SThomas Mingarelli 	hpwdt_timer_con = pci_mem_addr + 0x72;
3217f4da474SThomas Mingarelli 
322bb721d6bSJerry Hoemann 	/* Have the core update running timer until user space is ready */
323bb721d6bSJerry Hoemann 	if (hpwdt_hw_is_running()) {
324bb721d6bSJerry Hoemann 		dev_info(&dev->dev, "timer is running\n");
325bb721d6bSJerry Hoemann 		set_bit(WDOG_HW_RUNNING, &hpwdt_dev.status);
326bb721d6bSJerry Hoemann 	}
327308b135eSToshi Kani 
3282ec7ed67Sdann frazier 	/* Initialize NMI Decoding functionality */
3292ec7ed67Sdann frazier 	retval = hpwdt_init_nmi_decoding(dev);
3302ec7ed67Sdann frazier 	if (retval != 0)
3312ec7ed67Sdann frazier 		goto error_init_nmi_decoding;
3327f4da474SThomas Mingarelli 
33348b32199SJerry Hoemann 	watchdog_stop_on_unregister(&hpwdt_dev);
334d0a4027fSJerry Hoemann 	watchdog_set_nowayout(&hpwdt_dev, nowayout);
33587dfe210SWolfram Sang 	watchdog_init_timeout(&hpwdt_dev, soft_margin, NULL);
336d0a4027fSJerry Hoemann 
337acc195bdSJerry Hoemann 	if (is_kdump_kernel()) {
33817f0d1b9SJiapeng Zhong 		pretimeout = false;
339acc195bdSJerry Hoemann 		kdumptimeout = 0;
340acc195bdSJerry Hoemann 	}
341acc195bdSJerry Hoemann 
34210d790d1SJerry Hoemann 	if (pretimeout && hpwdt_dev.timeout <= PRETIMEOUT_SEC) {
34310d790d1SJerry Hoemann 		dev_warn(&dev->dev, "timeout <= pretimeout. Setting pretimeout to zero\n");
34417f0d1b9SJiapeng Zhong 		pretimeout = false;
34510d790d1SJerry Hoemann 	}
3464d9186d0SJerry Hoemann 	hpwdt_dev.pretimeout = pretimeout ? PRETIMEOUT_SEC : 0;
347be3d7f7cSJerry Hoemann 	kdumptimeout = min(kdumptimeout, HPWDT_MAX_TIMER);
3484d9186d0SJerry Hoemann 
349d0a4027fSJerry Hoemann 	hpwdt_dev.parent = &dev->dev;
350d0a4027fSJerry Hoemann 	retval = watchdog_register_device(&hpwdt_dev);
351f51540b8SWolfram Sang 	if (retval < 0)
352d0a4027fSJerry Hoemann 		goto error_wd_register;
3537f4da474SThomas Mingarelli 
35492301461SJerry Hoemann 	dev_info(&dev->dev, "HPE Watchdog Timer Driver: Version: %s\n",
35592301461SJerry Hoemann 				HPWDT_VERSION);
35692301461SJerry Hoemann 	dev_info(&dev->dev, "timeout: %d seconds (nowayout=%d)\n",
35792301461SJerry Hoemann 				hpwdt_dev.timeout, nowayout);
35892301461SJerry Hoemann 	dev_info(&dev->dev, "pretimeout: %s.\n",
35992301461SJerry Hoemann 				pretimeout ? "on" : "off");
360be3d7f7cSJerry Hoemann 	dev_info(&dev->dev, "kdumptimeout: %d.\n", kdumptimeout);
361d0a4027fSJerry Hoemann 
3627f4da474SThomas Mingarelli 	return 0;
3637f4da474SThomas Mingarelli 
364d0a4027fSJerry Hoemann error_wd_register:
3652ec7ed67Sdann frazier 	hpwdt_exit_nmi_decoding();
3662ec7ed67Sdann frazier error_init_nmi_decoding:
3677f4da474SThomas Mingarelli 	pci_iounmap(dev, pci_mem_addr);
3687f4da474SThomas Mingarelli error_pci_iomap:
3697f4da474SThomas Mingarelli 	pci_disable_device(dev);
3707f4da474SThomas Mingarelli 	return retval;
3717f4da474SThomas Mingarelli }
3727f4da474SThomas Mingarelli 
hpwdt_exit(struct pci_dev * dev)3734b12b896SBill Pemberton static void hpwdt_exit(struct pci_dev *dev)
3747f4da474SThomas Mingarelli {
375d0a4027fSJerry Hoemann 	watchdog_unregister_device(&hpwdt_dev);
3762ec7ed67Sdann frazier 	hpwdt_exit_nmi_decoding();
3777f4da474SThomas Mingarelli 	pci_iounmap(dev, pci_mem_addr);
3787f4da474SThomas Mingarelli 	pci_disable_device(dev);
3797f4da474SThomas Mingarelli }
3807f4da474SThomas Mingarelli 
hpwdt_suspend(struct device * dev)381*12b8ab42SJerry Hoemann static int hpwdt_suspend(struct device *dev)
382*12b8ab42SJerry Hoemann {
383*12b8ab42SJerry Hoemann 	if (watchdog_active(&hpwdt_dev))
384*12b8ab42SJerry Hoemann 		hpwdt_stop();
385*12b8ab42SJerry Hoemann 
386*12b8ab42SJerry Hoemann 	return 0;
387*12b8ab42SJerry Hoemann }
388*12b8ab42SJerry Hoemann 
hpwdt_resume(struct device * dev)389*12b8ab42SJerry Hoemann static int hpwdt_resume(struct device *dev)
390*12b8ab42SJerry Hoemann {
391*12b8ab42SJerry Hoemann 	if (watchdog_active(&hpwdt_dev))
392*12b8ab42SJerry Hoemann 		hpwdt_start(&hpwdt_dev);
393*12b8ab42SJerry Hoemann 
394*12b8ab42SJerry Hoemann 	return 0;
395*12b8ab42SJerry Hoemann }
396*12b8ab42SJerry Hoemann 
397*12b8ab42SJerry Hoemann static const struct dev_pm_ops hpwdt_pm_ops = {
398*12b8ab42SJerry Hoemann 	LATE_SYSTEM_SLEEP_PM_OPS(hpwdt_suspend, hpwdt_resume)
399*12b8ab42SJerry Hoemann };
400*12b8ab42SJerry Hoemann 
4017f4da474SThomas Mingarelli static struct pci_driver hpwdt_driver = {
4027f4da474SThomas Mingarelli 	.name = "hpwdt",
4037f4da474SThomas Mingarelli 	.id_table = hpwdt_devices,
4047f4da474SThomas Mingarelli 	.probe = hpwdt_init_one,
40582268714SBill Pemberton 	.remove = hpwdt_exit,
406*12b8ab42SJerry Hoemann 
407*12b8ab42SJerry Hoemann 	.driver = {
408*12b8ab42SJerry Hoemann 		.name = "hpwdt",
409*12b8ab42SJerry Hoemann 		.pm = &hpwdt_pm_ops,
410*12b8ab42SJerry Hoemann 	}
4117f4da474SThomas Mingarelli };
4127f4da474SThomas Mingarelli 
4137f4da474SThomas Mingarelli MODULE_AUTHOR("Tom Mingarelli");
4149a46fc4eSJerry Hoemann MODULE_DESCRIPTION("hpe watchdog driver");
4157f4da474SThomas Mingarelli MODULE_LICENSE("GPL");
416d8100c3aSThomas Mingarelli MODULE_VERSION(HPWDT_VERSION);
4177f4da474SThomas Mingarelli 
4187f4da474SThomas Mingarelli module_param(soft_margin, int, 0);
4197f4da474SThomas Mingarelli MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds");
4207f4da474SThomas Mingarelli 
421397a35d4SJerry Hoemann module_param_named(timeout, soft_margin, int, 0);
422397a35d4SJerry Hoemann MODULE_PARM_DESC(timeout, "Alias of soft_margin");
423397a35d4SJerry Hoemann 
42486a1e189SWim Van Sebroeck module_param(nowayout, bool, 0);
4257f4da474SThomas Mingarelli MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
4267f4da474SThomas Mingarelli 		__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
4277f4da474SThomas Mingarelli 
428be3d7f7cSJerry Hoemann module_param(kdumptimeout, int, 0444);
429be3d7f7cSJerry Hoemann MODULE_PARM_DESC(kdumptimeout, "Timeout applied for crash kernel transition in seconds");
430be3d7f7cSJerry Hoemann 
4310458f403SJerry Hoemann #ifdef CONFIG_HPWDT_NMI_DECODING
4320458f403SJerry Hoemann module_param(pretimeout, bool, 0);
4330458f403SJerry Hoemann MODULE_PARM_DESC(pretimeout, "Watchdog pretimeout enabled");
4340458f403SJerry Hoemann #endif
4350458f403SJerry Hoemann 
4365ce9c371SWim Van Sebroeck module_pci_driver(hpwdt_driver);
437