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