2011-05-06 01:00:23 +04:00
/*
* 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 .
*
2016-12-20 21:56:59 +03:00
* Copyright ( C ) 2010 John Crispin < john @ phrozen . org >
2017-08-20 01:18:11 +03:00
* Copyright ( C ) 2017 Hauke Mehrtens < hauke @ hauke - m . de >
2011-05-06 01:00:23 +04:00
* Based on EP93xx wdt driver
*/
# include <linux/module.h>
2018-09-14 00:32:09 +03:00
# include <linux/bitops.h>
2011-05-06 01:00:23 +04:00
# include <linux/watchdog.h>
2012-04-12 23:21:56 +04:00
# include <linux/of_platform.h>
2011-05-06 01:00:23 +04:00
# include <linux/uaccess.h>
# include <linux/clk.h>
# include <linux/io.h>
2017-08-20 01:18:11 +03:00
# include <linux/regmap.h>
# include <linux/mfd/syscon.h>
2011-05-06 01:00:23 +04:00
2012-04-12 23:21:56 +04:00
# include <lantiq_soc.h>
2011-05-06 01:00:23 +04:00
2017-08-20 01:18:11 +03:00
# define LTQ_XRX_RCU_RST_STAT 0x0014
# define LTQ_XRX_RCU_RST_STAT_WDT BIT(31)
/* CPU0 Reset Source Register */
# define LTQ_FALCON_SYS1_CPU0RS 0x0060
/* reset cause mask */
# define LTQ_FALCON_SYS1_CPU0RS_MASK 0x0007
# define LTQ_FALCON_SYS1_CPU0RS_WDT 0x02
2012-04-12 23:21:56 +04:00
/*
* Section 3.4 of the datasheet
2011-05-06 01:00:23 +04:00
* The password sequence protects the WDT control register from unintended
* write actions , which might cause malfunction of the WDT .
*
* essentially the following two magic passwords need to be written to allow
* IO access to the WDT core
*/
2018-09-14 00:32:09 +03:00
# define LTQ_WDT_CR_PW1 0x00BE0000
# define LTQ_WDT_CR_PW2 0x00DC0000
# define LTQ_WDT_CR 0x0 /* watchdog control register */
# define LTQ_WDT_CR_GEN BIT(31) /* enable bit */
/* Pre-warning limit set to 1/16 of max WDT period */
# define LTQ_WDT_CR_PWL (0x3 << 26)
/* set clock divider to 0x40000 */
# define LTQ_WDT_CR_CLKDIV (0x3 << 24)
# define LTQ_WDT_CR_PW_MASK GENMASK(23, 16) /* Password field */
# define LTQ_WDT_CR_MAX_TIMEOUT ((1 << 16) - 1) /* The reload field is 16 bit */
2018-09-14 00:32:10 +03:00
# define LTQ_WDT_SR 0x8 /* watchdog status register */
# define LTQ_WDT_SR_EN BIT(31) /* Enable */
2018-09-14 00:32:11 +03:00
# define LTQ_WDT_SR_VALUE_MASK GENMASK(15, 0) /* Timer value */
2011-05-06 01:00:23 +04:00
# define LTQ_WDT_DIVIDER 0x40000
2012-03-05 19:51:11 +04:00
static bool nowayout = WATCHDOG_NOWAYOUT ;
2011-05-06 01:00:23 +04:00
2018-09-14 00:32:10 +03:00
struct ltq_wdt_hw {
int ( * bootstatus_get ) ( struct device * dev ) ;
} ;
2011-05-06 01:00:23 +04:00
2018-09-14 00:32:10 +03:00
struct ltq_wdt_priv {
struct watchdog_device wdt ;
void __iomem * membase ;
unsigned long clk_rate ;
} ;
2011-05-06 01:00:23 +04:00
2018-09-14 00:32:10 +03:00
static u32 ltq_wdt_r32 ( struct ltq_wdt_priv * priv , u32 offset )
2011-05-06 01:00:23 +04:00
{
2018-09-14 00:32:10 +03:00
return __raw_readl ( priv - > membase + offset ) ;
2011-05-06 01:00:23 +04:00
}
2018-09-14 00:32:10 +03:00
static void ltq_wdt_w32 ( struct ltq_wdt_priv * priv , u32 val , u32 offset )
2011-05-06 01:00:23 +04:00
{
2018-09-14 00:32:10 +03:00
__raw_writel ( val , priv - > membase + offset ) ;
2011-05-06 01:00:23 +04:00
}
2018-09-14 00:32:10 +03:00
static void ltq_wdt_mask ( struct ltq_wdt_priv * priv , u32 clear , u32 set ,
u32 offset )
2011-05-06 01:00:23 +04:00
{
2018-09-14 00:32:10 +03:00
u32 val = ltq_wdt_r32 ( priv , offset ) ;
val & = ~ ( clear ) ;
val | = set ;
ltq_wdt_w32 ( priv , val , offset ) ;
}
2011-05-06 01:00:23 +04:00
2018-09-14 00:32:10 +03:00
static struct ltq_wdt_priv * ltq_wdt_get_priv ( struct watchdog_device * wdt )
{
return container_of ( wdt , struct ltq_wdt_priv , wdt ) ;
2011-05-06 01:00:23 +04:00
}
2018-09-14 00:32:10 +03:00
static struct watchdog_info ltq_wdt_info = {
2011-05-06 01:00:23 +04:00
. options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
2018-09-14 00:32:10 +03:00
WDIOF_CARDRESET ,
2011-05-06 01:00:23 +04:00
. identity = " ltq_wdt " ,
} ;
2018-09-14 00:32:10 +03:00
static int ltq_wdt_start ( struct watchdog_device * wdt )
2011-05-06 01:00:23 +04:00
{
2018-09-14 00:32:10 +03:00
struct ltq_wdt_priv * priv = ltq_wdt_get_priv ( wdt ) ;
u32 timeout ;
timeout = wdt - > timeout * priv - > clk_rate ;
ltq_wdt_mask ( priv , LTQ_WDT_CR_PW_MASK , LTQ_WDT_CR_PW1 , LTQ_WDT_CR ) ;
/* write the second magic plus the configuration and new timeout */
ltq_wdt_mask ( priv , LTQ_WDT_CR_PW_MASK | LTQ_WDT_CR_MAX_TIMEOUT ,
LTQ_WDT_CR_GEN | LTQ_WDT_CR_PWL | LTQ_WDT_CR_CLKDIV |
LTQ_WDT_CR_PW2 | timeout ,
LTQ_WDT_CR ) ;
return 0 ;
2011-05-06 01:00:23 +04:00
}
2018-09-14 00:32:10 +03:00
static int ltq_wdt_stop ( struct watchdog_device * wdt )
2011-05-06 01:00:23 +04:00
{
2018-09-14 00:32:10 +03:00
struct ltq_wdt_priv * priv = ltq_wdt_get_priv ( wdt ) ;
ltq_wdt_mask ( priv , LTQ_WDT_CR_PW_MASK , LTQ_WDT_CR_PW1 , LTQ_WDT_CR ) ;
ltq_wdt_mask ( priv , LTQ_WDT_CR_GEN | LTQ_WDT_CR_PW_MASK ,
LTQ_WDT_CR_PW2 , LTQ_WDT_CR ) ;
2011-05-06 01:00:23 +04:00
2018-09-14 00:32:10 +03:00
return 0 ;
2011-05-06 01:00:23 +04:00
}
2018-09-14 00:32:10 +03:00
static int ltq_wdt_ping ( struct watchdog_device * wdt )
2011-05-06 01:00:23 +04:00
{
2018-09-14 00:32:10 +03:00
struct ltq_wdt_priv * priv = ltq_wdt_get_priv ( wdt ) ;
u32 timeout ;
timeout = wdt - > timeout * priv - > clk_rate ;
ltq_wdt_mask ( priv , LTQ_WDT_CR_PW_MASK , LTQ_WDT_CR_PW1 , LTQ_WDT_CR ) ;
/* write the second magic plus the configuration and new timeout */
ltq_wdt_mask ( priv , LTQ_WDT_CR_PW_MASK | LTQ_WDT_CR_MAX_TIMEOUT ,
LTQ_WDT_CR_PW2 | timeout , LTQ_WDT_CR ) ;
2011-05-06 01:00:23 +04:00
return 0 ;
}
2018-09-14 00:32:11 +03:00
static unsigned int ltq_wdt_get_timeleft ( struct watchdog_device * wdt )
{
struct ltq_wdt_priv * priv = ltq_wdt_get_priv ( wdt ) ;
u64 timeout ;
timeout = ltq_wdt_r32 ( priv , LTQ_WDT_SR ) & LTQ_WDT_SR_VALUE_MASK ;
return do_div ( timeout , priv - > clk_rate ) ;
}
2018-09-14 00:32:10 +03:00
static const struct watchdog_ops ltq_wdt_ops = {
2011-05-06 01:00:23 +04:00
. owner = THIS_MODULE ,
2018-09-14 00:32:10 +03:00
. start = ltq_wdt_start ,
. stop = ltq_wdt_stop ,
. ping = ltq_wdt_ping ,
2018-09-14 00:32:11 +03:00
. get_timeleft = ltq_wdt_get_timeleft ,
2011-05-06 01:00:23 +04:00
} ;
2018-09-14 00:32:10 +03:00
static int ltq_wdt_xrx_bootstatus_get ( struct device * dev )
2017-08-20 01:18:11 +03:00
{
struct regmap * rcu_regmap ;
u32 val ;
int err ;
rcu_regmap = syscon_regmap_lookup_by_phandle ( dev - > of_node , " regmap " ) ;
if ( IS_ERR ( rcu_regmap ) )
return PTR_ERR ( rcu_regmap ) ;
err = regmap_read ( rcu_regmap , LTQ_XRX_RCU_RST_STAT , & val ) ;
if ( err )
return err ;
if ( val & LTQ_XRX_RCU_RST_STAT_WDT )
2018-09-14 00:32:10 +03:00
return WDIOF_CARDRESET ;
2017-08-20 01:18:11 +03:00
return 0 ;
}
2018-09-14 00:32:10 +03:00
static int ltq_wdt_falcon_bootstatus_get ( struct device * dev )
2017-08-20 01:18:11 +03:00
{
struct regmap * rcu_regmap ;
u32 val ;
int err ;
rcu_regmap = syscon_regmap_lookup_by_phandle ( dev - > of_node ,
" lantiq,rcu " ) ;
if ( IS_ERR ( rcu_regmap ) )
return PTR_ERR ( rcu_regmap ) ;
err = regmap_read ( rcu_regmap , LTQ_FALCON_SYS1_CPU0RS , & val ) ;
if ( err )
return err ;
if ( ( val & LTQ_FALCON_SYS1_CPU0RS_MASK ) = = LTQ_FALCON_SYS1_CPU0RS_WDT )
2018-09-14 00:32:10 +03:00
return WDIOF_CARDRESET ;
2017-08-20 01:18:11 +03:00
return 0 ;
}
2018-09-14 00:32:10 +03:00
static int ltq_wdt_probe ( struct platform_device * pdev )
2011-05-06 01:00:23 +04:00
{
2018-09-14 00:32:10 +03:00
struct device * dev = & pdev - > dev ;
struct ltq_wdt_priv * priv ;
struct watchdog_device * wdt ;
struct resource * res ;
2011-05-06 01:00:23 +04:00
struct clk * clk ;
2018-09-14 00:32:10 +03:00
const struct ltq_wdt_hw * ltq_wdt_hw ;
2017-08-20 01:18:11 +03:00
int ret ;
2018-09-14 00:32:10 +03:00
u32 status ;
2011-05-06 01:00:23 +04:00
2018-09-14 00:32:10 +03:00
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
2011-05-06 01:00:23 +04:00
2018-09-14 00:32:10 +03:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
priv - > membase = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( priv - > membase ) )
return PTR_ERR ( priv - > membase ) ;
2017-08-20 01:18:11 +03:00
2011-05-06 01:00:23 +04:00
/* we do not need to enable the clock as it is always running */
2012-04-12 23:21:56 +04:00
clk = clk_get_io ( ) ;
2018-09-14 00:32:10 +03:00
priv - > clk_rate = clk_get_rate ( clk ) / LTQ_WDT_DIVIDER ;
if ( ! priv - > clk_rate ) {
dev_err ( dev , " clock rate less than divider %i \n " ,
LTQ_WDT_DIVIDER ) ;
return - EINVAL ;
2012-04-12 23:21:56 +04:00
}
2011-05-06 01:00:23 +04:00
2018-09-14 00:32:10 +03:00
wdt = & priv - > wdt ;
wdt - > info = & ltq_wdt_info ;
wdt - > ops = & ltq_wdt_ops ;
wdt - > min_timeout = 1 ;
wdt - > max_timeout = LTQ_WDT_CR_MAX_TIMEOUT / priv - > clk_rate ;
wdt - > timeout = wdt - > max_timeout ;
wdt - > parent = dev ;
ltq_wdt_hw = of_device_get_match_data ( dev ) ;
if ( ltq_wdt_hw & & ltq_wdt_hw - > bootstatus_get ) {
ret = ltq_wdt_hw - > bootstatus_get ( dev ) ;
if ( ret > = 0 )
wdt - > bootstatus = ret ;
}
2011-05-06 01:00:23 +04:00
2018-09-14 00:32:10 +03:00
watchdog_set_nowayout ( wdt , nowayout ) ;
watchdog_init_timeout ( wdt , 0 , dev ) ;
status = ltq_wdt_r32 ( priv , LTQ_WDT_SR ) ;
if ( status & LTQ_WDT_SR_EN ) {
/*
* If the watchdog is already running overwrite it with our
* new settings . Stop is not needed as the start call will
* replace all settings anyway .
*/
ltq_wdt_start ( wdt ) ;
set_bit ( WDOG_HW_RUNNING , & wdt - > status ) ;
}
2011-05-06 01:00:23 +04:00
2018-09-14 00:32:10 +03:00
return devm_watchdog_register_device ( dev , wdt ) ;
2011-05-06 01:00:23 +04:00
}
2018-09-14 00:32:10 +03:00
static const struct ltq_wdt_hw ltq_wdt_xrx100 = {
. bootstatus_get = ltq_wdt_xrx_bootstatus_get ,
} ;
static const struct ltq_wdt_hw ltq_wdt_falcon = {
. bootstatus_get = ltq_wdt_falcon_bootstatus_get ,
} ;
2012-04-12 23:21:56 +04:00
static const struct of_device_id ltq_wdt_match [ ] = {
2018-09-14 00:32:10 +03:00
{ . compatible = " lantiq,wdt " , . data = NULL } ,
{ . compatible = " lantiq,xrx100-wdt " , . data = & ltq_wdt_xrx100 } ,
{ . compatible = " lantiq,falcon-wdt " , . data = & ltq_wdt_falcon } ,
2012-04-12 23:21:56 +04:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , ltq_wdt_match ) ;
2011-05-06 01:00:23 +04:00
static struct platform_driver ltq_wdt_driver = {
2012-04-12 23:21:56 +04:00
. probe = ltq_wdt_probe ,
2011-05-06 01:00:23 +04:00
. driver = {
2012-04-12 23:21:56 +04:00
. name = " wdt " ,
. of_match_table = ltq_wdt_match ,
2011-05-06 01:00:23 +04:00
} ,
} ;
2012-04-12 23:21:56 +04:00
module_platform_driver ( ltq_wdt_driver ) ;
2011-05-06 01:00:23 +04:00
2012-03-05 19:51:11 +04:00
module_param ( nowayout , bool , 0 ) ;
2011-05-06 01:00:23 +04:00
MODULE_PARM_DESC ( nowayout , " Watchdog cannot be stopped once started " ) ;
2016-12-20 21:56:59 +03:00
MODULE_AUTHOR ( " John Crispin <john@phrozen.org> " ) ;
2011-05-06 01:00:23 +04:00
MODULE_DESCRIPTION ( " Lantiq SoC Watchdog " ) ;
MODULE_LICENSE ( " GPL " ) ;