2006-08-24 19:11:50 +04:00
/*
* NS pc87413 - wdt Watchdog Timer driver for Linux 2.6 . x . x
*
2006-10-16 20:18:09 +04:00
* This code is based on wdt . c with original copyright .
2006-08-24 19:11:50 +04:00
*
2006-10-16 20:18:09 +04:00
* ( C ) Copyright 2006 Sven Anders , < anders @ anduras . de >
* and Marcus Junker , < junker @ anduras . de >
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-10-16 20:18:09 +04:00
* Neither Sven Anders , Marcus Junker nor ANDURAS AG
2006-08-24 19:11:50 +04:00
* admit liability nor provide warranty for any of this software .
* This material is provided " AS-IS " and at no charge .
*
2006-10-16 20:18:09 +04:00
* Release 1.1
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/types.h>
# include <linux/miscdevice.h>
# include <linux/watchdog.h>
# include <linux/ioport.h>
# include <linux/delay.h>
# include <linux/notifier.h>
# include <linux/fs.h>
# include <linux/reboot.h>
# include <linux/init.h>
# include <linux/spinlock.h>
# include <linux/moduleparam.h>
2008-05-19 17:07:37 +04:00
# include <linux/io.h>
# include <linux/uaccess.h>
2006-08-24 19:11:50 +04:00
2006-10-16 20:18:09 +04:00
/* #define DEBUG 1 */
2006-08-24 19:11:50 +04:00
2008-08-07 00:19:41 +04:00
# define DEFAULT_TIMEOUT 1 /* 1 minute */
2006-10-16 20:18:09 +04:00
# define MAX_TIMEOUT 255
2006-08-24 19:11:50 +04:00
2006-10-16 20:18:09 +04:00
# define VERSION "1.1"
# define MODNAME "pc87413 WDT"
# define DPFX MODNAME " - DEBUG: "
2006-08-24 19:11:50 +04:00
2008-08-07 00:19:41 +04:00
# define WDT_INDEX_IO_PORT (io+0) /* I/O port base (index register) */
2006-10-16 20:18:09 +04:00
# define WDT_DATA_IO_PORT (WDT_INDEX_IO_PORT+1)
# define SWC_LDN 0x04
2008-08-07 00:19:41 +04:00
# define SIOCFG2 0x22 /* Serial IO register */
2011-03-31 05:57:33 +04:00
# define WDCTL 0x10 /* Watchdog-Timer-Control-Register */
2008-08-07 00:19:41 +04:00
# define WDTO 0x11 /* Watchdog timeout register */
# define WDCFG 0x12 /* Watchdog config register */
2006-08-24 19:11:50 +04:00
2010-05-01 20:46:15 +04:00
# define IO_DEFAULT 0x2E /* Address used on Portwell Boards */
static int io = IO_DEFAULT ;
2011-04-14 23:02:39 +04:00
static int swc_base_addr = - 1 ;
2006-08-24 19:11:50 +04:00
2008-08-07 00:19:41 +04:00
static int timeout = DEFAULT_TIMEOUT ; /* timeout value */
2008-05-19 17:07:37 +04:00
static unsigned long timer_enabled ; /* is the timer enabled? */
2006-08-24 19:11:50 +04:00
2008-05-19 17:07:37 +04:00
static char expect_close ; /* is the close expected? */
2006-08-24 19:11:50 +04:00
2008-05-19 17:07:37 +04:00
static DEFINE_SPINLOCK ( io_lock ) ; /* to guard us from io races */
2006-08-24 19:11:50 +04:00
2012-03-05 19:51:11 +04:00
static bool nowayout = WATCHDOG_NOWAYOUT ;
2006-08-24 19:11:50 +04:00
2006-10-16 20:18:09 +04:00
/* -- Low level function ----------------------------------------*/
2006-08-24 19:11:50 +04:00
2006-10-16 20:18:09 +04:00
/* Select pins for Watchdog output */
2006-08-24 19:11:50 +04:00
2008-05-19 17:07:37 +04:00
static inline void pc87413_select_wdt_out ( void )
2006-08-24 19:11:50 +04:00
{
2006-10-16 20:18:09 +04:00
unsigned int cr_data = 0 ;
2006-08-24 19:11:50 +04:00
2006-10-16 20:18:09 +04:00
/* Step 1: Select multiple pin,pin55,as WDT output */
2006-08-24 19:11:50 +04:00
outb_p ( SIOCFG2 , WDT_INDEX_IO_PORT ) ;
2008-05-19 17:07:37 +04:00
cr_data = inb ( WDT_DATA_IO_PORT ) ;
2006-08-24 19:11:50 +04:00
cr_data | = 0x80 ; /* Set Bit7 to 1*/
outb_p ( SIOCFG2 , WDT_INDEX_IO_PORT ) ;
outb_p ( cr_data , WDT_DATA_IO_PORT ) ;
2006-10-16 20:18:09 +04:00
# ifdef DEBUG
2012-02-16 03:06:19 +04:00
pr_info ( DPFX
2008-05-19 17:07:37 +04:00
" Select multiple pin,pin55,as WDT output: Bit7 to 1: %d \n " ,
cr_data ) ;
2006-10-16 20:18:09 +04:00
# endif
2006-08-24 19:11:50 +04:00
}
2006-10-16 20:18:09 +04:00
/* Enable SWC functions */
2006-08-24 19:11:50 +04:00
2006-10-16 20:18:09 +04:00
static inline void pc87413_enable_swc ( void )
{
2008-05-19 17:07:37 +04:00
unsigned int cr_data = 0 ;
2006-08-24 19:11:50 +04:00
/* Step 2: Enable SWC functions */
2008-08-07 00:19:41 +04:00
outb_p ( 0x07 , WDT_INDEX_IO_PORT ) ; /* Point SWC_LDN (LDN=4) */
2006-08-24 19:11:50 +04:00
outb_p ( SWC_LDN , WDT_DATA_IO_PORT ) ;
2008-08-07 00:19:41 +04:00
outb_p ( 0x30 , WDT_INDEX_IO_PORT ) ; /* Read Index 0x30 First */
2006-10-16 20:18:09 +04:00
cr_data = inb ( WDT_DATA_IO_PORT ) ;
2008-08-07 00:19:41 +04:00
cr_data | = 0x01 ; /* Set Bit0 to 1 */
2006-08-24 19:11:50 +04:00
outb_p ( 0x30 , WDT_INDEX_IO_PORT ) ;
2008-08-07 00:19:41 +04:00
outb_p ( cr_data , WDT_DATA_IO_PORT ) ; /* Index0x30_bit0P1 */
2006-08-24 19:11:50 +04:00
2006-10-16 20:18:09 +04:00
# ifdef DEBUG
2012-02-16 03:06:19 +04:00
pr_info ( DPFX " pc87413 - Enable SWC functions \n " ) ;
2006-10-16 20:18:09 +04:00
# endif
2006-08-24 19:11:50 +04:00
}
2006-10-16 20:18:09 +04:00
/* Read SWC I/O base address */
2011-04-14 23:02:39 +04:00
static void pc87413_get_swc_base_addr ( void )
2006-08-24 19:11:50 +04:00
{
2006-10-16 20:18:09 +04:00
unsigned char addr_l , addr_h = 0 ;
2006-08-24 19:11:50 +04:00
/* Step 3: Read SWC I/O Base Address */
2008-08-07 00:19:41 +04:00
outb_p ( 0x60 , WDT_INDEX_IO_PORT ) ; /* Read Index 0x60 */
2006-10-16 20:18:09 +04:00
addr_h = inb ( WDT_DATA_IO_PORT ) ;
2006-08-24 19:11:50 +04:00
2008-08-07 00:19:41 +04:00
outb_p ( 0x61 , WDT_INDEX_IO_PORT ) ; /* Read Index 0x61 */
2006-08-24 19:11:50 +04:00
2006-10-16 20:18:09 +04:00
addr_l = inb ( WDT_DATA_IO_PORT ) ;
2006-08-24 19:11:50 +04:00
swc_base_addr = ( addr_h < < 8 ) + addr_l ;
2006-10-16 20:18:09 +04:00
# ifdef DEBUG
2012-02-16 03:06:19 +04:00
pr_info ( DPFX
2008-05-19 17:07:37 +04:00
" Read SWC I/O Base Address: low %d, high %d, res %d \n " ,
addr_l , addr_h , swc_base_addr ) ;
2006-10-16 20:18:09 +04:00
# endif
2006-08-24 19:11:50 +04:00
}
2006-10-16 20:18:09 +04:00
/* Select Bank 3 of SWC */
2011-04-14 23:02:39 +04:00
static inline void pc87413_swc_bank3 ( void )
2006-08-24 19:11:50 +04:00
{
/* Step 4: Select Bank3 of SWC */
2006-10-16 20:18:09 +04:00
outb_p ( inb ( swc_base_addr + 0x0f ) | 0x03 , swc_base_addr + 0x0f ) ;
# ifdef DEBUG
2012-02-16 03:06:19 +04:00
pr_info ( DPFX " Select Bank3 of SWC \n " ) ;
2006-10-16 20:18:09 +04:00
# endif
2006-08-24 19:11:50 +04:00
}
2006-10-16 20:18:09 +04:00
/* Set watchdog timeout to x minutes */
2011-04-14 23:02:39 +04:00
static inline void pc87413_programm_wdto ( char pc87413_time )
2006-08-24 19:11:50 +04:00
{
/* Step 5: Programm WDTO, Twd. */
2006-10-16 20:18:09 +04:00
outb_p ( pc87413_time , swc_base_addr + WDTO ) ;
# ifdef DEBUG
2012-02-16 03:06:19 +04:00
pr_info ( DPFX " Set WDTO to %d minutes \n " , pc87413_time ) ;
2006-10-16 20:18:09 +04:00
# endif
2006-08-24 19:11:50 +04:00
}
2006-10-16 20:18:09 +04:00
/* Enable WDEN */
2011-04-14 23:02:39 +04:00
static inline void pc87413_enable_wden ( void )
2006-08-24 19:11:50 +04:00
{
/* Step 6: Enable WDEN */
2008-05-19 17:07:37 +04:00
outb_p ( inb ( swc_base_addr + WDCTL ) | 0x01 , swc_base_addr + WDCTL ) ;
2006-10-16 20:18:09 +04:00
# ifdef DEBUG
2012-02-16 03:06:19 +04:00
pr_info ( DPFX " Enable WDEN \n " ) ;
2006-10-16 20:18:09 +04:00
# endif
2006-08-24 19:11:50 +04:00
}
2006-10-16 20:18:09 +04:00
/* Enable SW_WD_TREN */
2011-04-14 23:02:39 +04:00
static inline void pc87413_enable_sw_wd_tren ( void )
2006-08-24 19:11:50 +04:00
{
/* Enable SW_WD_TREN */
2008-05-19 17:07:37 +04:00
outb_p ( inb ( swc_base_addr + WDCFG ) | 0x80 , swc_base_addr + WDCFG ) ;
2006-10-16 20:18:09 +04:00
# ifdef DEBUG
2012-02-16 03:06:19 +04:00
pr_info ( DPFX " Enable SW_WD_TREN \n " ) ;
2006-10-16 20:18:09 +04:00
# endif
2006-08-24 19:11:50 +04:00
}
2006-10-16 20:18:09 +04:00
/* Disable SW_WD_TREN */
2011-04-14 23:02:39 +04:00
static inline void pc87413_disable_sw_wd_tren ( void )
2006-08-24 19:11:50 +04:00
{
/* Disable SW_WD_TREN */
2008-05-19 17:07:37 +04:00
outb_p ( inb ( swc_base_addr + WDCFG ) & 0x7f , swc_base_addr + WDCFG ) ;
2006-10-16 20:18:09 +04:00
# ifdef DEBUG
2012-02-16 03:06:19 +04:00
pr_info ( DPFX " pc87413 - Disable SW_WD_TREN \n " ) ;
2006-10-16 20:18:09 +04:00
# endif
2006-08-24 19:11:50 +04:00
}
2006-10-16 20:18:09 +04:00
/* Enable SW_WD_TRG */
2006-08-24 19:11:50 +04:00
2011-04-14 23:02:39 +04:00
static inline void pc87413_enable_sw_wd_trg ( void )
2006-08-24 19:11:50 +04:00
{
/* Enable SW_WD_TRG */
2008-05-19 17:07:37 +04:00
outb_p ( inb ( swc_base_addr + WDCTL ) | 0x80 , swc_base_addr + WDCTL ) ;
2006-10-16 20:18:09 +04:00
# ifdef DEBUG
2012-02-16 03:06:19 +04:00
pr_info ( DPFX " pc87413 - Enable SW_WD_TRG \n " ) ;
2006-10-16 20:18:09 +04:00
# endif
2006-08-24 19:11:50 +04:00
}
2006-10-16 20:18:09 +04:00
/* Disable SW_WD_TRG */
2006-08-24 19:11:50 +04:00
2011-04-14 23:02:39 +04:00
static inline void pc87413_disable_sw_wd_trg ( void )
2006-10-16 20:18:09 +04:00
{
2006-08-24 19:11:50 +04:00
/* Disable SW_WD_TRG */
2008-05-19 17:07:37 +04:00
outb_p ( inb ( swc_base_addr + WDCTL ) & 0x7f , swc_base_addr + WDCTL ) ;
2006-10-16 20:18:09 +04:00
# ifdef DEBUG
2012-02-16 03:06:19 +04:00
pr_info ( DPFX " Disable SW_WD_TRG \n " ) ;
2006-10-16 20:18:09 +04:00
# endif
2006-08-24 19:11:50 +04:00
}
2006-10-16 20:18:09 +04:00
/* -- Higher level functions ------------------------------------*/
2006-08-24 19:11:50 +04:00
2006-10-16 20:18:09 +04:00
/* Enable the watchdog */
2006-08-24 19:11:50 +04:00
2006-10-16 20:18:09 +04:00
static void pc87413_enable ( void )
2006-08-24 19:11:50 +04:00
{
2006-10-16 20:18:09 +04:00
spin_lock ( & io_lock ) ;
2006-08-24 19:11:50 +04:00
2011-04-14 23:02:39 +04:00
pc87413_swc_bank3 ( ) ;
pc87413_programm_wdto ( timeout ) ;
pc87413_enable_wden ( ) ;
pc87413_enable_sw_wd_tren ( ) ;
pc87413_enable_sw_wd_trg ( ) ;
2006-10-16 20:18:09 +04:00
spin_unlock ( & io_lock ) ;
2006-08-24 19:11:50 +04:00
}
2006-10-16 20:18:09 +04:00
/* Disable the watchdog */
2006-08-24 19:11:50 +04:00
2006-10-16 20:18:09 +04:00
static void pc87413_disable ( void )
2006-08-24 19:11:50 +04:00
{
2006-10-16 20:18:09 +04:00
spin_lock ( & io_lock ) ;
2006-08-24 19:11:50 +04:00
2011-04-14 23:02:39 +04:00
pc87413_swc_bank3 ( ) ;
pc87413_disable_sw_wd_tren ( ) ;
pc87413_disable_sw_wd_trg ( ) ;
pc87413_programm_wdto ( 0 ) ;
2006-10-16 20:18:09 +04:00
spin_unlock ( & io_lock ) ;
2006-08-24 19:11:50 +04:00
}
2006-10-16 20:18:09 +04:00
/* Refresh the watchdog */
2006-08-24 19:11:50 +04:00
2006-10-16 20:18:09 +04:00
static void pc87413_refresh ( void )
2006-08-24 19:11:50 +04:00
{
2006-10-16 20:18:09 +04:00
spin_lock ( & io_lock ) ;
2006-08-24 19:11:50 +04:00
2011-04-14 23:02:39 +04:00
pc87413_swc_bank3 ( ) ;
pc87413_disable_sw_wd_tren ( ) ;
pc87413_disable_sw_wd_trg ( ) ;
pc87413_programm_wdto ( timeout ) ;
pc87413_enable_wden ( ) ;
pc87413_enable_sw_wd_tren ( ) ;
pc87413_enable_sw_wd_trg ( ) ;
2006-08-24 19:11:50 +04:00
2006-10-16 20:18:09 +04:00
spin_unlock ( & io_lock ) ;
2006-08-24 19:11:50 +04:00
}
2006-10-16 20:18:09 +04:00
/* -- File operations -------------------------------------------*/
/**
* pc87413_open :
* @ inode : inode of device
* @ file : file handle to device
*
*/
static int pc87413_open ( struct inode * inode , struct file * file )
{
/* /dev/watchdog can only be opened once */
if ( test_and_set_bit ( 0 , & timer_enabled ) )
return - EBUSY ;
if ( nowayout )
__module_get ( THIS_MODULE ) ;
2006-08-24 19:11:50 +04:00
2006-10-16 20:18:09 +04:00
/* Reload and activate timer */
pc87413_refresh ( ) ;
2006-08-24 19:11:50 +04:00
2012-02-16 03:06:19 +04:00
pr_info ( " Watchdog enabled. Timeout set to %d minute(s). \n " , timeout ) ;
2006-10-16 20:18:09 +04:00
return nonseekable_open ( inode , file ) ;
}
2006-08-24 19:11:50 +04:00
/**
2006-10-16 20:18:09 +04:00
* pc87413_release :
* @ inode : inode to board
* @ file : file handle to board
2006-08-24 19:11:50 +04:00
*
2006-10-16 20:18:09 +04:00
* The watchdog has a configurable API . There is a religious dispute
* between people who want their watchdog to be able to shut down and
* those who want to be sure if the watchdog manager dies the machine
* reboots . In the former case we disable the counters , in the latter
* case you have to open it again very soon .
2006-08-24 19:11:50 +04:00
*/
2006-10-16 20:18:09 +04:00
static int pc87413_release ( struct inode * inode , struct file * file )
2006-08-24 19:11:50 +04:00
{
2006-10-16 20:18:09 +04:00
/* Shut off the timer. */
if ( expect_close = = 42 ) {
pc87413_disable ( ) ;
2012-02-16 03:06:19 +04:00
pr_info ( " Watchdog disabled, sleeping again... \n " ) ;
2006-10-16 20:18:09 +04:00
} else {
2012-02-16 03:06:19 +04:00
pr_crit ( " Unexpected close, not stopping watchdog! \n " ) ;
2006-10-16 20:18:09 +04:00
pc87413_refresh ( ) ;
}
clear_bit ( 0 , & timer_enabled ) ;
expect_close = 0 ;
return 0 ;
2006-08-24 19:11:50 +04:00
}
2006-10-16 20:18:09 +04:00
/**
* pc87413_status :
*
* return , if the watchdog is enabled ( timeout is set . . . )
*/
static int pc87413_status ( void )
2006-08-24 19:11:50 +04:00
{
2006-10-23 20:21:52 +04:00
return 0 ; /* currently not supported */
2006-08-24 19:11:50 +04:00
}
/**
* pc87413_write :
* @ file : file handle to the watchdog
2006-10-16 20:18:09 +04:00
* @ data : data buffer to write
* @ len : length in bytes
2006-08-24 19:11:50 +04:00
* @ ppos : pointer to the position to write . No seeks allowed
*
* A write to a watchdog device is defined as a keepalive signal . Any
* write of data will do , as we we don ' t define content meaning .
*/
2006-10-16 20:18:09 +04:00
static ssize_t pc87413_write ( struct file * file , const char __user * data ,
size_t len , loff_t * ppos )
2006-08-24 19:11:50 +04:00
{
2006-10-16 20:18:09 +04:00
/* 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:07:37 +04:00
/* scan to see whether or not we got the
magic character */
2006-10-16 20:18:09 +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-10-16 20:18:09 +04:00
return - EFAULT ;
if ( c = = ' V ' )
expect_close = 42 ;
}
}
/* someone wrote to us, we should reload the timer */
pc87413_refresh ( ) ;
2006-08-24 19:11:50 +04:00
}
2006-10-16 20:18:09 +04:00
return len ;
2006-08-24 19:11:50 +04:00
}
2006-10-16 20:18:09 +04:00
/**
2006-08-24 19:11:50 +04:00
* pc87413_ioctl :
* @ file : file handle to the device
* @ cmd : watchdog command
* @ arg : argument pointer
*
* The watchdog API defines a common set of functions for all watchdogs
* according to their available features . We only actually usefully support
* querying capabilities and current status .
*/
2008-05-19 17:07:37 +04:00
static long pc87413_ioctl ( struct file * file , unsigned int cmd ,
unsigned long arg )
2006-08-24 19:11:50 +04:00
{
2006-10-16 20:18:09 +04:00
int new_timeout ;
union {
struct watchdog_info __user * ident ;
int __user * i ;
} uarg ;
2009-12-26 21:55:22 +03:00
static const struct watchdog_info ident = {
2006-10-16 20:18:09 +04:00
. options = WDIOF_KEEPALIVEPING |
2008-05-19 17:07:37 +04:00
WDIOF_SETTIMEOUT |
WDIOF_MAGICCLOSE ,
2006-10-16 20:18:09 +04:00
. firmware_version = 1 ,
2008-08-07 00:19:41 +04:00
. identity = " PC87413(HF/F) watchdog " ,
2006-08-24 19:11:50 +04:00
} ;
2006-10-16 20:18:09 +04:00
uarg . i = ( int __user * ) arg ;
2006-08-24 19:11:50 +04:00
2008-05-19 17:07:37 +04:00
switch ( cmd ) {
case WDIOC_GETSUPPORT :
return copy_to_user ( uarg . ident , & ident ,
sizeof ( ident ) ) ? - EFAULT : 0 ;
case WDIOC_GETSTATUS :
return put_user ( pc87413_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 ) {
pc87413_disable ( ) ;
retval = 0 ;
}
if ( options & WDIOS_ENABLECARD ) {
pc87413_enable ( ) ;
retval = 0 ;
}
return retval ;
}
2008-05-19 17:07:37 +04:00
case WDIOC_KEEPALIVE :
pc87413_refresh ( ) ;
2006-10-16 20:18:09 +04:00
# ifdef DEBUG
2012-02-16 03:06:19 +04:00
pr_info ( DPFX " keepalive \n " ) ;
2006-10-16 20:18:09 +04:00
# endif
2008-05-19 17:07:37 +04:00
return 0 ;
case WDIOC_SETTIMEOUT :
if ( get_user ( new_timeout , uarg . i ) )
return - EFAULT ;
/* the API states this is given in secs */
new_timeout / = 60 ;
if ( new_timeout < 0 | | new_timeout > MAX_TIMEOUT )
return - EINVAL ;
timeout = new_timeout ;
pc87413_refresh ( ) ;
/* fall through and return the new timeout... */
case WDIOC_GETTIMEOUT :
new_timeout = timeout * 60 ;
return put_user ( new_timeout , uarg . i ) ;
default :
return - ENOTTY ;
2006-08-24 19:11:50 +04:00
}
}
2006-10-16 20:18:09 +04:00
/* -- Notifier funtions -----------------------------------------*/
2006-08-24 19:11:50 +04:00
/**
* notify_sys :
* @ this : our notifier block
* @ code : the event being reported
* @ unused : unused
*
* Our notifier is called on system shutdowns . We want to turn the card
* off at reboot otherwise the machine will reboot again during memory
* test or worse yet during the following fsck . This would suck , in fact
* trust me - if it happens it does suck .
*/
2006-10-16 20:18:09 +04:00
static int pc87413_notify_sys ( struct notifier_block * this ,
unsigned long code ,
void * unused )
2006-08-24 19:11:50 +04:00
{
2006-10-16 20:18:09 +04:00
if ( code = = SYS_DOWN | | code = = SYS_HALT )
2006-08-24 19:11:50 +04:00
/* Turn the card off */
pc87413_disable ( ) ;
return NOTIFY_DONE ;
}
2006-10-16 20:18:09 +04:00
/* -- Module's structures ---------------------------------------*/
2006-08-24 19:11:50 +04:00
2007-02-12 11:55:32 +03:00
static const struct file_operations pc87413_fops = {
2006-08-24 19:11:50 +04:00
. owner = THIS_MODULE ,
2006-10-16 20:18:09 +04:00
. llseek = no_llseek ,
2006-08-24 19:11:50 +04:00
. write = pc87413_write ,
2008-05-19 17:07:37 +04:00
. unlocked_ioctl = pc87413_ioctl ,
2006-08-24 19:11:50 +04:00
. open = pc87413_open ,
. release = pc87413_release ,
} ;
2008-05-19 17:07:37 +04:00
static struct notifier_block pc87413_notifier = {
2006-10-16 20:18:09 +04:00
. notifier_call = pc87413_notify_sys ,
2006-08-24 19:11:50 +04:00
} ;
2008-05-19 17:07:37 +04:00
static struct miscdevice pc87413_miscdev = {
2006-10-16 20:18:09 +04:00
. minor = WATCHDOG_MINOR ,
. name = " watchdog " ,
2008-08-07 00:19:41 +04:00
. fops = & pc87413_fops ,
2006-08-24 19:11:50 +04:00
} ;
2006-10-16 20:18:09 +04:00
/* -- Module init functions -------------------------------------*/
2006-08-24 19:11:50 +04:00
/**
2011-02-23 23:04:38 +03:00
* pc87413_init : module ' s " constructor "
2006-08-24 19:11:50 +04:00
*
* Set up the WDT watchdog board . All we have to do is grab the
* resources we require and bitch if anyone beat us to them .
* The open ( ) function will actually kick the board off .
*/
2006-10-16 20:18:09 +04:00
static int __init pc87413_init ( void )
2006-08-24 19:11:50 +04:00
{
int ret ;
2012-02-16 03:06:19 +04:00
pr_info ( " Version " VERSION " at io 0x%X \n " ,
2008-05-19 17:07:37 +04:00
WDT_INDEX_IO_PORT ) ;
2006-08-24 19:11:50 +04:00
2011-04-14 23:02:39 +04:00
if ( ! request_muxed_region ( io , 2 , MODNAME ) )
return - EBUSY ;
2006-08-24 19:11:50 +04:00
ret = register_reboot_notifier ( & pc87413_notifier ) ;
if ( ret ! = 0 ) {
2012-02-16 03:06:19 +04:00
pr_err ( " cannot register reboot notifier (err=%d) \n " , ret ) ;
2006-08-24 19:11:50 +04:00
}
ret = misc_register ( & pc87413_miscdev ) ;
if ( ret ! = 0 ) {
2012-02-16 03:06:19 +04:00
pr_err ( " cannot register miscdev on minor=%d (err=%d) \n " ,
WATCHDOG_MINOR , ret ) ;
2011-04-14 23:02:39 +04:00
goto reboot_unreg ;
2006-08-24 19:11:50 +04:00
}
2012-02-16 03:06:19 +04:00
pr_info ( " initialized. timeout=%d min \n " , timeout ) ;
2011-04-14 23:02:39 +04:00
pc87413_select_wdt_out ( ) ;
pc87413_enable_swc ( ) ;
pc87413_get_swc_base_addr ( ) ;
if ( ! request_region ( swc_base_addr , 0x20 , MODNAME ) ) {
2012-02-16 03:06:19 +04:00
pr_err ( " cannot request SWC region at 0x%x \n " , swc_base_addr ) ;
2011-04-14 23:02:39 +04:00
ret = - EBUSY ;
goto misc_unreg ;
}
2006-10-16 20:18:09 +04:00
pc87413_enable ( ) ;
2011-04-14 23:02:39 +04:00
release_region ( io , 2 ) ;
2006-08-24 19:11:50 +04:00
return 0 ;
2011-04-14 23:02:39 +04:00
misc_unreg :
misc_deregister ( & pc87413_miscdev ) ;
reboot_unreg :
unregister_reboot_notifier ( & pc87413_notifier ) ;
release_region ( io , 2 ) ;
return ret ;
2006-08-24 19:11:50 +04:00
}
2006-10-16 20:18:09 +04:00
/**
* pc87413_exit : module ' s " destructor "
*
* Unload the watchdog . You cannot do this with any file handles open .
* 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 .
*/
static void __exit pc87413_exit ( void )
{
/* Stop the timer before we leave */
2008-05-19 17:07:37 +04:00
if ( ! nowayout ) {
2006-10-16 20:18:09 +04:00
pc87413_disable ( ) ;
2012-02-16 03:06:19 +04:00
pr_info ( " Watchdog disabled \n " ) ;
2006-10-16 20:18:09 +04:00
}
misc_deregister ( & pc87413_miscdev ) ;
unregister_reboot_notifier ( & pc87413_notifier ) ;
2011-04-14 23:02:39 +04:00
release_region ( swc_base_addr , 0x20 ) ;
2006-10-16 20:18:09 +04:00
2012-02-16 03:06:19 +04:00
pr_info ( " watchdog component driver removed \n " ) ;
2006-10-16 20:18:09 +04:00
}
2006-08-24 19:11:50 +04:00
module_init ( pc87413_init ) ;
module_exit ( pc87413_exit ) ;
2009-03-18 11:35:09 +03:00
MODULE_AUTHOR ( " Sven Anders <anders@anduras.de>, "
" Marcus Junker <junker@anduras.de>, " ) ;
2006-08-24 19:11:50 +04:00
MODULE_DESCRIPTION ( " PC87413 WDT driver " ) ;
2006-10-16 20:18:09 +04:00
MODULE_LICENSE ( " GPL " ) ;
2006-08-24 19:11:50 +04:00
MODULE_ALIAS_MISCDEV ( WATCHDOG_MINOR ) ;
2006-10-16 20:18:09 +04:00
module_param ( io , int , 0 ) ;
2010-05-01 20:46:15 +04:00
MODULE_PARM_DESC ( io , MODNAME " I/O port (default: "
__MODULE_STRING ( IO_DEFAULT ) " ). " ) ;
2006-10-16 20:18:09 +04:00
module_param ( timeout , int , 0 ) ;
2008-05-19 17:07:37 +04:00
MODULE_PARM_DESC ( timeout ,
" Watchdog timeout in minutes (default= "
2010-05-01 20:46:15 +04:00
__MODULE_STRING ( DEFAULT_TIMEOUT ) " ). " ) ;
2006-10-16 20:18:09 +04:00
2012-03-05 19:51:11 +04:00
module_param ( nowayout , bool , 0 ) ;
2008-05-19 17:07:37 +04:00
MODULE_PARM_DESC ( nowayout ,
" Watchdog cannot be stopped once started (default= "
__MODULE_STRING ( WATCHDOG_NOWAYOUT ) " ) " ) ;
2006-10-16 20:18:09 +04:00