2006-03-14 12:11:04 +03:00
/*
* Watchdog driver for Atmel AT91RM9200 ( Thunder )
*
* Copyright ( C ) 2003 SAN People ( Pty ) Ltd
*
* 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 .
*/
2012-02-16 03:06:19 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2007-10-19 10:40:25 +04:00
# include <linux/bitops.h>
2006-03-14 12:11:04 +03:00
# include <linux/errno.h>
# include <linux/fs.h>
# include <linux/init.h>
2009-01-23 12:06:07 +03:00
# include <linux/io.h>
2006-03-14 12:11:04 +03:00
# include <linux/kernel.h>
# include <linux/miscdevice.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
2006-05-21 17:32:59 +04:00
# include <linux/platform_device.h>
2006-03-14 12:11:04 +03:00
# include <linux/types.h>
# include <linux/watchdog.h>
2008-05-19 17:05:19 +04:00
# include <linux/uaccess.h>
2008-08-05 19:14:15 +04:00
# include <mach/at91_st.h>
2006-03-14 12:11:04 +03:00
2006-05-21 17:32:59 +04:00
# define WDT_DEFAULT_TIME 5 /* seconds */
# define WDT_MAX_TIME 256 /* seconds */
2006-03-14 12:11:04 +03:00
static int wdt_time = WDT_DEFAULT_TIME ;
2012-03-05 19:51:11 +04:00
static bool nowayout = WATCHDOG_NOWAYOUT ;
2006-03-14 12:11:04 +03:00
module_param ( wdt_time , int , 0 ) ;
2008-05-19 17:05:19 +04:00
MODULE_PARM_DESC ( wdt_time , " Watchdog time in seconds. (default= "
__MODULE_STRING ( WDT_DEFAULT_TIME ) " ) " ) ;
2006-03-14 12:11:04 +03:00
2006-05-21 17:32:59 +04:00
# ifdef CONFIG_WATCHDOG_NOWAYOUT
2012-03-05 19:51:11 +04:00
module_param ( nowayout , bool , 0 ) ;
2008-05-19 17:05:19 +04:00
MODULE_PARM_DESC ( nowayout ,
" Watchdog cannot be stopped once started (default= "
__MODULE_STRING ( WATCHDOG_NOWAYOUT ) " ) " ) ;
2006-05-21 17:32:59 +04:00
# endif
2006-03-14 12:11:04 +03:00
static unsigned long at91wdt_busy ;
/* ......................................................................... */
/*
* Disable the watchdog .
*/
2008-05-19 17:05:19 +04:00
static inline void at91_wdt_stop ( void )
2006-03-14 12:11:04 +03:00
{
2012-02-20 14:07:39 +04:00
at91_st_write ( AT91_ST_WDMR , AT91_ST_EXTEN ) ;
2006-03-14 12:11:04 +03:00
}
/*
* Enable and reset the watchdog .
*/
2008-05-19 17:05:19 +04:00
static inline void at91_wdt_start ( void )
2006-03-14 12:11:04 +03:00
{
2012-02-20 14:07:39 +04:00
at91_st_write ( AT91_ST_WDMR , AT91_ST_EXTEN | AT91_ST_RSTEN |
2008-05-19 17:05:19 +04:00
( ( ( 65536 * wdt_time ) > > 8 ) & AT91_ST_WDV ) ) ;
2012-02-20 14:07:39 +04:00
at91_st_write ( AT91_ST_CR , AT91_ST_WDRST ) ;
2006-03-14 12:11:04 +03:00
}
/*
* Reload the watchdog timer . ( ie , pat the watchdog )
*/
2008-05-19 17:05:19 +04:00
static inline void at91_wdt_reload ( void )
2006-03-14 12:11:04 +03:00
{
2012-02-20 14:07:39 +04:00
at91_st_write ( AT91_ST_CR , AT91_ST_WDRST ) ;
2006-03-14 12:11:04 +03:00
}
/* ......................................................................... */
/*
* Watchdog device is opened , and watchdog starts running .
*/
static int at91_wdt_open ( struct inode * inode , struct file * file )
{
if ( test_and_set_bit ( 0 , & at91wdt_busy ) )
return - EBUSY ;
at91_wdt_start ( ) ;
return nonseekable_open ( inode , file ) ;
}
/*
* Close the watchdog device .
* If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also
* disabled .
*/
static int at91_wdt_close ( struct inode * inode , struct file * file )
{
2008-05-19 17:05:19 +04:00
/* Disable the watchdog when file is closed */
2006-03-14 12:11:04 +03:00
if ( ! nowayout )
2008-05-19 17:05:19 +04:00
at91_wdt_stop ( ) ;
2006-03-14 12:11:04 +03:00
clear_bit ( 0 , & at91wdt_busy ) ;
return 0 ;
}
/*
* Change the watchdog time interval .
*/
static int at91_wdt_settimeout ( int new_time )
{
/*
2009-02-11 23:23:10 +03:00
* All counting occurs at SLOW_CLOCK / 128 = 256 Hz
2006-03-14 12:11:04 +03:00
*
* Since WDV is a 16 - bit counter , the maximum period is
2009-02-11 23:23:10 +03:00
* 65536 / 256 = 256 seconds .
2006-03-14 12:11:04 +03:00
*/
if ( ( new_time < = 0 ) | | ( new_time > WDT_MAX_TIME ) )
return - EINVAL ;
2008-05-19 17:05:19 +04:00
/* Set new watchdog time. It will be used when
at91_wdt_start ( ) is called . */
2006-03-14 12:11:04 +03:00
wdt_time = new_time ;
return 0 ;
}
2009-12-26 21:55:22 +03:00
static const struct watchdog_info at91_wdt_info = {
2006-03-14 12:11:04 +03:00
. identity = " at91 watchdog " ,
. options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING ,
} ;
/*
* Handle commands from user - space .
*/
2008-08-08 19:57:45 +04:00
static long at91_wdt_ioctl ( struct file * file ,
2008-05-19 17:05:19 +04:00
unsigned int cmd , unsigned long arg )
2006-03-14 12:11:04 +03:00
{
void __user * argp = ( void __user * ) arg ;
int __user * p = argp ;
int new_value ;
2008-05-19 17:05:19 +04:00
switch ( cmd ) {
case WDIOC_GETSUPPORT :
return copy_to_user ( argp , & at91_wdt_info ,
sizeof ( at91_wdt_info ) ) ? - EFAULT : 0 ;
case WDIOC_GETSTATUS :
case WDIOC_GETBOOTSTATUS :
return put_user ( 0 , p ) ;
case WDIOC_SETOPTIONS :
if ( get_user ( new_value , p ) )
return - EFAULT ;
if ( new_value & WDIOS_DISABLECARD )
at91_wdt_stop ( ) ;
if ( new_value & WDIOS_ENABLECARD )
2006-03-14 12:11:04 +03:00
at91_wdt_start ( ) ;
2008-05-19 17:05:19 +04:00
return 0 ;
2008-07-18 15:41:17 +04:00
case WDIOC_KEEPALIVE :
at91_wdt_reload ( ) ; /* pat the watchdog */
return 0 ;
case WDIOC_SETTIMEOUT :
if ( get_user ( new_value , p ) )
return - EFAULT ;
if ( at91_wdt_settimeout ( new_value ) )
return - EINVAL ;
/* Enable new time value */
at91_wdt_start ( ) ;
/* Return current value */
return put_user ( wdt_time , p ) ;
case WDIOC_GETTIMEOUT :
return put_user ( wdt_time , p ) ;
2008-05-19 17:05:19 +04:00
default :
return - ENOTTY ;
2006-03-14 12:11:04 +03:00
}
}
/*
* Pat the watchdog whenever device is written to .
*/
2008-05-19 17:05:19 +04:00
static ssize_t at91_wdt_write ( struct file * file , const char * data ,
size_t len , loff_t * ppos )
2006-03-14 12:11:04 +03:00
{
at91_wdt_reload ( ) ; /* pat the watchdog */
return len ;
}
/* ......................................................................... */
2006-07-03 11:24:21 +04:00
static const struct file_operations at91wdt_fops = {
2006-03-14 12:11:04 +03:00
. owner = THIS_MODULE ,
. llseek = no_llseek ,
2008-05-19 17:05:19 +04:00
. unlocked_ioctl = at91_wdt_ioctl ,
2006-03-14 12:11:04 +03:00
. open = at91_wdt_open ,
. release = at91_wdt_close ,
. write = at91_wdt_write ,
} ;
static struct miscdevice at91wdt_miscdev = {
. minor = WATCHDOG_MINOR ,
. name = " watchdog " ,
. fops = & at91wdt_fops ,
} ;
2012-11-19 22:21:41 +04:00
static int at91wdt_probe ( struct platform_device * pdev )
2006-03-14 12:11:04 +03:00
{
int res ;
2006-12-04 16:56:18 +03:00
if ( at91wdt_miscdev . parent )
2006-05-21 17:32:59 +04:00
return - EBUSY ;
2006-12-04 16:56:18 +03:00
at91wdt_miscdev . parent = & pdev - > dev ;
2006-03-14 12:11:04 +03:00
res = misc_register ( & at91wdt_miscdev ) ;
if ( res )
return res ;
2012-02-16 03:06:19 +04:00
pr_info ( " AT91 Watchdog Timer enabled (%d seconds%s) \n " ,
wdt_time , nowayout ? " , nowayout " : " " ) ;
2006-03-14 12:11:04 +03:00
return 0 ;
}
2012-11-19 22:26:24 +04:00
static int at91wdt_remove ( struct platform_device * pdev )
2006-05-21 17:32:59 +04:00
{
int res ;
res = misc_deregister ( & at91wdt_miscdev ) ;
if ( ! res )
2006-12-04 16:56:18 +03:00
at91wdt_miscdev . parent = NULL ;
2006-05-21 17:32:59 +04:00
return res ;
}
static void at91wdt_shutdown ( struct platform_device * pdev )
{
at91_wdt_stop ( ) ;
}
# ifdef CONFIG_PM
static int at91wdt_suspend ( struct platform_device * pdev , pm_message_t message )
{
at91_wdt_stop ( ) ;
return 0 ;
}
static int at91wdt_resume ( struct platform_device * pdev )
{
if ( at91wdt_busy )
at91_wdt_start ( ) ;
2008-08-19 10:01:14 +04:00
return 0 ;
2006-05-21 17:32:59 +04:00
}
# else
# define at91wdt_suspend NULL
# define at91wdt_resume NULL
# endif
static struct platform_driver at91wdt_driver = {
. probe = at91wdt_probe ,
2012-11-19 22:21:12 +04:00
. remove = at91wdt_remove ,
2006-05-21 17:32:59 +04:00
. shutdown = at91wdt_shutdown ,
. suspend = at91wdt_suspend ,
. resume = at91wdt_resume ,
. driver = {
. name = " at91_wdt " ,
. owner = THIS_MODULE ,
} ,
} ;
static int __init at91_wdt_init ( void )
{
2008-05-19 17:05:19 +04:00
/* Check that the heartbeat value is within range;
if not reset to the default */
2006-05-21 17:32:59 +04:00
if ( at91_wdt_settimeout ( wdt_time ) ) {
at91_wdt_settimeout ( WDT_DEFAULT_TIME ) ;
2012-02-16 03:06:19 +04:00
pr_info ( " wdt_time value must be 1 <= wdt_time <= 256, using %d \n " ,
wdt_time ) ;
2006-05-21 17:32:59 +04:00
}
return platform_driver_register ( & at91wdt_driver ) ;
}
2006-03-14 12:11:04 +03:00
static void __exit at91_wdt_exit ( void )
{
2006-05-21 17:32:59 +04:00
platform_driver_unregister ( & at91wdt_driver ) ;
2006-03-14 12:11:04 +03:00
}
module_init ( at91_wdt_init ) ;
module_exit ( at91_wdt_exit ) ;
MODULE_AUTHOR ( " Andrew Victor " ) ;
MODULE_DESCRIPTION ( " Watchdog driver for Atmel AT91RM9200 " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS_MISCDEV ( WATCHDOG_MINOR ) ;
2008-04-11 08:29:23 +04:00
MODULE_ALIAS ( " platform:at91_wdt " ) ;