Merge branch 'timer/cleanup' into late/mvebu2
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / watchdog / omap_wdt.c
index 3e3ebbc83fafe8ba194470c0da8d9fe862897684..b0e541d022e68a23d6edb8d05bd306d21d291728 100644 (file)
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
-#include <linux/fs.h>
 #include <linux/mm.h>
-#include <linux/miscdevice.h>
 #include <linux/watchdog.h>
 #include <linux/reboot.h>
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/platform_device.h>
 #include <linux/moduleparam.h>
-#include <linux/bitops.h>
 #include <linux/io.h>
-#include <linux/uaccess.h>
 #include <linux/slab.h>
 #include <linux/pm_runtime.h>
 #include <linux/platform_data/omap-wd-timer.h>
 
 #include "omap_wdt.h"
 
-static struct platform_device *omap_wdt_dev;
-
 static unsigned timer_margin;
 module_param(timer_margin, uint, 0);
 MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)");
 
-static unsigned int wdt_trgr_pattern = 0x1234;
-static DEFINE_SPINLOCK(wdt_lock);
-
 struct omap_wdt_dev {
        void __iomem    *base;          /* physical */
        struct device   *dev;
-       int             omap_wdt_users;
+       bool            omap_wdt_users;
        struct resource *mem;
-       struct miscdevice omap_wdt_miscdev;
+       int             wdt_trgr_pattern;
+       struct mutex    lock;           /* to avoid races with PM */
 };
 
-static void omap_wdt_ping(struct omap_wdt_dev *wdev)
+static void omap_wdt_reload(struct omap_wdt_dev *wdev)
 {
        void __iomem    *base = wdev->base;
 
@@ -74,8 +66,8 @@ static void omap_wdt_ping(struct omap_wdt_dev *wdev)
        while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08)
                cpu_relax();
 
-       wdt_trgr_pattern = ~wdt_trgr_pattern;
-       __raw_writel(wdt_trgr_pattern, (base + OMAP_WATCHDOG_TGR));
+       wdev->wdt_trgr_pattern = ~wdev->wdt_trgr_pattern;
+       __raw_writel(wdev->wdt_trgr_pattern, (base + OMAP_WATCHDOG_TGR));
 
        /* wait for posted write to complete */
        while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08)
@@ -111,18 +103,10 @@ static void omap_wdt_disable(struct omap_wdt_dev *wdev)
                cpu_relax();
 }
 
-static void omap_wdt_adjust_timeout(unsigned new_timeout)
-{
-       if (new_timeout < TIMER_MARGIN_MIN)
-               new_timeout = TIMER_MARGIN_DEFAULT;
-       if (new_timeout > TIMER_MARGIN_MAX)
-               new_timeout = TIMER_MARGIN_MAX;
-       timer_margin = new_timeout;
-}
-
-static void omap_wdt_set_timeout(struct omap_wdt_dev *wdev)
+static void omap_wdt_set_timer(struct omap_wdt_dev *wdev,
+                                  unsigned int timeout)
 {
-       u32 pre_margin = GET_WLDR_VAL(timer_margin);
+       u32 pre_margin = GET_WLDR_VAL(timeout);
        void __iomem *base = wdev->base;
 
        /* just count up at 32 KHz */
@@ -134,16 +118,14 @@ static void omap_wdt_set_timeout(struct omap_wdt_dev *wdev)
                cpu_relax();
 }
 
-/*
- *     Allow only one task to hold it open
- */
-static int omap_wdt_open(struct inode *inode, struct file *file)
+static int omap_wdt_start(struct watchdog_device *wdog)
 {
-       struct omap_wdt_dev *wdev = platform_get_drvdata(omap_wdt_dev);
+       struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
        void __iomem *base = wdev->base;
 
-       if (test_and_set_bit(1, (unsigned long *)&(wdev->omap_wdt_users)))
-               return -EBUSY;
+       mutex_lock(&wdev->lock);
+
+       wdev->omap_wdt_users = true;
 
        pm_runtime_get_sync(wdev->dev);
 
@@ -155,223 +137,168 @@ static int omap_wdt_open(struct inode *inode, struct file *file)
        while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01)
                cpu_relax();
 
