2005-04-17 02:20:36 +04:00
/*
* Watchdog driver for the SA11x0 / PXA2xx
*
2009-03-18 11:35:09 +03:00
* ( c ) Copyright 2000 Oleg Drokin < green @ crimea . edu >
* Based on SoftDog driver by Alan Cox < alan @ lxorguk . ukuu . org . uk >
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 Oleg Drokin nor iXcelerator . com admit liability nor provide
* warranty for any of this software . This material is provided
* " AS-IS " and at no charge .
*
* ( c ) Copyright 2000 Oleg Drokin < green @ crimea . edu >
*
2009-03-18 11:35:09 +03:00
* 27 / 11 / 2000 Initial release
2005-04-17 02:20:36 +04:00
*/
2012-02-16 03:06:19 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2005-04-17 02:20:36 +04: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>
2012-03-20 23:33:19 +04:00
# include <linux/io.h>
2007-10-19 10:40:25 +04:00
# include <linux/bitops.h>
2008-05-19 17:08:05 +04:00
# include <linux/uaccess.h>
2008-11-29 20:35:51 +03:00
# include <linux/timex.h>
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_ARCH_PXA
2009-01-20 06:04:16 +03:00
# include <mach/regs-ost.h>
2005-04-17 02:20:36 +04:00
# endif
2008-08-07 14:05:25 +04:00
# include <mach/reset.h>
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
2005-04-17 02:20:36 +04:00
2008-12-24 06:32:45 +03:00
static unsigned long oscr_freq ;
2005-04-17 02:20:36 +04:00
static unsigned long sa1100wdt_users ;
2009-06-29 21:56:52 +04:00
static unsigned int pre_margin ;
2005-04-17 02:20:36 +04:00
static int boot_status ;
/*
* Allow only one person to hold it open
*/
static int sa1100dog_open ( struct inode * inode , struct file * file )
{
2008-05-19 17:08:05 +04:00
if ( test_and_set_bit ( 1 , & sa1100wdt_users ) )
2005-04-17 02:20:36 +04:00
return - EBUSY ;
/* Activate SA1100 Watchdog timer */
OSMR3 = OSCR + pre_margin ;
OSSR = OSSR_M3 ;
OWER = OWER_WME ;
OIER | = OIER_E3 ;
2007-07-25 01:38:37 +04:00
return nonseekable_open ( inode , file ) ;
2005-04-17 02:20:36 +04:00
}
/*
2005-08-03 23:34:52 +04:00
* The watchdog cannot be disabled .
*
* Previous comments suggested that turning off the interrupt by
* clearing OIER [ E3 ] would prevent the watchdog timing out but this
* does not appear to be true ( at least on the PXA255 ) .
2005-04-17 02:20:36 +04:00
*/
static int sa1100dog_release ( struct inode * inode , struct file * file )
{
2012-02-16 03:06:19 +04:00
pr_crit ( " Device closed - timer will not stop \n " ) ;
2005-04-17 02:20:36 +04:00
clear_bit ( 1 , & sa1100wdt_users ) ;
return 0 ;
}
2008-05-19 17:08:05 +04:00
static ssize_t sa1100dog_write ( struct file * file , const char __user * data ,
size_t len , loff_t * ppos )
2005-04-17 02:20:36 +04:00
{
2005-08-03 23:34:52 +04:00
if ( len )
2005-04-17 02:20:36 +04:00
/* Refresh OSMR3 timer. */
OSMR3 = OSCR + pre_margin ;
return len ;
}
2008-05-19 17:08:05 +04:00
static const struct watchdog_info ident = {
. options = WDIOF_CARDRESET | WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING ,
2005-08-03 23:34:52 +04:00
. identity = " SA1100/PXA255 Watchdog " ,
2009-06-29 21:56:52 +04:00
. firmware_version = 1 ,
2005-04-17 02:20:36 +04:00
} ;
2008-05-19 17:08:05 +04:00
static long sa1100dog_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 ;
2005-11-07 13:21:24 +03:00
void __user * argp = ( void __user * ) arg ;
int __user * p = argp ;
2005-04-17 02:20:36 +04:00
switch ( cmd ) {
case WDIOC_GETSUPPORT :
2005-11-07 13:21:24 +03:00
ret = copy_to_user ( argp , & ident ,
2005-04-17 02:20:36 +04:00
sizeof ( ident ) ) ? - EFAULT : 0 ;
break ;
case WDIOC_GETSTATUS :
2005-11-07 13:21:24 +03:00
ret = put_user ( 0 , p ) ;
2005-04-17 02:20:36 +04:00
break ;
case WDIOC_GETBOOTSTATUS :
2005-11-07 13:21:24 +03:00
ret = put_user ( boot_status , p ) ;
2005-04-17 02:20:36 +04:00
break ;
2008-07-18 15:41:17 +04:00
case WDIOC_KEEPALIVE :
OSMR3 = OSCR + pre_margin ;
ret = 0 ;
break ;
2005-04-17 02:20:36 +04:00
case WDIOC_SETTIMEOUT :
2005-11-07 13:21:24 +03:00
ret = get_user ( time , p ) ;
2005-04-17 02:20:36 +04:00
if ( ret )
break ;
2009-06-29 21:56:52 +04:00
if ( time < = 0 | | ( oscr_freq * ( long long ) time > = 0xffffffff ) ) {
2005-04-17 02:20:36 +04:00
ret = - EINVAL ;
break ;
}
2008-12-24 06:32:45 +03:00
pre_margin = oscr_freq * time ;
2005-04-17 02:20:36 +04:00
OSMR3 = OSCR + pre_margin ;
/*fall through*/
case WDIOC_GETTIMEOUT :
2008-12-24 06:32:45 +03:00
ret = put_user ( pre_margin / oscr_freq , p ) ;
2005-04-17 02:20:36 +04:00
break ;
}
return ret ;
}
2008-05-19 17:08:05 +04:00
static const struct file_operations sa1100dog_fops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. llseek = no_llseek ,
. write = sa1100dog_write ,
2008-05-19 17:08:05 +04:00
. unlocked_ioctl = sa1100dog_ioctl ,
2005-04-17 02:20:36 +04:00
. open = sa1100dog_open ,
. release = sa1100dog_release ,
} ;
2008-05-19 17:08:05 +04:00
static struct miscdevice sa1100dog_miscdev = {
2005-04-17 02:20:36 +04:00
. minor = WATCHDOG_MINOR ,
2005-08-03 23:34:52 +04:00
. name = " watchdog " ,
2005-04-17 02:20:36 +04:00
. fops = & sa1100dog_fops ,
} ;
static int margin __initdata = 60 ; /* (secs) Default is 1 minute */
static int __init sa1100dog_init ( void )
{
int ret ;
2008-12-24 06:32:45 +03:00
oscr_freq = get_clock_tick_rate ( ) ;
2005-04-17 02:20:36 +04:00
/*
* Read the reset status , and save it for later . If
* we suspend , RCSR will be cleared , and the watchdog
* reset reason will be lost .
*/
2008-07-29 10:39:34 +04:00
boot_status = ( reset_status & RESET_STATUS_WATCHDOG ) ?
WDIOF_CARDRESET : 0 ;
2008-12-24 06:32:45 +03:00
pre_margin = oscr_freq * margin ;
2005-04-17 02:20:36 +04:00
ret = misc_register ( & sa1100dog_miscdev ) ;
if ( ret = = 0 )
2012-02-16 03:06:19 +04:00
pr_info ( " SA1100/PXA2xx Watchdog Timer: timer margin %d sec \n " ,
margin ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
static void __exit sa1100dog_exit ( void )
{
misc_deregister ( & sa1100dog_miscdev ) ;
}
module_init ( sa1100dog_init ) ;
module_exit ( sa1100dog_exit ) ;
MODULE_AUTHOR ( " Oleg Drokin <green@crimea.edu> " ) ;
MODULE_DESCRIPTION ( " SA1100/PXA2xx Watchdog " ) ;
module_param ( margin , int , 0 ) ;
MODULE_PARM_DESC ( margin , " Watchdog margin in seconds (default 60s) " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS_MISCDEV ( WATCHDOG_MINOR ) ;