Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

watchdog: gpio_wdt: Add "always_running" feature to GPIO watchdog

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 <mike.looijmans@topic.nl>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>

authored by

Mike Looijmans and committed by
Wim Van Sebroeck
ba804a95 396f163c

+34 -8
+5
Documentation/devicetree/bindings/watchdog/gpio-wdt.txt
··· 13 13 by the GPIO flags. 14 14 - hw_margin_ms: Maximum time to reset watchdog circuit (milliseconds). 15 15 16 + Optional Properties: 17 + - always-running: If the watchdog timer cannot be disabled, add this flag to 18 + have the driver keep toggling the signal without a client. It will only cease 19 + to toggle the signal when the device is open and the timeout elapsed. 20 + 16 21 Example: 17 22 watchdog: watchdog { 18 23 /* ADM706 */
+29 -8
drivers/watchdog/gpio_wdt.c
··· 31 31 int gpio; 32 32 bool active_low; 33 33 bool state; 34 + bool always_running; 35 + bool armed; 34 36 unsigned int hw_algo; 35 37 unsigned int hw_margin; 36 38 unsigned long last_jiffies; ··· 50 48 gpio_direction_input(priv->gpio); 51 49 } 52 50 53 - static int gpio_wdt_start(struct watchdog_device *wdd) 51 + static void gpio_wdt_start_impl(struct gpio_wdt_priv *priv) 54 52 { 55 - struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); 56 - 57 53 priv->state = priv->active_low; 58 54 gpio_direction_output(priv->gpio, priv->state); 59 55 priv->last_jiffies = jiffies; 60 56 mod_timer(&priv->timer, priv->last_jiffies + priv->hw_margin); 57 + } 58 + 59 + static int gpio_wdt_start(struct watchdog_device *wdd) 60 + { 61 + struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); 62 + 63 + gpio_wdt_start_impl(priv); 64 + priv->armed = true; 61 65 62 66 return 0; 63 67 } ··· 72 64 { 73 65 struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); 74 66 75 - mod_timer(&priv->timer, 0); 76 - gpio_wdt_disable(priv); 67 + priv->armed = false; 68 + if (!priv->always_running) { 69 + mod_timer(&priv->timer, 0); 70 + gpio_wdt_disable(priv); 71 + } 77 72 78 73 return 0; 79 74 } ··· 102 91 struct watchdog_device *wdd = (struct watchdog_device *)data; 103 92 struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd); 104 93 105 - if (time_after(jiffies, priv->last_jiffies + 106 - msecs_to_jiffies(wdd->timeout * 1000))) { 94 + if (priv->armed && time_after(jiffies, priv->last_jiffies + 95 + msecs_to_jiffies(wdd->timeout * 1000))) { 107 96 dev_crit(wdd->dev, "Timer expired. System will reboot soon!\n"); 108 97 return; 109 98 } ··· 208 197 /* Use safe value (1/2 of real timeout) */ 209 198 priv->hw_margin = msecs_to_jiffies(hw_margin / 2); 210 199 200 + priv->always_running = of_property_read_bool(pdev->dev.of_node, 201 + "always-running"); 202 + 211 203 watchdog_set_drvdata(&priv->wdd, priv); 212 204 213 205 priv->wdd.info = &gpio_wdt_ident; ··· 230 216 priv->notifier.notifier_call = gpio_wdt_notify_sys; 231 217 ret = register_reboot_notifier(&priv->notifier); 232 218 if (ret) 233 - watchdog_unregister_device(&priv->wdd); 219 + goto error_unregister; 234 220 221 + if (priv->always_running) 222 + gpio_wdt_start_impl(priv); 223 + 224 + return 0; 225 + 226 + error_unregister: 227 + watchdog_unregister_device(&priv->wdd); 235 228 return ret; 236 229 } 237 230