Viresh has moved
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / watchdog / sp805_wdt.c
index bbb170e50055d43e55ff783f4aa645ec45ae1d85..e4841c36798bd758d9a037f8d89de467047d88ae 100644 (file)
@@ -4,7 +4,7 @@
  * Watchdog driver for ARM SP805 watchdog module
  *
  * Copyright (C) 2010 ST Microelectronics
- * Viresh Kumar<viresh.kumar@st.com>
+ * Viresh Kumar <viresh.linux@gmail.com>
  *
  * This file is licensed under the terms of the GNU General Public
  * License version 2 or later. This program is licensed "as is" without any
 #include <linux/amba/bus.h>
 #include <linux/bitops.h>
 #include <linux/clk.h>
-#include <linux/fs.h>
 #include <linux/init.h>
 #include <linux/io.h>
 #include <linux/ioport.h>
 #include <linux/kernel.h>
 #include <linux/math64.h>
-#include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/pm.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
-#include <linux/uaccess.h>
 #include <linux/watchdog.h>
 
 /* default timeout in seconds */
@@ -56,6 +53,7 @@
 
 /**
  * struct sp805_wdt: sp805 wdt device structure
+ * @wdd: instance of struct watchdog_device
  * @lock: spin lock protecting dev structure and io access
  * @base: base address of wdt
  * @clk: clock structure of wdt
  * @timeout: current programmed timeout
  */
 struct sp805_wdt {
+       struct watchdog_device          wdd;
        spinlock_t                      lock;
        void __iomem                    *base;
        struct clk                      *clk;
        struct amba_device              *adev;
-       unsigned long                   status;
-       #define WDT_BUSY                0
-       #define WDT_CAN_BE_CLOSED       1
        unsigned int                    load_val;
        unsigned int                    timeout;
 };
 
-/* local variables */
-static struct sp805_wdt *wdt;
 static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout,
+               "Set to 1 to keep watchdog running after device release");
 
 /* This routine finds load value that will reset system in required timout */
-static void wdt_setload(unsigned int timeout)
+static int wdt_setload(struct watchdog_device *wdd, unsigned int timeout)
 {
+       struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
        u64 load, rate;
 
        rate = clk_get_rate(wdt->clk);
@@ -103,11 +101,14 @@ static void wdt_setload(unsigned int timeout)
        /* roundup timeout to closest positive integer value */
        wdt->timeout = div_u64((load + 1) * 2 + (rate / 2), rate);
        spin_unlock(&wdt->lock);
+
+       return 0;
 }
 
 /* returns number of seconds left for reset to occur */
-static u32 wdt_timeleft(void)
+static unsigned int wdt_timeleft(struct watchdog_device *wdd)
 {
+       struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
        u64 load, rate;
 
        rate = clk_get_rate(wdt->clk);
@@ -123,166 +124,96 @@ static u32 wdt_timeleft(void)
        return div_u64(load, rate);
 }
 
-/* enables watchdog timers reset */
-static void wdt_enable(void)
+static int wdt_config(struct watchdog_device *wdd, bool ping)
 {
-       spin_lock(&wdt->lock);
+       struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
+       int ret;
 
-       writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
-       writel_relaxed(wdt->load_val, wdt->base + WDTLOAD);
-       writel_relaxed(INT_MASK, wdt->base + WDTINTCLR);
-       writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL);
-       writel_relaxed(LOCK, wdt->base + WDTLOCK);
+       if (!ping) {
+               ret = clk_prepare(wdt->clk);
+               if (ret) {
+                       dev_err(&wdt->adev->dev, "clock prepare fail");
+                       return ret;
+               }
 
-       /* Flush posted writes. */
-       readl_relaxed(wdt->base + WDTLOCK);
-       spin_unlock(&wdt->lock);
-}
+               ret = clk_enable(wdt->clk);
+               if (ret) {
+                       dev_err(&wdt->adev->dev, "clock enable fail");
+                       clk_unprepare(wdt->clk);
+                       return ret;
+               }
+       }
 
-/* disables watchdog timers reset */
-static void wdt_disable(void)
-{
        spin_lock(&wdt->lock);
 
        writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
-       writel_relaxed(0, wdt->base + WDTCONTROL);
+       writel_relaxed(wdt->load_val, wdt->base + WDTLOAD);
+
+       if (!ping) {
+               writel_relaxed(INT_MASK, wdt->base + WDTINTCLR);
+               writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base +
+                               WDTCONTROL);
+       }
+
        writel_relaxed(LOCK, wdt->base + WDTLOCK);
 
        /* Flush posted writes. */
        readl_relaxed(wdt->base + WDTLOCK);
        spin_unlock(&wdt->lock);
