watchdog: sp805_wdt: convert to watchdog core

This patch converts existing sp805 watchdog driver to use already in place
common infrastructure present in watchdog core. With this lot of code goes away.

Signed-off-by: Viresh Kumar <viresh.kumar@st.com>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
This commit is contained in:
Viresh Kumar 2012-03-12 09:52:16 +05:30 committed by Wim Van Sebroeck
parent 2d8c7ff52c
commit 4a516539fa
2 changed files with 85 additions and 161 deletions

View File

@ -99,6 +99,7 @@ config WM8350_WATCHDOG
config ARM_SP805_WATCHDOG config ARM_SP805_WATCHDOG
tristate "ARM SP805 Watchdog" tristate "ARM SP805 Watchdog"
depends on ARM_AMBA depends on ARM_AMBA
select WATCHDOG_CORE
help help
ARM Primecell SP805 Watchdog timer. This will reboot your system when ARM Primecell SP805 Watchdog timer. This will reboot your system when
the timeout is reached. the timeout is reached.

View File

@ -16,20 +16,17 @@
#include <linux/amba/bus.h> #include <linux/amba/bus.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/fs.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/math64.h> #include <linux/math64.h>
#include <linux/miscdevice.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/watchdog.h> #include <linux/watchdog.h>
/* default timeout in seconds */ /* default timeout in seconds */
@ -56,6 +53,7 @@
/** /**
* struct sp805_wdt: sp805 wdt device structure * struct sp805_wdt: sp805 wdt device structure
* @wdd: instance of struct watchdog_device
* @lock: spin lock protecting dev structure and io access * @lock: spin lock protecting dev structure and io access
* @base: base address of wdt * @base: base address of wdt
* @clk: clock structure of wdt * @clk: clock structure of wdt
@ -65,24 +63,24 @@
* @timeout: current programmed timeout * @timeout: current programmed timeout
*/ */
struct sp805_wdt { struct sp805_wdt {
struct watchdog_device wdd;
spinlock_t lock; spinlock_t lock;
void __iomem *base; void __iomem *base;
struct clk *clk; struct clk *clk;
struct amba_device *adev; struct amba_device *adev;
unsigned long status;
#define WDT_BUSY 0
#define WDT_CAN_BE_CLOSED 1
unsigned int load_val; unsigned int load_val;
unsigned int timeout; unsigned int timeout;
}; };
/* local variables */
static struct sp805_wdt *wdt;
static bool nowayout = WATCHDOG_NOWAYOUT; 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 */ /* 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; u64 load, rate;
rate = clk_get_rate(wdt->clk); rate = clk_get_rate(wdt->clk);
@ -103,11 +101,14 @@ static void wdt_setload(unsigned int timeout)
/* roundup timeout to closest positive integer value */ /* roundup timeout to closest positive integer value */
wdt->timeout = div_u64((load + 1) * 2 + (rate / 2), rate); wdt->timeout = div_u64((load + 1) * 2 + (rate / 2), rate);
spin_unlock(&wdt->lock); spin_unlock(&wdt->lock);
return 0;
} }
/* returns number of seconds left for reset to occur */ /* 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; u64 load, rate;
rate = clk_get_rate(wdt->clk); rate = clk_get_rate(wdt->clk);
@ -123,25 +124,55 @@ static u32 wdt_timeleft(void)
return div_u64(load, rate); return div_u64(load, rate);
} }
/* enables watchdog timers reset */ static int wdt_config(struct watchdog_device *wdd, bool ping)
static void wdt_enable(void)
{ {
struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
int ret;
if (!ping) {
ret = clk_enable(wdt->clk);
if (ret) {
dev_err(&wdt->adev->dev, "clock enable fail");
return ret;
}
}
spin_lock(&wdt->lock); spin_lock(&wdt->lock);
writel_relaxed(UNLOCK, wdt->base + WDTLOCK); writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
writel_relaxed(wdt->load_val, wdt->base + WDTLOAD); writel_relaxed(wdt->load_val, wdt->base + WDTLOAD);
writel_relaxed(INT_MASK, wdt->base + WDTINTCLR);
writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL); if (!ping) {
writel_relaxed(INT_MASK, wdt->base + WDTINTCLR);
writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base +
WDTCONTROL);
}
writel_relaxed(LOCK, wdt->base + WDTLOCK); writel_relaxed(LOCK, wdt->base + WDTLOCK);
/* Flush posted writes. */ /* Flush posted writes. */
readl_relaxed(wdt->base + WDTLOCK); readl_relaxed(wdt->base + WDTLOCK);
spin_unlock(&wdt->lock); spin_unlock(&wdt->lock);
return 0;
}
static int wdt_ping(struct watchdog_device *wdd)
{
return wdt_config(wdd, true);
}
/* enables watchdog timers reset */
static int wdt_enable(struct watchdog_device *wdd)
{
return wdt_config(wdd, false);
} }
/* disables watchdog timers reset */ /* disables watchdog timers reset */
static void wdt_disable(void) static int wdt_disable(struct watchdog_device *wdd)
{ {
struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
spin_lock(&wdt->lock); spin_lock(&wdt->lock);
writel_relaxed(UNLOCK, wdt->base + WDTLOCK); writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
@ -151,138 +182,30 @@ static void wdt_disable(void)
/* Flush posted writes. */ /* Flush posted writes. */
readl_relaxed(wdt->base + WDTLOCK); readl_relaxed(wdt->base + WDTLOCK);
spin_unlock(&wdt->lock); spin_unlock(&wdt->lock);
}
static ssize_t sp805_wdt_write(struct file *file, const char *data,
size_t len, loff_t *ppos)
{
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;
}
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)
{
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;
}
static int sp805_wdt_open(struct inode *inode, struct file *file)
{
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();
/* can not be closed, once enabled */
clear_bit(WDT_CAN_BE_CLOSED, &wdt->status);
return nonseekable_open(inode, file);
err:
clear_bit(WDT_BUSY, &wdt->status);
return ret;
}
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;
}
wdt_disable();
clk_disable(wdt->clk); clk_disable(wdt->clk);
clear_bit(WDT_BUSY, &wdt->status);
return 0; return 0;
} }
static const struct file_operations sp805_wdt_fops = { static const struct watchdog_info wdt_info = {
.owner = THIS_MODULE, .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
.llseek = no_llseek, .identity = MODULE_NAME,
.write = sp805_wdt_write,
.unlocked_ioctl = sp805_wdt_ioctl,
.open = sp805_wdt_open,
.release = sp805_wdt_release,
}; };
static struct miscdevice sp805_wdt_miscdev = { static const struct watchdog_ops wdt_ops = {
.minor = WATCHDOG_MINOR, .owner = THIS_MODULE,
.name = "watchdog", .start = wdt_enable,
.fops = &sp805_wdt_fops, .stop = wdt_disable,
.ping = wdt_ping,
.set_timeout = wdt_setload,
.get_timeleft = wdt_timeleft,
}; };
static int __devinit static int __devinit
sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id) sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
{ {
struct sp805_wdt *wdt;
int ret = 0; int ret = 0;
if (!devm_request_mem_region(&adev->dev, adev->res.start, if (!devm_request_mem_region(&adev->dev, adev->res.start,
@ -315,19 +238,26 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
} }
wdt->adev = adev; wdt->adev = adev;
spin_lock_init(&wdt->lock); wdt->wdd.info = &wdt_info;
wdt_setload(DEFAULT_TIMEOUT); wdt->wdd.ops = &wdt_ops;
ret = misc_register(&sp805_wdt_miscdev); spin_lock_init(&wdt->lock);
if (ret < 0) { watchdog_set_nowayout(&wdt->wdd, nowayout);
dev_warn(&adev->dev, "cannot register misc device\n"); watchdog_set_drvdata(&wdt->wdd, wdt);
goto err_misc_register; wdt_setload(&wdt->wdd, DEFAULT_TIMEOUT);
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"); dev_info(&adev->dev, "registration successful\n");
return 0; return 0;
err_misc_register: err_register:
clk_put(wdt->clk); clk_put(wdt->clk);
err: err:
dev_err(&adev->dev, "Probe Failed!!!\n"); dev_err(&adev->dev, "Probe Failed!!!\n");
@ -336,7 +266,11 @@ err:
static int __devexit sp805_wdt_remove(struct amba_device *adev) 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); clk_put(wdt->clk);
return 0; return 0;
@ -345,28 +279,22 @@ static int __devexit sp805_wdt_remove(struct amba_device *adev)
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int sp805_wdt_suspend(struct device *dev) static int sp805_wdt_suspend(struct device *dev)
{ {
if (test_bit(WDT_BUSY, &wdt->status)) { struct sp805_wdt *wdt = dev_get_drvdata(dev);
wdt_disable();
clk_disable(wdt->clk); if (watchdog_active(&wdt->wdd))
} return wdt_disable(&wdt->wdd);
return 0; return 0;
} }
static int sp805_wdt_resume(struct device *dev) 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)) { if (watchdog_active(&wdt->wdd))
ret = clk_enable(wdt->clk); return wdt_enable(&wdt->wdd);
if (ret) {
dev_err(dev, "clock enable fail");
return ret;
}
wdt_enable();
}
return ret; return 0;
} }
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
@ -395,11 +323,6 @@ static struct amba_driver sp805_wdt_driver = {
module_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.kumar@st.com>");
MODULE_DESCRIPTION("ARM SP805 Watchdog Driver"); MODULE_DESCRIPTION("ARM SP805 Watchdog Driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);