2005-04-17 02:20:36 +04:00
/*
* ICP Wafer 5823 Single Board Computer WDT driver
2008-08-07 00:19:41 +04:00
* http : //www.icpamerica.com/wafer_5823.php
* May also work on other similar models
2005-04-17 02:20:36 +04:00
*
* ( c ) Copyright 2002 Justin Cormack < justin @ street - vision . com >
*
2008-08-07 00:19:41 +04:00
* Release 0.02
2005-04-17 02:20:36 +04:00
*
* Based on advantechwdt . c which is based on wdt . c .
* Original copyright messages :
*
2008-10-27 18:17:56 +03:00
* ( c ) Copyright 1996 - 1997 Alan Cox < alan @ lxorguk . ukuu . org . uk > ,
* All Rights Reserved .
2005-04-17 02:20:36 +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 .
*
* Neither Alan Cox nor CymruNet Ltd . admit liability nor provide
* warranty for any of this software . This material is provided
* " AS-IS " and at no charge .
*
* ( c ) Copyright 1995 Alan Cox < alan @ lxorguk . ukuu . org . uk >
*
*/
2012-02-16 03:06:19 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/miscdevice.h>
# include <linux/watchdog.h>
# include <linux/fs.h>
# include <linux/ioport.h>
# include <linux/notifier.h>
# include <linux/reboot.h>
# include <linux/init.h>
# include <linux/spinlock.h>
2008-05-19 17:09:40 +04:00
# include <linux/io.h>
# include <linux/uaccess.h>
2005-04-17 02:20:36 +04:00
# define WATCHDOG_NAME "Wafer 5823 WDT"
# define PFX WATCHDOG_NAME ": "
# define WD_TIMO 60 /* 60 sec default timeout */
static unsigned long wafwdt_is_open ;
static char expect_close ;
2007-11-02 02:27:08 +03:00
static DEFINE_SPINLOCK ( wafwdt_lock ) ;
2005-04-17 02:20:36 +04:00
/*
* You must set these - there is no sane way to probe for this board .
*
2008-08-07 00:19:41 +04:00
* To enable , write the timeout value in seconds ( 1 to 255 ) to I / O
* port WDT_START , then read the port to start the watchdog . To pat
* the dog , read port WDT_STOP to stop the timer , then read WDT_START
* to restart it again .
2005-04-17 02:20:36 +04:00
*/
static int wdt_stop = 0x843 ;
static int wdt_start = 0x443 ;
static int timeout = WD_TIMO ; /* in seconds */
module_param ( timeout , int , 0 ) ;
2008-05-19 17:09:40 +04:00
MODULE_PARM_DESC ( timeout ,
" Watchdog timeout in seconds. 1 <= timeout <= 255, default= "
__MODULE_STRING ( WD_TIMO ) " . " ) ;
2005-04-17 02:20:36 +04:00
2012-03-05 19:51:11 +04:00
static bool nowayout = WATCHDOG_NOWAYOUT ;
module_param ( nowayout , bool , 0 ) ;
2008-05-19 17:09:40 +04:00
MODULE_PARM_DESC ( nowayout ,
" Watchdog cannot be stopped once started (default= "
__MODULE_STRING ( WATCHDOG_NOWAYOUT ) " ) " ) ;
2005-04-17 02:20:36 +04:00
static void wafwdt_ping ( void )
{
/* pat watchdog */
spin_lock ( & wafwdt_lock ) ;
inb_p ( wdt_stop ) ;
inb_p ( wdt_start ) ;
spin_unlock ( & wafwdt_lock ) ;
}
static void wafwdt_start ( void )
{
/* start up watchdog */
outb_p ( timeout , wdt_start ) ;
inb_p ( wdt_start ) ;
}
2008-08-07 00:19:41 +04:00
static void wafwdt_stop ( void )
2005-04-17 02:20:36 +04:00
{
/* stop watchdog */
inb_p ( wdt_stop ) ;
}
2008-05-19 17:09:40 +04:00
static ssize_t wafwdt_write ( struct file * file , const char __user * buf ,
size_t count , loff_t * ppos )
2005-04-17 02:20:36 +04:00
{
/* See if we got the magic character 'V' and reload the timer */
if ( count ) {
if ( ! nowayout ) {
size_t i ;
/* In case it was set long ago */
expect_close = 0 ;
2008-05-19 17:09:40 +04:00
/* scan to see whether or not we got the magic
character */
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i ! = count ; i + + ) {
char c ;
if ( get_user ( c , buf + i ) )
return - EFAULT ;
if ( c = = ' V ' )
expect_close = 42 ;
}
}
2008-05-19 17:09:40 +04:00
/* Well, anyhow someone wrote to us, we should
return that favour */
2005-04-17 02:20:36 +04:00
wafwdt_ping ( ) ;
}
return count ;
}
2008-05-19 17:09:40 +04:00
static long wafwdt_ioctl ( struct file * file , unsigned int cmd ,
unsigned long arg )
2005-04-17 02:20:36 +04:00
{
int new_timeout ;
void __user * argp = ( void __user * ) arg ;
int __user * p = argp ;
2008-05-19 17:09:40 +04:00
static const struct watchdog_info ident = {
. options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
WDIOF_MAGICCLOSE ,
2005-04-17 02:20:36 +04:00
. firmware_version = 1 ,
. identity = " Wafer 5823 WDT " ,
} ;
switch ( cmd ) {
case WDIOC_GETSUPPORT :
2008-05-19 17:09:40 +04:00
if ( copy_to_user ( argp , & ident , sizeof ( ident ) ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
break ;
case WDIOC_GETSTATUS :
case WDIOC_GETBOOTSTATUS :
return put_user ( 0 , p ) ;
case WDIOC_SETOPTIONS :
{
int options , retval = - EINVAL ;
if ( get_user ( options , p ) )
return - EFAULT ;
if ( options & WDIOS_DISABLECARD ) {
2012-01-18 15:26:43 +04:00
wafwdt_stop ( ) ;
2005-04-17 02:20:36 +04:00
retval = 0 ;
}
if ( options & WDIOS_ENABLECARD ) {
2012-01-18 15:26:43 +04:00
wafwdt_start ( ) ;
2005-04-17 02:20:36 +04:00
retval = 0 ;
}
return retval ;
}
2008-07-18 15:41:17 +04:00
case WDIOC_KEEPALIVE :
wafwdt_ping ( ) ;
break ;
case WDIOC_SETTIMEOUT :
if ( get_user ( new_timeout , p ) )
return - EFAULT ;
if ( ( new_timeout < 1 ) | | ( new_timeout > 255 ) )
return - EINVAL ;
timeout = new_timeout ;
wafwdt_stop ( ) ;
wafwdt_start ( ) ;
/* Fall */
case WDIOC_GETTIMEOUT :
return put_user ( timeout , p ) ;
2005-04-17 02:20:36 +04:00
default :
2006-09-09 19:34:31 +04:00
return - ENOTTY ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
static int wafwdt_open ( struct inode * inode , struct file * file )
{
if ( test_and_set_bit ( 0 , & wafwdt_is_open ) )
return - EBUSY ;
/*
* Activate
*/
wafwdt_start ( ) ;
return nonseekable_open ( inode , file ) ;
}
2008-08-07 00:19:41 +04:00
static int wafwdt_close ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
2008-05-19 17:09:40 +04:00
if ( expect_close = = 42 )
2005-04-17 02:20:36 +04:00
wafwdt_stop ( ) ;
2008-05-19 17:09:40 +04:00
else {
2012-02-16 03:06:19 +04:00
pr_crit ( " WDT device closed unexpectedly. WDT will not stop! \n " ) ;
2005-04-17 02:20:36 +04:00
wafwdt_ping ( ) ;
}
clear_bit ( 0 , & wafwdt_is_open ) ;
expect_close = 0 ;
return 0 ;
}
/*
* Notifier for system down
*/
2008-05-19 17:09:40 +04:00
static int wafwdt_notify_sys ( struct notifier_block * this , unsigned long code ,
void * unused )
2005-04-17 02:20:36 +04:00
{
2008-05-19 17:09:40 +04:00
if ( code = = SYS_DOWN | | code = = SYS_HALT )
2005-04-17 02:20:36 +04:00
wafwdt_stop ( ) ;
return NOTIFY_DONE ;
}
/*
* Kernel Interfaces
*/
2006-07-03 11:24:21 +04:00
static const struct file_operations wafwdt_fops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. llseek = no_llseek ,
. write = wafwdt_write ,
2008-05-19 17:09:40 +04:00
. unlocked_ioctl = wafwdt_ioctl ,
2005-04-17 02:20:36 +04:00
. open = wafwdt_open ,
. release = wafwdt_close ,
} ;
static struct miscdevice wafwdt_miscdev = {
. minor = WATCHDOG_MINOR ,
. name = " watchdog " ,
. fops = & wafwdt_fops ,
} ;
/*
* The WDT needs to learn about soft shutdowns in order to
* turn the timebomb registers off .
*/
static struct notifier_block wafwdt_notifier = {
. notifier_call = wafwdt_notify_sys ,
} ;
static int __init wafwdt_init ( void )
{
int ret ;
2012-02-16 03:06:19 +04:00
pr_info ( " WDT driver for Wafer 5823 single board computer initialising \n " ) ;
2005-04-17 02:20:36 +04:00
if ( timeout < 1 | | timeout > 255 ) {
timeout = WD_TIMO ;
2012-02-16 03:06:19 +04:00
pr_info ( " timeout value must be 1 <= x <= 255, using %d \n " ,
timeout ) ;
2005-04-17 02:20:36 +04:00
}
if ( wdt_stop ! = wdt_start ) {
2008-05-19 17:09:40 +04:00
if ( ! request_region ( wdt_stop , 1 , " Wafer 5823 WDT " ) ) {
2012-02-16 03:06:19 +04:00
pr_err ( " I/O address 0x%04x already in use \n " , wdt_stop ) ;
2005-04-17 02:20:36 +04:00
ret = - EIO ;
goto error ;
}
}
2008-05-19 17:09:40 +04:00
if ( ! request_region ( wdt_start , 1 , " Wafer 5823 WDT " ) ) {
2012-02-16 03:06:19 +04:00
pr_err ( " I/O address 0x%04x already in use \n " , wdt_start ) ;
2005-04-17 02:20:36 +04:00
ret = - EIO ;
goto error2 ;
}
ret = register_reboot_notifier ( & wafwdt_notifier ) ;
if ( ret ! = 0 ) {
2012-02-16 03:06:19 +04:00
pr_err ( " cannot register reboot notifier (err=%d) \n " , ret ) ;
2005-04-17 02:20:36 +04:00
goto error3 ;
}
ret = misc_register ( & wafwdt_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 ) ;
2005-04-17 02:20:36 +04:00
goto error4 ;
}
2012-02-16 03:06:19 +04:00
pr_info ( " initialized. timeout=%d sec (nowayout=%d) \n " ,
2005-04-17 02:20:36 +04:00
timeout , nowayout ) ;
return ret ;
error4 :
unregister_reboot_notifier ( & wafwdt_notifier ) ;
error3 :
release_region ( wdt_start , 1 ) ;
error2 :
if ( wdt_stop ! = wdt_start )
release_region ( wdt_stop , 1 ) ;
error :
return ret ;
}
static void __exit wafwdt_exit ( void )
{
misc_deregister ( & wafwdt_miscdev ) ;
unregister_reboot_notifier ( & wafwdt_notifier ) ;
2008-05-19 17:09:40 +04:00
if ( wdt_stop ! = wdt_start )
2005-04-17 02:20:36 +04:00
release_region ( wdt_stop , 1 ) ;
release_region ( wdt_start , 1 ) ;
}
module_init ( wafwdt_init ) ;
module_exit ( wafwdt_exit ) ;
MODULE_AUTHOR ( " Justin Cormack " ) ;
MODULE_DESCRIPTION ( " ICP Wafer 5823 Single Board Computer WDT driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
/* end of wafer5823wdt.c */