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
  25#define HPWDT_VERSION                   "2.0.3"
  26#define SECS_TO_TICKS(secs)             ((secs) * 1000 / 128)
  27#define TICKS_TO_SECS(ticks)            ((ticks) * 128 / 1000)
  28#define HPWDT_MAX_TICKS                 65535
  29#define HPWDT_MAX_TIMER                 TICKS_TO_SECS(HPWDT_MAX_TICKS)
  30#define DEFAULT_MARGIN                  30
  31#define PRETIMEOUT_SEC                  9
  32
  33static bool ilo5;
  34static unsigned int soft_margin = DEFAULT_MARGIN;       /* in seconds */
  35static bool nowayout = WATCHDOG_NOWAYOUT;
  36static bool pretimeout = IS_ENABLED(CONFIG_HPWDT_NMI_DECODING);
  37static int kdumptimeout = -1;
  38
  39static void __iomem *pci_mem_addr;              /* the PCI-memory address */
  40static unsigned long __iomem *hpwdt_nmistat;
  41static unsigned long __iomem *hpwdt_timer_reg;
  42static unsigned long __iomem *hpwdt_timer_con;
  43
  44static const struct pci_device_id hpwdt_devices[] = {
  45        { PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB203) },   /* iLO2 */
  46        { PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3306) },       /* iLO3 */
  47        {0},                    /* terminate list */
  48};
  49MODULE_DEVICE_TABLE(pci, hpwdt_devices);
  50
  51static const struct pci_device_id hpwdt_blacklist[] = {
  52        { PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3306, PCI_VENDOR_ID_HP, 0x1979) }, /* auxilary iLO */
  53        { PCI_DEVICE_SUB(PCI_VENDOR_ID_HP, 0x3306, PCI_VENDOR_ID_HP_3PAR, 0x0289) },  /* CL */
  54        {0},                    /* terminate list */
  55};
  56
  57static struct watchdog_device hpwdt_dev;
  58/*
  59 *      Watchdog operations
  60 */
  61static int hpwdt_hw_is_running(void)
  62{
  63        return ioread8(hpwdt_timer_con) & 0x01;
  64}
  65
  66static int hpwdt_start(struct watchdog_device *wdd)
  67{
  68        int control = 0x81 | (pretimeout ? 0x4 : 0);
  69        int reload = SECS_TO_TICKS(min(wdd->timeout, wdd->max_hw_heartbeat_ms/1000));
  70
  71        dev_dbg(wdd->parent, "start watchdog 0x%08x:0x%08x:0x%02x\n", wdd->timeout, reload, control);
  72        iowrite16(reload, hpwdt_timer_reg);
  73        iowrite8(control, hpwdt_timer_con);
  74
  75        return 0;
  76}
  77
  78static void hpwdt_stop(void)
  79{
  80        unsigned long data;
  81
  82        pr_debug("stop  watchdog\n");
  83
  84        data = ioread8(hpwdt_timer_con);
  85        data &= 0xFE;
  86        iowrite8(data, hpwdt_timer_con);
  87}
  88
  89static int hpwdt_stop_core(struct watchdog_device *wdd)
  90{
  91        hpwdt_stop();
  92
  93        return 0;
  94}
  95
  96static void hpwdt_ping_ticks(int val)
  97{
  98        val = min(val, HPWDT_MAX_TICKS);
  99        iowrite16(val, hpwdt_timer_reg);
 100}
 101
 102static int hpwdt_ping(struct watchdog_device *wdd)
 103{
 104        int reload = SECS_TO_TICKS(min(wdd->timeout, wdd->max_hw_heartbeat_ms/1000));
 105
 106        dev_dbg(wdd->parent, "ping  watchdog 0x%08x:0x%08x\n", wdd->timeout, reload);
 107        hpwdt_ping_ticks(reload);
 108
 109        return 0;
 110}
 111
 112static unsigned int hpwdt_gettimeleft(struct watchdog_device *wdd)
 113{
 114        return TICKS_TO_SECS(ioread16(hpwdt_timer_reg));
 115}
 116
 117static int hpwdt_settimeout(struct watchdog_device *wdd, unsigned int val)
 118{
 119        dev_dbg(wdd->parent, "set_timeout = %d\n", val);
 120
 121        wdd->timeout = val;
 122        if (val <= wdd->pretimeout) {
 123                dev_dbg(wdd->parent, "pretimeout < timeout. Setting to zero\n");
 124                wdd->pretimeout = 0;
 125                pretimeout = 0;
 126                if (watchdog_active(wdd))
 127                        hpwdt_start(wdd);
 128        }
 129        hpwdt_ping(wdd);
 130
 131        return 0;
 132}
 133
 134#ifdef CONFIG_HPWDT_NMI_DECODING
 135static int hpwdt_set_pretimeout(struct watchdog_device *wdd, unsigned int req)
 136{
 137        unsigned int val = 0;
 138
 139        dev_dbg(wdd->parent, "set_pretimeout = %d\n", req);
 140        if (req) {
 141                val = PRETIMEOUT_SEC;
 142                if (val >= wdd->timeout)
 143                        return -EINVAL;
 144        }
 145
 146        if (val != req)
 147                dev_dbg(wdd->parent, "Rounding pretimeout to: %d\n", val);
 148
 149        wdd->pretimeout = val;
 150        pretimeout = !!val;
 151
 152        if (watchdog_active(wdd))
 153                hpwdt_start(wdd);
 154
 155        return 0;
 156}
 157
 158static int hpwdt_my_nmi(void)
 159{
 160        return ioread8(hpwdt_nmistat) & 0x6;
 161}
 162
 163/*
 164 *      NMI Handler
 165 */
 166static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs)
 167{
 168        unsigned int mynmi = hpwdt_my_nmi();
 169        static char panic_msg[] =
 170                "00: An NMI occurred. Depending on your system the reason "
 171                "for the NMI is logged in any one of the following resources:\n"
 172                "1. Integrated Management Log (IML)\n"
 173                "2. OA Syslog\n"
 174                "3. OA Forward Progress Log\n"
 175                "4. iLO Event Log";
 176
 177        if (ilo5 && ulReason == NMI_UNKNOWN && !mynmi)
 178                return NMI_DONE;
 179
 180        if (ilo5 && !pretimeout && !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 interfaces
 210 */
 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_DECODING
 220        .set_pretimeout = hpwdt_set_pretimeout,
 221#endif
 222};
 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 & Exit
 236 */
 237
 238static int hpwdt_init_nmi_decoding(struct pci_dev *dev)
 239{
 240#ifdef CONFIG_HPWDT_NMI_DECODING
 241        int retval;
 242        /*
 243         * Only one function can register for NMI_UNKNOWN
 244         */
 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_DECODING
 276        unregister_nmi_handler(NMI_UNKNOWN, "hpwdt");
 277        unregister_nmi_handler(NMI_SERR, "hpwdt");
 278        unregister_nmi_handler(NMI_IO_CHECK, "hpwdt");
 279#endif
 280}
 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 will
 289         * 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 (pretimeout && hpwdt_dev.timeout <= PRETIMEOUT_SEC) {
 338                dev_warn(&dev->dev, "timeout <= pretimeout. Setting pretimeout to zero\n");
 339                pretimeout = 0;
 340        }
 341        hpwdt_dev.pretimeout = pretimeout ? PRETIMEOUT_SEC : 0;
 342        kdumptimeout = min(kdumptimeout, HPWDT_MAX_TIMER);
 343
 344        hpwdt_dev.parent = &dev->dev;
 345        retval = watchdog_register_device(&hpwdt_dev);
 346        if (retval < 0)
 347                goto error_wd_register;
 348
 349        dev_info(&dev->dev, "HPE Watchdog Timer Driver: Version: %s\n",
 350                                HPWDT_VERSION);
 351        dev_info(&dev->dev, "timeout: %d seconds (nowayout=%d)\n",
 352                                hpwdt_dev.timeout, nowayout);
 353        dev_info(&dev->dev, "pretimeout: %s.\n",
 354                                pretimeout ? "on" : "off");
 355        dev_info(&dev->dev, "kdumptimeout: %d.\n", kdumptimeout);
 356
 357        if (dev->subsystem_vendor == PCI_VENDOR_ID_HP_3PAR)
 358                ilo5 = true;
 359
 360        return 0;
 361
 362error_wd_register:
 363        hpwdt_exit_nmi_decoding();
 364error_init_nmi_decoding:
 365        pci_iounmap(dev, pci_mem_addr);
 366error_pci_iomap:
 367        pci_disable_device(dev);
 368        return retval;
 369}
 370
 371static void hpwdt_exit(struct pci_dev *dev)
 372{
 373        watchdog_unregister_device(&hpwdt_dev);
 374        hpwdt_exit_nmi_decoding();
 375        pci_iounmap(dev, pci_mem_addr);
 376        pci_disable_device(dev);
 377}
 378
 379static struct pci_driver hpwdt_driver = {
 380        .name = "hpwdt",
 381        .id_table = hpwdt_devices,
 382        .probe = hpwdt_init_one,
 383        .remove = hpwdt_exit,
 384};
 385
 386MODULE_AUTHOR("Tom Mingarelli");
 387MODULE_DESCRIPTION("hpe watchdog driver");
 388MODULE_LICENSE("GPL");
 389MODULE_VERSION(HPWDT_VERSION);
 390
 391module_param(soft_margin, int, 0);
 392MODULE_PARM_DESC(soft_margin, "Watchdog timeout in seconds");
 393
 394module_param_named(timeout, soft_margin, int, 0);
 395MODULE_PARM_DESC(timeout, "Alias of soft_margin");
 396
 397module_param(nowayout, bool, 0);
 398MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
 399                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 400
 401module_param(kdumptimeout, int, 0444);
 402MODULE_PARM_DESC(kdumptimeout, "Timeout applied for crash kernel transition in seconds");
 403
 404#ifdef CONFIG_HPWDT_NMI_DECODING
 405module_param(pretimeout, bool, 0);
 406MODULE_PARM_DESC(pretimeout, "Watchdog pretimeout enabled");
 407#endif
 408
 409module_pci_driver(hpwdt_driver);
 410