-       file->private_data = (void *) wdev;
-
-       omap_wdt_set_timeout(wdev);
-       omap_wdt_ping(wdev); /* trigger loading of new timeout value */
+       omap_wdt_set_timer(wdev, wdog->timeout);
+       omap_wdt_reload(wdev); /* trigger loading of new timeout value */
        omap_wdt_enable(wdev);
 
-       return nonseekable_open(inode, file);
+       mutex_unlock(&wdev->lock);
+
+       return 0;
 }
 
-static int omap_wdt_release(struct inode *inode, struct file *file)
+static int omap_wdt_stop(struct watchdog_device *wdog)
 {
-       struct omap_wdt_dev *wdev = file->private_data;
+       struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
 
-       /*
-        *      Shut off the timer unless NOWAYOUT is defined.
-        */
-#ifndef CONFIG_WATCHDOG_NOWAYOUT
+       mutex_lock(&wdev->lock);
        omap_wdt_disable(wdev);
-
        pm_runtime_put_sync(wdev->dev);
-#else
-       pr_crit("Unexpected close, not stopping!\n");
-#endif
-       wdev->omap_wdt_users = 0;
-
+       wdev->omap_wdt_users = false;
+       mutex_unlock(&wdev->lock);
        return 0;
 }
 
-static ssize_t omap_wdt_write(struct file *file, const char __user *data,
-               size_t len, loff_t *ppos)
+static int omap_wdt_ping(struct watchdog_device *wdog)
 {
-       struct omap_wdt_dev *wdev = file->private_data;
+       struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
 
-       /* Refresh LOAD_TIME. */
-       if (len) {
-               spin_lock(&wdt_lock);
-               omap_wdt_ping(wdev);
-               spin_unlock(&wdt_lock);
-       }
-       return len;
+       mutex_lock(&wdev->lock);
+       omap_wdt_reload(wdev);
+       mutex_unlock(&wdev->lock);
+
+       return 0;
 }
 
-static long omap_wdt_ioctl(struct file *file, unsigned int cmd,
-                                               unsigned long arg)
+static int omap_wdt_set_timeout(struct watchdog_device *wdog,
+                               unsigned int timeout)
 {
-       struct omap_wd_timer_platform_data *pdata;
-       struct omap_wdt_dev *wdev;
-       u32 rs;
-       int new_margin, bs;
-       static const struct watchdog_info ident = {
-               .identity = "OMAP Watchdog",
-               .options = WDIOF_SETTIMEOUT,
-               .firmware_version = 0,
-       };
-
-       wdev = file->private_data;
-       pdata = wdev->dev->platform_data;
-
-       switch (cmd) {
-       case WDIOC_GETSUPPORT:
-               return copy_to_user((struct watchdog_info __user *)arg, &ident,
-                               sizeof(ident));
-       case WDIOC_GETSTATUS:
-               return put_user(0, (int __user *)arg);
-       case WDIOC_GETBOOTSTATUS:
-               if (!pdata || !pdata->read_reset_sources)
-                       return put_user(0, (int __user *)arg);
-               rs = pdata->read_reset_sources();
-               bs = (rs & (1 << OMAP_MPU_WD_RST_SRC_ID_SHIFT)) ?
-                       WDIOF_CARDRESET : 0;
-               return put_user(bs, (int __user *)arg);
-       case WDIOC_KEEPALIVE:
-               spin_lock(&wdt_lock);
-               omap_wdt_ping(wdev);
-               spin_unlock(&wdt_lock);
-               return 0;
-       case WDIOC_SETTIMEOUT:
-               if (get_user(new_margin, (int __user *)arg))
-                       return -EFAULT;
-               omap_wdt_adjust_timeout(new_margin);
-
-               spin_lock(&wdt_lock);
-               omap_wdt_disable(wdev);
-               omap_wdt_set_timeout(wdev);
-               omap_wdt_enable(wdev);
+       struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
 
-               omap_wdt_ping(wdev);
-               spin_unlock(&wdt_lock);
-               /* Fall */
-       case WDIOC_GETTIMEOUT:
-               return put_user(timer_margin, (int __user *)arg);
-       default:
-               return -ENOTTY;
-       }
+       mutex_lock(&wdev->lock);
+       omap_wdt_disable(wdev);
+       omap_wdt_set_timer(wdev, timeout);
+       omap_wdt_enable(wdev);
+       omap_wdt_reload(wdev);
+       wdog->timeout = timeout;
+       mutex_unlock(&wdev->lock);
+
+       return 0;
 }
 
