^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * HPE WatchDog Driver
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * based on
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * SoftDog 0.05: A Software Watchdog Device
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * (c) Copyright 2018 Hewlett Packard Enterprise Development LP
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * Thomas Mingarelli <thomas.mingarelli@hpe.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/moduleparam.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/pci.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/pci_ids.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <linux/types.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #include <linux/watchdog.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #include <asm/nmi.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #define HPWDT_VERSION "2.0.3"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #define SECS_TO_TICKS(secs) ((secs) * 1000 / 128)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #define HPWDT_MAX_TICKS 65535
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) #define HPWDT_MAX_TIMER TICKS_TO_SECS(HPWDT_MAX_TICKS)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) #define DEFAULT_MARGIN 30
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) #define PRETIMEOUT_SEC 9
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) static bool ilo5;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) static unsigned int soft_margin = DEFAULT_MARGIN; /* in seconds */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) static bool nowayout = WATCHDOG_NOWAYOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) static bool pretimeout = IS_ENABLED(CONFIG_HPWDT_NMI_DECODING);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) static int kdumptimeout = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) static void __iomem *pci_mem_addr; /* the PCI-memory address */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) static unsigned long __iomem *hpwdt_nmistat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) static unsigned long __iomem *hpwdt_timer_reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) static unsigned long __iomem *hpwdt_timer_con;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) static const struct pci_device_id hpwdt_devices[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) { PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB203) }, /* iLO2 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) { PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3306) }, /* iLO3 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) {0}, /* terminate list */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) MODULE_DEVICE_TABLE(pci, hpwdt_devices);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) static const struct pci_device_id hpwdt_blacklist[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) { PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3306, PCI_VENDOR_ID_HP, 0x1979) }, /* auxilary iLO */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) { PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3306, PCI_VENDOR_ID_HP_3PAR, 0x0289) }, /* CL */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) {0}, /* terminate list */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) static struct watchdog_device hpwdt_dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) * Watchdog operations
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) static int hpwdt_hw_is_running(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) return ioread8(hpwdt_timer_con) & 0x01;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) static int hpwdt_start(struct watchdog_device *wdd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) int control = 0x81 | (pretimeout ? 0x4 : 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) int reload = SECS_TO_TICKS(min(wdd->timeout, wdd->max_hw_heartbeat_ms/1000));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) dev_dbg(wdd->parent, "start watchdog 0x%08x:0x%08x:0x%02x\n", wdd->timeout, reload, control);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) iowrite16(reload, hpwdt_timer_reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) iowrite8(control, hpwdt_timer_con);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) static void hpwdt_stop(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) unsigned long data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) pr_debug("stop watchdog\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) data = ioread8(hpwdt_timer_con);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) data &= 0xFE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) iowrite8(data, hpwdt_timer_con);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) static int hpwdt_stop_core(struct watchdog_device *wdd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) hpwdt_stop();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) static void hpwdt_ping_ticks(int val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) val = min(val, HPWDT_MAX_TICKS);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) iowrite16(val, hpwdt_timer_reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) static int hpwdt_ping(struct watchdog_device *wdd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) int reload = SECS_TO_TICKS(min(wdd->timeout, wdd->max_hw_heartbeat_ms/1000));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) dev_dbg(wdd->parent, "ping watchdog 0x%08x:0x%08x\n", wdd->timeout, reload);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) hpwdt_ping_ticks(reload);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) static unsigned int hpwdt_gettimeleft(struct watchdog_device *wdd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) return TICKS_TO_SECS(ioread16(hpwdt_timer_reg));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) static int hpwdt_settimeout(struct watchdog_device *wdd, unsigned int val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) dev_dbg(wdd->parent, "set_timeout = %d\n", val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) wdd->timeout = val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) if (val <= wdd->pretimeout) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) dev_dbg(wdd->parent, "pretimeout < timeout. Setting to zero\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) wdd->pretimeout = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) pretimeout = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) if (watchdog_active(wdd))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) hpwdt_start(wdd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) hpwdt_ping(wdd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) #ifdef CONFIG_HPWDT_NMI_DECODING
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) static int hpwdt_set_pretimeout(struct watchdog_device *wdd, unsigned int req)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) unsigned int val = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) dev_dbg(wdd->parent, "set_pretimeout = %d\n", req);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) if (req) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) val = PRETIMEOUT_SEC;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) if (val >= wdd->timeout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) if (val != req)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) dev_dbg(wdd->parent, "Rounding pretimeout to: %d\n", val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) wdd->pretimeout = val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) pretimeout = !!val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) if (watchdog_active(wdd))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) hpwdt_start(wdd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) static int hpwdt_my_nmi(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) return ioread8(hpwdt_nmistat) & 0x6;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) * NMI Handler
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) unsigned int mynmi = hpwdt_my_nmi();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) static char panic_msg[] =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) "00: An NMI occurred. Depending on your system the reason "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) "for the NMI is logged in any one of the following resources:\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) "1. Integrated Management Log (IML)\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) "2. OA Syslog\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) "3. OA Forward Progress Log\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) "4. iLO Event Log";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) if (ilo5 && ulReason == NMI_UNKNOWN && !mynmi)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) return NMI_DONE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) if (ilo5 && !pretimeout && !mynmi)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) return NMI_DONE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) if (kdumptimeout < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) hpwdt_stop();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) else if (kdumptimeout == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) ;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) unsigned int val = max((unsigned int)kdumptimeout, hpwdt_dev.timeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) hpwdt_ping_ticks(SECS_TO_TICKS(val));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) hex_byte_pack(panic_msg, mynmi);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) nmi_panic(regs, panic_msg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) return NMI_HANDLED;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) #endif /* CONFIG_HPWDT_NMI_DECODING */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) static const struct watchdog_info ident = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) .options = WDIOF_PRETIMEOUT |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) WDIOF_SETTIMEOUT |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) WDIOF_KEEPALIVEPING |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) WDIOF_MAGICCLOSE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) .identity = "HPE iLO2+ HW Watchdog Timer",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) * Kernel interfaces
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) static const struct watchdog_ops hpwdt_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) .start = hpwdt_start,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) .stop = hpwdt_stop_core,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) .ping = hpwdt_ping,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) .set_timeout = hpwdt_settimeout,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) .get_timeleft = hpwdt_gettimeleft,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) #ifdef CONFIG_HPWDT_NMI_DECODING
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) .set_pretimeout = hpwdt_set_pretimeout,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) static struct watchdog_device hpwdt_dev = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) .info = &ident,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) .ops = &hpwdt_ops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) .min_timeout = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) .timeout = DEFAULT_MARGIN,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) .pretimeout = PRETIMEOUT_SEC,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) .max_hw_heartbeat_ms = HPWDT_MAX_TIMER * 1000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) * Init & Exit
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) static int hpwdt_init_nmi_decoding(struct pci_dev *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) #ifdef CONFIG_HPWDT_NMI_DECODING
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) int retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) * Only one function can register for NMI_UNKNOWN
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, 0, "hpwdt");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) if (retval)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) goto error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) retval = register_nmi_handler(NMI_SERR, hpwdt_pretimeout, 0, "hpwdt");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) if (retval)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) goto error1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) retval = register_nmi_handler(NMI_IO_CHECK, hpwdt_pretimeout, 0, "hpwdt");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) if (retval)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) goto error2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) dev_info(&dev->dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) "HPE Watchdog Timer Driver: NMI decoding initialized\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) error2:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) unregister_nmi_handler(NMI_SERR, "hpwdt");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) error1:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) unregister_nmi_handler(NMI_UNKNOWN, "hpwdt");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) error:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) dev_warn(&dev->dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) "Unable to register a die notifier (err=%d).\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) retval);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) return retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) #endif /* CONFIG_HPWDT_NMI_DECODING */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) static void hpwdt_exit_nmi_decoding(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) #ifdef CONFIG_HPWDT_NMI_DECODING
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) unregister_nmi_handler(NMI_UNKNOWN, "hpwdt");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) unregister_nmi_handler(NMI_SERR, "hpwdt");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) unregister_nmi_handler(NMI_IO_CHECK, "hpwdt");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) static int hpwdt_init_one(struct pci_dev *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) const struct pci_device_id *ent)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) int retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) * First let's find out if we are on an iLO2+ server. We will
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) * not run on a legacy ASM box.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) * So we only support the G5 ProLiant servers and higher.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) if (dev->subsystem_vendor != PCI_VENDOR_ID_HP &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) dev->subsystem_vendor != PCI_VENDOR_ID_HP_3PAR) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) dev_warn(&dev->dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) "This server does not have an iLO2+ ASIC.\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) if (pci_match_id(hpwdt_blacklist, dev)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) dev_dbg(&dev->dev, "Not supported on this device\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) if (pci_enable_device(dev)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) dev_warn(&dev->dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) "Not possible to enable PCI Device: 0x%x:0x%x.\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) ent->vendor, ent->device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) pci_mem_addr = pci_iomap(dev, 1, 0x80);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) if (!pci_mem_addr) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) dev_warn(&dev->dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) "Unable to detect the iLO2+ server memory.\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) retval = -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) goto error_pci_iomap;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) hpwdt_nmistat = pci_mem_addr + 0x6e;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) hpwdt_timer_reg = pci_mem_addr + 0x70;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) hpwdt_timer_con = pci_mem_addr + 0x72;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) /* Have the core update running timer until user space is ready */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) if (hpwdt_hw_is_running()) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) dev_info(&dev->dev, "timer is running\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) set_bit(WDOG_HW_RUNNING, &hpwdt_dev.status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) /* Initialize NMI Decoding functionality */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) retval = hpwdt_init_nmi_decoding(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) if (retval != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) goto error_init_nmi_decoding;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) watchdog_stop_on_unregister(&hpwdt_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) watchdog_set_nowayout(&hpwdt_dev, nowayout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) watchdog_init_timeout(&hpwdt_dev, soft_margin, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) if (pretimeout && hpwdt_dev.timeout <= PRETIMEOUT_SEC) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) dev_warn(&dev->dev, "timeout <= pretimeout. Setting pretimeout to zero\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) pretimeout = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) hpwdt_dev.pretimeout = pretimeout ? PRETIMEOUT_SEC : 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) kdumptimeout = min(kdumptimeout, HPWDT_MAX_TIMER);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) hpwdt_dev.parent = &dev->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) retval = watchdog_register_device(&hpwdt_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) if (retval < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) goto error_wd_register;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) dev_info(&dev->dev, "HPE Watchdog Timer Driver: Version: %s\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) HPWDT_VERSION);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) dev_info(&dev->dev, "timeout: %d seconds (nowayout=%d)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) hpwdt_dev.timeout, nowayout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) dev_info(&dev->dev, "pretimeout: %s.\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) pretimeout ? "on" : "off");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) dev_info(&dev->dev, "kdumptimeout: %d.\n", kdumptimeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) if (dev->subsystem_vendor == PCI_VENDOR_ID_HP_3PAR)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) ilo5 = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) error_wd_register:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) hpwdt_exit_nmi_decoding();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) error_init_nmi_decoding:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) pci_iounmap(dev, pci_mem_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) error_pci_iomap:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) pci_disable_device(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) return retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) static void hpwdt_exit(struct pci_dev *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) watchdog_unregister_device(&hpwdt_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) hpwdt_exit_nmi_decoding();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) pci_iounmap(dev, pci_mem_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) pci_disable_device(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) static struct pci_driver hpwdt_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) .name = "hpwdt",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) .id_table = hpwdt_devices,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) .probe = hpwdt_init_one,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) .remove = hpwdt_exit,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) MODULE_AUTHOR("Tom Mingarelli");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) MODULE_DESCRIPTION("hpe watchdog driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) MODULE_VERSION(HPWDT_VERSION);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) module_param(soft_margin, int, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) module_param_named(timeout, soft_margin, int, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395) MODULE_PARM_DESC(timeout, "Alias of soft_margin");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) module_param(nowayout, bool, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) module_param(kdumptimeout, int, 0444);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) MODULE_PARM_DESC(kdumptimeout, "Timeout applied for crash kernel transition in seconds");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) #ifdef CONFIG_HPWDT_NMI_DECODING
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) module_param(pretimeout, bool, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) MODULE_PARM_DESC(pretimeout, "Watchdog pretimeout enabled");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) module_pci_driver(hpwdt_driver);