2005-04-17 02:20:36 +04:00
/*
2006-10-04 01:01:26 +04:00
* drivers / char / watchdog / ixp2000_wdt . c
2005-04-17 02:20:36 +04:00
*
* Watchdog driver for Intel IXP2000 network processors
*
* Adapted from the IXP4xx watchdog driver by Lennert Buytenhek .
* The original version carries these notices :
*
* Author : Deepak Saxena < dsaxena @ plexity . net >
*
* Copyright 2004 ( c ) MontaVista , Software , Inc .
* Based on sa1100 driver , Copyright ( C ) 2000 Oleg Drokin < green @ crimea . edu >
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/fs.h>
# include <linux/miscdevice.h>
# include <linux/watchdog.h>
# include <linux/init.h>
# include <linux/bitops.h>
2008-05-19 17:06:42 +04:00
# include <linux/uaccess.h>
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
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
static unsigned int heartbeat = 60 ; /* (secs) Default is 1 minute */
static unsigned long wdt_status ;
2008-05-19 17:06:42 +04:00
static spinlock_t wdt_lock ;
2005-04-17 02:20:36 +04:00
# define WDT_IN_USE 0
# define WDT_OK_TO_CLOSE 1
static unsigned long wdt_tick_rate ;
2008-05-19 17:06:42 +04:00
static void wdt_enable ( void )
2005-04-17 02:20:36 +04:00
{
2008-05-19 17:06:42 +04:00
spin_lock ( & wdt_lock ) ;
2005-04-17 02:20:36 +04:00
ixp2000_reg_write ( IXP2000_RESET0 , * ( IXP2000_RESET0 ) | WDT_RESET_ENABLE ) ;
ixp2000_reg_write ( IXP2000_TWDE , WDT_ENABLE ) ;
ixp2000_reg_write ( IXP2000_T4_CLD , heartbeat * wdt_tick_rate ) ;
ixp2000_reg_write ( IXP2000_T4_CTL , TIMER_DIVIDER_256 | TIMER_ENABLE ) ;
2008-05-19 17:06:42 +04:00
spin_unlock ( & wdt_lock ) ;
2005-04-17 02:20:36 +04:00
}
2008-05-19 17:06:42 +04:00
static void wdt_disable ( void )
2005-04-17 02:20:36 +04:00
{
2008-05-19 17:06:42 +04:00
spin_lock ( & wdt_lock ) ;
2005-04-17 02:20:36 +04:00
ixp2000_reg_write ( IXP2000_T4_CTL , 0 ) ;
2008-05-19 17:06:42 +04:00
spin_unlock ( & wdt_lock ) ;
2005-04-17 02:20:36 +04:00
}
2008-05-19 17:06:42 +04:00
static void wdt_keepalive ( void )
2005-04-17 02:20:36 +04:00
{
2008-05-19 17:06:42 +04:00
spin_lock ( & wdt_lock ) ;
2005-04-17 02:20:36 +04:00
ixp2000_reg_write ( IXP2000_T4_CLD , heartbeat * wdt_tick_rate ) ;
2008-05-19 17:06:42 +04:00
spin_unlock ( & wdt_lock ) ;
2005-04-17 02:20:36 +04:00
}
2008-05-19 17:06:42 +04:00
static int ixp2000_wdt_open ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
if ( test_and_set_bit ( WDT_IN_USE , & wdt_status ) )
return - EBUSY ;
clear_bit ( WDT_OK_TO_CLOSE , & wdt_status ) ;
wdt_enable ( ) ;
return nonseekable_open ( inode , file ) ;
}
2008-05-19 17:06:42 +04:00
static ssize_t ixp2000_wdt_write ( struct file * file , const char * data ,
size_t len , loff_t * ppos )
2005-04-17 02:20:36 +04:00
{
if ( len ) {
if ( ! nowayout ) {
size_t i ;
clear_bit ( WDT_OK_TO_CLOSE , & wdt_status ) ;
for ( i = 0 ; i ! = len ; i + + ) {
char c ;
if ( get_user ( c , data + i ) )
return - EFAULT ;
if ( c = = ' V ' )
set_bit ( WDT_OK_TO_CLOSE , & wdt_status ) ;
}
}
wdt_keepalive ( ) ;
}
return len ;
}
static struct watchdog_info ident = {
. options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING ,
. identity = " IXP2000 Watchdog " ,
} ;
2008-05-19 17:06:42 +04:00
static long ixp2000_wdt_ioctl ( struct file * file , unsigned int cmd ,
unsigned long arg )
2005-04-17 02:20:36 +04:00
{
2006-09-09 19:34:31 +04:00
int ret = - ENOTTY ;
2005-04-17 02:20:36 +04:00
int time ;
switch ( cmd ) {
case WDIOC_GETSUPPORT :
ret = copy_to_user ( ( struct watchdog_info * ) arg , & ident ,
sizeof ( ident ) ) ? - EFAULT : 0 ;
break ;
case WDIOC_GETSTATUS :
ret = put_user ( 0 , ( int * ) arg ) ;
break ;
case WDIOC_GETBOOTSTATUS :
ret = put_user ( 0 , ( int * ) arg ) ;
break ;
2008-07-18 15:41:17 +04:00
case WDIOC_KEEPALIVE :
wdt_enable ( ) ;
ret = 0 ;
break ;
2005-04-17 02:20:36 +04:00
case WDIOC_SETTIMEOUT :
ret = get_user ( time , ( int * ) arg ) ;
if ( ret )
break ;
if ( time < = 0 | | time > 60 ) {
ret = - EINVAL ;
break ;
}
heartbeat = time ;
wdt_keepalive ( ) ;
/* Fall through */
case WDIOC_GETTIMEOUT :
ret = put_user ( heartbeat , ( int * ) arg ) ;
break ;
}
return ret ;
}
2008-05-19 17:06:42 +04:00
static int ixp2000_wdt_release ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
2008-05-19 17:06:42 +04:00
if ( test_bit ( WDT_OK_TO_CLOSE , & wdt_status ) )
2005-04-17 02:20:36 +04:00
wdt_disable ( ) ;
2008-05-19 17:06:42 +04:00
else
2005-06-29 07:44:46 +04:00
printk ( KERN_CRIT " WATCHDOG: Device closed unexpectedly - "
2005-04-17 02:20:36 +04:00
" timer will not stop \n " ) ;
clear_bit ( WDT_IN_USE , & wdt_status ) ;
clear_bit ( WDT_OK_TO_CLOSE , & wdt_status ) ;
return 0 ;
}
2008-05-19 17:06:42 +04:00
static const struct file_operations ixp2000_wdt_fops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. llseek = no_llseek ,
. write = ixp2000_wdt_write ,
2008-05-19 17:06:42 +04:00
. unlocked_ioctl = ixp2000_wdt_ioctl ,
2005-04-17 02:20:36 +04:00
. open = ixp2000_wdt_open ,
. release = ixp2000_wdt_release ,
} ;
2008-05-19 17:06:42 +04:00
static struct miscdevice ixp2000_wdt_miscdev = {
2005-04-17 02:20:36 +04:00
. minor = WATCHDOG_MINOR ,
2005-08-17 10:58:34 +04:00
. name = " watchdog " ,
2005-04-17 02:20:36 +04:00
. fops = & ixp2000_wdt_fops ,
} ;
static int __init ixp2000_wdt_init ( void )
{
2005-06-20 21:51:07 +04:00
if ( ( * IXP2000_PRODUCT_ID & 0x001ffef0 ) = = 0x00000000 ) {
printk ( KERN_INFO " Unable to use IXP2000 watchdog due to IXP2800 erratum #25. \n " ) ;
return - EIO ;
}
wdt_tick_rate = ( * IXP2000_T1_CLD * HZ ) / 256 ;
2008-05-19 17:06:42 +04:00
spin_lock_init ( & wdt_lock ) ;
2005-04-17 02:20:36 +04:00
return misc_register ( & ixp2000_wdt_miscdev ) ;
}
static void __exit ixp2000_wdt_exit ( void )
{
misc_deregister ( & ixp2000_wdt_miscdev ) ;
}
module_init ( ixp2000_wdt_init ) ;
module_exit ( ixp2000_wdt_exit ) ;
2007-06-01 11:46:35 +04:00
MODULE_AUTHOR ( " Deepak Saxena <dsaxena@plexity.net> " ) ;
2005-04-17 02:20:36 +04:00
MODULE_DESCRIPTION ( " IXP2000 Network Processor Watchdog " ) ;
module_param ( heartbeat , int , 0 ) ;
MODULE_PARM_DESC ( heartbeat , " Watchdog heartbeat in seconds (default 60s) " ) ;
module_param ( nowayout , int , 0 ) ;
MODULE_PARM_DESC ( nowayout , " Watchdog cannot be stopped once started " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS_MISCDEV ( WATCHDOG_MINOR ) ;