Merge branch 'kvm-arm/vgic-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git...
[firefly-linux-kernel-4.4.55.git] / drivers / watchdog / at91sam9_wdt.c
index c08933cc565e19c2b73ec9b9824a55d7aa983071..be37dde4f864448c13643ee4535e5b6a830be34c 100644 (file)
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/errno.h>
-#include <linux/fs.h>
 #include <linux/init.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
-#include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/platform_device.h>
@@ -58,7 +56,7 @@
 
 /* User land timeout */
 #define WDT_HEARTBEAT 15
-static int heartbeat = WDT_HEARTBEAT;
+static int heartbeat;
 module_param(heartbeat, int, 0);
 MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
        "(default = " __MODULE_STRING(WDT_HEARTBEAT) ")");
@@ -68,19 +66,17 @@ module_param(nowayout, bool, 0);
 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
        "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 
+static struct watchdog_device at91_wdt_dev;
 static void at91_ping(unsigned long data);
 
 static struct {
        void __iomem *base;
        unsigned long next_heartbeat;   /* the next_heartbeat for the timer */
-       unsigned long open;
-       char expect_close;
        struct timer_list timer;        /* The timer that pings the watchdog */
 } at91wdt_private;
 
 /* ......................................................................... */
 
-
 /*
  * Reload the watchdog timer.  (ie, pat the watchdog)
  */
@@ -95,39 +91,37 @@ static inline void at91_wdt_reset(void)
 static void at91_ping(unsigned long data)
 {
        if (time_before(jiffies, at91wdt_private.next_heartbeat) ||
-                       (!nowayout && !at91wdt_private.open)) {
+           (!watchdog_active(&at91_wdt_dev))) {
                at91_wdt_reset();
                mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
        } else
                pr_crit("I will reset your machine !\n");
 }
 
-/*
- * Watchdog device is opened, and watchdog starts running.
- */
-static int at91_wdt_open(struct inode *inode, struct file *file)
+static int at91_wdt_ping(struct watchdog_device *wdd)
 {
-       if (test_and_set_bit(0, &at91wdt_private.open))
-               return -EBUSY;
+       /* calculate when the next userspace timeout will be */
+       at91wdt_private.next_heartbeat = jiffies + wdd->timeout * HZ;
+       return 0;
+}
 
-       at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
+static int at91_wdt_start(struct watchdog_device *wdd)
+{
+       /* calculate the next userspace timeout and modify the timer */
+       at91_wdt_ping(wdd);
        mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
-
-       return nonseekable_open(inode, file);
+       return 0;
 }
 
-/*
- * Close the watchdog device.
- */
-static int at91_wdt_close(struct inode *inode, struct file *file)
+static int at91_wdt_stop(struct watchdog_device *wdd)
 {
-       clear_bit(0, &at91wdt_private.open);
-
-       /* stop internal ping */
-       if (!at91wdt_private.expect_close)
-               del_timer(&at91wdt_private.timer);
+       /* The watchdog timer hardware can not be stopped... */
+       return 0;
+}
 
-       at91wdt_private.expect_close = 0;
+static int at91_wdt_set_timeout(struct watchdog_device *wdd, unsigned int new_timeout)
+{
+       wdd->timeout = new_timeout;
        return 0;
 }
 
@@ -163,96 +157,28 @@ static int at91_wdt_settimeout(unsigned int timeout)
        return 0;
 }
 
+/* ......................................................................... */
+
 static const struct watchdog_info at91_wdt_info = {
        .identity       = DRV_NAME,
        .options        = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
                                                WDIOF_MAGICCLOSE,
 };
 
