Commit a31ebc7d authored by Dave Jones's avatar Dave Jones

Merge tetrachloride.(none):/mnt/stuff/kernel/2.5/bk-linus

into tetrachloride.(none):/mnt/stuff/kernel/2.5/watchdog
parents 60e7fd5e 4b378dd8
...@@ -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)) {
{ spin_lock(&acq_lock);
case WATCHDOG_MINOR: if(acq_is_open) {
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);
spin_unlock(&acq_lock); spin_unlock(&acq_lock);
return 0; return -EBUSY;
default: }
return -ENODEV; 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) 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,17 +231,15 @@ static int __init acq_init(void) ...@@ -221,17 +231,15 @@ 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;
} }
register_reboot_notifier(&acq_notifier); register_reboot_notifier(&acq_notifier);
return 0; return 0;
......
...@@ -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,9 +129,11 @@ advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ...@@ -116,9 +129,11 @@ 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) {
case WDIOC_GETSUPPORT: case WDIOC_GETSUPPORT:
if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))) 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, ...@@ -143,26 +158,23 @@ 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);
return -EBUSY;
}
if (nowayout) {
MOD_INC_USE_COUNT;
}
/*
* Activate
*/
advwdt_is_open = 1;
advwdt_ping();
spin_unlock(&advwdt_lock); spin_unlock(&advwdt_lock);
return 0; return -EBUSY;
default: }
return -ENODEV;
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) ...@@ -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");
...@@ -7,23 +7,23 @@ ...@@ -7,23 +7,23 @@
* Based on wdt.c. * Based on wdt.c.
* Original copyright messages: * Original copyright messages:
* *
* (c) Copyright 1996-1997 Alan Cox <alan@redhat.com>, All Rights Reserved. * (c) Copyright 1996-1997 Alan Cox <alan@redhat.com>, All Rights Reserved.
* http://www.redhat.com * http://www.redhat.com
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version * as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version. * 2 of the License, or (at your option) any later version.
* *
* Neither Alan Cox nor CymruNet Ltd. admit liability nor provide * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
* 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
* Added timeout module option to override default * Added timeout module option to override default
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -49,17 +49,18 @@ ...@@ -49,17 +49,18 @@
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.
* You can use wdt=x,y to set these now. * You can use wdt=x,y to set these now.
*/ */
static int io = 0x3f0; static int io = 0x3f0;
static int irq = 10; static int irq = 10;
static char *ev = "int"; static char *ev = "int";
#define WDT_TIMEOUT 60 /* 1 minute */ #define WDT_TIMEOUT 60 /* 1 minute */
static int timeout = WDT_TIMEOUT; static int timeout = WDT_TIMEOUT;
MODULE_PARM(timeout,"i"); MODULE_PARM(timeout,"i");
...@@ -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
...@@ -91,27 +92,27 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CON ...@@ -91,27 +92,27 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CON
#ifndef MODULE #ifndef MODULE
/** /**
* eurwdt_setup: * eurwdt_setup:
* @str: command line string * @str: command line string
* *
* Setup options. The board isn't really probe-able so we have to * 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 * get the user to tell us the configuration. Sane people build it
* modular but the others come here. * modular but the others come here.
*/ */
static int __init eurwdt_setup(char *str) 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];
if (ints[0] > 1) if (ints[0] > 1)
irq = ints[2]; irq = ints[2];
} }
return 1; return 1;
} }
__setup("wdt=", eurwdt_setup); __setup("wdt=", eurwdt_setup);
...@@ -127,7 +128,7 @@ MODULE_PARM_DESC(ev, "Eurotech WDT event type (default is `reboot')"); ...@@ -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) static void __init eurwdt_validate_timeout(void)
...@@ -135,265 +136,276 @@ static void __init eurwdt_validate_timeout(void) ...@@ -135,265 +136,276 @@ static void __init eurwdt_validate_timeout(void)
if (timeout < 0 || timeout > 255) { if (timeout < 0 || timeout > 255) {
timeout = WDT_TIMEOUT; timeout = WDT_TIMEOUT;
printk(KERN_INFO "eurwdt: timeout must be 0 < x < 255, using %d\n", 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) static inline void eurwdt_write_reg(u8 index, u8 data)
{ {
outb(index, io); outb(index, io);
outb(data, io+1); outb(data, io+1);
} }
static inline void eurwdt_lock_chip(void) static inline void eurwdt_lock_chip(void)
{ {
outb(0xaa, io); outb(0xaa, io);
} }
static inline void eurwdt_unlock_chip(void) static inline void eurwdt_unlock_chip(void)
{ {
outb(0x55, io); outb(0x55, io);
eurwdt_write_reg(0x07, 0x08); /* set the logical device */ eurwdt_write_reg(0x07, 0x08); /* set the logical device */
} }
static inline void eurwdt_set_timeout(int timeout) 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) static inline void eurwdt_disable_timer(void)
{ {
eurwdt_set_timeout(0); eurwdt_set_timeout(0);
} }
static void eurwdt_activate_timer(void) 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");
irq = 0; /* if invalid we disable interrupt */ irq = 0; /* if invalid we disable interrupt */
} }
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_set_timeout(0); /* the default timeout */ 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) 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 #ifdef ONLY_TESTING
printk(KERN_CRIT "Would Reboot.\n"); printk(KERN_CRIT "Would Reboot.\n");
#else #else
printk(KERN_CRIT "Initiating system reboot.\n"); printk(KERN_CRIT "Initiating system reboot.\n");
machine_restart(NULL); machine_restart(NULL);
#endif #endif
} }
/** /**
* eurwdt_ping: * eurwdt_ping:
* *
* Reload counter one with the watchdog timeout. * Reload counter one with the watchdog timeout.
*/ */
static void eurwdt_ping(void) static void eurwdt_ping(void)
{ {
/* Write the watchdog default value */ /* Write the watchdog default value */
eurwdt_set_timeout(timeout); eurwdt_set_timeout(timeout);
} }
/** /**
* eurwdt_write: * eurwdt_write:
* @file: file handle to the watchdog * @file: file handle to the watchdog
* @buf: buffer to write (unused as data does not matter here * @buf: buffer to write (unused as data does not matter here
* @count: count of bytes * @count: count of bytes
* @ppos: pointer to the position to write. No seeks allowed * @ppos: pointer to the position to write. No seeks allowed
* *
* A write to a watchdog device is defined as a keepalive signal. Any * 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. * 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, static ssize_t eurwdt_write(struct file *file, const char *buf, size_t count,
loff_t *ppos) loff_t *ppos)
{ {
/* Can't seek (pwrite) on this device */ /* Can't seek (pwrite) on this device */
if (ppos != &file->f_pos) if (ppos != &file->f_pos)
return -ESPIPE; return -ESPIPE;
if (count) { if (count) {
eurwdt_ping(); /* the default timeout */ if (!nowayout) {
return 1; 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: * eurwdt_ioctl:
* @inode: inode of the device * @inode: inode of the device
* @file: file handle to the device * @file: file handle to the device
* @cmd: watchdog command * @cmd: watchdog command
* @arg: argument pointer * @arg: argument pointer
* *
* The watchdog API defines a common set of functions for all watchdogs * The watchdog API defines a common set of functions for all watchdogs
* according to their available features. * according to their available features.
*/ */
static int eurwdt_ioctl(struct inode *inode, struct file *file, 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 = { static struct watchdog_info ident = {
.options = WDIOF_CARDRESET, .options = WDIOF_CARDRESET,
.firmware_version = 1, .firmware_version = 1,
.identity = "WDT Eurotech CPU-1220/1410", .identity = "WDT Eurotech CPU-1220/1410",
}; };
int time; int time;
switch(cmd) { switch(cmd) {
default: default:
return -ENOTTY; return -ENOTTY;
case WDIOC_GETSUPPORT: case WDIOC_GETSUPPORT:
return copy_to_user((struct watchdog_info *)arg, &ident, return copy_to_user((struct watchdog_info *)arg, &ident,
sizeof(ident)) ? -EFAULT : 0; sizeof(ident)) ? -EFAULT : 0;
case WDIOC_GETBOOTSTATUS: case WDIOC_GETBOOTSTATUS:
return put_user(0, (int *) arg); return put_user(0, (int *) arg);
case WDIOC_KEEPALIVE: case WDIOC_KEEPALIVE:
eurwdt_ping(); eurwdt_ping();
return 0; return 0;
case WDIOC_SETTIMEOUT: case WDIOC_SETTIMEOUT:
if (copy_from_user(&time, (int *) arg, sizeof(int))) if (copy_from_user(&time, (int *) arg, sizeof(int)))
return -EFAULT; return -EFAULT;
/* Sanity check */ /* Sanity check */
if (time < 0 || time > 255) if (time < 0 || time > 255)
return -EINVAL; return -EINVAL;
timeout = time; timeout = time;
eurwdt_set_timeout(time); eurwdt_set_timeout(time);
return 0; return 0;
} }
} }
/** /**
* eurwdt_open: * eurwdt_open:
* @inode: inode of device * @inode: inode of device
* @file: file handle to device * @file: file handle to device
* *
* The misc device has been opened. The watchdog device is single * The misc device has been opened. The watchdog device is single
* open and on opening we load the counter. * open and on opening we load the counter.
*/ */
static int eurwdt_open(struct inode *inode, struct file *file) static int eurwdt_open(struct inode *inode, struct file *file)
{ {
switch (minor(inode->i_rdev)) { switch (minor(inode->i_rdev)) {
case WATCHDOG_MINOR: case WATCHDOG_MINOR:
spin_lock(&eurwdt_lock); spin_lock(&eurwdt_lock);
if (eurwdt_is_open) { if (eurwdt_is_open) {
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;
/* Activate the WDT */
/* Activate the WDT */ eurwdt_activate_timer();
eurwdt_activate_timer();
spin_unlock(&eurwdt_lock);
spin_unlock(&eurwdt_lock);
MOD_INC_USE_COUNT;
MOD_INC_USE_COUNT; return 0;
return 0; case TEMP_MINOR:
return 0;
case TEMP_MINOR:
return 0; default:
return -ENODEV;
default: }
return -ENODEV;
}
} }
/** /**
* eurwdt_release: * eurwdt_release:
* @inode: inode to board * @inode: inode to board
* @file: file handle to board * @file: file handle to board
* *
* The watchdog has a configurable API. There is a religious dispute * The watchdog has a configurable API. There is a religious dispute
* between people who want their watchdog to be able to shut down and * 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 * 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 * reboots. In the former case we disable the counters, in the latter
* case you have to open it again very soon. * case you have to open it again very soon.
*/ */
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;
MOD_DEC_USE_COUNT; eurwdt_is_open = 0;
} MOD_DEC_USE_COUNT;
}
return 0; return 0;
} }
/** /**
* eurwdt_notify_sys: * eurwdt_notify_sys:
* @this: our notifier block * @this: our notifier block
* @code: the event being reported * @code: the event being reported
* @unused: unused * @unused: unused
* *
* Our notifier is called on system shutdowns. We want to turn the card * Our notifier is called on system shutdowns. We want to turn the card
* off at reboot otherwise the machine will reboot again during memory * off at reboot otherwise the machine will reboot again during memory
* test or worse yet during the following fsck. This would suck, in fact * test or worse yet during the following fsck. This would suck, in fact
* trust me - if it happens it does suck. * trust me - if it happens it does suck.
*/ */
static int eurwdt_notify_sys(struct notifier_block *this, unsigned long code, static int eurwdt_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 */
eurwdt_disable_timer(); eurwdt_disable_timer();
} }
return NOTIFY_DONE; return NOTIFY_DONE;
} }
/* /*
* Kernel Interfaces * Kernel Interfaces
*/ */
static struct file_operations eurwdt_fops = { static struct file_operations eurwdt_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.llseek = no_llseek, .llseek = no_llseek,
.write = eurwdt_write, .write = eurwdt_write,
.ioctl = eurwdt_ioctl, .ioctl = eurwdt_ioctl,
.open = eurwdt_open, .open = eurwdt_open,
.release = eurwdt_release, .release = eurwdt_release,
}; };
static struct miscdevice eurwdt_miscdev = { static struct miscdevice eurwdt_miscdev = {
...@@ -403,8 +415,8 @@ 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 * The WDT card needs to learn about soft shutdowns in order to
* turn the timebomb registers off. * turn the timebomb registers off.
*/ */
static struct notifier_block eurwdt_notifier = { static struct notifier_block eurwdt_notifier = {
...@@ -412,85 +424,85 @@ 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. * 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 * 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 * 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 * will not touch PC memory so all is fine. You just have to load a new
* module in 60 seconds or reboot. * module in 60 seconds or reboot.
*/ */
static void __exit eurwdt_exit(void) 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); unregister_reboot_notifier(&eurwdt_notifier);
release_region(io, 2); release_region(io, 2);
free_irq(irq, NULL); free_irq(irq, NULL);
} }
/** /**
* eurwdt_init: * eurwdt_init:
* *
* Set up the WDT watchdog board. After grabbing the resources * Set up the WDT watchdog board. After grabbing the resources
* we require we need also to unlock the device. * we require we need also to unlock the device.
* The open() function will actually kick the board off. * The open() function will actually kick the board off.
*/ */
static int __init eurwdt_init(void) static int __init eurwdt_init(void)
{ {
int ret; int ret;
eurwdt_validate_timeout(); eurwdt_validate_timeout();
ret = misc_register(&eurwdt_miscdev); ret = misc_register(&eurwdt_miscdev);
if (ret) { if (ret) {
printk(KERN_ERR "eurwdt: can't misc_register on minor=%d\n", printk(KERN_ERR "eurwdt: can't misc_register on minor=%d\n",
WATCHDOG_MINOR); WATCHDOG_MINOR);
goto out; goto out;
} }
ret = request_irq(irq, eurwdt_interrupt, SA_INTERRUPT, "eurwdt", NULL); ret = request_irq(irq, eurwdt_interrupt, SA_INTERRUPT, "eurwdt", NULL);
if(ret) { if(ret) {
printk(KERN_ERR "eurwdt: IRQ %d is not free.\n", irq); printk(KERN_ERR "eurwdt: IRQ %d is not free.\n", irq);
goto outmisc; goto outmisc;
} }
if (!request_region(io, 2, "eurwdt")) { if (!request_region(io, 2, "eurwdt")) {
printk(KERN_ERR "eurwdt: IO %X is not free.\n", io); printk(KERN_ERR "eurwdt: IO %X is not free.\n", io);
ret = -EBUSY; ret = -EBUSY;
goto outirq; goto outirq;
} }
ret = register_reboot_notifier(&eurwdt_notifier); ret = register_reboot_notifier(&eurwdt_notifier);
if (ret) { if (ret) {
printk(KERN_ERR "eurwdt: can't register reboot notifier (err=%d)\n", ret); printk(KERN_ERR "eurwdt: can't register reboot notifier (err=%d)\n", ret);
goto outreg; goto outreg;
} }
eurwdt_unlock_chip(); eurwdt_unlock_chip();
ret = 0; ret = 0;
printk(KERN_INFO "Eurotech WDT driver 0.01 at %X (Interrupt %d)" printk(KERN_INFO "Eurotech WDT driver 0.01 at %X (Interrupt %d)"
" - timeout event: %s\n", " - timeout event: %s\n",
io, irq, (!strcmp("int", ev) ? "int" : "reboot")); io, irq, (!strcmp("int", ev) ? "int" : "reboot"));
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;
} }
module_init(eurwdt_init); module_init(eurwdt_init);
......
...@@ -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,42 +198,38 @@ ibwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ...@@ -180,42 +198,38 @@ 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);
return -EBUSY;
}
if (nowayout) {
MOD_INC_USE_COUNT;
}
/*
* Activate
*/
ibwdt_is_open = 1;
ibwdt_ping();
spin_unlock(&ibwdt_lock); spin_unlock(&ibwdt_lock);
return 0; return -EBUSY;
default: }
return -ENODEV; if (nowayout)
MOD_INC_USE_COUNT;
/* Activate */
ibwdt_is_open = 1;
ibwdt_ping();
spin_unlock(&ibwdt_lock);
return 0;
} else {
return -ENODEV;
} }
} }
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,10 +314,10 @@ static ssize_t zf_write(struct file *file, const char *buf, size_t count, ...@@ -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 */ /* 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 ;)
*/ */
if (!nowayout) { if (!nowayout) {
size_t ofs; size_t ofs;
...@@ -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;
} }
...@@ -145,8 +162,10 @@ static int mixcomwd_ioctl(struct inode *inode, struct file *file, ...@@ -145,8 +162,10 @@ static int mixcomwd_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
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,28 +427,26 @@ static ssize_t pcwd_write(struct file *file, const char *buf, size_t len, ...@@ -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) 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:
return (-ENODEV); return (-ENODEV);
} }
} }
static ssize_t pcwd_read(struct file *file, char *buf, size_t count, 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, ...@@ -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) 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); outb_p(0xA5, current_readport + 3);
outb_p(0xA5, current_readport + 3); outb_p(0xA5, current_readport + 3);
outb_p(0xA5, current_readport + 3); 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)
......
/*
* 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;
/*
* 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");
...@@ -79,7 +80,7 @@ static int timer_alive; ...@@ -79,7 +80,7 @@ static int timer_alive;
static void watchdog_fire(unsigned long data) static void watchdog_fire(unsigned long data)
{ {
#ifdef ONLY_TESTING #ifdef ONLY_TESTING
printk(KERN_CRIT "SOFTDOG: Would Reboot.\n"); printk(KERN_CRIT "SOFTDOG: Would Reboot.\n");
#else #else
printk(KERN_CRIT "SOFTDOG: Initiating system reboot.\n"); printk(KERN_CRIT "SOFTDOG: Initiating system reboot.\n");
machine_restart(NULL); machine_restart(NULL);
...@@ -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;
} }
...@@ -332,9 +343,10 @@ static int wdtpci_ioctl(struct inode *inode, struct file *file, unsigned int cmd ...@@ -332,9 +343,10 @@ static int wdtpci_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 = {
.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