@ -31,42 +31,34 @@
# include <linux/module.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/fs.h>
# include <linux/mm.h>
# include <linux/miscdevice.h>
# include <linux/watchdog.h>
# include <linux/reboot.h>
# include <linux/init.h>
# include <linux/err.h>
# include <linux/platform_device.h>
# include <linux/moduleparam.h>
# include <linux/bitops.h>
# include <linux/io.h>
# include <linux/uaccess.h>
# include <linux/slab.h>
# include <linux/pm_runtime.h>
# include <linux/platform_data/omap-wd-timer.h>
# include "omap_wdt.h"
static struct platform_device * omap_wdt_dev ;
static unsigned timer_margin ;
module_param ( timer_margin , uint , 0 ) ;
MODULE_PARM_DESC ( timer_margin , " initial watchdog timeout (in seconds) " ) ;
static unsigned int wdt_trgr_pattern = 0x1234 ;
static DEFINE_SPINLOCK ( wdt_lock ) ;
struct omap_wdt_dev {
void __iomem * base ; /* physical */
struct device * dev ;
int omap_wdt_users ;
bool omap_wdt_users ;
struct resource * mem ;
struct miscdevice omap_wdt_miscdev ;
int wdt_trgr_pattern ;
struct mutex lock ; /* to avoid races with PM */
} ;
static void omap_wdt_ping ( struct omap_wdt_dev * wdev )
static void omap_wdt_reload ( struct omap_wdt_dev * wdev )
{
void __iomem * base = wdev - > base ;
@ -74,8 +66,8 @@ static void omap_wdt_ping(struct omap_wdt_dev *wdev)
while ( ( __raw_readl ( base + OMAP_WATCHDOG_WPS ) ) & 0x08 )
cpu_relax ( ) ;
wdt_trgr_pattern = ~ wdt_trgr_pattern ;
__raw_writel ( wdt_trgr_pattern , ( base + OMAP_WATCHDOG_TGR ) ) ;
wdev - > wd t_trgr_pattern = ~ wdev - > wdt_trgr_pattern ;
__raw_writel ( wdev - > wd t_trgr_pattern , ( base + OMAP_WATCHDOG_TGR ) ) ;
/* wait for posted write to complete */
while ( ( __raw_readl ( base + OMAP_WATCHDOG_WPS ) ) & 0x08 )
@ -111,18 +103,10 @@ static void omap_wdt_disable(struct omap_wdt_dev *wdev)
cpu_relax ( ) ;
}
static void omap_wdt_adjust_timeout ( unsigned new_timeout )
{
if ( new_timeout < TIMER_MARGIN_MIN )
new_timeout = TIMER_MARGIN_DEFAULT ;
if ( new_timeout > TIMER_MARGIN_MAX )
new_timeout = TIMER_MARGIN_MAX ;
timer_margin = new_timeout ;
}
static void omap_wdt_set_timeout ( struct omap_wdt_dev * wdev )
static void omap_wdt_set_timer ( struct omap_wdt_dev * wdev ,
unsigned int timeout )
{
u32 pre_margin = GET_WLDR_VAL ( timer_margin ) ;
u32 pre_margin = GET_WLDR_VAL ( timeout ) ;
void __iomem * base = wdev - > base ;
/* just count up at 32 KHz */
@ -134,16 +118,14 @@ static void omap_wdt_set_timeout(struct omap_wdt_dev *wdev)
cpu_relax ( ) ;
}
/*
* Allow only one task to hold it open
*/
static int omap_wdt_open ( struct inode * inode , struct file * file )
static int omap_wdt_start ( struct watchdog_device * wdog )
{
struct omap_wdt_dev * wdev = platform_get_drvdata ( omap_wdt_dev ) ;
struct omap_wdt_dev * wdev = watchdog_get_drvdata ( wdog ) ;
void __iomem * base = wdev - > base ;
if ( test_and_set_bit ( 1 , ( unsigned long * ) & ( wdev - > omap_wdt_users ) ) )
return - EBUSY ;
mutex_lock ( & wdev - > lock ) ;
wdev - > omap_wdt_users = true ;
pm_runtime_get_sync ( wdev - > dev ) ;
@ -155,117 +137,81 @@ static int omap_wdt_open(struct inode *inode, struct file *file)
while ( __raw_readl ( base + OMAP_WATCHDOG_WPS ) & 0x01 )
cpu_relax ( ) ;
file - > private_data = ( void * ) wdev ;
omap_wdt_set_timeout ( wdev ) ;
omap_wdt_ping ( wdev ) ; /* trigger loading of new timeout value */
omap_wdt_set_timer ( wdev , wdog - > timeout ) ;
omap_wdt_reload ( wdev ) ; /* trigger loading of new timeout value */
omap_wdt_enable ( wdev ) ;
return nonseekable_open ( inode , file ) ;
mutex_unlock ( & wdev - > lock ) ;
return 0 ;
}
static int omap_wdt_release ( struct inode * inode , struct file * file )
static int omap_wdt_stop ( struct watchdog_device * wdog )
{
struct omap_wdt_dev * wdev = file - > private_data ;
struct omap_wdt_dev * wdev = watchdog_get_drvdata ( wdog ) ;
/*
* Shut off the timer unless NOWAYOUT is defined .
*/
# ifndef CONFIG_WATCHDOG_NOWAYOUT
mutex_lock ( & wdev - > lock ) ;
omap_wdt_disable ( wdev ) ;
pm_runtime_put_sync ( wdev - > dev ) ;
# else
pr_crit ( " Unexpected close, not stopping! \n " ) ;
# endif
wdev - > omap_wdt_users = 0 ;
wdev - > omap_wdt_users = false ;
mutex_unlock ( & wdev - > lock ) ;
return 0 ;
}
static ssize_t omap_wdt_write ( struct file * file , const char __user * data ,
size_t len , loff_t * ppos )
static int omap_wdt_ping ( struct watchdog_device * wdog )
{
struct omap_wdt_dev * wdev = file - > private_data ;
struct omap_wdt_dev * wdev = watchdog_get_drvdata ( wdog ) ;
/* Refresh LOAD_TIME. */
if ( len ) {
spin_lock ( & wdt_lock ) ;
omap_wdt_ping ( wdev ) ;
spin_unlock ( & wdt_lock ) ;
}
return len ;
mutex_lock ( & wdev - > lock ) ;
omap_wdt_reload ( wdev ) ;
mutex_unlock ( & wdev - > lock ) ;
return 0 ;
}
static long omap_wdt_ioctl ( struct file * file , unsigned int cmd ,
unsigned long arg )
static int omap_wdt_set_timeout ( struct watchdog_device * wdog ,
unsigned int timeout )
{
struct omap_wd_timer_platform_data * pdata ;
struct omap_wdt_dev * wdev ;
u32 rs ;
int new_margin , bs ;
static const struct watchdog_info ident = {
. identity = " OMAP Watchdog " ,
. options = WDIOF_SETTIMEOUT ,
. firmware_version = 0 ,
} ;
wdev = file - > private_data ;
pdata = wdev - > dev - > platform_data ;
switch ( cmd ) {
case WDIOC_GETSUPPORT :
return copy_to_user ( ( struct watchdog_info __user * ) arg , & ident ,
sizeof ( ident ) ) ;
case WDIOC_GETSTATUS :
return put_user ( 0 , ( int __user * ) arg ) ;
case WDIOC_GETBOOTSTATUS :
if ( ! pdata | | ! pdata - > read_reset_sources )
return put_user ( 0 , ( int __user * ) arg ) ;
rs = pdata - > read_reset_sources ( ) ;
bs = ( rs & ( 1 < < OMAP_MPU_WD_RST_SRC_ID_SHIFT ) ) ?
WDIOF_CARDRESET : 0 ;
return put_user ( bs , ( int __user * ) arg ) ;
case WDIOC_KEEPALIVE :
spin_lock ( & wdt_lock ) ;
omap_wdt_ping ( wdev ) ;
spin_unlock ( & wdt_lock ) ;
return 0 ;
case WDIOC_SETTIMEOUT :
if ( get_user ( new_margin , ( int __user * ) arg ) )
return - EFAULT ;
omap_wdt_adjust_timeout ( new_margin ) ;
spin_lock ( & wdt_lock ) ;
omap_wdt_disable ( wdev ) ;
omap_wdt_set_timeout ( wdev ) ;
omap_wdt_enable ( wdev ) ;
struct omap_wdt_dev * wdev = watchdog_get_drvdata ( wdog ) ;
omap_wdt_ping ( wdev ) ;
spin_unlock ( & wdt_lock ) ;
/* Fall */
case WDIOC_GETTIMEOUT :
return put_user ( timer_margin , ( int __user * ) arg ) ;
default :
return - ENOTTY ;
}
mutex_lock ( & wdev - > lock ) ;
omap_wdt_disable ( wdev ) ;
omap_wdt_set_timer ( wdev , timeout ) ;
omap_wdt_enable ( wdev ) ;
omap_wdt_reload ( wdev ) ;
wdog - > timeout = timeout ;
mutex_unlock ( & wdev - > lock ) ;
return 0 ;
}
static const struct file_operations omap_wdt_fops = {
. owner = THIS_MODULE ,
. write = omap_wdt_write ,
. unlocked_ioctl = omap_wdt_ioctl ,
. open = omap_wdt_open ,
. release = omap_wdt_release ,
. llseek = no_llseek ,
static const struct watchdog_info omap_wdt_info = {
. options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING ,
. identity = " OMAP Watchdog " ,
} ;
static const struct watchdog_ops omap_wdt_ops = {
. owner = THIS_MODULE ,
. start = omap_wdt_start ,
. stop = omap_wdt_stop ,
. ping = omap_wdt_ping ,
. set_timeout = omap_wdt_set_timeout ,
} ;
static int omap_wdt_probe ( struct platform_device * pdev )
{
struct omap_wd_timer_platform_data * pdata = pdev - > dev . platform_data ;
bool nowayout = WATCHDOG_NOWAYOUT ;
struct watchdog_device * omap_wdt ;
struct resource * res , * mem ;
struct omap_wdt_dev * wdev ;
u32 rs ;
int ret ;
omap_wdt = kzalloc ( sizeof ( * omap_wdt ) , GFP_KERNEL ) ;
if ( ! omap_wdt )
return - ENOMEM ;
/* reserve static register mappings */
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
@ -273,11 +219,6 @@ static int omap_wdt_probe(struct platform_device *pdev)
goto err_get_resource ;
}
if ( omap_wdt_dev ) {
ret = - EBUSY ;
goto err_busy ;
}
mem = request_mem_region ( res - > start , resource_size ( res ) , pdev - > name ) ;
if ( ! mem ) {
ret = - EBUSY ;
@ -290,9 +231,11 @@ static int omap_wdt_probe(struct platform_device *pdev)
goto err_kzalloc ;
}
wdev - > omap_wdt_users = 0 ;
wdev - > mem = mem ;
wdev - > dev = & pdev - > dev ;
wdev - > omap_wdt_users = false ;
wdev - > mem = mem ;
wdev - > dev = & pdev - > dev ;
wdev - > wdt_trgr_pattern = 0x1234 ;
mutex_init ( & wdev - > lock ) ;
wdev - > base = ioremap ( res - > start , resource_size ( res ) ) ;
if ( ! wdev - > base ) {
@ -300,34 +243,47 @@ static int omap_wdt_probe(struct platform_device *pdev)
goto err_ioremap ;
}
platform_set_drvdata ( pdev , wdev ) ;
omap_wdt - > info = & omap_wdt_info ;
omap_wdt - > ops = & omap_wdt_ops ;
omap_wdt - > min_timeout = TIMER_MARGIN_MIN ;
omap_wdt - > max_timeout = TIMER_MARGIN_MAX ;
if ( timer_margin > = TIMER_MARGIN_MIN & &
timer_margin < = TIMER_MARGIN_MAX )
omap_wdt - > timeout = timer_margin ;
else
omap_wdt - > timeout = TIMER_MARGIN_DEFAULT ;
watchdog_set_drvdata ( omap_wdt , wdev ) ;
watchdog_set_nowayout ( omap_wdt , nowayout ) ;
platform_set_drvdata ( pdev , omap_wdt ) ;
pm_runtime_enable ( wdev - > dev ) ;
pm_runtime_get_sync ( wdev - > dev ) ;
omap_wdt_disable ( wdev ) ;
omap_wdt_adjust_timeout ( timer_margin ) ;
if ( pdata & & pdata - > read_reset_sources )
rs = pdata - > read_reset_sources ( ) ;
else
rs = 0 ;
omap_wdt - > bootstatus = ( rs & ( 1 < < OMAP_MPU_WD_RST_SRC_ID_SHIFT ) ) ?
WDIOF_CARDRESET : 0 ;
wdev - > omap_wdt_miscdev . parent = & pdev - > dev ;
wdev - > omap_wdt_miscdev . minor = WATCHDOG_MINOR ;
wdev - > omap_wdt_miscdev . name = " watchdog " ;
wdev - > omap_wdt_miscdev . fops = & omap_wdt_fops ;
omap_wdt_disable ( wdev ) ;
ret = misc_register ( & ( wdev - > omap_wdt_miscdev ) ) ;
ret = watchdog_register_device ( omap_wdt ) ;
if ( ret )
goto err_misc ;
goto err_register ;
pr_info ( " OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec \n " ,
__raw_readl ( wdev - > base + OMAP_WATCHDOG_REV ) & 0xFF ,
timer_margin ) ;
omap_wdt - > timeout ) ;
pm_runtime_put_sync ( wdev - > dev ) ;
omap_wdt_dev = pdev ;
return 0 ;
err_misc :
err_register :
pm_runtime_disable ( wdev - > dev ) ;
platform_set_drvdata ( pdev , NULL ) ;
iounmap ( wdev - > base ) ;
@ -341,37 +297,38 @@ err_kzalloc:
err_busy :
err_get_resource :
kfree ( omap_wdt ) ;
return ret ;
}
static void omap_wdt_shutdown ( struct platform_device * pdev )
{
struct omap_wdt_dev * wdev = platform_get_drvdata ( pdev ) ;
struct watchdog_device * wdog = platform_get_drvdata ( pdev ) ;
struct omap_wdt_dev * wdev = watchdog_get_drvdata ( wdog ) ;
mutex_lock ( & wdev - > lock ) ;
if ( wdev - > omap_wdt_users ) {
omap_wdt_disable ( wdev ) ;
pm_runtime_put_sync ( wdev - > dev ) ;
}
mutex_unlock ( & wdev - > lock ) ;
}
static int omap_wdt_remove ( struct platform_device * pdev )
{
struct omap_wdt_dev * wdev = platform_get_drvdata ( pdev ) ;
struct watchdog_device * wdog = platform_get_drvdata ( pdev ) ;
struct omap_wdt_dev * wdev = watchdog_get_drvdata ( wdog ) ;
struct resource * res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
pm_runtime_disable ( wdev - > dev ) ;
if ( ! res )
return - ENOENT ;
misc_deregister ( & ( wdev - > omap_wdt_miscdev ) ) ;
watchdog_unregister_device ( wdog ) ;
release_mem_region ( res - > start , resource_size ( res ) ) ;
platform_set_drvdata ( pdev , NULL ) ;
iounmap ( wdev - > base ) ;
kfree ( wdev ) ;
omap_wdt_dev = NULL ;
kfree ( wdog ) ;
return 0 ;
}
@ -386,25 +343,31 @@ static int omap_wdt_remove(struct platform_device *pdev)
static int omap_wdt_suspend ( struct platform_device * pdev , pm_message_t state )
{
struct omap_wdt_dev * wdev = platform_get_drvdata ( pdev ) ;
struct watchdog_device * wdog = platform_get_drvdata ( pdev ) ;
struct omap_wdt_dev * wdev = watchdog_get_drvdata ( wdog ) ;
mutex_lock ( & wdev - > lock ) ;
if ( wdev - > omap_wdt_users ) {
omap_wdt_disable ( wdev ) ;
pm_runtime_put_sync ( wdev - > dev ) ;
}
mutex_unlock ( & wdev - > lock ) ;
return 0 ;
}
static int omap_wdt_resume ( struct platform_device * pdev )
{
struct omap_wdt_dev * wdev = platform_get_drvdata ( pdev ) ;
struct watchdog_device * wdog = platform_get_drvdata ( pdev ) ;
struct omap_wdt_dev * wdev = watchdog_get_drvdata ( wdog ) ;
mutex_lock ( & wdev - > lock ) ;
if ( wdev - > omap_wdt_users ) {
pm_runtime_get_sync ( wdev - > dev ) ;
omap_wdt_enable ( wdev ) ;
omap_wdt_ping ( wdev ) ;
omap_wdt_reload ( wdev ) ;
}
mutex_unlock ( & wdev - > lock ) ;
return 0 ;
}
@ -437,5 +400,4 @@ module_platform_driver(omap_wdt_driver);
MODULE_AUTHOR ( " George G. Davis " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS_MISCDEV ( WATCHDOG_MINOR ) ;
MODULE_ALIAS ( " platform:omap_wdt " ) ;