Merge tag 'dma-buf-for-4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/sumits...
[firefly-linux-kernel-4.4.55.git] / drivers / watchdog / at91rm9200_wdt.c
index d244112d5b6f0541da038f5366091c0e592d3e8a..41cecb55766c049b600834d6ee021ef45f15332e 100644 (file)
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/bitops.h>
+#include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/init.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/atmel-st.h>
 #include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/regmap.h>
 #include <linux/types.h>
 #include <linux/watchdog.h>
 #include <linux/uaccess.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
-#include <mach/at91_st.h>
 
 #define WDT_DEFAULT_TIME       5       /* seconds */
 #define WDT_MAX_TIME           256     /* seconds */
 
 static int wdt_time = WDT_DEFAULT_TIME;
 static bool nowayout = WATCHDOG_NOWAYOUT;
+static struct regmap *regmap_st;
 
 module_param(wdt_time, int, 0);
 MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="
@@ -50,12 +55,33 @@ static unsigned long at91wdt_busy;
 
 /* ......................................................................... */
 
+static int at91rm9200_restart(struct notifier_block *this,
+                                       unsigned long mode, void *cmd)
+{
+       /*
+        * Perform a hardware reset with the use of the Watchdog timer.
+        */
+       regmap_write(regmap_st, AT91_ST_WDMR,
+                    AT91_ST_RSTEN | AT91_ST_EXTEN | 1);
+       regmap_write(regmap_st, AT91_ST_CR, AT91_ST_WDRST);
+
+       mdelay(2000);
+
+       pr_emerg("Unable to restart system\n");
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block at91rm9200_restart_nb = {
+       .notifier_call = at91rm9200_restart,
+       .priority = 192,
+};
+
 /*
  * Disable the watchdog.
  */
 static inline void at91_wdt_stop(void)
 {
-       at91_st_write(AT91_ST_WDMR, AT91_ST_EXTEN);
+       regmap_write(regmap_st, AT91_ST_WDMR, AT91_ST_EXTEN);
 }
 
 /*
@@ -63,9 +89,9 @@ static inline void at91_wdt_stop(void)
  */
 static inline void at91_wdt_start(void)
 {
-       at91_st_write(AT91_ST_WDMR, AT91_ST_EXTEN | AT91_ST_RSTEN |
+       regmap_write(regmap_st, AT91_ST_WDMR, AT91_ST_EXTEN | AT91_ST_RSTEN |
                                (((65536 * wdt_time) >> 8) & AT91_ST_WDV));
-       at91_st_write(AT91_ST_CR, AT91_ST_WDRST);
+       regmap_write(regmap_st, AT91_ST_CR, AT91_ST_WDRST);
 }
 
 /*
@@ -73,7 +99,7 @@ static inline void at91_wdt_start(void)
  */
 static inline void at91_wdt_reload(void)
 {
-       at91_st_write(AT91_ST_CR, AT91_ST_WDRST);
+       regmap_write(regmap_st, AT91_ST_CR, AT91_ST_WDRST);
 }
 
 /* ......................................................................... */
@@ -203,16 +229,32 @@ static struct miscdevice at91wdt_miscdev = {
 
 static int at91wdt_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
+       struct device *parent;
        int res;
 
        if (at91wdt_miscdev.parent)
                return -EBUSY;
        at91wdt_miscdev.parent = &pdev->dev;
 
+       parent = dev->parent;
+       if (!parent) {
+               dev_err(dev, "no parent\n");
+               return -ENODEV;
+       }
+
+       regmap_st = syscon_node_to_regmap(parent->of_node);
+       if (!regmap_st)
+               return -ENODEV;
+
        res = misc_register(&at91wdt_miscdev);
        if (res)
                return res;
 
+       res = register_restart_handler(&at91rm9200_restart_nb);
+       if (res)
+               dev_warn(dev, "failed to register restart handler\n");
+
        pr_info("AT91 Watchdog Timer enabled (%d seconds%s)\n",
                wdt_time, nowayout ? ", nowayout" : "");
        return 0;
@@ -220,8 +262,13 @@ static int at91wdt_probe(struct platform_device *pdev)
 
 static int at91wdt_remove(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        int res;
 
+       res = unregister_restart_handler(&at91rm9200_restart_nb);
+       if (res)
+               dev_warn(dev, "failed to unregister restart handler\n");
+
        res = misc_deregister(&at91wdt_miscdev);
        if (!res)
                at91wdt_miscdev.parent = NULL;
@@ -267,7 +314,7 @@ static struct platform_driver at91wdt_driver = {
        .suspend        = at91wdt_suspend,
        .resume         = at91wdt_resume,
        .driver         = {
-               .name   = "at91_wdt",
+               .name   = "atmel_st_watchdog",
                .of_match_table = at91_wdt_dt_ids,
        },
 };
@@ -296,4 +343,4 @@ module_exit(at91_wdt_exit);
 MODULE_AUTHOR("Andrew Victor");
 MODULE_DESCRIPTION("Watchdog driver for Atmel AT91RM9200");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:at91_wdt");
+MODULE_ALIAS("platform:atmel_st_watchdog");