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,21 +62,102 @@ static int nowayout = 0; ...@@ -59,21 +62,102 @@ 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)");
/*
* Start the watchdog
*/
/* This is kicking the watchdog by simply re-writing the timeout to reg. 0xF2 */ static int wdt977_start(void)
static int kick_wdog(void)
{ {
/* /* unlock the SuperIO chip */
* Refresh the timer. outb(0x87,0x370);
outb(0x87,0x370);
/* select device Aux2 (device=8) and set watchdog regs F2, F3 and F4
* F2 has the timeout in minutes
* F3 could be set to the POWER LED blink (with GP17 set to PowerLed)
* at timeout, and to reset timer on kbd/mouse activity (not impl.)
* F4 is used to just clear the TIMEOUT'ed state (bit 0)
*/ */
outb(0x07,0x370);
outb(0x08,0x371);
outb(0xF2,0x370);
outb(timeoutM,0x371);
outb(0xF3,0x370);
outb(0x00,0x371); /* another setting is 0E for kbd/mouse/LED */
outb(0xF4,0x370);
outb(0x00,0x371);
/* at last select device Aux1 (dev=7) and set GP16 as a watchdog output */
/* in test mode watch the bit 1 on F4 to indicate "triggered" */
if (!testmode)
{
outb(0x07,0x370);
outb(0x07,0x371);
outb(0xE6,0x370);
outb(0x08,0x371);
}
/* lock the SuperIO chip */
outb(0xAA,0x370);
printk(KERN_INFO PFX "activated.\n");
return 0;
}
/*
* Stop the watchdog
*/
static int wdt977_stop(void)
{
/* unlock the SuperIO chip */
outb(0x87,0x370);
outb(0x87,0x370);
/* select device Aux2 (device=8) and set watchdog regs F2,F3 and F4
* F3 is reset to its default state
* F4 can clear the TIMEOUT'ed state (bit 0) - back to default
* We can not use GP17 as a PowerLed, as we use its usage as a RedLed
*/
outb(0x07,0x370);
outb(0x08,0x371);
outb(0xF2,0x370);
outb(0xFF,0x371);
outb(0xF3,0x370);
outb(0x00,0x371);
outb(0xF4,0x370);
outb(0x00,0x371);
outb(0xF2,0x370);
outb(0x00,0x371);
/* at last select device Aux1 (dev=7) and set GP16 as a watchdog output */
outb(0x07,0x370);
outb(0x07,0x371);
outb(0xE6,0x370);
outb(0x08,0x371);
/* lock the SuperIO chip */
outb(0xAA,0x370);
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 */ /* unlock the SuperIO chip */
outb(0x87,0x370); outb(0x87,0x370);
outb(0x87,0x370); outb(0x87,0x370);
/* select device Aux2 (device=8) and kicks watchdog reg F2 */ /* select device Aux2 (device=8) and kicks watchdog reg F2 */
/* F2 has the timeout in minutes */ /* F2 has the timeout in minutes */
outb(0x07,0x370); outb(0x07,0x370);
outb(0x08,0x371); outb(0x08,0x371);
outb(0xF2,0x370); outb(0xF2,0x370);
...@@ -85,77 +169,77 @@ static int kick_wdog(void) ...@@ -85,77 +169,77 @@ static int kick_wdog(void)
return 0; return 0;
} }
/* /*
* Allow only one person to hold it open * Set the watchdog timeout value
*/ */
static int wdt977_open(struct inode *inode, struct file *file) static int wdt977_set_timeout(int t)
{ {
int tmrval;
if( test_and_set_bit(0,&timer_alive) )
return -EBUSY;
/* convert seconds to minutes, rounding up */ /* convert seconds to minutes, rounding up */
timeoutM = timeout + 59; tmrval = (t + 59) / 60;
timeoutM /= 60;
if (nowayout)
{
__module_get(THIS_MODULE);
/* do not permit disabling the watchdog by writing 0 to reg. 0xF2 */ if (machine_is_netwinder()) {
if (!timeoutM) timeoutM = DEFAULT_TIMEOUT;
}
if (machine_is_netwinder())
{
/* we have a hw bug somewhere, so each 977 minute is actually only 30sec /* 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... * this limits the max timeout to half of device max of 255 minutes...
*/ */
timeoutM += timeoutM; tmrval += tmrval;
} }
/* max timeout value = 255 minutes (0xFF). Write 0 to disable WatchDog. */ if ((tmrval < 1) || (tmrval > 255))
if (timeoutM > 255) timeoutM = 255; return -EINVAL;
/* convert seconds to minutes */ /* timeout is the timeout in seconds, timeoutM is the timeout in minutes) */
printk(KERN_INFO "Wdt977 Watchdog activated: timeout = %d sec, nowayout = %i, testmode = %i.\n", timeout = t;
machine_is_netwinder() ? (timeoutM>>1)*60 : timeoutM*60, timeoutM = tmrval;
nowayout, testmode); return 0;
}
/*
* Get the watchdog status
*/
static int wdt977_get_status(int *status)
{
int new_status;
*status=0;
/* unlock the SuperIO chip */ /* unlock the SuperIO chip */
outb(0x87,0x370); outb(0x87,0x370);
outb(0x87,0x370); outb(0x87,0x370);
/* select device Aux2 (device=8) and set watchdog regs F2, F3 and F4 /* select device Aux2 (device=8) and read watchdog reg F4 */
* F2 has the timeout in minutes
* F3 could be set to the POWER LED blink (with GP17 set to PowerLed)
* at timeout, and to reset timer on kbd/mouse activity (not impl.)
* F4 is used to just clear the TIMEOUT'ed state (bit 0)
*/
outb(0x07,0x370); outb(0x07,0x370);
outb(0x08,0x371); outb(0x08,0x371);
outb(0xF2,0x370);
outb(timeoutM,0x371);
outb(0xF3,0x370);
outb(0x00,0x371); /* another setting is 0E for kbd/mouse/LED */
outb(0xF4,0x370); outb(0xF4,0x370);
outb(0x00,0x371); new_status = inb(0x371);
/* at last select device Aux1 (dev=7) and set GP16 as a watchdog output */
/* in test mode watch the bit 1 on F4 to indicate "triggered" */
if (!testmode)
{
outb(0x07,0x370);
outb(0x07,0x371);
outb(0xE6,0x370);
outb(0x08,0x371);
}
/* lock the SuperIO chip */ /* lock the SuperIO chip */
outb(0xAA,0x370); outb(0xAA,0x370);
if (new_status & 1)
*status |= WDIOF_CARDRESET;
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; return 0;
} }
...@@ -167,40 +251,11 @@ static int wdt977_release(struct inode *inode, struct file *file) ...@@ -167,40 +251,11 @@ static int wdt977_release(struct inode *inode, struct file *file)
*/ */
if (expect_close == 42) if (expect_close == 42)
{ {
/* unlock the SuperIO chip */ wdt977_stop();
outb(0x87,0x370);
outb(0x87,0x370);
/* select device Aux2 (device=8) and set watchdog regs F2,F3 and F4
* F3 is reset to its default state
* F4 can clear the TIMEOUT'ed state (bit 0) - back to default
* We can not use GP17 as a PowerLed, as we use its usage as a RedLed
*/
outb(0x07,0x370);
outb(0x08,0x371);
outb(0xF2,0x370);
outb(0xFF,0x371);
outb(0xF3,0x370);
outb(0x00,0x371);
outb(0xF4,0x370);
outb(0x00,0x371);
outb(0xF2,0x370);
outb(0x00,0x371);
/* at last select device Aux1 (dev=7) and set GP16 as a watchdog output */
outb(0x07,0x370);
outb(0x07,0x371);
outb(0xE6,0x370);
outb(0x08,0x371);
/* lock the SuperIO chip */
outb(0xAA,0x370);
clear_bit(0,&timer_alive); clear_bit(0,&timer_alive);
printk(KERN_INFO "Wdt977 Watchdog: shutdown\n");
} 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 |
.identity = "Winbond 83977", WDIOF_MAGICCLOSE |
WDIOF_KEEPALIVEPING,
.firmware_version = 1,
.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)
{ {
...@@ -272,62 +332,59 @@ static int wdt977_ioctl(struct inode *inode, struct file *file, ...@@ -272,62 +332,59 @@ static int wdt977_ioctl(struct inode *inode, struct file *file,
return -ENOIOCTLCMD; return -ENOIOCTLCMD;
case WDIOC_GETSUPPORT: case WDIOC_GETSUPPORT:
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_GETSTATUS:
wdt977_get_status(&status);
return put_user(status, (int *) arg);
case WDIOC_GETBOOTSTATUS: case WDIOC_GETBOOTSTATUS:
return put_user(0, (int *) arg); return put_user(0, (int *) arg);
case WDIOC_GETSTATUS: case WDIOC_KEEPALIVE:
/* unlock the SuperIO chip */ wdt977_keepalive();
outb(0x87,0x370); return 0;
outb(0x87,0x370);
/* select device Aux2 (device=8) and read watchdog reg F4 */ case WDIOC_SETOPTIONS:
outb(0x07,0x370); if (get_user (new_options, (int *) arg))
outb(0x08,0x371); return -EFAULT;
outb(0xF4,0x370);
temp = inb(0x371);
/* lock the SuperIO chip */ if (new_options & WDIOS_DISABLECARD) {
outb(0xAA,0x370); wdt977_stop();
retval = 0;
}
/* return info if "expired" in test mode */ if (new_options & WDIOS_ENABLECARD) {
return put_user(temp & 1, (int *) arg); wdt977_start();
retval = 0;
}
case WDIOC_KEEPALIVE: return retval;
kick_wdog();
return 0;
case WDIOC_SETTIMEOUT: case WDIOC_SETTIMEOUT:
if (copy_from_user(&temp, (int *) arg, sizeof(int))) if (get_user(new_timeout, (int *) arg))
return -EFAULT; return -EFAULT;
/* convert seconds to minutes, rounding up */ if (wdt977_set_timeout(new_timeout))
temp += 59; return -EINVAL;
temp /= 60;
/* 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...
*/
if (machine_is_netwinder())
{
temp += temp;
}
/* Sanity check */ wdt977_keepalive();
if (temp < 0 || temp > 255) /* Fall */
return -EINVAL;
if (!temp && nowayout) case WDIOC_GETTIMEOUT:
return -EINVAL; return put_user(timeout, (int *)arg);
timeoutM = temp;
kick_wdog();
return 0;
} }
} }
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",
return retval; WATCHDOG_MINOR, retval);
unregister_reboot_notifier(&wdt977_notifier);
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