Commit bff23431 authored by Wim Van Sebroeck's avatar Wim Van Sebroeck

watchdog: iTCO_wdt.c: convert to watchdog core

This patch converts the iTCO_wdt watchdog driver to use the
generic watchdog framework.
Signed-off-by: default avatarWim Van Sebroeck <wim@iguana.be>
parent 18cb2ae5
...@@ -578,6 +578,7 @@ config INTEL_SCU_WATCHDOG ...@@ -578,6 +578,7 @@ config INTEL_SCU_WATCHDOG
config ITCO_WDT config ITCO_WDT
tristate "Intel TCO Timer/Watchdog" tristate "Intel TCO Timer/Watchdog"
depends on (X86 || IA64) && PCI depends on (X86 || IA64) && PCI
select WATCHDOG_CORE
select LPC_ICH select LPC_ICH
---help--- ---help---
Hardware driver for the intel TCO timer based watchdog devices. Hardware driver for the intel TCO timer based watchdog devices.
......
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
/* Module and version information */ /* Module and version information */
#define DRV_NAME "iTCO_wdt" #define DRV_NAME "iTCO_wdt"
#define DRV_VERSION "1.07" #define DRV_VERSION "1.10"
/* Includes */ /* Includes */
#include <linux/module.h> /* For module specific items */ #include <linux/module.h> /* For module specific items */
...@@ -88,8 +88,6 @@ ...@@ -88,8 +88,6 @@
#define TCOv2_TMR (TCOBASE + 0x12) /* TCOv2 Timer Initial Value */ #define TCOv2_TMR (TCOBASE + 0x12) /* TCOv2 Timer Initial Value */
/* internal variables */ /* internal variables */
static unsigned long is_active;
static char expect_release;
static struct { /* this is private data for the iTCO_wdt device */ static struct { /* this is private data for the iTCO_wdt device */
/* TCO version/generation */ /* TCO version/generation */
unsigned int iTCO_version; unsigned int iTCO_version;
...@@ -106,12 +104,12 @@ static struct { /* this is private data for the iTCO_wdt device */ ...@@ -106,12 +104,12 @@ static struct { /* this is private data for the iTCO_wdt device */
} iTCO_wdt_private; } iTCO_wdt_private;
/* module parameters */ /* module parameters */
#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat */ #define WATCHDOG_TIMEOUT 30 /* 30 sec default heartbeat */
static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */ static int heartbeat = WATCHDOG_TIMEOUT; /* in seconds */
module_param(heartbeat, int, 0); module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat, "Watchdog timeout in seconds. " MODULE_PARM_DESC(heartbeat, "Watchdog timeout in seconds. "
"5..76 (TCO v1) or 3..614 (TCO v2), default=" "5..76 (TCO v1) or 3..614 (TCO v2), default="
__MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
static bool nowayout = WATCHDOG_NOWAYOUT; static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0); module_param(nowayout, bool, 0);
...@@ -178,13 +176,13 @@ static int iTCO_wdt_unset_NO_REBOOT_bit(void) ...@@ -178,13 +176,13 @@ static int iTCO_wdt_unset_NO_REBOOT_bit(void)
return ret; /* returns: 0 = OK, -EIO = Error */ return ret; /* returns: 0 = OK, -EIO = Error */
} }
static int iTCO_wdt_start(void) static int iTCO_wdt_start(struct watchdog_device *wd_dev)
{ {
unsigned int val; unsigned int val;
spin_lock(&iTCO_wdt_private.io_lock); spin_lock(&iTCO_wdt_private.io_lock);
iTCO_vendor_pre_start(iTCO_wdt_private.smi_res, heartbeat); iTCO_vendor_pre_start(iTCO_wdt_private.smi_res, wd_dev->timeout);
/* disable chipset's NO_REBOOT bit */ /* disable chipset's NO_REBOOT bit */
if (iTCO_wdt_unset_NO_REBOOT_bit()) { if (iTCO_wdt_unset_NO_REBOOT_bit()) {
...@@ -212,7 +210,7 @@ static int iTCO_wdt_start(void) ...@@ -212,7 +210,7 @@ static int iTCO_wdt_start(void)
return 0; return 0;
} }
static int iTCO_wdt_stop(void) static int iTCO_wdt_stop(struct watchdog_device *wd_dev)
{ {
unsigned int val; unsigned int val;
...@@ -236,11 +234,11 @@ static int iTCO_wdt_stop(void) ...@@ -236,11 +234,11 @@ static int iTCO_wdt_stop(void)
return 0; return 0;
} }
static int iTCO_wdt_keepalive(void) static int iTCO_wdt_ping(struct watchdog_device *wd_dev)
{ {
spin_lock(&iTCO_wdt_private.io_lock); spin_lock(&iTCO_wdt_private.io_lock);
iTCO_vendor_pre_keepalive(iTCO_wdt_private.smi_res, heartbeat); iTCO_vendor_pre_keepalive(iTCO_wdt_private.smi_res, wd_dev->timeout);
/* Reload the timer by writing to the TCO Timer Counter register */ /* Reload the timer by writing to the TCO Timer Counter register */
if (iTCO_wdt_private.iTCO_version == 2) if (iTCO_wdt_private.iTCO_version == 2)
...@@ -257,7 +255,7 @@ static int iTCO_wdt_keepalive(void) ...@@ -257,7 +255,7 @@ static int iTCO_wdt_keepalive(void)
return 0; return 0;
} }
static int iTCO_wdt_set_heartbeat(int t) static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev, unsigned int t)
{ {
unsigned int val16; unsigned int val16;
unsigned char val8; unsigned char val8;
...@@ -304,14 +302,15 @@ static int iTCO_wdt_set_heartbeat(int t) ...@@ -304,14 +302,15 @@ static int iTCO_wdt_set_heartbeat(int t)
return -EINVAL; return -EINVAL;
} }
heartbeat = t; wd_dev->timeout = t;
return 0; return 0;
} }
static int iTCO_wdt_get_timeleft(int *time_left) static unsigned int iTCO_wdt_get_timeleft(struct watchdog_device *wd_dev)
{ {
unsigned int val16; unsigned int val16;
unsigned char val8; unsigned char val8;
unsigned int time_left = 0;
/* read the TCO Timer */ /* read the TCO Timer */
if (iTCO_wdt_private.iTCO_version == 2) { if (iTCO_wdt_private.iTCO_version == 2) {
...@@ -320,7 +319,7 @@ static int iTCO_wdt_get_timeleft(int *time_left) ...@@ -320,7 +319,7 @@ static int iTCO_wdt_get_timeleft(int *time_left)
val16 &= 0x3ff; val16 &= 0x3ff;
spin_unlock(&iTCO_wdt_private.io_lock); spin_unlock(&iTCO_wdt_private.io_lock);
*time_left = (val16 * 6) / 10; time_left = (val16 * 6) / 10;
} else if (iTCO_wdt_private.iTCO_version == 1) { } else if (iTCO_wdt_private.iTCO_version == 1) {
spin_lock(&iTCO_wdt_private.io_lock); spin_lock(&iTCO_wdt_private.io_lock);
val8 = inb(TCO_RLD); val8 = inb(TCO_RLD);
...@@ -329,156 +328,35 @@ static int iTCO_wdt_get_timeleft(int *time_left) ...@@ -329,156 +328,35 @@ static int iTCO_wdt_get_timeleft(int *time_left)
val8 += (inb(TCOv1_TMR) & 0x3f); val8 += (inb(TCOv1_TMR) & 0x3f);
spin_unlock(&iTCO_wdt_private.io_lock); spin_unlock(&iTCO_wdt_private.io_lock);
*time_left = (val8 * 6) / 10; time_left = (val8 * 6) / 10;
} else
return -EINVAL;
return 0;
}
/*
* /dev/watchdog handling
*/
static int iTCO_wdt_open(struct inode *inode, struct file *file)
{
/* /dev/watchdog can only be opened once */
if (test_and_set_bit(0, &is_active))
return -EBUSY;
/*
* Reload and activate timer
*/
iTCO_wdt_start();
return nonseekable_open(inode, file);
}
static int iTCO_wdt_release(struct inode *inode, struct file *file)
{
/*
* Shut off the timer.
*/
if (expect_release == 42) {
iTCO_wdt_stop();
} else {
pr_crit("Unexpected close, not stopping watchdog!\n");
iTCO_wdt_keepalive();
}
clear_bit(0, &is_active);
expect_release = 0;
return 0;
}
static ssize_t iTCO_wdt_write(struct file *file, const char __user *data,
size_t len, loff_t *ppos)
{
/* See if we got the magic character 'V' and reload the timer */
if (len) {
if (!nowayout) {
size_t i;
/* note: just in case someone wrote the magic
character five months ago... */
expect_release = 0;
/* scan to see whether or not we got the
magic character */
for (i = 0; i != len; i++) {
char c;
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V')
expect_release = 42;
}
}
/* someone wrote to us, we should reload the timer */
iTCO_wdt_keepalive();
}
return len;
}
static long iTCO_wdt_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
int new_options, retval = -EINVAL;
int new_heartbeat;
void __user *argp = (void __user *)arg;
int __user *p = argp;
static const struct watchdog_info ident = {
.options = WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING |
WDIOF_MAGICCLOSE,
.firmware_version = 0,
.identity = DRV_NAME,
};
switch (cmd) {
case WDIOC_GETSUPPORT:
return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
return put_user(0, p);
case WDIOC_SETOPTIONS:
{
if (get_user(new_options, p))
return -EFAULT;
if (new_options & WDIOS_DISABLECARD) {
iTCO_wdt_stop();
retval = 0;
}
if (new_options & WDIOS_ENABLECARD) {
iTCO_wdt_keepalive();
iTCO_wdt_start();
retval = 0;
}
return retval;
}
case WDIOC_KEEPALIVE:
iTCO_wdt_keepalive();
return 0;
case WDIOC_SETTIMEOUT:
{
if (get_user(new_heartbeat, p))
return -EFAULT;
if (iTCO_wdt_set_heartbeat(new_heartbeat))
return -EINVAL;
iTCO_wdt_keepalive();
/* Fall */
}
case WDIOC_GETTIMEOUT:
return put_user(heartbeat, p);
case WDIOC_GETTIMELEFT:
{
int time_left;
if (iTCO_wdt_get_timeleft(&time_left))
return -EINVAL;
return put_user(time_left, p);
}
default:
return -ENOTTY;
} }
return time_left;
} }
/* /*
* Kernel Interfaces * Kernel Interfaces
*/ */
static const struct file_operations iTCO_wdt_fops = { static const struct watchdog_info ident = {
.options = WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING |
WDIOF_MAGICCLOSE,
.firmware_version = 0,
.identity = DRV_NAME,
};
static const struct watchdog_ops iTCO_wdt_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.llseek = no_llseek, .start = iTCO_wdt_start,
.write = iTCO_wdt_write, .stop = iTCO_wdt_stop,
.unlocked_ioctl = iTCO_wdt_ioctl, .ping = iTCO_wdt_ping,
.open = iTCO_wdt_open, .set_timeout = iTCO_wdt_set_timeout,
.release = iTCO_wdt_release, .get_timeleft = iTCO_wdt_get_timeleft,
}; };
static struct miscdevice iTCO_wdt_miscdev = { static struct watchdog_device iTCO_wdt_watchdog_dev = {
.minor = WATCHDOG_MINOR, .info = &ident,
.name = "watchdog", .ops = &iTCO_wdt_ops,
.fops = &iTCO_wdt_fops,
}; };
/* /*
...@@ -489,10 +367,10 @@ static void __devexit iTCO_wdt_cleanup(void) ...@@ -489,10 +367,10 @@ static void __devexit iTCO_wdt_cleanup(void)
{ {
/* Stop the timer before we leave */ /* Stop the timer before we leave */
if (!nowayout) if (!nowayout)
iTCO_wdt_stop(); iTCO_wdt_stop(&iTCO_wdt_watchdog_dev);
/* Deregister */ /* Deregister */
misc_deregister(&iTCO_wdt_miscdev); watchdog_unregister_device(&iTCO_wdt_watchdog_dev);
/* release resources */ /* release resources */
release_region(iTCO_wdt_private.tco_res->start, release_region(iTCO_wdt_private.tco_res->start,
...@@ -605,20 +483,25 @@ static int __devinit iTCO_wdt_probe(struct platform_device *dev) ...@@ -605,20 +483,25 @@ static int __devinit iTCO_wdt_probe(struct platform_device *dev)
outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */ outw(0x0002, TCO2_STS); /* Clear SECOND_TO_STS bit */
outw(0x0004, TCO2_STS); /* Clear BOOT_STS bit */ outw(0x0004, TCO2_STS); /* Clear BOOT_STS bit */
iTCO_wdt_watchdog_dev.bootstatus = 0;
iTCO_wdt_watchdog_dev.timeout = WATCHDOG_TIMEOUT;
watchdog_set_nowayout(&iTCO_wdt_watchdog_dev, nowayout);
iTCO_wdt_watchdog_dev.parent = dev->dev.parent;
/* Make sure the watchdog is not running */ /* Make sure the watchdog is not running */
iTCO_wdt_stop(); iTCO_wdt_stop(&iTCO_wdt_watchdog_dev);
/* Check that the heartbeat value is within it's range; /* Check that the heartbeat value is within it's range;
if not reset to the default */ if not reset to the default */
if (iTCO_wdt_set_heartbeat(heartbeat)) { if (iTCO_wdt_set_timeout(&iTCO_wdt_watchdog_dev, heartbeat)) {
iTCO_wdt_set_heartbeat(WATCHDOG_HEARTBEAT); iTCO_wdt_set_timeout(&iTCO_wdt_watchdog_dev, WATCHDOG_TIMEOUT);
pr_info("timeout value out of range, using %d\n", heartbeat); pr_info("timeout value out of range, using %d\n",
WATCHDOG_TIMEOUT);
} }
ret = misc_register(&iTCO_wdt_miscdev); ret = watchdog_register_device(&iTCO_wdt_watchdog_dev);
if (ret != 0) { if (ret != 0) {
pr_err("cannot register miscdev on minor=%d (err=%d)\n", pr_err("cannot register watchdog device (err=%d)\n", ret);
WATCHDOG_MINOR, ret);
goto unreg_tco; goto unreg_tco;
} }
...@@ -659,7 +542,7 @@ static int __devexit iTCO_wdt_remove(struct platform_device *dev) ...@@ -659,7 +542,7 @@ static int __devexit iTCO_wdt_remove(struct platform_device *dev)
static void iTCO_wdt_shutdown(struct platform_device *dev) static void iTCO_wdt_shutdown(struct platform_device *dev)
{ {
iTCO_wdt_stop(); iTCO_wdt_stop(NULL);
} }
static struct platform_driver iTCO_wdt_driver = { static struct platform_driver iTCO_wdt_driver = {
......
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