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
47c44594
Commit
47c44594
authored
Aug 13, 2003
by
Linus Torvalds
Browse files
Options
Browse Files
Download
Plain Diff
Merge
http://linux-watchdog.bkbits.net/linux-2.5-watchdog
into home.osdl.org:/home/torvalds/v2.5/linux
parents
e5dbafd4
e26d4f47
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
258 additions
and
148 deletions
+258
-148
drivers/char/watchdog/alim7101_wdt.c
drivers/char/watchdog/alim7101_wdt.c
+112
-60
drivers/char/watchdog/sc520_wdt.c
drivers/char/watchdog/sc520_wdt.c
+146
-88
No files found.
drivers/char/watchdog/alim7101_wdt.c
View file @
47c44594
/*
/*
* ALi M7101 PMU Computer Watchdog Timer driver
for Linux 2.4.x
* ALi M7101 PMU Computer Watchdog Timer driver
*
*
* Based on w83877f_wdt.c by Scott Jennings <
management
@oro.net>
* Based on w83877f_wdt.c by Scott Jennings <
linuxdrivers
@oro.net>
* and the Cobalt kernel WDT timer driver by Tim Hockin
* and the Cobalt kernel WDT timer driver by Tim Hockin
* <thockin@cobaltnet.com>
* <thockin@cobaltnet.com>
*
*
* (c)2002 Steve Hill <steve@navaho.co.uk>
* (c)2002 Steve Hill <steve@navaho.co.uk>
*
* Theory of operation:
* A Watchdog Timer (WDT) is a hardware circuit that can
* reset the computer system in case of a software fault.
* You probably knew that already.
*
* Usually a userspace daemon will notify the kernel WDT driver
* via the /proc/watchdog special device file that userspace is
* still alive, at regular intervals. When such a notification
* occurs, the driver will usually tell the hardware watchdog
* that everything is in order, and that the watchdog should wait
* for yet another little while to reset the system.
* If userspace fails (RAM error, kernel bug, whatever), the
* notifications cease to occur, and the hardware watchdog will
* reset the system (causing a reboot) after the timeout occurs.
*
*
* This WDT driver is different from most other Linux WDT
* This WDT driver is different from most other Linux WDT
* drivers in that the driver will ping the watchdog by itself,
* drivers in that the driver will ping the watchdog by itself,
...
@@ -30,6 +15,7 @@
...
@@ -30,6 +15,7 @@
*/
*/
#include <linux/module.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/types.h>
#include <linux/timer.h>
#include <linux/timer.h>
#include <linux/miscdevice.h>
#include <linux/miscdevice.h>
...
@@ -38,7 +24,6 @@
...
@@ -38,7 +24,6 @@
#include <linux/notifier.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/reboot.h>
#include <linux/init.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/pci.h>
#include <linux/pci.h>
#include <asm/io.h>
#include <asm/io.h>
...
@@ -46,6 +31,7 @@
...
@@ -46,6 +31,7 @@
#include <asm/system.h>
#include <asm/system.h>
#define OUR_NAME "alim7101_wdt"
#define OUR_NAME "alim7101_wdt"
#define PFX OUR_NAME ": "
#define WDT_ENABLE 0x9C
#define WDT_ENABLE 0x9C
#define WDT_DISABLE 0x8C
#define WDT_DISABLE 0x8C
...
@@ -65,13 +51,16 @@
...
@@ -65,13 +51,16 @@
* char to /dev/watchdog every 30 seconds.
* char to /dev/watchdog every 30 seconds.
*/
*/
#define WDT_HEARTBEAT (HZ * 30)
#define WATCHDOG_TIMEOUT 30
/* 30 sec default timeout */
static
int
timeout
=
WATCHDOG_TIMEOUT
;
/* in seconds, will be multiplied by HZ to get seconds to wait for a ping */
module_param
(
timeout
,
int
,
0
);
MODULE_PARM_DESC
(
timeout
,
"Watchdog timeout in seconds. (1<=timeout<=3600, default="
__MODULE_STRING
(
WATCHDOG_TIMEOUT
)
")"
);
static
void
wdt_timer_ping
(
unsigned
long
);
static
void
wdt_timer_ping
(
unsigned
long
);
static
struct
timer_list
timer
;
static
struct
timer_list
timer
;
static
unsigned
long
next_heartbeat
;
static
unsigned
long
next_heartbeat
;
static
unsigned
long
wdt_is_open
;
static
unsigned
long
wdt_is_open
;
static
int
wdt_expect_close
;
static
char
wdt_expect_close
;
static
struct
pci_dev
*
alim7101_pmu
;
static
struct
pci_dev
*
alim7101_pmu
;
#ifdef CONFIG_WATCHDOG_NOWAYOUT
#ifdef CONFIG_WATCHDOG_NOWAYOUT
...
@@ -79,7 +68,7 @@ static int nowayout = 1;
...
@@ -79,7 +68,7 @@ static int nowayout = 1;
#else
#else
static
int
nowayout
=
0
;
static
int
nowayout
=
0
;
#endif
#endif
module_param
(
nowayout
,
int
,
0
);
module_param
(
nowayout
,
int
,
0
);
MODULE_PARM_DESC
(
nowayout
,
"Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"
);
MODULE_PARM_DESC
(
nowayout
,
"Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"
);
...
@@ -90,25 +79,25 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CON
...
@@ -90,25 +79,25 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CON
static
void
wdt_timer_ping
(
unsigned
long
data
)
static
void
wdt_timer_ping
(
unsigned
long
data
)
{
{
/* If we got a heartbeat pulse within the WDT_US_INTERVAL
/* If we got a heartbeat pulse within the WDT_US_INTERVAL
* we agree to ping the WDT
* we agree to ping the WDT
*/
*/
char
tmp
;
char
tmp
;
if
(
time_before
(
jiffies
,
next_heartbeat
))
if
(
time_before
(
jiffies
,
next_heartbeat
))
{
{
/* Ping the WDT (this is actually a disarm/arm sequence) */
/* Ping the WDT (this is actually a disarm/arm sequence) */
pci_read_config_byte
(
alim7101_pmu
,
0x92
,
&
tmp
);
pci_read_config_byte
(
alim7101_pmu
,
0x92
,
&
tmp
);
pci_write_config_byte
(
alim7101_pmu
,
ALI_7101_WDT
,
(
tmp
&
~
ALI_WDT_ARM
));
pci_write_config_byte
(
alim7101_pmu
,
ALI_7101_WDT
,
(
tmp
&
~
ALI_WDT_ARM
));
pci_write_config_byte
(
alim7101_pmu
,
ALI_7101_WDT
,
(
tmp
|
ALI_WDT_ARM
));
pci_write_config_byte
(
alim7101_pmu
,
ALI_7101_WDT
,
(
tmp
|
ALI_WDT_ARM
));
}
else
{
}
else
{
printk
(
KERN_
INFO
OUR_NAME
":
Heartbeat lost! Will not ping the watchdog
\n
"
);
printk
(
KERN_
WARNING
PFX
"
Heartbeat lost! Will not ping the watchdog
\n
"
);
}
}
/* Re-set the timer interval */
/* Re-set the timer interval */
timer
.
expires
=
jiffies
+
WDT_INTERVAL
;
timer
.
expires
=
jiffies
+
WDT_INTERVAL
;
add_timer
(
&
timer
);
add_timer
(
&
timer
);
}
}
/*
/*
* Utility routines
* Utility routines
*/
*/
...
@@ -125,7 +114,7 @@ static void wdt_change(int writeval)
...
@@ -125,7 +114,7 @@ static void wdt_change(int writeval)
static
void
wdt_startup
(
void
)
static
void
wdt_startup
(
void
)
{
{
next_heartbeat
=
jiffies
+
WDT_HEARTBEAT
;
next_heartbeat
=
jiffies
+
(
timeout
*
HZ
)
;
/* We must enable before we kick off the timer in case the timer
/* We must enable before we kick off the timer in case the timer
occurs as we ping it */
occurs as we ping it */
...
@@ -133,11 +122,11 @@ static void wdt_startup(void)
...
@@ -133,11 +122,11 @@ static void wdt_startup(void)
wdt_change
(
WDT_ENABLE
);
wdt_change
(
WDT_ENABLE
);
/* Start the timer */
/* Start the timer */
timer
.
expires
=
jiffies
+
WDT_INTERVAL
;
timer
.
expires
=
jiffies
+
WDT_INTERVAL
;
add_timer
(
&
timer
);
add_timer
(
&
timer
);
printk
(
KERN_INFO
OUR_NAME
": Watchdog timer is now enabled.
\n
"
);
printk
(
KERN_INFO
PFX
"Watchdog timer is now enabled.
\n
"
);
}
}
static
void
wdt_turnoff
(
void
)
static
void
wdt_turnoff
(
void
)
...
@@ -145,7 +134,13 @@ static void wdt_turnoff(void)
...
@@ -145,7 +134,13 @@ static void wdt_turnoff(void)
/* Stop the timer */
/* Stop the timer */
del_timer_sync
(
&
timer
);
del_timer_sync
(
&
timer
);
wdt_change
(
WDT_DISABLE
);
wdt_change
(
WDT_DISABLE
);
printk
(
KERN_INFO
OUR_NAME
": Watchdog timer is now disabled...
\n
"
);
printk
(
KERN_INFO
PFX
"Watchdog timer is now disabled...
\n
"
);
}
static
void
wdt_keepalive
(
void
)
{
/* user land ping */
next_heartbeat
=
jiffies
+
(
timeout
*
HZ
);
}
}
/*
/*
...
@@ -158,7 +153,7 @@ static ssize_t fop_write(struct file * file, const char * buf, size_t count, lof
...
@@ -158,7 +153,7 @@ static ssize_t fop_write(struct file * file, const char * buf, size_t count, lof
if
(
ppos
!=
&
file
->
f_pos
)
if
(
ppos
!=
&
file
->
f_pos
)
return
-
ESPIPE
;
return
-
ESPIPE
;
/* See if we got the magic character */
/* See if we got the magic character
'V' and reload the timer
*/
if
(
count
)
{
if
(
count
)
{
if
(
!
nowayout
)
{
if
(
!
nowayout
)
{
size_t
ofs
;
size_t
ofs
;
...
@@ -173,14 +168,13 @@ static ssize_t fop_write(struct file * file, const char * buf, size_t count, lof
...
@@ -173,14 +168,13 @@ static ssize_t fop_write(struct file * file, const char * buf, size_t count, lof
if
(
get_user
(
c
,
buf
+
ofs
))
if
(
get_user
(
c
,
buf
+
ofs
))
return
-
EFAULT
;
return
-
EFAULT
;
if
(
c
==
'V'
)
if
(
c
==
'V'
)
wdt_expect_close
=
1
;
wdt_expect_close
=
42
;
}
}
}
}
/* someone wrote to us, we should restart timer */
/* someone wrote to us, we should restart timer */
next_heartbeat
=
jiffies
+
WDT_HEARTBEAT
;
wdt_keepalive
();
return
1
;
}
};
return
count
;
return
0
;
}
}
static
int
fop_open
(
struct
inode
*
inode
,
struct
file
*
file
)
static
int
fop_open
(
struct
inode
*
inode
,
struct
file
*
file
)
...
@@ -195,12 +189,14 @@ static int fop_open(struct inode * inode, struct file * file)
...
@@ -195,12 +189,14 @@ static int fop_open(struct inode * inode, struct file * file)
static
int
fop_close
(
struct
inode
*
inode
,
struct
file
*
file
)
static
int
fop_close
(
struct
inode
*
inode
,
struct
file
*
file
)
{
{
if
(
wdt_expect_close
)
if
(
wdt_expect_close
==
42
)
wdt_turnoff
();
wdt_turnoff
();
else
else
{
printk
(
KERN_INFO
OUR_NAME
": device file closed unexpectedly. Will not stop the WDT!
\n
"
);
/* wim: shouldn't there be a: del_timer(&timer); */
printk
(
KERN_CRIT
PFX
"device file closed unexpectedly. Will not stop the WDT!
\n
"
);
}
clear_bit
(
0
,
&
wdt_is_open
);
clear_bit
(
0
,
&
wdt_is_open
);
wdt_expect_close
=
0
;
return
0
;
return
0
;
}
}
...
@@ -208,20 +204,58 @@ static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, u
...
@@ -208,20 +204,58 @@ static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, u
{
{
static
struct
watchdog_info
ident
=
static
struct
watchdog_info
ident
=
{
{
.
options
=
WDIOF_MAGICCLOSE
,
.
options
=
WDIOF_
KEEPALIVEPING
|
WDIOF_SETTIMEOUT
|
WDIOF_
MAGICCLOSE
,
.
firmware_version
=
1
,
.
firmware_version
=
1
,
.
identity
=
"ALiM7101"
.
identity
=
"ALiM7101"
,
};
};
switch
(
cmd
)
switch
(
cmd
)
{
{
case
WDIOC_GETSUPPORT
:
case
WDIOC_GETSUPPORT
:
return
copy_to_user
((
struct
watchdog_info
*
)
arg
,
&
ident
,
sizeof
(
ident
))
?-
EFAULT
:
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
:
case
WDIOC_KEEPALIVE
:
next_heartbeat
=
jiffies
+
WDT_HEARTBEAT
;
wdt_keepalive
()
;
return
0
;
return
0
;
case
WDIOC_SETOPTIONS
:
{
int
new_options
,
retval
=
-
EINVAL
;
if
(
get_user
(
new_options
,
(
int
*
)
arg
))
return
-
EFAULT
;
if
(
new_options
&
WDIOS_DISABLECARD
)
{
wdt_turnoff
();
retval
=
0
;
}
if
(
new_options
&
WDIOS_ENABLECARD
)
{
wdt_startup
();
retval
=
0
;
}
return
retval
;
}
case
WDIOC_SETTIMEOUT
:
{
int
new_timeout
;
if
(
get_user
(
new_timeout
,
(
int
*
)
arg
))
return
-
EFAULT
;
if
(
new_timeout
<
1
||
new_timeout
>
3600
)
/* arbitrary upper limit */
return
-
EINVAL
;
timeout
=
new_timeout
;
wdt_keepalive
();
/* Fall through */
}
case
WDIOC_GETTIMEOUT
:
return
put_user
(
timeout
,
(
int
*
)
arg
);
default:
default:
return
-
ENO
TTY
;
return
-
ENO
IOCTLCMD
;
}
}
}
}
...
@@ -231,13 +265,13 @@ static struct file_operations wdt_fops = {
...
@@ -231,13 +265,13 @@ static struct file_operations wdt_fops = {
.
write
=
fop_write
,
.
write
=
fop_write
,
.
open
=
fop_open
,
.
open
=
fop_open
,
.
release
=
fop_close
,
.
release
=
fop_close
,
.
ioctl
=
fop_ioctl
.
ioctl
=
fop_ioctl
,
};
};
static
struct
miscdevice
wdt_miscdev
=
{
static
struct
miscdevice
wdt_miscdev
=
{
.
minor
=
WATCHDOG_MINOR
,
.
minor
=
WATCHDOG_MINOR
,
.
name
=
"watchdog"
,
.
name
=
"watchdog"
,
.
fops
=&
wdt_fops
.
fops
=&
wdt_fops
,
};
};
/*
/*
...
@@ -256,21 +290,21 @@ static int wdt_notify_sys(struct notifier_block *this, unsigned long code, void
...
@@ -256,21 +290,21 @@ static int wdt_notify_sys(struct notifier_block *this, unsigned long code, void
* reboot with no heartbeat
* reboot with no heartbeat
*/
*/
wdt_change
(
WDT_ENABLE
);
wdt_change
(
WDT_ENABLE
);
printk
(
KERN_INFO
OUR_NAME
":
Watchdog timer is now enabled with no heartbeat - should reboot in ~1 second.
\n
"
);
printk
(
KERN_INFO
PFX
"
Watchdog timer is now enabled with no heartbeat - should reboot in ~1 second.
\n
"
);
}
}
return
NOTIFY_DONE
;
return
NOTIFY_DONE
;
}
}
/*
/*
* The WDT needs to learn about soft shutdowns in order to
* The WDT needs to learn about soft shutdowns in order to
* turn the timebomb registers off.
* turn the timebomb registers off.
*/
*/
static
struct
notifier_block
wdt_notifier
=
static
struct
notifier_block
wdt_notifier
=
{
{
.
notifier_call
=
wdt_notify_sys
,
.
notifier_call
=
wdt_notify_sys
,
.
next
=
0
,
.
next
=
0
,
.
priority
=
0
.
priority
=
0
,
};
};
static
void
__exit
alim7101_wdt_unload
(
void
)
static
void
__exit
alim7101_wdt_unload
(
void
)
...
@@ -287,10 +321,10 @@ static int __init alim7101_wdt_init(void)
...
@@ -287,10 +321,10 @@ static int __init alim7101_wdt_init(void)
struct
pci_dev
*
ali1543_south
;
struct
pci_dev
*
ali1543_south
;
char
tmp
;
char
tmp
;
printk
(
KERN_INFO
OUR_NAME
":
Steve Hill <steve@navaho.co.uk>.
\n
"
);
printk
(
KERN_INFO
PFX
"
Steve Hill <steve@navaho.co.uk>.
\n
"
);
alim7101_pmu
=
pci_find_device
(
PCI_VENDOR_ID_AL
,
PCI_DEVICE_ID_AL_M7101
,
NULL
);
alim7101_pmu
=
pci_find_device
(
PCI_VENDOR_ID_AL
,
PCI_DEVICE_ID_AL_M7101
,
NULL
);
if
(
!
alim7101_pmu
)
{
if
(
!
alim7101_pmu
)
{
printk
(
KERN_INFO
OUR_NAME
":
ALi M7101 PMU not present - WDT not set
\n
"
);
printk
(
KERN_INFO
PFX
"
ALi M7101 PMU not present - WDT not set
\n
"
);
return
-
EBUSY
;
return
-
EBUSY
;
}
}
...
@@ -299,35 +333,53 @@ static int __init alim7101_wdt_init(void)
...
@@ -299,35 +333,53 @@ static int __init alim7101_wdt_init(void)
ali1543_south
=
pci_find_device
(
PCI_VENDOR_ID_AL
,
PCI_DEVICE_ID_AL_M1533
,
NULL
);
ali1543_south
=
pci_find_device
(
PCI_VENDOR_ID_AL
,
PCI_DEVICE_ID_AL_M1533
,
NULL
);
if
(
!
ali1543_south
)
{
if
(
!
ali1543_south
)
{
printk
(
KERN_INFO
OUR_NAME
":
ALi 1543 South-Bridge not present - WDT not set
\n
"
);
printk
(
KERN_INFO
PFX
"
ALi 1543 South-Bridge not present - WDT not set
\n
"
);
return
-
EBUSY
;
return
-
EBUSY
;
}
}
pci_read_config_byte
(
ali1543_south
,
0x5e
,
&
tmp
);
pci_read_config_byte
(
ali1543_south
,
0x5e
,
&
tmp
);
if
((
tmp
&
0x1e
)
!=
0x12
)
{
if
((
tmp
&
0x1e
)
!=
0x12
)
{
printk
(
KERN_INFO
OUR_NAME
":
ALi 1543 South-Bridge does not have the correct revision number (???1001?) - WDT not set
\n
"
);
printk
(
KERN_INFO
PFX
"
ALi 1543 South-Bridge does not have the correct revision number (???1001?) - WDT not set
\n
"
);
return
-
EBUSY
;
return
-
EBUSY
;
}
}
if
(
timeout
<
1
||
timeout
>
3600
)
/* arbitrary upper limit */
{
timeout
=
WATCHDOG_TIMEOUT
;
printk
(
KERN_INFO
PFX
"timeout value must be 1<=x<=3600, using %d
\n
"
,
timeout
);
}
init_timer
(
&
timer
);
init_timer
(
&
timer
);
timer
.
function
=
wdt_timer_ping
;
timer
.
function
=
wdt_timer_ping
;
timer
.
data
=
1
;
timer
.
data
=
1
;
rc
=
misc_register
(
&
wdt_miscdev
);
rc
=
misc_register
(
&
wdt_miscdev
);
if
(
rc
)
if
(
rc
)
{
return
rc
;
printk
(
KERN_ERR
PFX
"cannot register miscdev on minor=%d (err=%d)
\n
"
,
wdt_miscdev
.
minor
,
rc
);
goto
err_out
;
}
rc
=
register_reboot_notifier
(
&
wdt_notifier
);
rc
=
register_reboot_notifier
(
&
wdt_notifier
);
if
(
rc
)
{
if
(
rc
)
{
misc_deregister
(
&
wdt_miscdev
);
printk
(
KERN_ERR
PFX
"cannot register reboot notifier (err=%d)
\n
"
,
return
rc
;
rc
);
goto
err_out_miscdev
;
}
}
printk
(
KERN_INFO
OUR_NAME
": WDT driver for ALi M7101 initialised.
\n
"
);
printk
(
KERN_INFO
PFX
"WDT driver for ALi M7101 initialised. timeout=%d sec (nowayout=%d)
\n
"
,
timeout
,
nowayout
);
return
0
;
return
0
;
err_out_miscdev:
misc_deregister
(
&
wdt_miscdev
);
err_out:
return
rc
;
}
}
module_init
(
alim7101_wdt_init
);
module_init
(
alim7101_wdt_init
);
module_exit
(
alim7101_wdt_unload
);
module_exit
(
alim7101_wdt_unload
);
MODULE_AUTHOR
(
"Steve Hill"
);
MODULE_AUTHOR
(
"Steve Hill"
);
MODULE_DESCRIPTION
(
"ALi M7101 PMU Computer Watchdog Timer driver"
);
MODULE_LICENSE
(
"GPL"
);
MODULE_LICENSE
(
"GPL"
);
drivers/char/watchdog/sc520_wdt.c
View file @
47c44594
/*
/*
* AMD Elan SC520 processor Watchdog Timer driver
for Linux 2.4.x
* AMD Elan SC520 processor Watchdog Timer driver
*
*
* Based on acquirewdt.c by Alan Cox,
* Based on acquirewdt.c by Alan Cox,
* and sbc60xxwdt.c by Jakob Oestergaard <jakob@
ostenfeld.dk
>
* and sbc60xxwdt.c by Jakob Oestergaard <jakob@
unthought.net
>
*
*
* This program is free software; you can redistribute it and/or
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
* 2 of the License, or (at your option) any later version.
*
*
* The authors do NOT admit liability nor provide warranty for
* The authors do NOT admit liability nor provide warranty for
* any of this software. This material is provided "AS-IS" in
* any of this software. This material is provided "AS-IS" in
* the hope that it may be useful for others.
* the hope that it may be useful for others.
*
*
* (c) Copyright 2001 Scott Jennings <linuxdrivers@oro.net>
* (c) Copyright 2001 Scott Jennings <linuxdrivers@oro.net>
* 9/27 - 2001 [Initial release]
* 9/27 - 2001 [Initial release]
*
*
* Additional fixes Alan Cox
* Additional fixes Alan Cox
* - Fixed formatting
* - Fixed formatting
* - Removed debug printks
* - Removed debug printks
...
@@ -24,20 +24,21 @@
...
@@ -24,20 +24,21 @@
* - Used ioremap/writew/readw
* - Used ioremap/writew/readw
* - Added NOWAYOUT support
* - Added NOWAYOUT support
*
*
* Theory of operation:
* 4/12 - 2002 Changes by Rob Radez <rob@osinvestor.com>
* A Watchdog Timer (WDT) is a hardware circuit that can
* - Change comments
* reset the computer system in case of a software fault.
* - Eliminate fop_llseek
* You probably knew that already.
* - Change CONFIG_WATCHDOG_NOWAYOUT semantics
*
* - Add KERN_* tags to printks
* Usually a userspace daemon will notify the kernel WDT driver
* - fix possible wdt_is_open race
* via the /proc/watchdog special device file that userspace is
* - Report proper capabilities in watchdog_info
* still alive, at regular intervals. When such a notification
* - Add WDIOC_{GETSTATUS, GETBOOTSTATUS, SETTIMEOUT,
* occurs, the driver will usually tell the hardware watchdog
* GETTIMEOUT, SETOPTIONS} ioctls
* that everything is in order, and that the watchdog should wait
* 09/8 - 2003 Changes by Wim Van Sebroeck <wim@iguana.be>
* for yet another little while to reset the system.
* - cleanup of trailing spaces
* If userspace fails (RAM error, kernel bug, whatever), the
* - added extra printk's for startup problems
* notifications cease to occur, and the hardware watchdog will
* - use module_param
* reset the system (causing a reboot) after the timeout occurs.
* - made timeout (the emulated heartbeat) a module_param
* - made the keepalive ping an internal subroutine
*
*
* This WDT driver is different from most other Linux WDT
* This WDT driver is different from most other Linux WDT
* drivers in that the driver will ping the watchdog by itself,
* drivers in that the driver will ping the watchdog by itself,
...
@@ -77,7 +78,10 @@
...
@@ -77,7 +78,10 @@
* char to /dev/watchdog every 30 seconds.
* char to /dev/watchdog every 30 seconds.
*/
*/
#define WDT_HEARTBEAT (HZ * 30)
#define WATCHDOG_TIMEOUT 30
/* 30 sec default timeout */
static
int
timeout
=
WATCHDOG_TIMEOUT
;
/* in seconds, will be multiplied by HZ to get seconds to wait for a ping */
module_param
(
timeout
,
int
,
0
);
MODULE_PARM_DESC
(
timeout
,
"Watchdog timeout in seconds. (1<=timeout<=3600, default="
__MODULE_STRING
(
WATCHDOG_TIMEOUT
)
")"
);
/*
/*
* AMD Elan SC520 timeout value is 492us times a power of 2 (0-7)
* AMD Elan SC520 timeout value is 492us times a power of 2 (0-7)
...
@@ -95,6 +99,7 @@
...
@@ -95,6 +99,7 @@
#define WDT_WRST_ENB 0x4000
/* [14] Watchdog Timer Reset Enable */
#define WDT_WRST_ENB 0x4000
/* [14] Watchdog Timer Reset Enable */
#define OUR_NAME "sc520_wdt"
#define OUR_NAME "sc520_wdt"
#define PFX OUR_NAME ": "
#define WRT_DOG(data) *wdtmrctl=data
#define WRT_DOG(data) *wdtmrctl=data
...
@@ -104,7 +109,8 @@ static void wdt_timer_ping(unsigned long);
...
@@ -104,7 +109,8 @@ static void wdt_timer_ping(unsigned long);
static
struct
timer_list
timer
;
static
struct
timer_list
timer
;
static
unsigned
long
next_heartbeat
;
static
unsigned
long
next_heartbeat
;
static
unsigned
long
wdt_is_open
;
static
unsigned
long
wdt_is_open
;
static
int
wdt_expect_close
;
static
char
wdt_expect_close
;
static
spinlock_t
wdt_spinlock
;
#ifdef CONFIG_WATCHDOG_NOWAYOUT
#ifdef CONFIG_WATCHDOG_NOWAYOUT
static
int
nowayout
=
1
;
static
int
nowayout
=
1
;
...
@@ -115,7 +121,6 @@ static int nowayout = 0;
...
@@ -115,7 +121,6 @@ static int nowayout = 0;
module_param
(
nowayout
,
int
,
0
);
module_param
(
nowayout
,
int
,
0
);
MODULE_PARM_DESC
(
nowayout
,
"Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"
);
MODULE_PARM_DESC
(
nowayout
,
"Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"
);
static
spinlock_t
wdt_spinlock
;
/*
/*
* Whack the dog
* Whack the dog
*/
*/
...
@@ -123,9 +128,9 @@ static spinlock_t wdt_spinlock;
...
@@ -123,9 +128,9 @@ static spinlock_t wdt_spinlock;
static
void
wdt_timer_ping
(
unsigned
long
data
)
static
void
wdt_timer_ping
(
unsigned
long
data
)
{
{
/* If we got a heartbeat pulse within the WDT_US_INTERVAL
/* If we got a heartbeat pulse within the WDT_US_INTERVAL
* we agree to ping the WDT
* we agree to ping the WDT
*/
*/
if
(
time_before
(
jiffies
,
next_heartbeat
))
if
(
time_before
(
jiffies
,
next_heartbeat
))
{
{
/* Ping the WDT */
/* Ping the WDT */
spin_lock
(
&
wdt_spinlock
);
spin_lock
(
&
wdt_spinlock
);
...
@@ -137,11 +142,11 @@ static void wdt_timer_ping(unsigned long data)
...
@@ -137,11 +142,11 @@ static void wdt_timer_ping(unsigned long data)
timer
.
expires
=
jiffies
+
WDT_INTERVAL
;
timer
.
expires
=
jiffies
+
WDT_INTERVAL
;
add_timer
(
&
timer
);
add_timer
(
&
timer
);
}
else
{
}
else
{
printk
(
OUR_NAME
":
Heartbeat lost! Will not ping the watchdog
\n
"
);
printk
(
KERN_WARNING
PFX
"
Heartbeat lost! Will not ping the watchdog
\n
"
);
}
}
}
}
/*
/*
* Utility routines
* Utility routines
*/
*/
...
@@ -165,14 +170,14 @@ static void wdt_config(int writeval)
...
@@ -165,14 +170,14 @@ static void wdt_config(int writeval)
static
void
wdt_startup
(
void
)
static
void
wdt_startup
(
void
)
{
{
next_heartbeat
=
jiffies
+
WDT_HEARTBEAT
;
next_heartbeat
=
jiffies
+
(
timeout
*
HZ
)
;
/* Start the timer */
/* Start the timer */
timer
.
expires
=
jiffies
+
WDT_INTERVAL
;
timer
.
expires
=
jiffies
+
WDT_INTERVAL
;
add_timer
(
&
timer
);
add_timer
(
&
timer
);
wdt_config
(
WDT_ENB
|
WDT_WRST_ENB
|
TIMEOUT_EXPONENT
);
wdt_config
(
WDT_ENB
|
WDT_WRST_ENB
|
TIMEOUT_EXPONENT
);
printk
(
OUR_NAME
": Watchdog timer is now enabled.
\n
"
);
printk
(
KERN_INFO
PFX
"Watchdog timer is now enabled.
\n
"
);
}
}
static
void
wdt_turnoff
(
void
)
static
void
wdt_turnoff
(
void
)
...
@@ -181,10 +186,15 @@ static void wdt_turnoff(void)
...
@@ -181,10 +186,15 @@ static void wdt_turnoff(void)
/* Stop the timer */
/* Stop the timer */
del_timer
(
&
timer
);
del_timer
(
&
timer
);
wdt_config
(
0
);
wdt_config
(
0
);
printk
(
OUR_NAME
":
Watchdog timer is now disabled...
\n
"
);
printk
(
KERN_INFO
PFX
"
Watchdog timer is now disabled...
\n
"
);
}
}
}
}
static
void
wdt_keepalive
(
void
)
{
/* user land ping */
next_heartbeat
=
jiffies
+
(
timeout
*
HZ
);
}
/*
/*
* /dev/watchdog handling
* /dev/watchdog handling
...
@@ -196,62 +206,56 @@ static ssize_t fop_write(struct file * file, const char * buf, size_t count, lof
...
@@ -196,62 +206,56 @@ static ssize_t fop_write(struct file * file, const char * buf, size_t count, lof
if
(
ppos
!=
&
file
->
f_pos
)
if
(
ppos
!=
&
file
->
f_pos
)
return
-
ESPIPE
;
return
-
ESPIPE
;
/* See if we got the magic character */
/* See if we got the magic character
'V' and reload the timer
*/
if
(
count
)
if
(
count
)
{
{
size_t
ofs
;
if
(
!
nowayout
)
{
/* note: just in case someone wrote the magic character
size_t
ofs
;
* five months ago... */
wdt_expect_close
=
0
;
/* note: just in case someone wrote the magic character
* five months ago... */
/* now scan */
wdt_expect_close
=
0
;
for
(
ofs
=
0
;
ofs
!=
count
;
ofs
++
)
{
char
c
;
/* now scan */
if
(
get_user
(
c
,
buf
+
ofs
))
for
(
ofs
=
0
;
ofs
!=
count
;
ofs
++
)
{
return
-
EFAULT
;
char
c
;
if
(
c
==
'V'
)
if
(
get_user
(
c
,
buf
+
ofs
))
wdt_expect_close
=
1
;
return
-
EFAULT
;
if
(
c
==
'V'
)
wdt_expect_close
=
42
;
}
}
}
/* Well, anyhow someone wrote to us, we should return that favour */
/* Well, anyhow someone wrote to us, we should return that favour */
next_heartbeat
=
jiffies
+
WDT_HEARTBEAT
;
wdt_keepalive
();
return
1
;
}
}
return
0
;
return
count
;
}
}
static
int
fop_open
(
struct
inode
*
inode
,
struct
file
*
file
)
static
int
fop_open
(
struct
inode
*
inode
,
struct
file
*
file
)
{
{
switch
(
minor
(
inode
->
i_rdev
))
/* Just in case we're already talking to someone... */
{
if
(
test_and_set_bit
(
0
,
&
wdt_is_open
))
case
WATCHDOG_MINOR
:
return
-
EBUSY
;
/* Just in case we're already talking to someone... */
if
(
nowayout
)
if
(
test_and_set_bit
(
0
,
&
wdt_is_open
))
__module_get
(
THIS_MODULE
);
return
-
EBUSY
;
/* Good, fire up the show */
/* Good, fire up the show */
wdt_startup
();
wdt_startup
();
if
(
nowayout
)
return
0
;
__module_get
(
THIS_MODULE
);
return
0
;
default:
return
-
ENODEV
;
}
}
}
static
int
fop_close
(
struct
inode
*
inode
,
struct
file
*
file
)
static
int
fop_close
(
struct
inode
*
inode
,
struct
file
*
file
)
{
{
if
(
minor
(
inode
->
i_rdev
)
==
WATCHDOG_MINOR
)
if
(
wdt_expect_close
==
42
)
{
wdt_turnoff
();
if
(
wdt_expect_close
)
else
{
wdt_turnoff
();
del_timer
(
&
timer
);
else
{
printk
(
KERN_CRIT
PFX
"device file closed unexpectedly. Will not stop the WDT!
\n
"
);
del_timer
(
&
timer
);
printk
(
OUR_NAME
": device file closed unexpectedly. Will not stop the WDT!
\n
"
);
}
}
}
clear_bit
(
0
,
&
wdt_is_open
);
clear_bit
(
0
,
&
wdt_is_open
);
wdt_expect_close
=
0
;
return
0
;
return
0
;
}
}
...
@@ -260,20 +264,58 @@ static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
...
@@ -260,20 +264,58 @@ static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
{
{
static
struct
watchdog_info
ident
=
static
struct
watchdog_info
ident
=
{
{
.
options
=
WDIOF_MAGICCLOSE
,
.
options
=
WDIOF_
KEEPALIVEPING
|
WDIOF_SETTIMEOUT
|
WDIOF_
MAGICCLOSE
,
.
firmware_version
=
1
,
.
firmware_version
=
1
,
.
identity
=
"SC520"
.
identity
=
"SC520"
,
};
};
switch
(
cmd
)
switch
(
cmd
)
{
{
default:
default:
return
-
ENOIOCTLCMD
;
return
-
ENOIOCTLCMD
;
case
WDIOC_GETSUPPORT
:
case
WDIOC_GETSUPPORT
:
return
copy_to_user
((
struct
watchdog_info
*
)
arg
,
&
ident
,
sizeof
(
ident
))
?-
EFAULT
:
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
:
case
WDIOC_KEEPALIVE
:
next_heartbeat
=
jiffies
+
WDT_HEARTBEAT
;
wdt_keepalive
()
;
return
0
;
return
0
;
case
WDIOC_SETOPTIONS
:
{
int
new_options
,
retval
=
-
EINVAL
;
if
(
get_user
(
new_options
,
(
int
*
)
arg
))
return
-
EFAULT
;
if
(
new_options
&
WDIOS_DISABLECARD
)
{
wdt_turnoff
();
retval
=
0
;
}
if
(
new_options
&
WDIOS_ENABLECARD
)
{
wdt_startup
();
retval
=
0
;
}
return
retval
;
}
case
WDIOC_SETTIMEOUT
:
{
int
new_timeout
;
if
(
get_user
(
new_timeout
,
(
int
*
)
arg
))
return
-
EFAULT
;
if
(
new_timeout
<
1
||
new_timeout
>
3600
)
/* arbitrary upper limit */
return
-
EINVAL
;
timeout
=
new_timeout
;
wdt_keepalive
();
/* Fall through */
}
case
WDIOC_GETTIMEOUT
:
return
put_user
(
timeout
,
(
int
*
)
arg
);
}
}
}
}
...
@@ -283,13 +325,13 @@ static struct file_operations wdt_fops = {
...
@@ -283,13 +325,13 @@ static struct file_operations wdt_fops = {
.
write
=
fop_write
,
.
write
=
fop_write
,
.
open
=
fop_open
,
.
open
=
fop_open
,
.
release
=
fop_close
,
.
release
=
fop_close
,
.
ioctl
=
fop_ioctl
.
ioctl
=
fop_ioctl
,
};
};
static
struct
miscdevice
wdt_miscdev
=
{
static
struct
miscdevice
wdt_miscdev
=
{
.
minor
=
WATCHDOG_MINOR
,
.
minor
=
WATCHDOG_MINOR
,
.
name
=
"watchdog"
,
.
name
=
"watchdog"
,
.
fops
=
&
wdt_fops
.
fops
=
&
wdt_fops
,
};
};
/*
/*
...
@@ -299,21 +341,21 @@ static struct miscdevice wdt_miscdev = {
...
@@ -299,21 +341,21 @@ static struct miscdevice wdt_miscdev = {
static
int
wdt_notify_sys
(
struct
notifier_block
*
this
,
unsigned
long
code
,
static
int
wdt_notify_sys
(
struct
notifier_block
*
this
,
unsigned
long
code
,
void
*
unused
)
void
*
unused
)
{
{
if
(
code
==
SYS_DOWN
||
code
==
SYS_HALT
)
if
(
code
==
SYS_DOWN
||
code
==
SYS_HALT
)
wdt_turnoff
();
wdt_turnoff
();
return
NOTIFY_DONE
;
return
NOTIFY_DONE
;
}
}
/*
/*
* The WDT needs to learn about soft shutdowns in order to
* The WDT needs to learn about soft shutdowns in order to
* turn the timebomb registers off.
* turn the timebomb registers off.
*/
*/
static
struct
notifier_block
wdt_notifier
=
static
struct
notifier_block
wdt_notifier
=
{
{
.
notifier_call
=
wdt_notify_sys
,
.
notifier_call
=
wdt_notify_sys
,
.
next
=
NULL
,
.
next
=
NULL
,
.
priority
=
0
.
priority
=
0
,
};
};
static
void
__exit
sc520_wdt_unload
(
void
)
static
void
__exit
sc520_wdt_unload
(
void
)
...
@@ -333,27 +375,42 @@ static int __init sc520_wdt_init(void)
...
@@ -333,27 +375,42 @@ static int __init sc520_wdt_init(void)
spin_lock_init
(
&
wdt_spinlock
);
spin_lock_init
(
&
wdt_spinlock
);
if
(
timeout
<
1
||
timeout
>
3600
)
/* arbitrary upper limit */
{
timeout
=
WATCHDOG_TIMEOUT
;
printk
(
KERN_INFO
PFX
"timeout value must be 1<=x<=3600, using %d
\n
"
,
timeout
);
}
init_timer
(
&
timer
);
init_timer
(
&
timer
);
timer
.
function
=
wdt_timer_ping
;
timer
.
function
=
wdt_timer_ping
;
timer
.
data
=
0
;
timer
.
data
=
0
;
rc
=
misc_register
(
&
wdt_miscdev
);
rc
=
misc_register
(
&
wdt_miscdev
);
if
(
rc
)
if
(
rc
)
{
printk
(
KERN_ERR
PFX
"cannot register miscdev on minor=%d (err=%d)
\n
"
,
wdt_miscdev
.
minor
,
rc
);
goto
err_out_region2
;
goto
err_out_region2
;
}
rc
=
register_reboot_notifier
(
&
wdt_notifier
);
rc
=
register_reboot_notifier
(
&
wdt_notifier
);
if
(
rc
)
if
(
rc
)
{
printk
(
KERN_ERR
PFX
"cannot register reboot notifier (err=%d)
\n
"
,
rc
);
goto
err_out_miscdev
;
goto
err_out_miscdev
;
}
/* get the Base Address Register */
/* get the Base Address Register */
cbar
=
inl_p
(
0xfffc
);
cbar
=
inl_p
(
0xfffc
);
printk
(
OUR_NAME
":
CBAR: 0x%08lx
\n
"
,
cbar
);
printk
(
KERN_INFO
PFX
"
CBAR: 0x%08lx
\n
"
,
cbar
);
/* check if MMCR aliasing bit is set */
/* check if MMCR aliasing bit is set */
if
(
cbar
&
0x80000000
)
{
if
(
cbar
&
0x80000000
)
{
printk
(
OUR_NAME
":
MMCR Aliasing enabled.
\n
"
);
printk
(
KERN_INFO
PFX
"
MMCR Aliasing enabled.
\n
"
);
wdtmrctl
=
(
__u16
*
)(
cbar
&
0x3fffffff
);
wdtmrctl
=
(
__u16
*
)(
cbar
&
0x3fffffff
);
}
else
{
}
else
{
printk
(
OUR_NAME
"!!! WARNING !!!
\n
"
printk
(
KERN_INFO
PFX
"!!! WARNING !!!
\n
"
"
\t
MMCR Aliasing found NOT enabled!
\n
"
"
\t
MMCR Aliasing found NOT enabled!
\n
"
"
\t
Using default value of: %p
\n
"
"
\t
Using default value of: %p
\n
"
"
\t
This has not been tested!
\n
"
"
\t
This has not been tested!
\n
"
...
@@ -366,7 +423,8 @@ static int __init sc520_wdt_init(void)
...
@@ -366,7 +423,8 @@ static int __init sc520_wdt_init(void)
wdtmrctl
=
(
__u16
*
)((
char
*
)
wdtmrctl
+
OFFS_WDTMRCTL
);
wdtmrctl
=
(
__u16
*
)((
char
*
)
wdtmrctl
+
OFFS_WDTMRCTL
);
wdtmrctl
=
ioremap
((
unsigned
long
)
wdtmrctl
,
2
);
wdtmrctl
=
ioremap
((
unsigned
long
)
wdtmrctl
,
2
);
printk
(
KERN_INFO
OUR_NAME
": WDT driver for SC520 initialised.
\n
"
);
printk
(
KERN_INFO
PFX
"WDT driver for SC520 initialised. timeout=%d sec (nowayout=%d)
\n
"
,
timeout
,
nowayout
);
return
0
;
return
0
;
...
...
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