2005-04-17 02:20:36 +04:00
/*
* Advantech Single Board Computer WDT driver
*
* ( c ) Copyright 2000 - 2001 Marek Michalkiewicz < marekm @ linux . org . pl >
*
* Based on acquirewdt . c which is based on wdt . c .
* Original copyright messages :
*
2008-10-27 18:17:56 +03:00
* ( c ) Copyright 1996 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 .
*
2008-10-27 18:17:56 +03:00
* ( c ) Copyright 1995 Alan Cox < alan @ lxorguk . ukuu . org . uk >
2005-04-17 02:20:36 +04:00
*
* 14 - Dec - 2001 Matt Domsch < Matt_Domsch @ dell . com >
* Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
*
* 16 - Oct - 2002 Rob Radez < rob @ osinvestor . com >
* Clean up ioctls , clean up init + exit , add expect close support ,
* add wdt_start and wdt_stop as parameters .
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/types.h>
# include <linux/miscdevice.h>
# include <linux/watchdog.h>
# include <linux/fs.h>
# include <linux/ioport.h>
2007-01-12 00:35:40 +03:00
# include <linux/platform_device.h>
2005-04-17 02:20:36 +04:00
# include <linux/init.h>
2008-07-15 15:46:11 +04:00
# include <linux/io.h>
# include <linux/uaccess.h>
2005-04-17 02:20:36 +04:00
# include <asm/system.h>
2007-01-12 00:19:28 +03:00
# define DRV_NAME "advantechwdt"
# define PFX DRV_NAME ": "
2005-04-17 02:20:36 +04:00
# define WATCHDOG_NAME "Advantech WDT"
# define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
2008-08-07 00:19:41 +04:00
/* the watchdog platform device */
static struct platform_device * advwdt_platform_device ;
2005-04-17 02:20:36 +04:00
static unsigned long advwdt_is_open ;
static char adv_expect_close ;
/*
* You must set these - there is no sane way to probe for this board .
*
* To enable or restart , write the timeout value in seconds ( 1 to 63 )
* to I / O port wdt_start . To disable , read I / O port wdt_stop .
* Both are 0x443 for most boards ( tested on a PCA - 6276 VE - 00 B1 ) , but
* check your manual ( at least the PCA - 6159 seems to be different -
* the manual says wdt_stop is 0x43 , not 0x443 ) .
* ( 0x43 is also a write - only control register for the 8254 timer ! )
*/
static int wdt_stop = 0x443 ;
module_param ( wdt_stop , int , 0 ) ;
MODULE_PARM_DESC ( wdt_stop , " Advantech WDT 'stop' io port (default 0x443) " ) ;
static int wdt_start = 0x443 ;
module_param ( wdt_start , int , 0 ) ;
MODULE_PARM_DESC ( wdt_start , " Advantech WDT 'start' io port (default 0x443) " ) ;
static int timeout = WATCHDOG_TIMEOUT ; /* in seconds */
module_param ( timeout , int , 0 ) ;
2008-05-19 17:04:51 +04:00
MODULE_PARM_DESC ( timeout ,
" Watchdog timeout in seconds. 1<= timeout <=63, default= "
__MODULE_STRING ( WATCHDOG_TIMEOUT ) " . " ) ;
2005-04-17 02:20:36 +04:00
2005-07-27 22:43:58 +04:00
static int nowayout = WATCHDOG_NOWAYOUT ;
2005-04-17 02:20:36 +04:00
module_param ( nowayout , int , 0 ) ;
2008-05-19 17:04:51 +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
/*
2007-01-12 00:19:28 +03:00
* Watchdog Operations
2005-04-17 02:20:36 +04:00
*/
2008-05-19 17:04:51 +04:00
static void advwdt_ping ( void )
2005-04-17 02:20:36 +04:00
{
/* Write a watchdog value */
outb_p ( timeout , wdt_start ) ;
}
2008-05-19 17:04:51 +04:00
static void advwdt_disable ( void )
2005-04-17 02:20:36 +04:00
{
inb_p ( wdt_stop ) ;
}
2008-05-19 17:04:51 +04:00
static int advwdt_set_heartbeat ( int t )
2007-01-12 00:27:51 +03:00
{
2008-05-19 17:04:51 +04:00
if ( t < 1 | | t > 63 )
2007-01-12 00:27:51 +03:00
return - EINVAL ;
timeout = t ;
return 0 ;
}
2007-01-12 00:19:28 +03:00
/*
* / dev / watchdog handling
*/
2008-05-19 17:04:51 +04:00
static ssize_t advwdt_write ( struct file * file , const char __user * buf ,
size_t count , loff_t * ppos )
2005-04-17 02:20:36 +04:00
{
if ( count ) {
if ( ! nowayout ) {
size_t i ;
adv_expect_close = 0 ;
for ( i = 0 ; i ! = count ; i + + ) {
char c ;
2008-08-07 00:19:41 +04:00
if ( get_user ( c , buf + i ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
if ( c = = ' V ' )
adv_expect_close = 42 ;
}
}
advwdt_ping ( ) ;
}
return count ;
}
2008-05-19 17:04:51 +04:00
static long advwdt_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 ;
static struct watchdog_info ident = {
2009-03-18 11:35:09 +03:00
. options = WDIOF_KEEPALIVEPING |
WDIOF_SETTIMEOUT |
WDIOF_MAGICCLOSE ,
2005-04-17 02:20:36 +04:00
. firmware_version = 1 ,
2007-01-12 00:19:28 +03:00
. identity = WATCHDOG_NAME ,
2005-04-17 02:20:36 +04:00
} ;
switch ( cmd ) {
case WDIOC_GETSUPPORT :
2008-05-19 17:04:51 +04:00
if ( copy_to_user ( argp , & ident , sizeof ( ident ) ) )
return - EFAULT ;
break ;
2005-04-17 02:20:36 +04:00
case WDIOC_GETSTATUS :
case WDIOC_GETBOOTSTATUS :
2008-05-19 17:04:51 +04:00
return put_user ( 0 , p ) ;
2005-04-17 02:20:36 +04:00
case WDIOC_SETOPTIONS :
{
2008-05-19 17:04:51 +04:00
int options , retval = - EINVAL ;
2005-04-17 02:20:36 +04:00
2008-05-19 17:04:51 +04:00
if ( get_user ( options , p ) )
return - EFAULT ;
if ( options & WDIOS_DISABLECARD ) {
advwdt_disable ( ) ;
retval = 0 ;
}
if ( options & WDIOS_ENABLECARD ) {
advwdt_ping ( ) ;
retval = 0 ;
}
return retval ;
2005-04-17 02:20:36 +04:00
}
2008-07-18 15:41:17 +04:00
case WDIOC_KEEPALIVE :
advwdt_ping ( ) ;
break ;
case WDIOC_SETTIMEOUT :
if ( get_user ( new_timeout , p ) )
return - EFAULT ;
if ( advwdt_set_heartbeat ( new_timeout ) )
return - EINVAL ;
advwdt_ping ( ) ;
/* Fall */
case WDIOC_GETTIMEOUT :
return put_user ( timeout , p ) ;
2005-04-17 02:20:36 +04:00
default :
2008-05-19 17:04:51 +04:00
return - ENOTTY ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
2008-05-19 17:04:51 +04:00
static int advwdt_open ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
if ( test_and_set_bit ( 0 , & advwdt_is_open ) )
return - EBUSY ;
/*
* Activate
*/
advwdt_ping ( ) ;
return nonseekable_open ( inode , file ) ;
}
2008-08-07 00:19:41 +04:00
static int advwdt_close ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
if ( adv_expect_close = = 42 ) {
advwdt_disable ( ) ;
} else {
2008-05-19 17:04:51 +04:00
printk ( KERN_CRIT PFX
" Unexpected close, not stopping watchdog! \n " ) ;
2005-04-17 02:20:36 +04:00
advwdt_ping ( ) ;
}
clear_bit ( 0 , & advwdt_is_open ) ;
adv_expect_close = 0 ;
return 0 ;
}
/*
* Kernel Interfaces
*/
2006-07-03 11:24:21 +04:00
static const struct file_operations advwdt_fops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. llseek = no_llseek ,
. write = advwdt_write ,
2008-05-19 17:04:51 +04:00
. unlocked_ioctl = advwdt_ioctl ,
2005-04-17 02:20:36 +04:00
. open = advwdt_open ,
. release = advwdt_close ,
} ;
static struct miscdevice advwdt_miscdev = {
2007-01-12 00:19:28 +03:00
. minor = WATCHDOG_MINOR ,
. name = " watchdog " ,
. fops = & advwdt_fops ,
2005-04-17 02:20:36 +04:00
} ;
2007-01-12 00:19:28 +03:00
/*
* Init & exit routines
*/
2008-05-19 17:04:51 +04:00
static int __devinit advwdt_probe ( struct platform_device * dev )
2005-04-17 02:20:36 +04:00
{
int ret ;
if ( wdt_stop ! = wdt_start ) {
if ( ! request_region ( wdt_stop , 1 , WATCHDOG_NAME ) ) {
2008-05-19 17:04:51 +04:00
printk ( KERN_ERR PFX
" I/O address 0x%04x already in use \n " ,
wdt_stop ) ;
2005-04-17 02:20:36 +04:00
ret = - EIO ;
goto out ;
}
}
if ( ! request_region ( wdt_start , 1 , WATCHDOG_NAME ) ) {
2008-05-19 17:04:51 +04:00
printk ( KERN_ERR PFX
" I/O address 0x%04x already in use \n " ,
wdt_start ) ;
2005-04-17 02:20:36 +04:00
ret = - EIO ;
goto unreg_stop ;
}
2009-03-18 11:35:09 +03:00
/* Check that the heartbeat value is within it's range ;
* if not reset to the default */
2007-01-12 00:27:51 +03:00
if ( advwdt_set_heartbeat ( timeout ) ) {
advwdt_set_heartbeat ( WATCHDOG_TIMEOUT ) ;
2008-05-19 17:04:51 +04:00
printk ( KERN_INFO PFX
" timeout value must be 1<=x<=63, using %d \n " , timeout ) ;
2007-01-12 00:27:51 +03:00
}
2005-04-17 02:20:36 +04:00
ret = misc_register ( & advwdt_miscdev ) ;
if ( ret ! = 0 ) {
2008-05-19 17:04:51 +04:00
printk ( KERN_ERR PFX
" cannot register miscdev on minor=%d (err=%d) \n " ,
WATCHDOG_MINOR , ret ) ;
2007-01-12 00:42:41 +03:00
goto unreg_regions ;
2005-04-17 02:20:36 +04:00
}
2008-05-19 17:04:51 +04:00
printk ( KERN_INFO PFX " initialized. timeout=%d sec (nowayout=%d) \n " ,
2005-04-17 02:20:36 +04:00
timeout , nowayout ) ;
out :
return ret ;
unreg_regions :
release_region ( wdt_start , 1 ) ;
unreg_stop :
if ( wdt_stop ! = wdt_start )
release_region ( wdt_stop , 1 ) ;
goto out ;
}
2008-05-19 17:04:51 +04:00
static int __devexit advwdt_remove ( struct platform_device * dev )
2005-04-17 02:20:36 +04:00
{
misc_deregister ( & advwdt_miscdev ) ;
2008-08-07 00:19:41 +04:00
release_region ( wdt_start , 1 ) ;
if ( wdt_stop ! = wdt_start )
release_region ( wdt_stop , 1 ) ;
2007-01-12 00:35:40 +03:00
return 0 ;
}
2008-05-19 17:04:51 +04:00
static void advwdt_shutdown ( struct platform_device * dev )
2007-01-12 00:42:41 +03:00
{
/* Turn the WDT off if we have a soft shutdown */
advwdt_disable ( ) ;
}
2007-01-12 00:35:40 +03:00
static struct platform_driver advwdt_driver = {
. probe = advwdt_probe ,
. remove = __devexit_p ( advwdt_remove ) ,
2007-01-12 00:42:41 +03:00
. shutdown = advwdt_shutdown ,
2007-01-12 00:35:40 +03:00
. driver = {
. owner = THIS_MODULE ,
. name = DRV_NAME ,
} ,
} ;
2008-05-19 17:04:51 +04:00
static int __init advwdt_init ( void )
2007-01-12 00:35:40 +03:00
{
int err ;
2008-08-07 00:19:41 +04:00
printk ( KERN_INFO
" WDT driver for Advantech single board computer initialising. \n " ) ;
2007-01-12 00:35:40 +03:00
err = platform_driver_register ( & advwdt_driver ) ;
if ( err )
return err ;
2008-05-19 17:04:51 +04:00
advwdt_platform_device = platform_device_register_simple ( DRV_NAME ,
- 1 , NULL , 0 ) ;
2007-01-12 00:35:40 +03:00
if ( IS_ERR ( advwdt_platform_device ) ) {
err = PTR_ERR ( advwdt_platform_device ) ;
goto unreg_platform_driver ;
}
return 0 ;
unreg_platform_driver :
platform_driver_unregister ( & advwdt_driver ) ;
return err ;
}
2008-05-19 17:04:51 +04:00
static void __exit advwdt_exit ( void )
2007-01-12 00:35:40 +03:00
{
platform_device_unregister ( advwdt_platform_device ) ;
platform_driver_unregister ( & advwdt_driver ) ;
printk ( KERN_INFO PFX " Watchdog Module Unloaded. \n " ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( advwdt_init ) ;
module_exit ( advwdt_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Marek Michalkiewicz <marekm@linux.org.pl> " ) ;
MODULE_DESCRIPTION ( " Advantech Single Board Computer WDT driver " ) ;
MODULE_ALIAS_MISCDEV ( WATCHDOG_MINOR ) ;