-static const struct file_operations omap_wdt_fops = {
-       .owner = THIS_MODULE,
-       .write = omap_wdt_write,
-       .unlocked_ioctl = omap_wdt_ioctl,
-       .open = omap_wdt_open,
-       .release = omap_wdt_release,
-       .llseek = no_llseek,
+static const struct watchdog_info omap_wdt_info = {
+       .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+       .identity = "OMAP Watchdog",
+};
+
+static const struct watchdog_ops omap_wdt_ops = {
+       .owner          = THIS_MODULE,
+       .start          = omap_wdt_start,
+       .stop           = omap_wdt_stop,
+       .ping           = omap_wdt_ping,
+       .set_timeout    = omap_wdt_set_timeout,
 };
 
 static int omap_wdt_probe(struct platform_device *pdev)
 {
+       struct omap_wd_timer_platform_data *pdata = pdev->dev.platform_data;
+       bool nowayout = WATCHDOG_NOWAYOUT;
+       struct watchdog_device *omap_wdt;
        struct resource *res, *mem;
        struct omap_wdt_dev *wdev;
+       u32 rs;
        int ret;
 
+       omap_wdt = devm_kzalloc(&pdev->dev, sizeof(*omap_wdt), GFP_KERNEL);
+       if (!omap_wdt)
+               return -ENOMEM;
+
        /* reserve static register mappings */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               ret = -ENOENT;
-               goto err_get_resource;
-       }
+       if (!res)
+               return -ENOENT;
 
-       if (omap_wdt_dev) {
-               ret = -EBUSY;
-               goto err_busy;
-       }
+       mem = devm_request_mem_region(&pdev->dev, res->start,
+                                     resource_size(res), pdev->name);
+       if (!mem)
+               return -EBUSY;
 
-       mem = request_mem_region(res->start, resource_size(res), pdev->name);
-       if (!mem) {
-               ret = -EBUSY;
-               goto err_busy;
-       }
+       wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL);
+       if (!wdev)
+               return -ENOMEM;
 
-       wdev = kzalloc(sizeof(struct omap_wdt_dev), GFP_KERNEL);
-       if (!wdev) {
-               ret = -ENOMEM;
-               goto err_kzalloc;
-       }
+       wdev->omap_wdt_users    = false;
+       wdev->mem               = mem;
+       wdev->dev               = &pdev->dev;
+       wdev->wdt_trgr_pattern  = 0x1234;
+       mutex_init(&wdev->lock);
 
-       wdev->omap_wdt_users = 0;
-       wdev->mem = mem;
-       wdev->dev = &pdev->dev;
+       wdev->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+       if (!wdev->base)
+               return -ENOMEM;
 
-       wdev->base = ioremap(res->start, resource_size(res));
-       if (!wdev->base) {
-               ret = -ENOMEM;
-               goto err_ioremap;
-       }
+       omap_wdt->info        = &omap_wdt_info;
+       omap_wdt->ops         = &omap_wdt_ops;
+       omap_wdt->min_timeout = TIMER_MARGIN_MIN;
+       omap_wdt->max_timeout = TIMER_MARGIN_MAX;
+
+       if (timer_margin >= TIMER_MARGIN_MIN &&
+           timer_margin <= TIMER_MARGIN_MAX)
+               omap_wdt->timeout = timer_margin;
+       else
+               omap_wdt->timeout = TIMER_MARGIN_DEFAULT;
 
-       platform_set_drvdata(pdev, wdev);
+       watchdog_set_drvdata(omap_wdt, wdev);
+       watchdog_set_nowayout(omap_wdt, nowayout);
+
+       platform_set_drvdata(pdev, omap_wdt);
 
        pm_runtime_enable(wdev->dev);
        pm_runtime_get_sync(wdev->dev);
 
