Commit 0af98d37 authored by Phil Sutter's avatar Phil Sutter Committed by Wim Van Sebroeck

[WATCHDOG] rc32434_wdt: fix watchdog driver

The existing driver code wasn't working. Neither the timeout was set
correctly, nor system reset was being triggered, as the driver seemed
to keep the WDT alive himself. There was also some unnecessary code.
Signed-off-by: default avatarPhil Sutter <n0-1@freewrt.org>
Signed-off-by: default avatarWim Van Sebroeck <wim@iguana.be>
Cc: stable <stable@kernel.org>
parent 20f4d6c3
...@@ -34,104 +34,89 @@ ...@@ -34,104 +34,89 @@
#include <asm/time.h> #include <asm/time.h>
#include <asm/mach-rc32434/integ.h> #include <asm/mach-rc32434/integ.h>
#define MAX_TIMEOUT 20 #define VERSION "0.3"
#define RC32434_WDT_INTERVAL (15 * HZ)
#define VERSION "0.2"
static struct { static struct {
struct completion stop;
int running;
struct timer_list timer;
int queue;
int default_ticks;
unsigned long inuse; unsigned long inuse;
} rc32434_wdt_device; } rc32434_wdt_device;
static struct integ __iomem *wdt_reg; static struct integ __iomem *wdt_reg;
static int ticks = 100 * HZ;
static int expect_close; static int expect_close;
static int timeout;
/* Board internal clock speed in Hz,
* the watchdog timer ticks at. */
extern unsigned int idt_cpu_freq;
/* translate wtcompare value to seconds and vice versa */
#define WTCOMP2SEC(x) (x / idt_cpu_freq)
#define SEC2WTCOMP(x) (x * idt_cpu_freq)
/* Use a default timeout of 20s. This should be
* safe for CPU clock speeds up to 400MHz, as
* ((2 ^ 32) - 1) / (400MHz / 2) = 21s. */
#define WATCHDOG_TIMEOUT 20
static int timeout = WATCHDOG_TIMEOUT;
static int nowayout = WATCHDOG_NOWAYOUT; static int nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, int, 0); module_param(nowayout, int, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
/* apply or and nand masks to data read from addr and write back */
#define SET_BITS(addr, or, nand) \
writel((readl(&addr) | or) & ~nand, &addr)
static void rc32434_wdt_start(void) static void rc32434_wdt_start(void)
{ {
u32 val; u32 or, nand;
if (!rc32434_wdt_device.inuse) {
writel(0, &wdt_reg->wtcount);
val = RC32434_ERR_WRE; /* zero the counter before enabling */
writel(readl(&wdt_reg->errcs) | val, &wdt_reg->errcs); writel(0, &wdt_reg->wtcount);
val = RC32434_WTC_EN; /* don't generate a non-maskable interrupt,
writel(readl(&wdt_reg->wtc) | val, &wdt_reg->wtc); * do a warm reset instead */
} nand = 1 << RC32434_ERR_WNE;
rc32434_wdt_device.running++; or = 1 << RC32434_ERR_WRE;
}
static void rc32434_wdt_stop(void) /* reset the ERRCS timeout bit in case it's set */
{ nand |= 1 << RC32434_ERR_WTO;
u32 val;
if (rc32434_wdt_device.running) { SET_BITS(wdt_reg->errcs, or, nand);
val = ~RC32434_WTC_EN; /* reset WTC timeout bit and enable WDT */
writel(readl(&wdt_reg->wtc) & val, &wdt_reg->wtc); nand = 1 << RC32434_WTC_TO;
or = 1 << RC32434_WTC_EN;
val = ~RC32434_ERR_WRE; SET_BITS(wdt_reg->wtc, or, nand);
writel(readl(&wdt_reg->errcs) & val, &wdt_reg->errcs); }
rc32434_wdt_device.running = 0; static void rc32434_wdt_stop(void)
} {
/* Disable WDT */
SET_BITS(wdt_reg->wtc, 0, 1 << RC32434_WTC_EN);
} }
static void rc32434_wdt_set(int new_timeout) static int rc32434_wdt_set(int new_timeout)
{ {
u32 cmp = new_timeout * HZ; int max_to = WTCOMP2SEC((u32)-1);
u32 state, val;
if (new_timeout < 0 || new_timeout > max_to) {
printk(KERN_ERR KBUILD_MODNAME
": timeout value must be between 0 and %d",
max_to);
return -EINVAL;
}
timeout = new_timeout; timeout = new_timeout;
/* writel(SEC2WTCOMP(timeout), &wdt_reg->wtcompare);
* store and disable WTC
*/
state = (u32)(readl(&wdt_reg->wtc) & RC32434_WTC_EN);
val = ~RC32434_WTC_EN;
writel(readl(&wdt_reg->wtc) & val, &wdt_reg->wtc);
writel(0, &wdt_reg->wtcount);
writel(cmp, &wdt_reg->wtcompare);
/*
* restore WTC
*/
writel(readl(&wdt_reg->wtc) | state, &wdt_reg);
}
static void rc32434_wdt_reset(void) return 0;
{
ticks = rc32434_wdt_device.default_ticks;
} }
static void rc32434_wdt_update(unsigned long unused) static void rc32434_wdt_ping(void)
{ {
if (rc32434_wdt_device.running)
ticks--;
writel(0, &wdt_reg->wtcount); writel(0, &wdt_reg->wtcount);
if (rc32434_wdt_device.queue && ticks)
mod_timer(&rc32434_wdt_device.timer,
jiffies + RC32434_WDT_INTERVAL);
else
complete(&rc32434_wdt_device.stop);
} }
static int rc32434_wdt_open(struct inode *inode, struct file *file) static int rc32434_wdt_open(struct inode *inode, struct file *file)
...@@ -142,19 +127,23 @@ static int rc32434_wdt_open(struct inode *inode, struct file *file) ...@@ -142,19 +127,23 @@ static int rc32434_wdt_open(struct inode *inode, struct file *file)
if (nowayout) if (nowayout)
__module_get(THIS_MODULE); __module_get(THIS_MODULE);
rc32434_wdt_start();
rc32434_wdt_ping();
return nonseekable_open(inode, file); return nonseekable_open(inode, file);
} }
static int rc32434_wdt_release(struct inode *inode, struct file *file) static int rc32434_wdt_release(struct inode *inode, struct file *file)
{ {
if (expect_close && nowayout == 0) { if (expect_close == 42) {
rc32434_wdt_stop(); rc32434_wdt_stop();
printk(KERN_INFO KBUILD_MODNAME ": disabling watchdog timer\n"); printk(KERN_INFO KBUILD_MODNAME ": disabling watchdog timer\n");
module_put(THIS_MODULE); module_put(THIS_MODULE);
} else } else {
printk(KERN_CRIT KBUILD_MODNAME printk(KERN_CRIT KBUILD_MODNAME
": device closed unexpectedly. WDT will not stop !\n"); ": device closed unexpectedly. WDT will not stop !\n");
rc32434_wdt_ping();
}
clear_bit(0, &rc32434_wdt_device.inuse); clear_bit(0, &rc32434_wdt_device.inuse);
return 0; return 0;
} }
...@@ -174,10 +163,10 @@ static ssize_t rc32434_wdt_write(struct file *file, const char *data, ...@@ -174,10 +163,10 @@ static ssize_t rc32434_wdt_write(struct file *file, const char *data,
if (get_user(c, data + i)) if (get_user(c, data + i))
return -EFAULT; return -EFAULT;
if (c == 'V') if (c == 'V')
expect_close = 1; expect_close = 42;
} }
} }
rc32434_wdt_update(0); rc32434_wdt_ping();
return len; return len;
} }
return 0; return 0;
...@@ -197,11 +186,11 @@ static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd, ...@@ -197,11 +186,11 @@ static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd,
}; };
switch (cmd) { switch (cmd) {
case WDIOC_KEEPALIVE: case WDIOC_KEEPALIVE:
rc32434_wdt_reset(); rc32434_wdt_ping();
break; break;
case WDIOC_GETSTATUS: case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS: case WDIOC_GETBOOTSTATUS:
value = readl(&wdt_reg->wtcount); value = 0;
if (copy_to_user(argp, &value, sizeof(int))) if (copy_to_user(argp, &value, sizeof(int)))
return -EFAULT; return -EFAULT;
break; break;
...@@ -218,6 +207,7 @@ static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd, ...@@ -218,6 +207,7 @@ static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd,
break; break;
case WDIOS_DISABLECARD: case WDIOS_DISABLECARD:
rc32434_wdt_stop(); rc32434_wdt_stop();
break;
default: default:
return -EINVAL; return -EINVAL;
} }
...@@ -225,11 +215,9 @@ static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd, ...@@ -225,11 +215,9 @@ static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd,
case WDIOC_SETTIMEOUT: case WDIOC_SETTIMEOUT:
if (copy_from_user(&new_timeout, argp, sizeof(int))) if (copy_from_user(&new_timeout, argp, sizeof(int)))
return -EFAULT; return -EFAULT;
if (new_timeout < 1) if (rc32434_wdt_set(new_timeout))
return -EINVAL; return -EINVAL;
if (new_timeout > MAX_TIMEOUT) /* Fall through */
return -EINVAL;
rc32434_wdt_set(new_timeout);
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return copy_to_user(argp, &timeout, sizeof(int)); return copy_to_user(argp, &timeout, sizeof(int));
default: default:
...@@ -262,7 +250,7 @@ static int rc32434_wdt_probe(struct platform_device *pdev) ...@@ -262,7 +250,7 @@ static int rc32434_wdt_probe(struct platform_device *pdev)
int ret; int ret;
struct resource *r; struct resource *r;
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rb500_wdt_res"); r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rb532_wdt_res");
if (!r) { if (!r) {
printk(KERN_ERR KBUILD_MODNAME printk(KERN_ERR KBUILD_MODNAME
"failed to retrieve resources\n"); "failed to retrieve resources\n");
...@@ -277,24 +265,12 @@ static int rc32434_wdt_probe(struct platform_device *pdev) ...@@ -277,24 +265,12 @@ static int rc32434_wdt_probe(struct platform_device *pdev)
} }
ret = misc_register(&rc32434_wdt_miscdev); ret = misc_register(&rc32434_wdt_miscdev);
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR KBUILD_MODNAME printk(KERN_ERR KBUILD_MODNAME
"failed to register watchdog device\n"); "failed to register watchdog device\n");
goto unmap; goto unmap;
} }
init_completion(&rc32434_wdt_device.stop);
rc32434_wdt_device.queue = 0;
clear_bit(0, &rc32434_wdt_device.inuse);
setup_timer(&rc32434_wdt_device.timer, rc32434_wdt_update, 0L);
rc32434_wdt_device.default_ticks = ticks;
rc32434_wdt_start();
printk(banner, timeout); printk(banner, timeout);
return 0; return 0;
...@@ -306,14 +282,8 @@ static int rc32434_wdt_probe(struct platform_device *pdev) ...@@ -306,14 +282,8 @@ static int rc32434_wdt_probe(struct platform_device *pdev)
static int rc32434_wdt_remove(struct platform_device *pdev) static int rc32434_wdt_remove(struct platform_device *pdev)
{ {
if (rc32434_wdt_device.queue) {
rc32434_wdt_device.queue = 0;
wait_for_completion(&rc32434_wdt_device.stop);
}
misc_deregister(&rc32434_wdt_miscdev); misc_deregister(&rc32434_wdt_miscdev);
iounmap(wdt_reg); iounmap(wdt_reg);
return 0; return 0;
} }
......
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