-/*
- * Handle commands from user-space.
- */
-static long at91_wdt_ioctl(struct file *file,
-               unsigned int cmd, unsigned long arg)
-{
-       void __user *argp = (void __user *)arg;
-       int __user *p = argp;
-       int new_value;
-
-       switch (cmd) {
-       case WDIOC_GETSUPPORT:
-               return copy_to_user(argp, &at91_wdt_info,
-                                   sizeof(at91_wdt_info)) ? -EFAULT : 0;
-
-       case WDIOC_GETSTATUS:
-       case WDIOC_GETBOOTSTATUS:
-               return put_user(0, p);
-
-       case WDIOC_KEEPALIVE:
-               at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
-               return 0;
-
-       case WDIOC_SETTIMEOUT:
-               if (get_user(new_value, p))
-                       return -EFAULT;
-
-               heartbeat = new_value;
-               at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
-
-               return put_user(new_value, p);  /* return current value */
-
-       case WDIOC_GETTIMEOUT:
-               return put_user(heartbeat, p);
-       }
-       return -ENOTTY;
-}
-
-/*
- * Pat the watchdog whenever device is written to.
- */
-static ssize_t at91_wdt_write(struct file *file, const char *data, size_t len,
-                                                               loff_t *ppos)
-{
-       if (!len)
-               return 0;
-
-       /* Scan for magic character */
-       if (!nowayout) {
-               size_t i;
-
-               at91wdt_private.expect_close = 0;
-
-               for (i = 0; i < len; i++) {
-                       char c;
-                       if (get_user(c, data + i))
-                               return -EFAULT;
-                       if (c == 'V') {
-                               at91wdt_private.expect_close = 42;
-                               break;
-                       }
-               }
-       }
-
-       at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
-
-       return len;
-}
-
-/* ......................................................................... */
-
-static const struct file_operations at91wdt_fops = {
-       .owner                  = THIS_MODULE,
-       .llseek                 = no_llseek,
-       .unlocked_ioctl = at91_wdt_ioctl,
-       .open                   = at91_wdt_open,
-       .release                = at91_wdt_close,
-       .write                  = at91_wdt_write,
+static const struct watchdog_ops at91_wdt_ops = {
+       .owner =        THIS_MODULE,
+       .start =        at91_wdt_start,
+       .stop =         at91_wdt_stop,
+       .ping =         at91_wdt_ping,
+       .set_timeout =  at91_wdt_set_timeout,
 };
 
-static struct miscdevice at91wdt_miscdev = {
-       .minor          = WATCHDOG_MINOR,
-       .name           = "watchdog",
-       .fops           = &at91wdt_fops,
+static struct watchdog_device at91_wdt_dev = {
+       .info =         &at91_wdt_info,
+       .ops =          &at91_wdt_ops,
+       .timeout =      WDT_HEARTBEAT,
+       .min_timeout =  1,
+       .max_timeout =  0xFFFF,
 };
 
 static int __init at91wdt_probe(struct platform_device *pdev)
@@ -260,10 +186,6 @@ static int __init at91wdt_probe(struct platform_device *pdev)
        struct resource *r;
        int res;
 
-       if (at91wdt_miscdev.parent)
-               return -EBUSY;
-       at91wdt_miscdev.parent = &pdev->dev;
-
        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!r)
                return -ENODEV;
@@ -273,38 +195,41 @@ static int __init at91wdt_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
+       at91_wdt_dev.parent = &pdev->dev;
+       watchdog_init_timeout(&at91_wdt_dev, heartbeat, &pdev->dev);
+       watchdog_set_nowayout(&at91_wdt_dev, nowayout);
+
        /* Set watchdog */
        res = at91_wdt_settimeout(ms_to_ticks(WDT_HW_TIMEOUT * 1000));
        if (res)
                return res;
 
-       res = misc_register(&at91wdt_miscdev);
+       res = watchdog_register_device(&at91_wdt_dev);
        if (res)
                return res;
 
-       at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
+       at91wdt_private.next_heartbeat = jiffies + at91_wdt_dev.timeout * HZ;
        setup_timer(&at91wdt_private.timer, at91_ping, 0);
        mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
 
        pr_info("enabled (heartbeat=%d sec, nowayout=%d)\n",
-               heartbeat, nowayout);
+               at91_wdt_dev.timeout, nowayout);
 
        return 0;
 }
 
 static int __exit at91wdt_remove(struct platform_device *pdev)
 {
-       int res;
+       watchdog_unregister_device(&at91_wdt_dev);
 
-       res = misc_deregister(&at91wdt_miscdev);
-       if (!res)
-               at91wdt_miscdev.parent = NULL;
+       pr_warn("I quit now, hardware will probably reboot!\n");
+       del_timer(&at91wdt_private.timer);
 
-       return res;
+       return 0;
 }
 
 #if defined(CONFIG_OF)
-static const struct of_device_id at91_wdt_dt_ids[] __initconst = {
+static const struct of_device_id at91_wdt_dt_ids[] = {
        { .compatible = "atmel,at91sam9260-wdt" },
        { /* sentinel */ }
 };
@@ -326,4 +251,3 @@ module_platform_driver_probe(at91wdt_driver, at91wdt_probe);
 MODULE_AUTHOR("Renaud CERRATO <r.cerrato@til-technologies.fr>");
 MODULE_DESCRIPTION("Watchdog driver for Atmel AT91SAM9x processors");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);