2010-11-03 17:07:28 +03:00
/*
2011-01-22 02:21:24 +03:00
* drivers / watchdog / m54xx_wdt . c
2010-11-03 17:07:28 +03:00
*
2011-01-22 02:21:24 +03:00
* Watchdog driver for ColdFire MCF547x & MCF548x processors
2010-11-03 17:07:28 +03:00
* Copyright 2010 ( c ) Philippe De Muyter < phdm @ macqel . be >
*
* Adapted from the IXP4xx watchdog driver , which 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 .
*/
2012-02-16 03:06:19 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2010-11-03 17:07:28 +03:00
# 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>
# include <linux/ioport.h>
# include <linux/uaccess.h>
# include <asm/coldfire.h>
2011-01-22 02:21:24 +03:00
# include <asm/m54xxsim.h>
# include <asm/m54xxgpt.h>
2010-11-03 17:07:28 +03:00
2012-03-05 19:51:11 +04:00
static bool nowayout = WATCHDOG_NOWAYOUT ;
2010-11-03 17:07:28 +03:00
static unsigned int heartbeat = 30 ; /* (secs) Default is 0.5 minute */
static unsigned long wdt_status ;
# define WDT_IN_USE 0
# define WDT_OK_TO_CLOSE 1
static void wdt_enable ( void )
{
unsigned int gms0 ;
/* preserve GPIO usage, if any */
2012-09-18 08:51:46 +04:00
gms0 = __raw_readl ( MCF_GPT_GMS0 ) ;
2010-11-03 17:07:28 +03:00
if ( gms0 & MCF_GPT_GMS_TMS_GPIO )
gms0 & = ( MCF_GPT_GMS_TMS_GPIO | MCF_GPT_GMS_GPIO_MASK
| MCF_GPT_GMS_OD ) ;
else
gms0 = MCF_GPT_GMS_TMS_GPIO | MCF_GPT_GMS_OD ;
2012-09-18 08:51:46 +04:00
__raw_writel ( gms0 , MCF_GPT_GMS0 ) ;
2010-11-03 17:07:28 +03:00
__raw_writel ( MCF_GPT_GCIR_PRE ( heartbeat * ( MCF_BUSCLK / 0xffff ) ) |
2012-09-18 08:51:46 +04:00
MCF_GPT_GCIR_CNT ( 0xffff ) , MCF_GPT_GCIR0 ) ;
2010-11-03 17:07:28 +03:00
gms0 | = MCF_GPT_GMS_OCPW ( 0xA5 ) | MCF_GPT_GMS_WDEN | MCF_GPT_GMS_CE ;
2012-09-18 08:51:46 +04:00
__raw_writel ( gms0 , MCF_GPT_GMS0 ) ;
2010-11-03 17:07:28 +03:00
}
static void wdt_disable ( void )
{
unsigned int gms0 ;
/* disable watchdog */
2012-09-18 08:51:46 +04:00
gms0 = __raw_readl ( MCF_GPT_GMS0 ) ;
2010-11-03 17:07:28 +03:00
gms0 & = ~ ( MCF_GPT_GMS_WDEN | MCF_GPT_GMS_CE ) ;
2012-09-18 08:51:46 +04:00
__raw_writel ( gms0 , MCF_GPT_GMS0 ) ;
2010-11-03 17:07:28 +03:00
}
static void wdt_keepalive ( void )
{
unsigned int gms0 ;
2012-09-18 08:51:46 +04:00
gms0 = __raw_readl ( MCF_GPT_GMS0 ) ;
2010-11-03 17:07:28 +03:00
gms0 | = MCF_GPT_GMS_OCPW ( 0xA5 ) ;
2012-09-18 08:51:46 +04:00
__raw_writel ( gms0 , MCF_GPT_GMS0 ) ;
2010-11-03 17:07:28 +03:00
}
2011-01-22 02:21:24 +03:00
static int m54xx_wdt_open ( struct inode * inode , struct file * file )
2010-11-03 17:07:28 +03: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 ) ;
}
2011-01-22 02:21:24 +03:00
static ssize_t m54xx_wdt_write ( struct file * file , const char * data ,
2010-11-03 17:07:28 +03:00
size_t len , 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_keepalive ( ) ;
}
return len ;
}
static const struct watchdog_info ident = {
. options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING ,
2011-01-22 02:21:24 +03:00
. identity = " Coldfire M54xx Watchdog " ,
2010-11-03 17:07:28 +03:00
} ;
2011-01-22 02:21:24 +03:00
static long m54xx_wdt_ioctl ( struct file * file , unsigned int cmd ,
2010-11-03 17:07:28 +03:00
unsigned long arg )
{
int ret = - ENOTTY ;
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 ;
case WDIOC_KEEPALIVE :
wdt_keepalive ( ) ;
ret = 0 ;
break ;
case WDIOC_SETTIMEOUT :
ret = get_user ( time , ( int * ) arg ) ;
if ( ret )
break ;
if ( time < = 0 | | time > 30 ) {
ret = - EINVAL ;
break ;
}
heartbeat = time ;
wdt_enable ( ) ;
/* Fall through */
case WDIOC_GETTIMEOUT :
ret = put_user ( heartbeat , ( int * ) arg ) ;
break ;
}
return ret ;
}
2011-01-22 02:21:24 +03:00
static int m54xx_wdt_release ( struct inode * inode , struct file * file )
2010-11-03 17:07:28 +03:00
{
if ( test_bit ( WDT_OK_TO_CLOSE , & wdt_status ) )
wdt_disable ( ) ;
else {
2012-02-16 03:06:19 +04:00
pr_crit ( " Device closed unexpectedly - timer will not stop \n " ) ;
2010-11-03 17:07:28 +03:00
wdt_keepalive ( ) ;
}
clear_bit ( WDT_IN_USE , & wdt_status ) ;
clear_bit ( WDT_OK_TO_CLOSE , & wdt_status ) ;
return 0 ;
}
2011-01-22 02:21:24 +03:00
static const struct file_operations m54xx_wdt_fops = {
2010-11-03 17:07:28 +03:00
. owner = THIS_MODULE ,
. llseek = no_llseek ,
2011-01-22 02:21:24 +03:00
. write = m54xx_wdt_write ,
. unlocked_ioctl = m54xx_wdt_ioctl ,
. open = m54xx_wdt_open ,
. release = m54xx_wdt_release ,
2010-11-03 17:07:28 +03:00
} ;
2011-01-22 02:21:24 +03:00
static struct miscdevice m54xx_wdt_miscdev = {
2010-11-03 17:07:28 +03:00
. minor = WATCHDOG_MINOR ,
. name = " watchdog " ,
2011-01-22 02:21:24 +03:00
. fops = & m54xx_wdt_fops ,
2010-11-03 17:07:28 +03:00
} ;
2011-01-22 02:21:24 +03:00
static int __init m54xx_wdt_init ( void )
2010-11-03 17:07:28 +03:00
{
2012-09-18 08:51:46 +04:00
if ( ! request_mem_region ( MCF_GPT_GCIR0 , 4 , " Coldfire M54xx Watchdog " ) ) {
2012-02-16 03:06:19 +04:00
pr_warn ( " I/O region busy \n " ) ;
2010-11-03 17:07:28 +03:00
return - EBUSY ;
}
2012-02-16 03:06:19 +04:00
pr_info ( " driver is loaded \n " ) ;
2010-11-03 17:07:28 +03:00
2011-01-22 02:21:24 +03:00
return misc_register ( & m54xx_wdt_miscdev ) ;
2010-11-03 17:07:28 +03:00
}
2011-01-22 02:21:24 +03:00
static void __exit m54xx_wdt_exit ( void )
2010-11-03 17:07:28 +03:00
{
2011-01-22 02:21:24 +03:00
misc_deregister ( & m54xx_wdt_miscdev ) ;
2012-09-18 08:51:46 +04:00
release_mem_region ( MCF_GPT_GCIR0 , 4 ) ;
2010-11-03 17:07:28 +03:00
}
2011-01-22 02:21:24 +03:00
module_init ( m54xx_wdt_init ) ;
module_exit ( m54xx_wdt_exit ) ;
2010-11-03 17:07:28 +03:00
MODULE_AUTHOR ( " Philippe De Muyter <phdm@macqel.be> " ) ;
2011-01-22 02:21:24 +03:00
MODULE_DESCRIPTION ( " Coldfire M54xx Watchdog " ) ;
2010-11-03 17:07:28 +03:00
module_param ( heartbeat , int , 0 ) ;
MODULE_PARM_DESC ( heartbeat , " Watchdog heartbeat in seconds (default 30s) " ) ;
2012-03-05 19:51:11 +04:00
module_param ( nowayout , bool , 0 ) ;
2010-11-03 17:07:28 +03:00
MODULE_PARM_DESC ( nowayout , " Watchdog cannot be stopped once started " ) ;
MODULE_LICENSE ( " GPL " ) ;