Commit bdc91033 authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://linux-dj.bkbits.net/watchdog

into home.transmeta.com:/home/torvalds/v2.5/linux
parents 9cb52295 59409421
...@@ -283,4 +283,24 @@ config MACHZ_WDT ...@@ -283,4 +283,24 @@ config MACHZ_WDT
The module is called machzwd.o. If you want to compile it as a The module is called machzwd.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>. module, say M here and read <file:Documentation/modules.txt>.
config W83877F_WDT
tristate "W83877F Computer Watchdog"
depends on WATCHDOG
config SC520_WDT
tristate "AMD Elan SC520 processor Watchdog"
depends on WATCHDOG
config ALIM7101_WDT
tristate "ALi M7101 PMU Computer Watchdog"
depends on WATCHDOG
config SC1200_WDT
tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog"
depends on WATCHDOG
config WAFER_WDT
tristate "ICP Wafer 5823 Single Board Computer Watchdog"
depends on WATCHDOG
endmenu endmenu
...@@ -23,3 +23,8 @@ obj-$(CONFIG_MACHZ_WDT) += machzwd.o ...@@ -23,3 +23,8 @@ obj-$(CONFIG_MACHZ_WDT) += machzwd.o
obj-$(CONFIG_SH_WDT) += shwdt.o obj-$(CONFIG_SH_WDT) += shwdt.o
obj-$(CONFIG_EUROTECH_WDT) += eurotechwdt.o obj-$(CONFIG_EUROTECH_WDT) += eurotechwdt.o
obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o
obj-$(CONFIG_SC520_WDT) += sc520_wdt.o
obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o
obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o
obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
static int acq_is_open; static int acq_is_open;
static spinlock_t acq_lock; static spinlock_t acq_lock;
static int expect_close = 0;
/* /*
* You must set these - there is no sane way to probe for this board. * You must set these - there is no sane way to probe for this board.
...@@ -79,8 +80,20 @@ static ssize_t acq_write(struct file *file, const char *buf, size_t count, loff_ ...@@ -79,8 +80,20 @@ static ssize_t acq_write(struct file *file, const char *buf, size_t count, loff_
if (ppos != &file->f_pos) if (ppos != &file->f_pos)
return -ESPIPE; return -ESPIPE;
if(count) if(count) {
{ if (!nowayout) {
size_t i;
expect_close = 0;
for (i = 0; i != count; i++) {
char c;
if (get_user(c, buf + i))
return -EFAULT;
if (c == 'V')
expect_close = 1;
}
}
acq_ping(); acq_ping();
return 1; return 1;
} }
...@@ -97,9 +110,11 @@ static ssize_t acq_read(struct file *file, char *buf, size_t count, loff_t *ppos ...@@ -97,9 +110,11 @@ static ssize_t acq_read(struct file *file, char *buf, size_t count, loff_t *ppos
static int acq_ioctl(struct inode *inode, struct file *file, unsigned int cmd, static int acq_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg) unsigned long arg)
{ {
static struct watchdog_info ident= static struct watchdog_info ident =
{ {
WDIOF_KEEPALIVEPING, 1, "Acquire WDT" .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "Acquire WDT"
}; };
switch(cmd) switch(cmd)
...@@ -126,39 +141,35 @@ static int acq_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ...@@ -126,39 +141,35 @@ static int acq_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
static int acq_open(struct inode *inode, struct file *file) static int acq_open(struct inode *inode, struct file *file)
{ {
switch(minor(inode->i_rdev)) if ((minor(inode->i_rdev) == WATCHDOG_MINOR)) {
{
case WATCHDOG_MINOR:
spin_lock(&acq_lock); spin_lock(&acq_lock);
if(acq_is_open) if(acq_is_open) {
{
spin_unlock(&acq_lock); spin_unlock(&acq_lock);
return -EBUSY; return -EBUSY;
} }
if (nowayout) { if (nowayout)
MOD_INC_USE_COUNT; MOD_INC_USE_COUNT;
}
/*
* Activate
*/
/* Activate */
acq_is_open=1; acq_is_open=1;
inb_p(WDT_START); inb_p(WDT_START);
spin_unlock(&acq_lock); spin_unlock(&acq_lock);
return 0; return 0;
default:
} else {
return -ENODEV; return -ENODEV;
} }
} }
static int acq_close(struct inode *inode, struct file *file) static int acq_close(struct inode *inode, struct file *file)
{ {
if(minor(inode->i_rdev)==WATCHDOG_MINOR) if(minor(inode->i_rdev)==WATCHDOG_MINOR) {
{
spin_lock(&acq_lock); spin_lock(&acq_lock);
if (!nowayout) { if (expect_close)
inb_p(WDT_STOP); inb_p(WDT_STOP);
} else
printk(KERN_CRIT "WDT closed unexpectedly. WDT will not stop!\n");
acq_is_open=0; acq_is_open=0;
spin_unlock(&acq_lock); spin_unlock(&acq_lock);
} }
...@@ -173,10 +184,9 @@ static int acq_notify_sys(struct notifier_block *this, unsigned long code, ...@@ -173,10 +184,9 @@ static int acq_notify_sys(struct notifier_block *this, unsigned long code,
void *unused) void *unused)
{ {
if(code==SYS_DOWN || code==SYS_HALT) if(code==SYS_DOWN || code==SYS_HALT)
{
/* Turn the card off */ /* Turn the card off */
inb_p(WDT_STOP); inb_p(WDT_STOP);
}
return NOTIFY_DONE; return NOTIFY_DONE;
} }
...@@ -196,9 +206,9 @@ static struct file_operations acq_fops = { ...@@ -196,9 +206,9 @@ static struct file_operations acq_fops = {
static struct miscdevice acq_miscdev= static struct miscdevice acq_miscdev=
{ {
WATCHDOG_MINOR, .minor = WATCHDOG_MINOR,
"watchdog", .name = "watchdog",
&acq_fops .fops = &acq_fops
}; };
...@@ -207,11 +217,11 @@ static struct miscdevice acq_miscdev= ...@@ -207,11 +217,11 @@ static struct miscdevice acq_miscdev=
* turn the timebomb registers off. * turn the timebomb registers off.
*/ */
static struct notifier_block acq_notifier= static struct notifier_block acq_notifier =
{ {
acq_notify_sys, .notifier_call = acq_notify_sys,
NULL, .next = NULL,
0 .priority = 0
}; };
static int __init acq_init(void) static int __init acq_init(void)
...@@ -221,13 +231,11 @@ static int __init acq_init(void) ...@@ -221,13 +231,11 @@ static int __init acq_init(void)
spin_lock_init(&acq_lock); spin_lock_init(&acq_lock);
if (misc_register(&acq_miscdev)) if (misc_register(&acq_miscdev))
return -ENODEV; return -ENODEV;
if (!request_region(WDT_STOP, 1, "Acquire WDT")) if (!request_region(WDT_STOP, 1, "Acquire WDT")) {
{
misc_deregister(&acq_miscdev); misc_deregister(&acq_miscdev);
return -EIO; return -EIO;
} }
if (!request_region(WDT_START, 1, "Acquire WDT")) if (!request_region(WDT_START, 1, "Acquire WDT")) {
{
release_region(WDT_STOP, 1); release_region(WDT_STOP, 1);
misc_deregister(&acq_miscdev); misc_deregister(&acq_miscdev);
return -EIO; return -EIO;
......
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
static int advwdt_is_open; static int advwdt_is_open;
static char adv_expect_close;
static spinlock_t advwdt_lock; static spinlock_t advwdt_lock;
/* /*
...@@ -99,10 +100,22 @@ advwdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos) ...@@ -99,10 +100,22 @@ advwdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
return -ESPIPE; return -ESPIPE;
if (count) { if (count) {
if (!nowayout) {
size_t i;
adv_expect_close = 0;
for (i = 0; i != count; i++) {
char c;
if (get_user(c, buf+i))
return -EFAULT;
if (c == 'V')
adv_expect_close = 42;
}
}
advwdt_ping(); advwdt_ping();
return 1;
} }
return 0; return count;
} }
static ssize_t static ssize_t
...@@ -116,7 +129,9 @@ advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ...@@ -116,7 +129,9 @@ advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg) unsigned long arg)
{ {
static struct watchdog_info ident = { static struct watchdog_info ident = {
WDIOF_KEEPALIVEPING, 1, "Advantech WDT" .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "Advantech WDT"
}; };
switch (cmd) { switch (cmd) {
...@@ -143,25 +158,22 @@ advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ...@@ -143,25 +158,22 @@ advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
static int static int
advwdt_open(struct inode *inode, struct file *file) advwdt_open(struct inode *inode, struct file *file)
{ {
switch (minor(inode->i_rdev)) { if (minor(inode->i_rdev) == WATCHDOG_MINOR) {
case WATCHDOG_MINOR:
spin_lock(&advwdt_lock); spin_lock(&advwdt_lock);
if (advwdt_is_open) { if (advwdt_is_open) {
spin_unlock(&advwdt_lock); spin_unlock(&advwdt_lock);
return -EBUSY; return -EBUSY;
} }
if (nowayout) {
if (nowayout)
MOD_INC_USE_COUNT; MOD_INC_USE_COUNT;
}
/*
* Activate
*/
/* Activate */
advwdt_is_open = 1; advwdt_is_open = 1;
advwdt_ping(); advwdt_ping();
spin_unlock(&advwdt_lock); spin_unlock(&advwdt_lock);
return 0; return 0;
default: } else {
return -ENODEV; return -ENODEV;
} }
} }
...@@ -171,9 +183,9 @@ advwdt_close(struct inode *inode, struct file *file) ...@@ -171,9 +183,9 @@ advwdt_close(struct inode *inode, struct file *file)
{ {
if (minor(inode->i_rdev) == WATCHDOG_MINOR) { if (minor(inode->i_rdev) == WATCHDOG_MINOR) {
spin_lock(&advwdt_lock); spin_lock(&advwdt_lock);
if (!nowayout) { if (!nowayout)
inb_p(WDT_STOP); inb_p(WDT_STOP);
}
advwdt_is_open = 0; advwdt_is_open = 0;
spin_unlock(&advwdt_lock); spin_unlock(&advwdt_lock);
} }
...@@ -188,10 +200,10 @@ static int ...@@ -188,10 +200,10 @@ static int
advwdt_notify_sys(struct notifier_block *this, unsigned long code, advwdt_notify_sys(struct notifier_block *this, unsigned long code,
void *unused) void *unused)
{ {
if (code == SYS_DOWN || code == SYS_HALT) { if (code == SYS_DOWN || code == SYS_HALT)
/* Turn the WDT off */ /* Turn the WDT off */
inb_p(WDT_STOP); inb_p(WDT_STOP);
}
return NOTIFY_DONE; return NOTIFY_DONE;
} }
...@@ -209,9 +221,9 @@ static struct file_operations advwdt_fops = { ...@@ -209,9 +221,9 @@ static struct file_operations advwdt_fops = {
}; };
static struct miscdevice advwdt_miscdev = { static struct miscdevice advwdt_miscdev = {
WATCHDOG_MINOR, .minor = WATCHDOG_MINOR,
"watchdog", .name = "watchdog",
&advwdt_fops .fops = &advwdt_fops
}; };
/* /*
...@@ -220,9 +232,9 @@ static struct miscdevice advwdt_miscdev = { ...@@ -220,9 +232,9 @@ static struct miscdevice advwdt_miscdev = {
*/ */
static struct notifier_block advwdt_notifier = { static struct notifier_block advwdt_notifier = {
advwdt_notify_sys, .notifier_call = advwdt_notify_sys,
NULL, .next = NULL,
0 .priority = 0
}; };
static void __init static void __init
...@@ -276,5 +288,3 @@ module_exit(advwdt_exit); ...@@ -276,5 +288,3 @@ module_exit(advwdt_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
/* end of advantechwdt.c */
/*
* ALi M7101 PMU Computer Watchdog Timer driver for Linux 2.4.x
*
* Based on w83877f_wdt.c by Scott Jennings <management@oro.net>
* and the Cobalt kernel WDT timer driver by Tim Hockin
* <thockin@cobaltnet.com>
*
* (c)2002 Steve Hill <steve@navaho.co.uk>
*
* Theory of operation:
* A Watchdog Timer (WDT) is a hardware circuit that can
* reset the computer system in case of a software fault.
* You probably knew that already.
*
* Usually a userspace daemon will notify the kernel WDT driver
* via the /proc/watchdog special device file that userspace is
* still alive, at regular intervals. When such a notification
* occurs, the driver will usually tell the hardware watchdog
* that everything is in order, and that the watchdog should wait
* for yet another little while to reset the system.
* If userspace fails (RAM error, kernel bug, whatever), the
* notifications cease to occur, and the hardware watchdog will
* reset the system (causing a reboot) after the timeout occurs.
*
* This WDT driver is different from most other Linux WDT
* drivers in that the driver will ping the watchdog by itself,
* because this particular WDT has a very short timeout (1.6
* seconds) and it would be insane to count on any userspace
* daemon always getting scheduled within that time frame.
*/
#include <linux/module.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/sched.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/fcntl.h>
#include <linux/smp_lock.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/init.h>
#include <linux/pci.h>
#define OUR_NAME "alim7101_wdt"
#define WDT_ENABLE 0x9C
#define WDT_DISABLE 0x8C
#define ALI_7101_WDT 0x92
#define ALI_WDT_ARM 0x01
/*
* We're going to use a 1 second timeout.
* If we reset the watchdog every ~250ms we should be safe. */
#define WDT_INTERVAL (HZ/4+1)
/*
* We must not require too good response from the userspace daemon.
* Here we require the userspace daemon to send us a heartbeat
* char to /dev/watchdog every 30 seconds.
*/
#define WDT_HEARTBEAT (HZ * 30)
static void wdt_timer_ping(unsigned long);
static struct timer_list timer;
static unsigned long next_heartbeat;
static unsigned long wdt_is_open;
static int wdt_expect_close;
static struct pci_dev *alim7101_pmu;
#ifdef CONFIG_WATCHDOG_NOWAYOUT
static int nowayout = 1;
#else
static int nowayout = 0;
#endif
MODULE_PARM(nowayout,"i");
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
/*
* Whack the dog
*/
static void wdt_timer_ping(unsigned long data)
{
/* If we got a heartbeat pulse within the WDT_US_INTERVAL
* we agree to ping the WDT
*/
char tmp;
if(time_before(jiffies, next_heartbeat))
{
/* Ping the WDT (this is actually a disarm/arm sequence) */
pci_read_config_byte(alim7101_pmu, 0x92, &tmp);
pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp & ~ALI_WDT_ARM));
pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp | ALI_WDT_ARM));
} else {
printk(OUR_NAME ": Heartbeat lost! Will not ping the watchdog\n");
}
/* Re-set the timer interval */
timer.expires = jiffies + WDT_INTERVAL;
add_timer(&timer);
}
/*
* Utility routines
*/
static void wdt_change(int writeval)
{
char tmp;
pci_read_config_byte(alim7101_pmu, 0x92, &tmp);
if (writeval == WDT_ENABLE)
pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp | ALI_WDT_ARM));
else
pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp & ~ALI_WDT_ARM));
}
static void wdt_startup(void)
{
next_heartbeat = jiffies + WDT_HEARTBEAT;
/* We must enable before we kick off the timer in case the timer
occurs as we ping it */
wdt_change(WDT_ENABLE);
/* Start the timer */
timer.expires = jiffies + WDT_INTERVAL;
add_timer(&timer);
printk(OUR_NAME ": Watchdog timer is now enabled.\n");
}
static void wdt_turnoff(void)
{
/* Stop the timer */
del_timer_sync(&timer);
wdt_change(WDT_DISABLE);
printk(OUR_NAME ": Watchdog timer is now disabled...\n");
}
/*
* /dev/watchdog handling
*/
static ssize_t fop_write(struct file * file, const char * buf, size_t count, loff_t * ppos)
{
/* We can't seek */
if(ppos != &file->f_pos)
return -ESPIPE;
/* See if we got the magic character */
if(count) {
if (!nowayout) {
size_t ofs;
/* note: just in case someone wrote the magic character
* five months ago... */
wdt_expect_close = 0;
/* now scan */
for (ofs = 0; ofs != count; ofs++) {
char c;
if (get_user(c, buf+ofs))
return -EFAULT;
if (c == 'V')
wdt_expect_close = 1;
}
}
/* someone wrote to us, we should restart timer */
next_heartbeat = jiffies + WDT_HEARTBEAT;
return 1;
};
return 0;
}
static ssize_t fop_read(struct file * file, char * buf, size_t count, loff_t * ppos)
{
/* No can do */
return -EINVAL;
}
static int fop_open(struct inode * inode, struct file * file)
{
/* Just in case we're already talking to someone... */
if(test_and_set_bit(0, &wdt_is_open))
return -EBUSY;
/* Good, fire up the show */
wdt_startup();
return 0;
}
static int fop_close(struct inode * inode, struct file * file)
{
if(wdt_expect_close)
wdt_turnoff();
else
printk(OUR_NAME ": device file closed unexpectedly. Will not stop the WDT!\n");
clear_bit(0, &wdt_is_open);
return 0;
}
static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
static struct watchdog_info ident =
{
.options = WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "ALiM7101"
};
switch(cmd)
{
case WDIOC_GETSUPPORT:
return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))?-EFAULT:0;
case WDIOC_KEEPALIVE:
next_heartbeat = jiffies + WDT_HEARTBEAT;
return 0;
default:
return -ENOTTY;
}
}
static struct file_operations wdt_fops = {
.owner= THIS_MODULE,
.llseek= no_llseek,
.read= fop_read,
.write= fop_write,
.open= fop_open,
.release= fop_close,
.ioctl= fop_ioctl
};
static struct miscdevice wdt_miscdev = {
.minor=WATCHDOG_MINOR,
.name="watchdog",
.fops=&wdt_fops
};
/*
* Notifier for system down
*/
static int wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused)
{
if (code==SYS_DOWN || code==SYS_HALT)
wdt_turnoff();
if (code==SYS_RESTART) {
/*
* Cobalt devices have no way of rebooting themselves other than
* getting the watchdog to pull reset, so we restart the watchdog on
* reboot with no heartbeat
*/
wdt_change(WDT_ENABLE);
printk(OUR_NAME ": Watchdog timer is now enabled with no heartbeat - should reboot in ~1 second.\n");
}
return NOTIFY_DONE;
}
/*
* The WDT needs to learn about soft shutdowns in order to
* turn the timebomb registers off.
*/
static struct notifier_block wdt_notifier=
{
.notifier_call = wdt_notify_sys,
.next = 0,
.priority = 0
};
static void __exit alim7101_wdt_unload(void)
{
wdt_turnoff();
/* Deregister */
misc_deregister(&wdt_miscdev);
unregister_reboot_notifier(&wdt_notifier);
}
static int __init alim7101_wdt_init(void)
{
int rc = -EBUSY;
struct pci_dev *ali1543_south;
char tmp;
printk(KERN_INFO OUR_NAME ": Steve Hill <steve@navaho.co.uk>.\n");
alim7101_pmu = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101,NULL);
if (!alim7101_pmu) {
printk(KERN_INFO OUR_NAME ": ALi M7101 PMU not present - WDT not set\n");
return -EBUSY;
}
/* Set the WDT in the PMU to 1 second */
pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, 0x02);
ali1543_south = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, NULL);
if (!ali1543_south) {
printk(KERN_INFO OUR_NAME ": ALi 1543 South-Bridge not present - WDT not set\n");
return -EBUSY;
}
pci_read_config_byte(ali1543_south, 0x5e, &tmp);
if ((tmp & 0x1e) != 0x12) {
printk(KERN_INFO OUR_NAME ": ALi 1543 South-Bridge does not have the correct revision number (???1001?) - WDT not set\n");
return -EBUSY;
}
init_timer(&timer);
timer.function = wdt_timer_ping;
timer.data = 1;
rc = misc_register(&wdt_miscdev);
if (rc)
return rc;
rc = register_reboot_notifier(&wdt_notifier);
if (rc) {
misc_deregister(&wdt_miscdev);
return rc;
}
printk(KERN_INFO OUR_NAME ": WDT driver for ALi M7101 initialised.\n");
return 0;
}
module_init(alim7101_wdt_init);
module_exit(alim7101_wdt_unload);
MODULE_AUTHOR("Steve Hill");
MODULE_LICENSE("GPL");
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
* warranty for any of this software. This material is provided * warranty for any of this software. This material is provided
* "AS-IS" and at no charge. * "AS-IS" and at no charge.
* *
* (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>* * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
* *
* 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com> * 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
* Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
static int eurwdt_is_open; static int eurwdt_is_open;
static spinlock_t eurwdt_lock; static spinlock_t eurwdt_lock;
static char eur_expect_close;
/* /*
* You must set these - there is no sane way to probe for this board. * You must set these - there is no sane way to probe for this board.
...@@ -80,10 +81,10 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CON ...@@ -80,10 +81,10 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CON
#define WDT_CTRL_REG 0x30 #define WDT_CTRL_REG 0x30
#define WDT_OUTPIN_CFG 0xe2 #define WDT_OUTPIN_CFG 0xe2
#define WDT_EVENT_INT 0x00 #define WDT_EVENT_INT 0x00
#define WDT_EVENT_REBOOT 0x08 #define WDT_EVENT_REBOOT 0x08
#define WDT_UNIT_SEL 0xf1 #define WDT_UNIT_SEL 0xf1
#define WDT_UNIT_SECS 0x80 #define WDT_UNIT_SECS 0x80
#define WDT_TIMEOUT_VAL 0xf2 #define WDT_TIMEOUT_VAL 0xf2
#define WDT_TIMER_CFG 0xf3 #define WDT_TIMER_CFG 0xf3
...@@ -103,7 +104,7 @@ static int __init eurwdt_setup(char *str) ...@@ -103,7 +104,7 @@ static int __init eurwdt_setup(char *str)
{ {
int ints[4]; int ints[4];
str = get_options (str, ARRAY_SIZE(ints), ints); str = get_options (str, ARRAY_SIZE(ints), ints);
if (ints[0] > 0) { if (ints[0] > 0) {
io = ints[1]; io = ints[1];
...@@ -170,8 +171,8 @@ static void eurwdt_activate_timer(void) ...@@ -170,8 +171,8 @@ static void eurwdt_activate_timer(void)
{ {
eurwdt_disable_timer(); eurwdt_disable_timer();
eurwdt_write_reg(WDT_CTRL_REG, 0x01); /* activate the WDT */ eurwdt_write_reg(WDT_CTRL_REG, 0x01); /* activate the WDT */
eurwdt_write_reg(WDT_OUTPIN_CFG, !strcmp("int", ev) ? eurwdt_write_reg(WDT_OUTPIN_CFG, !strcmp("int", ev) ? WDT_EVENT_INT : WDT_EVENT_REBOOT);
WDT_EVENT_INT : WDT_EVENT_REBOOT);
/* Setting interrupt line */ /* Setting interrupt line */
if (irq == 2 || irq > 15 || irq < 0) { if (irq == 2 || irq > 15 || irq < 0) {
printk(KERN_ERR ": invalid irq number\n"); printk(KERN_ERR ": invalid irq number\n");
...@@ -179,6 +180,7 @@ static void eurwdt_activate_timer(void) ...@@ -179,6 +180,7 @@ static void eurwdt_activate_timer(void)
} }
if (irq == 0) if (irq == 0)
printk(KERN_INFO ": interrupt disabled\n"); printk(KERN_INFO ": interrupt disabled\n");
eurwdt_write_reg(WDT_TIMER_CFG, irq<<4); eurwdt_write_reg(WDT_TIMER_CFG, irq<<4);
eurwdt_write_reg(WDT_UNIT_SEL, WDT_UNIT_SECS); /* we use seconds */ eurwdt_write_reg(WDT_UNIT_SEL, WDT_UNIT_SECS); /* we use seconds */
...@@ -234,6 +236,19 @@ loff_t *ppos) ...@@ -234,6 +236,19 @@ loff_t *ppos)
return -ESPIPE; return -ESPIPE;
if (count) { if (count) {
if (!nowayout) {
size_t i;
eur_expect_close = 0;
for (i = 0; i != count; i++) {
char c;
if(get_user(c, buf+i))
return -EFAULT;
if (c == 'V')
eur_expect_close = 42;
}
}
eurwdt_ping(); /* the default timeout */ eurwdt_ping(); /* the default timeout */
return 1; return 1;
} }
...@@ -310,9 +325,8 @@ static int eurwdt_open(struct inode *inode, struct file *file) ...@@ -310,9 +325,8 @@ static int eurwdt_open(struct inode *inode, struct file *file)
spin_unlock(&eurwdt_lock); spin_unlock(&eurwdt_lock);
return -EBUSY; return -EBUSY;
} }
if (nowayout) { if (nowayout)
MOD_INC_USE_COUNT; MOD_INC_USE_COUNT;
}
eurwdt_is_open = 1; eurwdt_is_open = 1;
...@@ -322,7 +336,6 @@ static int eurwdt_open(struct inode *inode, struct file *file) ...@@ -322,7 +336,6 @@ static int eurwdt_open(struct inode *inode, struct file *file)
spin_unlock(&eurwdt_lock); spin_unlock(&eurwdt_lock);
MOD_INC_USE_COUNT; MOD_INC_USE_COUNT;
return 0; return 0;
case TEMP_MINOR: case TEMP_MINOR:
...@@ -348,11 +361,10 @@ static int eurwdt_open(struct inode *inode, struct file *file) ...@@ -348,11 +361,10 @@ static int eurwdt_open(struct inode *inode, struct file *file)
static int eurwdt_release(struct inode *inode, struct file *file) static int eurwdt_release(struct inode *inode, struct file *file)
{ {
if (minor(inode->i_rdev) == WATCHDOG_MINOR) { if (minor(inode->i_rdev) == WATCHDOG_MINOR) {
if (!nowayout) { if (!nowayout)
eurwdt_disable_timer(); eurwdt_disable_timer();
}
eurwdt_is_open = 0;
eurwdt_is_open = 0;
MOD_DEC_USE_COUNT; MOD_DEC_USE_COUNT;
} }
...@@ -479,16 +491,16 @@ static int __init eurwdt_init(void) ...@@ -479,16 +491,16 @@ static int __init eurwdt_init(void)
spin_lock_init(&eurwdt_lock); spin_lock_init(&eurwdt_lock);
out: out:
return ret; return ret;
outreg: outreg:
release_region(io, 2); release_region(io, 2);
outirq: outirq:
free_irq(irq, NULL); free_irq(irq, NULL);
outmisc: outmisc:
misc_deregister(&eurwdt_miscdev); misc_deregister(&eurwdt_miscdev);
goto out; goto out;
} }
......
...@@ -244,7 +244,9 @@ static int i810tco_ioctl (struct inode *inode, struct file *file, ...@@ -244,7 +244,9 @@ static int i810tco_ioctl (struct inode *inode, struct file *file,
int options, retval = -EINVAL; int options, retval = -EINVAL;
static struct watchdog_info ident = { static struct watchdog_info ident = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, .options = WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING |
WDIOF_MAGICCLOSE,
.firmware_version = 0, .firmware_version = 0,
.identity = "i810 TCO timer", .identity = "i810 TCO timer",
}; };
......
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
static int ibwdt_is_open; static int ibwdt_is_open;
static spinlock_t ibwdt_lock; static spinlock_t ibwdt_lock;
static int expect_close = 0;
/* /*
* *
...@@ -136,6 +137,21 @@ ibwdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos) ...@@ -136,6 +137,21 @@ ibwdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
return -ESPIPE; return -ESPIPE;
if (count) { if (count) {
if (!nowayout) {
size_t i;
/* In case it was set long ago */
expect_close = 0;
for (i = 0; i != count; i++) {
char c;
if (get_user(c, buf + i))
return -EFAULT;
if (c == 'V')
expect_close = 1;
}
}
ibwdt_ping(); ibwdt_ping();
return 1; return 1;
} }
...@@ -153,7 +169,9 @@ ibwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ...@@ -153,7 +169,9 @@ ibwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg) unsigned long arg)
{ {
static struct watchdog_info ident = { static struct watchdog_info ident = {
WDIOF_KEEPALIVEPING, 1, "IB700 WDT" .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "IB700 WDT"
}; };
switch (cmd) { switch (cmd) {
...@@ -180,25 +198,21 @@ ibwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ...@@ -180,25 +198,21 @@ ibwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
static int static int
ibwdt_open(struct inode *inode, struct file *file) ibwdt_open(struct inode *inode, struct file *file)
{ {
switch (minor(inode->i_rdev)) { if (minor(inode->i_rdev) == WATCHDOG_MINOR) {
case WATCHDOG_MINOR:
spin_lock(&ibwdt_lock); spin_lock(&ibwdt_lock);
if (ibwdt_is_open) { if (ibwdt_is_open) {
spin_unlock(&ibwdt_lock); spin_unlock(&ibwdt_lock);
return -EBUSY; return -EBUSY;
} }
if (nowayout) { if (nowayout)
MOD_INC_USE_COUNT; MOD_INC_USE_COUNT;
}
/*
* Activate
*/
/* Activate */
ibwdt_is_open = 1; ibwdt_is_open = 1;
ibwdt_ping(); ibwdt_ping();
spin_unlock(&ibwdt_lock); spin_unlock(&ibwdt_lock);
return 0; return 0;
default: } else {
return -ENODEV; return -ENODEV;
} }
} }
...@@ -206,16 +220,16 @@ ibwdt_open(struct inode *inode, struct file *file) ...@@ -206,16 +220,16 @@ ibwdt_open(struct inode *inode, struct file *file)
static int static int
ibwdt_close(struct inode *inode, struct file *file) ibwdt_close(struct inode *inode, struct file *file)
{ {
lock_kernel();
if (minor(inode->i_rdev) == WATCHDOG_MINOR) { if (minor(inode->i_rdev) == WATCHDOG_MINOR) {
spin_lock(&ibwdt_lock); spin_lock(&ibwdt_lock);
if (!nowayout) { if (expect_close)
outb_p(timeout_val, WDT_STOP); outb_p(timeout_val, WDT_STOP);
} else
printk(KERN_CRIT "WDT device closed unexpectedly. WDT will not stop!\n");
ibwdt_is_open = 0; ibwdt_is_open = 0;
spin_unlock(&ibwdt_lock); spin_unlock(&ibwdt_lock);
} }
unlock_kernel();
return 0; return 0;
} }
...@@ -248,9 +262,9 @@ static struct file_operations ibwdt_fops = { ...@@ -248,9 +262,9 @@ static struct file_operations ibwdt_fops = {
}; };
static struct miscdevice ibwdt_miscdev = { static struct miscdevice ibwdt_miscdev = {
WATCHDOG_MINOR, .minor = WATCHDOG_MINOR,
"watchdog", .name = "watchdog",
&ibwdt_fops .fops = &ibwdt_fops
}; };
/* /*
...@@ -259,9 +273,9 @@ static struct miscdevice ibwdt_miscdev = { ...@@ -259,9 +273,9 @@ static struct miscdevice ibwdt_miscdev = {
*/ */
static struct notifier_block ibwdt_notifier = { static struct notifier_block ibwdt_notifier = {
ibwdt_notify_sys, .notifier_call = ibwdt_notify_sys,
NULL, .next = NULL,
0 .priority = 0
}; };
static int __init static int __init
......
/*
* IndyDog 0.2 A Hardware Watchdog Device for SGI IP22
*
* (c) Copyright 2002 Guido Guenther <agx@sigxcpu.org>, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* based on softdog.c by Alan Cox <alan@redhat.com>
*/
#include <linux/module.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/smp_lock.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <asm/sgi/sgimc.h>
static unsigned long indydog_alive;
static struct sgimc_misc_ctrl *mcmisc_regs;
static int expect_close = 0;
#ifdef CONFIG_WATCHDOG_NOWAYOUT
static int nowayout = 1;
#else
static int nowayout = 0;
#endif
MODULE_PARM(nowayout,"i");
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
static void indydog_ping()
{
mcmisc_regs->watchdogt = 0;
}
/*
* Allow only one person to hold it open
*/
static int indydog_open(struct inode *inode, struct file *file)
{
u32 mc_ctrl0;
if( test_and_set_bit(0,&indydog_alive) )
return -EBUSY;
if (nowayout)
MOD_INC_USE_COUNT;
/*
* Activate timer
*/
mcmisc_regs = (struct sgimc_misc_ctrl *)(KSEG1+0x1fa00000);
mc_ctrl0 = mcmisc_regs->cpuctrl0 | SGIMC_CCTRL0_WDOG;
mcmisc_regs->cpuctrl0 = mc_ctrl0;
indydog_ping();
printk("Started watchdog timer.\n");
return 0;
}
static int indydog_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) {
u32 mc_ctrl0 = mcmisc_regs->cpuctrl0;
mc_ctrl0 &= ~SGIMC_CCTRL0_WDOG;
mcmisc_regs->cpuctrl0 = mc_ctrl0;
printk("Stopped watchdog timer.\n");
} else {
printk(KERN_CRIT "WDT device closed unexpectedly. WDT will not stop!\n");
}
clear_bit(0,&indydog_alive);
return 0;
}
static ssize_t indydog_write(struct file *file, const char *data, size_t len, loff_t *ppos)
{
/* Can't seek (pwrite) on this device */
if (ppos != &file->f_pos)
return -ESPIPE;
/* Refresh the timer. */
if (len) {
if (!nowayout) {
size_t i;
/* In case it was set long ago */
expect_close = 0;
for (i = 0; i != len; i++) {
char c;
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V')
expect_close = 1;
}
}
indydog_ping();
return 1;
}
return 0;
}
static int indydog_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
static struct watchdog_info ident = {
.options = WDIOF_MAGICCLOSE,
.identity = "Hardware Watchdog for SGI IP22",
};
switch (cmd) {
default:
return -ENOIOCTLCMD;
case WDIOC_GETSUPPORT:
if(copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)))
return -EFAULT;
return 0;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
return put_user(0,(int *)arg);
case WDIOC_KEEPALIVE:
indydog_ping();
return 0;
}
}
static struct file_operations indydog_fops = {
.owner = THIS_MODULE,
.write = indydog_write,
.ioctl = indydog_ioctl,
.open = indydog_open,
.release= indydog_release,
};
static struct miscdevice indydog_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &indydog_fops,
};
static const char banner[] __initdata = KERN_INFO "Hardware Watchdog Timer for SGI IP22: 0.2\n";
static int __init watchdog_init(void)
{
int ret;
ret = misc_register(&indydog_miscdev);
if (ret)
return ret;
printk(banner);
return 0;
}
static void __exit watchdog_exit(void)
{
misc_deregister(&indydog_miscdev);
}
module_init(watchdog_init);
module_exit(watchdog_exit);
MODULE_LICENSE("GPL");
...@@ -117,7 +117,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CON ...@@ -117,7 +117,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CON
#define PFX "machzwd" #define PFX "machzwd"
static struct watchdog_info zf_info = { static struct watchdog_info zf_info = {
.options = WDIOF_KEEPALIVEPING, .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.firmware_version = 1, .firmware_version = 1,
.identity = "ZF-Logic watchdog" .identity = "ZF-Logic watchdog"
}; };
...@@ -314,7 +314,7 @@ static ssize_t zf_write(struct file *file, const char *buf, size_t count, ...@@ -314,7 +314,7 @@ static ssize_t zf_write(struct file *file, const char *buf, size_t count,
/* See if we got the magic character */ /* See if we got the magic character */
if(count){ if(count){
/* /*
* no need to check for close confirmation * no need to check for close confirmation
* no way to disable watchdog ;) * no way to disable watchdog ;)
*/ */
...@@ -328,7 +328,7 @@ static ssize_t zf_write(struct file *file, const char *buf, size_t count, ...@@ -328,7 +328,7 @@ static ssize_t zf_write(struct file *file, const char *buf, size_t count,
zf_expect_close = 0; zf_expect_close = 0;
/* now scan */ /* now scan */
for(ofs = 0; ofs != count; ofs++){ for (ofs = 0; ofs != count; ofs++){
char c; char c;
if (get_user(c, buf + ofs)) if (get_user(c, buf + ofs))
return -EFAULT; return -EFAULT;
...@@ -338,6 +338,7 @@ static ssize_t zf_write(struct file *file, const char *buf, size_t count, ...@@ -338,6 +338,7 @@ static ssize_t zf_write(struct file *file, const char *buf, size_t count,
} }
} }
} }
/* /*
* Well, anyhow someone wrote to us, * Well, anyhow someone wrote to us,
* we should return that favour * we should return that favour
...@@ -395,9 +396,9 @@ static int zf_open(struct inode *inode, struct file *file) ...@@ -395,9 +396,9 @@ static int zf_open(struct inode *inode, struct file *file)
return -EBUSY; return -EBUSY;
} }
if (nowayout) { if (nowayout)
MOD_INC_USE_COUNT; MOD_INC_USE_COUNT;
}
zf_is_open = 1; zf_is_open = 1;
spin_unlock(&zf_lock); spin_unlock(&zf_lock);
......
...@@ -62,6 +62,7 @@ static long mixcomwd_opened; /* long req'd for setbit --RR */ ...@@ -62,6 +62,7 @@ static long mixcomwd_opened; /* long req'd for setbit --RR */
static int watchdog_port; static int watchdog_port;
static int mixcomwd_timer_alive; static int mixcomwd_timer_alive;
static struct timer_list mixcomwd_timer = TIMER_INITIALIZER(NULL, 0, 0); static struct timer_list mixcomwd_timer = TIMER_INITIALIZER(NULL, 0, 0);
static int expect_close = 0;
#ifdef CONFIG_WATCHDOG_NOWAYOUT #ifdef CONFIG_WATCHDOG_NOWAYOUT
static int nowayout = 1; static int nowayout = 1;
...@@ -109,8 +110,7 @@ static int mixcomwd_open(struct inode *inode, struct file *file) ...@@ -109,8 +110,7 @@ static int mixcomwd_open(struct inode *inode, struct file *file)
static int mixcomwd_release(struct inode *inode, struct file *file) static int mixcomwd_release(struct inode *inode, struct file *file)
{ {
if (expect_close) {
if (!nowayout) {
if(mixcomwd_timer_alive) { if(mixcomwd_timer_alive) {
printk(KERN_ERR "mixcomwd: release called while internal timer alive"); printk(KERN_ERR "mixcomwd: release called while internal timer alive");
return -EBUSY; return -EBUSY;
...@@ -121,7 +121,10 @@ static int mixcomwd_release(struct inode *inode, struct file *file) ...@@ -121,7 +121,10 @@ static int mixcomwd_release(struct inode *inode, struct file *file)
mixcomwd_timer.data=0; mixcomwd_timer.data=0;
mixcomwd_timer_alive=1; mixcomwd_timer_alive=1;
add_timer(&mixcomwd_timer); add_timer(&mixcomwd_timer);
} else {
printk(KERN_CRIT "mixcomwd: WDT device closed unexpectedly. WDT will not stop!\n");
} }
clear_bit(0,&mixcomwd_opened); clear_bit(0,&mixcomwd_opened);
return 0; return 0;
} }
...@@ -135,6 +138,20 @@ static ssize_t mixcomwd_write(struct file *file, const char *data, size_t len, l ...@@ -135,6 +138,20 @@ static ssize_t mixcomwd_write(struct file *file, const char *data, size_t len, l
if(len) if(len)
{ {
if (!nowayout) {
size_t i;
/* In case it was set long ago */
expect_close = 0;
for (i = 0; i != len; i++) {
char c;
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V')
expect_close = 1;
}
}
mixcomwd_ping(); mixcomwd_ping();
return 1; return 1;
} }
...@@ -146,7 +163,9 @@ static int mixcomwd_ioctl(struct inode *inode, struct file *file, ...@@ -146,7 +163,9 @@ static int mixcomwd_ioctl(struct inode *inode, struct file *file,
{ {
int status; int status;
static struct watchdog_info ident = { static struct watchdog_info ident = {
WDIOF_KEEPALIVEPING, 1, "MixCOM watchdog" .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "MixCOM watchdog"
}; };
switch(cmd) switch(cmd)
......
...@@ -40,6 +40,8 @@ ...@@ -40,6 +40,8 @@
* fairly useless proc entry. * fairly useless proc entry.
* 990610 removed said useless proc code for the merge <alan> * 990610 removed said useless proc code for the merge <alan>
* 000403 Removed last traces of proc code. <davej> * 000403 Removed last traces of proc code. <davej>
* 011214 Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT <Matt_Domsch@dell.com>
* Added timeout module option to override default
*/ */
#include <linux/module.h> #include <linux/module.h>
...@@ -76,7 +78,7 @@ ...@@ -76,7 +78,7 @@
*/ */
static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 }; static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 };
#define WD_VER "1.10 (06/05/99)" #define WD_VER "1.12 (12/14/2001)"
/* /*
* It should be noted that PCWD_REVISION_B was removed because A and B * It should be noted that PCWD_REVISION_B was removed because A and B
...@@ -88,7 +90,23 @@ static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 }; ...@@ -88,7 +90,23 @@ static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 };
#define PCWD_REVISION_A 1 #define PCWD_REVISION_A 1
#define PCWD_REVISION_C 2 #define PCWD_REVISION_C 2
#define WD_TIMEOUT 3 /* 1 1/2 seconds for a timeout */ #define WD_TIMEOUT 4 /* 2 seconds for a timeout */
static int timeout_val = WD_TIMEOUT;
static int timeout = 2;
static int expect_close = 0;
MODULE_PARM(timeout,"i");
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default=2)");
#ifdef CONFIG_WATCHDOG_NOWAYOUT
static int nowayout = 1;
#else
static int nowayout = 0;
#endif
MODULE_PARM(nowayout,"i");
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
/* /*
* These are the defines for the PC Watchdog card, revision A. * These are the defines for the PC Watchdog card, revision A.
...@@ -121,7 +139,7 @@ static int __init pcwd_checkcard(void) ...@@ -121,7 +139,7 @@ static int __init pcwd_checkcard(void)
if (prev_card_dat == 0xFF) if (prev_card_dat == 0xFF)
return 0; return 0;
while(count < WD_TIMEOUT) { while(count < timeout_val) {
/* Read the raw card data from the port, and strip off the /* Read the raw card data from the port, and strip off the
first 4 bits */ first 4 bits */
...@@ -256,7 +274,7 @@ static int pcwd_ioctl(struct inode *inode, struct file *file, ...@@ -256,7 +274,7 @@ static int pcwd_ioctl(struct inode *inode, struct file *file,
else else
cdat = inb(current_readport + 1 ); cdat = inb(current_readport + 1 );
spin_unlock(&io_lock); spin_unlock(&io_lock);
rv = 0; rv = WDIOF_MAGICCLOSE;
if (revision == PCWD_REVISION_A) if (revision == PCWD_REVISION_A)
{ {
...@@ -385,8 +403,22 @@ static ssize_t pcwd_write(struct file *file, const char *buf, size_t len, ...@@ -385,8 +403,22 @@ static ssize_t pcwd_write(struct file *file, const char *buf, size_t len,
if (ppos != &file->f_pos) if (ppos != &file->f_pos)
return -ESPIPE; return -ESPIPE;
if (len) if (len) {
{ if (!nowayout) {
size_t i;
/* In case it was set long ago */
expect_close = 0;
for (i = 0; i != len; i++) {
char c;
if (get_user(c, buf + i))
return -EFAULT;
if (c == 'V')
expect_close = 1;
}
}
pcwd_send_heartbeat(); pcwd_send_heartbeat();
return 1; return 1;
} }
...@@ -395,23 +427,21 @@ static ssize_t pcwd_write(struct file *file, const char *buf, size_t len, ...@@ -395,23 +427,21 @@ static ssize_t pcwd_write(struct file *file, const char *buf, size_t len,
static int pcwd_open(struct inode *ino, struct file *filep) static int pcwd_open(struct inode *ino, struct file *filep)
{ {
switch (minor(ino->i_rdev)) switch (minor(ino->i_rdev)) {
{
case WATCHDOG_MINOR: case WATCHDOG_MINOR:
if ( !atomic_dec_and_test(&open_allowed) ) if (!atomic_dec_and_test(&open_allowed) ) {
{
atomic_inc( &open_allowed ); atomic_inc( &open_allowed );
return -EBUSY; return -EBUSY;
} }
MOD_INC_USE_COUNT; MOD_INC_USE_COUNT;
/* Enable the port */ /* Enable the port */
if (revision == PCWD_REVISION_C) if (revision == PCWD_REVISION_C) {
{
spin_lock(&io_lock); spin_lock(&io_lock);
outb_p(0x00, current_readport + 3); outb_p(0x00, current_readport + 3);
spin_unlock(&io_lock); spin_unlock(&io_lock);
} }
return(0); return(0);
case TEMP_MINOR: case TEMP_MINOR:
return(0); return(0);
default: default:
...@@ -448,9 +478,8 @@ static ssize_t pcwd_read(struct file *file, char *buf, size_t count, ...@@ -448,9 +478,8 @@ static ssize_t pcwd_read(struct file *file, char *buf, size_t count,
static int pcwd_close(struct inode *ino, struct file *filep) static int pcwd_close(struct inode *ino, struct file *filep)
{ {
if (minor(ino->i_rdev)==WATCHDOG_MINOR) if (minor(ino->i_rdev)==WATCHDOG_MINOR) {
{ if (expect_close) {
#ifndef CONFIG_WATCHDOG_NOWAYOUT
/* Disable the board */ /* Disable the board */
if (revision == PCWD_REVISION_C) { if (revision == PCWD_REVISION_C) {
spin_lock(&io_lock); spin_lock(&io_lock);
...@@ -459,7 +488,7 @@ static int pcwd_close(struct inode *ino, struct file *filep) ...@@ -459,7 +488,7 @@ static int pcwd_close(struct inode *ino, struct file *filep)
spin_unlock(&io_lock); spin_unlock(&io_lock);
} }
atomic_inc( &open_allowed ); atomic_inc( &open_allowed );
#endif }
} }
return 0; return 0;
} }
...@@ -560,9 +589,15 @@ static struct miscdevice temp_miscdev = { ...@@ -560,9 +589,15 @@ static struct miscdevice temp_miscdev = {
&pcwd_fops &pcwd_fops
}; };
static void __init pcwd_validate_timeout(void)
{
timeout_val = timeout * 2;
}
static int __init pcwatchdog_init(void) static int __init pcwatchdog_init(void)
{ {
int i, found = 0; int i, found = 0;
pcwd_validate_timeout();
spin_lock_init(&io_lock); spin_lock_init(&io_lock);
revision = PCWD_REVISION_A; revision = PCWD_REVISION_A;
......
...@@ -244,9 +244,9 @@ static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ...@@ -244,9 +244,9 @@ static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
{ {
static struct watchdog_info ident= static struct watchdog_info ident=
{ {
0, .options = WDIOF_MAGICCLOSE,
1, .firmware_version = 1,
"SB60xx" .identity = "SB60xx"
}; };
switch(cmd) switch(cmd)
......
This diff is collapsed.
/*
* AMD Elan SC520 processor Watchdog Timer driver for Linux 2.4.x
*
* Based on acquirewdt.c by Alan Cox,
* and sbc60xxwdt.c by Jakob Oestergaard <jakob@ostenfeld.dk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* The authors do NOT admit liability nor provide warranty for
* any of this software. This material is provided "AS-IS" in
* the hope that it may be useful for others.
*
* (c) Copyright 2001 Scott Jennings <linuxdrivers@oro.net>
* 9/27 - 2001 [Initial release]
*
* Additional fixes Alan Cox
* - Fixed formatting
* - Removed debug printks
* - Fixed SMP built kernel deadlock
* - Switched to private locks not lock_kernel
* - Used ioremap/writew/readw
* - Added NOWAYOUT support
*
* Theory of operation:
* A Watchdog Timer (WDT) is a hardware circuit that can
* reset the computer system in case of a software fault.
* You probably knew that already.
*
* Usually a userspace daemon will notify the kernel WDT driver
* via the /proc/watchdog special device file that userspace is
* still alive, at regular intervals. When such a notification
* occurs, the driver will usually tell the hardware watchdog
* that everything is in order, and that the watchdog should wait
* for yet another little while to reset the system.
* If userspace fails (RAM error, kernel bug, whatever), the
* notifications cease to occur, and the hardware watchdog will
* reset the system (causing a reboot) after the timeout occurs.
*
* This WDT driver is different from most other Linux WDT
* drivers in that the driver will ping the watchdog by itself,
* because this particular WDT has a very short timeout (1.6
* seconds) and it would be insane to count on any userspace
* daemon always getting scheduled within that time frame.
*
* This driver uses memory mapped IO, and spinlock.
*/
#include <linux/module.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/sched.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/fcntl.h>
#include <linux/smp_lock.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/init.h>
/*
* The SC520 can timeout anywhere from 492us to 32.21s.
* If we reset the watchdog every ~250ms we should be safe.
*/
#define WDT_INTERVAL (HZ/4+1)
/*
* We must not require too good response from the userspace daemon.
* Here we require the userspace daemon to send us a heartbeat
* char to /dev/watchdog every 30 seconds.
*/
#define WDT_HEARTBEAT (HZ * 30)
/*
* AMD Elan SC520 timeout value is 492us times a power of 2 (0-7)
*
* 0: 492us 2: 1.01s 4: 4.03s 6: 16.22s
* 1: 503ms 3: 2.01s 5: 8.05s 7: 32.21s
*/
#define TIMEOUT_EXPONENT ( 1 << 3 ) /* 0x08 = 2.01s */
/* #define MMCR_BASE_DEFAULT 0xfffef000 */
#define MMCR_BASE_DEFAULT ((__u16 *)0xffffe)
#define OFFS_WDTMRCTL ((unsigned int)0xcb0)
#define WDT_ENB 0x8000 /* [15] Watchdog Timer Enable */
#define WDT_WRST_ENB 0x4000 /* [14] Watchdog Timer Reset Enable */
#define OUR_NAME "sc520_wdt"
#define WRT_DOG(data) *wdtmrctl=data
static __u16 *wdtmrctl;
static void wdt_timer_ping(unsigned long);
static struct timer_list timer;
static unsigned long next_heartbeat;
static unsigned long wdt_is_open;
static int wdt_expect_close;
#ifdef CONFIG_WATCHDOG_NOWAYOUT
static int nowayout = 1;
#else
static int nowayout = 0;
#endif
MODULE_PARM(nowayout,"i");
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
static spinlock_t wdt_spinlock;
/*
* Whack the dog
*/
static void wdt_timer_ping(unsigned long data)
{
/* If we got a heartbeat pulse within the WDT_US_INTERVAL
* we agree to ping the WDT
*/
if(time_before(jiffies, next_heartbeat))
{
/* Ping the WDT */
spin_lock(&wdt_spinlock);
writew(0xAAAA, wdtmrctl);
writew(0x5555, wdtmrctl);
spin_unlock(&wdt_spinlock);
/* Re-set the timer interval */
timer.expires = jiffies + WDT_INTERVAL;
add_timer(&timer);
} else {
printk(OUR_NAME ": Heartbeat lost! Will not ping the watchdog\n");
}
}
/*
* Utility routines
*/
static void wdt_config(int writeval)
{
__u16 dummy;
unsigned long flags;
/* buy some time (ping) */
spin_lock_irqsave(&wdt_spinlock, flags);
dummy=readw(wdtmrctl); /* ensure write synchronization */
writew(0xAAAA, wdtmrctl);
writew(0x5555, wdtmrctl);
/* make WDT configuration register writable one time */
writew(0x3333, wdtmrctl);
writew(0xCCCC, wdtmrctl);
/* write WDT configuration register */
writew(writeval, wdtmrctl);
spin_unlock_irqrestore(&wdt_spinlock, flags);
}
static void wdt_startup(void)
{
next_heartbeat = jiffies + WDT_HEARTBEAT;
/* Start the timer */
timer.expires = jiffies + WDT_INTERVAL;
add_timer(&timer);
wdt_config(WDT_ENB | WDT_WRST_ENB | TIMEOUT_EXPONENT);
printk(OUR_NAME ": Watchdog timer is now enabled.\n");
}
static void wdt_turnoff(void)
{
if (!nowayout) {
/* Stop the timer */
del_timer(&timer);
wdt_config(0);
printk(OUR_NAME ": Watchdog timer is now disabled...\n");
}
}
/*
* /dev/watchdog handling
*/
static ssize_t fop_write(struct file * file, const char * buf, size_t count, loff_t * ppos)
{
/* We can't seek */
if(ppos != &file->f_pos)
return -ESPIPE;
/* See if we got the magic character */
if(count)
{
size_t ofs;
/* note: just in case someone wrote the magic character
* five months ago... */
wdt_expect_close = 0;
/* now scan */
for(ofs = 0; ofs != count; ofs++) {
char c;
if (get_user(c, buf + ofs))
return -EFAULT;
if(c == 'V')
wdt_expect_close = 1;
}
/* Well, anyhow someone wrote to us, we should return that favour */
next_heartbeat = jiffies + WDT_HEARTBEAT;
return 1;
}
return 0;
}
static int fop_open(struct inode * inode, struct file * file)
{
switch(minor(inode->i_rdev))
{
case WATCHDOG_MINOR:
/* Just in case we're already talking to someone... */
if(test_and_set_bit(0, &wdt_is_open))
return -EBUSY;
/* Good, fire up the show */
wdt_startup();
if (nowayout)
MOD_INC_USE_COUNT;
return 0;
default:
return -ENODEV;
}
}
static int fop_close(struct inode * inode, struct file * file)
{
if(minor(inode->i_rdev) == WATCHDOG_MINOR)
{
if(wdt_expect_close)
wdt_turnoff();
else {
del_timer(&timer);
printk(OUR_NAME ": device file closed unexpectedly. Will not stop the WDT!\n");
}
}
clear_bit(0, &wdt_is_open);
return 0;
}
static long long fop_llseek(struct file *file, long long offset, int origin)
{
return -ESPIPE;
}
static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
static struct watchdog_info ident=
{
.options = WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "SC520"
};
switch(cmd)
{
default:
return -ENOIOCTLCMD;
case WDIOC_GETSUPPORT:
return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))?-EFAULT:0;
case WDIOC_KEEPALIVE:
next_heartbeat = jiffies + WDT_HEARTBEAT;
return 0;
}
}
static struct file_operations wdt_fops = {
owner: THIS_MODULE,
llseek: fop_llseek,
write: fop_write,
open: fop_open,
release: fop_close,
ioctl: fop_ioctl
};
static struct miscdevice wdt_miscdev = {
WATCHDOG_MINOR,
"watchdog",
&wdt_fops
};
/*
* Notifier for system down
*/
static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
void *unused)
{
if(code==SYS_DOWN || code==SYS_HALT)
wdt_turnoff();
return NOTIFY_DONE;
}
/*
* The WDT needs to learn about soft shutdowns in order to
* turn the timebomb registers off.
*/
static struct notifier_block wdt_notifier=
{
wdt_notify_sys,
0,
0
};
static void __exit sc520_wdt_unload(void)
{
wdt_turnoff();
/* Deregister */
misc_deregister(&wdt_miscdev);
iounmap(wdtmrctl);
unregister_reboot_notifier(&wdt_notifier);
}
static int __init sc520_wdt_init(void)
{
int rc = -EBUSY;
unsigned long cbar;
spin_lock_init(&wdt_spinlock);
init_timer(&timer);
timer.function = wdt_timer_ping;
timer.data = 0;
rc = misc_register(&wdt_miscdev);
if (rc)
goto err_out_region2;
rc = register_reboot_notifier(&wdt_notifier);
if (rc)
goto err_out_miscdev;
/* get the Base Address Register */
cbar = inl_p(0xfffc);
printk(OUR_NAME ": CBAR: 0x%08lx\n", cbar);
/* check if MMCR aliasing bit is set */
if (cbar & 0x80000000) {
printk(OUR_NAME ": MMCR Aliasing enabled.\n");
wdtmrctl = (__u16 *)(cbar & 0x3fffffff);
} else {
printk(OUR_NAME "!!! WARNING !!!\n"
"\t MMCR Aliasing found NOT enabled!\n"
"\t Using default value of: %p\n"
"\t This has not been tested!\n"
"\t Please email Scott Jennings <smj@oro.net>\n"
"\t and Bill Jennings <bj@oro.net> if it works!\n"
, MMCR_BASE_DEFAULT
);
wdtmrctl = MMCR_BASE_DEFAULT;
}
wdtmrctl = (__u16 *)((char *)wdtmrctl + OFFS_WDTMRCTL);
wdtmrctl = ioremap((unsigned long)wdtmrctl, 2);
printk(KERN_INFO OUR_NAME ": WDT driver for SC520 initialised.\n");
return 0;
err_out_miscdev:
misc_deregister(&wdt_miscdev);
err_out_region2:
return rc;
}
module_init(sc520_wdt_init);
module_exit(sc520_wdt_unload);
MODULE_AUTHOR("Scott and Bill Jennings");
MODULE_DESCRIPTION("Driver for watchdog timer in AMD \"Elan\" SC520 uProcessor");
MODULE_LICENSE("GPL");
EXPORT_NO_SYMBOLS;
...@@ -334,21 +334,21 @@ static struct file_operations sh_wdt_fops = { ...@@ -334,21 +334,21 @@ static struct file_operations sh_wdt_fops = {
}; };
static struct watchdog_info sh_wdt_info = { static struct watchdog_info sh_wdt_info = {
WDIOF_KEEPALIVEPING, .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
1, .firmware_version = 1,
"SH WDT", .identity = "SH WDT",
}; };
static struct notifier_block sh_wdt_notifier = { static struct notifier_block sh_wdt_notifier = {
sh_wdt_notify_sys, .notifier_call = sh_wdt_notify_sys,
NULL, .next = NULL,
0 .priority = 0
}; };
static struct miscdevice sh_wdt_miscdev = { static struct miscdevice sh_wdt_miscdev = {
WATCHDOG_MINOR, .minor = WATCHDOG_MINOR,
"watchdog", .name = "watchdog",
&sh_wdt_fops, .fops &sh_wdt_fops,
}; };
/** /**
......
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
#define TIMER_MARGIN 60 /* (secs) Default is 1 minute */ #define TIMER_MARGIN 60 /* (secs) Default is 1 minute */
static int expect_close = 0;
static int soft_margin = TIMER_MARGIN; /* in seconds */ static int soft_margin = TIMER_MARGIN; /* in seconds */
MODULE_PARM(soft_margin,"i"); MODULE_PARM(soft_margin,"i");
...@@ -114,6 +115,8 @@ static int softdog_release(struct inode *inode, struct file *file) ...@@ -114,6 +115,8 @@ static int softdog_release(struct inode *inode, struct file *file)
*/ */
if(!nowayout) { if(!nowayout) {
del_timer(&watchdog_ticktock); del_timer(&watchdog_ticktock);
} else {
printk(KERN_CRIT "SOFTDOG: WDT device closed unexpectedly. WDT will not stop!\n");
} }
timer_alive=0; timer_alive=0;
return 0; return 0;
...@@ -129,6 +132,21 @@ static ssize_t softdog_write(struct file *file, const char *data, size_t len, lo ...@@ -129,6 +132,21 @@ static ssize_t softdog_write(struct file *file, const char *data, size_t len, lo
* Refresh the timer. * Refresh the timer.
*/ */
if(len) { if(len) {
if (!nowayout) {
size_t i;
/* In case it was set long ago */
expect_close = 0;
for (i = 0; i != len; i++) {
char c;
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V')
expect_close = 1;
}
}
mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ)); mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ));
return 1; return 1;
} }
...@@ -139,6 +157,7 @@ static int softdog_ioctl(struct inode *inode, struct file *file, ...@@ -139,6 +157,7 @@ static int softdog_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
static struct watchdog_info ident = { static struct watchdog_info ident = {
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
.identity = "Software Watchdog", .identity = "Software Watchdog",
}; };
switch (cmd) { switch (cmd) {
......
...@@ -251,9 +251,9 @@ static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ...@@ -251,9 +251,9 @@ static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
{ {
static struct watchdog_info ident= static struct watchdog_info ident=
{ {
0, .options = WDIOF_MAGICCLOSE,
1, .firmware_version = 1,
"W83877F" .identity = "W83877F"
}; };
switch(cmd) switch(cmd)
......
/*
* ICP Wafer 5823 Single Board Computer WDT driver for Linux 2.4.x
* http://www.icpamerica.com/wafer_5823.php
* May also work on other similar models
*
* (c) Copyright 2002 Justin Cormack <justin@street-vision.com>
*
* Release 0.02
*
* Based on advantechwdt.c which is based on wdt.c.
* Original copyright messages:
*
* (c) Copyright 1996-1997 Alan Cox <alan@redhat.com>, All Rights Reserved.
* http://www.redhat.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
* warranty for any of this software. This material is provided
* "AS-IS" and at no charge.
*
* (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/init.h>
#include <linux/spinlock.h>
static unsigned long wafwdt_is_open;
static spinlock_t wafwdt_lock;
static int expect_close = 0;
/*
* You must set these - there is no sane way to probe for this board.
*
* To enable, write the timeout value in seconds (1 to 255) to I/O
* port WDT_START, then read the port to start the watchdog. To pat
* the dog, read port WDT_STOP to stop the timer, then read WDT_START
* to restart it again.
*/
#define WDT_START 0x443
#define WDT_STOP 0x843
#define WD_TIMO 60 /* 1 minute */
static int wd_margin = WD_TIMO;
#ifdef CONFIG_WATCHDOG_NOWAYOUT
static int nowayout = 1;
#else
static int nowayout = 0;
#endif
MODULE_PARM(nowayout,"i");
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
static void wafwdt_ping(void)
{
/* pat watchdog */
spin_lock(&wafwdt_lock);
inb_p(WDT_STOP);
inb_p(WDT_START);
spin_unlock(&wafwdt_lock);
}
static void wafwdt_start(void)
{
/* start up watchdog */
outb_p(wd_margin, WDT_START);
inb_p(WDT_START);
}
static void
wafwdt_stop(void)
{
/* stop watchdog */
inb_p(WDT_STOP);
}
static ssize_t wafwdt_write(struct file *file, const char *buf, size_t count, loff_t * ppos)
{
/* Can't seek (pwrite) on this device */
if (ppos != &file->f_pos)
return -ESPIPE;
if (count) {
if (!nowayout) {
size_t i;
/* In case it was set long ago */
expect_close = 0;
for (i = 0; i != count; i++) {
char c;
if (get_user(c, buf + i))
return -EFAULT;
if (c == 'V')
expect_close = 1;
}
}
wafwdt_ping();
return 1;
}
return 0;
}
static int wafwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
int new_margin;
static struct watchdog_info ident = {
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "Wafer 5823 WDT"
};
int one=1;
switch (cmd) {
case WDIOC_GETSUPPORT:
if (copy_to_user
((struct watchdog_info *) arg, &ident, sizeof (ident)))
return -EFAULT;
break;
case WDIOC_GETSTATUS:
if (copy_to_user((int *) arg, &one, sizeof (int)))
return -EFAULT;
break;
case WDIOC_KEEPALIVE:
wafwdt_ping();
break;
case WDIOC_SETTIMEOUT:
if (get_user(new_margin, (int *)arg))
return -EFAULT;
if ((new_margin < 1) || (new_margin > 255))
return -EINVAL;
wd_margin = new_margin;
wafwdt_stop();
wafwdt_start();
/* Fall */
case WDIOC_GETTIMEOUT:
return put_user(wd_margin, (int *)arg);
default:
return -ENOTTY;
}
return 0;
}
static int wafwdt_open(struct inode *inode, struct file *file)
{
if (test_and_set_bit(0, &wafwdt_is_open))
return -EBUSY;
wafwdt_start();
return 0;
}
static int
wafwdt_close(struct inode *inode, struct file *file)
{
clear_bit(0, &wafwdt_is_open);
if (expect_close) {
wafwdt_stop();
} else {
printk(KERN_CRIT "WDT device closed unexpectedly. WDT will not stop!\n");
}
return 0;
}
/*
* Notifier for system down
*/
static int wafwdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused)
{
if (code == SYS_DOWN || code == SYS_HALT) {
/* Turn the WDT off */
wafwdt_stop();
}
return NOTIFY_DONE;
}
/*
* Kernel Interfaces
*/
static struct file_operations wafwdt_fops = {
owner:THIS_MODULE,
write:wafwdt_write,
ioctl:wafwdt_ioctl,
open:wafwdt_open,
release:wafwdt_close,
};
static struct miscdevice wafwdt_miscdev = {
WATCHDOG_MINOR,
"watchdog",
&wafwdt_fops
};
/*
* The WDT needs to learn about soft shutdowns in order to
* turn the timebomb registers off.
*/
static struct notifier_block wafwdt_notifier = {
wafwdt_notify_sys,
NULL,
0
};
static int __init wafwdt_init(void)
{
printk(KERN_INFO "WDT driver for Wafer 5823 single board computer initialising.\n");
spin_lock_init(&wafwdt_lock);
if(!request_region(WDT_STOP, 1, "Wafer 5823 WDT"))
goto error;
if(!request_region(WDT_START, 1, "Wafer 5823 WDT"))
goto error2;
if(misc_register(&wafwdt_miscdev)<0)
goto error3;
register_reboot_notifier(&wafwdt_notifier);
return 0;
error3:
release_region(WDT_START, 1);
error2:
release_region(WDT_STOP, 1);
error:
return -ENODEV;
}
static void __exit wafwdt_exit(void)
{
misc_deregister(&wafwdt_miscdev);
unregister_reboot_notifier(&wafwdt_notifier);
release_region(WDT_STOP, 1);
release_region(WDT_START, 1);
}
module_init(wafwdt_init);
module_exit(wafwdt_exit);
MODULE_AUTHOR("Justin Cormack");
MODULE_LICENSE("GPL");
EXPORT_NO_SYMBOLS;
/* end of wafer5823wdt.c */
...@@ -52,6 +52,7 @@ ...@@ -52,6 +52,7 @@
#include <asm/system.h> #include <asm/system.h>
static unsigned long wdt_is_open; static unsigned long wdt_is_open;
static int expect_close;
/* /*
* You must set these - there is no sane way to probe for this board. * You must set these - there is no sane way to probe for this board.
...@@ -258,8 +259,21 @@ static ssize_t wdt_write(struct file *file, const char *buf, size_t count, loff_ ...@@ -258,8 +259,21 @@ static ssize_t wdt_write(struct file *file, const char *buf, size_t count, loff_
if (ppos != &file->f_pos) if (ppos != &file->f_pos)
return -ESPIPE; return -ESPIPE;
if(count) if(count) {
{ if (!nowayout) {
size_t i;
/* In case it was set long ago */
expect_close = 0;
for (i = 0; i != count; i++) {
char c;
if (get_user(c, buf + i))
return -EFAULT;
if (c == 'V')
expect_close = 1;
}
}
wdt_ping(); wdt_ping();
return 1; return 1;
} }
...@@ -317,10 +331,11 @@ static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ...@@ -317,10 +331,11 @@ static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
{ {
static struct watchdog_info ident= static struct watchdog_info ident=
{ {
WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER .options = WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER
|WDIOF_EXTERN1|WDIOF_EXTERN2|WDIOF_FANFAULT, |WDIOF_EXTERN1|WDIOF_EXTERN2|WDIOF_FANFAULT
1, |WDIOF_SETTIMEOUT|WDIOF_MAGICCLOSE,
"WDT500/501" .firmware_version = 1,
.identity = "WDT500/501"
}; };
ident.options&=WDT_OPTION_MASK; /* Mask down to the card we have */ ident.options&=WDT_OPTION_MASK; /* Mask down to the card we have */
...@@ -399,9 +414,11 @@ static int wdt_release(struct inode *inode, struct file *file) ...@@ -399,9 +414,11 @@ static int wdt_release(struct inode *inode, struct file *file)
{ {
if(minor(inode->i_rdev)==WATCHDOG_MINOR) if(minor(inode->i_rdev)==WATCHDOG_MINOR)
{ {
if (!nowayout) { if (expect_close) {
inb_p(WDT_DC); /* Disable counters */ inb_p(WDT_DC); /* Disable counters */
wdt_ctr_load(2,0); /* 0 length reset pulses now */ wdt_ctr_load(2,0); /* 0 length reset pulses now */
} else {
printk(KERN_CRIT "wdt: WDT device closed unexpectedly. WDT will not stop!\n");
} }
clear_bit(0, &wdt_is_open); clear_bit(0, &wdt_is_open);
} }
......
...@@ -41,6 +41,7 @@ static int timeout = DEFAULT_TIMEOUT*60; /* TO in seconds from user */ ...@@ -41,6 +41,7 @@ static int timeout = DEFAULT_TIMEOUT*60; /* TO in seconds from user */
static int timeoutM = DEFAULT_TIMEOUT; /* timeout in minutes */ static int timeoutM = DEFAULT_TIMEOUT; /* timeout in minutes */
static unsigned long timer_alive; static unsigned long timer_alive;
static int testmode; static int testmode;
static int expect_close = 0;
MODULE_PARM(timeout, "i"); MODULE_PARM(timeout, "i");
MODULE_PARM_DESC(timeout,"Watchdog timeout in seconds (60..15300), default=60"); MODULE_PARM_DESC(timeout,"Watchdog timeout in seconds (60..15300), default=60");
...@@ -196,6 +197,8 @@ static int wdt977_release(struct inode *inode, struct file *file) ...@@ -196,6 +197,8 @@ static int wdt977_release(struct inode *inode, struct file *file)
clear_bit(0,&timer_alive); clear_bit(0,&timer_alive);
printk(KERN_INFO "Wdt977 Watchdog: shutdown\n"); printk(KERN_INFO "Wdt977 Watchdog: shutdown\n");
} else {
printk(KERN_CRIT "WDT device closed unexpectedly. WDT will not stop!\n");
} }
return 0; return 0;
} }
...@@ -220,6 +223,21 @@ static ssize_t wdt977_write(struct file *file, const char *buf, size_t count, lo ...@@ -220,6 +223,21 @@ static ssize_t wdt977_write(struct file *file, const char *buf, size_t count, lo
if(count) if(count)
{ {
if (!nowayout) {
size_t i;
/* In case it was set long ago */
expect_close = 0;
for (i = 0; i != len; i++) {
char c;
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V')
expect_close = 1;
}
}
kick_wdog(); kick_wdog();
return 1; return 1;
} }
......
...@@ -73,6 +73,7 @@ ...@@ -73,6 +73,7 @@
#endif #endif
static unsigned long wdt_is_open; static unsigned long wdt_is_open;
static int expect_close = 0;
/* /*
* You must set these - there is no sane way to probe for this board. * You must set these - there is no sane way to probe for this board.
...@@ -276,6 +277,16 @@ static ssize_t wdtpci_write(struct file *file, const char *buf, size_t count, lo ...@@ -276,6 +277,16 @@ static ssize_t wdtpci_write(struct file *file, const char *buf, size_t count, lo
if(count) if(count)
{ {
if (!nowayout) {
size_t i;
for (i = 0; i != count; i++) {
char c;
if(get_user(c, buf+i))
return -EFAULT;
if (c == 'V')
expect_close = 1;
}
}
wdtpci_ping(); wdtpci_ping();
return 1; return 1;
} }
...@@ -334,7 +345,8 @@ static int wdtpci_ioctl(struct inode *inode, struct file *file, unsigned int cmd ...@@ -334,7 +345,8 @@ static int wdtpci_ioctl(struct inode *inode, struct file *file, unsigned int cmd
static struct watchdog_info ident = { static struct watchdog_info ident = {
.options = WDIOF_OVERHEAT | WDIOF_POWERUNDER | .options = WDIOF_OVERHEAT | WDIOF_POWERUNDER |
WDIOF_POWEROVER | WDIOF_EXTERN1 | WDIOF_POWEROVER | WDIOF_EXTERN1 |
WDIOF_EXTERN2 | WDIOF_FANFAULT, WDIOF_EXTERN2 | WDIOF_FANFAULT |
WDIOF_SETTIMEOUT|WDIOF_MAGICCLOSE,
.firmware_version = 1, .firmware_version = 1,
.identity = "WDT500/501PCI", .identity = "WDT500/501PCI",
}; };
......
...@@ -39,6 +39,7 @@ struct watchdog_info { ...@@ -39,6 +39,7 @@ struct watchdog_info {
#define WDIOF_CARDRESET 0x0020 /* Card previously reset the CPU */ #define WDIOF_CARDRESET 0x0020 /* Card previously reset the CPU */
#define WDIOF_POWEROVER 0x0040 /* Power over voltage */ #define WDIOF_POWEROVER 0x0040 /* Power over voltage */
#define WDIOF_SETTIMEOUT 0x0080 /* Set timeout (in seconds) */ #define WDIOF_SETTIMEOUT 0x0080 /* Set timeout (in seconds) */
#define WDIOF_MAGICCLOSE 0x0100 /* Supports magic close char */
#define WDIOF_KEEPALIVEPING 0x8000 /* Keep alive ping reply */ #define WDIOF_KEEPALIVEPING 0x8000 /* Keep alive ping reply */
#define WDIOS_DISABLECARD 0x0001 /* Turn off the watchdog timer */ #define WDIOS_DISABLECARD 0x0001 /* Turn off the watchdog timer */
......
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