2006-08-24 19:11:50 +04:00
/*
* SMsC 37 B787 Watchdog Timer driver for Linux 2.6 . x . x
*
2008-10-27 18:17:56 +03:00
* Based on acquirewdt . c by Alan Cox < alan @ lxorguk . ukuu . org . uk >
2009-03-18 11:35:09 +03:00
* and some other existing drivers
2006-08-24 19:11:50 +04:00
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
2006-09-02 21:32:26 +04:00
*
2006-08-24 19:11:50 +04:00
* The authors do NOT admit liability nor provide warranty for
* any of this software . This material is provided " AS-IS " in
2009-03-18 11:35:09 +03:00
* the hope that it may be useful for others .
2006-08-24 19:11:50 +04:00
*
* ( C ) Copyright 2003 - 2006 Sven Anders < anders @ anduras . de >
*
* History :
* 2003 - Created version 1.0 for Linux 2.4 . x .
* 2006 - Ported to Linux 2.6 , added nowayout and MAGICCLOSE
2008-05-19 17:09:00 +04:00
* features . Released version 1.1
2006-08-24 19:11:50 +04:00
*
* Theory of operation :
*
2009-03-18 11:35:09 +03:00
* 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 .
2006-08-24 19:11:50 +04:00
*
2009-03-18 11:35:09 +03:00
* Usually a userspace daemon will notify the kernel WDT driver
* via the / dev / 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 .
2006-08-24 19:11:50 +04:00
*
* Create device with :
* mknod / dev / watchdog c 10 130
*
* For an example userspace keep - alive daemon , see :
2011-08-15 04:02:26 +04:00
* Documentation / watchdog / wdt . txt
2006-08-24 19:11:50 +04:00
*/
2012-02-16 03:06:19 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2006-08-24 19:11:50 +04:00
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/types.h>
# include <linux/miscdevice.h>
# include <linux/watchdog.h>
# include <linux/delay.h>
# include <linux/fs.h>
# include <linux/ioport.h>
# include <linux/notifier.h>
# include <linux/reboot.h>
# include <linux/init.h>
2006-09-02 22:53:19 +04:00
# include <linux/spinlock.h>
2008-05-19 17:09:00 +04:00
# include <linux/io.h>
# include <linux/uaccess.h>
2006-08-24 19:11:50 +04:00
/* enable support for minutes as units? */
/* (does not always work correctly, so disabled by default!) */
# define SMSC_SUPPORT_MINUTES
# undef SMSC_SUPPORT_MINUTES
# define MAX_TIMEOUT 255
# define UNIT_SECOND 0
# define UNIT_MINUTE 1
2008-05-19 17:09:00 +04:00
# define VERSION "1.1"
2006-08-24 19:11:50 +04:00
2008-05-19 17:09:00 +04:00
# define IOPORT 0x3F0
2006-08-24 19:11:50 +04:00
# define IOPORT_SIZE 2
2008-05-19 17:09:00 +04:00
# define IODEV_NO 8
2006-08-24 19:11:50 +04:00
2008-05-19 17:09:00 +04:00
static int unit = UNIT_SECOND ; /* timer's unit */
static int timeout = 60 ; /* timeout value: default is 60 "units" */
static unsigned long timer_enabled ; /* is the timer enabled? */
2006-08-24 19:11:50 +04:00
static char expect_close ; /* is the close expected? */
2007-11-02 02:27:08 +03:00
static DEFINE_SPINLOCK ( io_lock ) ; /* to guard the watchdog from io races */
2006-09-02 22:53:19 +04:00
2012-03-05 19:51:11 +04:00
static bool nowayout = WATCHDOG_NOWAYOUT ;
2006-08-24 19:11:50 +04:00
/* -- Low level function ----------------------------------------*/
/* unlock the IO chip */
static inline void open_io_config ( void )
{
2008-05-19 17:09:00 +04:00
outb ( 0x55 , IOPORT ) ;
2006-08-24 19:11:50 +04:00
mdelay ( 1 ) ;
2008-05-19 17:09:00 +04:00
outb ( 0x55 , IOPORT ) ;
2006-08-24 19:11:50 +04:00
}
/* lock the IO chip */
static inline void close_io_config ( void )
{
2008-05-19 17:09:00 +04:00
outb ( 0xAA , IOPORT ) ;
2006-08-24 19:11:50 +04:00
}
/* select the IO device */
static inline void select_io_device ( unsigned char devno )
{
2008-05-19 17:09:00 +04:00
outb ( 0x07 , IOPORT ) ;
outb ( devno , IOPORT + 1 ) ;
2006-08-24 19:11:50 +04:00
}
/* write to the control register */
static inline void write_io_cr ( unsigned char reg , unsigned char data )
{
2008-05-19 17:09:00 +04:00
outb ( reg , IOPORT ) ;
outb ( data , IOPORT + 1 ) ;
2006-08-24 19:11:50 +04:00
}
/* read from the control register */
static inline char read_io_cr ( unsigned char reg )
{
2008-05-19 17:09:00 +04:00
outb ( reg , IOPORT ) ;
return inb ( IOPORT + 1 ) ;
2006-08-24 19:11:50 +04:00
}
/* -- Medium level functions ------------------------------------*/
static inline void gpio_bit12 ( unsigned char reg )
{
2008-05-19 17:09:00 +04:00
/* -- General Purpose I/O Bit 1.2 --
* Bit 0 , In / Out : 0 = Output , 1 = Input
* Bit 1 , Polarity : 0 = No Invert , 1 = Invert
* Bit 2 , Group Enable Intr . : 0 = Disable , 1 = Enable
* Bit 3 / 4 , Function select : 00 = GPI / O , 01 = WDT , 10 = P17 ,
* 11 = Either Edge Triggered Intr . 2
* Bit 5 / 6 ( Reserved )
* Bit 7 , Output Type : 0 = Push Pull Bit , 1 = Open Drain
*/
write_io_cr ( 0xE2 , reg ) ;
2006-08-24 19:11:50 +04:00
}
static inline void gpio_bit13 ( unsigned char reg )
{
2008-05-19 17:09:00 +04:00
/* -- General Purpose I/O Bit 1.3 --
* Bit 0 , In / Out : 0 = Output , 1 = Input
* Bit 1 , Polarity : 0 = No Invert , 1 = Invert
* Bit 2 , Group Enable Intr . : 0 = Disable , 1 = Enable
* Bit 3 , Function select : 0 = GPI / O , 1 = LED
* Bit 4 - 6 ( Reserved )
* Bit 7 , Output Type : 0 = Push Pull Bit , 1 = Open Drain
*/
write_io_cr ( 0xE3 , reg ) ;
2006-08-24 19:11:50 +04:00
}
static inline void wdt_timer_units ( unsigned char new_units )
{
2008-05-19 17:09:00 +04:00
/* -- Watchdog timer units --
* Bit 0 - 6 ( Reserved )
* Bit 7 , WDT Time - out Value Units Select
* ( 0 = Minutes , 1 = Seconds )
*/
write_io_cr ( 0xF1 , new_units ) ;
2006-08-24 19:11:50 +04:00
}
static inline void wdt_timeout_value ( unsigned char new_timeout )
{
2008-05-19 17:09:00 +04:00
/* -- Watchdog Timer Time-out Value --
* Bit 0 - 7 Binary coded units ( 0 = Disabled , 1. .255 )
*/
write_io_cr ( 0xF2 , new_timeout ) ;
2006-08-24 19:11:50 +04:00
}
static inline void wdt_timer_conf ( unsigned char conf )
{
2008-05-19 17:09:00 +04:00
/* -- Watchdog timer configuration --
* Bit 0 Joystick enable : 0 * = No Reset , 1 = Reset WDT upon
* Gameport I / O
* Bit 1 Keyboard enable : 0 * = No Reset , 1 = Reset WDT upon KBD Intr .
* Bit 2 Mouse enable : 0 * = No Reset , 1 = Reset WDT upon Mouse Intr
* Bit 3 Reset the timer
* ( Wrong in SMsC documentation ? Given as : PowerLED Timout
* Enabled )
* Bit 4 - 7 WDT Interrupt Mapping : ( 0000 * = Disabled ,
* 0001 = IRQ1 , 0010 = ( Invalid ) , 0011 = IRQ3 to 1111 = IRQ15 )
*/
write_io_cr ( 0xF3 , conf ) ;
2006-08-24 19:11:50 +04:00
}
static inline void wdt_timer_ctrl ( unsigned char reg )
{
2008-05-19 17:09:00 +04:00
/* -- Watchdog timer control --
2011-03-31 05:57:33 +04:00
* Bit 0 Status Bit : 0 = Timer counting , 1 = Timeout occurred
2008-05-19 17:09:00 +04:00
* Bit 1 Power LED Toggle : 0 = Disable Toggle , 1 = Toggle at 1 Hz
* Bit 2 Force Timeout : 1 = Forces WD timeout event ( self - cleaning )
* Bit 3 P20 Force Timeout enabled :
* 0 = P20 activity does not generate the WD timeout event
* 1 = P20 Allows rising edge of P20 , from the keyboard
* controller , to force the WD timeout event .
* Bit 4 ( Reserved )
* - - Soft power management - -
* Bit 5 Stop Counter : 1 = Stop software power down counter
* set via register 0xB8 , ( self - cleaning )
* ( Upon read : 0 = Counter running , 1 = Counter stopped )
* Bit 6 Restart Counter : 1 = Restart software power down counter
* set via register 0xB8 , ( self - cleaning )
* Bit 7 SPOFF : 1 = Force software power down ( self - cleaning )
*/
write_io_cr ( 0xF4 , reg ) ;
2006-08-24 19:11:50 +04:00
}
/* -- Higher level functions ------------------------------------*/
/* initialize watchdog */
static void wb_smsc_wdt_initialize ( void )
{
2008-05-19 17:09:00 +04:00
unsigned char old ;
2006-08-24 19:11:50 +04:00
2006-09-02 22:53:19 +04:00
spin_lock ( & io_lock ) ;
2008-05-19 17:09:00 +04:00
open_io_config ( ) ;
select_io_device ( IODEV_NO ) ;
2006-08-24 19:11:50 +04:00
2008-05-19 17:09:00 +04:00
/* enable the watchdog */
gpio_bit13 ( 0x08 ) ; /* Select pin 80 = LED not GPIO */
gpio_bit12 ( 0x0A ) ; /* Set pin 79 = WDT not
GPIO / Output / Polarity = Invert */
/* disable the timeout */
wdt_timeout_value ( 0 ) ;
2006-08-24 19:11:50 +04:00
2008-05-19 17:09:00 +04:00
/* reset control register */
wdt_timer_ctrl ( 0x00 ) ;
2006-08-24 19:11:50 +04:00
2008-05-19 17:09:00 +04:00
/* reset configuration register */
2006-08-24 19:11:50 +04:00
wdt_timer_conf ( 0x00 ) ;
2008-05-19 17:09:00 +04:00
/* read old (timer units) register */
old = read_io_cr ( 0xF1 ) & 0x7F ;
if ( unit = = UNIT_SECOND )
old | = 0x80 ; /* set to seconds */
2006-08-24 19:11:50 +04:00
2008-05-19 17:09:00 +04:00
/* set the watchdog timer units */
wdt_timer_units ( old ) ;
2006-08-24 19:11:50 +04:00
2008-05-19 17:09:00 +04:00
close_io_config ( ) ;
2006-09-02 22:53:19 +04:00
spin_unlock ( & io_lock ) ;
2006-08-24 19:11:50 +04:00
}
/* shutdown the watchdog */
static void wb_smsc_wdt_shutdown ( void )
{
2006-09-02 22:53:19 +04:00
spin_lock ( & io_lock ) ;
2008-05-19 17:09:00 +04:00
open_io_config ( ) ;
select_io_device ( IODEV_NO ) ;
2006-08-24 19:11:50 +04:00
2008-05-19 17:09:00 +04:00
/* disable the watchdog */
gpio_bit13 ( 0x09 ) ;
gpio_bit12 ( 0x09 ) ;
2006-08-24 19:11:50 +04:00
2008-05-19 17:09:00 +04:00
/* reset watchdog config register */
2006-08-24 19:11:50 +04:00
wdt_timer_conf ( 0x00 ) ;
2008-05-19 17:09:00 +04:00
/* reset watchdog control register */
wdt_timer_ctrl ( 0x00 ) ;
2006-08-24 19:11:50 +04:00
2008-05-19 17:09:00 +04:00
/* disable timeout */
wdt_timeout_value ( 0x00 ) ;
2006-08-24 19:11:50 +04:00
2008-05-19 17:09:00 +04:00
close_io_config ( ) ;
2006-09-02 22:53:19 +04:00
spin_unlock ( & io_lock ) ;
2006-08-24 19:11:50 +04:00
}
/* set timeout => enable watchdog */
static void wb_smsc_wdt_set_timeout ( unsigned char new_timeout )
{
2006-09-02 22:53:19 +04:00
spin_lock ( & io_lock ) ;
2008-05-19 17:09:00 +04:00
open_io_config ( ) ;
select_io_device ( IODEV_NO ) ;
2006-08-24 19:11:50 +04:00
2008-05-19 17:09:00 +04:00
/* set Power LED to blink, if we enable the timeout */
wdt_timer_ctrl ( ( new_timeout = = 0 ) ? 0x00 : 0x02 ) ;
2006-08-24 19:11:50 +04:00
2008-05-19 17:09:00 +04:00
/* set timeout value */
wdt_timeout_value ( new_timeout ) ;
2006-08-24 19:11:50 +04:00
2008-05-19 17:09:00 +04:00
close_io_config ( ) ;
2006-09-02 22:53:19 +04:00
spin_unlock ( & io_lock ) ;
2006-08-24 19:11:50 +04:00
}
/* get timeout */
static unsigned char wb_smsc_wdt_get_timeout ( void )
{
2008-05-19 17:09:00 +04:00
unsigned char set_timeout ;
2006-08-24 19:11:50 +04:00
2006-09-02 22:53:19 +04:00
spin_lock ( & io_lock ) ;
2008-05-19 17:09:00 +04:00
open_io_config ( ) ;
select_io_device ( IODEV_NO ) ;
set_timeout = read_io_cr ( 0xF2 ) ;
close_io_config ( ) ;
2006-09-02 22:53:19 +04:00
spin_unlock ( & io_lock ) ;
2006-08-24 19:11:50 +04:00
2008-05-19 17:09:00 +04:00
return set_timeout ;
2006-08-24 19:11:50 +04:00
}
/* disable watchdog */
static void wb_smsc_wdt_disable ( void )
{
2008-05-19 17:09:00 +04:00
/* set the timeout to 0 to disable the watchdog */
wb_smsc_wdt_set_timeout ( 0 ) ;
2006-08-24 19:11:50 +04:00
}
/* enable watchdog by setting the current timeout */
static void wb_smsc_wdt_enable ( void )
{
2008-05-19 17:09:00 +04:00
/* set the current timeout... */
wb_smsc_wdt_set_timeout ( timeout ) ;
2006-08-24 19:11:50 +04:00
}
/* reset the timer */
static void wb_smsc_wdt_reset_timer ( void )
{
2006-09-02 22:53:19 +04:00
spin_lock ( & io_lock ) ;
2008-05-19 17:09:00 +04:00
open_io_config ( ) ;
select_io_device ( IODEV_NO ) ;
2006-08-24 19:11:50 +04:00
2008-05-19 17:09:00 +04:00
/* reset the timer */
2006-08-24 19:11:50 +04:00
wdt_timeout_value ( timeout ) ;
wdt_timer_conf ( 0x08 ) ;
2008-05-19 17:09:00 +04:00
close_io_config ( ) ;
2006-09-02 22:53:19 +04:00
spin_unlock ( & io_lock ) ;
2006-08-24 19:11:50 +04:00
}
/* return, if the watchdog is enabled (timeout is set...) */
static int wb_smsc_wdt_status ( void )
{
return ( wb_smsc_wdt_get_timeout ( ) = = 0 ) ? 0 : WDIOF_KEEPALIVEPING ;
}
/* -- File operations -------------------------------------------*/
/* open => enable watchdog and set initial timeout */
static int wb_smsc_wdt_open ( struct inode * inode , struct file * file )
{
/* /dev/watchdog can only be opened once */
2006-09-02 22:53:19 +04:00
if ( test_and_set_bit ( 0 , & timer_enabled ) )
2006-08-24 19:11:50 +04:00
return - EBUSY ;
if ( nowayout )
__module_get ( THIS_MODULE ) ;
/* Reload and activate timer */
wb_smsc_wdt_enable ( ) ;
2012-02-16 03:06:19 +04:00
pr_info ( " Watchdog enabled. Timeout set to %d %s \n " ,
2008-05-19 17:09:00 +04:00
timeout , ( unit = = UNIT_SECOND ) ? " second(s) " : " minute(s) " ) ;
2006-08-24 19:11:50 +04:00
return nonseekable_open ( inode , file ) ;
}
/* close => shut off the timer */
static int wb_smsc_wdt_release ( struct inode * inode , struct file * file )
{
/* Shut off the timer. */
if ( expect_close = = 42 ) {
2008-05-19 17:09:00 +04:00
wb_smsc_wdt_disable ( ) ;
2012-02-16 03:06:19 +04:00
pr_info ( " Watchdog disabled, sleeping again... \n " ) ;
2006-08-24 19:11:50 +04:00
} else {
2012-02-16 03:06:19 +04:00
pr_crit ( " Unexpected close, not stopping watchdog! \n " ) ;
2006-08-24 19:11:50 +04:00
wb_smsc_wdt_reset_timer ( ) ;
}
2006-09-02 22:53:19 +04:00
clear_bit ( 0 , & timer_enabled ) ;
2006-08-24 19:11:50 +04:00
expect_close = 0 ;
return 0 ;
}
/* write => update the timer to keep the machine alive */
static ssize_t wb_smsc_wdt_write ( struct file * file , const char __user * data ,
size_t len , loff_t * ppos )
{
/* See if we got the magic character 'V' and reload the timer */
if ( len ) {
if ( ! nowayout ) {
size_t i ;
/* reset expect flag */
expect_close = 0 ;
2008-05-19 17:09:00 +04:00
/* scan to see whether or not we got the
magic character */
2006-08-24 19:11:50 +04:00
for ( i = 0 ; i ! = len ; i + + ) {
char c ;
2008-08-07 00:19:41 +04:00
if ( get_user ( c , data + i ) )
2006-08-24 19:11:50 +04:00
return - EFAULT ;
if ( c = = ' V ' )
expect_close = 42 ;
}
}
/* someone wrote to us, we should reload the timer */
wb_smsc_wdt_reset_timer ( ) ;
}
return len ;
}
/* ioctl => control interface */
2008-05-19 17:09:00 +04:00
static long wb_smsc_wdt_ioctl ( struct file * file ,
unsigned int cmd , unsigned long arg )
2006-08-24 19:11:50 +04:00
{
int new_timeout ;
union {
struct watchdog_info __user * ident ;
int __user * i ;
} uarg ;
2008-05-19 17:09:00 +04:00
static const struct watchdog_info ident = {
2011-02-23 23:04:38 +03:00
. options = WDIOF_KEEPALIVEPING |
2008-05-19 17:09:00 +04:00
WDIOF_SETTIMEOUT |
2006-08-24 19:11:50 +04:00
WDIOF_MAGICCLOSE ,
. firmware_version = 0 ,
2011-02-23 23:04:38 +03:00
. identity = " SMsC 37B787 Watchdog " ,
2006-08-24 19:11:50 +04:00
} ;
uarg . i = ( int __user * ) arg ;
switch ( cmd ) {
2008-05-19 17:09:00 +04:00
case WDIOC_GETSUPPORT :
return copy_to_user ( uarg . ident , & ident , sizeof ( ident ) )
? - EFAULT : 0 ;
case WDIOC_GETSTATUS :
return put_user ( wb_smsc_wdt_status ( ) , uarg . i ) ;
case WDIOC_GETBOOTSTATUS :
return put_user ( 0 , uarg . i ) ;
2008-07-18 15:41:17 +04:00
case WDIOC_SETOPTIONS :
{
int options , retval = - EINVAL ;
if ( get_user ( options , uarg . i ) )
return - EFAULT ;
if ( options & WDIOS_DISABLECARD ) {
wb_smsc_wdt_disable ( ) ;
retval = 0 ;
}
if ( options & WDIOS_ENABLECARD ) {
wb_smsc_wdt_enable ( ) ;
retval = 0 ;
}
return retval ;
}
2008-05-19 17:09:00 +04:00
case WDIOC_KEEPALIVE :
wb_smsc_wdt_reset_timer ( ) ;
return 0 ;
case WDIOC_SETTIMEOUT :
if ( get_user ( new_timeout , uarg . i ) )
return - EFAULT ;
/* the API states this is given in secs */
if ( unit = = UNIT_MINUTE )
new_timeout / = 60 ;
if ( new_timeout < 0 | | new_timeout > MAX_TIMEOUT )
return - EINVAL ;
timeout = new_timeout ;
wb_smsc_wdt_set_timeout ( timeout ) ;
/* fall through and return the new timeout... */
case WDIOC_GETTIMEOUT :
new_timeout = timeout ;
if ( unit = = UNIT_MINUTE )
2009-03-18 11:35:09 +03:00
new_timeout * = 60 ;
2008-05-19 17:09:00 +04:00
return put_user ( new_timeout , uarg . i ) ;
default :
return - ENOTTY ;
2006-08-24 19:11:50 +04:00
}
}
/* -- Notifier funtions -----------------------------------------*/
2008-05-19 17:09:00 +04:00
static int wb_smsc_wdt_notify_sys ( struct notifier_block * this ,
unsigned long code , void * unused )
2006-08-24 19:11:50 +04:00
{
2008-05-19 17:09:00 +04:00
if ( code = = SYS_DOWN | | code = = SYS_HALT ) {
/* set timeout to 0, to avoid possible race-condition */
timeout = 0 ;
2006-08-24 19:11:50 +04:00
wb_smsc_wdt_disable ( ) ;
}
return NOTIFY_DONE ;
}
/* -- Module's structures ---------------------------------------*/
2008-05-19 17:09:00 +04:00
static const struct file_operations wb_smsc_wdt_fops = {
. owner = THIS_MODULE ,
2006-08-24 19:11:50 +04:00
. llseek = no_llseek ,
. write = wb_smsc_wdt_write ,
2008-05-19 17:09:00 +04:00
. unlocked_ioctl = wb_smsc_wdt_ioctl ,
2006-08-24 19:11:50 +04:00
. open = wb_smsc_wdt_open ,
2006-09-02 22:53:19 +04:00
. release = wb_smsc_wdt_release ,
2006-08-24 19:11:50 +04:00
} ;
2008-05-19 17:09:00 +04:00
static struct notifier_block wb_smsc_wdt_notifier = {
2006-09-02 22:53:19 +04:00
. notifier_call = wb_smsc_wdt_notify_sys ,
2006-08-24 19:11:50 +04:00
} ;
2008-05-19 17:09:00 +04:00
static struct miscdevice wb_smsc_wdt_miscdev = {
2006-08-24 19:11:50 +04:00
. minor = WATCHDOG_MINOR ,
. name = " watchdog " ,
. fops = & wb_smsc_wdt_fops ,
} ;
/* -- Module init functions -------------------------------------*/
/* module's "constructor" */
static int __init wb_smsc_wdt_init ( void )
{
int ret ;
2012-02-16 03:06:19 +04:00
pr_info ( " SMsC 37B787 watchdog component driver "
VERSION " initialising... \n " ) ;
2006-08-24 19:11:50 +04:00
if ( ! request_region ( IOPORT , IOPORT_SIZE , " SMsC 37B787 watchdog " ) ) {
2012-02-16 03:06:19 +04:00
pr_err ( " Unable to register IO port %#x \n " , IOPORT ) ;
2006-08-24 19:11:50 +04:00
ret = - EBUSY ;
goto out_pnp ;
}
2008-05-19 17:09:00 +04:00
/* set new maximum, if it's too big */
if ( timeout > MAX_TIMEOUT )
timeout = MAX_TIMEOUT ;
2006-09-02 22:53:19 +04:00
2008-05-19 17:09:00 +04:00
/* init the watchdog timer */
wb_smsc_wdt_initialize ( ) ;
2006-09-02 22:53:19 +04:00
2006-08-24 19:11:50 +04:00
ret = register_reboot_notifier ( & wb_smsc_wdt_notifier ) ;
if ( ret ) {
2012-02-16 03:06:19 +04:00
pr_err ( " Unable to register reboot notifier err = %d \n " , ret ) ;
2006-08-24 19:11:50 +04:00
goto out_io ;
}
ret = misc_register ( & wb_smsc_wdt_miscdev ) ;
if ( ret ) {
2012-02-16 03:06:19 +04:00
pr_err ( " Unable to register miscdev on minor %d \n " ,
WATCHDOG_MINOR ) ;
2006-08-24 19:11:50 +04:00
goto out_rbt ;
}
2008-05-19 17:09:00 +04:00
/* output info */
2012-02-16 03:06:19 +04:00
pr_info ( " Timeout set to %d %s \n " ,
2008-05-19 17:09:00 +04:00
timeout , ( unit = = UNIT_SECOND ) ? " second(s) " : " minute(s) " ) ;
2012-02-16 03:06:19 +04:00
pr_info ( " Watchdog initialized and sleeping (nowayout=%d)... \n " ,
nowayout ) ;
2006-08-24 19:11:50 +04:00
out_clean :
return ret ;
out_rbt :
unregister_reboot_notifier ( & wb_smsc_wdt_notifier ) ;
out_io :
release_region ( IOPORT , IOPORT_SIZE ) ;
out_pnp :
goto out_clean ;
2006-09-02 21:32:26 +04:00
}
2006-08-24 19:11:50 +04:00
/* module's "destructor" */
static void __exit wb_smsc_wdt_exit ( void )
{
/* Stop the timer before we leave */
2008-05-19 17:09:00 +04:00
if ( ! nowayout ) {
2006-08-24 19:11:50 +04:00
wb_smsc_wdt_shutdown ( ) ;
2012-02-16 03:06:19 +04:00
pr_info ( " Watchdog disabled \n " ) ;
2006-08-24 19:11:50 +04:00
}
misc_deregister ( & wb_smsc_wdt_miscdev ) ;
unregister_reboot_notifier ( & wb_smsc_wdt_notifier ) ;
release_region ( IOPORT , IOPORT_SIZE ) ;
2012-02-16 03:06:19 +04:00
pr_info ( " SMsC 37B787 watchdog component driver removed \n " ) ;
2006-08-24 19:11:50 +04:00
}
module_init ( wb_smsc_wdt_init ) ;
module_exit ( wb_smsc_wdt_exit ) ;
MODULE_AUTHOR ( " Sven Anders <anders@anduras.de> " ) ;
2008-05-19 17:09:00 +04:00
MODULE_DESCRIPTION ( " Driver for SMsC 37B787 watchdog component (Version "
VERSION " ) " ) ;
2006-08-24 19:11:50 +04:00
MODULE_LICENSE ( " GPL " ) ;
# ifdef SMSC_SUPPORT_MINUTES
module_param ( unit , int , 0 ) ;
2008-05-19 17:09:00 +04:00
MODULE_PARM_DESC ( unit ,
" set unit to use, 0=seconds or 1=minutes, default is 0 " ) ;
2006-08-24 19:11:50 +04:00
# endif
2006-09-02 22:53:19 +04:00
module_param ( timeout , int , 0 ) ;
2006-08-24 19:11:50 +04:00
MODULE_PARM_DESC ( timeout , " range is 1-255 units, default is 60 " ) ;
2012-03-05 19:51:11 +04:00
module_param ( nowayout , bool , 0 ) ;
2008-05-19 17:09:00 +04:00
MODULE_PARM_DESC ( nowayout ,
" Watchdog cannot be stopped once started (default= "
__MODULE_STRING ( WATCHDOG_NOWAYOUT ) " ) " ) ;