linux/drivers/watchdog/wdat_wdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * ACPI Hardware Watchdog (WDAT) driver.
   4 *
   5 * Copyright (C) 2016, Intel Corporation
   6 * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
   7 */
   8
   9#include <linux/acpi.h>
  10#include <linux/ioport.h>
  11#include <linux/module.h>
  12#include <linux/platform_device.h>
  13#include <linux/pm.h>
  14#include <linux/watchdog.h>
  15
  16#define MAX_WDAT_ACTIONS ACPI_WDAT_ACTION_RESERVED
  17
  18/**
  19 * struct wdat_instruction - Single ACPI WDAT instruction
  20 * @entry: Copy of the ACPI table instruction
  21 * @reg: Register the instruction is accessing
  22 * @node: Next instruction in action sequence
  23 */
  24struct wdat_instruction {
  25        struct acpi_wdat_entry entry;
  26        void __iomem *reg;
  27        struct list_head node;
  28};
  29
  30/**
  31 * struct wdat_wdt - ACPI WDAT watchdog device
  32 * @pdev: Parent platform device
  33 * @wdd: Watchdog core device
  34 * @period: How long is one watchdog period in ms
  35 * @stopped_in_sleep: Is this watchdog stopped by the firmware in S1-S5
  36 * @stopped: Was the watchdog stopped by the driver in suspend
  37 * @instructions: An array of instruction lists indexed by an action number from
  38 *                the WDAT table. There can be %NULL entries for not implemented
  39 *                actions.
  40 */
  41struct wdat_wdt {
  42        struct platform_device *pdev;
  43        struct watchdog_device wdd;
  44        unsigned int period;
  45        bool stopped_in_sleep;
  46        bool stopped;
  47        struct list_head *instructions[MAX_WDAT_ACTIONS];
  48};
  49
  50#define to_wdat_wdt(wdd) container_of(wdd, struct wdat_wdt, wdd)
  51
  52static bool nowayout = WATCHDOG_NOWAYOUT;
  53module_param(nowayout, bool, 0);
  54MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
  55                 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  56
  57#define WDAT_DEFAULT_TIMEOUT    30
  58
  59static int timeout = WDAT_DEFAULT_TIMEOUT;
  60module_param(timeout, int, 0);
  61MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default="
  62                 __MODULE_STRING(WDAT_DEFAULT_TIMEOUT) ")");
  63
  64static int wdat_wdt_read(struct wdat_wdt *wdat,
  65         const struct wdat_instruction *instr, u32 *value)
  66{
  67        const struct acpi_generic_address *gas = &instr->entry.register_region;
  68
  69        switch (gas->access_width) {
  70        case 1:
  71                *value = ioread8(instr->reg);
  72                break;
  73        case 2:
  74                *value = ioread16(instr->reg);
  75                break;
  76        case 3:
  77                *value = ioread32(instr->reg);
  78                break;
  79        default:
  80                return -EINVAL;
  81        }
  82
  83        dev_dbg(&wdat->pdev->dev, "Read %#x from 0x%08llx\n", *value,
  84                gas->address);
  85
  86        return 0;
  87}
  88
  89static int wdat_wdt_write(struct wdat_wdt *wdat,
  90        const struct wdat_instruction *instr, u32 value)
  91{
  92        const struct acpi_generic_address *gas = &instr->entry.register_region;
  93
  94        switch (gas->access_width) {
  95        case 1:
  96                iowrite8((u8)value, instr->reg);
  97                break;
  98        case 2:
  99                iowrite16((u16)value, instr->reg);
 100                break;
 101        case 3:
 102                iowrite32(value, instr->reg);
 103                break;
 104        default:
 105                return -EINVAL;
 106        }
 107
 108        dev_dbg(&wdat->pdev->dev, "Wrote %#x to 0x%08llx\n", value,
 109                gas->address);
 110
 111        return 0;
 112}
 113
 114static int wdat_wdt_run_action(struct wdat_wdt *wdat, unsigned int action,
 115                               u32 param, u32 *retval)
 116{
 117        struct wdat_instruction *instr;
 118
 119        if (action >= ARRAY_SIZE(wdat->instructions))
 120                return -EINVAL;
 121
 122        if (!wdat->instructions[action])
 123                return -EOPNOTSUPP;
 124
 125        dev_dbg(&wdat->pdev->dev, "Running action %#x\n", action);
 126
 127        /* Run each instruction sequentially */
 128        list_for_each_entry(instr, wdat->instructions[action], node) {
 129                const struct acpi_wdat_entry *entry = &instr->entry;
 130                const struct acpi_generic_address *gas;
 131                u32 flags, value, mask, x, y;
 132                bool preserve;
 133                int ret;
 134
 135                gas = &entry->register_region;
 136
 137                preserve = entry->instruction & ACPI_WDAT_PRESERVE_REGISTER;
 138                flags = entry->instruction & ~ACPI_WDAT_PRESERVE_REGISTER;
 139                value = entry->value;
 140                mask = entry->mask;
 141
 142                switch (flags) {
 143                case ACPI_WDAT_READ_VALUE:
 144                        ret = wdat_wdt_read(wdat, instr, &x);
 145                        if (ret)
 146                                return ret;
 147                        x >>= gas->bit_offset;
 148                        x &= mask;
 149                        if (retval)
 150                                *retval = x == value;
 151                        break;
 152
 153                case ACPI_WDAT_READ_COUNTDOWN:
 154                        ret = wdat_wdt_read(wdat, instr, &x);
 155                        if (ret)
 156                                return ret;
 157                        x >>= gas->bit_offset;
 158                        x &= mask;
 159                        if (retval)
 160                                *retval = x;
 161                        break;
 162
 163                case ACPI_WDAT_WRITE_VALUE:
 164                        x = value & mask;
 165                        x <<= gas->bit_offset;
 166                        if (preserve) {
 167                                ret = wdat_wdt_read(wdat, instr, &y);
 168                                if (ret)
 169                                        return ret;
 170                                y = y & ~(mask << gas->bit_offset);
 171                                x |= y;
 172                        }
 173                        ret = wdat_wdt_write(wdat, instr, x);
 174                        if (ret)
 175                                return ret;
 176                        break;
 177
 178                case ACPI_WDAT_WRITE_COUNTDOWN:
 179                        x = param;
 180                        x &= mask;
 181                        x <<= gas->bit_offset;
 182                        if (preserve) {
 183                                ret = wdat_wdt_read(wdat, instr, &y);
 184                                if (ret)
 185                                        return ret;
 186                                y = y & ~(mask << gas->bit_offset);
 187                                x |= y;
 188                        }
 189                        ret = wdat_wdt_write(wdat, instr, x);
 190                        if (ret)
 191                                return ret;
 192                        break;
 193
 194                default:
 195                        dev_err(&wdat->pdev->dev, "Unknown instruction: %u\n",
 196                                flags);
 197                        return -EINVAL;
 198                }
 199        }
 200
 201        return 0;
 202}
 203
 204static int wdat_wdt_enable_reboot(struct wdat_wdt *wdat)
 205{
 206        int ret;
 207
 208        /*
 209         * WDAT specification says that the watchdog is required to reboot
 210         * the system when it fires. However, it also states that it is
 211         * recommended to make it configurable through hardware register. We
 212         * enable reboot now if it is configurable, just in case.
 213         */
 214        ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_REBOOT, 0, NULL);
 215        if (ret && ret != -EOPNOTSUPP) {
 216                dev_err(&wdat->pdev->dev,
 217                        "Failed to enable reboot when watchdog triggers\n");
 218                return ret;
 219        }
 220
 221        return 0;
 222}
 223
 224static void wdat_wdt_boot_status(struct wdat_wdt *wdat)
 225{
 226        u32 boot_status = 0;
 227        int ret;
 228
 229        ret = wdat_wdt_run_action(wdat, ACPI_WDAT_GET_STATUS, 0, &boot_status);
 230        if (ret && ret != -EOPNOTSUPP) {
 231                dev_err(&wdat->pdev->dev, "Failed to read boot status\n");
 232                return;
 233        }
 234
 235        if (boot_status)
 236                wdat->wdd.bootstatus = WDIOF_CARDRESET;
 237
 238        /* Clear the boot status in case BIOS did not do it */
 239        ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_STATUS, 0, NULL);
 240        if (ret && ret != -EOPNOTSUPP)
 241                dev_err(&wdat->pdev->dev, "Failed to clear boot status\n");
 242}
 243
 244static void wdat_wdt_set_running(struct wdat_wdt *wdat)
 245{
 246        u32 running = 0;
 247        int ret;
 248
 249        ret = wdat_wdt_run_action(wdat, ACPI_WDAT_GET_RUNNING_STATE, 0,
 250                                  &running);
 251        if (ret && ret != -EOPNOTSUPP)
 252                dev_err(&wdat->pdev->dev, "Failed to read running state\n");
 253
 254        if (running)
 255                set_bit(WDOG_HW_RUNNING, &wdat->wdd.status);
 256}
 257
 258static int wdat_wdt_start(struct watchdog_device *wdd)
 259{
 260        return wdat_wdt_run_action(to_wdat_wdt(wdd),
 261                                   ACPI_WDAT_SET_RUNNING_STATE, 0, NULL);
 262}
 263
 264static int wdat_wdt_stop(struct watchdog_device *wdd)
 265{
 266        return wdat_wdt_run_action(to_wdat_wdt(wdd),
 267                                   ACPI_WDAT_SET_STOPPED_STATE, 0, NULL);
 268}
 269
 270static int wdat_wdt_ping(struct watchdog_device *wdd)
 271{
 272        return wdat_wdt_run_action(to_wdat_wdt(wdd), ACPI_WDAT_RESET, 0, NULL);
 273}
 274
 275static int wdat_wdt_set_timeout(struct watchdog_device *wdd,
 276                                unsigned int timeout)
 277{
 278        struct wdat_wdt *wdat = to_wdat_wdt(wdd);
 279        unsigned int periods;
 280        int ret;
 281
 282        periods = timeout * 1000 / wdat->period;
 283        ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_COUNTDOWN, periods, NULL);
 284        if (!ret)
 285                wdd->timeout = timeout;
 286        return ret;
 287}
 288
 289static unsigned int wdat_wdt_get_timeleft(struct watchdog_device *wdd)
 290{
 291        struct wdat_wdt *wdat = to_wdat_wdt(wdd);
 292        u32 periods = 0;
 293
 294        wdat_wdt_run_action(wdat, ACPI_WDAT_GET_CURRENT_COUNTDOWN, 0, &periods);
 295        return periods * wdat->period / 1000;
 296}
 297
 298static const struct watchdog_info wdat_wdt_info = {
 299        .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
 300        .firmware_version = 0,
 301        .identity = "wdat_wdt",
 302};
 303
 304static const struct watchdog_ops wdat_wdt_ops = {
 305        .owner = THIS_MODULE,
 306        .start = wdat_wdt_start,
 307        .stop = wdat_wdt_stop,
 308        .ping = wdat_wdt_ping,
 309        .set_timeout = wdat_wdt_set_timeout,
 310        .get_timeleft = wdat_wdt_get_timeleft,
 311};
 312
 313static int wdat_wdt_probe(struct platform_device *pdev)
 314{
 315        struct device *dev = &pdev->dev;
 316        const struct acpi_wdat_entry *entries;
 317        const struct acpi_table_wdat *tbl;
 318        struct wdat_wdt *wdat;
 319        struct resource *res;
 320        void __iomem **regs;
 321        acpi_status status;
 322        int i, ret;
 323
 324        status = acpi_get_table(ACPI_SIG_WDAT, 0,
 325                                (struct acpi_table_header **)&tbl);
 326        if (ACPI_FAILURE(status))
 327                return -ENODEV;
 328
 329        wdat = devm_kzalloc(dev, sizeof(*wdat), GFP_KERNEL);
 330        if (!wdat)
 331                return -ENOMEM;
 332
 333        regs = devm_kcalloc(dev, pdev->num_resources, sizeof(*regs),
 334                            GFP_KERNEL);
 335        if (!regs)
 336                return -ENOMEM;
 337
 338        /* WDAT specification wants to have >= 1ms period */
 339        if (tbl->timer_period < 1)
 340                return -EINVAL;
 341        if (tbl->min_count > tbl->max_count)
 342                return -EINVAL;
 343
 344        wdat->period = tbl->timer_period;
 345        wdat->wdd.min_hw_heartbeat_ms = wdat->period * tbl->min_count;
 346        wdat->wdd.max_hw_heartbeat_ms = wdat->period * tbl->max_count;
 347        wdat->stopped_in_sleep = tbl->flags & ACPI_WDAT_STOPPED;
 348        wdat->wdd.info = &wdat_wdt_info;
 349        wdat->wdd.ops = &wdat_wdt_ops;
 350        wdat->pdev = pdev;
 351
 352        /* Request and map all resources */
 353        for (i = 0; i < pdev->num_resources; i++) {
 354                void __iomem *reg;
 355
 356                res = &pdev->resource[i];
 357                if (resource_type(res) == IORESOURCE_MEM) {
 358                        reg = devm_ioremap_resource(dev, res);
 359                        if (IS_ERR(reg))
 360                                return PTR_ERR(reg);
 361                } else if (resource_type(res) == IORESOURCE_IO) {
 362                        reg = devm_ioport_map(dev, res->start, 1);
 363                        if (!reg)
 364                                return -ENOMEM;
 365                } else {
 366                        dev_err(dev, "Unsupported resource\n");
 367                        return -EINVAL;
 368                }
 369
 370                regs[i] = reg;
 371        }
 372
 373        entries = (struct acpi_wdat_entry *)(tbl + 1);
 374        for (i = 0; i < tbl->entries; i++) {
 375                const struct acpi_generic_address *gas;
 376                struct wdat_instruction *instr;
 377                struct list_head *instructions;
 378                unsigned int action;
 379                struct resource r;
 380                int j;
 381
 382                action = entries[i].action;
 383                if (action >= MAX_WDAT_ACTIONS) {
 384                        dev_dbg(dev, "Skipping unknown action: %u\n", action);
 385                        continue;
 386                }
 387
 388                instr = devm_kzalloc(dev, sizeof(*instr), GFP_KERNEL);
 389                if (!instr)
 390                        return -ENOMEM;
 391
 392                INIT_LIST_HEAD(&instr->node);
 393                instr->entry = entries[i];
 394
 395                gas = &entries[i].register_region;
 396
 397                memset(&r, 0, sizeof(r));
 398                r.start = gas->address;
 399                r.end = r.start + ACPI_ACCESS_BYTE_WIDTH(gas->access_width) - 1;
 400                if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
 401                        r.flags = IORESOURCE_MEM;
 402                } else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
 403                        r.flags = IORESOURCE_IO;
 404                } else {
 405                        dev_dbg(dev, "Unsupported address space: %d\n",
 406                                gas->space_id);
 407                        continue;
 408                }
 409
 410                /* Find the matching resource */
 411                for (j = 0; j < pdev->num_resources; j++) {
 412                        res = &pdev->resource[j];
 413                        if (resource_contains(res, &r)) {
 414                                instr->reg = regs[j] + r.start - res->start;
 415                                break;
 416                        }
 417                }
 418
 419                if (!instr->reg) {
 420                        dev_err(dev, "I/O resource not found\n");
 421                        return -EINVAL;
 422                }
 423
 424                instructions = wdat->instructions[action];
 425                if (!instructions) {
 426                        instructions = devm_kzalloc(dev,
 427                                                    sizeof(*instructions),
 428                                                    GFP_KERNEL);
 429                        if (!instructions)
 430                                return -ENOMEM;
 431
 432                        INIT_LIST_HEAD(instructions);
 433                        wdat->instructions[action] = instructions;
 434                }
 435
 436                list_add_tail(&instr->node, instructions);
 437        }
 438
 439        wdat_wdt_boot_status(wdat);
 440        wdat_wdt_set_running(wdat);
 441
 442        ret = wdat_wdt_enable_reboot(wdat);
 443        if (ret)
 444                return ret;
 445
 446        platform_set_drvdata(pdev, wdat);
 447
 448        /*
 449         * Set initial timeout so that userspace has time to configure the
 450         * watchdog properly after it has opened the device. In some cases
 451         * the BIOS default is too short and causes immediate reboot.
 452         */
 453        if (timeout * 1000 < wdat->wdd.min_hw_heartbeat_ms ||
 454            timeout * 1000 > wdat->wdd.max_hw_heartbeat_ms) {
 455                dev_warn(dev, "Invalid timeout %d given, using %d\n",
 456                         timeout, WDAT_DEFAULT_TIMEOUT);
 457                timeout = WDAT_DEFAULT_TIMEOUT;
 458        }
 459
 460        ret = wdat_wdt_set_timeout(&wdat->wdd, timeout);
 461        if (ret)
 462                return ret;
 463
 464        watchdog_set_nowayout(&wdat->wdd, nowayout);
 465        return devm_watchdog_register_device(dev, &wdat->wdd);
 466}
 467
 468#ifdef CONFIG_PM_SLEEP
 469static int wdat_wdt_suspend_noirq(struct device *dev)
 470{
 471        struct wdat_wdt *wdat = dev_get_drvdata(dev);
 472        int ret;
 473
 474        if (!watchdog_active(&wdat->wdd))
 475                return 0;
 476
 477        /*
 478         * We need to stop the watchdog if firmware is not doing it or if we
 479         * are going suspend to idle (where firmware is not involved). If
 480         * firmware is stopping the watchdog we kick it here one more time
 481         * to give it some time.
 482         */
 483        wdat->stopped = false;
 484        if (acpi_target_system_state() == ACPI_STATE_S0 ||
 485            !wdat->stopped_in_sleep) {
 486                ret = wdat_wdt_stop(&wdat->wdd);
 487                if (!ret)
 488                        wdat->stopped = true;
 489        } else {
 490                ret = wdat_wdt_ping(&wdat->wdd);
 491        }
 492
 493        return ret;
 494}
 495
 496static int wdat_wdt_resume_noirq(struct device *dev)
 497{
 498        struct wdat_wdt *wdat = dev_get_drvdata(dev);
 499        int ret;
 500
 501        if (!watchdog_active(&wdat->wdd))
 502                return 0;
 503
 504        if (!wdat->stopped) {
 505                /*
 506                 * Looks like the boot firmware reinitializes the watchdog
 507                 * before it hands off to the OS on resume from sleep so we
 508                 * stop and reprogram the watchdog here.
 509                 */
 510                ret = wdat_wdt_stop(&wdat->wdd);
 511                if (ret)
 512                        return ret;
 513
 514                ret = wdat_wdt_set_timeout(&wdat->wdd, wdat->wdd.timeout);
 515                if (ret)
 516                        return ret;
 517
 518                ret = wdat_wdt_enable_reboot(wdat);
 519                if (ret)
 520                        return ret;
 521
 522                ret = wdat_wdt_ping(&wdat->wdd);
 523                if (ret)
 524                        return ret;
 525        }
 526
 527        return wdat_wdt_start(&wdat->wdd);
 528}
 529#endif
 530
 531static const struct dev_pm_ops wdat_wdt_pm_ops = {
 532        SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(wdat_wdt_suspend_noirq,
 533                                      wdat_wdt_resume_noirq)
 534};
 535
 536static struct platform_driver wdat_wdt_driver = {
 537        .probe = wdat_wdt_probe,
 538        .driver = {
 539                .name = "wdat_wdt",
 540                .pm = &wdat_wdt_pm_ops,
 541        },
 542};
 543
 544module_platform_driver(wdat_wdt_driver);
 545
 546MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
 547MODULE_DESCRIPTION("ACPI Hardware Watchdog (WDAT) driver");
 548MODULE_LICENSE("GPL v2");
 549MODULE_ALIAS("platform:wdat_wdt");
 550