2010-04-29 12:03:17 +04:00
/*
* Watchdog driver for IMX2 and later processors
*
* Copyright ( C ) 2010 Wolfram Sang , Pengutronix e . K . < w . sang @ pengutronix . de >
2014-01-13 15:58:34 +04:00
* Copyright ( C ) 2014 Freescale Semiconductor , Inc .
2010-04-29 12:03:17 +04:00
*
* some parts adapted by similar drivers from Darius Augulis and Vladimir
* Zapolskiy , additional improvements by Wim Van Sebroeck .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation .
*
* NOTE : MX1 has a slightly different Watchdog than MX2 and later :
*
* MX1 : MX2 + :
* - - - - - - - - -
* Registers : 32 - bit 16 - bit
* Stopable timer : Yes No
* Need to enable clk : No Yes
* Halt on suspend : Manual Can be automatic
*/
2014-04-04 05:33:24 +04:00
# include <linux/clk.h>
2010-04-29 12:03:17 +04:00
# include <linux/init.h>
2014-04-04 05:33:24 +04:00
# include <linux/io.h>
# include <linux/jiffies.h>
2010-04-29 12:03:17 +04:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
2014-06-03 06:45:14 +04:00
# include <linux/of_address.h>
2010-04-29 12:03:17 +04:00
# include <linux/platform_device.h>
2014-04-04 05:33:25 +04:00
# include <linux/regmap.h>
2010-04-29 12:03:17 +04:00
# include <linux/timer.h>
2014-04-04 05:33:24 +04:00
# include <linux/watchdog.h>
2010-04-29 12:03:17 +04:00
# define DRIVER_NAME "imx2-wdt"
# define IMX2_WDT_WCR 0x00 /* Control Register */
# define IMX2_WDT_WCR_WT (0xFF << 8) /* -> Watchdog Timeout Field */
# define IMX2_WDT_WCR_WRE (1 << 3) /* -> WDOG Reset Enable */
# define IMX2_WDT_WCR_WDE (1 << 2) /* -> Watchdog Enable */
2014-01-13 15:58:34 +04:00
# define IMX2_WDT_WCR_WDZST (1 << 0) /* -> Watchdog timer Suspend */
2010-04-29 12:03:17 +04:00
# define IMX2_WDT_WSR 0x02 /* Service Register */
# define IMX2_WDT_SEQ1 0x5555 /* -> service sequence 1 */
# define IMX2_WDT_SEQ2 0xAAAA /* -> service sequence 2 */
2012-02-16 16:17:45 +04:00
# define IMX2_WDT_WRSR 0x04 /* Reset Status Register */
# define IMX2_WDT_WRSR_TOUT (1 << 1) /* -> Reset due to Timeout */
2010-04-29 12:03:17 +04:00
# define IMX2_WDT_MAX_TIME 128
# define IMX2_WDT_DEFAULT_TIME 60 /* in seconds */
# define WDOG_SEC_TO_COUNT(s) ((s * 2 - 1) << 8)
2014-04-11 10:57:14 +04:00
struct imx2_wdt_device {
2010-04-29 12:03:17 +04:00
struct clk * clk ;
2014-04-04 05:33:25 +04:00
struct regmap * regmap ;
2010-04-29 12:03:17 +04:00
struct timer_list timer ; /* Pings the watchdog when closed */
2014-04-11 10:57:14 +04:00
struct watchdog_device wdog ;
} ;
2010-04-29 12:03:17 +04:00
2012-03-05 19:51:11 +04:00
static bool nowayout = WATCHDOG_NOWAYOUT ;
module_param ( nowayout , bool , 0 ) ;
2010-04-29 12:03:17 +04:00
MODULE_PARM_DESC ( nowayout , " Watchdog cannot be stopped once started (default= "
__MODULE_STRING ( WATCHDOG_NOWAYOUT ) " ) " ) ;
static unsigned timeout = IMX2_WDT_DEFAULT_TIME ;
module_param ( timeout , uint , 0 ) ;
MODULE_PARM_DESC ( timeout , " Watchdog timeout in seconds (default= "
__MODULE_STRING ( IMX2_WDT_DEFAULT_TIME ) " ) " ) ;
static const struct watchdog_info imx2_wdt_info = {
. identity = " imx2+ watchdog " ,
. options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE ,
} ;
2014-04-11 10:57:14 +04:00
static inline void imx2_wdt_setup ( struct watchdog_device * wdog )
2010-04-29 12:03:17 +04:00
{
2014-04-11 10:57:14 +04:00
struct imx2_wdt_device * wdev = watchdog_get_drvdata ( wdog ) ;
2014-04-04 05:33:25 +04:00
u32 val ;
2014-04-11 10:57:14 +04:00
regmap_read ( wdev - > regmap , IMX2_WDT_WCR , & val ) ;
2010-04-29 12:03:17 +04:00
2014-01-13 15:58:34 +04:00
/* Suspend timer in low power mode, write once-only */
val | = IMX2_WDT_WCR_WDZST ;
2010-04-29 12:03:17 +04:00
/* Strip the old watchdog Time-Out value */
val & = ~ IMX2_WDT_WCR_WT ;
/* Generate reset if WDOG times out */
val & = ~ IMX2_WDT_WCR_WRE ;
/* Keep Watchdog Disabled */
val & = ~ IMX2_WDT_WCR_WDE ;
/* Set the watchdog's Time-Out value */
2014-04-11 10:57:14 +04:00
val | = WDOG_SEC_TO_COUNT ( wdog - > timeout ) ;
2010-04-29 12:03:17 +04:00
2014-04-11 10:57:14 +04:00
regmap_write ( wdev - > regmap , IMX2_WDT_WCR , val ) ;
2010-04-29 12:03:17 +04:00
/* enable the watchdog */
val | = IMX2_WDT_WCR_WDE ;
2014-04-11 10:57:14 +04:00
regmap_write ( wdev - > regmap , IMX2_WDT_WCR , val ) ;
2010-04-29 12:03:17 +04:00
}
2014-04-11 10:57:14 +04:00
static inline bool imx2_wdt_is_running ( struct imx2_wdt_device * wdev )
2010-04-29 12:03:17 +04:00
{
2014-04-11 10:57:14 +04:00
u32 val ;
2010-04-29 12:03:17 +04:00
2014-04-11 10:57:14 +04:00
regmap_read ( wdev - > regmap , IMX2_WDT_WCR , & val ) ;
return val & IMX2_WDT_WCR_WDE ;
2010-04-29 12:03:17 +04:00
}
2014-04-11 10:57:14 +04:00
static int imx2_wdt_ping ( struct watchdog_device * wdog )
2010-04-29 12:03:17 +04:00
{
2014-04-11 10:57:14 +04:00
struct imx2_wdt_device * wdev = watchdog_get_drvdata ( wdog ) ;
2010-04-29 12:03:17 +04:00
2014-04-11 10:57:14 +04:00
regmap_write ( wdev - > regmap , IMX2_WDT_WSR , IMX2_WDT_SEQ1 ) ;
regmap_write ( wdev - > regmap , IMX2_WDT_WSR , IMX2_WDT_SEQ2 ) ;
return 0 ;
2010-04-29 12:03:17 +04:00
}
2014-04-11 10:57:14 +04:00
static void imx2_wdt_timer_ping ( unsigned long arg )
2010-04-29 12:03:17 +04:00
{
2014-04-11 10:57:14 +04:00
struct watchdog_device * wdog = ( struct watchdog_device * ) arg ;
struct imx2_wdt_device * wdev = watchdog_get_drvdata ( wdog ) ;
/* ping it every wdog->timeout / 2 seconds to prevent reboot */
imx2_wdt_ping ( wdog ) ;
mod_timer ( & wdev - > timer , jiffies + wdog - > timeout * HZ / 2 ) ;
2010-04-29 12:03:17 +04:00
}
2014-04-11 10:57:14 +04:00
static int imx2_wdt_set_timeout ( struct watchdog_device * wdog ,
unsigned int new_timeout )
2010-04-29 12:03:17 +04:00
{
2014-04-11 10:57:14 +04:00
struct imx2_wdt_device * wdev = watchdog_get_drvdata ( wdog ) ;
regmap_update_bits ( wdev - > regmap , IMX2_WDT_WCR , IMX2_WDT_WCR_WT ,
2014-04-04 05:33:25 +04:00
WDOG_SEC_TO_COUNT ( new_timeout ) ) ;
2014-04-11 10:57:14 +04:00
return 0 ;
2010-04-29 12:03:17 +04:00
}
2014-04-11 10:57:14 +04:00
static int imx2_wdt_start ( struct watchdog_device * wdog )
2010-04-29 12:03:17 +04:00
{
2014-04-11 10:57:14 +04:00
struct imx2_wdt_device * wdev = watchdog_get_drvdata ( wdog ) ;
2010-04-29 12:03:17 +04:00
2014-04-11 10:57:14 +04:00
if ( imx2_wdt_is_running ( wdev ) ) {
/* delete the timer that pings the watchdog after close */
del_timer_sync ( & wdev - > timer ) ;
imx2_wdt_set_timeout ( wdog , wdog - > timeout ) ;
} else
imx2_wdt_setup ( wdog ) ;
return imx2_wdt_ping ( wdog ) ;
2010-04-29 12:03:17 +04:00
}
2014-04-11 10:57:14 +04:00
static int imx2_wdt_stop ( struct watchdog_device * wdog )
2010-04-29 12:03:17 +04:00
{
2014-04-11 10:57:14 +04:00
/*
* We don ' t need a clk_disable , it cannot be disabled once started .
* We use a timer to ping the watchdog while / dev / watchdog is closed
*/
imx2_wdt_timer_ping ( ( unsigned long ) wdog ) ;
2010-04-29 12:03:17 +04:00
return 0 ;
}
2014-04-11 10:57:14 +04:00
static inline void imx2_wdt_ping_if_active ( struct watchdog_device * wdog )
2010-04-29 12:03:17 +04:00
{
2014-04-11 10:57:14 +04:00
struct imx2_wdt_device * wdev = watchdog_get_drvdata ( wdog ) ;
2010-04-29 12:03:17 +04:00
2014-04-11 10:57:14 +04:00
if ( imx2_wdt_is_running ( wdev ) ) {
imx2_wdt_set_timeout ( wdog , wdog - > timeout ) ;
imx2_wdt_timer_ping ( ( unsigned long ) wdog ) ;
2010-04-29 12:03:17 +04:00
}
}
2014-04-11 10:57:14 +04:00
static struct watchdog_ops imx2_wdt_ops = {
2010-04-29 12:03:17 +04:00
. owner = THIS_MODULE ,
2014-04-11 10:57:14 +04:00
. start = imx2_wdt_start ,
. stop = imx2_wdt_stop ,
. ping = imx2_wdt_ping ,
. set_timeout = imx2_wdt_set_timeout ,
2010-04-29 12:03:17 +04:00
} ;
2014-04-04 05:33:25 +04:00
static struct regmap_config imx2_wdt_regmap_config = {
. reg_bits = 16 ,
. reg_stride = 2 ,
. val_bits = 16 ,
. max_register = 0x8 ,
} ;
2010-04-29 12:03:17 +04:00
static int __init imx2_wdt_probe ( struct platform_device * pdev )
{
2014-06-03 06:45:14 +04:00
struct device_node * np = pdev - > dev . of_node ;
2014-04-11 10:57:14 +04:00
struct imx2_wdt_device * wdev ;
struct watchdog_device * wdog ;
2010-04-29 12:03:17 +04:00
struct resource * res ;
2014-04-04 05:33:25 +04:00
void __iomem * base ;
2014-06-03 06:45:14 +04:00
bool big_endian ;
2014-04-04 05:33:25 +04:00
int ret ;
2014-04-11 10:57:14 +04:00
u32 val ;
wdev = devm_kzalloc ( & pdev - > dev , sizeof ( * wdev ) , GFP_KERNEL ) ;
if ( ! wdev )
return - ENOMEM ;
2010-04-29 12:03:17 +04:00
2014-06-03 06:45:14 +04:00
big_endian = of_property_read_bool ( np , " big-endian " ) ;
if ( big_endian )
imx2_wdt_regmap_config . val_format_endian = REGMAP_ENDIAN_BIG ;
2010-04-29 12:03:17 +04:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2014-04-04 05:33:25 +04:00
base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( base ) )
return PTR_ERR ( base ) ;
2014-04-11 10:57:14 +04:00
wdev - > regmap = devm_regmap_init_mmio_clk ( & pdev - > dev , NULL , base ,
& imx2_wdt_regmap_config ) ;
if ( IS_ERR ( wdev - > regmap ) ) {
2014-04-04 05:33:25 +04:00
dev_err ( & pdev - > dev , " regmap init failed \n " ) ;
2014-04-11 10:57:14 +04:00
return PTR_ERR ( wdev - > regmap ) ;
2014-04-04 05:33:25 +04:00
}
2010-04-29 12:03:17 +04:00
2014-04-11 10:57:14 +04:00
wdev - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( wdev - > clk ) ) {
2010-04-29 12:03:17 +04:00
dev_err ( & pdev - > dev , " can't get Watchdog clock \n " ) ;
2014-04-11 10:57:14 +04:00
return PTR_ERR ( wdev - > clk ) ;
2010-04-29 12:03:17 +04:00
}
2014-04-11 10:57:14 +04:00
wdog = & wdev - > wdog ;
wdog - > info = & imx2_wdt_info ;
wdog - > ops = & imx2_wdt_ops ;
wdog - > min_timeout = 1 ;
wdog - > max_timeout = IMX2_WDT_MAX_TIME ;
2010-04-29 12:03:17 +04:00
2014-04-11 10:57:14 +04:00
clk_prepare_enable ( wdev - > clk ) ;
2010-04-29 12:03:17 +04:00
2014-04-11 10:57:14 +04:00
regmap_read ( wdev - > regmap , IMX2_WDT_WRSR , & val ) ;
wdog - > bootstatus = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0 ;
2010-04-29 12:03:17 +04:00
2014-04-11 10:57:14 +04:00
wdog - > timeout = clamp_t ( unsigned , timeout , 1 , IMX2_WDT_MAX_TIME ) ;
if ( wdog - > timeout ! = timeout )
dev_warn ( & pdev - > dev , " Initial timeout out of range! Clamped from %u to %u \n " ,
timeout , wdog - > timeout ) ;
platform_set_drvdata ( pdev , wdog ) ;
watchdog_set_drvdata ( wdog , wdev ) ;
watchdog_set_nowayout ( wdog , nowayout ) ;
watchdog_init_timeout ( wdog , timeout , & pdev - > dev ) ;
setup_timer ( & wdev - > timer , imx2_wdt_timer_ping , ( unsigned long ) wdog ) ;
2010-04-29 12:03:17 +04:00
2014-04-11 10:57:14 +04:00
imx2_wdt_ping_if_active ( wdog ) ;
ret = watchdog_register_device ( wdog ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " cannot register watchdog device \n " ) ;
return ret ;
}
dev_info ( & pdev - > dev , " timeout %d sec (nowayout=%d) \n " ,
wdog - > timeout , nowayout ) ;
return 0 ;
2010-04-29 12:03:17 +04:00
}
static int __exit imx2_wdt_remove ( struct platform_device * pdev )
{
2014-04-11 10:57:14 +04:00
struct watchdog_device * wdog = platform_get_drvdata ( pdev ) ;
struct imx2_wdt_device * wdev = watchdog_get_drvdata ( wdog ) ;
2010-04-29 12:03:17 +04:00
2014-04-11 10:57:14 +04:00
watchdog_unregister_device ( wdog ) ;
2010-04-29 12:03:17 +04:00
2014-04-11 10:57:14 +04:00
if ( imx2_wdt_is_running ( wdev ) ) {
del_timer_sync ( & wdev - > timer ) ;
imx2_wdt_ping ( wdog ) ;
dev_crit ( & pdev - > dev , " Device removed: Expect reboot! \n " ) ;
2013-04-29 13:15:53 +04:00
}
2010-04-29 12:03:17 +04:00
return 0 ;
}
static void imx2_wdt_shutdown ( struct platform_device * pdev )
{
2014-04-11 10:57:14 +04:00
struct watchdog_device * wdog = platform_get_drvdata ( pdev ) ;
struct imx2_wdt_device * wdev = watchdog_get_drvdata ( wdog ) ;
if ( imx2_wdt_is_running ( wdev ) ) {
/*
* We are running , we need to delete the timer but will
* give max timeout before reboot will take place
*/
del_timer_sync ( & wdev - > timer ) ;
imx2_wdt_set_timeout ( wdog , IMX2_WDT_MAX_TIME ) ;
imx2_wdt_ping ( wdog ) ;
dev_crit ( & pdev - > dev , " Device shutdown: Expect reboot! \n " ) ;
2010-04-29 12:03:17 +04:00
}
}
2011-07-18 07:15:21 +04:00
static const struct of_device_id imx2_wdt_dt_ids [ ] = {
{ . compatible = " fsl,imx21-wdt " , } ,
{ /* sentinel */ }
} ;
2013-07-29 11:38:18 +04:00
MODULE_DEVICE_TABLE ( of , imx2_wdt_dt_ids ) ;
2011-07-18 07:15:21 +04:00
2010-04-29 12:03:17 +04:00
static struct platform_driver imx2_wdt_driver = {
. remove = __exit_p ( imx2_wdt_remove ) ,
. shutdown = imx2_wdt_shutdown ,
. driver = {
. name = DRIVER_NAME ,
. owner = THIS_MODULE ,
2011-07-18 07:15:21 +04:00
. of_match_table = imx2_wdt_dt_ids ,
2010-04-29 12:03:17 +04:00
} ,
} ;
2013-01-09 15:15:27 +04:00
module_platform_driver_probe ( imx2_wdt_driver , imx2_wdt_probe ) ;
2010-04-29 12:03:17 +04:00
MODULE_AUTHOR ( " Wolfram Sang " ) ;
MODULE_DESCRIPTION ( " Watchdog driver for IMX2 and later " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform: " DRIVER_NAME ) ;