From: Lad Prabhakar <prabhakar.mahadev-lad.rj@xxxxxxxxxxxxxx> On the RZ/V2H(P) SoC we can determine if the current boot is due to `Power-on-Reset` or due to the `Watchdog`. The information used to determine this is present on the CPG block. The CPG_ERROR_RSTm(m = 2 - 8) registers are set in response to an error interrupt causing an reset. CPG_ERROR_RST2[ERROR_RST0/1/2] is set if there was an underflow/overflow on WDT1 causing an error interrupt. To fetch this information from CPG block `syscon` is used and bootstatus field in the watchdog device is updated based on the CPG_ERROR_RST2[ERROR_RST0/1/2] bit. Upon consumig CPG_ERROR_RST2[ERROR_RST0/1/2] bit we clear it. Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@xxxxxxxxxxxxxx> --- drivers/watchdog/rzv2h_wdt.c | 48 +++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/rzv2h_wdt.c b/drivers/watchdog/rzv2h_wdt.c index 8defd0241213..5ba5965ac170 100644 --- a/drivers/watchdog/rzv2h_wdt.c +++ b/drivers/watchdog/rzv2h_wdt.c @@ -4,14 +4,17 @@ * * Copyright (C) 2024 Renesas Electronics Corporation. */ +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/io.h> #include <linux/kernel.h> +#include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/regmap.h> #include <linux/reset.h> #include <linux/units.h> #include <linux/watchdog.h> @@ -40,6 +43,9 @@ #define WDT_DEFAULT_TIMEOUT 60U +#define CPG_ERROR_RST2(x) BIT(x) +#define CPG_ERROR_RST2_WEN(x) BIT((x) + 16) + static bool nowayout = WATCHDOG_NOWAYOUT; module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" @@ -139,6 +145,11 @@ static const struct watchdog_info rzv2h_wdt_ident = { .identity = "Renesas RZ/V2H WDT Watchdog", }; +static const struct watchdog_info rzv2h_wdt_ident_card_reset = { + .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_CARDRESET, + .identity = "Renesas RZ/V2H WDT Watchdog", +}; + static int rzv2h_wdt_restart(struct watchdog_device *wdev, unsigned long action, void *data) { @@ -206,9 +217,40 @@ static const struct watchdog_ops rzv2h_wdt_ops = { static int rzv2h_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; struct rzv2h_wdt_priv *priv; + unsigned int bootstatus = 0; + bool cardreset = false; + struct regmap *syscon; int ret; + /* Do not error out to maintain old DT compatibility */ + syscon = syscon_regmap_lookup_by_phandle(np, "renesas,r9a09g057-syscon-wdt-errorrst"); + if (!IS_ERR(syscon)) { + struct of_phandle_args args; + u32 reg; + + ret = of_parse_phandle_with_fixed_args(np, "renesas,r9a09g057-syscon-wdt-errorrst", + 2, 0, &args); + if (ret) + return ret; + + ret = regmap_read(syscon, args.args[0], ®); + if (ret) + return -EINVAL; + + if (reg & CPG_ERROR_RST2(args.args[1])) { + ret = regmap_write(syscon, args.args[0], + CPG_ERROR_RST2(args.args[1]) | + CPG_ERROR_RST2_WEN(args.args[1])); + if (ret) + return -EINVAL; + } + cardreset = true; + bootstatus = reg & CPG_ERROR_RST2(args.args[1]) ? WDIOF_CARDRESET : 0; + regmap_read(syscon, args.args[0], ®); + } + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -240,9 +282,13 @@ static int rzv2h_wdt_probe(struct platform_device *pdev) priv->wdev.min_timeout = 1; priv->wdev.timeout = WDT_DEFAULT_TIMEOUT; - priv->wdev.info = &rzv2h_wdt_ident; + if (!cardreset) + priv->wdev.info = &rzv2h_wdt_ident; + else + priv->wdev.info = &rzv2h_wdt_ident_card_reset; priv->wdev.ops = &rzv2h_wdt_ops; priv->wdev.parent = dev; + priv->wdev.bootstatus = bootstatus; watchdog_set_drvdata(&priv->wdev, priv); watchdog_set_nowayout(&priv->wdev, nowayout); watchdog_stop_on_unregister(&priv->wdev); -- 2.43.0