| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * ACPI Hardware Watchdog (WDAT) driver. |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 2016, Intel Corporation |
|---|
| 5 | 6 | * Author: Mika Westerberg <mika.westerberg@linux.intel.com> |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 8 | | - * it under the terms of the GNU General Public License version 2 as |
|---|
| 9 | | - * published by the Free Software Foundation. |
|---|
| 10 | 7 | */ |
|---|
| 11 | 8 | |
|---|
| 12 | 9 | #include <linux/acpi.h> |
|---|
| .. | .. |
|---|
| 56 | 53 | module_param(nowayout, bool, 0); |
|---|
| 57 | 54 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" |
|---|
| 58 | 55 | __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
|---|
| 56 | + |
|---|
| 57 | +#define WDAT_DEFAULT_TIMEOUT 30 |
|---|
| 58 | + |
|---|
| 59 | +static int timeout = WDAT_DEFAULT_TIMEOUT; |
|---|
| 60 | +module_param(timeout, int, 0); |
|---|
| 61 | +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default=" |
|---|
| 62 | + __MODULE_STRING(WDAT_DEFAULT_TIMEOUT) ")"); |
|---|
| 59 | 63 | |
|---|
| 60 | 64 | static int wdat_wdt_read(struct wdat_wdt *wdat, |
|---|
| 61 | 65 | const struct wdat_instruction *instr, u32 *value) |
|---|
| .. | .. |
|---|
| 205 | 209 | * WDAT specification says that the watchdog is required to reboot |
|---|
| 206 | 210 | * the system when it fires. However, it also states that it is |
|---|
| 207 | 211 | * recommeded to make it configurable through hardware register. We |
|---|
| 208 | | - * enable reboot now if it is configrable, just in case. |
|---|
| 212 | + * enable reboot now if it is configurable, just in case. |
|---|
| 209 | 213 | */ |
|---|
| 210 | 214 | ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_REBOOT, 0, NULL); |
|---|
| 211 | 215 | if (ret && ret != -EOPNOTSUPP) { |
|---|
| .. | .. |
|---|
| 287 | 291 | struct wdat_wdt *wdat = to_wdat_wdt(wdd); |
|---|
| 288 | 292 | u32 periods = 0; |
|---|
| 289 | 293 | |
|---|
| 290 | | - wdat_wdt_run_action(wdat, ACPI_WDAT_GET_COUNTDOWN, 0, &periods); |
|---|
| 294 | + wdat_wdt_run_action(wdat, ACPI_WDAT_GET_CURRENT_COUNTDOWN, 0, &periods); |
|---|
| 291 | 295 | return periods * wdat->period / 1000; |
|---|
| 292 | 296 | } |
|---|
| 293 | 297 | |
|---|
| .. | .. |
|---|
| 308 | 312 | |
|---|
| 309 | 313 | static int wdat_wdt_probe(struct platform_device *pdev) |
|---|
| 310 | 314 | { |
|---|
| 315 | + struct device *dev = &pdev->dev; |
|---|
| 311 | 316 | const struct acpi_wdat_entry *entries; |
|---|
| 312 | 317 | const struct acpi_table_wdat *tbl; |
|---|
| 313 | 318 | struct wdat_wdt *wdat; |
|---|
| .. | .. |
|---|
| 321 | 326 | if (ACPI_FAILURE(status)) |
|---|
| 322 | 327 | return -ENODEV; |
|---|
| 323 | 328 | |
|---|
| 324 | | - wdat = devm_kzalloc(&pdev->dev, sizeof(*wdat), GFP_KERNEL); |
|---|
| 329 | + wdat = devm_kzalloc(dev, sizeof(*wdat), GFP_KERNEL); |
|---|
| 325 | 330 | if (!wdat) |
|---|
| 326 | 331 | return -ENOMEM; |
|---|
| 327 | 332 | |
|---|
| 328 | | - regs = devm_kcalloc(&pdev->dev, pdev->num_resources, sizeof(*regs), |
|---|
| 333 | + regs = devm_kcalloc(dev, pdev->num_resources, sizeof(*regs), |
|---|
| 329 | 334 | GFP_KERNEL); |
|---|
| 330 | 335 | if (!regs) |
|---|
| 331 | 336 | return -ENOMEM; |
|---|
| .. | .. |
|---|
| 350 | 355 | |
|---|
| 351 | 356 | res = &pdev->resource[i]; |
|---|
| 352 | 357 | if (resource_type(res) == IORESOURCE_MEM) { |
|---|
| 353 | | - reg = devm_ioremap_resource(&pdev->dev, res); |
|---|
| 358 | + reg = devm_ioremap_resource(dev, res); |
|---|
| 354 | 359 | if (IS_ERR(reg)) |
|---|
| 355 | 360 | return PTR_ERR(reg); |
|---|
| 356 | 361 | } else if (resource_type(res) == IORESOURCE_IO) { |
|---|
| 357 | | - reg = devm_ioport_map(&pdev->dev, res->start, 1); |
|---|
| 362 | + reg = devm_ioport_map(dev, res->start, 1); |
|---|
| 358 | 363 | if (!reg) |
|---|
| 359 | 364 | return -ENOMEM; |
|---|
| 360 | 365 | } else { |
|---|
| 361 | | - dev_err(&pdev->dev, "Unsupported resource\n"); |
|---|
| 366 | + dev_err(dev, "Unsupported resource\n"); |
|---|
| 362 | 367 | return -EINVAL; |
|---|
| 363 | 368 | } |
|---|
| 364 | 369 | |
|---|
| .. | .. |
|---|
| 376 | 381 | |
|---|
| 377 | 382 | action = entries[i].action; |
|---|
| 378 | 383 | if (action >= MAX_WDAT_ACTIONS) { |
|---|
| 379 | | - dev_dbg(&pdev->dev, "Skipping unknown action: %u\n", |
|---|
| 380 | | - action); |
|---|
| 384 | + dev_dbg(dev, "Skipping unknown action: %u\n", action); |
|---|
| 381 | 385 | continue; |
|---|
| 382 | 386 | } |
|---|
| 383 | 387 | |
|---|
| 384 | | - instr = devm_kzalloc(&pdev->dev, sizeof(*instr), GFP_KERNEL); |
|---|
| 388 | + instr = devm_kzalloc(dev, sizeof(*instr), GFP_KERNEL); |
|---|
| 385 | 389 | if (!instr) |
|---|
| 386 | 390 | return -ENOMEM; |
|---|
| 387 | 391 | |
|---|
| .. | .. |
|---|
| 398 | 402 | } else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) { |
|---|
| 399 | 403 | r.flags = IORESOURCE_IO; |
|---|
| 400 | 404 | } else { |
|---|
| 401 | | - dev_dbg(&pdev->dev, "Unsupported address space: %d\n", |
|---|
| 405 | + dev_dbg(dev, "Unsupported address space: %d\n", |
|---|
| 402 | 406 | gas->space_id); |
|---|
| 403 | 407 | continue; |
|---|
| 404 | 408 | } |
|---|
| .. | .. |
|---|
| 413 | 417 | } |
|---|
| 414 | 418 | |
|---|
| 415 | 419 | if (!instr->reg) { |
|---|
| 416 | | - dev_err(&pdev->dev, "I/O resource not found\n"); |
|---|
| 420 | + dev_err(dev, "I/O resource not found\n"); |
|---|
| 417 | 421 | return -EINVAL; |
|---|
| 418 | 422 | } |
|---|
| 419 | 423 | |
|---|
| 420 | 424 | instructions = wdat->instructions[action]; |
|---|
| 421 | 425 | if (!instructions) { |
|---|
| 422 | | - instructions = devm_kzalloc(&pdev->dev, |
|---|
| 423 | | - sizeof(*instructions), GFP_KERNEL); |
|---|
| 426 | + instructions = devm_kzalloc(dev, |
|---|
| 427 | + sizeof(*instructions), |
|---|
| 428 | + GFP_KERNEL); |
|---|
| 424 | 429 | if (!instructions) |
|---|
| 425 | 430 | return -ENOMEM; |
|---|
| 426 | 431 | |
|---|
| .. | .. |
|---|
| 440 | 445 | |
|---|
| 441 | 446 | platform_set_drvdata(pdev, wdat); |
|---|
| 442 | 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 | + |
|---|
| 443 | 464 | watchdog_set_nowayout(&wdat->wdd, nowayout); |
|---|
| 444 | | - return devm_watchdog_register_device(&pdev->dev, &wdat->wdd); |
|---|
| 465 | + watchdog_stop_on_reboot(&wdat->wdd); |
|---|
| 466 | + return devm_watchdog_register_device(dev, &wdat->wdd); |
|---|
| 445 | 467 | } |
|---|
| 446 | 468 | |
|---|
| 447 | 469 | #ifdef CONFIG_PM_SLEEP |
|---|