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