diff options
Diffstat (limited to 'drivers/watchdog/wdt-uclass.c')
-rw-r--r-- | drivers/watchdog/wdt-uclass.c | 192 |
1 files changed, 141 insertions, 51 deletions
diff --git a/drivers/watchdog/wdt-uclass.c b/drivers/watchdog/wdt-uclass.c index 17334db..7570710 100644 --- a/drivers/watchdog/wdt-uclass.c +++ b/drivers/watchdog/wdt-uclass.c @@ -20,53 +20,67 @@ DECLARE_GLOBAL_DATA_PTR; #define WATCHDOG_TIMEOUT_SECS (CONFIG_WATCHDOG_TIMEOUT_MSECS / 1000) -/* - * Reset every 1000ms, or however often is required as indicated by a - * hw_margin_ms property. - */ -static ulong reset_period = 1000; +struct wdt_priv { + /* Timeout, in seconds, to configure this device to. */ + u32 timeout; + /* + * Time, in milliseconds, between calling the device's ->reset() + * method from watchdog_reset(). + */ + ulong reset_period; + /* + * Next time (as returned by get_timer(0)) to call + * ->reset(). + */ + ulong next_reset; + /* Whether watchdog_start() has been called on the device. */ + bool running; +}; -int initr_watchdog(void) +static void init_watchdog_dev(struct udevice *dev) { - u32 timeout = WATCHDOG_TIMEOUT_SECS; + struct wdt_priv *priv; int ret; - /* - * Init watchdog: This will call the probe function of the - * watchdog driver, enabling the use of the device - */ - if (uclass_get_device_by_seq(UCLASS_WDT, 0, - (struct udevice **)&gd->watchdog_dev)) { - debug("WDT: Not found by seq!\n"); - if (uclass_get_device(UCLASS_WDT, 0, - (struct udevice **)&gd->watchdog_dev)) { - printf("WDT: Not found!\n"); - return 0; - } - } - - if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) { - timeout = dev_read_u32_default(gd->watchdog_dev, "timeout-sec", - WATCHDOG_TIMEOUT_SECS); - reset_period = dev_read_u32_default(gd->watchdog_dev, - "hw_margin_ms", - 4 * reset_period) / 4; - } + priv = dev_get_uclass_priv(dev); if (!IS_ENABLED(CONFIG_WATCHDOG_AUTOSTART)) { - printf("WDT: Not starting\n"); - return 0; + printf("WDT: Not starting %s\n", dev->name); + return; } - ret = wdt_start(gd->watchdog_dev, timeout * 1000, 0); + ret = wdt_start(dev, priv->timeout * 1000, 0); if (ret != 0) { - printf("WDT: Failed to start\n"); + printf("WDT: Failed to start %s\n", dev->name); + return; + } + + printf("WDT: Started %s with%s servicing (%ds timeout)\n", dev->name, + IS_ENABLED(CONFIG_WATCHDOG) ? "" : "out", priv->timeout); +} + +int initr_watchdog(void) +{ + struct udevice *dev; + struct uclass *uc; + int ret; + + ret = uclass_get(UCLASS_WDT, &uc); + if (ret) { + log_debug("Error getting UCLASS_WDT: %d\n", ret); return 0; } - printf("WDT: Started with%s servicing (%ds timeout)\n", - IS_ENABLED(CONFIG_WATCHDOG) ? "" : "out", timeout); + uclass_foreach_dev(dev, uc) { + ret = device_probe(dev); + if (ret) { + log_debug("Error probing %s: %d\n", dev->name, ret); + continue; + } + init_watchdog_dev(dev); + } + gd->flags |= GD_FLG_WDT_READY; return 0; } @@ -79,8 +93,11 @@ int wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags) return -ENOSYS; ret = ops->start(dev, timeout_ms, flags); - if (ret == 0) - gd->flags |= GD_FLG_WDT_READY; + if (ret == 0) { + struct wdt_priv *priv = dev_get_uclass_priv(dev); + + priv->running = true; + } return ret; } @@ -94,8 +111,36 @@ int wdt_stop(struct udevice *dev) return -ENOSYS; ret = ops->stop(dev); - if (ret == 0) - gd->flags &= ~GD_FLG_WDT_READY; + if (ret == 0) { + struct wdt_priv *priv = dev_get_uclass_priv(dev); + + priv->running = false; + } + + return ret; +} + +int wdt_stop_all(void) +{ + struct wdt_priv *priv; + struct udevice *dev; + struct uclass *uc; + int ret, err; + + ret = uclass_get(UCLASS_WDT, &uc); + if (ret) + return ret; + + uclass_foreach_dev(dev, uc) { + if (!device_active(dev)) + continue; + priv = dev_get_uclass_priv(dev); + if (!priv->running) + continue; + err = wdt_stop(dev); + if (!ret) + ret = err; + } return ret; } @@ -120,10 +165,8 @@ int wdt_expire_now(struct udevice *dev, ulong flags) if (ops->expire_now) { return ops->expire_now(dev, flags); } else { - if (!ops->start) - return -ENOSYS; + ret = wdt_start(dev, 1, flags); - ret = ops->start(dev, 1, flags); if (ret < 0) return ret; @@ -141,18 +184,36 @@ int wdt_expire_now(struct udevice *dev, ulong flags) */ void watchdog_reset(void) { - static ulong next_reset; + struct wdt_priv *priv; + struct udevice *dev; + struct uclass *uc; ulong now; /* Exit if GD is not ready or watchdog is not initialized yet */ if (!gd || !(gd->flags & GD_FLG_WDT_READY)) return; - /* Do not reset the watchdog too often */ - now = get_timer(0); - if (time_after_eq(now, next_reset)) { - next_reset = now + reset_period; - wdt_reset(gd->watchdog_dev); + if (uclass_get(UCLASS_WDT, &uc)) + return; + + /* + * All devices bound to the wdt uclass should have been probed + * in initr_watchdog(). But just in case something went wrong, + * check device_active() before accessing the uclass private + * data. + */ + uclass_foreach_dev(dev, uc) { + if (!device_active(dev)) + continue; + priv = dev_get_uclass_priv(dev); + if (!priv->running) + continue; + /* Do not reset the watchdog too often */ + now = get_timer(0); + if (time_after_eq(now, priv->next_reset)) { + priv->next_reset = now + priv->reset_period; + wdt_reset(dev); + } } } #endif @@ -179,9 +240,38 @@ static int wdt_post_bind(struct udevice *dev) return 0; } +static int wdt_pre_probe(struct udevice *dev) +{ + u32 timeout = WATCHDOG_TIMEOUT_SECS; + /* + * Reset every 1000ms, or however often is required as + * indicated by a hw_margin_ms property. + */ + ulong reset_period = 1000; + struct wdt_priv *priv; + + if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) { + timeout = dev_read_u32_default(dev, "timeout-sec", timeout); + reset_period = dev_read_u32_default(dev, "hw_margin_ms", + 4 * reset_period) / 4; + } + priv = dev_get_uclass_priv(dev); + priv->timeout = timeout; + priv->reset_period = reset_period; + /* + * Pretend this device was last reset "long" ago so the first + * watchdog_reset will actually call its ->reset method. + */ + priv->next_reset = get_timer(0); + + return 0; +} + UCLASS_DRIVER(wdt) = { - .id = UCLASS_WDT, - .name = "watchdog", - .flags = DM_UC_FLAG_SEQ_ALIAS, - .post_bind = wdt_post_bind, + .id = UCLASS_WDT, + .name = "watchdog", + .flags = DM_UC_FLAG_SEQ_ALIAS, + .post_bind = wdt_post_bind, + .pre_probe = wdt_pre_probe, + .per_device_auto = sizeof(struct wdt_priv), }; |