2019-05-28 19:57:24 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2013-01-29 12:57:19 +04:00
/*
* Copyright ( C ) ST - Ericsson SA 2011 - 2013
*
* Author : Mathieu Poirier < mathieu . poirier @ linaro . org > for ST - Ericsson
* Author : Jonas Aaberg < jonas . aberg @ stericsson . com > for ST - Ericsson
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/moduleparam.h>
# include <linux/err.h>
# include <linux/uaccess.h>
# include <linux/watchdog.h>
# include <linux/platform_device.h>
# include <linux/platform_data/ux500_wdt.h>
# include <linux/mfd/dbx500-prcmu.h>
# define WATCHDOG_TIMEOUT 600 /* 10 minutes */
# define WATCHDOG_MIN 0
# define WATCHDOG_MAX28 268435 /* 28 bit resolution in ms == 268435.455 s */
# define WATCHDOG_MAX32 4294967 /* 32 bit resolution in ms == 4294967.295 s */
static unsigned int timeout = WATCHDOG_TIMEOUT ;
module_param ( timeout , uint , 0 ) ;
MODULE_PARM_DESC ( timeout ,
" Watchdog timeout in seconds. default= "
__MODULE_STRING ( WATCHDOG_TIMEOUT ) " . " ) ;
static bool nowayout = WATCHDOG_NOWAYOUT ;
module_param ( nowayout , bool , 0 ) ;
MODULE_PARM_DESC ( nowayout ,
" Watchdog cannot be stopped once started (default= "
__MODULE_STRING ( WATCHDOG_NOWAYOUT ) " ) " ) ;
static int ux500_wdt_start ( struct watchdog_device * wdd )
{
return prcmu_enable_a9wdog ( PRCMU_WDOG_ALL ) ;
}
static int ux500_wdt_stop ( struct watchdog_device * wdd )
{
return prcmu_disable_a9wdog ( PRCMU_WDOG_ALL ) ;
}
static int ux500_wdt_keepalive ( struct watchdog_device * wdd )
{
return prcmu_kick_a9wdog ( PRCMU_WDOG_ALL ) ;
}
static int ux500_wdt_set_timeout ( struct watchdog_device * wdd ,
unsigned int timeout )
{
ux500_wdt_stop ( wdd ) ;
prcmu_load_a9wdog ( PRCMU_WDOG_ALL , timeout * 1000 ) ;
ux500_wdt_start ( wdd ) ;
return 0 ;
}
static const struct watchdog_info ux500_wdt_info = {
. options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE ,
. identity = " Ux500 WDT " ,
. firmware_version = 1 ,
} ;
static const struct watchdog_ops ux500_wdt_ops = {
. owner = THIS_MODULE ,
. start = ux500_wdt_start ,
. stop = ux500_wdt_stop ,
. ping = ux500_wdt_keepalive ,
. set_timeout = ux500_wdt_set_timeout ,
} ;
static struct watchdog_device ux500_wdt = {
. info = & ux500_wdt_info ,
. ops = & ux500_wdt_ops ,
. min_timeout = WATCHDOG_MIN ,
. max_timeout = WATCHDOG_MAX32 ,
} ;
static int ux500_wdt_probe ( struct platform_device * pdev )
{
2019-04-10 19:27:55 +03:00
struct device * dev = & pdev - > dev ;
2013-01-29 12:57:19 +04:00
int ret ;
2019-04-10 19:27:55 +03:00
struct ux500_wdt_data * pdata = dev_get_platdata ( dev ) ;
2013-01-29 12:57:19 +04:00
if ( pdata ) {
if ( pdata - > timeout > 0 )
timeout = pdata - > timeout ;
if ( pdata - > has_28_bits_resolution )
ux500_wdt . max_timeout = WATCHDOG_MAX28 ;
}
2019-04-10 19:27:55 +03:00
ux500_wdt . parent = dev ;
2013-01-29 12:57:19 +04:00
watchdog_set_nowayout ( & ux500_wdt , nowayout ) ;
/* disable auto off on sleep */
prcmu_config_a9wdog ( PRCMU_WDOG_CPU1 , false ) ;
/* set HW initial value */
prcmu_load_a9wdog ( PRCMU_WDOG_ALL , timeout * 1000 ) ;
2019-04-10 19:27:55 +03:00
ret = devm_watchdog_register_device ( dev , & ux500_wdt ) ;
2013-01-29 12:57:19 +04:00
if ( ret )
return ret ;
2019-04-10 19:27:55 +03:00
dev_info ( dev , " initialized \n " ) ;
2013-01-29 12:57:19 +04:00
return 0 ;
}
# ifdef CONFIG_PM
static int ux500_wdt_suspend ( struct platform_device * pdev ,
pm_message_t state )
{
if ( watchdog_active ( & ux500_wdt ) ) {
ux500_wdt_stop ( & ux500_wdt ) ;
prcmu_config_a9wdog ( PRCMU_WDOG_CPU1 , true ) ;
prcmu_load_a9wdog ( PRCMU_WDOG_ALL , timeout * 1000 ) ;
ux500_wdt_start ( & ux500_wdt ) ;
}
return 0 ;
}
static int ux500_wdt_resume ( struct platform_device * pdev )
{
if ( watchdog_active ( & ux500_wdt ) ) {
ux500_wdt_stop ( & ux500_wdt ) ;
prcmu_config_a9wdog ( PRCMU_WDOG_CPU1 , false ) ;
prcmu_load_a9wdog ( PRCMU_WDOG_ALL , timeout * 1000 ) ;
ux500_wdt_start ( & ux500_wdt ) ;
}
return 0 ;
}
# else
# define ux500_wdt_suspend NULL
# define ux500_wdt_resume NULL
# endif
static struct platform_driver ux500_wdt_driver = {
. probe = ux500_wdt_probe ,
. suspend = ux500_wdt_suspend ,
. resume = ux500_wdt_resume ,
. driver = {
. name = " ux500_wdt " ,
} ,
} ;
module_platform_driver ( ux500_wdt_driver ) ;
MODULE_AUTHOR ( " Jonas Aaberg <jonas.aberg@stericsson.com> " ) ;
MODULE_DESCRIPTION ( " Ux500 Watchdog Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:ux500_wdt " ) ;