The WOR register is 32 bits, so any tick count exceeding U32_MAX is
truncated by writel(). A large requested timeout can wrap to a small
value causing the watchdog to fire sooner than requested.

Clamp the calculated value to U32_MAX prior to writing the register so
over-large requests will be set to the maximum timeout value.

Found by code review.

Signed-off-by: Juuso Rinta <[email protected]>
---
 drivers/watchdog/sbsa_gwdt.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/watchdog/sbsa_gwdt.c b/drivers/watchdog/sbsa_gwdt.c
index 807884c5bc7..3a924cb2b9a 100644
--- a/drivers/watchdog/sbsa_gwdt.c
+++ b/drivers/watchdog/sbsa_gwdt.c
@@ -50,6 +50,7 @@ static int sbsa_gwdt_start(struct udevice *dev, u64 timeout, 
ulong flags)
 {
        struct sbsa_gwdt_priv *priv = dev_get_priv(dev);
        u32 clk;
+       u64 tout_wdog;
 
        /*
         * it work in the single stage mode in u-boot,
@@ -58,8 +59,13 @@ static int sbsa_gwdt_start(struct udevice *dev, u64 timeout, 
ulong flags)
         * to half value of timeout.
         */
        clk = get_tbclk();
-       writel(clk / (2 * 1000) * timeout,
-              priv->reg_control + SBSA_GWDT_WOR);
+
+       /* if requested timeout overflows, clamp it to u32_max */
+       tout_wdog = ((u64)clk * timeout) / (2 * 1000);
+       if (tout_wdog > U32_MAX)
+               tout_wdog = U32_MAX;
+
+       writel(tout_wdog, priv->reg_control + SBSA_GWDT_WOR);
 
        /* writing WCS will cause an explicit watchdog refresh */
        writel(SBSA_GWDT_WCS_EN, priv->reg_control + SBSA_GWDT_WCS);

---
base-commit: 4433253ecf2041f9362a763bb6cb79960921ac7e
change-id: 20260428-fix-sbsa-timeout-overflow-20cf84fa8a6b

Best regards,
--
Juuso Rinta <[email protected]>
-- 
Juuso Rinta <[email protected]>

Reply via email to