watchdog: add support to extend WDT disable on suspend
Laxman Dewangan [Mon, 4 Jan 2016 08:26:13 +0000 (13:26 +0530)]
Add flag which can be slected from DT to disable the
watchdog in the syscore suspend instead of device
suspend to have more time for WDT running.

Similarly, enable this in syscore resume instead of
device resume.

This helps to recover system from hang even after device
suspend happen.

bug 200130544

Change-Id: Iaeaaeaa9ef2d85e6f750cafaee25fa6f85797b6b
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Reviewed-on: http://git-master/r/928559
Reviewed-by: Gagan Grover <ggrover@nvidia.com>
Tested-by: Gagan Grover <ggrover@nvidia.com>
GVS: Gerrit_Virtual_Submit

Documentation/devicetree/bindings/watchdog/tegra-wdt.txt
drivers/watchdog/tegra_wdt.c

index 1393184..d0c4a5f 100644 (file)
@@ -12,6 +12,10 @@ Optional properties:
 - nvidia,expiry-count: total expiry count of WDT (1 if not specified)
 - nvidia,enable-on-init: Toggle switch for enable during init
 - nvidia,heartbeat-init: default timeout in secs (120 secs if not specified)
+- nvidia,extend-watchdog-suspend: Boolean. Extend the disable of watchdog
+       timer to syscore suspend and re-eanble on syscore resume. If this
+       is selected then do not disable watchdog timer in device suspend
+       otherwise disable in device suspend.
 
 Examples:
 
index 9ca0391..9ccc153 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
+#include <linux/syscore_ops.h>
 #include <linux/uaccess.h>
 #include <linux/watchdog.h>
 #include <linux/tegra-soc.h>
@@ -52,6 +53,7 @@ struct tegra_wdt {
        int                     irq;
        unsigned long           status;
        struct notifier_block   wdt_pm_nb;
+       bool                    extended_suspend;
 /* Bit numbers for status flags */
 #define WDT_ENABLED            0
 #define WDT_ENABLED_ON_INIT    1
@@ -73,6 +75,10 @@ static int expiry_count = 1;
  */
 static int heartbeat = 120;
 
+static struct syscore_ops tegra_wdt_syscore_ops;
+
+struct tegra_wdt *s_tegra_wdt;
+
 static inline struct tegra_wdt *to_tegra_wdt(struct watchdog_device *wdt)
 {
        return container_of(wdt, struct tegra_wdt, wdt);
@@ -307,6 +313,7 @@ static int tegra_wdt_probe(struct platform_device *pdev)
        u32 pval = 0;
        int ret = 0;
        bool enable_on_init;
+       bool extended_suspend = false;
 
        if (!np) {
                dev_err(&pdev->dev, "Supprot registration from DT only");
@@ -322,6 +329,8 @@ static int tegra_wdt_probe(struct platform_device *pdev)
        if (!ret)
                expiry_count = pval;
 
+       extended_suspend = of_property_read_bool(np,
+                                       "nvidia,extend-watchdog-suspend");
        tegra_wdt = devm_kzalloc(&pdev->dev, sizeof(*tegra_wdt), GFP_KERNEL);
        if (!tegra_wdt) {
                dev_err(&pdev->dev, "out of memory\n");
@@ -329,6 +338,7 @@ static int tegra_wdt_probe(struct platform_device *pdev)
        }
 
        tegra_wdt->pdev = pdev;
+       tegra_wdt->extended_suspend = extended_suspend;
        tegra_wdt->wdt.info = &tegra_wdt_info;
        tegra_wdt->wdt.ops = &tegra_wdt_ops;
        tegra_wdt->wdt.min_timeout = MIN_WDT_PERIOD * expiry_count;
@@ -416,6 +426,10 @@ static int tegra_wdt_probe(struct platform_device *pdev)
 
        tegra_wdt_debugfs_init(tegra_wdt);
 
+       if (tegra_wdt->extended_suspend)
+               register_syscore_ops(&tegra_wdt_syscore_ops);
+
+       s_tegra_wdt = tegra_wdt;
        dev_info(&pdev->dev, "%s done\n", __func__);
        return 0;
 }
@@ -439,7 +453,10 @@ static int tegra_wdt_suspend(struct device *dev)
 {
        struct tegra_wdt *tegra_wdt = dev_get_drvdata(dev);
 
-       __tegra_wdt_disable(tegra_wdt);
+       if (!tegra_wdt->extended_suspend)
+               __tegra_wdt_disable(tegra_wdt);
+       else
+               __tegra_wdt_ping(tegra_wdt);
        return 0;
 }
 
@@ -447,13 +464,48 @@ static int tegra_wdt_resume(struct device *dev)
 {
        struct tegra_wdt *tegra_wdt = dev_get_drvdata(dev);
 
-       if (watchdog_active(&tegra_wdt->wdt))
-               __tegra_wdt_enable(tegra_wdt);
+       if (watchdog_active(&tegra_wdt->wdt)) {
+               if (tegra_wdt->extended_suspend)
+                       __tegra_wdt_ping(tegra_wdt);
+               else
+                       __tegra_wdt_enable(tegra_wdt);
+       } else {
+               if (tegra_wdt->extended_suspend)
+                       __tegra_wdt_disable(tegra_wdt);
+       }
 
        return 0;
 }
+
+static int tegra_wdt_syscore_suspend(void)
+{
+       if (!s_tegra_wdt->extended_suspend)
+               return 0;
+
+       __tegra_wdt_disable(s_tegra_wdt);
+       return 0;
+}
+
+static void tegra_wdt_syscore_resume(void)
+{
+       if (!s_tegra_wdt->extended_suspend)
+               return;
+
+       __tegra_wdt_enable(s_tegra_wdt);
+}
+#else
+static int tegra_wdt_syscore_suspend(void)
+{
+       return 0;
+}
+static void tegra_wdt_syscore_resume(void) { }
 #endif
 
+static struct syscore_ops tegra_wdt_syscore_ops = {
+       .suspend =      tegra_wdt_syscore_suspend,
+       .resume =       tegra_wdt_syscore_resume,
+};
+
 static const struct dev_pm_ops tegra_wdt_pm_ops = {
        SET_SYSTEM_SLEEP_PM_OPS(tegra_wdt_suspend, tegra_wdt_resume)
 };