2018-03-16 18:14:11 +03:00
// SPDX-License-Identifier: GPL-2.0+
2011-06-03 01:13:11 +04:00
/*
2013-05-31 09:56:33 +04:00
* Watchdog Device Driver for Xilinx axi / xps_timebase_wdt
*
2014-02-12 17:34:32 +04:00
* ( C ) Copyright 2013 - 2014 Xilinx , Inc .
2013-05-31 09:56:33 +04:00
* ( C ) Copyright 2011 ( Alejandro Cabrera < aldaya @ gmail . com > )
*/
2011-06-03 01:13:11 +04:00
2021-03-29 19:19:36 +03:00
# include <linux/bits.h>
2016-08-12 09:47:01 +03:00
# include <linux/clk.h>
2014-02-12 17:34:34 +04:00
# include <linux/err.h>
2011-06-03 01:13:11 +04:00
# include <linux/module.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/ioport.h>
# include <linux/watchdog.h>
# include <linux/io.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/of_address.h>
/* Register offsets for the Wdt device */
# define XWT_TWCSR0_OFFSET 0x0 /* Control/Status Register0 */
# define XWT_TWCSR1_OFFSET 0x4 /* Control/Status Register1 */
# define XWT_TBR_OFFSET 0x8 /* Timebase Register Offset */
/* Control/Status Register Masks */
2021-03-29 19:19:36 +03:00
# define XWT_CSR0_WRS_MASK BIT(3) /* Reset status */
# define XWT_CSR0_WDS_MASK BIT(2) /* Timer state */
# define XWT_CSR0_EWDT1_MASK BIT(1) /* Enable bit 1 */
2011-06-03 01:13:11 +04:00
/* Control/Status Register 0/1 bits */
2021-03-29 19:19:36 +03:00
# define XWT_CSRX_EWDT2_MASK BIT(0) /* Enable bit 2 */
2011-06-03 01:13:11 +04:00
/* SelfTest constants */
# define XWT_MAX_SELFTEST_LOOP_COUNT 0x00010000
# define XWT_TIMER_FAILED 0xFFFFFFFF
# define WATCHDOG_NAME "Xilinx Watchdog"
struct xwdt_device {
void __iomem * base ;
u32 wdt_interval ;
2021-03-29 19:19:35 +03:00
spinlock_t spinlock ; /* spinlock for register handling */
2014-02-12 17:41:19 +04:00
struct watchdog_device xilinx_wdt_wdd ;
2016-08-12 09:47:01 +03:00
struct clk * clk ;
2011-06-03 01:13:11 +04:00
} ;
2014-02-12 17:34:32 +04:00
static int xilinx_wdt_start ( struct watchdog_device * wdd )
2011-06-03 01:13:11 +04:00
{
2017-08-07 14:24:22 +03:00
int ret ;
2014-02-12 17:34:33 +04:00
u32 control_status_reg ;
2014-02-12 17:41:19 +04:00
struct xwdt_device * xdev = watchdog_get_drvdata ( wdd ) ;
2014-02-12 17:34:33 +04:00
2017-08-07 14:24:22 +03:00
ret = clk_enable ( xdev - > clk ) ;
if ( ret ) {
dev_err ( wdd - > parent , " Failed to enable clock \n " ) ;
return ret ;
}
2014-02-12 17:41:19 +04:00
spin_lock ( & xdev - > spinlock ) ;
2011-06-03 01:13:11 +04:00
/* Clean previous status and enable the watchdog timer */
2014-02-12 17:41:19 +04:00
control_status_reg = ioread32 ( xdev - > base + XWT_TWCSR0_OFFSET ) ;
2011-06-03 01:13:11 +04:00
control_status_reg | = ( XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK ) ;
iowrite32 ( ( control_status_reg | XWT_CSR0_EWDT1_MASK ) ,
2014-02-12 17:41:19 +04:00
xdev - > base + XWT_TWCSR0_OFFSET ) ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:41:19 +04:00
iowrite32 ( XWT_CSRX_EWDT2_MASK , xdev - > base + XWT_TWCSR1_OFFSET ) ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:41:19 +04:00
spin_unlock ( & xdev - > spinlock ) ;
2014-02-12 17:34:32 +04:00
2021-03-29 19:19:37 +03:00
dev_dbg ( wdd - > parent , " Watchdog Started! \n " ) ;
2014-02-12 17:34:32 +04:00
return 0 ;
2011-06-03 01:13:11 +04:00
}
2014-02-12 17:34:32 +04:00
static int xilinx_wdt_stop ( struct watchdog_device * wdd )
2011-06-03 01:13:11 +04:00
{
2014-02-12 17:34:33 +04:00
u32 control_status_reg ;
2014-02-12 17:41:19 +04:00
struct xwdt_device * xdev = watchdog_get_drvdata ( wdd ) ;
2014-02-12 17:34:33 +04:00
2014-02-12 17:41:19 +04:00
spin_lock ( & xdev - > spinlock ) ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:41:19 +04:00
control_status_reg = ioread32 ( xdev - > base + XWT_TWCSR0_OFFSET ) ;
2011-06-03 01:13:11 +04:00
iowrite32 ( ( control_status_reg & ~ XWT_CSR0_EWDT1_MASK ) ,
2014-02-12 17:41:19 +04:00
xdev - > base + XWT_TWCSR0_OFFSET ) ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:41:19 +04:00
iowrite32 ( 0 , xdev - > base + XWT_TWCSR1_OFFSET ) ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:41:19 +04:00
spin_unlock ( & xdev - > spinlock ) ;
2017-08-07 14:24:22 +03:00
clk_disable ( xdev - > clk ) ;
2021-03-29 19:19:37 +03:00
dev_dbg ( wdd - > parent , " Watchdog Stopped! \n " ) ;
2014-02-12 17:34:32 +04:00
return 0 ;
2011-06-03 01:13:11 +04:00
}
2014-02-12 17:34:32 +04:00
static int xilinx_wdt_keepalive ( struct watchdog_device * wdd )
2011-06-03 01:13:11 +04:00
{
2014-02-12 17:34:33 +04:00
u32 control_status_reg ;
2014-02-12 17:41:19 +04:00
struct xwdt_device * xdev = watchdog_get_drvdata ( wdd ) ;
2014-02-12 17:34:33 +04:00
2014-02-12 17:41:19 +04:00
spin_lock ( & xdev - > spinlock ) ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:41:19 +04:00
control_status_reg = ioread32 ( xdev - > base + XWT_TWCSR0_OFFSET ) ;
2011-06-03 01:13:11 +04:00
control_status_reg | = ( XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK ) ;
2014-02-12 17:41:19 +04:00
iowrite32 ( control_status_reg , xdev - > base + XWT_TWCSR0_OFFSET ) ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:41:19 +04:00
spin_unlock ( & xdev - > spinlock ) ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:34:32 +04:00
return 0 ;
}
2011-06-03 01:13:11 +04:00
2014-02-12 17:34:32 +04:00
static const struct watchdog_info xilinx_wdt_ident = {
. options = WDIOF_MAGICCLOSE |
WDIOF_KEEPALIVEPING ,
. firmware_version = 1 ,
. identity = WATCHDOG_NAME ,
} ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:34:32 +04:00
static const struct watchdog_ops xilinx_wdt_ops = {
. owner = THIS_MODULE ,
. start = xilinx_wdt_start ,
. stop = xilinx_wdt_stop ,
. ping = xilinx_wdt_keepalive ,
} ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:41:19 +04:00
static u32 xwdt_selftest ( struct xwdt_device * xdev )
2011-06-03 01:13:11 +04:00
{
int i ;
u32 timer_value1 ;
u32 timer_value2 ;
2014-02-12 17:41:19 +04:00
spin_lock ( & xdev - > spinlock ) ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:41:19 +04:00
timer_value1 = ioread32 ( xdev - > base + XWT_TBR_OFFSET ) ;
timer_value2 = ioread32 ( xdev - > base + XWT_TBR_OFFSET ) ;
2011-06-03 01:13:11 +04:00
for ( i = 0 ;
( ( i < = XWT_MAX_SELFTEST_LOOP_COUNT ) & &
( timer_value2 = = timer_value1 ) ) ; i + + ) {
2014-02-12 17:41:19 +04:00
timer_value2 = ioread32 ( xdev - > base + XWT_TBR_OFFSET ) ;
2011-06-03 01:13:11 +04:00
}
2014-02-12 17:41:19 +04:00
spin_unlock ( & xdev - > spinlock ) ;
2011-06-03 01:13:11 +04:00
if ( timer_value2 ! = timer_value1 )
return ~ XWT_TIMER_FAILED ;
else
return XWT_TIMER_FAILED ;
}
2019-04-09 20:23:48 +03:00
static void xwdt_clk_disable_unprepare ( void * data )
{
clk_disable_unprepare ( data ) ;
}
2012-11-19 22:21:41 +04:00
static int xwdt_probe ( struct platform_device * pdev )
2011-06-03 01:13:11 +04:00
{
2019-04-09 20:23:48 +03:00
struct device * dev = & pdev - > dev ;
2011-06-03 01:13:11 +04:00
int rc ;
2014-02-12 17:41:25 +04:00
u32 pfreq = 0 , enable_once = 0 ;
2014-02-12 17:41:19 +04:00
struct xwdt_device * xdev ;
struct watchdog_device * xilinx_wdt_wdd ;
2019-04-09 20:23:48 +03:00
xdev = devm_kzalloc ( dev , sizeof ( * xdev ) , GFP_KERNEL ) ;
2014-02-12 17:41:19 +04:00
if ( ! xdev )
return - ENOMEM ;
xilinx_wdt_wdd = & xdev - > xilinx_wdt_wdd ;
xilinx_wdt_wdd - > info = & xilinx_wdt_ident ;
xilinx_wdt_wdd - > ops = & xilinx_wdt_ops ;
2019-04-09 20:23:48 +03:00
xilinx_wdt_wdd - > parent = dev ;
2011-06-03 01:13:11 +04:00
watchdog: Convert to use devm_platform_ioremap_resource
Use devm_platform_ioremap_resource to reduce source code size,
improve readability, and reduce the likelyhood of bugs.
The conversion was done automatically with coccinelle using the
following semantic patch.
@r@
identifier res, pdev;
expression a;
expression index;
expression e;
@@
<+...
- res = platform_get_resource(pdev, IORESOURCE_MEM, index);
- a = devm_ioremap_resource(e, res);
+ a = devm_platform_ioremap_resource(pdev, index);
...+>
@depends on r@
identifier r.res;
@@
- struct resource *res;
... when != res
@@
identifier res, pdev;
expression index;
expression a;
@@
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, index);
- a = devm_ioremap_resource(&pdev->dev, res);
+ a = devm_platform_ioremap_resource(pdev, index);
Cc: Joel Stanley <joel@jms.id.au>
Cc: Nicolas Ferre <nicolas.ferre@microchip.com>
Cc: Alexandre Belloni <alexandre.belloni@bootlin.com>
Cc: Florian Fainelli <f.fainelli@gmail.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Baruch Siach <baruch@tkos.co.il>
Cc: Keguang Zhang <keguang.zhang@gmail.com>
Cc: Vladimir Zapolskiy <vz@mleia.com>
Cc: Kevin Hilman <khilman@baylibre.com>
Cc: Matthias Brugger <matthias.bgg@gmail.com>
Cc: Avi Fishman <avifishman70@gmail.com>
Cc: Nancy Yuen <yuenn@google.com>
Cc: Brendan Higgins <brendanhiggins@google.com>
Cc: Wan ZongShun <mcuos.com@gmail.com>
Cc: Michal Simek <michal.simek@xilinx.com>
Cc: Sylvain Lemieux <slemieux.tyco@gmail.com>
Cc: Kukjin Kim <kgene@kernel.org>
Cc: Barry Song <baohua@kernel.org>
Cc: Orson Zhai <orsonzhai@gmail.com>
Cc: Patrice Chotard <patrice.chotard@st.com>
Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com>
Cc: Maxime Ripard <maxime.ripard@bootlin.com>
Cc: Chen-Yu Tsai <wens@csie.org>
Cc: Marc Gonzalez <marc.w.gonzalez@free.fr>
Cc: Thierry Reding <thierry.reding@gmail.com>
Cc: Shawn Guo <shawnguo@kernel.org>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Acked-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Tested-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Acked-by: Maxime Ripard <maxime.ripard@bootlin.com>
Acked-by: Michal Simek <michal.simek@xilinx.com> (cadence/xilinx wdts)
Acked-by: Thierry Reding <treding@nvidia.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Acked-by: Patrice Chotard <patrice.chotard@st.com>
Acked-by: Vladimir Zapolskiy <vz@mleia.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2019-04-02 22:01:53 +03:00
xdev - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2014-02-12 17:41:19 +04:00
if ( IS_ERR ( xdev - > base ) )
return PTR_ERR ( xdev - > base ) ;
2014-02-12 17:34:34 +04:00
2019-04-09 20:23:48 +03:00
rc = of_property_read_u32 ( dev - > of_node , " xlnx,wdt-interval " ,
2014-02-12 17:41:21 +04:00
& xdev - > wdt_interval ) ;
2014-02-12 17:41:25 +04:00
if ( rc )
2019-04-09 20:23:48 +03:00
dev_warn ( dev , " Parameter \" xlnx,wdt-interval \" not found \n " ) ;
2011-06-03 01:13:11 +04:00
2019-04-09 20:23:48 +03:00
rc = of_property_read_u32 ( dev - > of_node , " xlnx,wdt-enable-once " ,
2014-02-12 17:41:21 +04:00
& enable_once ) ;
if ( rc )
2019-04-09 20:23:48 +03:00
dev_warn ( dev ,
2014-02-12 17:41:20 +04:00
" Parameter \" xlnx,wdt-enable-once \" not found \n " ) ;
2014-02-12 17:41:21 +04:00
watchdog_set_nowayout ( xilinx_wdt_wdd , enable_once ) ;
2011-06-03 01:13:11 +04:00
2019-04-09 20:23:48 +03:00
xdev - > clk = devm_clk_get ( dev , NULL ) ;
2017-08-07 14:24:22 +03:00
if ( IS_ERR ( xdev - > clk ) ) {
if ( PTR_ERR ( xdev - > clk ) ! = - ENOENT )
return PTR_ERR ( xdev - > clk ) ;
/*
* Clock framework support is optional , continue on
* anyways if we don ' t find a matching clock .
*/
xdev - > clk = NULL ;
2019-04-09 20:23:48 +03:00
rc = of_property_read_u32 ( dev - > of_node , " clock-frequency " ,
2017-08-07 14:24:22 +03:00
& pfreq ) ;
if ( rc )
2019-04-09 20:23:48 +03:00
dev_warn ( dev ,
2017-08-07 14:24:22 +03:00
" The watchdog clock freq cannot be obtained \n " ) ;
} else {
pfreq = clk_get_rate ( xdev - > clk ) ;
2021-03-29 19:19:38 +03:00
rc = clk_prepare_enable ( xdev - > clk ) ;
if ( rc ) {
dev_err ( dev , " unable to enable clock \n " ) ;
return rc ;
}
rc = devm_add_action_or_reset ( dev , xwdt_clk_disable_unprepare ,
xdev - > clk ) ;
if ( rc )
return rc ;
2017-08-07 14:24:22 +03:00
}
2014-02-12 17:41:22 +04:00
/*
* Twice of the 2 ^ wdt_interval / freq because the first wdt overflow is
* ignored ( interrupt ) , reset is only generated at second wdt overflow
*/
2014-02-12 17:41:25 +04:00
if ( pfreq & & xdev - > wdt_interval )
2014-02-12 17:41:19 +04:00
xilinx_wdt_wdd - > timeout = 2 * ( ( 1 < < xdev - > wdt_interval ) /
2014-02-12 17:41:21 +04:00
pfreq ) ;
2014-02-12 17:41:19 +04:00
spin_lock_init ( & xdev - > spinlock ) ;
watchdog_set_drvdata ( xilinx_wdt_wdd , xdev ) ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:41:19 +04:00
rc = xwdt_selftest ( xdev ) ;
2011-06-03 01:13:11 +04:00
if ( rc = = XWT_TIMER_FAILED ) {
2019-04-09 20:23:48 +03:00
dev_err ( dev , " SelfTest routine error \n " ) ;
return rc ;
2011-06-03 01:13:11 +04:00
}
2019-04-09 20:23:48 +03:00
rc = devm_watchdog_register_device ( dev , xilinx_wdt_wdd ) ;
2019-05-19 00:27:44 +03:00
if ( rc )
2019-04-09 20:23:48 +03:00
return rc ;
2011-06-03 01:13:11 +04:00
2017-08-07 14:24:22 +03:00
clk_disable ( xdev - > clk ) ;
2021-03-29 19:19:39 +03:00
dev_info ( dev , " Xilinx Watchdog Timer with timeout %ds \n " ,
xilinx_wdt_wdd - > timeout ) ;
2014-02-12 17:41:19 +04:00
platform_set_drvdata ( pdev , xdev ) ;
2011-06-03 01:13:11 +04:00
return 0 ;
}
2017-08-07 14:24:23 +03:00
/**
* xwdt_suspend - Suspend the device .
*
* @ dev : handle to the device structure .
* Return : 0 always .
*/
static int __maybe_unused xwdt_suspend ( struct device * dev )
{
2018-04-19 17:06:29 +03:00
struct xwdt_device * xdev = dev_get_drvdata ( dev ) ;
2017-08-07 14:24:23 +03:00
if ( watchdog_active ( & xdev - > xilinx_wdt_wdd ) )
xilinx_wdt_stop ( & xdev - > xilinx_wdt_wdd ) ;
return 0 ;
}
/**
* xwdt_resume - Resume the device .
*
* @ dev : handle to the device structure .
* Return : 0 on success , errno otherwise .
*/
static int __maybe_unused xwdt_resume ( struct device * dev )
{
2018-04-19 17:06:29 +03:00
struct xwdt_device * xdev = dev_get_drvdata ( dev ) ;
2017-08-07 14:24:23 +03:00
int ret = 0 ;
if ( watchdog_active ( & xdev - > xilinx_wdt_wdd ) )
ret = xilinx_wdt_start ( & xdev - > xilinx_wdt_wdd ) ;
return ret ;
}
static SIMPLE_DEV_PM_OPS ( xwdt_pm_ops , xwdt_suspend , xwdt_resume ) ;
2011-06-03 01:13:11 +04:00
/* Match table for of_platform binding */
2014-05-07 12:42:22 +04:00
static const struct of_device_id xwdt_of_match [ ] = {
2013-05-31 09:56:34 +04:00
{ . compatible = " xlnx,xps-timebase-wdt-1.00.a " , } ,
2011-06-03 01:13:11 +04:00
{ . compatible = " xlnx,xps-timebase-wdt-1.01.a " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , xwdt_of_match ) ;
static struct platform_driver xwdt_driver = {
. probe = xwdt_probe ,
. driver = {
. name = WATCHDOG_NAME ,
. of_match_table = xwdt_of_match ,
2017-08-07 14:24:23 +03:00
. pm = & xwdt_pm_ops ,
2011-06-03 01:13:11 +04:00
} ,
} ;
2011-11-29 09:56:27 +04:00
module_platform_driver ( xwdt_driver ) ;
2011-06-03 01:13:11 +04:00
MODULE_AUTHOR ( " Alejandro Cabrera <aldaya@gmail.com> " ) ;
MODULE_DESCRIPTION ( " Xilinx Watchdog driver " ) ;
2018-03-16 18:14:11 +03:00
MODULE_LICENSE ( " GPL " ) ;