Commit 2523e5b6 authored by Wim Van Sebroeck's avatar Wim Van Sebroeck Committed by Linus Torvalds

[WATCHDOG] v2.6.2 i8xx_tco-v0.06_update

Version 0.06 of the intel i8xx TCO driver:
* change i810_margin to heartbeat (in seconds)
* use module_param
* added notify system support
* renamed module to i8xx_tco
parent a546bacd
...@@ -204,25 +204,23 @@ config IB700_WDT ...@@ -204,25 +204,23 @@ config IB700_WDT
Most people will say N. Most people will say N.
config I810_TCO config I8XX_TCO
tristate "Intel i8xx TCO timer / Watchdog" tristate "Intel i8xx TCO timer / Watchdog"
depends on WATCHDOG && X86 depends on WATCHDOG && (X86 || IA64) && PCI
---help--- ---help---
Hardware driver for the TCO timer built into the Intel i8xx Hardware driver for the TCO timer built into the Intel 82801
chipset family. The TCO (Total Cost of Ownership) timer is a I/O Controller Hub family. The TCO (Total Cost of Ownership)
watchdog timer that will reboot the machine after its second timer is a watchdog timer that will reboot the machine after
expiration. The expiration time can be configured by commandline its second expiration. The expiration time can be configured
argument "i810_margin=<n>" where <n> is the counter initial value. with the "heartbeat" parameter.
It is decremented every 0.6 secs, the default is 50 which gives a
timeout of 30 seconds and one minute until reset.
On some motherboards the driver may fail to reset the chipset's On some motherboards the driver may fail to reset the chipset's
NO_REBOOT flag which prevents the watchdog from rebooting the NO_REBOOT flag which prevents the watchdog from rebooting the
machine. If this is the case you will get a kernel message like machine. If this is the case you will get a kernel message like
"i810tco init: failed to reset NO_REBOOT flag". "failed to reset NO_REBOOT flag, reboot disabled by hardware".
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called i810-tco. module will be called i8xx_tco.
config MIXCOMWD config MIXCOMWD
tristate "Mixcom Watchdog" tristate "Mixcom Watchdog"
......
...@@ -18,7 +18,7 @@ obj-$(CONFIG_WDT) += wdt.o ...@@ -18,7 +18,7 @@ obj-$(CONFIG_WDT) += wdt.o
obj-$(CONFIG_WDTPCI) += wdt_pci.o obj-$(CONFIG_WDTPCI) += wdt_pci.o
obj-$(CONFIG_21285_WATCHDOG) += wdt285.o obj-$(CONFIG_21285_WATCHDOG) += wdt285.o
obj-$(CONFIG_977_WATCHDOG) += wdt977.o obj-$(CONFIG_977_WATCHDOG) += wdt977.o
obj-$(CONFIG_I810_TCO) += i810-tco.o obj-$(CONFIG_I8XX_TCO) += i8xx_tco.o
obj-$(CONFIG_MACHZ_WDT) += machzwd.o obj-$(CONFIG_MACHZ_WDT) += machzwd.o
obj-$(CONFIG_SH_WDT) += shwdt.o obj-$(CONFIG_SH_WDT) += shwdt.o
obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o
......
/* /*
* i810-tco 0.05: TCO timer driver for i8xx chipsets * i8xx_tco 0.06: TCO timer driver for i8xx chipsets
* *
* (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights Reserved. * (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights Reserved.
* http://www.kernelconcepts.de * http://www.kernelconcepts.de
...@@ -46,36 +46,50 @@ ...@@ -46,36 +46,50 @@
* added support for 82801DB and 82801E chipset, * added support for 82801DB and 82801E chipset,
* added support for 82801EB and 8280ER chipset, * added support for 82801EB and 8280ER chipset,
* general cleanup. * general cleanup.
* 20030921 Wim Van Sebroeck <wim@iguana.be>
* 0.06 change i810_margin to heartbeat, use module_param,
* added notify system support, renamed module to i8xx_tco.
*/
/*
* Includes, defines, variables, module parameters, ...
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/miscdevice.h> #include <linux/miscdevice.h>
#include <linux/watchdog.h> #include <linux/watchdog.h>
#include <linux/notifier.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/fs.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/io.h> #include <asm/io.h>
#include "i810-tco.h"
#include "i8xx_tco.h"
/* Module and version information */ /* Module and version information */
#define TCO_VERSION "0.05" #define TCO_VERSION "0.06"
#define TCO_MODULE_NAME "i810 TCO timer" #define TCO_MODULE_NAME "i8xx TCO timer"
#define TCO_DRIVER_NAME TCO_MODULE_NAME ", v" TCO_VERSION #define TCO_DRIVER_NAME TCO_MODULE_NAME ", v" TCO_VERSION
#define PFX TCO_MODULE_NAME ": "
/* Default expire timeout */ /* internal variables */
#define TIMER_MARGIN 50 /* steps of 0.6sec, 3<n<64. Default is 30 seconds */
static unsigned int ACPIBASE; static unsigned int ACPIBASE;
static spinlock_t tco_lock; /* Guards the hardware */ static spinlock_t tco_lock; /* Guards the hardware */
static unsigned long timer_alive;
static char tco_expect_close;
static struct pci_dev *i8xx_tco_pci;
static int i810_margin = TIMER_MARGIN; /* steps of 0.6sec */ /* module parameters */
#define WATCHDOG_HEARTBEAT 30 /* 30 sec default heartbeat (2<heartbeat<39) */
MODULE_PARM(i810_margin, "i"); static int heartbeat = WATCHDOG_HEARTBEAT; /* in seconds */
MODULE_PARM_DESC(i810_margin, "i810-tco timeout in steps of 0.6sec, 3<n<64. Default = 50 (30 seconds)"); module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (2<heartbeat<39, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
#ifdef CONFIG_WATCHDOG_NOWAYOUT #ifdef CONFIG_WATCHDOG_NOWAYOUT
static int nowayout = 1; static int nowayout = 1;
...@@ -83,25 +97,20 @@ static int nowayout = 1; ...@@ -83,25 +97,20 @@ static int nowayout = 1;
static int nowayout = 0; static int nowayout = 0;
#endif #endif
MODULE_PARM(nowayout,"i"); module_param(nowayout, int, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
/*
* Timer active flag
*/
static unsigned long timer_alive;
static char tco_expect_close;
/* /*
* Some TCO specific functions * Some TCO specific functions
*/ */
static inline unsigned char seconds_to_ticks(int seconds)
{
/* the internal timer is stored as ticks which decrement
* every 0.6 seconds */
return (seconds * 10) / 6;
}
/*
* Start the timer countdown
*/
static int tco_timer_start (void) static int tco_timer_start (void)
{ {
unsigned char val; unsigned char val;
...@@ -118,9 +127,6 @@ static int tco_timer_start (void) ...@@ -118,9 +127,6 @@ static int tco_timer_start (void)
return 0; return 0;
} }
/*
* Stop the timer countdown
*/
static int tco_timer_stop (void) static int tco_timer_stop (void)
{ {
unsigned char val; unsigned char val;
...@@ -137,18 +143,26 @@ static int tco_timer_stop (void) ...@@ -137,18 +143,26 @@ static int tco_timer_stop (void)
return 0; return 0;
} }
/* static int tco_timer_keepalive (void)
* Set the timer reload value {
*/ spin_lock(&tco_lock);
static int tco_timer_settimer (unsigned char tmrval) outb (0x01, TCO1_RLD);
spin_unlock(&tco_lock);
return 0;
}
static int tco_timer_set_heartbeat (int t)
{ {
unsigned char val; unsigned char val;
unsigned char tmrval;
tmrval = seconds_to_ticks(t);
/* from the specs: */ /* from the specs: */
/* "Values of 0h-3h are ignored and should not be attempted" */ /* "Values of 0h-3h are ignored and should not be attempted" */
if (tmrval > 0x3f || tmrval < 0x04) if (tmrval > 0x3f || tmrval < 0x04)
return -1; return -EINVAL;
/* Write new heartbeat to watchdog */
spin_lock(&tco_lock); spin_lock(&tco_lock);
val = inb (TCO1_TMR); val = inb (TCO1_TMR);
val &= 0xc0; val &= 0xc0;
...@@ -158,41 +172,31 @@ static int tco_timer_settimer (unsigned char tmrval) ...@@ -158,41 +172,31 @@ static int tco_timer_settimer (unsigned char tmrval)
spin_unlock(&tco_lock); spin_unlock(&tco_lock);
if ((val & 0x3f) != tmrval) if ((val & 0x3f) != tmrval)
return -1; return -EINVAL;
heartbeat = t;
return 0; return 0;
} }
/* /*
* Reload (trigger) the timer. Lock is needed so we don't reload it during * /dev/watchdog handling
* a reprogramming event
*/ */
static void tco_timer_reload (void) static int i8xx_tco_open (struct inode *inode, struct file *file)
{
spin_lock(&tco_lock);
outb (0x01, TCO1_RLD);
spin_unlock(&tco_lock);
}
/*
* Allow only one person to hold it open
*/
static int i810tco_open (struct inode *inode, struct file *file)
{ {
/* /dev/watchdog can only be opened once */
if (test_and_set_bit(0, &timer_alive)) if (test_and_set_bit(0, &timer_alive))
return -EBUSY; return -EBUSY;
/* /*
* Reload and activate timer * Reload and activate timer
*/ */
tco_timer_reload (); tco_timer_keepalive ();
tco_timer_start (); tco_timer_start ();
return 0; return 0;
} }
static int i810tco_release (struct inode *inode, struct file *file) static int i8xx_tco_release (struct inode *inode, struct file *file)
{ {
/* /*
* Shut off the timer. * Shut off the timer.
...@@ -200,15 +204,15 @@ static int i810tco_release (struct inode *inode, struct file *file) ...@@ -200,15 +204,15 @@ static int i810tco_release (struct inode *inode, struct file *file)
if (tco_expect_close == 42) { if (tco_expect_close == 42) {
tco_timer_stop (); tco_timer_stop ();
} else { } else {
tco_timer_reload (); printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
printk(KERN_CRIT TCO_MODULE_NAME ": Unexpected close, not stopping watchdog!\n"); tco_timer_keepalive ();
} }
clear_bit(0, &timer_alive); clear_bit(0, &timer_alive);
tco_expect_close = 0; tco_expect_close = 0;
return 0; return 0;
} }
static ssize_t i810tco_write (struct file *file, const char *data, static ssize_t i8xx_tco_write (struct file *file, const char *data,
size_t len, loff_t * ppos) size_t len, loff_t * ppos)
{ {
/* Can't seek (pwrite) on this device */ /* Can't seek (pwrite) on this device */
...@@ -226,7 +230,7 @@ static ssize_t i810tco_write (struct file *file, const char *data, ...@@ -226,7 +230,7 @@ static ssize_t i810tco_write (struct file *file, const char *data,
/* scan to see whether or not we got the magic character */ /* scan to see whether or not we got the magic character */
for (i = 0; i != len; i++) { for (i = 0; i != len; i++) {
u8 c; char c;
if(get_user(c, data+i)) if(get_user(c, data+i))
return -EFAULT; return -EFAULT;
if (c == 'V') if (c == 'V')
...@@ -235,67 +239,113 @@ static ssize_t i810tco_write (struct file *file, const char *data, ...@@ -235,67 +239,113 @@ static ssize_t i810tco_write (struct file *file, const char *data,
} }
/* someone wrote to us, we should reload the timer */ /* someone wrote to us, we should reload the timer */
tco_timer_reload (); tco_timer_keepalive ();
} }
return len; return len;
} }
static int i810tco_ioctl (struct inode *inode, struct file *file, static int i8xx_tco_ioctl (struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
int new_margin, u_margin; int new_options, retval = -EINVAL;
int options, retval = -EINVAL; int new_heartbeat;
static struct watchdog_info ident = { static struct watchdog_info ident = {
.options = WDIOF_SETTIMEOUT | .options = WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING | WDIOF_KEEPALIVEPING |
WDIOF_MAGICCLOSE, WDIOF_MAGICCLOSE,
.firmware_version = 0, .firmware_version = 0,
.identity = "i810 TCO timer", .identity = TCO_MODULE_NAME,
}; };
switch (cmd) { switch (cmd) {
default:
return -ENOIOCTLCMD;
case WDIOC_GETSUPPORT: case WDIOC_GETSUPPORT:
if (copy_to_user return copy_to_user((struct watchdog_info *) arg, &ident,
((struct watchdog_info *) arg, &ident, sizeof (ident))) sizeof (ident)) ? -EFAULT : 0;
return -EFAULT;
return 0;
case WDIOC_GETSTATUS: case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS: case WDIOC_GETBOOTSTATUS:
return put_user (0, (int *) arg); return put_user (0, (int *) arg);
case WDIOC_KEEPALIVE:
tco_timer_keepalive ();
return 0;
case WDIOC_SETOPTIONS: case WDIOC_SETOPTIONS:
if (get_user (options, (int *) arg)) {
if (get_user (new_options, (int *) arg))
return -EFAULT; return -EFAULT;
if (options & WDIOS_DISABLECARD) {
if (new_options & WDIOS_DISABLECARD) {
tco_timer_stop (); tco_timer_stop ();
retval = 0; retval = 0;
} }
if (options & WDIOS_ENABLECARD) {
tco_timer_reload (); if (new_options & WDIOS_ENABLECARD) {
tco_timer_keepalive ();
tco_timer_start (); tco_timer_start ();
retval = 0; retval = 0;
} }
return retval; return retval;
case WDIOC_KEEPALIVE: }
tco_timer_reload ();
return 0;
case WDIOC_SETTIMEOUT: case WDIOC_SETTIMEOUT:
if (get_user (u_margin, (int *) arg)) {
if (get_user(new_heartbeat, (int *) arg))
return -EFAULT; return -EFAULT;
new_margin = (u_margin * 10 + 5) / 6;
if ((new_margin < 4) || (new_margin > 63)) if (tco_timer_set_heartbeat(new_heartbeat))
return -EINVAL;
if (tco_timer_settimer ((unsigned char) new_margin))
return -EINVAL; return -EINVAL;
i810_margin = new_margin;
tco_timer_reload (); tco_timer_keepalive ();
/* Fall */ /* Fall */
}
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user ((int)(i810_margin * 6 / 10), (int *) arg); return put_user(heartbeat, (int *)arg);
default:
return -ENOIOCTLCMD;
} }
} }
/*
* Notify system
*/
static int i8xx_tco_notify_sys (struct notifier_block *this, unsigned long code, void *unused)
{
if (code==SYS_DOWN || code==SYS_HALT) {
/* Turn the WDT off */
tco_timer_stop ();
}
return NOTIFY_DONE;
}
/*
* Kernel Interfaces
*/
static struct file_operations i8xx_tco_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = i8xx_tco_write,
.ioctl = i8xx_tco_ioctl,
.open = i8xx_tco_open,
.release = i8xx_tco_release,
};
static struct miscdevice i8xx_tco_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &i8xx_tco_fops,
};
static struct notifier_block i8xx_tco_notifier = {
.notifier_call = i8xx_tco_notify_sys,
};
/* /*
* Data for PCI driver interface * Data for PCI driver interface
* *
...@@ -304,7 +354,7 @@ static int i810tco_ioctl (struct inode *inode, struct file *file, ...@@ -304,7 +354,7 @@ static int i810tco_ioctl (struct inode *inode, struct file *file,
* register a pci_driver, because someone else might one day * register a pci_driver, because someone else might one day
* want to register another driver on the same PCI id. * want to register another driver on the same PCI id.
*/ */
static struct pci_device_id i810tco_pci_tbl[] = { static struct pci_device_id i8xx_tco_pci_tbl[] = {
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_0, PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_0, PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, PCI_ANY_ID, PCI_ANY_ID, },
...@@ -314,13 +364,15 @@ static struct pci_device_id i810tco_pci_tbl[] = { ...@@ -314,13 +364,15 @@ static struct pci_device_id i810tco_pci_tbl[] = {
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801E_0, PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801E_0, PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, PCI_ANY_ID, PCI_ANY_ID, }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, PCI_ANY_ID, PCI_ANY_ID, },
{ 0, }, { 0, }, /* End of list */
}; };
MODULE_DEVICE_TABLE (pci, i810tco_pci_tbl); MODULE_DEVICE_TABLE (pci, i8xx_tco_pci_tbl);
static struct pci_dev *i810tco_pci; /*
* Init & exit routines
*/
static unsigned char __init i810tco_getdevice (void) static unsigned char __init i8xx_tco_getdevice (void)
{ {
struct pci_dev *dev = NULL; struct pci_dev *dev = NULL;
u8 val1, val2; u8 val1, val2;
...@@ -330,37 +382,37 @@ static unsigned char __init i810tco_getdevice (void) ...@@ -330,37 +382,37 @@ static unsigned char __init i810tco_getdevice (void)
*/ */
while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
if (pci_match_device(i810tco_pci_tbl, dev)) { if (pci_match_device(i8xx_tco_pci_tbl, dev)) {
i810tco_pci = dev; i8xx_tco_pci = dev;
break; break;
} }
} }
if (i810tco_pci) { if (i8xx_tco_pci) {
/* /*
* Find the ACPI base I/O address which is the base * Find the ACPI base I/O address which is the base
* for the TCO registers (TCOBASE=ACPIBASE + 0x60) * for the TCO registers (TCOBASE=ACPIBASE + 0x60)
* ACPIBASE is bits [15:7] from 0x40-0x43 * ACPIBASE is bits [15:7] from 0x40-0x43
*/ */
pci_read_config_byte (i810tco_pci, 0x40, &val1); pci_read_config_byte (i8xx_tco_pci, 0x40, &val1);
pci_read_config_byte (i810tco_pci, 0x41, &val2); pci_read_config_byte (i8xx_tco_pci, 0x41, &val2);
badr = ((val2 << 1) | (val1 >> 7)) << 7; badr = ((val2 << 1) | (val1 >> 7)) << 7;
ACPIBASE = badr; ACPIBASE = badr;
/* Something's wrong here, ACPIBASE has to be set */ /* Something's wrong here, ACPIBASE has to be set */
if (badr == 0x0001 || badr == 0x0000) { if (badr == 0x0001 || badr == 0x0000) {
printk (KERN_ERR TCO_MODULE_NAME " init: failed to get TCOBASE address\n"); printk (KERN_ERR PFX "failed to get TCOBASE address\n");
return 0; return 0;
} }
/* /*
* Check chipset's NO_REBOOT bit * Check chipset's NO_REBOOT bit
*/ */
pci_read_config_byte (i810tco_pci, 0xd4, &val1); pci_read_config_byte (i8xx_tco_pci, 0xd4, &val1);
if (val1 & 0x02) { if (val1 & 0x02) {
val1 &= 0xfd; val1 &= 0xfd;
pci_write_config_byte (i810tco_pci, 0xd4, val1); pci_write_config_byte (i8xx_tco_pci, 0xd4, val1);
pci_read_config_byte (i810tco_pci, 0xd4, &val1); pci_read_config_byte (i8xx_tco_pci, 0xd4, &val1);
if (val1 & 0x02) { if (val1 & 0x02) {
printk (KERN_ERR TCO_MODULE_NAME " init: failed to reset NO_REBOOT flag, reboot disabled by hardware\n"); printk (KERN_ERR PFX "failed to reset NO_REBOOT flag, reboot disabled by hardware\n");
return 0; /* Cannot reset NO_REBOOT bit */ return 0; /* Cannot reset NO_REBOOT bit */
} }
} }
...@@ -376,57 +428,77 @@ static unsigned char __init i810tco_getdevice (void) ...@@ -376,57 +428,77 @@ static unsigned char __init i810tco_getdevice (void)
return 0; return 0;
} }
static struct file_operations i810tco_fops = {
.owner = THIS_MODULE,
.write = i810tco_write,
.ioctl = i810tco_ioctl,
.open = i810tco_open,
.release = i810tco_release,
};
static struct miscdevice i810tco_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &i810tco_fops,
};
static int __init watchdog_init (void) static int __init watchdog_init (void)
{ {
int ret;
spin_lock_init(&tco_lock); spin_lock_init(&tco_lock);
if (!i810tco_getdevice () || i810tco_pci == NULL)
/* Check whether or not the hardware watchdog is there */
if (!i8xx_tco_getdevice () || i8xx_tco_pci == NULL)
return -ENODEV; return -ENODEV;
if (!request_region (TCOBASE, 0x10, "i810 TCO")) {
printk (KERN_ERR TCO_MODULE_NAME if (!request_region (TCOBASE, 0x10, "i8xx TCO")) {
": I/O address 0x%04x already in use\n", printk (KERN_ERR PFX "I/O address 0x%04x already in use\n",
TCOBASE); TCOBASE);
return -EIO; ret = -EIO;
goto out;
} }
if (misc_register (&i810tco_miscdev) != 0) {
release_region (TCOBASE, 0x10); /* Check that the heartbeat value is within it's range ; if not reset to the default */
printk (KERN_ERR TCO_MODULE_NAME ": cannot register miscdev\n"); if (tco_timer_set_heartbeat (heartbeat)) {
return -EIO; heartbeat = WATCHDOG_HEARTBEAT;
tco_timer_set_heartbeat (heartbeat);
printk(KERN_INFO PFX "heartbeat value must be 2<heartbeat<39, using %d\n",
heartbeat);
}
ret = register_reboot_notifier(&i8xx_tco_notifier);
if (ret != 0) {
printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n",
ret);
goto unreg_region;
}
ret = misc_register(&i8xx_tco_miscdev);
if (ret != 0) {
printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
WATCHDOG_MINOR, ret);
goto unreg_notifier;
} }
tco_timer_settimer ((unsigned char) i810_margin);
tco_timer_reload ();
printk (KERN_INFO TCO_DRIVER_NAME tco_timer_keepalive ();
": timer margin: %d sec (0x%04x) (nowayout=%d)\n",
(int) (i810_margin * 6 / 10), TCOBASE, nowayout); printk (KERN_INFO PFX "initialized (0x%04x). heartbeat=%d sec (nowayout=%d)\n",
TCOBASE, heartbeat, nowayout);
return 0; return 0;
unreg_notifier:
unregister_reboot_notifier(&i8xx_tco_notifier);
unreg_region:
release_region (TCOBASE, 0x10);
out:
return ret;
} }
static void __exit watchdog_cleanup (void) static void __exit watchdog_cleanup (void)
{ {
u8 val; u8 val;
/* Reset the timer before we leave */ /* Stop the timer before we leave */
tco_timer_reload (); if (!nowayout)
tco_timer_stop ();
/* Set the NO_REBOOT bit to prevent later reboots, just for sure */ /* Set the NO_REBOOT bit to prevent later reboots, just for sure */
pci_read_config_byte (i810tco_pci, 0xd4, &val); pci_read_config_byte (i8xx_tco_pci, 0xd4, &val);
val |= 0x02; val |= 0x02;
pci_write_config_byte (i810tco_pci, 0xd4, val); pci_write_config_byte (i8xx_tco_pci, 0xd4, val);
/* Deregister */
misc_deregister (&i8xx_tco_miscdev);
unregister_reboot_notifier(&i8xx_tco_notifier);
release_region (TCOBASE, 0x10); release_region (TCOBASE, 0x10);
misc_deregister (&i810tco_miscdev);
} }
module_init(watchdog_init); module_init(watchdog_init);
...@@ -435,3 +507,4 @@ module_exit(watchdog_cleanup); ...@@ -435,3 +507,4 @@ module_exit(watchdog_cleanup);
MODULE_AUTHOR("Nils Faerber"); MODULE_AUTHOR("Nils Faerber");
MODULE_DESCRIPTION("TCO timer driver for i8xx chipsets"); MODULE_DESCRIPTION("TCO timer driver for i8xx chipsets");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
/* /*
* i810-tco: TCO timer driver for i8xx chipsets * i8xx_tco: TCO timer driver for i8xx chipsets
* *
* (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights Reserved. * (c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights Reserved.
* http://www.kernelconcepts.de * http://www.kernelconcepts.de
...@@ -21,12 +21,12 @@ ...@@ -21,12 +21,12 @@
* based on softdog.c by Alan Cox <alan@redhat.com> * based on softdog.c by Alan Cox <alan@redhat.com>
* *
* For history and the complete list of supported I/O Controller Hub's * For history and the complete list of supported I/O Controller Hub's
* see i810-tco.c * see i8xx_tco.c
*/ */
/* /*
* Some address definitions for the i810 TCO * Some address definitions for the TCO
*/ */
#define TCOBASE ACPIBASE + 0x60 /* TCO base address */ #define TCOBASE ACPIBASE + 0x60 /* TCO base address */
......
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