+
+       return 0;
 }
 
-static ssize_t sp805_wdt_write(struct file *file, const char *data,
-               size_t len, loff_t *ppos)
+static int wdt_ping(struct watchdog_device *wdd)
 {
-       if (len) {
-               if (!nowayout) {
-                       size_t i;
-
-                       clear_bit(WDT_CAN_BE_CLOSED, &wdt->status);
-
-                       for (i = 0; i != len; i++) {
-                               char c;
-
-                               if (get_user(c, data + i))
-                                       return -EFAULT;
-                               /* Check for Magic Close character */
-                               if (c == 'V') {
-                                       set_bit(WDT_CAN_BE_CLOSED,
-                                                       &wdt->status);
-                                       break;
-                               }
-                       }
-               }
-               wdt_enable();
-       }
-       return len;
+       return wdt_config(wdd, true);
 }
 
-static const struct watchdog_info ident = {
-       .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
-       .identity = MODULE_NAME,
-};
-
-static long sp805_wdt_ioctl(struct file *file, unsigned int cmd,
-               unsigned long arg)
+/* enables watchdog timers reset */
+static int wdt_enable(struct watchdog_device *wdd)
 {
-       int ret = -ENOTTY;
-       unsigned int timeout;
-
-       switch (cmd) {
-       case WDIOC_GETSUPPORT:
-               ret = copy_to_user((struct watchdog_info *)arg, &ident,
-                               sizeof(ident)) ? -EFAULT : 0;
-               break;
-
-       case WDIOC_GETSTATUS:
-               ret = put_user(0, (int *)arg);
-               break;
-
-       case WDIOC_KEEPALIVE:
-               wdt_enable();
-               ret = 0;
-               break;
-
-       case WDIOC_SETTIMEOUT:
-               ret = get_user(timeout, (unsigned int *)arg);
-               if (ret)
-                       break;
-
-               wdt_setload(timeout);
-
-               wdt_enable();
-               /* Fall through */
-
-       case WDIOC_GETTIMEOUT:
-               ret = put_user(wdt->timeout, (unsigned int *)arg);
-               break;
-       case WDIOC_GETTIMELEFT:
-               ret = put_user(wdt_timeleft(), (unsigned int *)arg);
-               break;
-       }
-       return ret;
+       return wdt_config(wdd, false);
 }
 
-static int sp805_wdt_open(struct inode *inode, struct file *file)
+/* disables watchdog timers reset */
+static int wdt_disable(struct watchdog_device *wdd)
 {
-       int ret = 0;
-
-       if (test_and_set_bit(WDT_BUSY, &wdt->status))
-               return -EBUSY;
-
-       ret = clk_enable(wdt->clk);
-       if (ret) {
-               dev_err(&wdt->adev->dev, "clock enable fail");
-               goto err;
-       }
-
-       wdt_enable();
+       struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
 
-       /* can not be closed, once enabled */
-       clear_bit(WDT_CAN_BE_CLOSED, &wdt->status);
-       return nonseekable_open(inode, file);
+       spin_lock(&wdt->lock);
 
-err:
-       clear_bit(WDT_BUSY, &wdt->status);
-       return ret;
-}
+       writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
+       writel_relaxed(0, wdt->base + WDTCONTROL);
+       writel_relaxed(LOCK, wdt->base + WDTLOCK);
 
-static int sp805_wdt_release(struct inode *inode, struct file *file)
-{
-       if (!test_bit(WDT_CAN_BE_CLOSED, &wdt->status)) {
-               clear_bit(WDT_BUSY, &wdt->status);
-               dev_warn(&wdt->adev->dev, "Device closed unexpectedly\n");
-               return 0;
-       }
+       /* Flush posted writes. */
+       readl_relaxed(wdt->base + WDTLOCK);
+       spin_unlock(&wdt->lock);
 
-       wdt_disable();
        clk_disable(wdt->clk);
-       clear_bit(WDT_BUSY, &wdt->status);
+       clk_unprepare(wdt->clk);
 
        return 0;
 }
 
