watchdog: tegra: Add virtualization support

Tegra TKE WDT can be used from a Guest OS to check if the system is
responsive. In case of system hang/crash the WDT should report an
error event to HSM.

Add device tree property "nvidia,wdt-error-threshold" to configure
expiry level for HSM event trigger.

This patch also includes following bug fixes:
 - fix an issue in which WDT keeps running after system shutdown even
   if it is disabled from device tree.
 - fix an issue where the driver keeps patting WDT even if
   enable-on-init is disabled.

Bug 4080471

Change-Id: I8e660e68edbb7c97fa8fb2fdd7fafa8bf2a87884
Signed-off-by: Kartik <kkartik@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2903911
Reviewed-by: svcacv <svcacv@nvidia.com>
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
(cherry picked from commit ff027900b83e985e50c78adf2b898ad078d5b60a)
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2897226
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
This commit is contained in:
Kartik
2023-05-02 12:48:24 +00:00
committed by mobile promotions
parent db78501061
commit 966af3a139

View File

@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
// Copyright (C) 2022 NVIDIA CORPORATION. All rights reserved. // Copyright (C) 2023 NVIDIA CORPORATION. All rights reserved.
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/fs.h> #include <linux/fs.h>
@@ -42,9 +42,6 @@
*/ */
#define HEARTBEAT 120 #define HEARTBEAT 120
/* Watchdog configured to this time before reset during shutdown */
#define SHUTDOWN_TIMEOUT 150
/* Bit numbers for status flags */ /* Bit numbers for status flags */
#define WDT_ENABLED 0 #define WDT_ENABLED 0
#define WDT_ENABLED_ON_INIT 1 #define WDT_ENABLED_ON_INIT 1
@@ -440,6 +437,7 @@ static int tegra_wdt_t18x_probe(struct platform_device *pdev)
int timer_id, wdt_id; int timer_id, wdt_id;
int skip_count = 0; int skip_count = 0;
u32 pval = 0; u32 pval = 0;
u32 error_threshold;
int ret; int ret;
twdt_t18x = devm_kzalloc(&pdev->dev, sizeof(*twdt_t18x), GFP_KERNEL); twdt_t18x = devm_kzalloc(&pdev->dev, sizeof(*twdt_t18x), GFP_KERNEL);
@@ -447,9 +445,17 @@ static int tegra_wdt_t18x_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
ret = of_property_read_u32(np, "nvidia,shutdown-timeout", &pval); ret = of_property_read_u32(np, "nvidia,shutdown-timeout", &pval);
twdt_t18x->shutdown_timeout = (ret) ? SHUTDOWN_TIMEOUT : pval; if (ret)
dev_info(&pdev->dev, "shutdown timeout disabled\n");
else
twdt_t18x->shutdown_timeout = pval;
twdt_t18x->soc = of_device_get_match_data(&pdev->dev); twdt_t18x->soc = of_device_get_match_data(&pdev->dev);
if (!twdt_t18x->soc) {
dev_err(&pdev->dev, "Unable to find soc data\n");
return -EINVAL;
}
twdt_t18x->dev = &pdev->dev; twdt_t18x->dev = &pdev->dev;
twdt_t18x->wdt.info = &tegra_wdt_t18x_info; twdt_t18x->wdt.info = &tegra_wdt_t18x_info;
twdt_t18x->wdt.ops = &tegra_wdt_t18x_ops; twdt_t18x->wdt.ops = &tegra_wdt_t18x_ops;
@@ -604,10 +610,23 @@ static int tegra_wdt_t18x_probe(struct platform_device *pdev)
/* /*
* 'ErrorThreshold' field @ TKE_TOP_WDT1_WDTCR_0 decides the * 'ErrorThreshold' field @ TKE_TOP_WDT1_WDTCR_0 decides the
* indication to HSM. The WDT logic asserts an error signal to * indication to HSM. The WDT logic asserts an error signal to
* HSM when ExpirationLevel >= ErrorThreshold. Retain the POR * HSM when ExpirationLevel >= ErrorThreshold. Update the error
* value to avoid nuisance trigger to HSM. * threshold value, if provided in the device tree.
*
* Else retain the POR value to avoid nuisance trigger to HSM.
*/ */
ret = of_property_read_u32(np, "nvidia,wdt-error-threshold",
&error_threshold);
if (!ret) {
if (error_threshold > 7) {
dev_warn(&pdev->dev, "Invalid error threshold value\n");
twdt_t18x->config |= WDT_CFG_ERR_THRESHOLD; twdt_t18x->config |= WDT_CFG_ERR_THRESHOLD;
} else {
twdt_t18x->config |= (error_threshold << 20);
}
} else {
twdt_t18x->config |= WDT_CFG_ERR_THRESHOLD;
}
/* Enable local FIQ and remote interrupt for debug dump */ /* Enable local FIQ and remote interrupt for debug dump */
twdt_t18x->config |= WDT_CFG_FINT_EN; twdt_t18x->config |= WDT_CFG_FINT_EN;
@@ -648,6 +667,8 @@ static int tegra_wdt_t18x_probe(struct platform_device *pdev)
tegra_wdt_t18x_disable(&twdt_t18x->wdt); tegra_wdt_t18x_disable(&twdt_t18x->wdt);
writel(TOP_TKE_TMR_PCR_INTR, twdt_t18x->wdt_timer + TOP_TKE_TMR_PCR); writel(TOP_TKE_TMR_PCR_INTR, twdt_t18x->wdt_timer + TOP_TKE_TMR_PCR);
if (twdt_t18x->enable_on_init) {
/* Register interrupt handler only when WDT is configured to enable on boot */
ret = devm_request_threaded_irq(&pdev->dev, twdt_t18x->irq, ret = devm_request_threaded_irq(&pdev->dev, twdt_t18x->irq,
NULL, tegra_wdt_t18x_isr, NULL, tegra_wdt_t18x_isr,
IRQF_ONESHOT | IRQF_TRIGGER_HIGH, IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
@@ -658,7 +679,6 @@ static int tegra_wdt_t18x_probe(struct platform_device *pdev)
return ret; return ret;
} }
if (twdt_t18x->enable_on_init) {
tegra_wdt_t18x_enable(&twdt_t18x->wdt); tegra_wdt_t18x_enable(&twdt_t18x->wdt);
set_bit(WDOG_ACTIVE, &twdt_t18x->wdt.status); set_bit(WDOG_ACTIVE, &twdt_t18x->wdt.status);
set_bit(WDT_ENABLED_ON_INIT, &twdt_t18x->status); set_bit(WDT_ENABLED_ON_INIT, &twdt_t18x->status);
@@ -697,6 +717,9 @@ static void tegra_wdt_t18x_shutdown(struct platform_device *pdev)
struct tegra_wdt_t18x *twdt_t18x = platform_get_drvdata(pdev); struct tegra_wdt_t18x *twdt_t18x = platform_get_drvdata(pdev);
if (twdt_t18x->shutdown_timeout) { if (twdt_t18x->shutdown_timeout) {
dev_dbg(twdt_t18x->dev,
"Watchdog%d: setting shutdown timeout as %d seconds",
twdt_t18x->wdt.id, twdt_t18x->shutdown_timeout);
twdt_t18x->wdt.timeout = twdt_t18x->shutdown_timeout; twdt_t18x->wdt.timeout = twdt_t18x->shutdown_timeout;
__tegra_wdt_t18x_ping(twdt_t18x); __tegra_wdt_t18x_ping(twdt_t18x);
return; return;