Skip to content

Commit

Permalink
watchdog: sp805_wdt: convert to watchdog core
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Viresh Kumar authored and Wim Van Sebroeck committed May 30, 2012
1 parent 2d8c7ff commit 4a51653
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 159 deletions.
1 change: 1 addition & 0 deletions drivers/watchdog/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ config WM8350_WATCHDOG
config ARM_SP805_WATCHDOG
tristate "ARM SP805 Watchdog"
depends on ARM_AMBA
select WATCHDOG_CORE
help
ARM Primecell SP805 Watchdog timer. This will reboot your system when
the timeout is reached.
Expand Down
241 changes: 82 additions & 159 deletions drivers/watchdog/sp805_wdt.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,17 @@
#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 */
Expand All @@ -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
Expand All @@ -65,24 +63,24 @@
* @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);
Expand All @@ -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);
Expand All @@ -123,166 +124,88 @@ 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)
{
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);

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);

/* Flush posted writes. */
readl_relaxed(wdt->base + WDTLOCK);
spin_unlock(&wdt->lock);
}

/* disables watchdog timers reset */
static void wdt_disable(void)
{
spin_lock(&wdt->lock);
if (!ping) {
writel_relaxed(INT_MASK, wdt->base + WDTINTCLR);
writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base +
WDTCONTROL);
}

writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
writel_relaxed(0, 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;
struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);

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);
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);

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,
Expand Down Expand Up @@ -315,19 +238,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");
Expand All @@ -336,7 +266,11 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)

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;
Expand All @@ -345,28 +279,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 */

Expand Down Expand Up @@ -395,11 +323,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_DESCRIPTION("ARM SP805 Watchdog Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);

0 comments on commit 4a51653

Please sign in to comment.