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