Commit 67c0f554 authored by Aaro Koskinen's avatar Aaro Koskinen Committed by Wim Van Sebroeck

watchdog: omap_wdt: convert to new watchdog core

Convert omap_wdt to new watchdog core. On OMAP boards, there are usually
multiple watchdogs. Since the new watchdog core supports multiple
watchdogs, all watchdog drivers used on OMAP should be converted.

The legacy watchdog device node is still created, so this should not
break existing users.
Signed-off-by: default avatarAaro Koskinen <aaro.koskinen@iki.fi>
Tested-by: default avatarJarkko Nikula <jarkko.nikula@jollamobile.com>
Tested-by: default avatarLokesh Vutla <lokeshvutla@ti.com>
Signed-off-by: default avatarWim Van Sebroeck <wim@iguana.be>
parent cf13a84d
...@@ -232,6 +232,7 @@ config EP93XX_WATCHDOG ...@@ -232,6 +232,7 @@ config EP93XX_WATCHDOG
config OMAP_WATCHDOG config OMAP_WATCHDOG
tristate "OMAP Watchdog" tristate "OMAP Watchdog"
depends on ARCH_OMAP16XX || ARCH_OMAP2PLUS depends on ARCH_OMAP16XX || ARCH_OMAP2PLUS
select WATCHDOG_CORE
help help
Support for TI OMAP1610/OMAP1710/OMAP2420/OMAP3430/OMAP4430 watchdog. Say 'Y' Support for TI OMAP1610/OMAP1710/OMAP2420/OMAP3430/OMAP4430 watchdog. Say 'Y'
here to enable the OMAP1610/OMAP1710/OMAP2420/OMAP3430/OMAP4430 watchdog timer. here to enable the OMAP1610/OMAP1710/OMAP2420/OMAP3430/OMAP4430 watchdog timer.
......
...@@ -31,42 +31,34 @@ ...@@ -31,42 +31,34 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h> #include <linux/watchdog.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/bitops.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/platform_data/omap-wd-timer.h> #include <linux/platform_data/omap-wd-timer.h>
#include "omap_wdt.h" #include "omap_wdt.h"
static struct platform_device *omap_wdt_dev;
static unsigned timer_margin; static unsigned timer_margin;
module_param(timer_margin, uint, 0); module_param(timer_margin, uint, 0);
MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)"); 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 { struct omap_wdt_dev {
void __iomem *base; /* physical */ void __iomem *base; /* physical */
struct device *dev; struct device *dev;
int omap_wdt_users; bool omap_wdt_users;
struct resource *mem; 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; void __iomem *base = wdev->base;
...@@ -74,8 +66,8 @@ static void omap_wdt_ping(struct omap_wdt_dev *wdev) ...@@ -74,8 +66,8 @@ static void omap_wdt_ping(struct omap_wdt_dev *wdev)
while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08) while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08)
cpu_relax(); cpu_relax();
wdt_trgr_pattern = ~wdt_trgr_pattern; wdev->wdt_trgr_pattern = ~wdev->wdt_trgr_pattern;
__raw_writel(wdt_trgr_pattern, (base + OMAP_WATCHDOG_TGR)); __raw_writel(wdev->wdt_trgr_pattern, (base + OMAP_WATCHDOG_TGR));
/* wait for posted write to complete */ /* wait for posted write to complete */
while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08) while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08)
...@@ -111,18 +103,10 @@ static void omap_wdt_disable(struct omap_wdt_dev *wdev) ...@@ -111,18 +103,10 @@ static void omap_wdt_disable(struct omap_wdt_dev *wdev)
cpu_relax(); cpu_relax();
} }
static void omap_wdt_adjust_timeout(unsigned new_timeout) static void omap_wdt_set_timer(struct omap_wdt_dev *wdev,
{ unsigned int 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)
{ {
u32 pre_margin = GET_WLDR_VAL(timer_margin); u32 pre_margin = GET_WLDR_VAL(timeout);
void __iomem *base = wdev->base; void __iomem *base = wdev->base;
/* just count up at 32 KHz */ /* just count up at 32 KHz */
...@@ -134,16 +118,14 @@ static void omap_wdt_set_timeout(struct omap_wdt_dev *wdev) ...@@ -134,16 +118,14 @@ static void omap_wdt_set_timeout(struct omap_wdt_dev *wdev)
cpu_relax(); cpu_relax();
} }
/* static int omap_wdt_start(struct watchdog_device *wdog)
* Allow only one task to hold it open
*/
static int omap_wdt_open(struct inode *inode, struct file *file)
{ {
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; void __iomem *base = wdev->base;
if (test_and_set_bit(1, (unsigned long *)&(wdev->omap_wdt_users))) mutex_lock(&wdev->lock);
return -EBUSY;
wdev->omap_wdt_users = true;
pm_runtime_get_sync(wdev->dev); pm_runtime_get_sync(wdev->dev);
...@@ -155,117 +137,81 @@ static int omap_wdt_open(struct inode *inode, struct file *file) ...@@ -155,117 +137,81 @@ static int omap_wdt_open(struct inode *inode, struct file *file)
while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01) while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01)
cpu_relax(); cpu_relax();
file->private_data = (void *) wdev; omap_wdt_set_timer(wdev, wdog->timeout);
omap_wdt_reload(wdev); /* trigger loading of new timeout value */
omap_wdt_set_timeout(wdev);
omap_wdt_ping(wdev); /* trigger loading of new timeout value */
omap_wdt_enable(wdev); 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);
/* mutex_lock(&wdev->lock);
* Shut off the timer unless NOWAYOUT is defined.
*/
#ifndef CONFIG_WATCHDOG_NOWAYOUT
omap_wdt_disable(wdev); omap_wdt_disable(wdev);
pm_runtime_put_sync(wdev->dev); pm_runtime_put_sync(wdev->dev);
#else wdev->omap_wdt_users = false;
pr_crit("Unexpected close, not stopping!\n"); mutex_unlock(&wdev->lock);
#endif
wdev->omap_wdt_users = 0;
return 0; return 0;
} }
static ssize_t omap_wdt_write(struct file *file, const char __user *data, static int omap_wdt_ping(struct watchdog_device *wdog)
size_t len, loff_t *ppos)
{ {
struct omap_wdt_dev *wdev = file->private_data; struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
/* Refresh LOAD_TIME. */ mutex_lock(&wdev->lock);
if (len) { omap_wdt_reload(wdev);
spin_lock(&wdt_lock); mutex_unlock(&wdev->lock);
omap_wdt_ping(wdev);
spin_unlock(&wdt_lock); return 0;
}
return len;
} }
static long omap_wdt_ioctl(struct file *file, unsigned int cmd, static int omap_wdt_set_timeout(struct watchdog_device *wdog,
unsigned long arg) unsigned int timeout)
{ {
struct omap_wd_timer_platform_data *pdata; struct omap_wdt_dev *wdev = watchdog_get_drvdata(wdog);
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);
omap_wdt_ping(wdev); mutex_lock(&wdev->lock);
spin_unlock(&wdt_lock); omap_wdt_disable(wdev);
/* Fall */ omap_wdt_set_timer(wdev, timeout);
case WDIOC_GETTIMEOUT: omap_wdt_enable(wdev);
return put_user(timer_margin, (int __user *)arg); omap_wdt_reload(wdev);
default: wdog->timeout = timeout;
return -ENOTTY; mutex_unlock(&wdev->lock);
}
return 0;
} }
static const struct file_operations omap_wdt_fops = { static const struct watchdog_info omap_wdt_info = {
.owner = THIS_MODULE, .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
.write = omap_wdt_write, .identity = "OMAP Watchdog",
.unlocked_ioctl = omap_wdt_ioctl, };
.open = omap_wdt_open,
.release = omap_wdt_release, static const struct watchdog_ops omap_wdt_ops = {
.llseek = no_llseek, .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) 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 resource *res, *mem;
struct omap_wdt_dev *wdev; struct omap_wdt_dev *wdev;
u32 rs;
int ret; int ret;
omap_wdt = kzalloc(sizeof(*omap_wdt), GFP_KERNEL);
if (!omap_wdt)
return -ENOMEM;
/* reserve static register mappings */ /* reserve static register mappings */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) { if (!res) {
...@@ -273,11 +219,6 @@ static int omap_wdt_probe(struct platform_device *pdev) ...@@ -273,11 +219,6 @@ static int omap_wdt_probe(struct platform_device *pdev)
goto err_get_resource; goto err_get_resource;
} }
if (omap_wdt_dev) {
ret = -EBUSY;
goto err_busy;
}
mem = request_mem_region(res->start, resource_size(res), pdev->name); mem = request_mem_region(res->start, resource_size(res), pdev->name);
if (!mem) { if (!mem) {
ret = -EBUSY; ret = -EBUSY;
...@@ -290,9 +231,11 @@ static int omap_wdt_probe(struct platform_device *pdev) ...@@ -290,9 +231,11 @@ static int omap_wdt_probe(struct platform_device *pdev)
goto err_kzalloc; goto err_kzalloc;
} }
wdev->omap_wdt_users = 0; wdev->omap_wdt_users = false;
wdev->mem = mem; wdev->mem = mem;
wdev->dev = &pdev->dev; wdev->dev = &pdev->dev;
wdev->wdt_trgr_pattern = 0x1234;
mutex_init(&wdev->lock);
wdev->base = ioremap(res->start, resource_size(res)); wdev->base = ioremap(res->start, resource_size(res));
if (!wdev->base) { if (!wdev->base) {
...@@ -300,34 +243,47 @@ static int omap_wdt_probe(struct platform_device *pdev) ...@@ -300,34 +243,47 @@ static int omap_wdt_probe(struct platform_device *pdev)
goto err_ioremap; goto err_ioremap;
} }
platform_set_drvdata(pdev, wdev); 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;
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_enable(wdev->dev);
pm_runtime_get_sync(wdev->dev); pm_runtime_get_sync(wdev->dev);
omap_wdt_disable(wdev); if (pdata && pdata->read_reset_sources)
omap_wdt_adjust_timeout(timer_margin); 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; omap_wdt_disable(wdev);
wdev->omap_wdt_miscdev.minor = WATCHDOG_MINOR;
wdev->omap_wdt_miscdev.name = "watchdog";
wdev->omap_wdt_miscdev.fops = &omap_wdt_fops;
ret = misc_register(&(wdev->omap_wdt_miscdev)); ret = watchdog_register_device(omap_wdt);
if (ret) if (ret)
goto err_misc; goto err_register;
pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n", pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n",
__raw_readl(wdev->base + OMAP_WATCHDOG_REV) & 0xFF, __raw_readl(wdev->base + OMAP_WATCHDOG_REV) & 0xFF,
timer_margin); omap_wdt->timeout);
pm_runtime_put_sync(wdev->dev); pm_runtime_put_sync(wdev->dev);
omap_wdt_dev = pdev;
return 0; return 0;
err_misc: err_register:
pm_runtime_disable(wdev->dev); pm_runtime_disable(wdev->dev);
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
iounmap(wdev->base); iounmap(wdev->base);
...@@ -341,37 +297,38 @@ static int omap_wdt_probe(struct platform_device *pdev) ...@@ -341,37 +297,38 @@ static int omap_wdt_probe(struct platform_device *pdev)
err_busy: err_busy:
err_get_resource: err_get_resource:
kfree(omap_wdt);
return ret; return ret;
} }
static void omap_wdt_shutdown(struct platform_device *pdev) 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) { if (wdev->omap_wdt_users) {
omap_wdt_disable(wdev); omap_wdt_disable(wdev);
pm_runtime_put_sync(wdev->dev); pm_runtime_put_sync(wdev->dev);
} }
mutex_unlock(&wdev->lock);
} }
static int omap_wdt_remove(struct platform_device *pdev) static int omap_wdt_remove(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);
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pm_runtime_disable(wdev->dev); pm_runtime_disable(wdev->dev);
if (!res) watchdog_unregister_device(wdog);
return -ENOENT;
misc_deregister(&(wdev->omap_wdt_miscdev));
release_mem_region(res->start, resource_size(res)); release_mem_region(res->start, resource_size(res));
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
iounmap(wdev->base); iounmap(wdev->base);
kfree(wdev); kfree(wdev);
omap_wdt_dev = NULL; kfree(wdog);
return 0; return 0;
} }
...@@ -386,25 +343,31 @@ static int omap_wdt_remove(struct platform_device *pdev) ...@@ -386,25 +343,31 @@ static int omap_wdt_remove(struct platform_device *pdev)
static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state) 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) { if (wdev->omap_wdt_users) {
omap_wdt_disable(wdev); omap_wdt_disable(wdev);
pm_runtime_put_sync(wdev->dev); pm_runtime_put_sync(wdev->dev);
} }
mutex_unlock(&wdev->lock);
return 0; return 0;
} }
static int omap_wdt_resume(struct platform_device *pdev) 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) { if (wdev->omap_wdt_users) {
pm_runtime_get_sync(wdev->dev); pm_runtime_get_sync(wdev->dev);
omap_wdt_enable(wdev); omap_wdt_enable(wdev);
omap_wdt_ping(wdev); omap_wdt_reload(wdev);
} }
mutex_unlock(&wdev->lock);
return 0; return 0;
} }
...@@ -437,5 +400,4 @@ module_platform_driver(omap_wdt_driver); ...@@ -437,5 +400,4 @@ module_platform_driver(omap_wdt_driver);
MODULE_AUTHOR("George G. Davis"); MODULE_AUTHOR("George G. Davis");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS("platform:omap_wdt"); MODULE_ALIAS("platform:omap_wdt");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment