.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0+ |
---|
1 | 2 | /* |
---|
2 | 3 | * intel TCO Watchdog Driver |
---|
3 | 4 | * |
---|
4 | 5 | * (c) Copyright 2006-2011 Wim Van Sebroeck <wim@iguana.be>. |
---|
5 | | - * |
---|
6 | | - * This program is free software; you can redistribute it and/or |
---|
7 | | - * modify it under the terms of the GNU General Public License |
---|
8 | | - * as published by the Free Software Foundation; either version |
---|
9 | | - * 2 of the License, or (at your option) any later version. |
---|
10 | 6 | * |
---|
11 | 7 | * Neither Wim Van Sebroeck nor Iguana vzw. admit liability nor |
---|
12 | 8 | * provide warranty for any of this software. This material is |
---|
.. | .. |
---|
52 | 48 | |
---|
53 | 49 | /* Includes */ |
---|
54 | 50 | #include <linux/acpi.h> /* For ACPI support */ |
---|
| 51 | +#include <linux/bits.h> /* For BIT() */ |
---|
55 | 52 | #include <linux/module.h> /* For module specific items */ |
---|
56 | 53 | #include <linux/moduleparam.h> /* For new moduleparam's */ |
---|
57 | 54 | #include <linux/types.h> /* For standard types (like size_t) */ |
---|
.. | .. |
---|
67 | 64 | #include <linux/uaccess.h> /* For copy_to_user/put_user/... */ |
---|
68 | 65 | #include <linux/io.h> /* For inb/outb/... */ |
---|
69 | 66 | #include <linux/platform_data/itco_wdt.h> |
---|
| 67 | +#include <linux/mfd/intel_pmc_bxt.h> |
---|
70 | 68 | |
---|
71 | 69 | #include "iTCO_vendor.h" |
---|
72 | 70 | |
---|
.. | .. |
---|
219 | 217 | return 0; |
---|
220 | 218 | } |
---|
221 | 219 | |
---|
222 | | -static void iTCO_wdt_no_reboot_bit_setup(struct iTCO_wdt_private *p, |
---|
223 | | - struct itco_wdt_platform_data *pdata) |
---|
| 220 | +static int update_no_reboot_bit_cnt(void *priv, bool set) |
---|
224 | 221 | { |
---|
225 | | - if (pdata->update_no_reboot_bit) { |
---|
226 | | - p->update_no_reboot_bit = pdata->update_no_reboot_bit; |
---|
227 | | - p->no_reboot_priv = pdata->no_reboot_priv; |
---|
| 222 | + struct iTCO_wdt_private *p = priv; |
---|
| 223 | + u16 val, newval; |
---|
| 224 | + |
---|
| 225 | + val = inw(TCO1_CNT(p)); |
---|
| 226 | + if (set) |
---|
| 227 | + val |= BIT(0); |
---|
| 228 | + else |
---|
| 229 | + val &= ~BIT(0); |
---|
| 230 | + outw(val, TCO1_CNT(p)); |
---|
| 231 | + newval = inw(TCO1_CNT(p)); |
---|
| 232 | + |
---|
| 233 | + /* make sure the update is successful */ |
---|
| 234 | + return val != newval ? -EIO : 0; |
---|
| 235 | +} |
---|
| 236 | + |
---|
| 237 | +static int update_no_reboot_bit_pmc(void *priv, bool set) |
---|
| 238 | +{ |
---|
| 239 | + struct intel_pmc_dev *pmc = priv; |
---|
| 240 | + u32 bits = PMC_CFG_NO_REBOOT_EN; |
---|
| 241 | + u32 value = set ? bits : 0; |
---|
| 242 | + |
---|
| 243 | + return intel_pmc_gcr_update(pmc, PMC_GCR_PMC_CFG_REG, bits, value); |
---|
| 244 | +} |
---|
| 245 | + |
---|
| 246 | +static void iTCO_wdt_no_reboot_bit_setup(struct iTCO_wdt_private *p, |
---|
| 247 | + struct platform_device *pdev, |
---|
| 248 | + struct itco_wdt_platform_data *pdata) |
---|
| 249 | +{ |
---|
| 250 | + if (pdata->no_reboot_use_pmc) { |
---|
| 251 | + struct intel_pmc_dev *pmc = dev_get_drvdata(pdev->dev.parent); |
---|
| 252 | + |
---|
| 253 | + p->update_no_reboot_bit = update_no_reboot_bit_pmc; |
---|
| 254 | + p->no_reboot_priv = pmc; |
---|
228 | 255 | return; |
---|
229 | 256 | } |
---|
230 | 257 | |
---|
231 | | - if (p->iTCO_version >= 2) |
---|
| 258 | + if (p->iTCO_version >= 6) |
---|
| 259 | + p->update_no_reboot_bit = update_no_reboot_bit_cnt; |
---|
| 260 | + else if (p->iTCO_version >= 2) |
---|
232 | 261 | p->update_no_reboot_bit = update_no_reboot_bit_mem; |
---|
233 | 262 | else if (p->iTCO_version == 1) |
---|
234 | 263 | p->update_no_reboot_bit = update_no_reboot_bit_pci; |
---|
.. | .. |
---|
304 | 333 | |
---|
305 | 334 | spin_lock(&p->io_lock); |
---|
306 | 335 | |
---|
307 | | - iTCO_vendor_pre_keepalive(p->smi_res, wd_dev->timeout); |
---|
308 | | - |
---|
309 | 336 | /* Reload the timer by writing to the TCO Timer Counter register */ |
---|
310 | 337 | if (p->iTCO_version >= 2) { |
---|
311 | 338 | outw(0x01, TCO_RLD(p)); |
---|
.. | .. |
---|
341 | 368 | if ((p->iTCO_version >= 2 && tmrval > 0x3ff) || |
---|
342 | 369 | (p->iTCO_version == 1 && tmrval > 0x03f)) |
---|
343 | 370 | return -EINVAL; |
---|
344 | | - |
---|
345 | | - iTCO_vendor_pre_set_heartbeat(tmrval); |
---|
346 | 371 | |
---|
347 | 372 | /* Write new heartbeat to watchdog */ |
---|
348 | 373 | if (p->iTCO_version >= 2) { |
---|
.. | .. |
---|
447 | 472 | if (!p->tco_res) |
---|
448 | 473 | return -ENODEV; |
---|
449 | 474 | |
---|
450 | | - p->smi_res = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_IO_SMI); |
---|
451 | | - if (!p->smi_res) |
---|
452 | | - return -ENODEV; |
---|
453 | | - |
---|
454 | 475 | p->iTCO_version = pdata->version; |
---|
455 | 476 | p->pci_dev = to_pci_dev(dev->parent); |
---|
456 | 477 | |
---|
457 | | - iTCO_wdt_no_reboot_bit_setup(p, pdata); |
---|
| 478 | + p->smi_res = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_IO_SMI); |
---|
| 479 | + if (p->smi_res) { |
---|
| 480 | + /* The TCO logic uses the TCO_EN bit in the SMI_EN register */ |
---|
| 481 | + if (!devm_request_region(dev, p->smi_res->start, |
---|
| 482 | + resource_size(p->smi_res), |
---|
| 483 | + pdev->name)) { |
---|
| 484 | + pr_err("I/O address 0x%04llx already in use, device disabled\n", |
---|
| 485 | + (u64)SMI_EN(p)); |
---|
| 486 | + return -EBUSY; |
---|
| 487 | + } |
---|
| 488 | + } else if (iTCO_vendorsupport || |
---|
| 489 | + turn_SMI_watchdog_clear_off >= p->iTCO_version) { |
---|
| 490 | + pr_err("SMI I/O resource is missing\n"); |
---|
| 491 | + return -ENODEV; |
---|
| 492 | + } |
---|
| 493 | + |
---|
| 494 | + iTCO_wdt_no_reboot_bit_setup(p, pdev, pdata); |
---|
458 | 495 | |
---|
459 | 496 | /* |
---|
460 | 497 | * Get the Memory-Mapped GCS or PMC register, we need it for the |
---|
461 | 498 | * NO_REBOOT flag (TCO v2 and v3). |
---|
462 | 499 | */ |
---|
463 | | - if (p->iTCO_version >= 2 && !pdata->update_no_reboot_bit) { |
---|
| 500 | + if (p->iTCO_version >= 2 && p->iTCO_version < 6 && |
---|
| 501 | + !pdata->no_reboot_use_pmc) { |
---|
464 | 502 | p->gcs_pmc_res = platform_get_resource(pdev, |
---|
465 | 503 | IORESOURCE_MEM, |
---|
466 | 504 | ICH_RES_MEM_GCS_PMC); |
---|
.. | .. |
---|
479 | 517 | /* Set the NO_REBOOT bit to prevent later reboots, just for sure */ |
---|
480 | 518 | p->update_no_reboot_bit(p->no_reboot_priv, true); |
---|
481 | 519 | |
---|
482 | | - /* The TCO logic uses the TCO_EN bit in the SMI_EN register */ |
---|
483 | | - if (!devm_request_region(dev, p->smi_res->start, |
---|
484 | | - resource_size(p->smi_res), |
---|
485 | | - pdev->name)) { |
---|
486 | | - pr_err("I/O address 0x%04llx already in use, device disabled\n", |
---|
487 | | - (u64)SMI_EN(p)); |
---|
488 | | - return -EBUSY; |
---|
489 | | - } |
---|
490 | 520 | if (turn_SMI_watchdog_clear_off >= p->iTCO_version) { |
---|
491 | 521 | /* |
---|
492 | 522 | * Bit 13: TCO_EN -> 0 |
---|
.. | .. |
---|
510 | 540 | |
---|
511 | 541 | /* Clear out the (probably old) status */ |
---|
512 | 542 | switch (p->iTCO_version) { |
---|
| 543 | + case 6: |
---|
513 | 544 | case 5: |
---|
514 | 545 | case 4: |
---|
515 | 546 | outw(0x0008, TCO1_STS(p)); /* Clear the Time Out Status bit */ |
---|
.. | .. |
---|
549 | 580 | } |
---|
550 | 581 | |
---|
551 | 582 | watchdog_stop_on_reboot(&p->wddev); |
---|
| 583 | + watchdog_stop_on_unregister(&p->wddev); |
---|
552 | 584 | ret = devm_watchdog_register_device(dev, &p->wddev); |
---|
553 | 585 | if (ret != 0) { |
---|
554 | 586 | pr_err("cannot register watchdog device (err=%d)\n", ret); |
---|
.. | .. |
---|
557 | 589 | |
---|
558 | 590 | pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n", |
---|
559 | 591 | heartbeat, nowayout); |
---|
560 | | - |
---|
561 | | - return 0; |
---|
562 | | -} |
---|
563 | | - |
---|
564 | | -static int iTCO_wdt_remove(struct platform_device *pdev) |
---|
565 | | -{ |
---|
566 | | - struct iTCO_wdt_private *p = platform_get_drvdata(pdev); |
---|
567 | | - |
---|
568 | | - /* Stop the timer before we leave */ |
---|
569 | | - if (!nowayout) |
---|
570 | | - iTCO_wdt_stop(&p->wddev); |
---|
571 | 592 | |
---|
572 | 593 | return 0; |
---|
573 | 594 | } |
---|
.. | .. |
---|
624 | 645 | |
---|
625 | 646 | static struct platform_driver iTCO_wdt_driver = { |
---|
626 | 647 | .probe = iTCO_wdt_probe, |
---|
627 | | - .remove = iTCO_wdt_remove, |
---|
628 | 648 | .driver = { |
---|
629 | 649 | .name = DRV_NAME, |
---|
630 | 650 | .pm = ITCO_WDT_PM_OPS, |
---|