-       omap_wdt_disable(wdev);
-       omap_wdt_adjust_timeout(timer_margin);
+       if (pdata && pdata->read_reset_sources)
+               rs = pdata->read_reset_sources();
+       else
+               rs = 0;
+       omap_wdt->bootstatus = (rs & (1 << OMAP_MPU_WD_RST_SRC_ID_SHIFT)) ?
+                               WDIOF_CARDRESET : 0;
 
-       wdev->omap_wdt_miscdev.parent = &pdev->dev;
-       wdev->omap_wdt_miscdev.minor = WATCHDOG_MINOR;
-       wdev->omap_wdt_miscdev.name = "watchdog";
-       wdev->omap_wdt_miscdev.fops = &omap_wdt_fops;
+       omap_wdt_disable(wdev);
 
-       ret = misc_register(&(wdev->omap_wdt_miscdev));
-       if (ret)
-               goto err_misc;
+       ret = watchdog_register_device(omap_wdt);
+       if (ret) {
+               pm_runtime_disable(wdev->dev);
+               return ret;
+       }
 
        pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n",
                __raw_readl(wdev->base + OMAP_WATCHDOG_REV) & 0xFF,
-               timer_margin);
+               omap_wdt->timeout);
 
        pm_runtime_put_sync(wdev->dev);
 
-       omap_wdt_dev = pdev;
-
        return 0;
-
-err_misc:
-       pm_runtime_disable(wdev->dev);
-       platform_set_drvdata(pdev, NULL);
-       iounmap(wdev->base);
-
-err_ioremap:
-       wdev->base = NULL;
-       kfree(wdev);
-
-err_kzalloc:
-       release_mem_region(res->start, resource_size(res));
-
-err_busy:
-err_get_resource:
-
-       return ret;
 }
 
 static void omap_wdt_shutdown(struct platform_device *pdev)
 {
-       struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
+       struct watchdog_device *wdog = platform_get_drvdata(pdev);
+       struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
 
+       mutex_lock(&wdev->lock);
        if (wdev->omap_wdt_users) {
                omap_wdt_disable(wdev);
                pm_runtime_put_sync(wdev->dev);
        }
+       mutex_unlock(&wdev->lock);
 }
 
 static int omap_wdt_remove(struct platform_device *pdev)
 {
-       struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
-       struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       struct watchdog_device *wdog = platform_get_drvdata(pdev);
+       struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
 
        pm_runtime_disable(wdev->dev);
-       if (!res)
-               return -ENOENT;
-
-       misc_deregister(&(wdev->omap_wdt_miscdev));
-       release_mem_region(res->start, resource_size(res));
-       platform_set_drvdata(pdev, NULL);
-
-       iounmap(wdev->base);
-
-       kfree(wdev);
-       omap_wdt_dev = NULL;
+       watchdog_unregister_device(wdog);
 
        return 0;
 }
@@ -386,25 +313,31 @@ static int omap_wdt_remove(struct platform_device *pdev)
 
 static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state)
 {
-       struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
+       struct watchdog_device *wdog = platform_get_drvdata(pdev);
+       struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
 
+       mutex_lock(&wdev->lock);
        if (wdev->omap_wdt_users) {
                omap_wdt_disable(wdev);
                pm_runtime_put_sync(wdev->dev);
        }
+       mutex_unlock(&wdev->lock);
 
        return 0;
 }
 
 static int omap_wdt_resume(struct platform_device *pdev)
 {
-       struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
+       struct watchdog_device *wdog = platform_get_drvdata(pdev);
+       struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
 
+       mutex_lock(&wdev->lock);
        if (wdev->omap_wdt_users) {
                pm_runtime_get_sync(wdev->dev);
                omap_wdt_enable(wdev);
-               omap_wdt_ping(wdev);
+               omap_wdt_reload(wdev);
        }
+       mutex_unlock(&wdev->lock);
 
        return 0;
 }
@@ -437,5 +370,4 @@ module_platform_driver(omap_wdt_driver);
 
 MODULE_AUTHOR("George G. Davis");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 MODULE_ALIAS("platform:omap_wdt");