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

WATCHDOG] v2.6.4 wdt977-v0.03-patch

Version 0.03 of wdt977.c - Changes that were made are:
* Extract the stop code in a seperate function (wdt977_stop)
* Extract the start code in a seperate function (wdt977_start)
* Rename kick_wdog to wdt977_keepalive for consistency
* Extract the watchdog's status code to a seperate function (wdt977_get_status)
* Change the way we deal with the watchdog timeout:
   Up till now we used timeoutM (in minutes) as the correct value and then
   calculated timeout as being timeoutM*60 or *timeoutM*120 (depending on
   wether or not we have the netwinder hardware bug).

   From now on timeout is the correct value and we calculate timeoutM out
   of it. Because of this we start with checking wether or not we have a
   correct timeout value (if not we reset it to the default value) and we
   automatically calculate timeoutM. Each time we change timeout with a
   correct timeout value, we recalculate timeoutM.
* Extended ioctl code with WDIOC_SETOPTIONS and updated the watchdog_info structure
* Added notifier support

Code has been tested by Woody
parent 591095cb
/* /*
* Wdt977 0.02: A Watchdog Device for Netwinder W83977AF chip * Wdt977 0.03: A Watchdog Device for Netwinder W83977AF chip
* *
* (c) Copyright 1998 Rebel.com (Woody Suwalski <woody@netwinder.org>) * (c) Copyright 1998 Rebel.com (Woody Suwalski <woody@netwinder.org>)
* *
...@@ -29,24 +29,27 @@ ...@@ -29,24 +29,27 @@
#include <linux/miscdevice.h> #include <linux/miscdevice.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/watchdog.h> #include <linux/watchdog.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/mach-types.h> #include <asm/mach-types.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#define PFX "Wdt977: "
#define WATCHDOG_MINOR 130 #define WATCHDOG_MINOR 130
#define DEFAULT_TIMEOUT 1 /* default timeout = 1 minute */ #define DEFAULT_TIMEOUT 60 /* default timeout in seconds */
static int timeout = DEFAULT_TIMEOUT*60; /* TO in seconds from user */ static int timeout = DEFAULT_TIMEOUT;
static int timeoutM = DEFAULT_TIMEOUT; /* timeout in minutes */ static int timeoutM; /* timeout in minutes */
static unsigned long timer_alive; static unsigned long timer_alive;
static int testmode; static int testmode;
static char expect_close; static char expect_close;
module_param(timeout, int, 0); module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout,"Watchdog timeout in seconds (60..15300), default=60"); MODULE_PARM_DESC(timeout,"Watchdog timeout in seconds (60..15300), default=" __MODULE_STRING(DEFAULT_TIMEOUT) ")");
module_param(testmode, int, 0); module_param(testmode, int, 0);
MODULE_PARM_DESC(testmode,"Watchdog testmode (1 = no reboot), default=0"); MODULE_PARM_DESC(testmode,"Watchdog testmode (1 = no reboot), default=0");
...@@ -59,71 +62,12 @@ static int nowayout = 0; ...@@ -59,71 +62,12 @@ static int nowayout = 0;
module_param(nowayout, int, 0); module_param(nowayout, int, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
/* This is kicking the watchdog by simply re-writing the timeout to reg. 0xF2 */
static int kick_wdog(void)
{
/*
* Refresh the timer.
*/
/* unlock the SuperIO chip */
outb(0x87,0x370);
outb(0x87,0x370);
/* select device Aux2 (device=8) and kicks watchdog reg F2 */
/* F2 has the timeout in minutes */
outb(0x07,0x370);
outb(0x08,0x371);
outb(0xF2,0x370);
outb(timeoutM,0x371);
/* lock the SuperIO chip */
outb(0xAA,0x370);
return 0;
}
/* /*
* Allow only one person to hold it open * Start the watchdog
*/ */
static int wdt977_open(struct inode *inode, struct file *file) static int wdt977_start(void)
{ {
if( test_and_set_bit(0,&timer_alive) )
return -EBUSY;
/* convert seconds to minutes, rounding up */
timeoutM = timeout + 59;
timeoutM /= 60;
if (nowayout)
{
__module_get(THIS_MODULE);
/* do not permit disabling the watchdog by writing 0 to reg. 0xF2 */
if (!timeoutM) timeoutM = DEFAULT_TIMEOUT;
}
if (machine_is_netwinder())
{
/* we have a hw bug somewhere, so each 977 minute is actually only 30sec
* this limits the max timeout to half of device max of 255 minutes...
*/
timeoutM += timeoutM;
}
/* max timeout value = 255 minutes (0xFF). Write 0 to disable WatchDog. */
if (timeoutM > 255) timeoutM = 255;
/* convert seconds to minutes */
printk(KERN_INFO "Wdt977 Watchdog activated: timeout = %d sec, nowayout = %i, testmode = %i.\n",
machine_is_netwinder() ? (timeoutM>>1)*60 : timeoutM*60,
nowayout, testmode);
/* unlock the SuperIO chip */ /* unlock the SuperIO chip */
outb(0x87,0x370); outb(0x87,0x370);
outb(0x87,0x370); outb(0x87,0x370);
...@@ -156,17 +100,17 @@ static int wdt977_open(struct inode *inode, struct file *file) ...@@ -156,17 +100,17 @@ static int wdt977_open(struct inode *inode, struct file *file)
/* lock the SuperIO chip */ /* lock the SuperIO chip */
outb(0xAA,0x370); outb(0xAA,0x370);
printk(KERN_INFO PFX "activated.\n");
return 0; return 0;
} }
static int wdt977_release(struct inode *inode, struct file *file) /*
{ * Stop the watchdog
/*
* Shut off the timer.
* Lock it in if it's a module and we set nowayout
*/ */
if (expect_close == 42)
{ static int wdt977_stop(void)
{
/* unlock the SuperIO chip */ /* unlock the SuperIO chip */
outb(0x87,0x370); outb(0x87,0x370);
outb(0x87,0x370); outb(0x87,0x370);
...@@ -196,11 +140,122 @@ static int wdt977_release(struct inode *inode, struct file *file) ...@@ -196,11 +140,122 @@ static int wdt977_release(struct inode *inode, struct file *file)
/* lock the SuperIO chip */ /* lock the SuperIO chip */
outb(0xAA,0x370); outb(0xAA,0x370);
clear_bit(0,&timer_alive); printk(KERN_INFO PFX "shutdown.\n");
return 0;
}
/*
* Send a keepalive ping to the watchdog
* This is done by simply re-writing the timeout to reg. 0xF2
*/
static int wdt977_keepalive(void)
{
/* unlock the SuperIO chip */
outb(0x87,0x370);
outb(0x87,0x370);
/* select device Aux2 (device=8) and kicks watchdog reg F2 */
/* F2 has the timeout in minutes */
outb(0x07,0x370);
outb(0x08,0x371);
outb(0xF2,0x370);
outb(timeoutM,0x371);
/* lock the SuperIO chip */
outb(0xAA,0x370);
return 0;
}
/*
* Set the watchdog timeout value
*/
static int wdt977_set_timeout(int t)
{
int tmrval;
/* convert seconds to minutes, rounding up */
tmrval = (t + 59) / 60;
if (machine_is_netwinder()) {
/* we have a hw bug somewhere, so each 977 minute is actually only 30sec
* this limits the max timeout to half of device max of 255 minutes...
*/
tmrval += tmrval;
}
if ((tmrval < 1) || (tmrval > 255))
return -EINVAL;
/* timeout is the timeout in seconds, timeoutM is the timeout in minutes) */
timeout = t;
timeoutM = tmrval;
return 0;
}
/*
* Get the watchdog status
*/
static int wdt977_get_status(int *status)
{
int new_status;
*status=0;
/* unlock the SuperIO chip */
outb(0x87,0x370);
outb(0x87,0x370);
/* select device Aux2 (device=8) and read watchdog reg F4 */
outb(0x07,0x370);
outb(0x08,0x371);
outb(0xF4,0x370);
new_status = inb(0x371);
/* lock the SuperIO chip */
outb(0xAA,0x370);
if (new_status & 1)
*status |= WDIOF_CARDRESET;
printk(KERN_INFO "Wdt977 Watchdog: shutdown\n"); return 0;
}
/*
* /dev/watchdog handling
*/
static int wdt977_open(struct inode *inode, struct file *file)
{
/* If the watchdog is alive we don't need to start it again */
if( test_and_set_bit(0,&timer_alive) )
return -EBUSY;
if (nowayout)
__module_get(THIS_MODULE);
wdt977_start();
return 0;
}
static int wdt977_release(struct inode *inode, struct file *file)
{
/*
* Shut off the timer.
* Lock it in if it's a module and we set nowayout
*/
if (expect_close == 42)
{
wdt977_stop();
clear_bit(0,&timer_alive);
} else { } else {
printk(KERN_CRIT "WDT device closed unexpectedly. WDT will not stop!\n"); printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
wdt977_keepalive();
} }
expect_close = 0; expect_close = 0;
return 0; return 0;
...@@ -240,7 +295,7 @@ static ssize_t wdt977_write(struct file *file, const char *buf, size_t count, lo ...@@ -240,7 +295,7 @@ static ssize_t wdt977_write(struct file *file, const char *buf, size_t count, lo
} }
} }
kick_wdog(); wdt977_keepalive();
} }
return count; return count;
} }
...@@ -257,14 +312,19 @@ static ssize_t wdt977_write(struct file *file, const char *buf, size_t count, lo ...@@ -257,14 +312,19 @@ static ssize_t wdt977_write(struct file *file, const char *buf, size_t count, lo
*/ */
static struct watchdog_info ident = { static struct watchdog_info ident = {
.options = WDIOF_SETTIMEOUT, .options = WDIOF_SETTIMEOUT |
WDIOF_MAGICCLOSE |
WDIOF_KEEPALIVEPING,
.firmware_version = 1,
.identity = "Winbond 83977", .identity = "Winbond 83977",
}; };
static int wdt977_ioctl(struct inode *inode, struct file *file, static int wdt977_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
int temp; int status;
int new_options, retval = -EINVAL;
int new_timeout;
switch(cmd) switch(cmd)
{ {
...@@ -275,59 +335,56 @@ static int wdt977_ioctl(struct inode *inode, struct file *file, ...@@ -275,59 +335,56 @@ static int wdt977_ioctl(struct inode *inode, struct file *file,
return copy_to_user((struct watchdog_info *)arg, &ident, return copy_to_user((struct watchdog_info *)arg, &ident,
sizeof(ident)) ? -EFAULT : 0; sizeof(ident)) ? -EFAULT : 0;
case WDIOC_GETBOOTSTATUS:
return put_user(0, (int *) arg);
case WDIOC_GETSTATUS: case WDIOC_GETSTATUS:
/* unlock the SuperIO chip */ wdt977_get_status(&status);
outb(0x87,0x370); return put_user(status, (int *) arg);
outb(0x87,0x370);
/* select device Aux2 (device=8) and read watchdog reg F4 */
outb(0x07,0x370);
outb(0x08,0x371);
outb(0xF4,0x370);
temp = inb(0x371);
/* lock the SuperIO chip */
outb(0xAA,0x370);
/* return info if "expired" in test mode */ case WDIOC_GETBOOTSTATUS:
return put_user(temp & 1, (int *) arg); return put_user(0, (int *) arg);
case WDIOC_KEEPALIVE: case WDIOC_KEEPALIVE:
kick_wdog(); wdt977_keepalive();
return 0; return 0;
case WDIOC_SETTIMEOUT: case WDIOC_SETOPTIONS:
if (copy_from_user(&temp, (int *) arg, sizeof(int))) if (get_user (new_options, (int *) arg))
return -EFAULT; return -EFAULT;
/* convert seconds to minutes, rounding up */ if (new_options & WDIOS_DISABLECARD) {
temp += 59; wdt977_stop();
temp /= 60; retval = 0;
}
/* we have a hw bug somewhere, so each 977 minute is actually only 30sec if (new_options & WDIOS_ENABLECARD) {
* this limits the max timeout to half of device max of 255 minutes... wdt977_start();
*/ retval = 0;
if (machine_is_netwinder())
{
temp += temp;
} }
/* Sanity check */ return retval;
if (temp < 0 || temp > 255)
return -EINVAL; case WDIOC_SETTIMEOUT:
if (get_user(new_timeout, (int *) arg))
return -EFAULT;
if (!temp && nowayout) if (wdt977_set_timeout(new_timeout))
return -EINVAL; return -EINVAL;
timeoutM = temp; wdt977_keepalive();
kick_wdog(); /* Fall */
return 0;
case WDIOC_GETTIMEOUT:
return put_user(timeout, (int *)arg);
} }
} }
static int wdt977_notify_sys(struct notifier_block *this, unsigned long code,
void *unused)
{
if(code==SYS_DOWN || code==SYS_HALT)
wdt977_stop();
return NOTIFY_DONE;
}
static struct file_operations wdt977_fops= static struct file_operations wdt977_fops=
{ {
...@@ -345,21 +402,48 @@ static struct miscdevice wdt977_miscdev= ...@@ -345,21 +402,48 @@ static struct miscdevice wdt977_miscdev=
.fops = &wdt977_fops, .fops = &wdt977_fops,
}; };
static struct notifier_block wdt977_notifier = {
.notifier_call = wdt977_notify_sys,
};
static int __init nwwatchdog_init(void) static int __init nwwatchdog_init(void)
{ {
int retval; int retval;
if (!machine_is_netwinder()) if (!machine_is_netwinder())
return -ENODEV; return -ENODEV;
/* Check that the timeout value is within it's range ; if not reset to the default */
if (wdt977_set_timeout(timeout)) {
wdt977_set_timeout(DEFAULT_TIMEOUT);
printk(KERN_INFO PFX "timeout value must be 60<timeout<15300, using %d\n",
DEFAULT_TIMEOUT);
}
retval = register_reboot_notifier(&wdt977_notifier);
if (retval) {
printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n",
retval);
return retval;
}
retval = misc_register(&wdt977_miscdev); retval = misc_register(&wdt977_miscdev);
if (!retval) if (retval) {
printk(KERN_INFO "Wdt977 Watchdog sleeping.\n"); printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
WATCHDOG_MINOR, retval);
unregister_reboot_notifier(&wdt977_notifier);
return retval; return retval;
}
printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d, testmode = %i)\n",
timeout, nowayout, testmode);
return 0;
} }
static void __exit nwwatchdog_exit(void) static void __exit nwwatchdog_exit(void)
{ {
misc_deregister(&wdt977_miscdev); misc_deregister(&wdt977_miscdev);
unregister_reboot_notifier(&wdt977_notifier);
} }
module_init(nwwatchdog_init); module_init(nwwatchdog_init);
......
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