2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2016-01-04 22:36:38 +03:00
/*
* Ralink MT7621 / MT7628 built - in hardware watchdog timer
*
2016-12-20 21:56:59 +03:00
* Copyright ( C ) 2014 John Crispin < john @ phrozen . org >
2016-01-04 22:36:38 +03:00
*
* This driver was based on : drivers / watchdog / rt2880_wdt . c
*/
# include <linux/clk.h>
# include <linux/reset.h>
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/watchdog.h>
# include <linux/moduleparam.h>
# include <linux/platform_device.h>
2018-12-30 06:21:52 +03:00
# include <linux/mod_devicetable.h>
2023-02-14 13:39:36 +03:00
# include <linux/mfd/syscon.h>
# include <linux/regmap.h>
2016-01-04 22:36:38 +03:00
# define SYSC_RSTSTAT 0x38
# define WDT_RST_CAUSE BIT(1)
# define RALINK_WDT_TIMEOUT 30
# define TIMER_REG_TMRSTAT 0x00
# define TIMER_REG_TMR1LOAD 0x24
# define TIMER_REG_TMR1CTL 0x20
# define TMR1CTL_ENABLE BIT(7)
# define TMR1CTL_RESTART BIT(9)
# define TMR1CTL_PRESCALE_SHIFT 16
2023-02-14 13:39:35 +03:00
struct mt7621_wdt_data {
void __iomem * base ;
struct reset_control * rst ;
2023-02-14 13:39:36 +03:00
struct regmap * sysc ;
2023-02-14 13:39:35 +03:00
struct watchdog_device wdt ;
} ;
2016-01-04 22:36:38 +03:00
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 ) " ) " ) ;
2023-02-14 13:39:35 +03:00
static inline void rt_wdt_w32 ( void __iomem * base , unsigned int reg , u32 val )
2016-01-04 22:36:38 +03:00
{
2023-02-14 13:39:35 +03:00
iowrite32 ( val , base + reg ) ;
2016-01-04 22:36:38 +03:00
}
2023-02-14 13:39:35 +03:00
static inline u32 rt_wdt_r32 ( void __iomem * base , unsigned int reg )
2016-01-04 22:36:38 +03:00
{
2023-02-14 13:39:35 +03:00
return ioread32 ( base + reg ) ;
2016-01-04 22:36:38 +03:00
}
static int mt7621_wdt_ping ( struct watchdog_device * w )
{
2023-02-14 13:39:35 +03:00
struct mt7621_wdt_data * drvdata = watchdog_get_drvdata ( w ) ;
rt_wdt_w32 ( drvdata - > base , TIMER_REG_TMRSTAT , TMR1CTL_RESTART ) ;
2016-01-04 22:36:38 +03:00
return 0 ;
}
static int mt7621_wdt_set_timeout ( struct watchdog_device * w , unsigned int t )
{
2023-02-14 13:39:35 +03:00
struct mt7621_wdt_data * drvdata = watchdog_get_drvdata ( w ) ;
2016-01-04 22:36:38 +03:00
w - > timeout = t ;
2023-02-14 13:39:35 +03:00
rt_wdt_w32 ( drvdata - > base , TIMER_REG_TMR1LOAD , t * 1000 ) ;
2016-01-04 22:36:38 +03:00
mt7621_wdt_ping ( w ) ;
return 0 ;
}
static int mt7621_wdt_start ( struct watchdog_device * w )
{
2023-02-14 13:39:35 +03:00
struct mt7621_wdt_data * drvdata = watchdog_get_drvdata ( w ) ;
2016-01-04 22:36:38 +03:00
u32 t ;
/* set the prescaler to 1ms == 1000us */
2023-02-14 13:39:35 +03:00
rt_wdt_w32 ( drvdata - > base , TIMER_REG_TMR1CTL , 1000 < < TMR1CTL_PRESCALE_SHIFT ) ;
2016-01-04 22:36:38 +03:00
mt7621_wdt_set_timeout ( w , w - > timeout ) ;
2023-02-14 13:39:35 +03:00
t = rt_wdt_r32 ( drvdata - > base , TIMER_REG_TMR1CTL ) ;
2016-01-04 22:36:38 +03:00
t | = TMR1CTL_ENABLE ;
2023-02-14 13:39:35 +03:00
rt_wdt_w32 ( drvdata - > base , TIMER_REG_TMR1CTL , t ) ;
2016-01-04 22:36:38 +03:00
return 0 ;
}
static int mt7621_wdt_stop ( struct watchdog_device * w )
{
2023-02-14 13:39:35 +03:00
struct mt7621_wdt_data * drvdata = watchdog_get_drvdata ( w ) ;
2016-01-04 22:36:38 +03:00
u32 t ;
mt7621_wdt_ping ( w ) ;
2023-02-14 13:39:35 +03:00
t = rt_wdt_r32 ( drvdata - > base , TIMER_REG_TMR1CTL ) ;
2016-01-04 22:36:38 +03:00
t & = ~ TMR1CTL_ENABLE ;
2023-02-14 13:39:35 +03:00
rt_wdt_w32 ( drvdata - > base , TIMER_REG_TMR1CTL , t ) ;
2016-01-04 22:36:38 +03:00
return 0 ;
}
2023-02-14 13:39:36 +03:00
static int mt7621_wdt_bootcause ( struct mt7621_wdt_data * d )
2016-01-04 22:36:38 +03:00
{
2023-02-14 13:39:36 +03:00
u32 val ;
regmap_read ( d - > sysc , SYSC_RSTSTAT , & val ) ;
if ( val & WDT_RST_CAUSE )
2016-01-04 22:36:38 +03:00
return WDIOF_CARDRESET ;
return 0 ;
}
2018-01-12 12:44:53 +03:00
static int mt7621_wdt_is_running ( struct watchdog_device * w )
{
2023-02-14 13:39:35 +03:00
struct mt7621_wdt_data * drvdata = watchdog_get_drvdata ( w ) ;
return ! ! ( rt_wdt_r32 ( drvdata - > base , TIMER_REG_TMR1CTL ) & TMR1CTL_ENABLE ) ;
2018-01-12 12:44:53 +03:00
}
2017-08-04 00:21:31 +03:00
static const struct watchdog_info mt7621_wdt_info = {
2016-01-04 22:36:38 +03:00
. identity = " Mediatek Watchdog " ,
. options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE ,
} ;
2017-01-28 10:41:17 +03:00
static const struct watchdog_ops mt7621_wdt_ops = {
2016-01-04 22:36:38 +03:00
. owner = THIS_MODULE ,
. start = mt7621_wdt_start ,
. stop = mt7621_wdt_stop ,
. ping = mt7621_wdt_ping ,
. set_timeout = mt7621_wdt_set_timeout ,
} ;
static int mt7621_wdt_probe ( struct platform_device * pdev )
{
2023-02-14 13:39:36 +03:00
struct device_node * np = pdev - > dev . of_node ;
2019-04-10 19:27:59 +03:00
struct device * dev = & pdev - > dev ;
2023-02-14 13:39:35 +03:00
struct watchdog_device * mt7621_wdt ;
struct mt7621_wdt_data * drvdata ;
int err ;
drvdata = devm_kzalloc ( dev , sizeof ( * drvdata ) , GFP_KERNEL ) ;
if ( ! drvdata )
return - ENOMEM ;
2016-01-04 22:36:38 +03:00
2023-02-14 13:39:36 +03:00
drvdata - > sysc = syscon_regmap_lookup_by_phandle ( np , " mediatek,sysctl " ) ;
if ( IS_ERR ( drvdata - > sysc ) ) {
drvdata - > sysc = syscon_regmap_lookup_by_compatible ( " mediatek,mt7621-sysc " ) ;
if ( IS_ERR ( drvdata - > sysc ) )
return PTR_ERR ( drvdata - > sysc ) ;
}
2023-02-14 13:39:35 +03:00
drvdata - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( drvdata - > base ) )
return PTR_ERR ( drvdata - > base ) ;
2016-01-04 22:36:38 +03:00
2023-02-14 13:39:35 +03:00
drvdata - > rst = devm_reset_control_get_exclusive ( dev , NULL ) ;
if ( ! IS_ERR ( drvdata - > rst ) )
reset_control_deassert ( drvdata - > rst ) ;
2016-01-04 22:36:38 +03:00
2023-02-14 13:39:35 +03:00
mt7621_wdt = & drvdata - > wdt ;
mt7621_wdt - > info = & mt7621_wdt_info ;
mt7621_wdt - > ops = & mt7621_wdt_ops ;
mt7621_wdt - > min_timeout = 1 ;
mt7621_wdt - > max_timeout = 0xfffful / 1000 ;
mt7621_wdt - > parent = dev ;
2023-02-14 13:39:36 +03:00
mt7621_wdt - > bootstatus = mt7621_wdt_bootcause ( drvdata ) ;
2023-02-14 13:39:35 +03:00
watchdog_init_timeout ( mt7621_wdt , mt7621_wdt - > max_timeout , dev ) ;
watchdog_set_nowayout ( mt7621_wdt , nowayout ) ;
watchdog_set_drvdata ( mt7621_wdt , drvdata ) ;
if ( mt7621_wdt_is_running ( mt7621_wdt ) ) {
2018-01-12 12:44:53 +03:00
/*
* Make sure to apply timeout from watchdog core , taking
* the prescaler of this driver here into account ( the
* boot loader might be using a different prescaler ) .
*
* To avoid spurious resets because of different scaling ,
* we first disable the watchdog , set the new prescaler
* and timeout , and then re - enable the watchdog .
*/
2023-02-14 13:39:35 +03:00
mt7621_wdt_stop ( mt7621_wdt ) ;
mt7621_wdt_start ( mt7621_wdt ) ;
set_bit ( WDOG_HW_RUNNING , & mt7621_wdt - > status ) ;
2018-01-12 12:44:53 +03:00
}
2016-01-04 22:36:38 +03:00
2023-02-14 13:39:35 +03:00
err = devm_watchdog_register_device ( dev , & drvdata - > wdt ) ;
if ( err )
return err ;
platform_set_drvdata ( pdev , drvdata ) ;
return 0 ;
2016-01-04 22:36:38 +03:00
}
static void mt7621_wdt_shutdown ( struct platform_device * pdev )
{
2023-02-14 13:39:35 +03:00
struct mt7621_wdt_data * drvdata = platform_get_drvdata ( pdev ) ;
mt7621_wdt_stop ( & drvdata - > wdt ) ;
2016-01-04 22:36:38 +03:00
}
static const struct of_device_id mt7621_wdt_match [ ] = {
{ . compatible = " mediatek,mt7621-wdt " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , mt7621_wdt_match ) ;
static struct platform_driver mt7621_wdt_driver = {
. probe = mt7621_wdt_probe ,
. shutdown = mt7621_wdt_shutdown ,
. driver = {
. name = KBUILD_MODNAME ,
. of_match_table = mt7621_wdt_match ,
} ,
} ;
module_platform_driver ( mt7621_wdt_driver ) ;
MODULE_DESCRIPTION ( " MediaTek MT762x hardware watchdog driver " ) ;
2016-12-20 21:56:59 +03:00
MODULE_AUTHOR ( " John Crispin <john@phrozen.org " ) ;
2016-01-04 22:36:38 +03:00
MODULE_LICENSE ( " GPL v2 " ) ;