Commit bdc91033 authored by Linus Torvalds's avatar Linus Torvalds

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

into home.transmeta.com:/home/torvalds/v2.5/linux
parents 9cb52295 59409421
......@@ -283,4 +283,24 @@ config MACHZ_WDT
The module is called machzwd.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>.
config W83877F_WDT
tristate "W83877F Computer Watchdog"
depends on WATCHDOG
config SC520_WDT
tristate "AMD Elan SC520 processor Watchdog"
depends on WATCHDOG
config ALIM7101_WDT
tristate "ALi M7101 PMU Computer Watchdog"
depends on WATCHDOG
config SC1200_WDT
tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog"
depends on WATCHDOG
config WAFER_WDT
tristate "ICP Wafer 5823 Single Board Computer Watchdog"
depends on WATCHDOG
endmenu
......@@ -23,3 +23,8 @@ obj-$(CONFIG_MACHZ_WDT) += machzwd.o
obj-$(CONFIG_SH_WDT) += shwdt.o
obj-$(CONFIG_EUROTECH_WDT) += eurotechwdt.o
obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o
obj-$(CONFIG_SC520_WDT) += sc520_wdt.o
obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o
obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o
obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o
......@@ -45,6 +45,7 @@
static int acq_is_open;
static spinlock_t acq_lock;
static int expect_close = 0;
/*
* You must set these - there is no sane way to probe for this board.
......@@ -79,8 +80,20 @@ static ssize_t acq_write(struct file *file, const char *buf, size_t count, loff_
if (ppos != &file->f_pos)
return -ESPIPE;
if(count)
{
if(count) {
if (!nowayout) {
size_t i;
expect_close = 0;
for (i = 0; i != count; i++) {
char c;
if (get_user(c, buf + i))
return -EFAULT;
if (c == 'V')
expect_close = 1;
}
}
acq_ping();
return 1;
}
......@@ -97,9 +110,11 @@ static ssize_t acq_read(struct file *file, char *buf, size_t count, loff_t *ppos
static int acq_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
static struct watchdog_info ident=
static struct watchdog_info ident =
{
WDIOF_KEEPALIVEPING, 1, "Acquire WDT"
.options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "Acquire WDT"
};
switch(cmd)
......@@ -126,39 +141,35 @@ static int acq_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
static int acq_open(struct inode *inode, struct file *file)
{
switch(minor(inode->i_rdev))
{
case WATCHDOG_MINOR:
spin_lock(&acq_lock);
if(acq_is_open)
{
spin_unlock(&acq_lock);
return -EBUSY;
}
if (nowayout) {
MOD_INC_USE_COUNT;
}
/*
* Activate
*/
acq_is_open=1;
inb_p(WDT_START);
if ((minor(inode->i_rdev) == WATCHDOG_MINOR)) {
spin_lock(&acq_lock);
if(acq_is_open) {
spin_unlock(&acq_lock);
return 0;
default:
return -ENODEV;
return -EBUSY;
}
if (nowayout)
MOD_INC_USE_COUNT;
/* Activate */
acq_is_open=1;
inb_p(WDT_START);
spin_unlock(&acq_lock);
return 0;
} else {
return -ENODEV;
}
}
static int acq_close(struct inode *inode, struct file *file)
{
if(minor(inode->i_rdev)==WATCHDOG_MINOR)
{
if(minor(inode->i_rdev)==WATCHDOG_MINOR) {
spin_lock(&acq_lock);
if (!nowayout) {
if (expect_close)
inb_p(WDT_STOP);
}
else
printk(KERN_CRIT "WDT closed unexpectedly. WDT will not stop!\n");
acq_is_open=0;
spin_unlock(&acq_lock);
}
......@@ -173,10 +184,9 @@ static int acq_notify_sys(struct notifier_block *this, unsigned long code,
void *unused)
{
if(code==SYS_DOWN || code==SYS_HALT)
{
/* Turn the card off */
inb_p(WDT_STOP);
}
return NOTIFY_DONE;
}
......@@ -196,9 +206,9 @@ static struct file_operations acq_fops = {
static struct miscdevice acq_miscdev=
{
WATCHDOG_MINOR,
"watchdog",
&acq_fops
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &acq_fops
};
......@@ -207,11 +217,11 @@ static struct miscdevice acq_miscdev=
* turn the timebomb registers off.
*/
static struct notifier_block acq_notifier=
static struct notifier_block acq_notifier =
{
acq_notify_sys,
NULL,
0
.notifier_call = acq_notify_sys,
.next = NULL,
.priority = 0
};
static int __init acq_init(void)
......@@ -221,17 +231,15 @@ static int __init acq_init(void)
spin_lock_init(&acq_lock);
if (misc_register(&acq_miscdev))
return -ENODEV;
if (!request_region(WDT_STOP, 1, "Acquire WDT"))
{
if (!request_region(WDT_STOP, 1, "Acquire WDT")) {
misc_deregister(&acq_miscdev);
return -EIO;
}
if (!request_region(WDT_START, 1, "Acquire WDT"))
{
}
if (!request_region(WDT_START, 1, "Acquire WDT")) {
release_region(WDT_STOP, 1);
misc_deregister(&acq_miscdev);
return -EIO;
}
}
register_reboot_notifier(&acq_notifier);
return 0;
......
......@@ -47,6 +47,7 @@
#include <linux/smp_lock.h>
static int advwdt_is_open;
static char adv_expect_close;
static spinlock_t advwdt_lock;
/*
......@@ -99,10 +100,22 @@ advwdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
return -ESPIPE;
if (count) {
if (!nowayout) {
size_t i;
adv_expect_close = 0;
for (i = 0; i != count; i++) {
char c;
if (get_user(c, buf+i))
return -EFAULT;
if (c == 'V')
adv_expect_close = 42;
}
}
advwdt_ping();
return 1;
}
return 0;
return count;
}
static ssize_t
......@@ -116,9 +129,11 @@ advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
static struct watchdog_info ident = {
WDIOF_KEEPALIVEPING, 1, "Advantech WDT"
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "Advantech WDT"
};
switch (cmd) {
case WDIOC_GETSUPPORT:
if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)))
......@@ -143,26 +158,23 @@ advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
static int
advwdt_open(struct inode *inode, struct file *file)
{
switch (minor(inode->i_rdev)) {
case WATCHDOG_MINOR:
spin_lock(&advwdt_lock);
if (advwdt_is_open) {
spin_unlock(&advwdt_lock);
return -EBUSY;
}
if (nowayout) {
MOD_INC_USE_COUNT;
}
/*
* Activate
*/
advwdt_is_open = 1;
advwdt_ping();
if (minor(inode->i_rdev) == WATCHDOG_MINOR) {
spin_lock(&advwdt_lock);
if (advwdt_is_open) {
spin_unlock(&advwdt_lock);
return 0;
default:
return -ENODEV;
return -EBUSY;
}
if (nowayout)
MOD_INC_USE_COUNT;
/* Activate */
advwdt_is_open = 1;
advwdt_ping();
spin_unlock(&advwdt_lock);
return 0;
} else {
return -ENODEV;
}
}
......@@ -171,9 +183,9 @@ advwdt_close(struct inode *inode, struct file *file)
{
if (minor(inode->i_rdev) == WATCHDOG_MINOR) {
spin_lock(&advwdt_lock);
if (!nowayout) {
if (!nowayout)
inb_p(WDT_STOP);
}
advwdt_is_open = 0;
spin_unlock(&advwdt_lock);
}
......@@ -188,10 +200,10 @@ static int
advwdt_notify_sys(struct notifier_block *this, unsigned long code,
void *unused)
{
if (code == SYS_DOWN || code == SYS_HALT) {
if (code == SYS_DOWN || code == SYS_HALT)
/* Turn the WDT off */
inb_p(WDT_STOP);
}
return NOTIFY_DONE;
}
......@@ -209,9 +221,9 @@ static struct file_operations advwdt_fops = {
};
static struct miscdevice advwdt_miscdev = {
WATCHDOG_MINOR,
"watchdog",
&advwdt_fops
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &advwdt_fops
};
/*
......@@ -220,9 +232,9 @@ static struct miscdevice advwdt_miscdev = {
*/
static struct notifier_block advwdt_notifier = {
advwdt_notify_sys,
NULL,
0
.notifier_call = advwdt_notify_sys,
.next = NULL,
.priority = 0
};
static void __init
......@@ -276,5 +288,3 @@ module_exit(advwdt_exit);
MODULE_LICENSE("GPL");
/* end of advantechwdt.c */
/*
* 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");
This diff is collapsed.
......@@ -244,7 +244,9 @@ static int i810tco_ioctl (struct inode *inode, struct file *file,
int options, retval = -EINVAL;
static struct watchdog_info ident = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
.options = WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING |
WDIOF_MAGICCLOSE,
.firmware_version = 0,
.identity = "i810 TCO timer",
};
......
......@@ -54,6 +54,7 @@
static int ibwdt_is_open;
static spinlock_t ibwdt_lock;
static int expect_close = 0;
/*
*
......@@ -136,6 +137,21 @@ ibwdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
return -ESPIPE;
if (count) {
if (!nowayout) {
size_t i;
/* In case it was set long ago */
expect_close = 0;
for (i = 0; i != count; i++) {
char c;
if (get_user(c, buf + i))
return -EFAULT;
if (c == 'V')
expect_close = 1;
}
}
ibwdt_ping();
return 1;
}
......@@ -153,7 +169,9 @@ ibwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
static struct watchdog_info ident = {
WDIOF_KEEPALIVEPING, 1, "IB700 WDT"
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "IB700 WDT"
};
switch (cmd) {
......@@ -180,42 +198,38 @@ ibwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
static int
ibwdt_open(struct inode *inode, struct file *file)
{
switch (minor(inode->i_rdev)) {
case WATCHDOG_MINOR:
spin_lock(&ibwdt_lock);
if (ibwdt_is_open) {
spin_unlock(&ibwdt_lock);
return -EBUSY;
}
if (nowayout) {
MOD_INC_USE_COUNT;
}
/*
* Activate
*/
ibwdt_is_open = 1;
ibwdt_ping();
if (minor(inode->i_rdev) == WATCHDOG_MINOR) {
spin_lock(&ibwdt_lock);
if (ibwdt_is_open) {
spin_unlock(&ibwdt_lock);
return 0;
default:
return -ENODEV;
return -EBUSY;
}
if (nowayout)
MOD_INC_USE_COUNT;
/* Activate */
ibwdt_is_open = 1;
ibwdt_ping();
spin_unlock(&ibwdt_lock);
return 0;
} else {
return -ENODEV;
}
}
static int
ibwdt_close(struct inode *inode, struct file *file)
{
lock_kernel();
if (minor(inode->i_rdev) == WATCHDOG_MINOR) {
spin_lock(&ibwdt_lock);
if (!nowayout) {
if (expect_close)
outb_p(timeout_val, WDT_STOP);
}
else
printk(KERN_CRIT "WDT device closed unexpectedly. WDT will not stop!\n");
ibwdt_is_open = 0;
spin_unlock(&ibwdt_lock);
}
unlock_kernel();
return 0;
}
......@@ -248,9 +262,9 @@ static struct file_operations ibwdt_fops = {
};
static struct miscdevice ibwdt_miscdev = {
WATCHDOG_MINOR,
"watchdog",
&ibwdt_fops
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &ibwdt_fops
};
/*
......@@ -259,9 +273,9 @@ static struct miscdevice ibwdt_miscdev = {
*/
static struct notifier_block ibwdt_notifier = {
ibwdt_notify_sys,
NULL,
0
.notifier_call = ibwdt_notify_sys,
.next = NULL,
.priority = 0
};
static int __init
......
/*
* 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
#define PFX "machzwd"
static struct watchdog_info zf_info = {
.options = WDIOF_KEEPALIVEPING,
.options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "ZF-Logic watchdog"
};
......@@ -314,10 +314,10 @@ static ssize_t zf_write(struct file *file, const char *buf, size_t count,
/* See if we got the magic character */
if(count){
/*
* no need to check for close confirmation
* no way to disable watchdog ;)
*/
/*
* no need to check for close confirmation
* no way to disable watchdog ;)
*/
if (!nowayout) {
size_t ofs;
......@@ -328,7 +328,7 @@ static ssize_t zf_write(struct file *file, const char *buf, size_t count,
zf_expect_close = 0;
/* now scan */
for(ofs = 0; ofs != count; ofs++){
for (ofs = 0; ofs != count; ofs++){
char c;
if (get_user(c, buf + ofs))
return -EFAULT;
......@@ -338,6 +338,7 @@ static ssize_t zf_write(struct file *file, const char *buf, size_t count,
}
}
}
/*
* Well, anyhow someone wrote to us,
* we should return that favour
......@@ -395,9 +396,9 @@ static int zf_open(struct inode *inode, struct file *file)
return -EBUSY;
}
if (nowayout) {
if (nowayout)
MOD_INC_USE_COUNT;
}
zf_is_open = 1;
spin_unlock(&zf_lock);
......
......@@ -62,6 +62,7 @@ static long mixcomwd_opened; /* long req'd for setbit --RR */
static int watchdog_port;
static int mixcomwd_timer_alive;
static struct timer_list mixcomwd_timer = TIMER_INITIALIZER(NULL, 0, 0);
static int expect_close = 0;
#ifdef CONFIG_WATCHDOG_NOWAYOUT
static int nowayout = 1;
......@@ -109,8 +110,7 @@ static int mixcomwd_open(struct inode *inode, struct file *file)
static int mixcomwd_release(struct inode *inode, struct file *file)
{
if (!nowayout) {
if (expect_close) {
if(mixcomwd_timer_alive) {
printk(KERN_ERR "mixcomwd: release called while internal timer alive");
return -EBUSY;
......@@ -121,7 +121,10 @@ static int mixcomwd_release(struct inode *inode, struct file *file)
mixcomwd_timer.data=0;
mixcomwd_timer_alive=1;
add_timer(&mixcomwd_timer);
} else {
printk(KERN_CRIT "mixcomwd: WDT device closed unexpectedly. WDT will not stop!\n");
}
clear_bit(0,&mixcomwd_opened);
return 0;
}
......@@ -135,6 +138,20 @@ static ssize_t mixcomwd_write(struct file *file, const char *data, size_t len, l
if(len)
{
if (!nowayout) {
size_t i;
/* In case it was set long ago */
expect_close = 0;
for (i = 0; i != len; i++) {
char c;
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V')
expect_close = 1;
}
}
mixcomwd_ping();
return 1;
}
......@@ -145,8 +162,10 @@ static int mixcomwd_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int status;
static struct watchdog_info ident = {
WDIOF_KEEPALIVEPING, 1, "MixCOM watchdog"
static struct watchdog_info ident = {
.options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "MixCOM watchdog"
};
switch(cmd)
......
......@@ -40,6 +40,8 @@
* fairly useless proc entry.
* 990610 removed said useless proc code for the merge <alan>
* 000403 Removed last traces of proc code. <davej>
* 011214 Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT <Matt_Domsch@dell.com>
* Added timeout module option to override default
*/
#include <linux/module.h>
......@@ -76,7 +78,7 @@
*/
static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 };
#define WD_VER "1.10 (06/05/99)"
#define WD_VER "1.12 (12/14/2001)"
/*
* It should be noted that PCWD_REVISION_B was removed because A and B
......@@ -88,7 +90,23 @@ static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 };
#define PCWD_REVISION_A 1
#define PCWD_REVISION_C 2
#define WD_TIMEOUT 3 /* 1 1/2 seconds for a timeout */
#define WD_TIMEOUT 4 /* 2 seconds for a timeout */
static int timeout_val = WD_TIMEOUT;
static int timeout = 2;
static int expect_close = 0;
MODULE_PARM(timeout,"i");
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default=2)");
#ifdef CONFIG_WATCHDOG_NOWAYOUT
static int nowayout = 1;
#else
static int nowayout = 0;
#endif
MODULE_PARM(nowayout,"i");
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
/*
* These are the defines for the PC Watchdog card, revision A.
......@@ -121,7 +139,7 @@ static int __init pcwd_checkcard(void)
if (prev_card_dat == 0xFF)
return 0;
while(count < WD_TIMEOUT) {
while(count < timeout_val) {
/* Read the raw card data from the port, and strip off the
first 4 bits */
......@@ -256,7 +274,7 @@ static int pcwd_ioctl(struct inode *inode, struct file *file,
else
cdat = inb(current_readport + 1 );
spin_unlock(&io_lock);
rv = 0;
rv = WDIOF_MAGICCLOSE;
if (revision == PCWD_REVISION_A)
{
......@@ -385,8 +403,22 @@ static ssize_t pcwd_write(struct file *file, const char *buf, size_t len,
if (ppos != &file->f_pos)
return -ESPIPE;
if (len)
{
if (len) {
if (!nowayout) {
size_t i;
/* In case it was set long ago */
expect_close = 0;
for (i = 0; i != len; i++) {
char c;
if (get_user(c, buf + i))
return -EFAULT;
if (c == 'V')
expect_close = 1;
}
}
pcwd_send_heartbeat();
return 1;
}
......@@ -395,28 +427,26 @@ static ssize_t pcwd_write(struct file *file, const char *buf, size_t len,
static int pcwd_open(struct inode *ino, struct file *filep)
{
switch (minor(ino->i_rdev))
{
case WATCHDOG_MINOR:
if ( !atomic_dec_and_test(&open_allowed) )
{
switch (minor(ino->i_rdev)) {
case WATCHDOG_MINOR:
if (!atomic_dec_and_test(&open_allowed) ) {
atomic_inc( &open_allowed );
return -EBUSY;
}
MOD_INC_USE_COUNT;
/* Enable the port */
if (revision == PCWD_REVISION_C)
{
spin_lock(&io_lock);
outb_p(0x00, current_readport + 3);
spin_unlock(&io_lock);
}
return(0);
case TEMP_MINOR:
return(0);
default:
return (-ENODEV);
}
return -EBUSY;
}
MOD_INC_USE_COUNT;
/* Enable the port */
if (revision == PCWD_REVISION_C) {
spin_lock(&io_lock);
outb_p(0x00, current_readport + 3);
spin_unlock(&io_lock);
}
return(0);
case TEMP_MINOR:
return(0);
default:
return (-ENODEV);
}
}
static ssize_t pcwd_read(struct file *file, char *buf, size_t count,
......@@ -448,18 +478,17 @@ static ssize_t pcwd_read(struct file *file, char *buf, size_t count,
static int pcwd_close(struct inode *ino, struct file *filep)
{
if (minor(ino->i_rdev)==WATCHDOG_MINOR)
{
#ifndef CONFIG_WATCHDOG_NOWAYOUT
/* Disable the board */
if (revision == PCWD_REVISION_C) {
spin_lock(&io_lock);
outb_p(0xA5, current_readport + 3);
outb_p(0xA5, current_readport + 3);
spin_unlock(&io_lock);
if (minor(ino->i_rdev)==WATCHDOG_MINOR) {
if (expect_close) {
/* Disable the board */
if (revision == PCWD_REVISION_C) {
spin_lock(&io_lock);
outb_p(0xA5, current_readport + 3);
outb_p(0xA5, current_readport + 3);
spin_unlock(&io_lock);
}
atomic_inc( &open_allowed );
}
atomic_inc( &open_allowed );
#endif
}
return 0;
}
......@@ -560,9 +589,15 @@ static struct miscdevice temp_miscdev = {
&pcwd_fops
};
static void __init pcwd_validate_timeout(void)
{
timeout_val = timeout * 2;
}
static int __init pcwatchdog_init(void)
{
int i, found = 0;
pcwd_validate_timeout();
spin_lock_init(&io_lock);
revision = PCWD_REVISION_A;
......
......@@ -244,9 +244,9 @@ static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
{
static struct watchdog_info ident=
{
0,
1,
"SB60xx"
.options = WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "SB60xx"
};
switch(cmd)
......
This diff is collapsed.
/*
* AMD Elan SC520 processor Watchdog Timer driver for Linux 2.4.x
*
* Based on acquirewdt.c by Alan Cox,
* and sbc60xxwdt.c by Jakob Oestergaard <jakob@ostenfeld.dk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* The authors do NOT admit liability nor provide warranty for
* any of this software. This material is provided "AS-IS" in
* the hope that it may be useful for others.
*
* (c) Copyright 2001 Scott Jennings <linuxdrivers@oro.net>
* 9/27 - 2001 [Initial release]
*
* Additional fixes Alan Cox
* - Fixed formatting
* - Removed debug printks
* - Fixed SMP built kernel deadlock
* - Switched to private locks not lock_kernel
* - Used ioremap/writew/readw
* - Added NOWAYOUT support
*
* Theory of operation:
* A Watchdog Timer (WDT) is a hardware circuit that can
* reset the computer system in case of a software fault.
* You probably knew that already.
*
* Usually a userspace daemon will notify the kernel WDT driver
* via the /proc/watchdog special device file that userspace is
* still alive, at regular intervals. When such a notification
* occurs, the driver will usually tell the hardware watchdog
* that everything is in order, and that the watchdog should wait
* for yet another little while to reset the system.
* If userspace fails (RAM error, kernel bug, whatever), the
* notifications cease to occur, and the hardware watchdog will
* reset the system (causing a reboot) after the timeout occurs.
*
* This WDT driver is different from most other Linux WDT
* drivers in that the driver will ping the watchdog by itself,
* because this particular WDT has a very short timeout (1.6
* seconds) and it would be insane to count on any userspace
* daemon always getting scheduled within that time frame.
*
* This driver uses memory mapped IO, and spinlock.
*/
#include <linux/module.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/sched.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/fcntl.h>
#include <linux/smp_lock.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/init.h>
/*
* The SC520 can timeout anywhere from 492us to 32.21s.
* If we reset the watchdog every ~250ms we should be safe.
*/
#define WDT_INTERVAL (HZ/4+1)
/*
* We must not require too good response from the userspace daemon.
* Here we require the userspace daemon to send us a heartbeat
* char to /dev/watchdog every 30 seconds.
*/
#define WDT_HEARTBEAT (HZ * 30)
/*
* AMD Elan SC520 timeout value is 492us times a power of 2 (0-7)
*
* 0: 492us 2: 1.01s 4: 4.03s 6: 16.22s
* 1: 503ms 3: 2.01s 5: 8.05s 7: 32.21s
*/
#define TIMEOUT_EXPONENT ( 1 << 3 ) /* 0x08 = 2.01s */
/* #define MMCR_BASE_DEFAULT 0xfffef000 */
#define MMCR_BASE_DEFAULT ((__u16 *)0xffffe)
#define OFFS_WDTMRCTL ((unsigned int)0xcb0)
#define WDT_ENB 0x8000 /* [15] Watchdog Timer Enable */
#define WDT_WRST_ENB 0x4000 /* [14] Watchdog Timer Reset Enable */
#define OUR_NAME "sc520_wdt"
#define WRT_DOG(data) *wdtmrctl=data
static __u16 *wdtmrctl;
static void wdt_timer_ping(unsigned long);
static struct timer_list timer;
static unsigned long next_heartbeat;
static unsigned long wdt_is_open;
static int wdt_expect_close;
#ifdef CONFIG_WATCHDOG_NOWAYOUT
static int nowayout = 1;
#else
static int nowayout = 0;
#endif
MODULE_PARM(nowayout,"i");
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
static spinlock_t wdt_spinlock;
/*
* Whack the dog
*/
static void wdt_timer_ping(unsigned long data)
{
/* If we got a heartbeat pulse within the WDT_US_INTERVAL
* we agree to ping the WDT
*/
if(time_before(jiffies, next_heartbeat))
{
/* Ping the WDT */
spin_lock(&wdt_spinlock);
writew(0xAAAA, wdtmrctl);
writew(0x5555, wdtmrctl);
spin_unlock(&wdt_spinlock);
/* Re-set the timer interval */
timer.expires = jiffies + WDT_INTERVAL;
add_timer(&timer);
} else {
printk(OUR_NAME ": Heartbeat lost! Will not ping the watchdog\n");
}
}
/*
* Utility routines
*/
static void wdt_config(int writeval)
{
__u16 dummy;
unsigned long flags;
/* buy some time (ping) */
spin_lock_irqsave(&wdt_spinlock, flags);
dummy=readw(wdtmrctl); /* ensure write synchronization */
writew(0xAAAA, wdtmrctl);
writew(0x5555, wdtmrctl);
/* make WDT configuration register writable one time */
writew(0x3333, wdtmrctl);
writew(0xCCCC, wdtmrctl);
/* write WDT configuration register */
writew(writeval, wdtmrctl);
spin_unlock_irqrestore(&wdt_spinlock, flags);
}
static void wdt_startup(void)
{
next_heartbeat = jiffies + WDT_HEARTBEAT;
/* Start the timer */
timer.expires = jiffies + WDT_INTERVAL;
add_timer(&timer);
wdt_config(WDT_ENB | WDT_WRST_ENB | TIMEOUT_EXPONENT);
printk(OUR_NAME ": Watchdog timer is now enabled.\n");
}
static void wdt_turnoff(void)
{
if (!nowayout) {
/* Stop the timer */
del_timer(&timer);
wdt_config(0);
printk(OUR_NAME ": Watchdog timer is now disabled...\n");
}
}
/*
* /dev/watchdog handling
*/
static ssize_t fop_write(struct file * file, const char * buf, size_t count, loff_t * ppos)
{
/* We can't seek */
if(ppos != &file->f_pos)
return -ESPIPE;
/* See if we got the magic character */
if(count)
{
size_t ofs;
/* note: just in case someone wrote the magic character
* five months ago... */
wdt_expect_close = 0;
/* now scan */
for(ofs = 0; ofs != count; ofs++) {
char c;
if (get_user(c, buf + ofs))
return -EFAULT;
if(c == 'V')
wdt_expect_close = 1;
}
/* Well, anyhow someone wrote to us, we should return that favour */
next_heartbeat = jiffies + WDT_HEARTBEAT;
return 1;
}
return 0;
}
static int fop_open(struct inode * inode, struct file * file)
{
switch(minor(inode->i_rdev))
{
case WATCHDOG_MINOR:
/* Just in case we're already talking to someone... */
if(test_and_set_bit(0, &wdt_is_open))
return -EBUSY;
/* Good, fire up the show */
wdt_startup();
if (nowayout)
MOD_INC_USE_COUNT;
return 0;
default:
return -ENODEV;
}
}
static int fop_close(struct inode * inode, struct file * file)
{
if(minor(inode->i_rdev) == WATCHDOG_MINOR)
{
if(wdt_expect_close)
wdt_turnoff();
else {
del_timer(&timer);
printk(OUR_NAME ": device file closed unexpectedly. Will not stop the WDT!\n");
}
}
clear_bit(0, &wdt_is_open);
return 0;
}
static long long fop_llseek(struct file *file, long long offset, int origin)
{
return -ESPIPE;
}
static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
static struct watchdog_info ident=
{
.options = WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "SC520"
};
switch(cmd)
{
default:
return -ENOIOCTLCMD;
case WDIOC_GETSUPPORT:
return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))?-EFAULT:0;
case WDIOC_KEEPALIVE:
next_heartbeat = jiffies + WDT_HEARTBEAT;
return 0;
}
}
static struct file_operations wdt_fops = {
owner: THIS_MODULE,
llseek: fop_llseek,
write: fop_write,
open: fop_open,
release: fop_close,
ioctl: fop_ioctl
};
static struct miscdevice wdt_miscdev = {
WATCHDOG_MINOR,
"watchdog",
&wdt_fops
};
/*
* Notifier for system down
*/
static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
void *unused)
{
if(code==SYS_DOWN || code==SYS_HALT)
wdt_turnoff();
return NOTIFY_DONE;
}
/*
* The WDT needs to learn about soft shutdowns in order to
* turn the timebomb registers off.
*/
static struct notifier_block wdt_notifier=
{
wdt_notify_sys,
0,
0
};
static void __exit sc520_wdt_unload(void)
{
wdt_turnoff();
/* Deregister */
misc_deregister(&wdt_miscdev);
iounmap(wdtmrctl);
unregister_reboot_notifier(&wdt_notifier);
}
static int __init sc520_wdt_init(void)
{
int rc = -EBUSY;
unsigned long cbar;
spin_lock_init(&wdt_spinlock);
init_timer(&timer);
timer.function = wdt_timer_ping;
timer.data = 0;
rc = misc_register(&wdt_miscdev);
if (rc)
goto err_out_region2;
rc = register_reboot_notifier(&wdt_notifier);
if (rc)
goto err_out_miscdev;
/* get the Base Address Register */
cbar = inl_p(0xfffc);
printk(OUR_NAME ": CBAR: 0x%08lx\n", cbar);
/* check if MMCR aliasing bit is set */
if (cbar & 0x80000000) {
printk(OUR_NAME ": MMCR Aliasing enabled.\n");
wdtmrctl = (__u16 *)(cbar & 0x3fffffff);
} else {
printk(OUR_NAME "!!! WARNING !!!\n"
"\t MMCR Aliasing found NOT enabled!\n"
"\t Using default value of: %p\n"
"\t This has not been tested!\n"
"\t Please email Scott Jennings <smj@oro.net>\n"
"\t and Bill Jennings <bj@oro.net> if it works!\n"
, MMCR_BASE_DEFAULT
);
wdtmrctl = MMCR_BASE_DEFAULT;
}
wdtmrctl = (__u16 *)((char *)wdtmrctl + OFFS_WDTMRCTL);
wdtmrctl = ioremap((unsigned long)wdtmrctl, 2);
printk(KERN_INFO OUR_NAME ": WDT driver for SC520 initialised.\n");
return 0;
err_out_miscdev:
misc_deregister(&wdt_miscdev);
err_out_region2:
return rc;
}
module_init(sc520_wdt_init);
module_exit(sc520_wdt_unload);
MODULE_AUTHOR("Scott and Bill Jennings");
MODULE_DESCRIPTION("Driver for watchdog timer in AMD \"Elan\" SC520 uProcessor");
MODULE_LICENSE("GPL");
EXPORT_NO_SYMBOLS;
......@@ -334,21 +334,21 @@ static struct file_operations sh_wdt_fops = {
};
static struct watchdog_info sh_wdt_info = {
WDIOF_KEEPALIVEPING,
1,
"SH WDT",
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "SH WDT",
};
static struct notifier_block sh_wdt_notifier = {
sh_wdt_notify_sys,
NULL,
0
.notifier_call = sh_wdt_notify_sys,
.next = NULL,
.priority = 0
};
static struct miscdevice sh_wdt_miscdev = {
WATCHDOG_MINOR,
"watchdog",
&sh_wdt_fops,
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops &sh_wdt_fops,
};
/**
......
......@@ -47,6 +47,7 @@
#define TIMER_MARGIN 60 /* (secs) Default is 1 minute */
static int expect_close = 0;
static int soft_margin = TIMER_MARGIN; /* in seconds */
MODULE_PARM(soft_margin,"i");
......@@ -79,7 +80,7 @@ static int timer_alive;
static void watchdog_fire(unsigned long data)
{
#ifdef ONLY_TESTING
printk(KERN_CRIT "SOFTDOG: Would Reboot.\n");
printk(KERN_CRIT "SOFTDOG: Would Reboot.\n");
#else
printk(KERN_CRIT "SOFTDOG: Initiating system reboot.\n");
machine_restart(NULL);
......@@ -114,6 +115,8 @@ static int softdog_release(struct inode *inode, struct file *file)
*/
if(!nowayout) {
del_timer(&watchdog_ticktock);
} else {
printk(KERN_CRIT "SOFTDOG: WDT device closed unexpectedly. WDT will not stop!\n");
}
timer_alive=0;
return 0;
......@@ -129,6 +132,21 @@ static ssize_t softdog_write(struct file *file, const char *data, size_t len, lo
* Refresh the timer.
*/
if(len) {
if (!nowayout) {
size_t i;
/* In case it was set long ago */
expect_close = 0;
for (i = 0; i != len; i++) {
char c;
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V')
expect_close = 1;
}
}
mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ));
return 1;
}
......@@ -139,6 +157,7 @@ static int softdog_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
static struct watchdog_info ident = {
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
.identity = "Software Watchdog",
};
switch (cmd) {
......
......@@ -251,9 +251,9 @@ static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
{
static struct watchdog_info ident=
{
0,
1,
"W83877F"
.options = WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "W83877F"
};
switch(cmd)
......
/*
* 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 @@
#include <asm/system.h>
static unsigned long wdt_is_open;
static int expect_close;
/*
* You must set these - there is no sane way to probe for this board.
......@@ -258,8 +259,21 @@ static ssize_t wdt_write(struct file *file, const char *buf, size_t count, loff_
if (ppos != &file->f_pos)
return -ESPIPE;
if(count)
{
if(count) {
if (!nowayout) {
size_t i;
/* In case it was set long ago */
expect_close = 0;
for (i = 0; i != count; i++) {
char c;
if (get_user(c, buf + i))
return -EFAULT;
if (c == 'V')
expect_close = 1;
}
}
wdt_ping();
return 1;
}
......@@ -317,10 +331,11 @@ static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
{
static struct watchdog_info ident=
{
WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER
|WDIOF_EXTERN1|WDIOF_EXTERN2|WDIOF_FANFAULT,
1,
"WDT500/501"
.options = WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER
|WDIOF_EXTERN1|WDIOF_EXTERN2|WDIOF_FANFAULT
|WDIOF_SETTIMEOUT|WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "WDT500/501"
};
ident.options&=WDT_OPTION_MASK; /* Mask down to the card we have */
......@@ -399,9 +414,11 @@ static int wdt_release(struct inode *inode, struct file *file)
{
if(minor(inode->i_rdev)==WATCHDOG_MINOR)
{
if (!nowayout) {
if (expect_close) {
inb_p(WDT_DC); /* Disable counters */
wdt_ctr_load(2,0); /* 0 length reset pulses now */
} else {
printk(KERN_CRIT "wdt: WDT device closed unexpectedly. WDT will not stop!\n");
}
clear_bit(0, &wdt_is_open);
}
......
......@@ -41,6 +41,7 @@ static int timeout = DEFAULT_TIMEOUT*60; /* TO in seconds from user */
static int timeoutM = DEFAULT_TIMEOUT; /* timeout in minutes */
static unsigned long timer_alive;
static int testmode;
static int expect_close = 0;
MODULE_PARM(timeout, "i");
MODULE_PARM_DESC(timeout,"Watchdog timeout in seconds (60..15300), default=60");
......@@ -196,6 +197,8 @@ static int wdt977_release(struct inode *inode, struct file *file)
clear_bit(0,&timer_alive);
printk(KERN_INFO "Wdt977 Watchdog: shutdown\n");
} else {
printk(KERN_CRIT "WDT device closed unexpectedly. WDT will not stop!\n");
}
return 0;
}
......@@ -220,6 +223,21 @@ static ssize_t wdt977_write(struct file *file, const char *buf, size_t count, lo
if(count)
{
if (!nowayout) {
size_t i;
/* In case it was set long ago */
expect_close = 0;
for (i = 0; i != len; i++) {
char c;
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V')
expect_close = 1;
}
}
kick_wdog();
return 1;
}
......
......@@ -73,6 +73,7 @@
#endif
static unsigned long wdt_is_open;
static int expect_close = 0;
/*
* You must set these - there is no sane way to probe for this board.
......@@ -276,6 +277,16 @@ static ssize_t wdtpci_write(struct file *file, const char *buf, size_t count, lo
if(count)
{
if (!nowayout) {
size_t i;
for (i = 0; i != count; i++) {
char c;
if(get_user(c, buf+i))
return -EFAULT;
if (c == 'V')
expect_close = 1;
}
}
wdtpci_ping();
return 1;
}
......@@ -332,9 +343,10 @@ static int wdtpci_ioctl(struct inode *inode, struct file *file, unsigned int cmd
unsigned long arg)
{
static struct watchdog_info ident = {
.options = WDIOF_OVERHEAT | WDIOF_POWERUNDER |
WDIOF_POWEROVER | WDIOF_EXTERN1 |
WDIOF_EXTERN2 | WDIOF_FANFAULT,
.options = WDIOF_OVERHEAT | WDIOF_POWERUNDER |
WDIOF_POWEROVER | WDIOF_EXTERN1 |
WDIOF_EXTERN2 | WDIOF_FANFAULT |
WDIOF_SETTIMEOUT|WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "WDT500/501PCI",
};
......
......@@ -39,6 +39,7 @@ struct watchdog_info {
#define WDIOF_CARDRESET 0x0020 /* Card previously reset the CPU */
#define WDIOF_POWEROVER 0x0040 /* Power over voltage */
#define WDIOF_SETTIMEOUT 0x0080 /* Set timeout (in seconds) */
#define WDIOF_MAGICCLOSE 0x0100 /* Supports magic close char */
#define WDIOF_KEEPALIVEPING 0x8000 /* Keep alive ping reply */
#define WDIOS_DISABLECARD 0x0001 /* Turn off the watchdog timer */
......
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