2007-07-20 05:07:26 +04:00
/*
* drivers / char / watchdog / iop_wdt . c
*
* WDT driver for Intel I / O Processors
* Copyright ( C ) 2005 , Intel Corporation .
*
* Based on ixp4xx driver , Copyright 2004 ( c ) MontaVista , Software , Inc .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License along with
* this program ; if not , write to the Free Software Foundation , Inc . , 59 Temple
* Place - Suite 330 , Boston , MA 02111 - 1307 USA .
*
* Curt E Bruns < curt . e . bruns @ intel . com >
* Peter Milne < peter . milne @ d - tacq . com >
* Dan Williams < dan . j . williams @ intel . com >
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/fs.h>
# include <linux/init.h>
# include <linux/device.h>
# include <linux/miscdevice.h>
# include <linux/watchdog.h>
# include <linux/uaccess.h>
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
2007-07-20 05:07:26 +04:00
static int nowayout = WATCHDOG_NOWAYOUT ;
static unsigned long wdt_status ;
static unsigned long boot_status ;
2008-05-19 17:06:14 +04:00
static spinlock_t wdt_lock ;
2007-07-20 05:07:26 +04:00
# define WDT_IN_USE 0
# define WDT_OK_TO_CLOSE 1
# define WDT_ENABLED 2
static unsigned long iop_watchdog_timeout ( void )
{
return ( 0xffffffffUL / get_iop_tick_rate ( ) ) ;
}
/**
* wdt_supports_disable - determine if we are accessing a iop13xx watchdog
* or iop3xx by whether it has a disable command
*/
static int wdt_supports_disable ( void )
{
int can_disable ;
if ( IOP_WDTCR_EN_ARM ! = IOP_WDTCR_DIS_ARM )
can_disable = 1 ;
else
can_disable = 0 ;
return can_disable ;
}
static void wdt_enable ( void )
{
/* Arm and enable the Timer to starting counting down from 0xFFFF.FFFF
* Takes approx . 10.7 s to timeout
*/
2008-05-19 17:06:14 +04:00
spin_lock ( & wdt_lock ) ;
2007-07-20 05:07:26 +04:00
write_wdtcr ( IOP_WDTCR_EN_ARM ) ;
write_wdtcr ( IOP_WDTCR_EN ) ;
2008-05-19 17:06:14 +04:00
spin_unlock ( & wdt_lock ) ;
2007-07-20 05:07:26 +04:00
}
/* returns 0 if the timer was successfully disabled */
static int wdt_disable ( void )
{
/* Stop Counting */
if ( wdt_supports_disable ( ) ) {
2008-05-19 17:06:14 +04:00
spin_lock ( & wdt_lock ) ;
2007-07-20 05:07:26 +04:00
write_wdtcr ( IOP_WDTCR_DIS_ARM ) ;
write_wdtcr ( IOP_WDTCR_DIS ) ;
clear_bit ( WDT_ENABLED , & wdt_status ) ;
2008-05-19 17:06:14 +04:00
spin_unlock ( & wdt_lock ) ;
2007-07-20 05:07:26 +04:00
printk ( KERN_INFO " WATCHDOG: Disabled \n " ) ;
return 0 ;
} else
return 1 ;
}
static int iop_wdt_open ( struct inode * inode , struct file * file )
{
if ( test_and_set_bit ( WDT_IN_USE , & wdt_status ) )
return - EBUSY ;
clear_bit ( WDT_OK_TO_CLOSE , & wdt_status ) ;
wdt_enable ( ) ;
set_bit ( WDT_ENABLED , & wdt_status ) ;
return nonseekable_open ( inode , file ) ;
}
2008-05-19 17:06:14 +04:00
static ssize_t iop_wdt_write ( struct file * file , const char * data , size_t len ,
2007-07-20 05:07:26 +04:00
loff_t * ppos )
{
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_enable ( ) ;
}
return len ;
}
2008-05-19 17:06:14 +04:00
static const struct watchdog_info ident = {
2007-07-20 05:07:26 +04:00
. options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING ,
. identity = " iop watchdog " ,
} ;
2008-05-19 17:06:14 +04:00
static long iop_wdt_ioctl ( struct file * file ,
unsigned int cmd , unsigned long arg )
2007-07-20 05:07:26 +04:00
{
int options ;
int ret = - ENOTTY ;
2008-05-19 17:06:14 +04:00
int __user * argp = ( int __user * ) arg ;
2007-07-20 05:07:26 +04:00
switch ( cmd ) {
case WDIOC_GETSUPPORT :
2009-09-02 13:10:07 +04:00
if ( copy_to_user ( argp , & ident , sizeof ( ident ) ) )
2007-07-20 05:07:26 +04:00
ret = - EFAULT ;
else
ret = 0 ;
break ;
case WDIOC_GETSTATUS :
2008-05-19 17:06:14 +04:00
ret = put_user ( 0 , argp ) ;
2007-07-20 05:07:26 +04:00
break ;
case WDIOC_GETBOOTSTATUS :
2008-05-19 17:06:14 +04:00
ret = put_user ( boot_status , argp ) ;
2007-07-20 05:07:26 +04:00
break ;
case WDIOC_SETOPTIONS :
if ( get_user ( options , ( int * ) arg ) )
return - EFAULT ;
if ( options & WDIOS_DISABLECARD ) {
if ( ! nowayout ) {
if ( wdt_disable ( ) = = 0 ) {
set_bit ( WDT_OK_TO_CLOSE , & wdt_status ) ;
ret = 0 ;
} else
ret = - ENXIO ;
} else
ret = 0 ;
}
if ( options & WDIOS_ENABLECARD ) {
wdt_enable ( ) ;
ret = 0 ;
}
break ;
2008-07-18 15:41:17 +04:00
case WDIOC_KEEPALIVE :
wdt_enable ( ) ;
ret = 0 ;
break ;
case WDIOC_GETTIMEOUT :
ret = put_user ( iop_watchdog_timeout ( ) , argp ) ;
break ;
2007-07-20 05:07:26 +04:00
}
return ret ;
}
static int iop_wdt_release ( struct inode * inode , struct file * file )
{
int state = 1 ;
if ( test_bit ( WDT_OK_TO_CLOSE , & wdt_status ) )
if ( test_bit ( WDT_ENABLED , & wdt_status ) )
state = wdt_disable ( ) ;
2009-04-15 06:14:10 +04:00
/* if the timer is not disabled reload and notify that we are still
2007-07-20 05:07:26 +04:00
* going down
*/
if ( state ! = 0 ) {
wdt_enable ( ) ;
printk ( KERN_CRIT " WATCHDOG: Device closed unexpectedly - "
" reset in %lu seconds \n " , iop_watchdog_timeout ( ) ) ;
}
clear_bit ( WDT_IN_USE , & wdt_status ) ;
clear_bit ( WDT_OK_TO_CLOSE , & wdt_status ) ;
return 0 ;
}
static const struct file_operations iop_wdt_fops = {
. owner = THIS_MODULE ,
. llseek = no_llseek ,
. write = iop_wdt_write ,
2008-05-19 17:06:14 +04:00
. unlocked_ioctl = iop_wdt_ioctl ,
2007-07-20 05:07:26 +04:00
. open = iop_wdt_open ,
. release = iop_wdt_release ,
} ;
static struct miscdevice iop_wdt_miscdev = {
. minor = WATCHDOG_MINOR ,
. name = " watchdog " ,
. fops = & iop_wdt_fops ,
} ;
static int __init iop_wdt_init ( void )
{
int ret ;
2008-05-19 17:06:14 +04:00
spin_lock_init ( & wdt_lock ) ;
2007-07-20 05:07:26 +04:00
/* check if the reset was caused by the watchdog timer */
boot_status = ( read_rcsr ( ) & IOP_RCSR_WDT ) ? WDIOF_CARDRESET : 0 ;
/* Configure Watchdog Timeout to cause an Internal Bus (IB) Reset
* NOTE : An IB Reset will Reset both cores in the IOP342
*/
write_wdtsr ( IOP13XX_WDTCR_IB_RESET ) ;
2008-05-19 17:06:14 +04:00
/* Register after we have the device set up so we cannot race
with an open */
ret = misc_register ( & iop_wdt_miscdev ) ;
if ( ret = = 0 )
2008-08-07 00:19:41 +04:00
printk ( KERN_INFO " iop watchdog timer: timeout %lu sec \n " ,
2008-05-19 17:06:14 +04:00
iop_watchdog_timeout ( ) ) ;
2007-07-20 05:07:26 +04:00
return ret ;
}
static void __exit iop_wdt_exit ( void )
{
misc_deregister ( & iop_wdt_miscdev ) ;
}
module_init ( iop_wdt_init ) ;
module_exit ( iop_wdt_exit ) ;
module_param ( nowayout , int , 0 ) ;
MODULE_PARM_DESC ( nowayout , " Watchdog cannot be stopped once started " ) ;
MODULE_AUTHOR ( " Curt E Bruns <curt.e.bruns@intel.com> " ) ;
MODULE_DESCRIPTION ( " iop watchdog timer driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS_MISCDEV ( WATCHDOG_MINOR ) ;