Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@home.transmeta.com>2003-01-09 18:14:43 -0800
committerLinus Torvalds <torvalds@home.transmeta.com>2003-01-09 18:14:43 -0800
commitbdc910332cd5cc406e0ced072fe282b94d2b6595 (patch)
tree7406cbd439fa3df37adf48a7e46ff7de4841a406
parent9cb52295208d6a1b07cf1f6c2ec240f89179a589 (diff)
parent59409421c889f855c1daccef6f53dfc0e9a62129 (diff)
Merge bk://linux-dj.bkbits.net/watchdog
into home.transmeta.com:/home/torvalds/v2.5/linux
-rw-r--r--drivers/char/watchdog/Kconfig20
-rw-r--r--drivers/char/watchdog/Makefile5
-rw-r--r--drivers/char/watchdog/acquirewdt.c96
-rw-r--r--drivers/char/watchdog/advantechwdt.c80
-rw-r--r--drivers/char/watchdog/alim7101_wdt.c345
-rw-r--r--drivers/char/watchdog/eurotechwdt.c516
-rw-r--r--drivers/char/watchdog/i810-tco.c4
-rw-r--r--drivers/char/watchdog/ib700wdt.c74
-rw-r--r--drivers/char/watchdog/indydog.c182
-rw-r--r--drivers/char/watchdog/machzwd.c17
-rw-r--r--drivers/char/watchdog/mixcomwd.c27
-rw-r--r--drivers/char/watchdog/pcwd.c111
-rw-r--r--drivers/char/watchdog/sbc60xxwdt.c6
-rw-r--r--drivers/char/watchdog/sc1200wdt.c479
-rw-r--r--drivers/char/watchdog/sc520_wdt.c394
-rw-r--r--drivers/char/watchdog/shwdt.c18
-rw-r--r--drivers/char/watchdog/softdog.c21
-rw-r--r--drivers/char/watchdog/w83877f_wdt.c6
-rw-r--r--drivers/char/watchdog/wafer5823wdt.c262
-rw-r--r--drivers/char/watchdog/wdt.c31
-rw-r--r--drivers/char/watchdog/wdt977.c18
-rw-r--r--drivers/char/watchdog/wdt_pci.c18
-rw-r--r--include/linux/watchdog.h1
23 files changed, 2293 insertions, 438 deletions
diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig
index d329cdc5dfb9..7825533927bc 100644
--- a/drivers/char/watchdog/Kconfig
+++ b/drivers/char/watchdog/Kconfig
@@ -283,4 +283,24 @@ config MACHZ_WDT
The module is called machzwd.o. If you want to compile it as a
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
diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile
index cdd69eb962ad..51d904e418f4 100644
--- a/drivers/char/watchdog/Makefile
+++ b/drivers/char/watchdog/Makefile
@@ -23,3 +23,8 @@ obj-$(CONFIG_MACHZ_WDT) += machzwd.o
obj-$(CONFIG_SH_WDT) += shwdt.o
obj-$(CONFIG_EUROTECH_WDT) += eurotechwdt.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
diff --git a/drivers/char/watchdog/acquirewdt.c b/drivers/char/watchdog/acquirewdt.c
index 3a0ecbcf2033..b509fb731454 100644
--- a/drivers/char/watchdog/acquirewdt.c
+++ b/drivers/char/watchdog/acquirewdt.c
@@ -45,6 +45,7 @@
static int acq_is_open;
static spinlock_t acq_lock;
+static int expect_close = 0;
/*
* 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_
if (ppos != &file->f_pos)
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();
return 1;
}
@@ -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,
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)
@@ -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)
{
- switch(minor(inode->i_rdev))
- {
- case WATCHDOG_MINOR:
- spin_lock(&acq_lock);
- if(acq_is_open)
- {
- spin_unlock(&acq_lock);
- return -EBUSY;
- }
- if (nowayout) {
- MOD_INC_USE_COUNT;
- }
- /*
- * Activate
- */
-
- acq_is_open=1;
- inb_p(WDT_START);
+ if ((minor(inode->i_rdev) == WATCHDOG_MINOR)) {
+ spin_lock(&acq_lock);
+ if(acq_is_open) {
spin_unlock(&acq_lock);
- return 0;
- default:
- return -ENODEV;
+ return -EBUSY;
+ }
+ if (nowayout)
+ MOD_INC_USE_COUNT;
+
+ /* Activate */
+ acq_is_open=1;
+ inb_p(WDT_START);
+ spin_unlock(&acq_lock);
+ return 0;
+
+ } else {
+ return -ENODEV;
}
}
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);
- if (!nowayout) {
+ if (expect_close)
inb_p(WDT_STOP);
- }
+ else
+ printk(KERN_CRIT "WDT closed unexpectedly. WDT will not stop!\n");
+
acq_is_open=0;
spin_unlock(&acq_lock);
}
@@ -173,10 +184,9 @@ static int acq_notify_sys(struct notifier_block *this, unsigned long code,
void *unused)
{
if(code==SYS_DOWN || code==SYS_HALT)
- {
/* Turn the card off */
inb_p(WDT_STOP);
- }
+
return NOTIFY_DONE;
}
@@ -196,9 +206,9 @@ static struct file_operations acq_fops = {
static struct miscdevice acq_miscdev=
{
- WATCHDOG_MINOR,
- "watchdog",
- &acq_fops
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &acq_fops
};
@@ -207,11 +217,11 @@ static struct miscdevice acq_miscdev=
* turn the timebomb registers off.
*/
-static struct notifier_block acq_notifier=
+static struct notifier_block acq_notifier =
{
- acq_notify_sys,
- NULL,
- 0
+ .notifier_call = acq_notify_sys,
+ .next = NULL,
+ .priority = 0
};
static int __init acq_init(void)
@@ -221,17 +231,15 @@ static int __init acq_init(void)
spin_lock_init(&acq_lock);
if (misc_register(&acq_miscdev))
return -ENODEV;
- if (!request_region(WDT_STOP, 1, "Acquire WDT"))
- {
+ if (!request_region(WDT_STOP, 1, "Acquire WDT")) {
misc_deregister(&acq_miscdev);
return -EIO;
- }
- if (!request_region(WDT_START, 1, "Acquire WDT"))
- {
+ }
+ if (!request_region(WDT_START, 1, "Acquire WDT")) {
release_region(WDT_STOP, 1);
misc_deregister(&acq_miscdev);
return -EIO;
- }
+ }
register_reboot_notifier(&acq_notifier);
return 0;
diff --git a/drivers/char/watchdog/advantechwdt.c b/drivers/char/watchdog/advantechwdt.c
index 226e3a75d08a..f994c8fc5e69 100644
--- a/drivers/char/watchdog/advantechwdt.c
+++ b/drivers/char/watchdog/advantechwdt.c
@@ -47,6 +47,7 @@
#include <linux/smp_lock.h>
static int advwdt_is_open;
+static char adv_expect_close;
static spinlock_t advwdt_lock;
/*
@@ -99,10 +100,22 @@ advwdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
return -ESPIPE;
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();
- return 1;
}
- return 0;
+ return count;
}
static ssize_t
@@ -116,9 +129,11 @@ advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
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) {
case WDIOC_GETSUPPORT:
if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)))
@@ -143,26 +158,23 @@ advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
static int
advwdt_open(struct inode *inode, struct file *file)
{
- switch (minor(inode->i_rdev)) {
- case WATCHDOG_MINOR:
- spin_lock(&advwdt_lock);
- if (advwdt_is_open) {
- spin_unlock(&advwdt_lock);
- return -EBUSY;
- }
- if (nowayout) {
- MOD_INC_USE_COUNT;
- }
- /*
- * Activate
- */
-
- advwdt_is_open = 1;
- advwdt_ping();
+ if (minor(inode->i_rdev) == WATCHDOG_MINOR) {
+ spin_lock(&advwdt_lock);
+ if (advwdt_is_open) {
spin_unlock(&advwdt_lock);
- return 0;
- default:
- return -ENODEV;
+ return -EBUSY;
+ }
+
+ if (nowayout)
+ MOD_INC_USE_COUNT;
+
+ /* Activate */
+ advwdt_is_open = 1;
+ advwdt_ping();
+ spin_unlock(&advwdt_lock);
+ return 0;
+ } else {
+ return -ENODEV;
}
}
@@ -171,9 +183,9 @@ advwdt_close(struct inode *inode, struct file *file)
{
if (minor(inode->i_rdev) == WATCHDOG_MINOR) {
spin_lock(&advwdt_lock);
- if (!nowayout) {
+ if (!nowayout)
inb_p(WDT_STOP);
- }
+
advwdt_is_open = 0;
spin_unlock(&advwdt_lock);
}
@@ -188,10 +200,10 @@ static int
advwdt_notify_sys(struct notifier_block *this, unsigned long code,
void *unused)
{
- if (code == SYS_DOWN || code == SYS_HALT) {
+ if (code == SYS_DOWN || code == SYS_HALT)
/* Turn the WDT off */
inb_p(WDT_STOP);
- }
+
return NOTIFY_DONE;
}
@@ -209,9 +221,9 @@ static struct file_operations advwdt_fops = {
};
static struct miscdevice advwdt_miscdev = {
- WATCHDOG_MINOR,
- "watchdog",
- &advwdt_fops
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &advwdt_fops
};
/*
@@ -220,9 +232,9 @@ static struct miscdevice advwdt_miscdev = {
*/
static struct notifier_block advwdt_notifier = {
- advwdt_notify_sys,
- NULL,
- 0
+ .notifier_call = advwdt_notify_sys,
+ .next = NULL,
+ .priority = 0
};
static void __init
@@ -276,5 +288,3 @@ module_exit(advwdt_exit);
MODULE_LICENSE("GPL");
-/* end of advantechwdt.c */
-
diff --git a/drivers/char/watchdog/alim7101_wdt.c b/drivers/char/watchdog/alim7101_wdt.c
new file mode 100644
index 000000000000..653ae2d767fe
--- /dev/null
+++ b/drivers/char/watchdog/alim7101_wdt.c
@@ -0,0 +1,345 @@
+/*
+ * 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");
diff --git a/drivers/char/watchdog/eurotechwdt.c b/drivers/char/watchdog/eurotechwdt.c
index 6ce0e3c7dfed..46c015c372a9 100644
--- a/drivers/char/watchdog/eurotechwdt.c
+++ b/drivers/char/watchdog/eurotechwdt.c
@@ -7,23 +7,23 @@
* Based on wdt.c.
* Original copyright messages:
*
- * (c) Copyright 1996-1997 Alan Cox <alan@redhat.com>, All Rights Reserved.
- * http://www.redhat.com
+ * (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.
+ * 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.
+ * 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>*
+ * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
*
- * 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
- * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
- * Added timeout module option to override default
+ * 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
+ * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
+ * Added timeout module option to override default
*/
#include <linux/config.h>
@@ -49,17 +49,18 @@
static int eurwdt_is_open;
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 can use wdt=x,y to set these now.
+ * You must set these - there is no sane way to probe for this board.
+ * You can use wdt=x,y to set these now.
*/
static int io = 0x3f0;
static int irq = 10;
static char *ev = "int";
-#define WDT_TIMEOUT 60 /* 1 minute */
+#define WDT_TIMEOUT 60 /* 1 minute */
static int timeout = WDT_TIMEOUT;
MODULE_PARM(timeout,"i");
@@ -80,10 +81,10 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CON
#define WDT_CTRL_REG 0x30
#define WDT_OUTPIN_CFG 0xe2
- #define WDT_EVENT_INT 0x00
- #define WDT_EVENT_REBOOT 0x08
+#define WDT_EVENT_INT 0x00
+#define WDT_EVENT_REBOOT 0x08
#define WDT_UNIT_SEL 0xf1
- #define WDT_UNIT_SECS 0x80
+#define WDT_UNIT_SECS 0x80
#define WDT_TIMEOUT_VAL 0xf2
#define WDT_TIMER_CFG 0xf3
@@ -91,27 +92,27 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CON
#ifndef MODULE
/**
- * eurwdt_setup:
- * @str: command line string
+ * eurwdt_setup:
+ * @str: command line string
*
- * Setup options. The board isn't really probe-able so we have to
- * get the user to tell us the configuration. Sane people build it
- * modular but the others come here.
+ * Setup options. The board isn't really probe-able so we have to
+ * get the user to tell us the configuration. Sane people build it
+ * modular but the others come here.
*/
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) {
- io = ints[1];
- if (ints[0] > 1)
- irq = ints[2];
- }
+ if (ints[0] > 0) {
+ io = ints[1];
+ if (ints[0] > 1)
+ irq = ints[2];
+ }
- return 1;
+ return 1;
}
__setup("wdt=", eurwdt_setup);
@@ -127,7 +128,7 @@ MODULE_PARM_DESC(ev, "Eurotech WDT event type (default is `reboot')");
/*
- * Programming support
+ * Programming support
*/
static void __init eurwdt_validate_timeout(void)
@@ -135,265 +136,276 @@ static void __init eurwdt_validate_timeout(void)
if (timeout < 0 || timeout > 255) {
timeout = WDT_TIMEOUT;
printk(KERN_INFO "eurwdt: timeout must be 0 < x < 255, using %d\n",
- timeout);
+ timeout);
}
}
static inline void eurwdt_write_reg(u8 index, u8 data)
{
- outb(index, io);
- outb(data, io+1);
+ outb(index, io);
+ outb(data, io+1);
}
static inline void eurwdt_lock_chip(void)
{
- outb(0xaa, io);
+ outb(0xaa, io);
}
static inline void eurwdt_unlock_chip(void)
{
- outb(0x55, io);
- eurwdt_write_reg(0x07, 0x08); /* set the logical device */
+ outb(0x55, io);
+ eurwdt_write_reg(0x07, 0x08); /* set the logical device */
}
static inline void eurwdt_set_timeout(int timeout)
{
- eurwdt_write_reg(WDT_TIMEOUT_VAL, (u8) timeout);
+ eurwdt_write_reg(WDT_TIMEOUT_VAL, (u8) timeout);
}
static inline void eurwdt_disable_timer(void)
{
- eurwdt_set_timeout(0);
+ eurwdt_set_timeout(0);
}
static void eurwdt_activate_timer(void)
{
- eurwdt_disable_timer();
- eurwdt_write_reg(WDT_CTRL_REG, 0x01); /* activate the WDT */
- eurwdt_write_reg(WDT_OUTPIN_CFG, !strcmp("int", ev) ?
- WDT_EVENT_INT : WDT_EVENT_REBOOT);
- /* Setting interrupt line */
- if (irq == 2 || irq > 15 || irq < 0) {
- printk(KERN_ERR ": invalid irq number\n");
- irq = 0; /* if invalid we disable interrupt */
- }
- if (irq == 0)
- printk(KERN_INFO ": interrupt disabled\n");
- eurwdt_write_reg(WDT_TIMER_CFG, irq<<4);
-
- eurwdt_write_reg(WDT_UNIT_SEL, WDT_UNIT_SECS); /* we use seconds */
- eurwdt_set_timeout(0); /* the default timeout */
+ eurwdt_disable_timer();
+ eurwdt_write_reg(WDT_CTRL_REG, 0x01); /* activate the WDT */
+ eurwdt_write_reg(WDT_OUTPIN_CFG, !strcmp("int", ev) ? WDT_EVENT_INT : WDT_EVENT_REBOOT);
+
+ /* Setting interrupt line */
+ if (irq == 2 || irq > 15 || irq < 0) {
+ printk(KERN_ERR ": invalid irq number\n");
+ irq = 0; /* if invalid we disable interrupt */
+ }
+ if (irq == 0)
+ printk(KERN_INFO ": interrupt disabled\n");
+
+ eurwdt_write_reg(WDT_TIMER_CFG, irq<<4);
+
+ eurwdt_write_reg(WDT_UNIT_SEL, WDT_UNIT_SECS); /* we use seconds */
+ eurwdt_set_timeout(0); /* the default timeout */
}
/*
- * Kernel methods.
+ * Kernel methods.
*/
void eurwdt_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- printk(KERN_CRIT "timeout WDT timeout\n");
+ printk(KERN_CRIT "timeout WDT timeout\n");
#ifdef ONLY_TESTING
- printk(KERN_CRIT "Would Reboot.\n");
+ printk(KERN_CRIT "Would Reboot.\n");
#else
- printk(KERN_CRIT "Initiating system reboot.\n");
- machine_restart(NULL);
+ printk(KERN_CRIT "Initiating system reboot.\n");
+ machine_restart(NULL);
#endif
}
/**
- * eurwdt_ping:
+ * eurwdt_ping:
*
- * Reload counter one with the watchdog timeout.
+ * Reload counter one with the watchdog timeout.
*/
static void eurwdt_ping(void)
{
- /* Write the watchdog default value */
- eurwdt_set_timeout(timeout);
+ /* Write the watchdog default value */
+ eurwdt_set_timeout(timeout);
}
/**
- * eurwdt_write:
- * @file: file handle to the watchdog
- * @buf: buffer to write (unused as data does not matter here
- * @count: count of bytes
- * @ppos: pointer to the position to write. No seeks allowed
+ * eurwdt_write:
+ * @file: file handle to the watchdog
+ * @buf: buffer to write (unused as data does not matter here
+ * @count: count of bytes
+ * @ppos: pointer to the position to write. No seeks allowed
*
- * A write to a watchdog device is defined as a keepalive signal. Any
- * write of data will do, as we we don't define content meaning.
+ * A write to a watchdog device is defined as a keepalive signal. Any
+ * write of data will do, as we we don't define content meaning.
*/
static ssize_t eurwdt_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) {
- eurwdt_ping(); /* the default timeout */
- return 1;
- }
+ /* Can't seek (pwrite) on this device */
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
+
+ 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 */
+ return 1;
+ }
- return 0;
+ return 0;
}
/**
- * eurwdt_ioctl:
- * @inode: inode of the device
- * @file: file handle to the device
- * @cmd: watchdog command
- * @arg: argument pointer
+ * eurwdt_ioctl:
+ * @inode: inode of the device
+ * @file: file handle to the device
+ * @cmd: watchdog command
+ * @arg: argument pointer
*
- * The watchdog API defines a common set of functions for all watchdogs
- * according to their available features.
+ * The watchdog API defines a common set of functions for all watchdogs
+ * according to their available features.
*/
static int eurwdt_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+ unsigned int cmd, unsigned long arg)
{
- static struct watchdog_info ident = {
- .options = WDIOF_CARDRESET,
- .firmware_version = 1,
- .identity = "WDT Eurotech CPU-1220/1410",
- };
+ static struct watchdog_info ident = {
+ .options = WDIOF_CARDRESET,
+ .firmware_version = 1,
+ .identity = "WDT Eurotech CPU-1220/1410",
+ };
- int time;
+ int time;
- switch(cmd) {
- default:
- return -ENOTTY;
+ switch(cmd) {
+ default:
+ return -ENOTTY;
- case WDIOC_GETSUPPORT:
- return copy_to_user((struct watchdog_info *)arg, &ident,
- sizeof(ident)) ? -EFAULT : 0;
+ case WDIOC_GETSUPPORT:
+ return copy_to_user((struct watchdog_info *)arg, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
- case WDIOC_GETBOOTSTATUS:
- return put_user(0, (int *) arg);
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, (int *) arg);
- case WDIOC_KEEPALIVE:
- eurwdt_ping();
- return 0;
+ case WDIOC_KEEPALIVE:
+ eurwdt_ping();
+ return 0;
- case WDIOC_SETTIMEOUT:
- if (copy_from_user(&time, (int *) arg, sizeof(int)))
- return -EFAULT;
+ case WDIOC_SETTIMEOUT:
+ if (copy_from_user(&time, (int *) arg, sizeof(int)))
+ return -EFAULT;
- /* Sanity check */
- if (time < 0 || time > 255)
- return -EINVAL;
+ /* Sanity check */
+ if (time < 0 || time > 255)
+ return -EINVAL;
- timeout = time;
- eurwdt_set_timeout(time);
- return 0;
- }
+ timeout = time;
+ eurwdt_set_timeout(time);
+ return 0;
+ }
}
/**
- * eurwdt_open:
- * @inode: inode of device
- * @file: file handle to device
+ * eurwdt_open:
+ * @inode: inode of device
+ * @file: file handle to device
*
- * The misc device has been opened. The watchdog device is single
- * open and on opening we load the counter.
+ * The misc device has been opened. The watchdog device is single
+ * open and on opening we load the counter.
*/
static int eurwdt_open(struct inode *inode, struct file *file)
{
- switch (minor(inode->i_rdev)) {
- case WATCHDOG_MINOR:
- spin_lock(&eurwdt_lock);
- if (eurwdt_is_open) {
- spin_unlock(&eurwdt_lock);
- return -EBUSY;
- }
- if (nowayout) {
- MOD_INC_USE_COUNT;
- }
-
- eurwdt_is_open = 1;
-
- /* Activate the WDT */
- eurwdt_activate_timer();
-
- spin_unlock(&eurwdt_lock);
-
- MOD_INC_USE_COUNT;
-
- return 0;
-
- case TEMP_MINOR:
- return 0;
-
- default:
- return -ENODEV;
- }
+ switch (minor(inode->i_rdev)) {
+ case WATCHDOG_MINOR:
+ spin_lock(&eurwdt_lock);
+ if (eurwdt_is_open) {
+ spin_unlock(&eurwdt_lock);
+ return -EBUSY;
+ }
+ if (nowayout)
+ MOD_INC_USE_COUNT;
+
+ eurwdt_is_open = 1;
+
+ /* Activate the WDT */
+ eurwdt_activate_timer();
+
+ spin_unlock(&eurwdt_lock);
+
+ MOD_INC_USE_COUNT;
+ return 0;
+
+ case TEMP_MINOR:
+ return 0;
+
+ default:
+ return -ENODEV;
+ }
}
/**
- * eurwdt_release:
- * @inode: inode to board
- * @file: file handle to board
+ * eurwdt_release:
+ * @inode: inode to board
+ * @file: file handle to board
*
- * The watchdog has a configurable API. There is a religious dispute
- * between people who want their watchdog to be able to shut down and
- * those who want to be sure if the watchdog manager dies the machine
- * reboots. In the former case we disable the counters, in the latter
- * case you have to open it again very soon.
+ * The watchdog has a configurable API. There is a religious dispute
+ * between people who want their watchdog to be able to shut down and
+ * those who want to be sure if the watchdog manager dies the machine
+ * reboots. In the former case we disable the counters, in the latter
+ * case you have to open it again very soon.
*/
static int eurwdt_release(struct inode *inode, struct file *file)
{
- if (minor(inode->i_rdev) == WATCHDOG_MINOR) {
- if (!nowayout) {
- eurwdt_disable_timer();
- }
- eurwdt_is_open = 0;
+ if (minor(inode->i_rdev) == WATCHDOG_MINOR) {
+ if (!nowayout)
+ eurwdt_disable_timer();
- MOD_DEC_USE_COUNT;
- }
+ eurwdt_is_open = 0;
+ MOD_DEC_USE_COUNT;
+ }
- return 0;
+ return 0;
}
/**
- * eurwdt_notify_sys:
- * @this: our notifier block
- * @code: the event being reported
- * @unused: unused
+ * eurwdt_notify_sys:
+ * @this: our notifier block
+ * @code: the event being reported
+ * @unused: unused
*
- * Our notifier is called on system shutdowns. We want to turn the card
- * off at reboot otherwise the machine will reboot again during memory
- * test or worse yet during the following fsck. This would suck, in fact
- * trust me - if it happens it does suck.
+ * Our notifier is called on system shutdowns. We want to turn the card
+ * off at reboot otherwise the machine will reboot again during memory
+ * test or worse yet during the following fsck. This would suck, in fact
+ * trust me - if it happens it does suck.
*/
static int eurwdt_notify_sys(struct notifier_block *this, unsigned long code,
- void *unused)
+ void *unused)
{
- if (code == SYS_DOWN || code == SYS_HALT) {
- /* Turn the card off */
- eurwdt_disable_timer();
- }
+ if (code == SYS_DOWN || code == SYS_HALT) {
+ /* Turn the card off */
+ eurwdt_disable_timer();
+ }
- return NOTIFY_DONE;
+ return NOTIFY_DONE;
}
/*
- * Kernel Interfaces
+ * Kernel Interfaces
*/
static struct file_operations eurwdt_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = eurwdt_write,
- .ioctl = eurwdt_ioctl,
- .open = eurwdt_open,
- .release = eurwdt_release,
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = eurwdt_write,
+ .ioctl = eurwdt_ioctl,
+ .open = eurwdt_open,
+ .release = eurwdt_release,
};
static struct miscdevice eurwdt_miscdev = {
@@ -403,8 +415,8 @@ static struct miscdevice eurwdt_miscdev = {
};
/*
- * The WDT card needs to learn about soft shutdowns in order to
- * turn the timebomb registers off.
+ * The WDT card needs to learn about soft shutdowns in order to
+ * turn the timebomb registers off.
*/
static struct notifier_block eurwdt_notifier = {
@@ -412,85 +424,85 @@ static struct notifier_block eurwdt_notifier = {
};
/**
- * cleanup_module:
+ * cleanup_module:
*
- * Unload the watchdog. You cannot do this with any file handles open.
- * If your watchdog is set to continue ticking on close and you unload
- * it, well it keeps ticking. We won't get the interrupt but the board
- * will not touch PC memory so all is fine. You just have to load a new
- * module in 60 seconds or reboot.
+ * Unload the watchdog. You cannot do this with any file handles open.
+ * If your watchdog is set to continue ticking on close and you unload
+ * it, well it keeps ticking. We won't get the interrupt but the board
+ * will not touch PC memory so all is fine. You just have to load a new
+ * module in 60 seconds or reboot.
*/
static void __exit eurwdt_exit(void)
{
- eurwdt_lock_chip();
+ eurwdt_lock_chip();
- misc_deregister(&eurwdt_miscdev);
+ misc_deregister(&eurwdt_miscdev);
- unregister_reboot_notifier(&eurwdt_notifier);
- release_region(io, 2);
- free_irq(irq, NULL);
+ unregister_reboot_notifier(&eurwdt_notifier);
+ release_region(io, 2);
+ free_irq(irq, NULL);
}
/**
- * eurwdt_init:
+ * eurwdt_init:
*
- * Set up the WDT watchdog board. After grabbing the resources
- * we require we need also to unlock the device.
- * The open() function will actually kick the board off.
+ * Set up the WDT watchdog board. After grabbing the resources
+ * we require we need also to unlock the device.
+ * The open() function will actually kick the board off.
*/
static int __init eurwdt_init(void)
{
- int ret;
-
- eurwdt_validate_timeout();
- ret = misc_register(&eurwdt_miscdev);
- if (ret) {
- printk(KERN_ERR "eurwdt: can't misc_register on minor=%d\n",
- WATCHDOG_MINOR);
- goto out;
- }
-
- ret = request_irq(irq, eurwdt_interrupt, SA_INTERRUPT, "eurwdt", NULL);
- if(ret) {
- printk(KERN_ERR "eurwdt: IRQ %d is not free.\n", irq);
- goto outmisc;
- }
-
- if (!request_region(io, 2, "eurwdt")) {
- printk(KERN_ERR "eurwdt: IO %X is not free.\n", io);
- ret = -EBUSY;
- goto outirq;
- }
-
- ret = register_reboot_notifier(&eurwdt_notifier);
- if (ret) {
- printk(KERN_ERR "eurwdt: can't register reboot notifier (err=%d)\n", ret);
- goto outreg;
- }
-
- eurwdt_unlock_chip();
-
- ret = 0;
- printk(KERN_INFO "Eurotech WDT driver 0.01 at %X (Interrupt %d)"
- " - timeout event: %s\n",
- io, irq, (!strcmp("int", ev) ? "int" : "reboot"));
-
- spin_lock_init(&eurwdt_lock);
-
- out:
- return ret;
-
- outreg:
- release_region(io, 2);
-
- outirq:
- free_irq(irq, NULL);
-
- outmisc:
- misc_deregister(&eurwdt_miscdev);
- goto out;
+ int ret;
+
+ eurwdt_validate_timeout();
+ ret = misc_register(&eurwdt_miscdev);
+ if (ret) {
+ printk(KERN_ERR "eurwdt: can't misc_register on minor=%d\n",
+ WATCHDOG_MINOR);
+ goto out;
+ }
+
+ ret = request_irq(irq, eurwdt_interrupt, SA_INTERRUPT, "eurwdt", NULL);
+ if(ret) {
+ printk(KERN_ERR "eurwdt: IRQ %d is not free.\n", irq);
+ goto outmisc;
+ }
+
+ if (!request_region(io, 2, "eurwdt")) {
+ printk(KERN_ERR "eurwdt: IO %X is not free.\n", io);
+ ret = -EBUSY;
+ goto outirq;
+ }
+
+ ret = register_reboot_notifier(&eurwdt_notifier);
+ if (ret) {
+ printk(KERN_ERR "eurwdt: can't register reboot notifier (err=%d)\n", ret);
+ goto outreg;
+ }
+
+ eurwdt_unlock_chip();
+
+ ret = 0;
+ printk(KERN_INFO "Eurotech WDT driver 0.01 at %X (Interrupt %d)"
+ " - timeout event: %s\n",
+ io, irq, (!strcmp("int", ev) ? "int" : "reboot"));
+
+ spin_lock_init(&eurwdt_lock);
+
+out:
+ return ret;
+
+outreg:
+ release_region(io, 2);
+
+outirq:
+ free_irq(irq, NULL);
+
+outmisc:
+ misc_deregister(&eurwdt_miscdev);
+ goto out;
}
module_init(eurwdt_init);
diff --git a/drivers/char/watchdog/i810-tco.c b/drivers/char/watchdog/i810-tco.c
index 3697dd0f81b3..f24465c0a8d5 100644
--- a/drivers/char/watchdog/i810-tco.c
+++ b/drivers/char/watchdog/i810-tco.c
@@ -244,7 +244,9 @@ static int i810tco_ioctl (struct inode *inode, struct file *file,
int options, retval = -EINVAL;
static struct watchdog_info ident = {
- .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
.firmware_version = 0,
.identity = "i810 TCO timer",
};
diff --git a/drivers/char/watchdog/ib700wdt.c b/drivers/char/watchdog/ib700wdt.c
index 448c7be4e717..68a9454a6dae 100644
--- a/drivers/char/watchdog/ib700wdt.c
+++ b/drivers/char/watchdog/ib700wdt.c
@@ -54,6 +54,7 @@
static int ibwdt_is_open;
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)
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;
+ }
+ }
ibwdt_ping();
return 1;
}
@@ -153,7 +169,9 @@ ibwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
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) {
@@ -180,42 +198,38 @@ ibwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
static int
ibwdt_open(struct inode *inode, struct file *file)
{
- switch (minor(inode->i_rdev)) {
- case WATCHDOG_MINOR:
- spin_lock(&ibwdt_lock);
- if (ibwdt_is_open) {
- spin_unlock(&ibwdt_lock);
- return -EBUSY;
- }
- if (nowayout) {
- MOD_INC_USE_COUNT;
- }
- /*
- * Activate
- */
-
- ibwdt_is_open = 1;
- ibwdt_ping();
+ if (minor(inode->i_rdev) == WATCHDOG_MINOR) {
+ spin_lock(&ibwdt_lock);
+ if (ibwdt_is_open) {
spin_unlock(&ibwdt_lock);
- return 0;
- default:
- return -ENODEV;
+ return -EBUSY;
+ }
+ if (nowayout)
+ MOD_INC_USE_COUNT;
+
+ /* Activate */
+ ibwdt_is_open = 1;
+ ibwdt_ping();
+ spin_unlock(&ibwdt_lock);
+ return 0;
+ } else {
+ return -ENODEV;
}
}
static int
ibwdt_close(struct inode *inode, struct file *file)
{
- lock_kernel();
if (minor(inode->i_rdev) == WATCHDOG_MINOR) {
spin_lock(&ibwdt_lock);
- if (!nowayout) {
+ if (expect_close)
outb_p(timeout_val, WDT_STOP);
- }
+ else
+ printk(KERN_CRIT "WDT device closed unexpectedly. WDT will not stop!\n");
+
ibwdt_is_open = 0;
spin_unlock(&ibwdt_lock);
}
- unlock_kernel();
return 0;
}
@@ -248,9 +262,9 @@ static struct file_operations ibwdt_fops = {
};
static struct miscdevice ibwdt_miscdev = {
- WATCHDOG_MINOR,
- "watchdog",
- &ibwdt_fops
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &ibwdt_fops
};
/*
@@ -259,9 +273,9 @@ static struct miscdevice ibwdt_miscdev = {
*/
static struct notifier_block ibwdt_notifier = {
- ibwdt_notify_sys,
- NULL,
- 0
+ .notifier_call = ibwdt_notify_sys,
+ .next = NULL,
+ .priority = 0
};
static int __init
diff --git a/drivers/char/watchdog/indydog.c b/drivers/char/watchdog/indydog.c
new file mode 100644
index 000000000000..c41dac5b6b07
--- /dev/null
+++ b/drivers/char/watchdog/indydog.c
@@ -0,0 +1,182 @@
+/*
+ * 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");
diff --git a/drivers/char/watchdog/machzwd.c b/drivers/char/watchdog/machzwd.c
index ab3f538d2d72..3d78c2f37ec6 100644
--- a/drivers/char/watchdog/machzwd.c
+++ b/drivers/char/watchdog/machzwd.c
@@ -117,7 +117,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CON
#define PFX "machzwd"
static struct watchdog_info zf_info = {
- .options = WDIOF_KEEPALIVEPING,
+ .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "ZF-Logic watchdog"
};
@@ -314,10 +314,10 @@ static ssize_t zf_write(struct file *file, const char *buf, size_t count,
/* See if we got the magic character */
if(count){
-/*
- * no need to check for close confirmation
- * no way to disable watchdog ;)
- */
+ /*
+ * no need to check for close confirmation
+ * no way to disable watchdog ;)
+ */
if (!nowayout) {
size_t ofs;
@@ -328,7 +328,7 @@ static ssize_t zf_write(struct file *file, const char *buf, size_t count,
zf_expect_close = 0;
/* now scan */
- for(ofs = 0; ofs != count; ofs++){
+ for (ofs = 0; ofs != count; ofs++){
char c;
if (get_user(c, buf + ofs))
return -EFAULT;
@@ -338,6 +338,7 @@ static ssize_t zf_write(struct file *file, const char *buf, size_t count,
}
}
}
+
/*
* Well, anyhow someone wrote to us,
* we should return that favour
@@ -395,9 +396,9 @@ static int zf_open(struct inode *inode, struct file *file)
return -EBUSY;
}
- if (nowayout) {
+ if (nowayout)
MOD_INC_USE_COUNT;
- }
+
zf_is_open = 1;
spin_unlock(&zf_lock);
diff --git a/drivers/char/watchdog/mixcomwd.c b/drivers/char/watchdog/mixcomwd.c
index 6ee6576e6528..f19a5d9230ef 100644
--- a/drivers/char/watchdog/mixcomwd.c
+++ b/drivers/char/watchdog/mixcomwd.c
@@ -62,6 +62,7 @@ static long mixcomwd_opened; /* long req'd for setbit --RR */
static int watchdog_port;
static int mixcomwd_timer_alive;
static struct timer_list mixcomwd_timer = TIMER_INITIALIZER(NULL, 0, 0);
+static int expect_close = 0;
#ifdef CONFIG_WATCHDOG_NOWAYOUT
static int nowayout = 1;
@@ -109,8 +110,7 @@ static int mixcomwd_open(struct inode *inode, struct file *file)
static int mixcomwd_release(struct inode *inode, struct file *file)
{
-
- if (!nowayout) {
+ if (expect_close) {
if(mixcomwd_timer_alive) {
printk(KERN_ERR "mixcomwd: release called while internal timer alive");
return -EBUSY;
@@ -121,7 +121,10 @@ static int mixcomwd_release(struct inode *inode, struct file *file)
mixcomwd_timer.data=0;
mixcomwd_timer_alive=1;
add_timer(&mixcomwd_timer);
+ } else {
+ printk(KERN_CRIT "mixcomwd: WDT device closed unexpectedly. WDT will not stop!\n");
}
+
clear_bit(0,&mixcomwd_opened);
return 0;
}
@@ -135,6 +138,20 @@ static ssize_t mixcomwd_write(struct file *file, const char *data, size_t len, l
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();
return 1;
}
@@ -145,8 +162,10 @@ static int mixcomwd_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int status;
- static struct watchdog_info ident = {
- WDIOF_KEEPALIVEPING, 1, "MixCOM watchdog"
+ static struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = "MixCOM watchdog"
};
switch(cmd)
diff --git a/drivers/char/watchdog/pcwd.c b/drivers/char/watchdog/pcwd.c
index 4fdd439aa06b..be2bcd00c5fa 100644
--- a/drivers/char/watchdog/pcwd.c
+++ b/drivers/char/watchdog/pcwd.c
@@ -40,6 +40,8 @@
* fairly useless proc entry.
* 990610 removed said useless proc code for the merge <alan>
* 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>
@@ -76,7 +78,7 @@
*/
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
@@ -88,7 +90,23 @@ static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 };
#define PCWD_REVISION_A 1
#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.
@@ -121,7 +139,7 @@ static int __init pcwd_checkcard(void)
if (prev_card_dat == 0xFF)
return 0;
- while(count < WD_TIMEOUT) {
+ while(count < timeout_val) {
/* Read the raw card data from the port, and strip off the
first 4 bits */
@@ -256,7 +274,7 @@ static int pcwd_ioctl(struct inode *inode, struct file *file,
else
cdat = inb(current_readport + 1 );
spin_unlock(&io_lock);
- rv = 0;
+ rv = WDIOF_MAGICCLOSE;
if (revision == PCWD_REVISION_A)
{
@@ -385,8 +403,22 @@ static ssize_t pcwd_write(struct file *file, const char *buf, size_t len,
if (ppos != &file->f_pos)
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();
return 1;
}
@@ -395,28 +427,26 @@ static ssize_t pcwd_write(struct file *file, const char *buf, size_t len,
static int pcwd_open(struct inode *ino, struct file *filep)
{
- switch (minor(ino->i_rdev))
- {
- case WATCHDOG_MINOR:
- if ( !atomic_dec_and_test(&open_allowed) )
- {
+ switch (minor(ino->i_rdev)) {
+ case WATCHDOG_MINOR:
+ if (!atomic_dec_and_test(&open_allowed) ) {
atomic_inc( &open_allowed );
- return -EBUSY;
- }
- MOD_INC_USE_COUNT;
- /* Enable the port */
- if (revision == PCWD_REVISION_C)
- {
- spin_lock(&io_lock);
- outb_p(0x00, current_readport + 3);
- spin_unlock(&io_lock);
- }
- return(0);
- case TEMP_MINOR:
- return(0);
- default:
- return (-ENODEV);
- }
+ return -EBUSY;
+ }
+ MOD_INC_USE_COUNT;
+ /* Enable the port */
+ if (revision == PCWD_REVISION_C) {
+ spin_lock(&io_lock);
+ outb_p(0x00, current_readport + 3);
+ spin_unlock(&io_lock);
+ }
+ return(0);
+
+ case TEMP_MINOR:
+ return(0);
+ default:
+ return (-ENODEV);
+ }
}
static ssize_t pcwd_read(struct file *file, char *buf, size_t count,
@@ -448,18 +478,17 @@ static ssize_t pcwd_read(struct file *file, char *buf, size_t count,
static int pcwd_close(struct inode *ino, struct file *filep)
{
- if (minor(ino->i_rdev)==WATCHDOG_MINOR)
- {
-#ifndef CONFIG_WATCHDOG_NOWAYOUT
- /* Disable the board */
- if (revision == PCWD_REVISION_C) {
- spin_lock(&io_lock);
- outb_p(0xA5, current_readport + 3);
- outb_p(0xA5, current_readport + 3);
- spin_unlock(&io_lock);
+ if (minor(ino->i_rdev)==WATCHDOG_MINOR) {
+ if (expect_close) {
+ /* Disable the board */
+ if (revision == PCWD_REVISION_C) {
+ spin_lock(&io_lock);
+ outb_p(0xA5, current_readport + 3);
+ outb_p(0xA5, current_readport + 3);
+ spin_unlock(&io_lock);
+ }
+ atomic_inc( &open_allowed );
}
- atomic_inc( &open_allowed );
-#endif
}
return 0;
}
@@ -560,9 +589,15 @@ static struct miscdevice temp_miscdev = {
&pcwd_fops
};
+static void __init pcwd_validate_timeout(void)
+{
+ timeout_val = timeout * 2;
+}
+
static int __init pcwatchdog_init(void)
{
int i, found = 0;
+ pcwd_validate_timeout();
spin_lock_init(&io_lock);
revision = PCWD_REVISION_A;
diff --git a/drivers/char/watchdog/sbc60xxwdt.c b/drivers/char/watchdog/sbc60xxwdt.c
index e6799e4ee4ae..4b61d1a8db40 100644
--- a/drivers/char/watchdog/sbc60xxwdt.c
+++ b/drivers/char/watchdog/sbc60xxwdt.c
@@ -244,9 +244,9 @@ static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
{
static struct watchdog_info ident=
{
- 0,
- 1,
- "SB60xx"
+ .options = WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = "SB60xx"
};
switch(cmd)
diff --git a/drivers/char/watchdog/sc1200wdt.c b/drivers/char/watchdog/sc1200wdt.c
new file mode 100644
index 000000000000..005cdaa5c4cf
--- /dev/null
+++ b/drivers/char/watchdog/sc1200wdt.c
@@ -0,0 +1,479 @@
+/*
+ * National Semiconductor PC87307/PC97307 (ala SC1200) WDT driver
+ * (c) Copyright 2002 Zwane Mwaikambo <zwane@commfireservices.com>,
+ * All Rights Reserved.
+ * Based on wdt.c and wdt977.c by Alan Cox and Woody Suwalski respectively.
+ *
+ * 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 author(s) of this software shall not be held liable for damages
+ * of any nature resulting due to the use of this software. This
+ * software is provided AS-IS with no warranties.
+ *
+ * Changelog:
+ * 20020220 Zwane Mwaikambo Code based on datasheet, no hardware.
+ * 20020221 Zwane Mwaikambo Cleanups as suggested by Jeff Garzik and Alan Cox.
+ * 20020222 Zwane Mwaikambo Added probing.
+ * 20020225 Zwane Mwaikambo Added ISAPNP support.
+ * 20020412 Rob Radez Broke out start/stop functions
+ * <rob@osinvestor.com> Return proper status instead of temperature warning
+ * Add WDIOC_GETBOOTSTATUS and WDIOC_SETOPTIONS ioctls
+ * Fix CONFIG_WATCHDOG_NOWAYOUT
+ * 20020530 Joel Becker Add Matt Domsch's nowayout module option
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/ioport.h>
+#include <asm/semaphore.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/isapnp.h>
+#include <linux/pci.h>
+
+#define SC1200_MODULE_VER "build 20020303"
+#define SC1200_MODULE_NAME "sc1200wdt"
+#define PFX SC1200_MODULE_NAME ": "
+
+#define MAX_TIMEOUT 255 /* 255 minutes */
+#define PMIR (io) /* Power Management Index Register */
+#define PMDR (io+1) /* Power Management Data Register */
+
+/* Data Register indexes */
+#define FER1 0x00 /* Function enable register 1 */
+#define FER2 0x01 /* Function enable register 2 */
+#define PMC1 0x02 /* Power Management Ctrl 1 */
+#define PMC2 0x03 /* Power Management Ctrl 2 */
+#define PMC3 0x04 /* Power Management Ctrl 3 */
+#define WDTO 0x05 /* Watchdog timeout register */
+#define WDCF 0x06 /* Watchdog config register */
+#define WDST 0x07 /* Watchdog status register */
+
+/* WDCF bitfields - which devices assert WDO */
+#define KBC_IRQ 0x01 /* Keyboard Controller */
+#define MSE_IRQ 0x02 /* Mouse */
+#define UART1_IRQ 0x03 /* Serial0 */
+#define UART2_IRQ 0x04 /* Serial1 */
+/* 5 -7 are reserved */
+
+static char banner[] __initdata = KERN_INFO PFX SC1200_MODULE_VER;
+static int timeout = 1;
+static int io = -1;
+static int io_len = 2; /* for non plug and play */
+struct semaphore open_sem;
+static char expect_close;
+spinlock_t sc1200wdt_lock; /* io port access serialisation */
+
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+static int isapnp = 1;
+static struct pci_dev *wdt_dev;
+
+MODULE_PARM(isapnp, "i");
+MODULE_PARM_DESC(isapnp, "When set to 0 driver ISA PnP support will be disabled");
+#endif
+
+MODULE_PARM(io, "i");
+MODULE_PARM_DESC(io, "io port");
+MODULE_PARM(timeout, "i");
+MODULE_PARM_DESC(timeout, "range is 0-255 minutes, default is 1");
+
+#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)");
+
+
+
+/* Read from Data Register */
+static inline void sc1200wdt_read_data(unsigned char index, unsigned char *data)
+{
+ spin_lock(&sc1200wdt_lock);
+ outb_p(index, PMIR);
+ *data = inb(PMDR);
+ spin_unlock(&sc1200wdt_lock);
+}
+
+
+/* Write to Data Register */
+static inline void sc1200wdt_write_data(unsigned char index, unsigned char data)
+{
+ spin_lock(&sc1200wdt_lock);
+ outb_p(index, PMIR);
+ outb(data, PMDR);
+ spin_unlock(&sc1200wdt_lock);
+}
+
+
+static void sc1200wdt_start(void)
+{
+ unsigned char reg;
+
+ sc1200wdt_read_data(WDCF, &reg);
+ /* assert WDO when any of the following interrupts are triggered too */
+ reg |= (KBC_IRQ | MSE_IRQ | UART1_IRQ | UART2_IRQ);
+ sc1200wdt_write_data(WDCF, reg);
+ /* set the timeout and get the ball rolling */
+ sc1200wdt_write_data(WDTO, timeout);
+}
+
+
+static void sc1200wdt_stop(void)
+{
+ sc1200wdt_write_data(WDTO, 0);
+}
+
+
+/* This returns the status of the WDO signal, inactive high. */
+static inline int sc1200wdt_status(void)
+{
+ unsigned char ret;
+
+ sc1200wdt_read_data(WDST, &ret);
+ /* If the bit is inactive, the watchdog is enabled, so return
+ * KEEPALIVEPING which is a bit of a kludge because there's nothing
+ * else for enabled/disabled status
+ */
+ return (ret & 0x01) ? 0 : WDIOF_KEEPALIVEPING; /* bits 1 - 7 are undefined */
+}
+
+
+static int sc1200wdt_open(struct inode *inode, struct file *file)
+{
+ /* allow one at a time */
+ if (down_trylock(&open_sem))
+ return -EBUSY;
+
+ if (timeout > MAX_TIMEOUT)
+ timeout = MAX_TIMEOUT;
+
+ sc1200wdt_start();
+ printk(KERN_INFO PFX "Watchdog enabled, timeout = %d min(s)", timeout);
+
+ return 0;
+}
+
+
+static int sc1200wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int new_timeout;
+ static struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
+ .firmware_version = 0,
+ .identity = "PC87307/PC97307"
+ };
+
+ switch (cmd) {
+ default:
+ return -ENOTTY; /* Keep Pavel Machek amused ;) */
+
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof ident))
+ return -EFAULT;
+ return 0;
+
+ case WDIOC_GETSTATUS:
+ return put_user(sc1200wdt_status(), (int *)arg);
+
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, (int *)arg);
+
+ case WDIOC_KEEPALIVE:
+ sc1200wdt_write_data(WDTO, timeout);
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_timeout, (int *)arg))
+ return -EFAULT;
+
+ /* the API states this is given in secs */
+ new_timeout /= 60;
+ if (new_timeout < 0 || new_timeout > MAX_TIMEOUT)
+ return -EINVAL;
+
+ timeout = new_timeout;
+ sc1200wdt_write_data(WDTO, timeout);
+ /* fall through and return the new timeout */
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout * 60, (int *)arg);
+
+ case WDIOC_SETOPTIONS:
+ {
+ int options, retval = -EINVAL;
+
+ if (get_user(options, (int *)arg))
+ return -EFAULT;
+
+ if (options & WDIOS_DISABLECARD) {
+ sc1200wdt_stop();
+ retval = 0;
+ }
+
+ if (options & WDIOS_ENABLECARD) {
+ sc1200wdt_start();
+ retval = 0;
+ }
+
+ return retval;
+ }
+ }
+}
+
+
+static int sc1200wdt_release(struct inode *inode, struct file *file)
+{
+ if (expect_close == 42) {
+ sc1200wdt_stop();
+ printk(KERN_INFO PFX "Watchdog disabled\n");
+ } else {
+ sc1200wdt_write_data(WDTO, timeout);
+ printk(KERN_CRIT PFX "Unexpected close!, timeout = %d min(s)\n", timeout);
+ }
+ up(&open_sem);
+ expect_close = 0;
+
+ return 0;
+}
+
+
+static ssize_t sc1200wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos)
+{
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
+
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ expect_close = 0;
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data+i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+
+ sc1200wdt_write_data(WDTO, timeout);
+ return len;
+ }
+
+ return 0;
+}
+
+
+static int sc1200wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ sc1200wdt_stop();
+
+ return NOTIFY_DONE;
+}
+
+
+static struct notifier_block sc1200wdt_notifier =
+{
+ notifier_call: sc1200wdt_notify_sys
+};
+
+static struct file_operations sc1200wdt_fops =
+{
+ owner: THIS_MODULE,
+ write: sc1200wdt_write,
+ ioctl: sc1200wdt_ioctl,
+ open: sc1200wdt_open,
+ release: sc1200wdt_release
+};
+
+static struct miscdevice sc1200wdt_miscdev =
+{
+ minor: WATCHDOG_MINOR,
+ name: "watchdog",
+ fops: &sc1200wdt_fops,
+};
+
+
+static int __init sc1200wdt_probe(void)
+{
+ /* The probe works by reading the PMC3 register's default value of 0x0e
+ * there is one caveat, if the device disables the parallel port or any
+ * of the UARTs we won't be able to detect it.
+ * Nb. This could be done with accuracy by reading the SID registers, but
+ * we don't have access to those io regions.
+ */
+
+ unsigned char reg;
+
+ sc1200wdt_read_data(PMC3, &reg);
+ reg &= 0x0f; /* we don't want the UART busy bits */
+ return (reg == 0x0e) ? 0 : -ENODEV;
+}
+
+
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+
+static int __init sc1200wdt_isapnp_probe(void)
+{
+ int ret;
+
+ /* The WDT is logical device 8 on the main device */
+ wdt_dev = isapnp_find_dev(NULL, ISAPNP_VENDOR('N','S','C'), ISAPNP_FUNCTION(0x08), NULL);
+ if (!wdt_dev)
+ return -ENODEV;
+
+ if (wdt_dev->prepare(wdt_dev) < 0) {
+ printk(KERN_ERR PFX "ISA PnP found device that could not be autoconfigured\n");
+ return -EAGAIN;
+ }
+
+ if (!(pci_resource_flags(wdt_dev, 0) & IORESOURCE_IO)) {
+ printk(KERN_ERR PFX "ISA PnP could not find io ports\n");
+ return -ENODEV;
+ }
+
+ ret = wdt_dev->activate(wdt_dev);
+ if (ret && (ret != -EBUSY))
+ return -ENOMEM;
+
+ /* io port resource overriding support? */
+ io = pci_resource_start(wdt_dev, 0);
+ io_len = pci_resource_len(wdt_dev, 0);
+
+ printk(KERN_DEBUG PFX "ISA PnP found device at io port %#x/%d\n", io, io_len);
+ return 0;
+}
+
+#endif /* CONFIG_ISAPNP */
+
+
+static int __init sc1200wdt_init(void)
+{
+ int ret;
+
+ printk(banner);
+
+ spin_lock_init(&sc1200wdt_lock);
+ sema_init(&open_sem, 1);
+
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+ if (isapnp) {
+ ret = sc1200wdt_isapnp_probe();
+ if (ret)
+ goto out_clean;
+ }
+#endif
+
+ if (io == -1) {
+ printk(KERN_ERR PFX "io parameter must be specified\n");
+ ret = -EINVAL;
+ goto out_clean;
+ }
+
+ if (!request_region(io, io_len, SC1200_MODULE_NAME)) {
+ printk(KERN_ERR PFX "Unable to register IO port %#x\n", io);
+ ret = -EBUSY;
+ goto out_pnp;
+ }
+
+ ret = sc1200wdt_probe();
+ if (ret)
+ goto out_io;
+
+ ret = register_reboot_notifier(&sc1200wdt_notifier);
+ if (ret) {
+ printk(KERN_ERR PFX "Unable to register reboot notifier err = %d\n", ret);
+ goto out_io;
+ }
+
+ ret = misc_register(&sc1200wdt_miscdev);
+ if (ret) {
+ printk(KERN_ERR PFX "Unable to register miscdev on minor %d\n", WATCHDOG_MINOR);
+ goto out_rbt;
+ }
+
+ /* ret = 0 */
+
+out_clean:
+ return ret;
+
+out_rbt:
+ unregister_reboot_notifier(&sc1200wdt_notifier);
+
+out_io:
+ release_region(io, io_len);
+
+out_pnp:
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+ if (isapnp && wdt_dev)
+ wdt_dev->deactivate(wdt_dev);
+#endif
+ goto out_clean;
+}
+
+
+static void __exit sc1200wdt_exit(void)
+{
+ misc_deregister(&sc1200wdt_miscdev);
+ unregister_reboot_notifier(&sc1200wdt_notifier);
+
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+ if(isapnp && wdt_dev)
+ wdt_dev->deactivate(wdt_dev);
+#endif
+
+ release_region(io, io_len);
+}
+
+
+#ifndef MODULE
+static int __init sc1200wdt_setup(char *str)
+{
+ int ints[4];
+
+ str = get_options (str, ARRAY_SIZE(ints), ints);
+
+ if (ints[0] > 0) {
+ io = ints[1];
+ if (ints[0] > 1)
+ timeout = ints[2];
+
+#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+ if (ints[0] > 2)
+ isapnp = ints[3];
+#endif
+ }
+
+ return 1;
+}
+
+__setup("sc1200wdt=", sc1200wdt_setup);
+#endif /* MODULE */
+
+
+module_init(sc1200wdt_init);
+module_exit(sc1200wdt_exit);
+
+MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>");
+MODULE_DESCRIPTION("Driver for National Semiconductor PC87307/PC97307 watchdog component");
+MODULE_LICENSE("GPL");
+EXPORT_NO_SYMBOLS;
+
diff --git a/drivers/char/watchdog/sc520_wdt.c b/drivers/char/watchdog/sc520_wdt.c
new file mode 100644
index 000000000000..1d30c4ef4f29
--- /dev/null
+++ b/drivers/char/watchdog/sc520_wdt.c
@@ -0,0 +1,394 @@
+/*
+ * 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;
diff --git a/drivers/char/watchdog/shwdt.c b/drivers/char/watchdog/shwdt.c
index 8761e4a92736..2f32ebc5708e 100644
--- a/drivers/char/watchdog/shwdt.c
+++ b/drivers/char/watchdog/shwdt.c
@@ -334,21 +334,21 @@ static struct file_operations sh_wdt_fops = {
};
static struct watchdog_info sh_wdt_info = {
- WDIOF_KEEPALIVEPING,
- 1,
- "SH WDT",
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = "SH WDT",
};
static struct notifier_block sh_wdt_notifier = {
- sh_wdt_notify_sys,
- NULL,
- 0
+ .notifier_call = sh_wdt_notify_sys,
+ .next = NULL,
+ .priority = 0
};
static struct miscdevice sh_wdt_miscdev = {
- WATCHDOG_MINOR,
- "watchdog",
- &sh_wdt_fops,
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops &sh_wdt_fops,
};
/**
diff --git a/drivers/char/watchdog/softdog.c b/drivers/char/watchdog/softdog.c
index 6894bb034fc1..7abbabad53fc 100644
--- a/drivers/char/watchdog/softdog.c
+++ b/drivers/char/watchdog/softdog.c
@@ -47,6 +47,7 @@
#define TIMER_MARGIN 60 /* (secs) Default is 1 minute */
+static int expect_close = 0;
static int soft_margin = TIMER_MARGIN; /* in seconds */
MODULE_PARM(soft_margin,"i");
@@ -79,7 +80,7 @@ static int timer_alive;
static void watchdog_fire(unsigned long data)
{
#ifdef ONLY_TESTING
- printk(KERN_CRIT "SOFTDOG: Would Reboot.\n");
+ printk(KERN_CRIT "SOFTDOG: Would Reboot.\n");
#else
printk(KERN_CRIT "SOFTDOG: Initiating system reboot.\n");
machine_restart(NULL);
@@ -114,6 +115,8 @@ static int softdog_release(struct inode *inode, struct file *file)
*/
if(!nowayout) {
del_timer(&watchdog_ticktock);
+ } else {
+ printk(KERN_CRIT "SOFTDOG: WDT device closed unexpectedly. WDT will not stop!\n");
}
timer_alive=0;
return 0;
@@ -129,6 +132,21 @@ static ssize_t softdog_write(struct file *file, const char *data, size_t len, lo
* 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;
+ }
+ }
mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ));
return 1;
}
@@ -139,6 +157,7 @@ static int softdog_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
static struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
.identity = "Software Watchdog",
};
switch (cmd) {
diff --git a/drivers/char/watchdog/w83877f_wdt.c b/drivers/char/watchdog/w83877f_wdt.c
index 66749672d39c..152c5cf41311 100644
--- a/drivers/char/watchdog/w83877f_wdt.c
+++ b/drivers/char/watchdog/w83877f_wdt.c
@@ -251,9 +251,9 @@ static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
{
static struct watchdog_info ident=
{
- 0,
- 1,
- "W83877F"
+ .options = WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = "W83877F"
};
switch(cmd)
diff --git a/drivers/char/watchdog/wafer5823wdt.c b/drivers/char/watchdog/wafer5823wdt.c
new file mode 100644
index 000000000000..dc611c96835e
--- /dev/null
+++ b/drivers/char/watchdog/wafer5823wdt.c
@@ -0,0 +1,262 @@
+/*
+ * 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 */
diff --git a/drivers/char/watchdog/wdt.c b/drivers/char/watchdog/wdt.c
index 33deb303e3d4..f50fd376e0ec 100644
--- a/drivers/char/watchdog/wdt.c
+++ b/drivers/char/watchdog/wdt.c
@@ -52,6 +52,7 @@
#include <asm/system.h>
static unsigned long wdt_is_open;
+static int expect_close;
/*
* 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_
if (ppos != &file->f_pos)
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();
return 1;
}
@@ -317,10 +331,11 @@ static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
{
static struct watchdog_info ident=
{
- WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER
- |WDIOF_EXTERN1|WDIOF_EXTERN2|WDIOF_FANFAULT,
- 1,
- "WDT500/501"
+ .options = WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER
+ |WDIOF_EXTERN1|WDIOF_EXTERN2|WDIOF_FANFAULT
+ |WDIOF_SETTIMEOUT|WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = "WDT500/501"
};
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)
{
if(minor(inode->i_rdev)==WATCHDOG_MINOR)
{
- if (!nowayout) {
+ if (expect_close) {
inb_p(WDT_DC); /* Disable counters */
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);
}
diff --git a/drivers/char/watchdog/wdt977.c b/drivers/char/watchdog/wdt977.c
index ce522c581879..28c9e5842917 100644
--- a/drivers/char/watchdog/wdt977.c
+++ b/drivers/char/watchdog/wdt977.c
@@ -41,6 +41,7 @@ static int timeout = DEFAULT_TIMEOUT*60; /* TO in seconds from user */
static int timeoutM = DEFAULT_TIMEOUT; /* timeout in minutes */
static unsigned long timer_alive;
static int testmode;
+static int expect_close = 0;
MODULE_PARM(timeout, "i");
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)
clear_bit(0,&timer_alive);
printk(KERN_INFO "Wdt977 Watchdog: shutdown\n");
+ } else {
+ printk(KERN_CRIT "WDT device closed unexpectedly. WDT will not stop!\n");
}
return 0;
}
@@ -220,6 +223,21 @@ static ssize_t wdt977_write(struct file *file, const char *buf, size_t count, lo
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();
return 1;
}
diff --git a/drivers/char/watchdog/wdt_pci.c b/drivers/char/watchdog/wdt_pci.c
index 28374dc4f631..588f369ede0d 100644
--- a/drivers/char/watchdog/wdt_pci.c
+++ b/drivers/char/watchdog/wdt_pci.c
@@ -73,6 +73,7 @@
#endif
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.
@@ -276,6 +277,16 @@ static ssize_t wdtpci_write(struct file *file, const char *buf, size_t count, lo
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();
return 1;
}
@@ -332,9 +343,10 @@ static int wdtpci_ioctl(struct inode *inode, struct file *file, unsigned int cmd
unsigned long arg)
{
static struct watchdog_info ident = {
- .options = WDIOF_OVERHEAT | WDIOF_POWERUNDER |
- WDIOF_POWEROVER | WDIOF_EXTERN1 |
- WDIOF_EXTERN2 | WDIOF_FANFAULT,
+ .options = WDIOF_OVERHEAT | WDIOF_POWERUNDER |
+ WDIOF_POWEROVER | WDIOF_EXTERN1 |
+ WDIOF_EXTERN2 | WDIOF_FANFAULT |
+ WDIOF_SETTIMEOUT|WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "WDT500/501PCI",
};
diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h
index 391779c8b965..e208b6f07d67 100644
--- a/include/linux/watchdog.h
+++ b/include/linux/watchdog.h
@@ -39,6 +39,7 @@ struct watchdog_info {
#define WDIOF_CARDRESET 0x0020 /* Card previously reset the CPU */
#define WDIOF_POWEROVER 0x0040 /* Power over voltage */
#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 WDIOS_DISABLECARD 0x0001 /* Turn off the watchdog timer */