-static const struct file_operations sp805_wdt_fops = {
-       .owner = THIS_MODULE,
-       .llseek = no_llseek,
-       .write = sp805_wdt_write,
-       .unlocked_ioctl = sp805_wdt_ioctl,
-       .open = sp805_wdt_open,
-       .release = sp805_wdt_release,
+static const struct watchdog_info wdt_info = {
+       .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+       .identity = MODULE_NAME,
 };
 
-static struct miscdevice sp805_wdt_miscdev = {
-       .minor = WATCHDOG_MINOR,
-       .name = "watchdog",
-       .fops = &sp805_wdt_fops,
+static const struct watchdog_ops wdt_ops = {
+       .owner          = THIS_MODULE,
+       .start          = wdt_enable,
+       .stop           = wdt_disable,
+       .ping           = wdt_ping,
+       .set_timeout    = wdt_setload,
+       .get_timeleft   = wdt_timeleft,
 };
 
 static int __devinit
 sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
 {
+       struct sp805_wdt *wdt;
        int ret = 0;
 
        if (!devm_request_mem_region(&adev->dev, adev->res.start,
@@ -315,19 +246,26 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
        }
 
        wdt->adev = adev;
+       wdt->wdd.info = &wdt_info;
+       wdt->wdd.ops = &wdt_ops;
+
        spin_lock_init(&wdt->lock);
-       wdt_setload(DEFAULT_TIMEOUT);
+       watchdog_set_nowayout(&wdt->wdd, nowayout);
+       watchdog_set_drvdata(&wdt->wdd, wdt);
+       wdt_setload(&wdt->wdd, DEFAULT_TIMEOUT);
 
-       ret = misc_register(&sp805_wdt_miscdev);
-       if (ret < 0) {
-               dev_warn(&adev->dev, "cannot register misc device\n");
-               goto err_misc_register;
+       ret = watchdog_register_device(&wdt->wdd);
+       if (ret) {
+               dev_err(&adev->dev, "watchdog_register_device() failed: %d\n",
+                               ret);
+               goto err_register;
        }
+       amba_set_drvdata(adev, wdt);
 
        dev_info(&adev->dev, "registration successful\n");
        return 0;
 
-err_misc_register:
+err_register:
        clk_put(wdt->clk);
 err:
        dev_err(&adev->dev, "Probe Failed!!!\n");
@@ -336,7 +274,11 @@ err:
 
 static int __devexit sp805_wdt_remove(struct amba_device *adev)
 {
-       misc_deregister(&sp805_wdt_miscdev);
+       struct sp805_wdt *wdt = amba_get_drvdata(adev);
+
+       watchdog_unregister_device(&wdt->wdd);
+       amba_set_drvdata(adev, NULL);
+       watchdog_set_drvdata(&wdt->wdd, NULL);
        clk_put(wdt->clk);
 
        return 0;
@@ -345,28 +287,22 @@ static int __devexit sp805_wdt_remove(struct amba_device *adev)
 #ifdef CONFIG_PM
 static int sp805_wdt_suspend(struct device *dev)
 {
-       if (test_bit(WDT_BUSY, &wdt->status)) {
-               wdt_disable();
-               clk_disable(wdt->clk);
-       }
+       struct sp805_wdt *wdt = dev_get_drvdata(dev);
+
+       if (watchdog_active(&wdt->wdd))
+               return wdt_disable(&wdt->wdd);
 
        return 0;
 }
 
 static int sp805_wdt_resume(struct device *dev)
 {
-       int ret = 0;
+       struct sp805_wdt *wdt = dev_get_drvdata(dev);
 
-       if (test_bit(WDT_BUSY, &wdt->status)) {
-               ret = clk_enable(wdt->clk);
-               if (ret) {
-                       dev_err(dev, "clock enable fail");
-                       return ret;
-               }
-               wdt_enable();
-       }
+       if (watchdog_active(&wdt->wdd))
+               return wdt_enable(&wdt->wdd);
 
-       return ret;
+       return 0;
 }
 #endif /* CONFIG_PM */
 
@@ -395,11 +331,6 @@ static struct amba_driver sp805_wdt_driver = {
 
 module_amba_driver(sp805_wdt_driver);
 
-module_param(nowayout, bool, 0);
-MODULE_PARM_DESC(nowayout,
-               "Set to 1 to keep watchdog running after device release");
-
-MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>");
+MODULE_AUTHOR("Viresh Kumar <viresh.linux@gmail.com>");
 MODULE_DESCRIPTION("ARM SP805 Watchdog Driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);