Skip to content

Commit

Permalink
watchdog: gpio_wdt: Add "always_running" feature to GPIO watchdog
Browse files Browse the repository at this point in the history
On some chips, like the TPS386000, the trigger cannot be disabled
and the CPU must keep toggling the line at all times. Add a switch
"always_running" to keep toggling the GPIO line regardless of the
state of the soft part of the watchdog. The "armed" member keeps
track of whether a timeout must also cause a reset.

Signed-off-by: Mike Looijmans <[email protected]>
Reviewed-by: Guenter Roeck <[email protected]>
Signed-off-by: Wim Van Sebroeck <[email protected]>
  • Loading branch information
MikeLooijmans authored and Wim Van Sebroeck committed Feb 17, 2015
1 parent 396f163 commit ba804a9
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 8 deletions.
5 changes: 5 additions & 0 deletions Documentation/devicetree/bindings/watchdog/gpio-wdt.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ Required Properties:
by the GPIO flags.
- hw_margin_ms: Maximum time to reset watchdog circuit (milliseconds).

Optional Properties:
- always-running: If the watchdog timer cannot be disabled, add this flag to
have the driver keep toggling the signal without a client. It will only cease
to toggle the signal when the device is open and the timeout elapsed.

Example:
watchdog: watchdog {
/* ADM706 */
Expand Down
37 changes: 29 additions & 8 deletions drivers/watchdog/gpio_wdt.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ struct gpio_wdt_priv {
int gpio;
bool active_low;
bool state;
bool always_running;
bool armed;
unsigned int hw_algo;
unsigned int hw_margin;
unsigned long last_jiffies;
Expand All @@ -48,14 +50,20 @@ static void gpio_wdt_disable(struct gpio_wdt_priv *priv)
gpio_direction_input(priv->gpio);
}

static int gpio_wdt_start(struct watchdog_device *wdd)
static void gpio_wdt_start_impl(struct gpio_wdt_priv *priv)
{
struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);

priv->state = priv->active_low;
gpio_direction_output(priv->gpio, priv->state);
priv->last_jiffies = jiffies;
mod_timer(&priv->timer, priv->last_jiffies + priv->hw_margin);
}

static int gpio_wdt_start(struct watchdog_device *wdd)
{
struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);

gpio_wdt_start_impl(priv);
priv->armed = true;

return 0;
}
Expand All @@ -64,8 +72,11 @@ static int gpio_wdt_stop(struct watchdog_device *wdd)
{
struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);

mod_timer(&priv->timer, 0);
gpio_wdt_disable(priv);
priv->armed = false;
if (!priv->always_running) {
mod_timer(&priv->timer, 0);
gpio_wdt_disable(priv);
}

return 0;
}
Expand All @@ -91,8 +102,8 @@ static void gpio_wdt_hwping(unsigned long data)
struct watchdog_device *wdd = (struct watchdog_device *)data;
struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);

if (time_after(jiffies, priv->last_jiffies +
msecs_to_jiffies(wdd->timeout * 1000))) {
if (priv->armed && time_after(jiffies, priv->last_jiffies +
msecs_to_jiffies(wdd->timeout * 1000))) {
dev_crit(wdd->dev, "Timer expired. System will reboot soon!\n");
return;
}
Expand Down Expand Up @@ -197,6 +208,9 @@ static int gpio_wdt_probe(struct platform_device *pdev)
/* Use safe value (1/2 of real timeout) */
priv->hw_margin = msecs_to_jiffies(hw_margin / 2);

priv->always_running = of_property_read_bool(pdev->dev.of_node,
"always-running");

watchdog_set_drvdata(&priv->wdd, priv);

priv->wdd.info = &gpio_wdt_ident;
Expand All @@ -216,8 +230,15 @@ static int gpio_wdt_probe(struct platform_device *pdev)
priv->notifier.notifier_call = gpio_wdt_notify_sys;
ret = register_reboot_notifier(&priv->notifier);
if (ret)
watchdog_unregister_device(&priv->wdd);
goto error_unregister;

if (priv->always_running)
gpio_wdt_start_impl(priv);

return 0;

error_unregister:
watchdog_unregister_device(&priv->wdd);
return ret;
}

Expand Down

0 comments on commit ba804a9

Please sign in to comment.