watchdog: s3c2410: add restart handler
authorHeiko Stuebner <heiko@sntech.de>
Wed, 20 Aug 2014 00:45:36 +0000 (17:45 -0700)
committerWim Van Sebroeck <wim@iguana.be>
Mon, 20 Oct 2014 19:03:37 +0000 (21:03 +0200)
On a lot of Samsung systems the watchdog is responsible for restarting the
system and until now this code was contained in plat-samsung/watchdog-reset.c.

With the introduction of the restart handlers, this code can now move into
driver itself, removing the need for arch-specific code.

Tested on a S3C2442 based GTA02

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
drivers/watchdog/s3c2410_wdt.c

index 015256e..8532c3e 100644 (file)
@@ -41,6 +41,8 @@
 #include <linux/of.h>
 #include <linux/mfd/syscon.h>
 #include <linux/regmap.h>
+#include <linux/reboot.h>
+#include <linux/delay.h>
 
 #define S3C2410_WTCON          0x00
 #define S3C2410_WTDAT          0x04
@@ -128,6 +130,7 @@ struct s3c2410_wdt {
        unsigned long           wtdat_save;
        struct watchdog_device  wdt_device;
        struct notifier_block   freq_transition;
+       struct notifier_block   restart_handler;
        struct s3c2410_wdt_variant *drv_data;
        struct regmap *pmureg;
 };
@@ -449,6 +452,31 @@ static inline void s3c2410wdt_cpufreq_deregister(struct s3c2410_wdt *wdt)
 }
 #endif
 
+static int s3c2410wdt_restart(struct notifier_block *this,
+                             unsigned long mode, void *cmd)
+{
+       struct s3c2410_wdt *wdt = container_of(this, struct s3c2410_wdt,
+                                              restart_handler);
+       void __iomem *wdt_base = wdt->reg_base;
+
+       /* disable watchdog, to be safe  */
+       writel(0, wdt_base + S3C2410_WTCON);
+
+       /* put initial values into count and data */
+       writel(0x80, wdt_base + S3C2410_WTCNT);
+       writel(0x80, wdt_base + S3C2410_WTDAT);
+
+       /* set the watchdog to go and reset... */
+       writel(S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV16 |
+               S3C2410_WTCON_RSTEN | S3C2410_WTCON_PRESCALE(0x20),
+               wdt_base + S3C2410_WTCON);
+
+       /* wait for reset to assert... */
+       mdelay(500);
+
+       return NOTIFY_DONE;
+}
+
 static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt)
 {
        unsigned int rst_stat;
@@ -603,6 +631,12 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, wdt);
 
+       wdt->restart_handler.notifier_call = s3c2410wdt_restart;
+       wdt->restart_handler.priority = 128;
+       ret = register_restart_handler(&wdt->restart_handler);
+       if (ret)
+               pr_err("cannot register restart handler, %d\n", ret);
+
        /* print out a statement of readiness */
 
        wtcon = readl(wdt->reg_base + S3C2410_WTCON);
@@ -632,6 +666,8 @@ static int s3c2410wdt_remove(struct platform_device *dev)
        int ret;
        struct s3c2410_wdt *wdt = platform_get_drvdata(dev);
 
+       unregister_restart_handler(&wdt->restart_handler);
+
        ret = s3c2410wdt_mask_and_disable_reset(wdt, true);
        if (ret < 0)
                return ret;