The wdt_start() function may be called with a timeout greater
than the hardware-supported maximum. This in turn can result
in a bogus print, for example, if the device clamps the timeout
to 10s but wdt_start() is called with a 60s timeout:
Started <watchdog@> with servicing every 1000ms (60s timeout)

Add a max_timeout_ms field to uclass-plat data, so drivers can
set the hardware-limited max timeout value there during probing.
For drivers that support this feature, the wdt_start() function
clamps the timeout_ms value before calling ops->start() and the
startup print now shows both the actual and requested timeouts, e.g.
Started <watchdog@> with servicing every 1000ms (10s timeout, requested 60s)

The timeout comparison is done in whole seconds to avoid noise
from sub-second rounding.

The value defaults to 0. For drivers that do not support
this feature, the resulting print remains the same as before.

Signed-off-by: Juuso Rinta <[email protected]>
---
 drivers/watchdog/wdt-uclass.c | 23 +++++++++++++++++++++--
 include/wdt.h                 | 11 +++++++++++
 2 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/drivers/watchdog/wdt-uclass.c b/drivers/watchdog/wdt-uclass.c
index cce874dc000..418f1023a85 100644
--- a/drivers/watchdog/wdt-uclass.c
+++ b/drivers/watchdog/wdt-uclass.c
@@ -119,15 +119,23 @@ int initr_watchdog(void)
 int wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
 {
        const struct wdt_ops *ops = device_get_ops(dev);
+       struct wdt_uc_plat *plat = dev_get_uclass_plat(dev);
+       u64 req_timeout_ms = timeout_ms;
        int ret;
 
        if (!ops->start)
                return -ENOSYS;
+       /* Clamp to max timeout if reported by driver */
+       if (plat->max_timeout_ms && timeout_ms > plat->max_timeout_ms)
+               timeout_ms = plat->max_timeout_ms;
 
        ret = ops->start(dev, timeout_ms, flags);
        if (ret == 0) {
                struct wdt_priv *priv = dev_get_uclass_priv(dev);
                char svc_str[16];
+               char req_str[32];
+               u32 req_s = lldiv(req_timeout_ms, 1000);
+               u32 tout_s = lldiv(timeout_ms, 1000);
 
                svc_str[0] = '\0';
                if (IS_ENABLED(CONFIG_WATCHDOG)) {
@@ -144,9 +152,19 @@ int wdt_start(struct udevice *dev, u64 timeout_ms, ulong 
flags)
                }
 
                priv->running = true;
-               printf("WDT:   Started %s with%s servicing %s (%ds timeout)\n",
+
+               /*
+                * If the requested timeout was clamped, note the value
+                * when it differs at whole-second resolution. Sub-second
+                * rounding is ignored to avoid noise.
+                */
+               req_str[0] = '\0';
+               if (req_s != tout_s)
+                       snprintf(req_str, sizeof(req_str), ", requested %ds", 
req_s);
+
+               printf("WDT:   Started %s with%s servicing %s (%ds 
timeout%s)\n",
                       dev->name, IS_ENABLED(CONFIG_WATCHDOG) ? "" : "out",
-                      svc_str, (u32)lldiv(timeout_ms, 1000));
+                      svc_str, tout_s, req_str);
        }
 
        return ret;
@@ -269,4 +287,5 @@ UCLASS_DRIVER(wdt) = {
        .flags                  = DM_UC_FLAG_SEQ_ALIAS,
        .pre_probe              = wdt_pre_probe,
        .per_device_auto        = sizeof(struct wdt_priv),
+       .per_device_plat_auto   = sizeof(struct wdt_uc_plat),
 };
diff --git a/include/wdt.h b/include/wdt.h
index 1ef656585c4..db1faa321b3 100644
--- a/include/wdt.h
+++ b/include/wdt.h
@@ -74,6 +74,17 @@ int wdt_reset(struct udevice *dev);
  */
 int wdt_expire_now(struct udevice *dev, ulong flags);
 
+/**
+ * struct wdt_uc_plat - uclass platform data for a watchdog device
+ *
+ * @max_timeout_ms: Maximum timeout (in ms) that the hardware can honour.
+ *  A driver should set this typically at probe time. Default value 0
+ *  means no limit set by the driver.
+ */
+struct wdt_uc_plat {
+       u32 max_timeout_ms;
+};
+
 /*
  * struct wdt_ops - Driver model wdt operations
  *

-- 
2.39.2

Reply via email to