2018-03-16 18:14:11 +03:00
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2016-07-10 12:11:04 +03:00
/*
* Copyright ( c ) 2016 BayLibre , SAS .
* Author : Neil Armstrong < narmstrong @ baylibre . com >
*
*/
# include <linux/clk.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
# include <linux/types.h>
# include <linux/watchdog.h>
# define DEFAULT_TIMEOUT 30 /* seconds */
# define GXBB_WDT_CTRL_REG 0x0
# define GXBB_WDT_TCNT_REG 0x8
# define GXBB_WDT_RSET_REG 0xc
# define GXBB_WDT_CTRL_CLKDIV_EN BIT(25)
# define GXBB_WDT_CTRL_CLK_EN BIT(24)
# define GXBB_WDT_CTRL_EE_RESET BIT(21)
# define GXBB_WDT_CTRL_EN BIT(18)
# define GXBB_WDT_CTRL_DIV_MASK (BIT(18) - 1)
# define GXBB_WDT_TCNT_SETUP_MASK (BIT(16) - 1)
# define GXBB_WDT_TCNT_CNT_SHIFT 16
2021-07-30 07:13:53 +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 ) " ) " ) ;
2021-07-30 07:13:54 +03:00
static unsigned int timeout ;
module_param ( timeout , uint , 0 ) ;
MODULE_PARM_DESC ( timeout , " Watchdog heartbeat in seconds= "
__MODULE_STRING ( DEFAULT_TIMEOUT ) " ) " ) ;
2016-07-10 12:11:04 +03:00
struct meson_gxbb_wdt {
void __iomem * reg_base ;
struct watchdog_device wdt_dev ;
struct clk * clk ;
} ;
static int meson_gxbb_wdt_start ( struct watchdog_device * wdt_dev )
{
struct meson_gxbb_wdt * data = watchdog_get_drvdata ( wdt_dev ) ;
writel ( readl ( data - > reg_base + GXBB_WDT_CTRL_REG ) | GXBB_WDT_CTRL_EN ,
data - > reg_base + GXBB_WDT_CTRL_REG ) ;
return 0 ;
}
static int meson_gxbb_wdt_stop ( struct watchdog_device * wdt_dev )
{
struct meson_gxbb_wdt * data = watchdog_get_drvdata ( wdt_dev ) ;
writel ( readl ( data - > reg_base + GXBB_WDT_CTRL_REG ) & ~ GXBB_WDT_CTRL_EN ,
data - > reg_base + GXBB_WDT_CTRL_REG ) ;
return 0 ;
}
static int meson_gxbb_wdt_ping ( struct watchdog_device * wdt_dev )
{
struct meson_gxbb_wdt * data = watchdog_get_drvdata ( wdt_dev ) ;
writel ( 0 , data - > reg_base + GXBB_WDT_RSET_REG ) ;
return 0 ;
}
static int meson_gxbb_wdt_set_timeout ( struct watchdog_device * wdt_dev ,
unsigned int timeout )
{
struct meson_gxbb_wdt * data = watchdog_get_drvdata ( wdt_dev ) ;
unsigned long tcnt = timeout * 1000 ;
if ( tcnt > GXBB_WDT_TCNT_SETUP_MASK )
tcnt = GXBB_WDT_TCNT_SETUP_MASK ;
wdt_dev - > timeout = timeout ;
meson_gxbb_wdt_ping ( wdt_dev ) ;
writel ( tcnt , data - > reg_base + GXBB_WDT_TCNT_REG ) ;
return 0 ;
}
static unsigned int meson_gxbb_wdt_get_timeleft ( struct watchdog_device * wdt_dev )
{
struct meson_gxbb_wdt * data = watchdog_get_drvdata ( wdt_dev ) ;
unsigned long reg ;
reg = readl ( data - > reg_base + GXBB_WDT_TCNT_REG ) ;
2019-09-29 13:53:49 +03:00
return ( ( reg & GXBB_WDT_TCNT_SETUP_MASK ) -
( reg > > GXBB_WDT_TCNT_CNT_SHIFT ) ) / 1000 ;
2016-07-10 12:11:04 +03:00
}
static const struct watchdog_ops meson_gxbb_wdt_ops = {
. start = meson_gxbb_wdt_start ,
. stop = meson_gxbb_wdt_stop ,
. ping = meson_gxbb_wdt_ping ,
. set_timeout = meson_gxbb_wdt_set_timeout ,
. get_timeleft = meson_gxbb_wdt_get_timeleft ,
} ;
static const struct watchdog_info meson_gxbb_wdt_info = {
. identity = " Meson GXBB Watchdog " ,
. options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE ,
} ;
static int __maybe_unused meson_gxbb_wdt_resume ( struct device * dev )
{
struct meson_gxbb_wdt * data = dev_get_drvdata ( dev ) ;
if ( watchdog_active ( & data - > wdt_dev ) )
meson_gxbb_wdt_start ( & data - > wdt_dev ) ;
return 0 ;
}
static int __maybe_unused meson_gxbb_wdt_suspend ( struct device * dev )
{
struct meson_gxbb_wdt * data = dev_get_drvdata ( dev ) ;
if ( watchdog_active ( & data - > wdt_dev ) )
meson_gxbb_wdt_stop ( & data - > wdt_dev ) ;
return 0 ;
}
static const struct dev_pm_ops meson_gxbb_wdt_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( meson_gxbb_wdt_suspend , meson_gxbb_wdt_resume )
} ;
static const struct of_device_id meson_gxbb_wdt_dt_ids [ ] = {
{ . compatible = " amlogic,meson-gxbb-wdt " , } ,
{ /* sentinel */ } ,
} ;
MODULE_DEVICE_TABLE ( of , meson_gxbb_wdt_dt_ids ) ;
static int meson_gxbb_wdt_probe ( struct platform_device * pdev )
{
2019-04-09 20:23:42 +03:00
struct device * dev = & pdev - > dev ;
2016-07-10 12:11:04 +03:00
struct meson_gxbb_wdt * data ;
2022-08-01 12:21:50 +03:00
u32 ctrl_reg ;
2016-07-10 12:11:04 +03:00
2019-04-09 20:23:42 +03:00
data = devm_kzalloc ( dev , sizeof ( * data ) , GFP_KERNEL ) ;
2016-07-10 12:11:04 +03:00
if ( ! data )
return - ENOMEM ;
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
data - > reg_base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2016-07-10 12:11:04 +03:00
if ( IS_ERR ( data - > reg_base ) )
return PTR_ERR ( data - > reg_base ) ;
2022-12-31 15:34:44 +03:00
data - > clk = devm_clk_get_enabled ( dev , NULL ) ;
2016-07-10 12:11:04 +03:00
if ( IS_ERR ( data - > clk ) )
return PTR_ERR ( data - > clk ) ;
platform_set_drvdata ( pdev , data ) ;
2019-04-09 20:23:42 +03:00
data - > wdt_dev . parent = dev ;
2016-07-10 12:11:04 +03:00
data - > wdt_dev . info = & meson_gxbb_wdt_info ;
data - > wdt_dev . ops = & meson_gxbb_wdt_ops ;
data - > wdt_dev . max_hw_heartbeat_ms = GXBB_WDT_TCNT_SETUP_MASK ;
data - > wdt_dev . min_timeout = 1 ;
data - > wdt_dev . timeout = DEFAULT_TIMEOUT ;
2021-07-30 07:13:54 +03:00
watchdog_init_timeout ( & data - > wdt_dev , timeout , dev ) ;
2021-07-30 07:13:53 +03:00
watchdog_set_nowayout ( & data - > wdt_dev , nowayout ) ;
2016-07-10 12:11:04 +03:00
watchdog_set_drvdata ( & data - > wdt_dev , data ) ;
2022-08-01 12:21:50 +03:00
ctrl_reg = readl ( data - > reg_base + GXBB_WDT_CTRL_REG ) &
GXBB_WDT_CTRL_EN ;
if ( ctrl_reg ) {
/* Watchdog is running - keep it running but extend timeout
* to the maximum while setting the timebase
*/
set_bit ( WDOG_HW_RUNNING , & data - > wdt_dev . status ) ;
meson_gxbb_wdt_set_timeout ( & data - > wdt_dev ,
GXBB_WDT_TCNT_SETUP_MASK / 1000 ) ;
}
2016-07-10 12:11:04 +03:00
/* Setup with 1ms timebase */
2022-08-01 12:21:50 +03:00
ctrl_reg | = ( ( clk_get_rate ( data - > clk ) / 1000 ) &
GXBB_WDT_CTRL_DIV_MASK ) |
GXBB_WDT_CTRL_EE_RESET |
GXBB_WDT_CTRL_CLK_EN |
GXBB_WDT_CTRL_CLKDIV_EN ;
2016-07-10 12:11:04 +03:00
2022-08-01 12:21:50 +03:00
writel ( ctrl_reg , data - > reg_base + GXBB_WDT_CTRL_REG ) ;
2016-07-10 12:11:04 +03:00
meson_gxbb_wdt_set_timeout ( & data - > wdt_dev , data - > wdt_dev . timeout ) ;
2019-04-09 20:23:42 +03:00
return devm_watchdog_register_device ( dev , & data - > wdt_dev ) ;
2016-07-10 12:11:04 +03:00
}
static struct platform_driver meson_gxbb_wdt_driver = {
. probe = meson_gxbb_wdt_probe ,
. driver = {
. name = " meson-gxbb-wdt " ,
. pm = & meson_gxbb_wdt_pm_ops ,
. of_match_table = meson_gxbb_wdt_dt_ids ,
} ,
} ;
module_platform_driver ( meson_gxbb_wdt_driver ) ;
MODULE_AUTHOR ( " Neil Armstrong <narmstrong@baylibre.com> " ) ;
MODULE_DESCRIPTION ( " Amlogic Meson GXBB Watchdog timer driver " ) ;
MODULE_LICENSE ( " Dual BSD/GPL " ) ;