Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
72f7e48f
Commit
72f7e48f
authored
Mar 20, 2004
by
Linus Torvalds
Browse files
Options
Browse Files
Download
Plain Diff
Merge
http://linux-watchdog.bkbits.net/linux-2.6-watchdog
into ppc970.osdl.org:/home/torvalds/v2.6/linux
parents
cfc615f2
84d1e7b7
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
868 additions
and
579 deletions
+868
-579
drivers/char/watchdog/pcwd.c
drivers/char/watchdog/pcwd.c
+130
-88
drivers/char/watchdog/softdog.c
drivers/char/watchdog/softdog.c
+105
-32
drivers/char/watchdog/wd501p.h
drivers/char/watchdog/wd501p.h
+10
-49
drivers/char/watchdog/wdt.c
drivers/char/watchdog/wdt.c
+286
-185
drivers/char/watchdog/wdt_pci.c
drivers/char/watchdog/wdt_pci.c
+337
-225
No files found.
drivers/char/watchdog/pcwd.c
View file @
72f7e48f
...
...
@@ -71,7 +71,7 @@
*/
static
int
pcwd_ioports
[]
=
{
0x270
,
0x350
,
0x370
,
0x000
};
#define WD_VER "1.1
2 (12/14/2001
)"
#define WD_VER "1.1
4 (03/12/2004
)"
/*
* It should be noted that PCWD_REVISION_B was removed because A and B
...
...
@@ -227,6 +227,45 @@ void pcwd_showprevstate(void)
}
}
static
int
pcwd_start
(
void
)
{
int
stat_reg
;
/* Enable the port */
if
(
revision
==
PCWD_REVISION_C
)
{
spin_lock
(
&
io_lock
);
outb_p
(
0x00
,
current_readport
+
3
);
stat_reg
=
inb_p
(
current_readport
+
2
);
spin_unlock
(
&
io_lock
);
if
(
stat_reg
&
0x10
)
{
printk
(
KERN_INFO
"pcwd: Could not start watchdog.
\n
"
);
return
-
EIO
;
}
}
return
0
;
}
static
int
pcwd_stop
(
void
)
{
int
stat_reg
;
/* Disable the board */
if
(
revision
==
PCWD_REVISION_C
)
{
spin_lock
(
&
io_lock
);
outb_p
(
0xA5
,
current_readport
+
3
);
outb_p
(
0xA5
,
current_readport
+
3
);
stat_reg
=
inb_p
(
current_readport
+
2
);
spin_unlock
(
&
io_lock
);
if
((
stat_reg
&
0x10
)
==
0
)
{
printk
(
KERN_INFO
"pcwd: Could not stop watchdog.
\n
"
);
return
-
EIO
;
}
}
return
0
;
}
static
void
pcwd_send_heartbeat
(
void
)
{
int
wdrst_stat
;
...
...
@@ -242,13 +281,41 @@ static void pcwd_send_heartbeat(void)
outb_p
(
wdrst_stat
,
current_readport
);
}
static
int
pcwd_get_temperature
(
int
*
temperature
)
{
/* check that port 0 gives temperature info and no command results */
if
(
mode_debug
)
return
-
1
;
*
temperature
=
0
;
if
(
!
supports_temp
)
return
-
ENODEV
;
/*
* Convert celsius to fahrenheit, since this was
* the decided 'standard' for this return value.
*/
spin_lock
(
&
io_lock
);
*
temperature
=
((
inb
(
current_readport
))
*
9
/
5
)
+
32
;
spin_unlock
(
&
io_lock
);
return
0
;
}
/*
* /dev/watchdog handling
*/
static
int
pcwd_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
int
cdat
,
rv
;
int
temperature
;
static
struct
watchdog_info
ident
=
{
.
options
=
WDIOF_OVERHEAT
|
WDIOF_CARDRESET
,
.
options
=
WDIOF_OVERHEAT
|
WDIOF_CARDRESET
|
WDIOF_MAGICCLOSE
,
.
firmware_version
=
1
,
.
identity
=
"PCWD"
,
};
...
...
@@ -332,17 +399,10 @@ static int pcwd_ioctl(struct inode *inode, struct file *file,
case
WDIOC_GETTEMP
:
rv
=
0
;
if
((
supports_temp
)
&&
(
mode_debug
==
0
))
{
spin_lock
(
&
io_lock
);
rv
=
inb
(
current_readport
);
spin_unlock
(
&
io_lock
);
if
(
put_user
(
rv
,
(
int
*
)
arg
))
if
(
pcwd_get_temperature
(
&
temperature
))
return
-
EFAULT
;
}
else
if
(
put_user
(
rv
,
(
int
*
)
arg
))
return
-
EFAULT
;
return
0
;
return
put_user
(
temperature
,
(
int
*
)
arg
);
case
WDIOC_SETOPTIONS
:
if
(
revision
==
PCWD_REVISION_C
)
...
...
@@ -352,32 +412,12 @@ static int pcwd_ioctl(struct inode *inode, struct file *file,
if
(
rv
&
WDIOS_DISABLECARD
)
{
spin_lock
(
&
io_lock
);
outb_p
(
0xA5
,
current_readport
+
3
);
outb_p
(
0xA5
,
current_readport
+
3
);
cdat
=
inb_p
(
current_readport
+
2
);
spin_unlock
(
&
io_lock
);
if
((
cdat
&
0x10
)
==
0
)
{
printk
(
KERN_INFO
"pcwd: Could not disable card.
\n
"
);
return
-
EIO
;
}
return
0
;
return
pcwd_stop
();
}
if
(
rv
&
WDIOS_ENABLECARD
)
{
spin_lock
(
&
io_lock
);
outb_p
(
0x00
,
current_readport
+
3
);
cdat
=
inb_p
(
current_readport
+
2
);
spin_unlock
(
&
io_lock
);
if
(
cdat
&
0x10
)
{
printk
(
KERN_INFO
"pcwd: Could not enable card.
\n
"
);
return
-
EIO
;
}
return
0
;
return
pcwd_start
();
}
if
(
rv
&
WDIOS_TEMPPANIC
)
...
...
@@ -423,72 +463,66 @@ static ssize_t pcwd_write(struct file *file, const char *buf, size_t len,
return
len
;
}
static
int
pcwd_open
(
struct
inode
*
ino
,
struct
file
*
filep
)
static
int
pcwd_open
(
struct
inode
*
ino
de
,
struct
file
*
file
)
{
switch
(
iminor
(
ino
))
{
case
WATCHDOG_MINOR
:
if
(
!
atomic_dec_and_test
(
&
open_allowed
)
)
{
atomic_inc
(
&
open_allowed
);
return
-
EBUSY
;
}
if
(
nowayout
)
__module_get
(
THIS_MODULE
);
/* 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
:
/* Activate */
pcwd_start
();
return
(
0
);
default:
return
(
-
ENODEV
);
}
static
int
pcwd_close
(
struct
inode
*
inode
,
struct
file
*
file
)
{
if
(
expect_close
==
42
)
{
pcwd_stop
();
atomic_inc
(
&
open_allowed
);
}
else
{
printk
(
KERN_CRIT
"pcwd: Unexpected close, not stopping watchdog!
\n
"
);
pcwd_send_heartbeat
();
}
expect_close
=
0
;
return
0
;
}
static
ssize_t
pcwd_read
(
struct
file
*
file
,
char
*
buf
,
size_t
count
,
/*
* /dev/temperature handling
*/
static
ssize_t
pcwd_temp_read
(
struct
file
*
file
,
char
*
buf
,
size_t
count
,
loff_t
*
ppos
)
{
unsigned
short
c
;
unsigned
char
cp
;
int
temperature
;
/* Can't seek (pread) on this device */
if
(
ppos
!=
&
file
->
f_pos
)
return
-
ESPIPE
;
switch
(
iminor
(
file
->
f_dentry
->
d_inode
))
{
case
TEMP_MINOR
:
/*
* Convert metric to Fahrenheit, since this was
* the decided 'standard' for this return value.
*/
c
=
inb
(
current_readport
);
cp
=
(
c
*
9
/
5
)
+
32
;
if
(
copy_to_user
(
buf
,
&
cp
,
1
))
if
(
pcwd_get_temperature
(
&
temperature
))
return
-
EFAULT
;
if
(
copy_to_user
(
buf
,
&
temperature
,
1
))
return
-
EFAULT
;
return
1
;
default:
return
-
EINVAL
;
}
}
static
int
pcwd_close
(
struct
inode
*
ino
,
struct
file
*
filep
)
static
int
pcwd_temp_open
(
struct
inode
*
inode
,
struct
file
*
file
)
{
if
(
!
supports_temp
)
return
-
ENODEV
;
return
0
;
}
static
int
pcwd_temp_close
(
struct
inode
*
inode
,
struct
file
*
file
)
{
if
(
iminor
(
ino
)
==
WATCHDOG_MINOR
)
{
if
(
expect_close
==
42
)
{
/* 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
);
}
}
expect_close
=
0
;
return
0
;
}
...
...
@@ -569,7 +603,7 @@ static void debug_off(void)
static
struct
file_operations
pcwd_fops
=
{
.
owner
=
THIS_MODULE
,
.
read
=
pcwd_read
,
.
llseek
=
no_llseek
,
.
write
=
pcwd_write
,
.
ioctl
=
pcwd_ioctl
,
.
open
=
pcwd_open
,
...
...
@@ -582,10 +616,18 @@ static struct miscdevice pcwd_miscdev = {
.
fops
=
&
pcwd_fops
,
};
static
struct
file_operations
pcwd_temp_fops
=
{
.
owner
=
THIS_MODULE
,
.
llseek
=
no_llseek
,
.
read
=
pcwd_temp_read
,
.
open
=
pcwd_temp_open
,
.
release
=
pcwd_temp_close
,
};
static
struct
miscdevice
temp_miscdev
=
{
.
minor
=
TEMP_MINOR
,
.
name
=
"temperature"
,
.
fops
=
&
pcwd_fops
,
.
fops
=
&
pcwd_
temp_
fops
,
};
static
void
__init
pcwd_validate_timeout
(
void
)
...
...
drivers/char/watchdog/softdog.c
View file @
72f7e48f
/*
* SoftDog 0.0
6
: A Software Watchdog Device
* SoftDog 0.0
7
: A Software Watchdog Device
*
* (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
* http://www.redhat.com
...
...
@@ -40,26 +40,21 @@
#include <linux/moduleparam.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/timer.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/fs.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#define
TIMER_MARGIN 60
/* (secs) Default is 1 minute */
#define
PFX "SoftDog: "
static
char
expect_close
;
#define TIMER_MARGIN 60
/* Default is 60 seconds */
static
int
soft_margin
=
TIMER_MARGIN
;
/* in seconds */
#ifdef ONLY_TESTING
static
int
soft_noboot
=
1
;
#else
static
int
soft_noboot
=
0
;
#endif
/* ONLY_TESTING */
module_param
(
soft_margin
,
int
,
0
);
module_param
(
soft_noboot
,
int
,
0
);
MODULE_LICENSE
(
"GPL"
);
MODULE_PARM_DESC
(
soft_margin
,
"Watchdog soft_margin in seconds. (0<soft_margin<65536, default="
__MODULE_STRING
(
TIMER_MARGIN
)
")"
);
#ifdef CONFIG_WATCHDOG_NOWAYOUT
static
int
nowayout
=
1
;
...
...
@@ -70,6 +65,15 @@ static int nowayout = 0;
module_param
(
nowayout
,
int
,
0
);
MODULE_PARM_DESC
(
nowayout
,
"Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"
);
#ifdef ONLY_TESTING
static
int
soft_noboot
=
1
;
#else
static
int
soft_noboot
=
0
;
#endif
/* ONLY_TESTING */
module_param
(
soft_noboot
,
int
,
0
);
MODULE_PARM_DESC
(
soft_noboot
,
"Softdog action, set to 1 to ignore reboots, 0 to reboot (default depends on ONLY_TESTING)"
);
/*
* Our timer
*/
...
...
@@ -79,6 +83,7 @@ static void watchdog_fire(unsigned long);
static
struct
timer_list
watchdog_ticktock
=
TIMER_INITIALIZER
(
watchdog_fire
,
0
,
0
);
static
unsigned
long
timer_alive
;
static
char
expect_close
;
/*
...
...
@@ -88,17 +93,42 @@ static unsigned long timer_alive;
static
void
watchdog_fire
(
unsigned
long
data
)
{
if
(
soft_noboot
)
printk
(
KERN_CRIT
"SOFTDOG:
Triggered - Reboot ignored.
\n
"
);
printk
(
KERN_CRIT
PFX
"
Triggered - Reboot ignored.
\n
"
);
else
{
printk
(
KERN_CRIT
"SOFTDOG:
Initiating system reboot.
\n
"
);
printk
(
KERN_CRIT
PFX
"
Initiating system reboot.
\n
"
);
machine_restart
(
NULL
);
printk
(
"SOFTDOG:
Reboot didn't ?????
\n
"
);
printk
(
KERN_CRIT
PFX
"
Reboot didn't ?????
\n
"
);
}
}
/*
* Allow only one person to hold it open
* Softdog operations
*/
static
int
softdog_keepalive
(
void
)
{
mod_timer
(
&
watchdog_ticktock
,
jiffies
+
(
soft_margin
*
HZ
));
return
0
;
}
static
int
softdog_stop
(
void
)
{
del_timer
(
&
watchdog_ticktock
);
return
0
;
}
static
int
softdog_set_heartbeat
(
int
t
)
{
if
((
t
<
0x0001
)
||
(
t
>
0xFFFF
))
return
-
EINVAL
;
soft_margin
=
t
;
return
0
;
}
/*
* /dev/watchdog handling
*/
static
int
softdog_open
(
struct
inode
*
inode
,
struct
file
*
file
)
...
...
@@ -110,7 +140,7 @@ static int softdog_open(struct inode *inode, struct file *file)
/*
* Activate timer
*/
mod_timer
(
&
watchdog_ticktock
,
jiffies
+
(
soft_margin
*
HZ
)
);
softdog_keepalive
(
);
return
0
;
}
...
...
@@ -121,9 +151,10 @@ static int softdog_release(struct inode *inode, struct file *file)
* Lock it in if it's a module and we set nowayout
*/
if
(
expect_close
==
42
)
{
del_timer
(
&
watchdog_ticktock
);
softdog_stop
(
);
}
else
{
printk
(
KERN_CRIT
"SOFTDOG: WDT device closed unexpectedly. WDT will not stop!
\n
"
);
printk
(
KERN_CRIT
PFX
"Unexpected close, not stopping watchdog!
\n
"
);
softdog_keepalive
();
}
clear_bit
(
0
,
&
timer_alive
);
expect_close
=
0
;
...
...
@@ -155,7 +186,7 @@ static ssize_t softdog_write(struct file *file, const char *data, size_t len, lo
expect_close
=
42
;
}
}
mod_timer
(
&
watchdog_ticktock
,
jiffies
+
(
soft_margin
*
HZ
)
);
softdog_keepalive
(
);
}
return
len
;
}
...
...
@@ -165,37 +196,57 @@ static int softdog_ioctl(struct inode *inode, struct file *file,
{
int
new_margin
;
static
struct
watchdog_info
ident
=
{
.
options
=
WDIOF_SETTIMEOUT
|
WDIOF_MAGICCLOSE
,
.
options
=
WDIOF_SETTIMEOUT
|
WDIOF_KEEPALIVEPING
|
WDIOF_MAGICCLOSE
,
.
firmware_version
=
0
,
.
identity
=
"Software Watchdog"
,
};
switch
(
cmd
)
{
default:
return
-
ENOIOCTLCMD
;
case
WDIOC_GETSUPPORT
:
if
(
copy_to_user
((
struct
watchdog_info
*
)
arg
,
&
ident
,
sizeof
(
ident
)))
return
-
EFAULT
;
return
0
;
return
copy_to_user
((
struct
watchdog_info
*
)
arg
,
&
ident
,
sizeof
(
ident
))
?
-
EFAULT
:
0
;
case
WDIOC_GETSTATUS
:
case
WDIOC_GETBOOTSTATUS
:
return
put_user
(
0
,(
int
*
)
arg
);
case
WDIOC_KEEPALIVE
:
mod_timer
(
&
watchdog_ticktock
,
jiffies
+
(
soft_margin
*
HZ
)
);
softdog_keepalive
(
);
return
0
;
case
WDIOC_SETTIMEOUT
:
if
(
get_user
(
new_margin
,
(
int
*
)
arg
))
return
-
EFAULT
;
if
(
new_margin
<
1
)
if
(
softdog_set_heartbeat
(
new_margin
)
)
return
-
EINVAL
;
soft_margin
=
new_margin
;
mod_timer
(
&
watchdog_ticktock
,
jiffies
+
(
soft_margin
*
HZ
));
softdog_keepalive
();
/* Fall */
case
WDIOC_GETTIMEOUT
:
return
put_user
(
soft_margin
,
(
int
*
)
arg
);
}
}
/*
* Notifier for system down
*/
static
int
softdog_notify_sys
(
struct
notifier_block
*
this
,
unsigned
long
code
,
void
*
unused
)
{
if
(
code
==
SYS_DOWN
||
code
==
SYS_HALT
)
{
/* Turn the WDT off */
softdog_stop
();
}
return
NOTIFY_DONE
;
}
/*
* Kernel Interfaces
*/
static
struct
file_operations
softdog_fops
=
{
.
owner
=
THIS_MODULE
,
.
llseek
=
no_llseek
,
.
write
=
softdog_write
,
.
ioctl
=
softdog_ioctl
,
.
open
=
softdog_open
,
...
...
@@ -208,18 +259,39 @@ static struct miscdevice softdog_miscdev = {
.
fops
=
&
softdog_fops
,
};
static
char
banner
[]
__initdata
=
KERN_INFO
"Software Watchdog Timer: 0.06, soft_margin: %d sec, nowayout: %d
\n
"
;
static
struct
notifier_block
softdog_notifier
=
{
.
notifier_call
=
softdog_notify_sys
,
};
static
char
banner
[]
__initdata
=
KERN_INFO
"Software Watchdog Timer: 0.07 initialized. soft_noboot=%d soft_margin=%d sec (nowayout= %d)
\n
"
;
static
int
__init
watchdog_init
(
void
)
{
int
ret
;
ret
=
misc_register
(
&
softdog_miscdev
);
/* Check that the soft_margin value is within it's range ; if not reset to the default */
if
(
softdog_set_heartbeat
(
soft_margin
))
{
softdog_set_heartbeat
(
TIMER_MARGIN
);
printk
(
KERN_INFO
PFX
"soft_margin value must be 0<soft_margin<65536, using %d
\n
"
,
TIMER_MARGIN
);
}
if
(
ret
)
ret
=
register_reboot_notifier
(
&
softdog_notifier
);
if
(
ret
)
{
printk
(
KERN_ERR
PFX
"cannot register reboot notifier (err=%d)
\n
"
,
ret
);
return
ret
;
}
ret
=
misc_register
(
&
softdog_miscdev
);
if
(
ret
)
{
printk
(
KERN_ERR
PFX
"cannot register miscdev on minor=%d (err=%d)
\n
"
,
WATCHDOG_MINOR
,
ret
);
unregister_reboot_notifier
(
&
softdog_notifier
);
return
ret
;
}
printk
(
banner
,
soft_margin
,
nowayout
);
printk
(
banner
,
soft_
noboot
,
soft_
margin
,
nowayout
);
return
0
;
}
...
...
@@ -227,6 +299,7 @@ static int __init watchdog_init(void)
static
void
__exit
watchdog_exit
(
void
)
{
misc_deregister
(
&
softdog_miscdev
);
unregister_reboot_notifier
(
&
softdog_notifier
);
}
module_init
(
watchdog_init
);
...
...
drivers/char/watchdog/wd501p.h
View file @
72f7e48f
/*
* Industrial Computer Source WDT500/501 driver
for Linux 1.3.x
* Industrial Computer Source WDT500/501 driver
*
* (c) Copyright 1995 CymruNET Ltd
* Innovation Centre
...
...
@@ -40,52 +40,13 @@
/* programmable outputs: */
#define WDT_PROGOUT (io+15)
/* wr=enable, rd=disable */
#define WDC_SR_WCCR 1
/* Active low */
#define WDC_SR_TGOOD 2
#define WDC_SR_ISOI0 4
#define WDC_SR_ISII1 8
#define WDC_SR_FANGOOD 16
#define WDC_SR_PSUOVER 32
/* Active low */
#define WDC_SR_PSUUNDR 64
/* Active low */
#define WDC_SR_IRQ 128
/* Active low */
/* FAN 501 500 */
#define WDC_SR_WCCR 1
/* Active low */
/* X X X */
#define WDC_SR_TGOOD 2
/* X X - */
#define WDC_SR_ISOI0 4
/* X X X */
#define WDC_SR_ISII1 8
/* X X X */
#define WDC_SR_FANGOOD 16
/* X - - */
#define WDC_SR_PSUOVER 32
/* Active low */
/* X X - */
#define WDC_SR_PSUUNDR 64
/* Active low */
/* X X - */
#define WDC_SR_IRQ 128
/* Active low */
/* X X X */
#ifndef WDT_IS_PCI
/*
* Feature Map 1 is the active high inputs not supported on your card.
* Feature Map 2 is the active low inputs not supported on your card.
*/
#ifdef CONFIG_WDT_501
/* Full board */
#ifdef CONFIG_WDT501_FAN
/* Full board, Fan has no tachometer */
#define FEATUREMAP1 0
#define WDT_OPTION_MASK (WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER|WDIOF_EXTERN1|WDIOF_EXTERN2|WDIOF_FANFAULT)
#else
#define FEATUREMAP1 WDC_SR_FANGOOD
#define WDT_OPTION_MASK (WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER|WDIOF_EXTERN1|WDIOF_EXTERN2)
#endif
#define FEATUREMAP2 0
#endif
#ifndef CONFIG_WDT_501
#define CONFIG_WDT_500
#endif
#ifdef CONFIG_WDT_500
/* Minimal board */
#define FEATUREMAP1 (WDC_SR_TGOOD|WDC_SR_FANGOOD)
#define FEATUREMAP2 (WDC_SR_PSUOVER|WDC_SR_PSUUNDR)
#define WDT_OPTION_MASK (WDIOF_OVERHEAT)
#endif
#else
#define FEATUREMAP1 (WDC_SR_TGOOD|WDC_SR_FANGOOD)
#define FEATUREMAP2 (WDC_SR_PSUOVER|WDC_SR_PSUUNDR)
#define WDT_OPTION_MASK (WDIOF_OVERHEAT)
#endif
#ifndef FEATUREMAP1
#error "Config option not set"
#endif
drivers/char/watchdog/wdt.c
View file @
72f7e48f
/*
* Industrial Computer Source WDT500/501 driver
for Linux 2.1.x
* Industrial Computer Source WDT500/501 driver
*
* (c) Copyright 1996-1997 Alan Cox <alan@redhat.com>, All Rights Reserved.
* http://www.redhat.com
...
...
@@ -15,7 +15,7 @@
*
* (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
*
* Release 0.
09
.
* Release 0.
10
.
*
* Fixes
* Dave Gregorich : Modularisation and minor bugs
...
...
@@ -53,17 +53,15 @@ static unsigned long wdt_is_open;
static
char
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.
* Module parameters
*/
static
int
io
=
0x240
;
static
int
irq
=
11
;
#define WD_TIMO 60
/* Default heartbeat = 60 seconds */
/* Default margin */
#define WD_TIMO (100*60)
/* 1 minute */
static
int
wd_margin
=
WD_TIMO
;
static
int
heartbeat
=
WD_TIMO
;
static
int
wd_heartbeat
;
module_param
(
heartbeat
,
int
,
0
);
MODULE_PARM_DESC
(
heartbeat
,
"Watchdog heartbeat in seconds. (0<heartbeat<65536, default="
__MODULE_STRING
(
WD_TIMO
)
")"
)
;
#ifdef CONFIG_WATCHDOG_NOWAYOUT
static
int
nowayout
=
1
;
...
...
@@ -74,11 +72,23 @@ static int nowayout = 0;
module_param
(
nowayout
,
int
,
0
);
MODULE_PARM_DESC
(
nowayout
,
"Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"
);
/* You must set these - there is no sane way to probe for this board. */
static
int
io
=
0x240
;
static
int
irq
=
11
;
module_param
(
io
,
int
,
0
);
MODULE_PARM_DESC
(
io
,
"WDT io port (default=0x240)"
);
module_param
(
irq
,
int
,
0
);
MODULE_PARM_DESC
(
irq
,
"WDT irq (default=11)"
);
#ifdef CONFIG_WDT_501
/* Support for the Fan Tachometer on the WDT501-P */
static
int
tachometer
;
module_param
(
tachometer
,
int
,
0
);
MODULE_PARM_DESC
(
tachometer
,
"WDT501-P Fan Tachometer support (0=disable, default=0)"
);
#endif
/* CONFIG_WDT_501 */
/*
* Programming support
*/
...
...
@@ -97,13 +107,77 @@ static void wdt_ctr_load(int ctr, int val)
outb_p
(
val
>>
8
,
WDT_COUNT0
+
ctr
);
}
/*
* Kernel methods.
/**
* wdt_start:
*
* Start the watchdog driver.
*/
static
int
wdt_start
(
void
)
{
inb_p
(
WDT_DC
);
/* Disable watchdog */
wdt_ctr_mode
(
0
,
3
);
/* Program CTR0 for Mode 3: Square Wave Generator */
wdt_ctr_mode
(
1
,
2
);
/* Program CTR1 for Mode 2: Rate Generator */
wdt_ctr_mode
(
2
,
0
);
/* Program CTR2 for Mode 0: Pulse on Terminal Count */
wdt_ctr_load
(
0
,
8948
);
/* Count at 100Hz */
wdt_ctr_load
(
1
,
wd_heartbeat
);
/* Heartbeat */
wdt_ctr_load
(
2
,
65535
);
/* Length of reset pulse */
outb_p
(
0
,
WDT_DC
);
/* Enable watchdog */
return
0
;
}
/**
* wdt_stop:
*
* Stop the watchdog driver.
*/
static
int
wdt_stop
(
void
)
{
/* Turn the card off */
inb_p
(
WDT_DC
);
/* Disable watchdog */
wdt_ctr_load
(
2
,
0
);
/* 0 length reset pulses now */
return
0
;
}
/**
* wdt_ping:
*
* Reload counter one with the watchdog heartbeat. We don't bother reloading
* the cascade counter.
*/
static
int
wdt_ping
(
void
)
{
/* Write a watchdog value */
inb_p
(
WDT_DC
);
/* Disable watchdog */
wdt_ctr_mode
(
1
,
2
);
/* Re-Program CTR1 for Mode 2: Rate Generator */
wdt_ctr_load
(
1
,
wd_heartbeat
);
/* Heartbeat */
outb_p
(
0
,
WDT_DC
);
/* Enable watchdog */
return
0
;
}
/**
* wdt_set_heartbeat:
* @t: the new heartbeat value that needs to be set.
*
* Set a new heartbeat value for the watchdog device. If the heartbeat value is
* incorrect we keep the old value and return -EINVAL. If successfull we
* return 0.
*/
static
int
wdt_set_heartbeat
(
int
t
)
{
if
((
t
<
1
)
||
(
t
>
65535
))
return
-
EINVAL
;
heartbeat
=
t
;
wd_heartbeat
=
t
*
100
;
return
0
;
}
/**
* wdt_status:
* wdt_get_status:
* @status: the new status.
*
* Extract the status information from a WDT watchdog device. There are
* several board variants so we have to know which bits are valid. Some
...
...
@@ -112,31 +186,46 @@ static void wdt_ctr_load(int ctr, int val)
* we then map the bits onto the status ioctl flags.
*/
static
int
wdt_
status
(
void
)
static
int
wdt_
get_status
(
int
*
status
)
{
/*
* Status register to bit flags
unsigned
char
new_status
=
inb_p
(
WDT_SR
);
*
status
=
0
;
if
(
new_status
&
WDC_SR_ISOI0
)
*
status
|=
WDIOF_EXTERN1
;
if
(
new_status
&
WDC_SR_ISII1
)
*
status
|=
WDIOF_EXTERN2
;
#ifdef CONFIG_WDT_501
if
(
!
(
new_status
&
WDC_SR_TGOOD
))
*
status
|=
WDIOF_OVERHEAT
;
if
(
!
(
new_status
&
WDC_SR_PSUOVER
))
*
status
|=
WDIOF_POWEROVER
;
if
(
!
(
new_status
&
WDC_SR_PSUUNDR
))
*
status
|=
WDIOF_POWERUNDER
;
if
(
tachometer
)
{
if
(
!
(
new_status
&
WDC_SR_FANGOOD
))
*
status
|=
WDIOF_FANFAULT
;
}
#endif
/* CONFIG_WDT_501 */
return
0
;
}
#ifdef CONFIG_WDT_501
/**
* wdt_get_temperature:
*
* Reports the temperature in degrees Fahrenheit. The API is in
* farenheit. It was designed by an imperial measurement luddite.
*/
int
flag
=
0
;
unsigned
char
status
=
inb_p
(
WDT_SR
);
status
|=
FEATUREMAP1
;
status
&=~
FEATUREMAP2
;
if
(
!
(
status
&
WDC_SR_TGOOD
))
flag
|=
WDIOF_OVERHEAT
;
if
(
!
(
status
&
WDC_SR_PSUOVER
))
flag
|=
WDIOF_POWEROVER
;
if
(
!
(
status
&
WDC_SR_PSUUNDR
))
flag
|=
WDIOF_POWERUNDER
;
if
(
!
(
status
&
WDC_SR_FANGOOD
))
flag
|=
WDIOF_FANFAULT
;
if
(
status
&
WDC_SR_ISOI0
)
flag
|=
WDIOF_EXTERN1
;
if
(
status
&
WDC_SR_ISII1
)
flag
|=
WDIOF_EXTERN2
;
return
flag
;
static
int
wdt_get_temperature
(
int
*
temperature
)
{
unsigned
short
c
=
inb_p
(
WDT_RT
);
*
temperature
=
(
c
*
11
/
15
)
+
7
;
return
0
;
}
#endif
/* CONFIG_WDT_501 */
/**
* wdt_interrupt:
...
...
@@ -155,23 +244,23 @@ static irqreturn_t wdt_interrupt(int irq, void *dev_id, struct pt_regs *regs)
* Read the status register see what is up and
* then printk it.
*/
unsigned
char
status
=
inb_p
(
WDT_SR
);
status
|=
FEATUREMAP1
;
status
&=~
FEATUREMAP2
;
printk
(
KERN_CRIT
"WDT status %d
\n
"
,
status
);
if
(
!
(
status
&
WDC_SR_TGOOD
))
#ifdef CONFIG_WDT_501
if
(
!
(
status
&
WDC_SR_TGOOD
))
printk
(
KERN_CRIT
"Overheat alarm.(%d)
\n
"
,
inb_p
(
WDT_RT
));
if
(
!
(
status
&
WDC_SR_PSUOVER
))
if
(
!
(
status
&
WDC_SR_PSUOVER
))
printk
(
KERN_CRIT
"PSU over voltage.
\n
"
);
if
(
!
(
status
&
WDC_SR_PSUUNDR
))
if
(
!
(
status
&
WDC_SR_PSUUNDR
))
printk
(
KERN_CRIT
"PSU under voltage.
\n
"
);
if
(
!
(
status
&
WDC_SR_FANGOOD
))
if
(
tachometer
)
{
if
(
!
(
status
&
WDC_SR_FANGOOD
))
printk
(
KERN_CRIT
"Possible fan fault.
\n
"
);
if
(
!
(
status
&
WDC_SR_WCCR
))
}
#endif
/* CONFIG_WDT_501 */
if
(
!
(
status
&
WDC_SR_WCCR
))
#ifdef SOFTWARE_REBOOT
#ifdef ONLY_TESTING
printk
(
KERN_CRIT
"Would Reboot.
\n
"
);
...
...
@@ -186,22 +275,6 @@ static irqreturn_t wdt_interrupt(int irq, void *dev_id, struct pt_regs *regs)
}
/**
* wdt_ping:
*
* Reload counter one with the watchdog timeout. We don't bother reloading
* the cascade counter.
*/
static
void
wdt_ping
(
void
)
{
/* Write a watchdog value */
inb_p
(
WDT_DC
);
wdt_ctr_mode
(
1
,
2
);
wdt_ctr_load
(
1
,
wd_margin
);
/* Timeout */
outb_p
(
0
,
WDT_DC
);
}
/**
* wdt_write:
* @file: file handle to the watchdog
...
...
@@ -239,40 +312,6 @@ static ssize_t wdt_write(struct file *file, const char *buf, size_t count, loff_
return
count
;
}
/**
* wdt_read:
* @file: file handle to the watchdog board
* @buf: buffer to write 1 byte into
* @count: length of buffer
* @ptr: offset (no seek allowed)
*
* Read reports the temperature in degrees Fahrenheit. The API is in
* farenheit. It was designed by an imperial measurement luddite.
*/
static
ssize_t
wdt_read
(
struct
file
*
file
,
char
*
buf
,
size_t
count
,
loff_t
*
ptr
)
{
unsigned
short
c
=
inb_p
(
WDT_RT
);
unsigned
char
cp
;
/* Can't seek (pread) on this device */
if
(
ptr
!=
&
file
->
f_pos
)
return
-
ESPIPE
;
switch
(
iminor
(
file
->
f_dentry
->
d_inode
))
{
case
TEMP_MINOR
:
c
*=
11
;
c
/=
15
;
cp
=
c
+
7
;
if
(
copy_to_user
(
buf
,
&
cp
,
1
))
return
-
EFAULT
;
return
1
;
default:
return
-
EINVAL
;
}
}
/**
* wdt_ioctl:
* @inode: inode of the device
...
...
@@ -288,18 +327,25 @@ static ssize_t wdt_read(struct file *file, char *buf, size_t count, loff_t *ptr)
static
int
wdt_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
int
new_margin
;
int
new_heartbeat
;
int
status
;
static
struct
watchdog_info
ident
=
{
.
options
=
WDIOF_OVERHEAT
|
WDIOF_POWERUNDER
|
WDIOF_POWEROVER
|
WDIOF_EXTERN1
|
WDIOF_EXTERN2
|
WDIOF_FANFAULT
|
WDIOF_SETTIMEOUT
|
WDIOF_MAGICCLOSE
,
static
struct
watchdog_info
ident
=
{
.
options
=
WDIOF_SETTIMEOUT
|
WDIOF_MAGICCLOSE
|
WDIOF_KEEPALIVEPING
,
.
firmware_version
=
1
,
.
identity
=
"WDT500/501"
,
};
ident
.
options
&=
WDT_OPTION_MASK
;
/* Mask down to the card we have */
/* Add options according to the card we have */
ident
.
options
|=
(
WDIOF_EXTERN1
|
WDIOF_EXTERN2
);
#ifdef CONFIG_WDT_501
ident
.
options
|=
(
WDIOF_OVERHEAT
|
WDIOF_POWERUNDER
|
WDIOF_POWEROVER
);
if
(
tachometer
)
ident
.
options
|=
WDIOF_FANFAULT
;
#endif
/* CONFIG_WDT_501 */
switch
(
cmd
)
{
default:
...
...
@@ -308,23 +354,24 @@ static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
return
copy_to_user
((
struct
watchdog_info
*
)
arg
,
&
ident
,
sizeof
(
ident
))
?-
EFAULT
:
0
;
case
WDIOC_GETSTATUS
:
return
put_user
(
wdt_status
(),(
int
*
)
arg
);
wdt_get_status
(
&
status
);
return
put_user
(
status
,(
int
*
)
arg
);
case
WDIOC_GETBOOTSTATUS
:
return
put_user
(
0
,
(
int
*
)
arg
);
case
WDIOC_KEEPALIVE
:
wdt_ping
();
return
0
;
case
WDIOC_SETTIMEOUT
:
if
(
get_user
(
new_
margin
,
(
int
*
)
arg
))
if
(
get_user
(
new_
heartbeat
,
(
int
*
)
arg
))
return
-
EFAULT
;
/* Arbitrary, can't find the card's limits */
if
(
(
new_margin
<
0
)
||
(
new_margin
>
60
))
if
(
wdt_set_heartbeat
(
new_heartbeat
))
return
-
EINVAL
;
wd_margin
=
new_margin
*
100
;
wdt_ping
();
/* Fall */
case
WDIOC_GETTIMEOUT
:
return
put_user
(
wd_margin
/
100
,
(
int
*
)
arg
);
return
put_user
(
heartbeat
,
(
int
*
)
arg
);
}
}
...
...
@@ -333,43 +380,26 @@ static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
* @inode: inode of device
* @file: file handle to device
*
*
One of our two misc devices has been opened. The watchdog device is
*
single open and on opening we load the counters. Counter zero is a
*
100Hz cascade, into counter 1 which downcounts to reboot. When the
*
counter triggers counter 2 downcounts the length of the reset pulse
*
which
set set to be as long as possible.
*
The watchdog device has been opened. The watchdog device is single
*
open and on opening we load the counters. Counter zero is a 100Hz
*
cascade, into counter 1 which downcounts to reboot. When the counter
*
triggers counter 2 downcounts the length of the reset pulse which
* set set to be as long as possible.
*/
static
int
wdt_open
(
struct
inode
*
inode
,
struct
file
*
file
)
{
switch
(
iminor
(
inode
))
{
case
WATCHDOG_MINOR
:
if
(
test_and_set_bit
(
0
,
&
wdt_is_open
))
return
-
EBUSY
;
/*
* Activate
*/
wdt_is_open
=
1
;
inb_p
(
WDT_DC
);
/* Disable */
wdt_ctr_mode
(
0
,
3
);
wdt_ctr_mode
(
1
,
2
);
wdt_ctr_mode
(
2
,
0
);
wdt_ctr_load
(
0
,
8948
);
/* count at 100Hz */
wdt_ctr_load
(
1
,
wd_margin
);
/* Timeout 120 seconds */
wdt_ctr_load
(
2
,
65535
);
outb_p
(
0
,
WDT_DC
);
/* Enable */
return
0
;
case
TEMP_MINOR
:
wdt_start
();
return
0
;
default:
return
-
ENODEV
;
}
}
/**
* wdt_
clo
se:
* wdt_
relea
se:
* @inode: inode to board
* @file: file handle to board
*
...
...
@@ -382,20 +412,73 @@ static int wdt_open(struct inode *inode, struct file *file)
static
int
wdt_release
(
struct
inode
*
inode
,
struct
file
*
file
)
{
if
(
iminor
(
inode
)
==
WATCHDOG_MINOR
)
{
if
(
expect_close
==
42
)
{
inb_p
(
WDT_DC
);
/* Disable counters */
wdt_ctr_load
(
2
,
0
);
/* 0 length reset pulses now */
wdt_stop
();
clear_bit
(
0
,
&
wdt_is_open
);
}
else
{
printk
(
KERN_CRIT
"wdt: WDT device closed unexpectedly. WDT will not stop!
\n
"
);
wdt_ping
();
}
clear_bit
(
0
,
&
wdt_is_open
);
expect_close
=
0
;
}
return
0
;
}
#ifdef CONFIG_WDT_501
/**
* wdt_temp_read:
* @file: file handle to the watchdog board
* @buf: buffer to write 1 byte into
* @count: length of buffer
* @ptr: offset (no seek allowed)
*
* Temp_read reports the temperature in degrees Fahrenheit. The API is in
* farenheit. It was designed by an imperial measurement luddite.
*/
static
ssize_t
wdt_temp_read
(
struct
file
*
file
,
char
*
buf
,
size_t
count
,
loff_t
*
ptr
)
{
int
temperature
;
/* Can't seek (pread) on this device */
if
(
ptr
!=
&
file
->
f_pos
)
return
-
ESPIPE
;
if
(
wdt_get_temperature
(
&
temperature
))
return
-
EFAULT
;
if
(
copy_to_user
(
buf
,
&
temperature
,
1
))
return
-
EFAULT
;
return
1
;
}
/**
* wdt_temp_open:
* @inode: inode of device
* @file: file handle to device
*
* The temperature device has been opened.
*/
static
int
wdt_temp_open
(
struct
inode
*
inode
,
struct
file
*
file
)
{
return
0
;
}
/**
* wdt_temp_release:
* @inode: inode to board
* @file: file handle to board
*
* The temperature device has been closed.
*/
static
int
wdt_temp_release
(
struct
inode
*
inode
,
struct
file
*
file
)
{
return
0
;
}
#endif
/* CONFIG_WDT_501 */
/**
* notify_sys:
* @this: our notifier block
...
...
@@ -411,11 +494,9 @@ static int wdt_release(struct inode *inode, struct file *file)
static
int
wdt_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 card off */
inb_p
(
WDT_DC
);
wdt_ctr_load
(
2
,
0
);
wdt_stop
();
}
return
NOTIFY_DONE
;
}
...
...
@@ -428,36 +509,40 @@ static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
static
struct
file_operations
wdt_fops
=
{
.
owner
=
THIS_MODULE
,
.
llseek
=
no_llseek
,
.
read
=
wdt_read
,
.
write
=
wdt_write
,
.
ioctl
=
wdt_ioctl
,
.
open
=
wdt_open
,
.
release
=
wdt_release
,
};
static
struct
miscdevice
wdt_miscdev
=
{
static
struct
miscdevice
wdt_miscdev
=
{
.
minor
=
WATCHDOG_MINOR
,
.
name
=
"watchdog"
,
.
fops
=
&
wdt_fops
,
};
#ifdef CONFIG_WDT_501
static
struct
miscdevice
temp_miscdev
=
{
static
struct
file_operations
wdt_temp_fops
=
{
.
owner
=
THIS_MODULE
,
.
llseek
=
no_llseek
,
.
read
=
wdt_temp_read
,
.
open
=
wdt_temp_open
,
.
release
=
wdt_temp_release
,
};
static
struct
miscdevice
temp_miscdev
=
{
.
minor
=
TEMP_MINOR
,
.
name
=
"temperature"
,
.
fops
=
&
wdt_fops
,
.
fops
=
&
wdt_
temp_
fops
,
};
#endif
#endif
/* CONFIG_WDT_501 */
/*
* The WDT card needs to learn about soft shutdowns in order to
* turn the timebomb registers off.
*/
static
struct
notifier_block
wdt_notifier
=
{
static
struct
notifier_block
wdt_notifier
=
{
.
notifier_call
=
wdt_notify_sys
,
};
...
...
@@ -476,10 +561,10 @@ static void __exit wdt_exit(void)
misc_deregister
(
&
wdt_miscdev
);
#ifdef CONFIG_WDT_501
misc_deregister
(
&
temp_miscdev
);
#endif
#endif
/* CONFIG_WDT_501 */
unregister_reboot_notifier
(
&
wdt_notifier
);
release_region
(
io
,
8
);
free_irq
(
irq
,
NULL
);
release_region
(
io
,
8
);
}
/**
...
...
@@ -494,51 +579,67 @@ static int __init wdt_init(void)
{
int
ret
;
ret
=
misc_register
(
&
wdt_miscdev
);
if
(
ret
)
{
printk
(
KERN_ERR
"wdt: can't misc_register on minor=%d
\n
"
,
WATCHDOG_MINOR
);
/* Check that the heartbeat value is within it's range ; if not reset to the default */
if
(
wdt_set_heartbeat
(
heartbeat
))
{
wdt_set_heartbeat
(
WD_TIMO
);
printk
(
KERN_INFO
"wdt: heartbeat value must be 0<heartbeat<65536, using %d
\n
"
,
WD_TIMO
);
}
if
(
!
request_region
(
io
,
8
,
"wdt501p"
))
{
printk
(
KERN_ERR
"wdt: I/O address 0x%04x already in use
\n
"
,
io
);
ret
=
-
EBUSY
;
goto
out
;
}
ret
=
request_irq
(
irq
,
wdt_interrupt
,
SA_INTERRUPT
,
"wdt501p"
,
NULL
);
if
(
ret
)
{
printk
(
KERN_ERR
"wdt: IRQ %d is not free.
\n
"
,
irq
);
goto
outmisc
;
}
if
(
!
request_region
(
io
,
8
,
"wdt501p"
))
{
printk
(
KERN_ERR
"wdt: IO %X is not free.
\n
"
,
io
);
ret
=
-
EBUSY
;
goto
outirq
;
goto
outreg
;
}
ret
=
register_reboot_notifier
(
&
wdt_notifier
);
if
(
ret
)
{
printk
(
KERN_ERR
"wdt: can
'
t register reboot notifier (err=%d)
\n
"
,
ret
);
goto
out
reg
;
printk
(
KERN_ERR
"wdt: can
no
t register reboot notifier (err=%d)
\n
"
,
ret
);
goto
out
irq
;
}
#ifdef CONFIG_WDT_501
ret
=
misc_register
(
&
temp_miscdev
);
if
(
ret
)
{
printk
(
KERN_ERR
"wdt: can't misc_register (temp) on minor=%d
\n
"
,
TEMP_MINOR
);
printk
(
KERN_ERR
"wdt: cannot register miscdev on minor=%d (err=%d)
\n
"
,
TEMP_MINOR
,
ret
);
goto
outrbt
;
}
#endif
#endif
/* CONFIG_WDT_501 */
ret
=
misc_register
(
&
wdt_miscdev
);
if
(
ret
)
{
printk
(
KERN_ERR
"wdt: cannot register miscdev on minor=%d (err=%d)
\n
"
,
WATCHDOG_MINOR
,
ret
);
goto
outmisc
;
}
ret
=
0
;
printk
(
KERN_INFO
"WDT500/501-P driver 0.07 at %X (Interrupt %d)
\n
"
,
io
,
irq
);
printk
(
KERN_INFO
"WDT500/501-P driver 0.10 at 0x%04x (Interrupt %d). heartbeat=%d sec (nowayout=%d)
\n
"
,
io
,
irq
,
heartbeat
,
nowayout
);
#ifdef CONFIG_WDT_501
printk
(
KERN_INFO
"wdt: Fan Tachometer is %s
\n
"
,
(
tachometer
?
"Enabled"
:
"Disabled"
));
#endif
/* CONFIG_WDT_501 */
out:
return
ret
;
outmisc:
#ifdef CONFIG_WDT_501
misc_deregister
(
&
temp_miscdev
);
#endif
/* CONFIG_WDT_501 */
outrbt:
unregister_reboot_notifier
(
&
wdt_notifier
);
#endif
outreg:
release_region
(
io
,
8
);
outirq:
free_irq
(
irq
,
NULL
);
out
misc
:
misc_deregister
(
&
wdt_miscdev
);
out
reg
:
release_region
(
io
,
8
);
goto
out
;
}
...
...
drivers/char/watchdog/wdt_pci.c
View file @
72f7e48f
/*
* Industrial Computer Source
WDT500/501 driver for Linux 2.1.x
* Industrial Computer Source
PCI-WDT500/501 driver
*
* (c) Copyright 1996-1997 Alan Cox <alan@redhat.com>, All Rights Reserved.
* http://www.redhat.com
...
...
@@ -15,7 +15,7 @@
*
* (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
*
* Release 0.
09
.
* Release 0.
10
.
*
* Fixes
* Dave Gregorich : Modularisation and minor bugs
...
...
@@ -46,6 +46,7 @@
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/pci.h>
#include <asm/io.h>
...
...
@@ -70,6 +71,9 @@
#define PCI_DEVICE_ID_WDG_CSM 0x22c0
#endif
/* We can only use 1 card due to the /dev/watchdog restriction */
static
int
dev_count
;
static
struct
semaphore
open_sem
;
static
spinlock_t
wdtpci_lock
;
static
char
expect_close
;
...
...
@@ -78,10 +82,12 @@ static int io;
static
int
irq
;
/* Default timeout */
#define WD_TIMO (100*60)
/* 1 minute */
#define WD_TIMO_MAX (WD_TIMO*60)
/* 1 hour(?) */
#define WD_TIMO 60
/* Default heartbeat = 60 seconds */
static
int
wd_margin
=
WD_TIMO
;
static
int
heartbeat
=
WD_TIMO
;
static
int
wd_heartbeat
;
module_param
(
heartbeat
,
int
,
0
);
MODULE_PARM_DESC
(
heartbeat
,
"Watchdog heartbeat in seconds. (0<heartbeat<65536, default="
__MODULE_STRING
(
WD_TIMO
)
")"
);
#ifdef CONFIG_WATCHDOG_NOWAYOUT
static
int
nowayout
=
1
;
...
...
@@ -92,6 +98,14 @@ static int nowayout = 0;
module_param
(
nowayout
,
int
,
0
);
MODULE_PARM_DESC
(
nowayout
,
"Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"
);
#ifdef CONFIG_WDT_501_PCI
/* Support for the Fan Tachometer on the PCI-WDT501 */
static
int
tachometer
;
module_param
(
tachometer
,
int
,
0
);
MODULE_PARM_DESC
(
tachometer
,
"PCI-WDT501 Fan Tachometer support (0=disable, default=0)"
);
#endif
/* CONFIG_WDT_501_PCI */
/*
* Programming support
*/
...
...
@@ -110,13 +124,105 @@ static void wdtpci_ctr_load(int ctr, int val)
outb_p
(
val
>>
8
,
WDT_COUNT0
+
ctr
);
}
/*
* Kernel methods.
/**
* wdtpci_start:
*
* Start the watchdog driver.
*/
static
int
wdtpci_start
(
void
)
{
unsigned
long
flags
;
spin_lock_irqsave
(
&
wdtpci_lock
,
flags
);
/*
* "pet" the watchdog, as Access says.
* This resets the clock outputs.
*/
inb_p
(
WDT_DC
);
/* Disable watchdog */
wdtpci_ctr_mode
(
2
,
0
);
/* Program CTR2 for Mode 0: Pulse on Terminal Count */
outb_p
(
0
,
WDT_DC
);
/* Enable watchdog */
inb_p
(
WDT_DC
);
/* Disable watchdog */
outb_p
(
0
,
WDT_CLOCK
);
/* 2.0833MHz clock */
inb_p
(
WDT_BUZZER
);
/* disable */
inb_p
(
WDT_OPTONOTRST
);
/* disable */
inb_p
(
WDT_OPTORST
);
/* disable */
inb_p
(
WDT_PROGOUT
);
/* disable */
wdtpci_ctr_mode
(
0
,
3
);
/* Program CTR0 for Mode 3: Square Wave Generator */
wdtpci_ctr_mode
(
1
,
2
);
/* Program CTR1 for Mode 2: Rate Generator */
wdtpci_ctr_mode
(
2
,
1
);
/* Program CTR2 for Mode 1: Retriggerable One-Shot */
wdtpci_ctr_load
(
0
,
20833
);
/* count at 100Hz */
wdtpci_ctr_load
(
1
,
wd_heartbeat
);
/* Heartbeat */
/* DO NOT LOAD CTR2 on PCI card! -- JPN */
outb_p
(
0
,
WDT_DC
);
/* Enable watchdog */
spin_unlock_irqrestore
(
&
wdtpci_lock
,
flags
);
return
0
;
}
/**
* wdtpci_stop:
*
* Stop the watchdog driver.
*/
static
int
wdtpci_stop
(
void
)
{
unsigned
long
flags
;
/* Turn the card off */
spin_lock_irqsave
(
&
wdtpci_lock
,
flags
);
inb_p
(
WDT_DC
);
/* Disable watchdog */
wdtpci_ctr_load
(
2
,
0
);
/* 0 length reset pulses now */
spin_unlock_irqrestore
(
&
wdtpci_lock
,
flags
);
return
0
;
}
/**
* wdtpci_ping:
*
* Reload counter one with the watchdog heartbeat. We don't bother reloading
* the cascade counter.
*/
static
int
wdtpci_ping
(
void
)
{
unsigned
long
flags
;
/* Write a watchdog value */
spin_lock_irqsave
(
&
wdtpci_lock
,
flags
);
inb_p
(
WDT_DC
);
/* Disable watchdog */
wdtpci_ctr_mode
(
1
,
2
);
/* Re-Program CTR1 for Mode 2: Rate Generator */
wdtpci_ctr_load
(
1
,
wd_heartbeat
);
/* Heartbeat */
outb_p
(
0
,
WDT_DC
);
/* Enable watchdog */
spin_unlock_irqrestore
(
&
wdtpci_lock
,
flags
);
return
0
;
}
/**
* wdtpci_set_heartbeat:
* @t: the new heartbeat value that needs to be set.
*
* Set a new heartbeat value for the watchdog device. If the heartbeat value is
* incorrect we keep the old value and return -EINVAL. If successfull we
* return 0.
*/
static
int
wdtpci_set_heartbeat
(
int
t
)
{
/* Arbitrary, can't find the card's limits */
if
((
t
<
1
)
||
(
t
>
65535
))
return
-
EINVAL
;
heartbeat
=
t
;
wd_heartbeat
=
t
*
100
;
return
0
;
}
/**
* wdtpci_status:
* wdtpci_get_status:
* @status: the new status.
*
* Extract the status information from a WDT watchdog device. There are
* several board variants so we have to know which bits are valid. Some
...
...
@@ -125,31 +231,46 @@ static void wdtpci_ctr_load(int ctr, int val)
* we then map the bits onto the status ioctl flags.
*/
static
int
wdtpci_
status
(
void
)
static
int
wdtpci_
get_status
(
int
*
status
)
{
/*
* Status register to bit flags
unsigned
char
new_status
=
inb_p
(
WDT_SR
);
*
status
=
0
;
if
(
new_status
&
WDC_SR_ISOI0
)
*
status
|=
WDIOF_EXTERN1
;
if
(
new_status
&
WDC_SR_ISII1
)
*
status
|=
WDIOF_EXTERN2
;
#ifdef CONFIG_WDT_501_PCI
if
(
!
(
new_status
&
WDC_SR_TGOOD
))
*
status
|=
WDIOF_OVERHEAT
;
if
(
!
(
new_status
&
WDC_SR_PSUOVER
))
*
status
|=
WDIOF_POWEROVER
;
if
(
!
(
new_status
&
WDC_SR_PSUUNDR
))
*
status
|=
WDIOF_POWERUNDER
;
if
(
tachometer
)
{
if
(
!
(
new_status
&
WDC_SR_FANGOOD
))
*
status
|=
WDIOF_FANFAULT
;
}
#endif
/* CONFIG_WDT_501_PCI */
return
0
;
}
#ifdef CONFIG_WDT_501_PCI
/**
* wdtpci_get_temperature:
*
* Reports the temperature in degrees Fahrenheit. The API is in
* farenheit. It was designed by an imperial measurement luddite.
*/
int
flag
=
0
;
unsigned
char
status
=
inb_p
(
WDT_SR
);
status
|=
FEATUREMAP1
;
status
&=~
FEATUREMAP2
;
if
(
!
(
status
&
WDC_SR_TGOOD
))
flag
|=
WDIOF_OVERHEAT
;
if
(
!
(
status
&
WDC_SR_PSUOVER
))
flag
|=
WDIOF_POWEROVER
;
if
(
!
(
status
&
WDC_SR_PSUUNDR
))
flag
|=
WDIOF_POWERUNDER
;
if
(
!
(
status
&
WDC_SR_FANGOOD
))
flag
|=
WDIOF_FANFAULT
;
if
(
status
&
WDC_SR_ISOI0
)
flag
|=
WDIOF_EXTERN1
;
if
(
status
&
WDC_SR_ISII1
)
flag
|=
WDIOF_EXTERN2
;
return
flag
;
static
int
wdtpci_get_temperature
(
int
*
temperature
)
{
unsigned
short
c
=
inb_p
(
WDT_RT
);
*
temperature
=
(
c
*
11
/
15
)
+
7
;
return
0
;
}
#endif
/* CONFIG_WDT_501_PCI */
/**
* wdtpci_interrupt:
...
...
@@ -168,57 +289,37 @@ static irqreturn_t wdtpci_interrupt(int irq, void *dev_id, struct pt_regs *regs)
* Read the status register see what is up and
* then printk it.
*/
unsigned
char
status
=
inb_p
(
WDT_SR
);
status
|=
FEATUREMAP1
;
status
&=~
FEATUREMAP2
;
printk
(
KERN_CRIT
PFX
"status %d
\n
"
,
status
);
printk
(
KERN_CRIT
"WDT status %d
\n
"
,
status
);
if
(
!
(
status
&
WDC_SR_TGOOD
))
printk
(
KERN_CRIT
"Overheat alarm.(%d)
\n
"
,
inb_p
(
WDT_RT
));
if
(
!
(
status
&
WDC_SR_PSUOVER
))
printk
(
KERN_CRIT
"PSU over voltage.
\n
"
);
if
(
!
(
status
&
WDC_SR_PSUUNDR
))
printk
(
KERN_CRIT
"PSU under voltage.
\n
"
);
if
(
!
(
status
&
WDC_SR_FANGOOD
))
printk
(
KERN_CRIT
"Possible fan fault.
\n
"
);
if
(
!
(
status
&
WDC_SR_WCCR
))
#ifdef CONFIG_WDT_501_PCI
if
(
!
(
status
&
WDC_SR_TGOOD
))
printk
(
KERN_CRIT
PFX
"Overheat alarm.(%d)
\n
"
,
inb_p
(
WDT_RT
));
if
(
!
(
status
&
WDC_SR_PSUOVER
))
printk
(
KERN_CRIT
PFX
"PSU over voltage.
\n
"
);
if
(
!
(
status
&
WDC_SR_PSUUNDR
))
printk
(
KERN_CRIT
PFX
"PSU under voltage.
\n
"
);
if
(
tachometer
)
{
if
(
!
(
status
&
WDC_SR_FANGOOD
))
printk
(
KERN_CRIT
PFX
"Possible fan fault.
\n
"
);
}
#endif
/* CONFIG_WDT_501_PCI */
if
(
!
(
status
&
WDC_SR_WCCR
))
#ifdef SOFTWARE_REBOOT
#ifdef ONLY_TESTING
printk
(
KERN_CRIT
"Would Reboot.
\n
"
);
printk
(
KERN_CRIT
PFX
"Would Reboot.
\n
"
);
#else
printk
(
KERN_CRIT
"Initiating system reboot.
\n
"
);
printk
(
KERN_CRIT
PFX
"Initiating system reboot.
\n
"
);
machine_restart
(
NULL
);
#endif
#else
printk
(
KERN_CRIT
"Reset in 5ms.
\n
"
);
printk
(
KERN_CRIT
PFX
"Reset in 5ms.
\n
"
);
#endif
return
IRQ_HANDLED
;
}
/**
* wdtpci_ping:
*
* Reload counter one with the watchdog timeout. We don't bother reloading
* the cascade counter.
*/
static
void
wdtpci_ping
(
void
)
{
unsigned
long
flags
;
/* Write a watchdog value */
spin_lock_irqsave
(
&
wdtpci_lock
,
flags
);
inb_p
(
WDT_DC
);
wdtpci_ctr_mode
(
1
,
2
);
wdtpci_ctr_load
(
1
,
wd_margin
);
/* Timeout */
outb_p
(
0
,
WDT_DC
);
spin_unlock_irqrestore
(
&
wdtpci_lock
,
flags
);
}
/**
* wdtpci_write:
* @file: file handle to the watchdog
...
...
@@ -256,40 +357,6 @@ static ssize_t wdtpci_write(struct file *file, const char *buf, size_t count, lo
return
count
;
}
/**
* wdtpci_read:
* @file: file handle to the watchdog board
* @buf: buffer to write 1 byte into
* @count: length of buffer
* @ptr: offset (no seek allowed)
*
* Read reports the temperature in degrees Fahrenheit. The API is in
* fahrenheit. It was designed by an imperial measurement luddite.
*/
static
ssize_t
wdtpci_read
(
struct
file
*
file
,
char
*
buf
,
size_t
count
,
loff_t
*
ptr
)
{
unsigned
short
c
=
inb_p
(
WDT_RT
);
unsigned
char
cp
;
/* Can't seek (pread) on this device */
if
(
ptr
!=
&
file
->
f_pos
)
return
-
ESPIPE
;
switch
(
iminor
(
file
->
f_dentry
->
d_inode
))
{
case
TEMP_MINOR
:
c
*=
11
;
c
/=
15
;
cp
=
c
+
7
;
if
(
copy_to_user
(
buf
,
&
cp
,
1
))
return
-
EFAULT
;
return
1
;
default:
return
-
EINVAL
;
}
}
/**
* wdtpci_ioctl:
* @inode: inode of the device
...
...
@@ -305,17 +372,25 @@ static ssize_t wdtpci_read(struct file *file, char *buf, size_t count, loff_t *p
static
int
wdtpci_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
int
new_margin
;
int
new_heartbeat
;
int
status
;
static
struct
watchdog_info
ident
=
{
.
options
=
WDIOF_OVERHEAT
|
WDIOF_POWERUNDER
|
WDIOF_POWEROVER
|
WDIOF_EXTERN1
|
WDIOF_EXTERN2
|
WDIOF_FANFAULT
|
WDIOF_SETTIMEOUT
|
WDIOF_MAGICCLOSE
,
.
options
=
WDIOF_SETTIMEOUT
|
WDIOF_MAGICCLOSE
|
WDIOF_KEEPALIVEPING
,
.
firmware_version
=
1
,
.
identity
=
"WDT500/501PCI
"
,
.
identity
=
"PCI-WDT500/501
"
,
};
ident
.
options
&=
WDT_OPTION_MASK
;
/* Mask down to the card we have */
/* Add options according to the card we have */
ident
.
options
|=
(
WDIOF_EXTERN1
|
WDIOF_EXTERN2
);
#ifdef CONFIG_WDT_501_PCI
ident
.
options
|=
(
WDIOF_OVERHEAT
|
WDIOF_POWERUNDER
|
WDIOF_POWEROVER
);
if
(
tachometer
)
ident
.
options
|=
WDIOF_FANFAULT
;
#endif
/* CONFIG_WDT_501_PCI */
switch
(
cmd
)
{
default:
...
...
@@ -324,24 +399,24 @@ static int wdtpci_ioctl(struct inode *inode, struct file *file, unsigned int cmd
return
copy_to_user
((
struct
watchdog_info
*
)
arg
,
&
ident
,
sizeof
(
ident
))
?-
EFAULT
:
0
;
case
WDIOC_GETSTATUS
:
return
put_user
(
wdtpci_status
(),(
int
*
)
arg
);
wdtpci_get_status
(
&
status
);
return
put_user
(
status
,(
int
*
)
arg
);
case
WDIOC_GETBOOTSTATUS
:
return
put_user
(
0
,
(
int
*
)
arg
);
case
WDIOC_KEEPALIVE
:
wdtpci_ping
();
return
0
;
case
WDIOC_SETTIMEOUT
:
if
(
get_user
(
new_
margin
,
(
int
*
)
arg
))
if
(
get_user
(
new_
heartbeat
,
(
int
*
)
arg
))
return
-
EFAULT
;
/* Arbitrary, can't find the card's limits */
new_margin
*=
100
;
if
((
new_margin
<
0
)
||
(
new_margin
>
WD_TIMO_MAX
))
if
(
wdtpci_set_heartbeat
(
new_heartbeat
))
return
-
EINVAL
;
wd_margin
=
new_margin
;
wdtpci_ping
();
/* Fall */
case
WDIOC_GETTIMEOUT
:
return
put_user
(
wd_margin
/
100
,
(
int
*
)
arg
);
return
put_user
(
heartbeat
,
(
int
*
)
arg
);
}
}
...
...
@@ -350,20 +425,15 @@ static int wdtpci_ioctl(struct inode *inode, struct file *file, unsigned int cmd
* @inode: inode of device
* @file: file handle to device
*
*
One of our two misc devices has been opened. The watchdog device is
*
single open and on opening we load the counters. Counter zero is a
*
100Hz cascade, into counter 1 which downcounts to reboot. When the
*
counter triggers counter 2 downcounts the length of the reset pulse
*
which
set set to be as long as possible.
*
The watchdog device has been opened. The watchdog device is single
*
open and on opening we load the counters. Counter zero is a 100Hz
*
cascade, into counter 1 which downcounts to reboot. When the counter
*
triggers counter 2 downcounts the length of the reset pulse which
* set set to be as long as possible.
*/
static
int
wdtpci_open
(
struct
inode
*
inode
,
struct
file
*
file
)
{
unsigned
long
flags
;
switch
(
iminor
(
inode
))
{
case
WATCHDOG_MINOR
:
if
(
down_trylock
(
&
open_sem
))
return
-
EBUSY
;
...
...
@@ -373,43 +443,12 @@ static int wdtpci_open(struct inode *inode, struct file *file)
/*
* Activate
*/
spin_lock_irqsave
(
&
wdtpci_lock
,
flags
);
inb_p
(
WDT_DC
);
/* Disable */
/*
* "pet" the watchdog, as Access says.
* This resets the clock outputs.
*/
wdtpci_ctr_mode
(
2
,
0
);
outb_p
(
0
,
WDT_DC
);
inb_p
(
WDT_DC
);
outb_p
(
0
,
WDT_CLOCK
);
/* 2.0833MHz clock */
inb_p
(
WDT_BUZZER
);
/* disable */
inb_p
(
WDT_OPTONOTRST
);
/* disable */
inb_p
(
WDT_OPTORST
);
/* disable */
inb_p
(
WDT_PROGOUT
);
/* disable */
wdtpci_ctr_mode
(
0
,
3
);
wdtpci_ctr_mode
(
1
,
2
);
wdtpci_ctr_mode
(
2
,
1
);
wdtpci_ctr_load
(
0
,
20833
);
/* count at 100Hz */
wdtpci_ctr_load
(
1
,
wd_margin
);
/* Timeout 60 seconds */
/* DO NOT LOAD CTR2 on PCI card! -- JPN */
outb_p
(
0
,
WDT_DC
);
/* Enable */
spin_unlock_irqrestore
(
&
wdtpci_lock
,
flags
);
wdtpci_start
();
return
0
;
case
TEMP_MINOR
:
return
0
;
default:
return
-
ENODEV
;
}
}
/**
* wdtpci_
clo
se:
* wdtpci_
relea
se:
* @inode: inode to board
* @file: file handle to board
*
...
...
@@ -422,24 +461,73 @@ static int wdtpci_open(struct inode *inode, struct file *file)
static
int
wdtpci_release
(
struct
inode
*
inode
,
struct
file
*
file
)
{
if
(
iminor
(
inode
)
==
WATCHDOG_MINOR
)
{
unsigned
long
flags
;
if
(
expect_close
==
42
)
{
spin_lock_irqsave
(
&
wdtpci_lock
,
flags
);
inb_p
(
WDT_DC
);
/* Disable counters */
wdtpci_ctr_load
(
2
,
0
);
/* 0 length reset pulses now */
spin_unlock_irqrestore
(
&
wdtpci_lock
,
flags
);
wdtpci_stop
();
}
else
{
printk
(
KERN_CRIT
PFX
"Unexpected close, not stopping timer!"
);
wdtpci_ping
();
}
expect_close
=
0
;
up
(
&
open_sem
);
}
return
0
;
}
#ifdef CONFIG_WDT_501_PCI
/**
* wdtpci_temp_read:
* @file: file handle to the watchdog board
* @buf: buffer to write 1 byte into
* @count: length of buffer
* @ptr: offset (no seek allowed)
*
* Read reports the temperature in degrees Fahrenheit. The API is in
* fahrenheit. It was designed by an imperial measurement luddite.
*/
static
ssize_t
wdtpci_temp_read
(
struct
file
*
file
,
char
*
buf
,
size_t
count
,
loff_t
*
ptr
)
{
int
temperature
;
/* Can't seek (pread) on this device */
if
(
ptr
!=
&
file
->
f_pos
)
return
-
ESPIPE
;
if
(
wdtpci_get_temperature
(
&
temperature
))
return
-
EFAULT
;
if
(
copy_to_user
(
buf
,
&
temperature
,
1
))
return
-
EFAULT
;
return
1
;
}
/**
* wdtpci_temp_open:
* @inode: inode of device
* @file: file handle to device
*
* The temperature device has been opened.
*/
static
int
wdtpci_temp_open
(
struct
inode
*
inode
,
struct
file
*
file
)
{
return
0
;
}
/**
* wdtpci_temp_release:
* @inode: inode to board
* @file: file handle to board
*
* The temperature device has been closed.
*/
static
int
wdtpci_temp_release
(
struct
inode
*
inode
,
struct
file
*
file
)
{
return
0
;
}
#endif
/* CONFIG_WDT_501_PCI */
/**
* notify_sys:
* @this: our notifier block
...
...
@@ -455,14 +543,9 @@ static int wdtpci_release(struct inode *inode, struct file *file)
static
int
wdtpci_notify_sys
(
struct
notifier_block
*
this
,
unsigned
long
code
,
void
*
unused
)
{
unsigned
long
flags
;
if
(
code
==
SYS_DOWN
||
code
==
SYS_HALT
)
{
/* Turn the card off */
spin_lock_irqsave
(
&
wdtpci_lock
,
flags
);
inb_p
(
WDT_DC
);
wdtpci_ctr_load
(
2
,
0
);
spin_unlock_irqrestore
(
&
wdtpci_lock
,
flags
);
wdtpci_stop
();
}
return
NOTIFY_DONE
;
}
...
...
@@ -475,7 +558,6 @@ static int wdtpci_notify_sys(struct notifier_block *this, unsigned long code,
static
struct
file_operations
wdtpci_fops
=
{
.
owner
=
THIS_MODULE
,
.
llseek
=
no_llseek
,
.
read
=
wdtpci_read
,
.
write
=
wdtpci_write
,
.
ioctl
=
wdtpci_ioctl
,
.
open
=
wdtpci_open
,
...
...
@@ -489,12 +571,20 @@ static struct miscdevice wdtpci_miscdev = {
};
#ifdef CONFIG_WDT_501_PCI
static
struct
file_operations
wdtpci_temp_fops
=
{
.
owner
=
THIS_MODULE
,
.
llseek
=
no_llseek
,
.
read
=
wdtpci_temp_read
,
.
open
=
wdtpci_temp_open
,
.
release
=
wdtpci_temp_release
,
};
static
struct
miscdevice
temp_miscdev
=
{
.
minor
=
TEMP_MINOR
,
.
name
=
"temperature"
,
.
fops
=
&
wdtpci_fops
,
.
fops
=
&
wdtpci_
temp_
fops
,
};
#endif
#endif
/* CONFIG_WDT_501_PCI */
/*
* The WDT card needs to learn about soft shutdowns in order to
...
...
@@ -509,71 +599,96 @@ static struct notifier_block wdtpci_notifier = {
static
int
__devinit
wdtpci_init_one
(
struct
pci_dev
*
dev
,
const
struct
pci_device_id
*
ent
)
{
static
int
dev_count
=
0
;
int
ret
=
-
EIO
;
dev_count
++
;
if
(
dev_count
>
1
)
{
printk
(
KERN_ERR
PFX
"this driver only supports 1 device
\n
"
);
printk
(
KERN_ERR
PFX
"this driver only supports 1 device
\n
"
);
return
-
ENODEV
;
}
if
(
pci_enable_device
(
dev
))
goto
out
;
if
(
pci_enable_device
(
dev
))
{
printk
(
KERN_ERR
PFX
"Not possible to enable PCI Device
\n
"
);
return
-
ENODEV
;
}
if
(
pci_resource_start
(
dev
,
2
)
==
0x0000
)
{
printk
(
KERN_ERR
PFX
"No I/O-Address for card detected
\n
"
);
ret
=
-
ENODEV
;
goto
out_pci
;
}
sema_init
(
&
open_sem
,
1
);
spin_lock_init
(
&
wdtpci_lock
);
irq
=
dev
->
irq
;
io
=
pci_resource_start
(
dev
,
2
);
printk
(
"WDT501-P(PCI-WDG-CSM) driver 0.07 at %X "
"(Interrupt %d)
\n
"
,
io
,
irq
);
if
(
request_region
(
io
,
16
,
"wdt
-
pci"
)
==
NULL
)
{
printk
(
KERN_ERR
PFX
"I/O
%d is not free.
\n
"
,
io
);
goto
out
;
if
(
request_region
(
io
,
16
,
"wdt
_
pci"
)
==
NULL
)
{
printk
(
KERN_ERR
PFX
"I/O
address 0x%04x already in use
\n
"
,
io
);
goto
out
_pci
;
}
if
(
request_irq
(
irq
,
wdtpci_interrupt
,
SA_INTERRUPT
|
SA_SHIRQ
,
"wdt
-
pci"
,
&
wdtpci_miscdev
))
{
printk
(
KERN_ERR
PFX
"IRQ %d is not free
.
\n
"
,
irq
);
"wdt
_
pci"
,
&
wdtpci_miscdev
))
{
printk
(
KERN_ERR
PFX
"IRQ %d is not free
\n
"
,
irq
);
goto
out_reg
;
}
ret
=
misc_register
(
&
wdtpci_miscdev
);
if
(
ret
)
{
printk
(
KERN_ERR
PFX
"can't misc_register on minor=%d
\n
"
,
WATCHDOG_MINOR
);
goto
out_irq
;
printk
(
"PCI-WDT500/501 (PCI-WDG-CSM) driver 0.10 at 0x%04x (Interrupt %d)
\n
"
,
io
,
irq
);
/* Check that the heartbeat value is within it's range ; if not reset to the default */
if
(
wdtpci_set_heartbeat
(
heartbeat
))
{
wdtpci_set_heartbeat
(
WD_TIMO
);
printk
(
KERN_INFO
PFX
"heartbeat value must be 0<heartbeat<65536, using %d
\n
"
,
WD_TIMO
);
}
ret
=
register_reboot_notifier
(
&
wdtpci_notifier
);
if
(
ret
)
{
printk
(
KERN_ERR
PFX
"can
't misc_register on minor=%d
\n
"
,
WATCHDOG_MINOR
);
goto
out_
misc
;
printk
(
KERN_ERR
PFX
"can
not register reboot notifier (err=%d)
\n
"
,
ret
);
goto
out_
irq
;
}
#ifdef CONFIG_WDT_501_PCI
ret
=
misc_register
(
&
temp_miscdev
);
if
(
ret
)
{
printk
(
KERN_ERR
PFX
"can't misc_register (temp) on minor=%d
\n
"
,
TEMP_MINOR
);
printk
(
KERN_ERR
PFX
"cannot register miscdev on minor=%d (err=%d)
\n
"
,
TEMP_MINOR
,
ret
);
goto
out_rbt
;
}
#endif
#endif
/* CONFIG_WDT_501_PCI */
ret
=
misc_register
(
&
wdtpci_miscdev
);
if
(
ret
)
{
printk
(
KERN_ERR
PFX
"cannot register miscdev on minor=%d (err=%d)
\n
"
,
WATCHDOG_MINOR
,
ret
);
goto
out_misc
;
}
printk
(
KERN_INFO
PFX
"initialized. heartbeat=%d sec (nowayout=%d)
\n
"
,
heartbeat
,
nowayout
);
#ifdef CONFIG_WDT_501_PCI
printk
(
KERN_INFO
"wdt: Fan Tachometer is %s
\n
"
,
(
tachometer
?
"Enabled"
:
"Disabled"
));
#endif
/* CONFIG_WDT_501_PCI */
ret
=
0
;
out:
return
ret
;
out_misc:
#ifdef CONFIG_WDT_501_PCI
misc_deregister
(
&
temp_miscdev
);
#endif
/* CONFIG_WDT_501_PCI */
out_rbt:
unregister_reboot_notifier
(
&
wdtpci_notifier
);
#endif
out_misc:
misc_deregister
(
&
wdtpci_miscdev
);
out_irq:
free_irq
(
irq
,
&
wdtpci_miscdev
);
out_reg:
release_region
(
io
,
16
);
out_pci:
pci_disable_device
(
dev
);
goto
out
;
}
...
...
@@ -582,13 +697,15 @@ static void __devexit wdtpci_remove_one (struct pci_dev *pdev)
{
/* here we assume only one device will ever have
* been picked up and registered by probe function */
unregister_reboot_notifier
(
&
wdtpci_notifier
);
misc_deregister
(
&
wdtpci_miscdev
);
#ifdef CONFIG_WDT_501_PCI
misc_deregister
(
&
temp_miscdev
);
#endif
misc_deregister
(
&
wdtpci_miscdev
);
#endif
/* CONFIG_WDT_501_PCI */
unregister_reboot_notifier
(
&
wdtpci_notifier
);
free_irq
(
irq
,
&
wdtpci_miscdev
);
release_region
(
io
,
16
);
pci_disable_device
(
pdev
);
dev_count
--
;
}
...
...
@@ -605,7 +722,7 @@ MODULE_DEVICE_TABLE(pci, wdtpci_pci_tbl);
static
struct
pci_driver
wdtpci_driver
=
{
.
name
=
"wdt
-
pci"
,
.
name
=
"wdt
_
pci"
,
.
id_table
=
wdtpci_pci_tbl
,
.
probe
=
wdtpci_init_one
,
.
remove
=
__devexit_p
(
wdtpci_remove_one
),
...
...
@@ -619,7 +736,7 @@ static struct pci_driver wdtpci_driver = {
* 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.
* module in
xx
seconds or reboot.
*/
static
void
__exit
wdtpci_cleanup
(
void
)
...
...
@@ -638,12 +755,7 @@ static void __exit wdtpci_cleanup(void)
static
int
__init
wdtpci_init
(
void
)
{
int
rc
=
pci_register_driver
(
&
wdtpci_driver
);
if
(
rc
<
1
)
return
-
ENODEV
;
return
0
;
return
pci_register_driver
(
&
wdtpci_driver
);
}
...
...
@@ -651,7 +763,7 @@ module_init(wdtpci_init);
module_exit
(
wdtpci_cleanup
);
MODULE_AUTHOR
(
"JP Nollmann, Alan Cox"
);
MODULE_DESCRIPTION
(
"Driver for the ICS PCI watchdog cards"
);
MODULE_DESCRIPTION
(
"Driver for the ICS PCI
-WDT500/501
watchdog cards"
);
MODULE_LICENSE
(
"GPL"
);
MODULE_ALIAS_MISCDEV
(
WATCHDOG_MINOR
);
MODULE_ALIAS_MISCDEV
(
TEMP_MINOR
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment