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
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");
......@@ -7,23 +7,23 @@
* Based on wdt.c.
* Original copyright messages:
*
* (c) Copyright 1996-1997 Alan Cox <alan@redhat.com>, All Rights Reserved.
* http://www.redhat.com
* (c) Copyright 1996-1997 Alan Cox <alan@redhat.com>, All Rights Reserved.
* http://www.redhat.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
* warranty for any of this software. This material is provided
* "AS-IS" and at no charge.
* Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
* warranty for any of this software. This material is provided
* "AS-IS" and at no charge.
*
* (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>*
* (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
*
* 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
* Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
* Added timeout module option to override default
* 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
* Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
* Added timeout module option to override default
*/
#include <linux/config.h>
......@@ -49,17 +49,18 @@
static int eurwdt_is_open;
static spinlock_t eurwdt_lock;
static char eur_expect_close;
/*
* You must set these - there is no sane way to probe for this board.
* You can use wdt=x,y to set these now.
* You must set these - there is no sane way to probe for this board.
* You can use wdt=x,y to set these now.
*/
static int io = 0x3f0;
static int irq = 10;
static char *ev = "int";
#define WDT_TIMEOUT 60 /* 1 minute */
#define WDT_TIMEOUT 60 /* 1 minute */
static int timeout = WDT_TIMEOUT;
MODULE_PARM(timeout,"i");
......@@ -80,10 +81,10 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CON
#define WDT_CTRL_REG 0x30
#define WDT_OUTPIN_CFG 0xe2
#define WDT_EVENT_INT 0x00
#define WDT_EVENT_REBOOT 0x08
#define WDT_EVENT_INT 0x00
#define WDT_EVENT_REBOOT 0x08
#define WDT_UNIT_SEL 0xf1
#define WDT_UNIT_SECS 0x80
#define WDT_UNIT_SECS 0x80
#define WDT_TIMEOUT_VAL 0xf2
#define WDT_TIMER_CFG 0xf3
......@@ -91,27 +92,27 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CON
#ifndef MODULE
/**
* eurwdt_setup:
* @str: command line string
* eurwdt_setup:
* @str: command line string
*
* Setup options. The board isn't really probe-able so we have to
* get the user to tell us the configuration. Sane people build it
* modular but the others come here.
* Setup options. The board isn't really probe-able so we have to
* get the user to tell us the configuration. Sane people build it
* modular but the others come here.
*/
static int __init eurwdt_setup(char *str)
{
int ints[4];
int ints[4];
str = get_options (str, ARRAY_SIZE(ints), ints);
str = get_options (str, ARRAY_SIZE(ints), ints);
if (ints[0] > 0) {
io = ints[1];
if (ints[0] > 1)
irq = ints[2];
}
if (ints[0] > 0) {
io = ints[1];
if (ints[0] > 1)
irq = ints[2];
}
return 1;
return 1;
}
__setup("wdt=", eurwdt_setup);
......@@ -127,7 +128,7 @@ MODULE_PARM_DESC(ev, "Eurotech WDT event type (default is `reboot')");
/*
* Programming support
* Programming support
*/
static void __init eurwdt_validate_timeout(void)
......@@ -135,265 +136,276 @@ static void __init eurwdt_validate_timeout(void)
if (timeout < 0 || timeout > 255) {
timeout = WDT_TIMEOUT;
printk(KERN_INFO "eurwdt: timeout must be 0 < x < 255, using %d\n",
timeout);
timeout);
}
}
static inline void eurwdt_write_reg(u8 index, u8 data)
{
outb(index, io);
outb(data, io+1);
outb(index, io);
outb(data, io+1);
}
static inline void eurwdt_lock_chip(void)
{
outb(0xaa, io);
outb(0xaa, io);
}
static inline void eurwdt_unlock_chip(void)
{
outb(0x55, io);
eurwdt_write_reg(0x07, 0x08); /* set the logical device */
outb(0x55, io);
eurwdt_write_reg(0x07, 0x08); /* set the logical device */
}
static inline void eurwdt_set_timeout(int timeout)
{
eurwdt_write_reg(WDT_TIMEOUT_VAL, (u8) timeout);
eurwdt_write_reg(WDT_TIMEOUT_VAL, (u8) timeout);
}
static inline void eurwdt_disable_timer(void)
{
eurwdt_set_timeout(0);
eurwdt_set_timeout(0);
}
static void eurwdt_activate_timer(void)
{
eurwdt_disable_timer();
eurwdt_write_reg(WDT_CTRL_REG, 0x01); /* activate the WDT */
eurwdt_write_reg(WDT_OUTPIN_CFG, !strcmp("int", ev) ?
WDT_EVENT_INT : WDT_EVENT_REBOOT);
/* Setting interrupt line */
if (irq == 2 || irq > 15 || irq < 0) {
printk(KERN_ERR ": invalid irq number\n");
irq = 0; /* if invalid we disable interrupt */
}
if (irq == 0)
printk(KERN_INFO ": interrupt disabled\n");
eurwdt_write_reg(WDT_TIMER_CFG, irq<<4);
eurwdt_write_reg(WDT_UNIT_SEL, WDT_UNIT_SECS); /* we use seconds */
eurwdt_set_timeout(0); /* the default timeout */
eurwdt_disable_timer();
eurwdt_write_reg(WDT_CTRL_REG, 0x01); /* activate the WDT */
eurwdt_write_reg(WDT_OUTPIN_CFG, !strcmp("int", ev) ? WDT_EVENT_INT : WDT_EVENT_REBOOT);
/* Setting interrupt line */
if (irq == 2 || irq > 15 || irq < 0) {
printk(KERN_ERR ": invalid irq number\n");
irq = 0; /* if invalid we disable interrupt */
}
if (irq == 0)
printk(KERN_INFO ": interrupt disabled\n");
eurwdt_write_reg(WDT_TIMER_CFG, irq<<4);
eurwdt_write_reg(WDT_UNIT_SEL, WDT_UNIT_SECS); /* we use seconds */
eurwdt_set_timeout(0); /* the default timeout */
}
/*
* Kernel methods.
* Kernel methods.
*/
void eurwdt_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
printk(KERN_CRIT "timeout WDT timeout\n");
printk(KERN_CRIT "timeout WDT timeout\n");
#ifdef ONLY_TESTING
printk(KERN_CRIT "Would Reboot.\n");
printk(KERN_CRIT "Would Reboot.\n");
#else
printk(KERN_CRIT "Initiating system reboot.\n");
machine_restart(NULL);
printk(KERN_CRIT "Initiating system reboot.\n");
machine_restart(NULL);
#endif
}
/**
* eurwdt_ping:
* eurwdt_ping:
*
* Reload counter one with the watchdog timeout.
* Reload counter one with the watchdog timeout.
*/
static void eurwdt_ping(void)
{
/* Write the watchdog default value */
eurwdt_set_timeout(timeout);
/* Write the watchdog default value */
eurwdt_set_timeout(timeout);
}
/**
* eurwdt_write:
* @file: file handle to the watchdog
* @buf: buffer to write (unused as data does not matter here
* @count: count of bytes
* @ppos: pointer to the position to write. No seeks allowed
* eurwdt_write:
* @file: file handle to the watchdog
* @buf: buffer to write (unused as data does not matter here
* @count: count of bytes
* @ppos: pointer to the position to write. No seeks allowed
*
* A write to a watchdog device is defined as a keepalive signal. Any
* write of data will do, as we we don't define content meaning.
* A write to a watchdog device is defined as a keepalive signal. Any
* write of data will do, as we we don't define content meaning.
*/
static ssize_t eurwdt_write(struct file *file, const char *buf, size_t count,
loff_t *ppos)
{
/* Can't seek (pwrite) on this device */
if (ppos != &file->f_pos)
return -ESPIPE;
if (count) {
eurwdt_ping(); /* the default timeout */
return 1;
}
/* Can't seek (pwrite) on this device */
if (ppos != &file->f_pos)
return -ESPIPE;
if (count) {
if (!nowayout) {
size_t i;
eur_expect_close = 0;
for (i = 0; i != count; i++) {
char c;
if(get_user(c, buf+i))
return -EFAULT;
if (c == 'V')
eur_expect_close = 42;
}
}
eurwdt_ping(); /* the default timeout */
return 1;
}
return 0;
return 0;
}
/**
* eurwdt_ioctl:
* @inode: inode of the device
* @file: file handle to the device
* @cmd: watchdog command
* @arg: argument pointer
* eurwdt_ioctl:
* @inode: inode of the device
* @file: file handle to the device
* @cmd: watchdog command
* @arg: argument pointer
*
* The watchdog API defines a common set of functions for all watchdogs
* according to their available features.
* The watchdog API defines a common set of functions for all watchdogs
* according to their available features.
*/
static int eurwdt_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
unsigned int cmd, unsigned long arg)
{
static struct watchdog_info ident = {
.options = WDIOF_CARDRESET,
.firmware_version = 1,
.identity = "WDT Eurotech CPU-1220/1410",
};
static struct watchdog_info ident = {
.options = WDIOF_CARDRESET,
.firmware_version = 1,
.identity = "WDT Eurotech CPU-1220/1410",
};
int time;
int time;
switch(cmd) {
default:
return -ENOTTY;
switch(cmd) {
default:
return -ENOTTY;
case WDIOC_GETSUPPORT:
return copy_to_user((struct watchdog_info *)arg, &ident,
sizeof(ident)) ? -EFAULT : 0;
case WDIOC_GETSUPPORT:
return copy_to_user((struct watchdog_info *)arg, &ident,
sizeof(ident)) ? -EFAULT : 0;
case WDIOC_GETBOOTSTATUS:
return put_user(0, (int *) arg);
case WDIOC_GETBOOTSTATUS:
return put_user(0, (int *) arg);
case WDIOC_KEEPALIVE:
eurwdt_ping();
return 0;
case WDIOC_KEEPALIVE:
eurwdt_ping();
return 0;
case WDIOC_SETTIMEOUT:
if (copy_from_user(&time, (int *) arg, sizeof(int)))
return -EFAULT;
case WDIOC_SETTIMEOUT:
if (copy_from_user(&time, (int *) arg, sizeof(int)))
return -EFAULT;
/* Sanity check */
if (time < 0 || time > 255)
return -EINVAL;
/* Sanity check */
if (time < 0 || time > 255)
return -EINVAL;
timeout = time;
eurwdt_set_timeout(time);
return 0;
}
timeout = time;
eurwdt_set_timeout(time);
return 0;
}
}
/**
* eurwdt_open:
* @inode: inode of device
* @file: file handle to device
* eurwdt_open:
* @inode: inode of device
* @file: file handle to device
*
* The misc device has been opened. The watchdog device is single
* open and on opening we load the counter.
* The misc device has been opened. The watchdog device is single
* open and on opening we load the counter.
*/
static int eurwdt_open(struct inode *inode, struct file *file)
{
switch (minor(inode->i_rdev)) {
case WATCHDOG_MINOR:
spin_lock(&eurwdt_lock);
if (eurwdt_is_open) {
spin_unlock(&eurwdt_lock);
return -EBUSY;
}
if (nowayout) {
MOD_INC_USE_COUNT;
}
eurwdt_is_open = 1;
/* Activate the WDT */
eurwdt_activate_timer();
spin_unlock(&eurwdt_lock);
MOD_INC_USE_COUNT;
return 0;
case TEMP_MINOR:
return 0;
default:
return -ENODEV;
}
switch (minor(inode->i_rdev)) {
case WATCHDOG_MINOR:
spin_lock(&eurwdt_lock);
if (eurwdt_is_open) {
spin_unlock(&eurwdt_lock);
return -EBUSY;
}
if (nowayout)
MOD_INC_USE_COUNT;
eurwdt_is_open = 1;
/* Activate the WDT */
eurwdt_activate_timer();
spin_unlock(&eurwdt_lock);
MOD_INC_USE_COUNT;
return 0;
case TEMP_MINOR:
return 0;
default:
return -ENODEV;
}
}
/**
* eurwdt_release:
* @inode: inode to board
* @file: file handle to board
* eurwdt_release:
* @inode: inode to board
* @file: file handle to board
*
* The watchdog has a configurable API. There is a religious dispute
* between people who want their watchdog to be able to shut down and
* those who want to be sure if the watchdog manager dies the machine
* reboots. In the former case we disable the counters, in the latter
* case you have to open it again very soon.
* The watchdog has a configurable API. There is a religious dispute
* between people who want their watchdog to be able to shut down and
* those who want to be sure if the watchdog manager dies the machine
* reboots. In the former case we disable the counters, in the latter
* case you have to open it again very soon.
*/
static int eurwdt_release(struct inode *inode, struct file *file)
{
if (minor(inode->i_rdev) == WATCHDOG_MINOR) {
if (!nowayout) {
eurwdt_disable_timer();
}
eurwdt_is_open = 0;
if (minor(inode->i_rdev) == WATCHDOG_MINOR) {
if (!nowayout)
eurwdt_disable_timer();
MOD_DEC_USE_COUNT;
}
eurwdt_is_open = 0;
MOD_DEC_USE_COUNT;
}
return 0;
return 0;
}
/**
* eurwdt_notify_sys:
* @this: our notifier block
* @code: the event being reported
* @unused: unused
* eurwdt_notify_sys:
* @this: our notifier block
* @code: the event being reported
* @unused: unused
*
* Our notifier is called on system shutdowns. We want to turn the card
* off at reboot otherwise the machine will reboot again during memory
* test or worse yet during the following fsck. This would suck, in fact
* trust me - if it happens it does suck.
* Our notifier is called on system shutdowns. We want to turn the card
* off at reboot otherwise the machine will reboot again during memory
* test or worse yet during the following fsck. This would suck, in fact
* trust me - if it happens it does suck.
*/
static int eurwdt_notify_sys(struct notifier_block *this, unsigned long code,
void *unused)
void *unused)
{
if (code == SYS_DOWN || code == SYS_HALT) {
/* Turn the card off */
eurwdt_disable_timer();
}
if (code == SYS_DOWN || code == SYS_HALT) {
/* Turn the card off */
eurwdt_disable_timer();
}
return NOTIFY_DONE;
return NOTIFY_DONE;
}
/*
* Kernel Interfaces
* Kernel Interfaces
*/
static struct file_operations eurwdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = eurwdt_write,
.ioctl = eurwdt_ioctl,
.open = eurwdt_open,
.release = eurwdt_release,
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = eurwdt_write,
.ioctl = eurwdt_ioctl,
.open = eurwdt_open,
.release = eurwdt_release,
};
static struct miscdevice eurwdt_miscdev = {
......@@ -403,8 +415,8 @@ static struct miscdevice eurwdt_miscdev = {
};
/*
* The WDT card needs to learn about soft shutdowns in order to
* turn the timebomb registers off.
* The WDT card needs to learn about soft shutdowns in order to
* turn the timebomb registers off.
*/
static struct notifier_block eurwdt_notifier = {
......@@ -412,85 +424,85 @@ static struct notifier_block eurwdt_notifier = {
};
/**
* cleanup_module:
* cleanup_module:
*
* Unload the watchdog. You cannot do this with any file handles open.
* If your watchdog is set to continue ticking on close and you unload
* it, well it keeps ticking. We won't get the interrupt but the board
* will not touch PC memory so all is fine. You just have to load a new
* module in 60 seconds or reboot.
* Unload the watchdog. You cannot do this with any file handles open.
* If your watchdog is set to continue ticking on close and you unload
* it, well it keeps ticking. We won't get the interrupt but the board
* will not touch PC memory so all is fine. You just have to load a new
* module in 60 seconds or reboot.
*/
static void __exit eurwdt_exit(void)
{
eurwdt_lock_chip();
eurwdt_lock_chip();
misc_deregister(&eurwdt_miscdev);
misc_deregister(&eurwdt_miscdev);
unregister_reboot_notifier(&eurwdt_notifier);
release_region(io, 2);
free_irq(irq, NULL);
unregister_reboot_notifier(&eurwdt_notifier);
release_region(io, 2);
free_irq(irq, NULL);
}
/**
* eurwdt_init:
* eurwdt_init:
*
* Set up the WDT watchdog board. After grabbing the resources
* we require we need also to unlock the device.
* The open() function will actually kick the board off.
* Set up the WDT watchdog board. After grabbing the resources
* we require we need also to unlock the device.
* The open() function will actually kick the board off.
*/
static int __init eurwdt_init(void)
{
int ret;
eurwdt_validate_timeout();
ret = misc_register(&eurwdt_miscdev);
if (ret) {
printk(KERN_ERR "eurwdt: can't misc_register on minor=%d\n",
WATCHDOG_MINOR);
goto out;
}
ret = request_irq(irq, eurwdt_interrupt, SA_INTERRUPT, "eurwdt", NULL);
if(ret) {
printk(KERN_ERR "eurwdt: IRQ %d is not free.\n", irq);
goto outmisc;
}
if (!request_region(io, 2, "eurwdt")) {
printk(KERN_ERR "eurwdt: IO %X is not free.\n", io);
ret = -EBUSY;
goto outirq;
}
ret = register_reboot_notifier(&eurwdt_notifier);
if (ret) {
printk(KERN_ERR "eurwdt: can't register reboot notifier (err=%d)\n", ret);
goto outreg;
}
eurwdt_unlock_chip();
ret = 0;
printk(KERN_INFO "Eurotech WDT driver 0.01 at %X (Interrupt %d)"
" - timeout event: %s\n",
io, irq, (!strcmp("int", ev) ? "int" : "reboot"));
spin_lock_init(&eurwdt_lock);
out:
return ret;
outreg:
release_region(io, 2);
outirq:
free_irq(irq, NULL);
outmisc:
misc_deregister(&eurwdt_miscdev);
goto out;
int ret;
eurwdt_validate_timeout();
ret = misc_register(&eurwdt_miscdev);
if (ret) {
printk(KERN_ERR "eurwdt: can't misc_register on minor=%d\n",
WATCHDOG_MINOR);
goto out;
}
ret = request_irq(irq, eurwdt_interrupt, SA_INTERRUPT, "eurwdt", NULL);
if(ret) {
printk(KERN_ERR "eurwdt: IRQ %d is not free.\n", irq);
goto outmisc;
}
if (!request_region(io, 2, "eurwdt")) {
printk(KERN_ERR "eurwdt: IO %X is not free.\n", io);
ret = -EBUSY;
goto outirq;
}
ret = register_reboot_notifier(&eurwdt_notifier);
if (ret) {
printk(KERN_ERR "eurwdt: can't register reboot notifier (err=%d)\n", ret);
goto outreg;
}
eurwdt_unlock_chip();
ret = 0;
printk(KERN_INFO "Eurotech WDT driver 0.01 at %X (Interrupt %d)"
" - timeout event: %s\n",
io, irq, (!strcmp("int", ev) ? "int" : "reboot"));
spin_lock_init(&eurwdt_lock);
out:
return ret;
outreg:
release_region(io, 2);
outirq:
free_irq(irq, NULL);
outmisc:
misc_deregister(&eurwdt_miscdev);
goto out;
}
module_init(eurwdt_init);
......
......@@ -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)
......
/*
* 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 = {
};
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