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
nexedi
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
Hide 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
))
return
-
EFAULT
;
}
else
if
(
put_user
(
rv
,
(
int
*
)
arg
))
return
-
EFAULT
;
return
0
;
if
(
pcwd_get_temperature
(
&
temperature
))
return
-
EFAULT
;
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
(
!
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
:
return
(
0
);
default:
return
(
-
ENODEV
);
/* Activate */
pcwd_start
();
return
(
0
);
}
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
))
return
-
EFAULT
;
return
1
;
default:
return
-
EINVAL
;
}
if
(
pcwd_get_temperature
(
&
temperature
))
return
-
EFAULT
;
if
(
copy_to_user
(
buf
,
&
temperature
,
1
))
return
-
EFAULT
;
return
1
;
}
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
,
.
identity
=
"Software Watchdog"
,
.
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_status:
* 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_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
);
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
;
*
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.
*/
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
))
printk
(
KERN_CRIT
"Possible fan fault.
\n
"
);
if
(
!
(
status
&
WDC_SR_WCCR
))
if
(
tachometer
)
{
if
(
!
(
status
&
WDC_SR_FANGOOD
))
printk
(
KERN_CRIT
"Possible fan fault.
\n
"
);
}
#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
;
static
struct
watchdog_info
ident
=
{
.
options
=
WDIOF_OVERHEAT
|
WDIOF_POWERUNDER
|
WDIOF_POWEROVER
|
WDIOF_EXTERN1
|
WDIOF_EXTERN2
|
WDIOF_FANFAULT
|
WDIOF_SETTIMEOUT
|
WDIOF_MAGICCLOSE
,
.
firmware_version
=
1
,
.
identity
=
"WDT500/501"
,
int
new_
heartbeat
;
int
status
;
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
:
return
0
;
default:
return
-
ENODEV
;
}
if
(
test_and_set_bit
(
0
,
&
wdt_is_open
))
return
-
EBUSY
;
/*
* Activate
*/
wdt_start
();
return
0
;
}
/**
* wdt_
clo
se:
* wdt_
relea
se:
* @inode: inode to board
* @file: file handle to board
*
...
...
@@ -382,19 +412,72 @@ 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 */
}
else
{
printk
(
KERN_CRIT
"wdt: WDT device closed unexpectedly. WDT will not stop!
\n
"
);
}
if
(
expect_close
==
42
)
{
wdt_stop
();
clear_bit
(
0
,
&
wdt_is_open
);
expect_close
=
0
;
}
else
{
printk
(
KERN_CRIT
"wdt: WDT device closed unexpectedly. WDT will not stop!
\n
"
);
wdt_ping
();
}
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:
...
...
@@ -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,32 +231,47 @@ 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
);
int
flag
=
0
;
unsigned
char
status
=
inb_p
(
WDT_SR
);
status
|=
FEATUREMAP
1
;
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
;
*
status
=
0
;
if
(
new_status
&
WDC_SR_ISOI0
)
*
status
|=
WDIOF_EXTERN
1
;
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.
*/
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:
* @irq: Interrupt number
...
...
@@ -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
,
.
firmware_version
=
1
,
.
identity
=
"WDT500/501PCI"
,
.
options
=
WDIOF_SETTIMEOUT
|
WDIOF_MAGICCLOSE
|
WDIOF_KEEPALIVEPING
,
.
firmware_version
=
1
,
.
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,66 +425,30 @@ 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
;
if
(
down_trylock
(
&
open_sem
))
return
-
EBUSY
;
switch
(
iminor
(
inode
))
{
case
WATCHDOG_MINOR
:
if
(
down_trylock
(
&
open_sem
))
return
-
EBUSY
;
if
(
nowayout
)
{
__module_get
(
THIS_MODULE
);
}
/*
* 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
);
return
0
;
case
TEMP_MINOR
:
return
0
;
default:
return
-
ENODEV
;
if
(
nowayout
)
{
__module_get
(
THIS_MODULE
);
}
/*
* Activate
*/
wdtpci_start
();
return
0
;
}
/**
* wdtpci_
clo
se:
* wdtpci_
relea
se:
* @inode: inode to board
* @file: file handle to board
*
...
...
@@ -422,23 +461,72 @@ 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
);
}
else
{
printk
(
KERN_CRIT
PFX
"Unexpected close, not stopping timer!"
);
wdtpci_ping
();
}
expect_close
=
0
;
up
(
&
open_sem
);
if
(
expect_close
==
42
)
{
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:
...
...
@@ -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