2023-04-20 13:42:30 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Window watchdog device driver for Xilinx Versal WWDT
*
* Copyright ( C ) 2022 - 2023 , Advanced Micro Devices , Inc .
*/
# include <linux/clk.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/ioport.h>
2023-08-15 22:06:50 +03:00
# include <linux/math64.h>
2023-07-27 02:33:00 +03:00
# include <linux/mod_devicetable.h>
2023-04-20 13:42:30 +03:00
# include <linux/module.h>
2023-07-27 02:33:00 +03:00
# include <linux/platform_device.h>
2023-04-20 13:42:30 +03:00
# include <linux/watchdog.h>
/* Max timeout is calculated at 100MHz source clock */
# define XWWDT_DEFAULT_TIMEOUT 42
# define XWWDT_MIN_TIMEOUT 1
/* Register offsets for the WWDT device */
# define XWWDT_MWR_OFFSET 0x00
# define XWWDT_ESR_OFFSET 0x04
# define XWWDT_FCR_OFFSET 0x08
# define XWWDT_FWR_OFFSET 0x0c
# define XWWDT_SWR_OFFSET 0x10
/* Master Write Control Register Masks */
# define XWWDT_MWR_MASK BIT(0)
/* Enable and Status Register Masks */
# define XWWDT_ESR_WINT_MASK BIT(16)
# define XWWDT_ESR_WSW_MASK BIT(8)
# define XWWDT_ESR_WEN_MASK BIT(0)
# define XWWDT_CLOSE_WINDOW_PERCENT 50
static int wwdt_timeout ;
static int closed_window_percent ;
module_param ( wwdt_timeout , int , 0 ) ;
MODULE_PARM_DESC ( wwdt_timeout ,
" Watchdog time in seconds. (default= "
__MODULE_STRING ( XWWDT_DEFAULT_TIMEOUT ) " ) " ) ;
module_param ( closed_window_percent , int , 0 ) ;
MODULE_PARM_DESC ( closed_window_percent ,
" Watchdog closed window percentage. (default= "
__MODULE_STRING ( XWWDT_CLOSE_WINDOW_PERCENT ) " ) " ) ;
/**
* struct xwwdt_device - Watchdog device structure
* @ base : base io address of WDT device
* @ spinlock : spinlock for IO register access
* @ xilinx_wwdt_wdd : watchdog device structure
* @ freq : source clock frequency of WWDT
* @ close_percent : Closed window percent
*/
struct xwwdt_device {
void __iomem * base ;
spinlock_t spinlock ; /* spinlock for register handling */
struct watchdog_device xilinx_wwdt_wdd ;
unsigned long freq ;
u32 close_percent ;
} ;
static int xilinx_wwdt_start ( struct watchdog_device * wdd )
{
struct xwwdt_device * xdev = watchdog_get_drvdata ( wdd ) ;
struct watchdog_device * xilinx_wwdt_wdd = & xdev - > xilinx_wwdt_wdd ;
u64 time_out , closed_timeout , open_timeout ;
u32 control_status_reg ;
/* Calculate timeout count */
time_out = xdev - > freq * wdd - > timeout ;
2023-08-15 22:06:50 +03:00
closed_timeout = div_u64 ( time_out * xdev - > close_percent , 100 ) ;
2023-04-20 13:42:30 +03:00
open_timeout = time_out - closed_timeout ;
wdd - > min_hw_heartbeat_ms = xdev - > close_percent * 10 * wdd - > timeout ;
spin_lock ( & xdev - > spinlock ) ;
iowrite32 ( XWWDT_MWR_MASK , xdev - > base + XWWDT_MWR_OFFSET ) ;
iowrite32 ( ~ ( u32 ) XWWDT_ESR_WEN_MASK , xdev - > base + XWWDT_ESR_OFFSET ) ;
iowrite32 ( ( u32 ) closed_timeout , xdev - > base + XWWDT_FWR_OFFSET ) ;
iowrite32 ( ( u32 ) open_timeout , xdev - > base + XWWDT_SWR_OFFSET ) ;
/* Enable the window watchdog timer */
control_status_reg = ioread32 ( xdev - > base + XWWDT_ESR_OFFSET ) ;
control_status_reg | = XWWDT_ESR_WEN_MASK ;
iowrite32 ( control_status_reg , xdev - > base + XWWDT_ESR_OFFSET ) ;
spin_unlock ( & xdev - > spinlock ) ;
dev_dbg ( xilinx_wwdt_wdd - > parent , " Watchdog Started! \n " ) ;
return 0 ;
}
static int xilinx_wwdt_keepalive ( struct watchdog_device * wdd )
{
struct xwwdt_device * xdev = watchdog_get_drvdata ( wdd ) ;
u32 control_status_reg ;
spin_lock ( & xdev - > spinlock ) ;
/* Enable write access control bit for the window watchdog */
iowrite32 ( XWWDT_MWR_MASK , xdev - > base + XWWDT_MWR_OFFSET ) ;
/* Trigger restart kick to watchdog */
control_status_reg = ioread32 ( xdev - > base + XWWDT_ESR_OFFSET ) ;
control_status_reg | = XWWDT_ESR_WSW_MASK ;
iowrite32 ( control_status_reg , xdev - > base + XWWDT_ESR_OFFSET ) ;
spin_unlock ( & xdev - > spinlock ) ;
return 0 ;
}
static const struct watchdog_info xilinx_wwdt_ident = {
. options = WDIOF_KEEPALIVEPING |
WDIOF_SETTIMEOUT ,
. firmware_version = 1 ,
. identity = " xlnx_window watchdog " ,
} ;
static const struct watchdog_ops xilinx_wwdt_ops = {
. owner = THIS_MODULE ,
. start = xilinx_wwdt_start ,
. ping = xilinx_wwdt_keepalive ,
} ;
static int xwwdt_probe ( struct platform_device * pdev )
{
struct watchdog_device * xilinx_wwdt_wdd ;
struct device * dev = & pdev - > dev ;
struct xwwdt_device * xdev ;
struct clk * clk ;
int ret ;
xdev = devm_kzalloc ( dev , sizeof ( * xdev ) , GFP_KERNEL ) ;
if ( ! xdev )
return - ENOMEM ;
xilinx_wwdt_wdd = & xdev - > xilinx_wwdt_wdd ;
xilinx_wwdt_wdd - > info = & xilinx_wwdt_ident ;
xilinx_wwdt_wdd - > ops = & xilinx_wwdt_ops ;
xilinx_wwdt_wdd - > parent = dev ;
xdev - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( xdev - > base ) )
return PTR_ERR ( xdev - > base ) ;
clk = devm_clk_get_enabled ( dev , NULL ) ;
if ( IS_ERR ( clk ) )
return PTR_ERR ( clk ) ;
xdev - > freq = clk_get_rate ( clk ) ;
if ( ! xdev - > freq )
return - EINVAL ;
xilinx_wwdt_wdd - > min_timeout = XWWDT_MIN_TIMEOUT ;
xilinx_wwdt_wdd - > timeout = XWWDT_DEFAULT_TIMEOUT ;
xilinx_wwdt_wdd - > max_hw_heartbeat_ms = 1000 * xilinx_wwdt_wdd - > timeout ;
if ( closed_window_percent = = 0 | | closed_window_percent > = 100 )
xdev - > close_percent = XWWDT_CLOSE_WINDOW_PERCENT ;
else
xdev - > close_percent = closed_window_percent ;
watchdog_init_timeout ( xilinx_wwdt_wdd , wwdt_timeout , & pdev - > dev ) ;
spin_lock_init ( & xdev - > spinlock ) ;
watchdog_set_drvdata ( xilinx_wwdt_wdd , xdev ) ;
watchdog_set_nowayout ( xilinx_wwdt_wdd , 1 ) ;
ret = devm_watchdog_register_device ( dev , xilinx_wwdt_wdd ) ;
if ( ret )
return ret ;
dev_info ( dev , " Xilinx window watchdog Timer with timeout %ds \n " ,
xilinx_wwdt_wdd - > timeout ) ;
return 0 ;
}
static const struct of_device_id xwwdt_of_match [ ] = {
{ . compatible = " xlnx,versal-wwdt " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , xwwdt_of_match ) ;
static struct platform_driver xwwdt_driver = {
. probe = xwwdt_probe ,
. driver = {
. name = " Xilinx window watchdog " ,
. of_match_table = xwwdt_of_match ,
} ,
} ;
module_platform_driver ( xwwdt_driver ) ;
MODULE_AUTHOR ( " Neeli Srinivas <srinivas.neeli@amd.com> " ) ;
MODULE_DESCRIPTION ( " Xilinx window watchdog driver " ) ;
MODULE_LICENSE ( " GPL " ) ;