diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 416d29d256a..9ea617f1e43 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -356,6 +356,13 @@ config WDT_RENESAS help Enables Renesas SoC R8A779F0 watchdog timer support. +config WDT_RENESAS_WWDT + bool "Renesas window watchdog timer support" + depends on WDT + select SYSCON + help + Enables Renesas window watchdog timer support. + config WDT_SANDBOX bool "Enable Watchdog Timer support for Sandbox" depends on SANDBOX && WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 02e2674f8af..84faefdb4c2 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_WDT_NPCM) += npcm_wdt.o obj-$(CONFIG_WDT_OCTEONTX) += octeontx_wdt.o obj-$(CONFIG_WDT_OMAP3) += omap_wdt.o obj-$(CONFIG_WDT_RENESAS) += renesas_wdt.o +obj-$(CONFIG_WDT_RENESAS_WWDT) += renesas_wwdt.o obj-$(CONFIG_WDT_SBSA) += sbsa_gwdt.o obj-$(CONFIG_WDT_K3_RTI) += rti_wdt.o obj-$(CONFIG_WDT_SIEMENS_PMIC) += siemens_pmic_wdt.o diff --git a/drivers/watchdog/renesas_wwdt.c b/drivers/watchdog/renesas_wwdt.c new file mode 100644 index 00000000000..f6f508c95c2 --- /dev/null +++ b/drivers/watchdog/renesas_wwdt.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2026 Marek Vasut + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask)) + +#define RSIP_CTL_CFG4 0xc0 +#define RSIP_CTL_CFG4_OPWDEN BIT(3) +#define RSIP_CTL_CFG4_OPWDVAC BIT(5) + +#define WDTA0_ACT_CODE 0xac +#define WDTA0WDTE 0x0 +#define WDTA0EVAC 0x4 +#define WDTA0REF 0x8 +#define WDTA0MD 0xc +#define WDTA0MD_OVF_MASK GENMASK(6, 4) +#define WDTA0MD_OVF(n) field_prep(WDTA0MD_OVF_MASK, (n)) +#define WDTA0MD_NWIE BIT(3) +#define WDTA0MD_NERM BIT(2) +#define WDTA0MD_NVS_MASK GENMASK(1, 0) +#define WDTA0MD_NVS_75P FIELD_PREP(WDTA0MD_NVS_MASK, 3) + +struct wwdt_priv { + void __iomem *base; + struct regmap *ctl; + unsigned int timeout; +}; + +/** + * wwdt_reset() - Reset or ping Window WDT + * @dev: Watchdog device + */ +static int wwdt_reset(struct udevice *dev) +{ + struct wwdt_priv *priv = dev_get_priv(dev); + const u32 cfg = readl(priv->ctl->ranges[0].start + RSIP_CTL_CFG4); + u32 rv; + + /* WDT disabled, do nothing. */ + if (!(cfg & RSIP_CTL_CFG4_OPWDEN)) + return 0; + + /* WDT with variable activation code */ + if (cfg & RSIP_CTL_CFG4_OPWDVAC) { + rv = readb(priv->base + WDTA0REF); + rv = WDTA0_ACT_CODE - rv; + writeb(rv, priv->base + WDTA0EVAC); + } else { + writeb(WDTA0_ACT_CODE, priv->base + WDTA0WDTE); + } + + return 0; +} + +/** + * wwdt_start() - Start Window WDT + * @dev: Watchdog device + * @timeout: Watchdog timeout (not used) + * @flags: Flags (not used) + */ +static int wwdt_start(struct udevice *dev, u64 timeout, ulong flags) +{ + struct wwdt_priv *priv = dev_get_priv(dev); + + clrsetbits_8(priv->base + WDTA0MD, + WDTA0MD_OVF_MASK | WDTA0MD_NWIE | + WDTA0MD_NERM | WDTA0MD_NVS_MASK, + WDTA0MD_OVF(priv->timeout) | WDTA0MD_NWIE | + WDTA0MD_NVS_75P); + + wwdt_reset(dev); + + return 0; +} + +/** + * wwdt_probe() - Initialize Window WDT hardware + * @dev: Watchdog device + */ +static int wwdt_probe(struct udevice *dev) +{ + struct wwdt_priv *priv = dev_get_priv(dev); + struct udevice *syscon; + unsigned long rate; + struct clk *clk; + int ret; + + priv->base = dev_remap_addr(dev); + if (!priv->base) + return -EINVAL; + + ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, "syscon", &syscon); + if (ret) { + dev_err(dev, "Failed to get syscon\n"); + return ret; + } + + priv->ctl = syscon_get_regmap(syscon); + if (!priv->ctl) { + dev_err(dev, "Failed to get regmap\n"); + return -ENODEV; + } + + clk = devm_clk_get(dev, "cnt"); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + dev_err(dev, "Failed to get counter clock: %d\n", ret); + return ret; + } + + ret = clk_enable(clk); + if (ret) + return ret; + + rate = clk_get_rate(clk); + if (!rate) { + clk_disable(clk); + return -ENOENT; + } + + /* + * Interval time is in "2^9..2^16 / clk_wdt" range. WDTA0OVFx is + * in 0..7 range. The code below does the WDTA0OVFx calculation + * from "interval_time = (1 << N) / clk_wdt" by caculating the N. + * The N rounded down is MSbit of (interval_time * clk_wdt). The + * result is then clamped to fit into the N in 9..16 range, and + * decremented by 9 to fit into WDTA0OVFx in 0..7 range . + */ + priv->timeout = clamp(fls(CONFIG_WATCHDOG_TIMEOUT_MSECS * rate) - 1, 9, 16) - 9; + + return 0; +} + +static const struct wdt_ops wwdt_ops = { + .start = wwdt_start, + .reset = wwdt_reset, +}; + +static const struct udevice_id wwdt_ids[] = { + { .compatible = "renesas,rcar-gen5-wwdt" }, + {} +}; + +U_BOOT_DRIVER(wwdt_renesas) = { + .name = "wwdt_renesas", + .id = UCLASS_WDT, + .of_match = wwdt_ids, + .ops = &wwdt_ops, + .probe = wwdt_probe, + .priv_auto = sizeof(struct wwdt_priv), +};