.. | .. |
---|
| 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 |
---|