2018-03-16 18:14:11 +03:00
// SPDX-License-Identifier: GPL-2.0
2014-02-15 00:03:05 +04:00
/*
* Copyright ( c ) 2014 , NVIDIA CORPORATION . All rights reserved .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/of.h>
# include <linux/platform_device.h>
# include <linux/watchdog.h>
/* minimum and maximum watchdog trigger timeout, in seconds */
# define MIN_WDT_TIMEOUT 1
# define MAX_WDT_TIMEOUT 255
/*
* Base of the WDT registers , from the timer base address . There are
* actually 5 watchdogs that can be configured ( by pairing with an available
* timer ) , at bases 0x100 + ( WDT ID ) * 0x20 , where WDT ID is 0 through 4.
* This driver only configures the first watchdog ( WDT ID 0 ) .
*/
# define WDT_BASE 0x100
# define WDT_ID 0
/*
* Register base of the timer that ' s selected for pairing with the watchdog .
* This driver arbitrarily uses timer 5 , which is currently unused by
* other drivers ( in particular , the Tegra clocksource driver ) . If this
* needs to change , take care that the new timer is not used by the
* clocksource driver .
*/
# define WDT_TIMER_BASE 0x60
# define WDT_TIMER_ID 5
/* WDT registers */
# define WDT_CFG 0x0
# define WDT_CFG_PERIOD_SHIFT 4
# define WDT_CFG_PERIOD_MASK 0xff
# define WDT_CFG_INT_EN (1 << 12)
# define WDT_CFG_PMC2CAR_RST_EN (1 << 15)
# define WDT_STS 0x4
# define WDT_STS_COUNT_SHIFT 4
# define WDT_STS_COUNT_MASK 0xff
# define WDT_STS_EXP_SHIFT 12
# define WDT_STS_EXP_MASK 0x3
# define WDT_CMD 0x8
# define WDT_CMD_START_COUNTER (1 << 0)
# define WDT_CMD_DISABLE_COUNTER (1 << 1)
# define WDT_UNLOCK (0xc)
# define WDT_UNLOCK_PATTERN (0xc45a << 0)
/* Timer registers */
# define TIMER_PTV 0x0
# define TIMER_EN (1 << 31)
# define TIMER_PERIODIC (1 << 30)
struct tegra_wdt {
struct watchdog_device wdd ;
void __iomem * wdt_regs ;
void __iomem * tmr_regs ;
} ;
# define WDT_HEARTBEAT 120
static int heartbeat = WDT_HEARTBEAT ;
module_param ( heartbeat , int , 0 ) ;
MODULE_PARM_DESC ( heartbeat ,
" Watchdog heartbeats in seconds. (default = "
__MODULE_STRING ( WDT_HEARTBEAT ) " ) " ) ;
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 ) " ) " ) ;
static int tegra_wdt_start ( struct watchdog_device * wdd )
{
struct tegra_wdt * wdt = watchdog_get_drvdata ( wdd ) ;
u32 val ;
/*
* This thing has a fixed 1 MHz clock . Normally , we would set the
* period to 1 second by writing 1000000ul , but the watchdog system
* reset actually occurs on the 4 th expiration of this counter ,
* so we set the period to 1 / 4 of this amount .
*/
val = 1000000ul / 4 ;
val | = ( TIMER_EN | TIMER_PERIODIC ) ;
writel ( val , wdt - > tmr_regs + TIMER_PTV ) ;
/*
* Set number of periods and start counter .
*
* Interrupt handler is not required for user space
* WDT accesses , since the caller is responsible to ping the
* WDT to reset the counter before expiration , through ioctls .
*/
val = WDT_TIMER_ID |
( wdd - > timeout < < WDT_CFG_PERIOD_SHIFT ) |
WDT_CFG_PMC2CAR_RST_EN ;
writel ( val , wdt - > wdt_regs + WDT_CFG ) ;
writel ( WDT_CMD_START_COUNTER , wdt - > wdt_regs + WDT_CMD ) ;
return 0 ;
}
static int tegra_wdt_stop ( struct watchdog_device * wdd )
{
struct tegra_wdt * wdt = watchdog_get_drvdata ( wdd ) ;
writel ( WDT_UNLOCK_PATTERN , wdt - > wdt_regs + WDT_UNLOCK ) ;
writel ( WDT_CMD_DISABLE_COUNTER , wdt - > wdt_regs + WDT_CMD ) ;
writel ( 0 , wdt - > tmr_regs + TIMER_PTV ) ;
return 0 ;
}
static int tegra_wdt_ping ( struct watchdog_device * wdd )
{
struct tegra_wdt * wdt = watchdog_get_drvdata ( wdd ) ;
writel ( WDT_CMD_START_COUNTER , wdt - > wdt_regs + WDT_CMD ) ;
return 0 ;
}
static int tegra_wdt_set_timeout ( struct watchdog_device * wdd ,
unsigned int timeout )
{
wdd - > timeout = timeout ;
2015-11-10 03:11:38 +03:00
if ( watchdog_active ( wdd ) ) {
tegra_wdt_stop ( wdd ) ;
2014-02-15 00:03:05 +04:00
return tegra_wdt_start ( wdd ) ;
2015-11-10 03:11:38 +03:00
}
2014-02-15 00:03:05 +04:00
return 0 ;
}
static unsigned int tegra_wdt_get_timeleft ( struct watchdog_device * wdd )
{
struct tegra_wdt * wdt = watchdog_get_drvdata ( wdd ) ;
u32 val ;
int count ;
int exp ;
val = readl ( wdt - > wdt_regs + WDT_STS ) ;
/* Current countdown (from timeout) */
count = ( val > > WDT_STS_COUNT_SHIFT ) & WDT_STS_COUNT_MASK ;
/* Number of expirations (we are waiting for the 4th expiration) */
exp = ( val > > WDT_STS_EXP_SHIFT ) & WDT_STS_EXP_MASK ;
/*
* The entire thing is divided by 4 because we are ticking down 4 times
* faster due to needing to wait for the 4 th expiration .
*/
return ( ( ( 3 - exp ) * wdd - > timeout ) + count ) / 4 ;
}
static const struct watchdog_info tegra_wdt_info = {
. options = WDIOF_SETTIMEOUT |
WDIOF_MAGICCLOSE |
WDIOF_KEEPALIVEPING ,
. firmware_version = 0 ,
. identity = " Tegra Watchdog " ,
} ;
2016-09-01 20:35:25 +03:00
static const struct watchdog_ops tegra_wdt_ops = {
2014-02-15 00:03:05 +04:00
. owner = THIS_MODULE ,
. start = tegra_wdt_start ,
. stop = tegra_wdt_stop ,
. ping = tegra_wdt_ping ,
. set_timeout = tegra_wdt_set_timeout ,
. get_timeleft = tegra_wdt_get_timeleft ,
} ;
static int tegra_wdt_probe ( struct platform_device * pdev )
{
2019-04-10 19:27:42 +03:00
struct device * dev = & pdev - > dev ;
2014-02-15 00:03:05 +04:00
struct watchdog_device * wdd ;
struct tegra_wdt * wdt ;
void __iomem * regs ;
int ret ;
/* This is the timer base. */
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
regs = devm_platform_ioremap_resource ( pdev , 0 ) ;
2014-02-15 00:03:05 +04:00
if ( IS_ERR ( regs ) )
return PTR_ERR ( regs ) ;
/*
* Allocate our watchdog driver data , which has the
* struct watchdog_device nested within it .
*/
2019-04-10 19:27:42 +03:00
wdt = devm_kzalloc ( dev , sizeof ( * wdt ) , GFP_KERNEL ) ;
2014-02-15 00:03:05 +04:00
if ( ! wdt )
return - ENOMEM ;
/* Initialize struct tegra_wdt. */
wdt - > wdt_regs = regs + WDT_BASE ;
wdt - > tmr_regs = regs + WDT_TIMER_BASE ;
/* Initialize struct watchdog_device. */
wdd = & wdt - > wdd ;
wdd - > timeout = heartbeat ;
wdd - > info = & tegra_wdt_info ;
wdd - > ops = & tegra_wdt_ops ;
wdd - > min_timeout = MIN_WDT_TIMEOUT ;
wdd - > max_timeout = MAX_WDT_TIMEOUT ;
2019-04-10 19:27:42 +03:00
wdd - > parent = dev ;
2014-02-15 00:03:05 +04:00
watchdog_set_drvdata ( wdd , wdt ) ;
watchdog_set_nowayout ( wdd , nowayout ) ;
2019-04-10 19:27:42 +03:00
watchdog_stop_on_unregister ( wdd ) ;
ret = devm_watchdog_register_device ( dev , wdd ) ;
2019-05-19 00:27:58 +03:00
if ( ret )
2014-02-15 00:03:05 +04:00
return ret ;
platform_set_drvdata ( pdev , wdt ) ;
2019-04-10 19:27:42 +03:00
dev_info ( dev , " initialized (heartbeat = %d sec, nowayout = %d) \n " ,
2014-02-15 00:03:05 +04:00
heartbeat , nowayout ) ;
return 0 ;
}
# ifdef CONFIG_PM_SLEEP
static int tegra_wdt_runtime_suspend ( struct device * dev )
{
struct tegra_wdt * wdt = dev_get_drvdata ( dev ) ;
if ( watchdog_active ( & wdt - > wdd ) )
tegra_wdt_stop ( & wdt - > wdd ) ;
return 0 ;
}
static int tegra_wdt_runtime_resume ( struct device * dev )
{
struct tegra_wdt * wdt = dev_get_drvdata ( dev ) ;
if ( watchdog_active ( & wdt - > wdd ) )
tegra_wdt_start ( & wdt - > wdd ) ;
return 0 ;
}
# endif
static const struct of_device_id tegra_wdt_of_match [ ] = {
{ . compatible = " nvidia,tegra30-timer " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , tegra_wdt_of_match ) ;
static const struct dev_pm_ops tegra_wdt_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( tegra_wdt_runtime_suspend ,
tegra_wdt_runtime_resume )
} ;
static struct platform_driver tegra_wdt_driver = {
. probe = tegra_wdt_probe ,
. driver = {
. name = " tegra-wdt " ,
. pm = & tegra_wdt_pm_ops ,
. of_match_table = tegra_wdt_of_match ,
} ,
} ;
module_platform_driver ( tegra_wdt_driver ) ;
MODULE_AUTHOR ( " NVIDIA Corporation " ) ;
MODULE_DESCRIPTION ( " Tegra Watchdog Driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;