|
@@ -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),
|
|
|
};
|