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