2018-09-07 05:11:17 +03:00
// SPDX-License-Identifier: GPL-2.0
2017-03-05 01:37:35 +03:00
/*
* Renesas RZ / A Series WDT Driver
*
* Copyright ( C ) 2017 Renesas Electronics America , Inc .
* Copyright ( C ) 2017 Chris Brandt
*/
# include <linux/bitops.h>
# include <linux/clk.h>
# include <linux/delay.h>
2023-07-27 02:33:00 +03:00
# include <linux/io.h>
2017-03-05 01:37:35 +03:00
# include <linux/module.h>
2023-07-27 02:33:00 +03:00
# include <linux/of.h>
2017-03-05 01:37:35 +03:00
# include <linux/platform_device.h>
# include <linux/watchdog.h>
# define DEFAULT_TIMEOUT 30
/* Watchdog Timer Registers */
# define WTCSR 0
# define WTCSR_MAGIC 0xA500
# define WTSCR_WT BIT(6)
# define WTSCR_TME BIT(5)
# define WTSCR_CKS(i) (i)
# define WTCNT 2
# define WTCNT_MAGIC 0x5A00
# define WRCSR 4
# define WRCSR_MAGIC 0x5A00
# define WRCSR_RSTE BIT(6)
# define WRCSR_CLEAR_WOVF 0xA500 /* special value */
2018-09-24 16:58:15 +03:00
/* The maximum CKS register setting value to get the longest timeout */
# define CKS_3BIT 0x7
# define CKS_4BIT 0xF
# define DIVIDER_3BIT 16384 /* Clock divider when CKS = 0x7 */
# define DIVIDER_4BIT 4194304 /* Clock divider when CKS = 0xF */
2017-03-05 01:37:35 +03:00
struct rza_wdt {
struct watchdog_device wdev ;
void __iomem * base ;
struct clk * clk ;
2018-09-24 16:58:15 +03:00
u8 count ;
u8 cks ;
2017-03-05 01:37:35 +03:00
} ;
2018-09-24 16:58:15 +03:00
static void rza_wdt_calc_timeout ( struct rza_wdt * priv , int timeout )
{
unsigned long rate = clk_get_rate ( priv - > clk ) ;
unsigned int ticks ;
if ( priv - > cks = = CKS_4BIT ) {
ticks = DIV_ROUND_UP ( timeout * rate , DIVIDER_4BIT ) ;
/*
* Since max_timeout was set in probe , we know that the timeout
* value passed will never calculate to a tick value greater
* than 256.
*/
priv - > count = 256 - ticks ;
} else {
/* Start timer with longest timeout */
priv - > count = 0 ;
}
pr_debug ( " %s: timeout set to %u (WTCNT=%d) \n " , __func__ ,
timeout , priv - > count ) ;
}
2017-03-05 01:37:35 +03:00
static int rza_wdt_start ( struct watchdog_device * wdev )
{
struct rza_wdt * priv = watchdog_get_drvdata ( wdev ) ;
/* Stop timer */
writew ( WTCSR_MAGIC | 0 , priv - > base + WTCSR ) ;
/* Must dummy read WRCSR:WOVF at least once before clearing */
readb ( priv - > base + WRCSR ) ;
writew ( WRCSR_CLEAR_WOVF , priv - > base + WRCSR ) ;
2018-09-24 16:58:15 +03:00
rza_wdt_calc_timeout ( priv , wdev - > timeout ) ;
2017-03-05 01:37:35 +03:00
writew ( WRCSR_MAGIC | WRCSR_RSTE , priv - > base + WRCSR ) ;
2018-09-24 16:58:15 +03:00
writew ( WTCNT_MAGIC | priv - > count , priv - > base + WTCNT ) ;
writew ( WTCSR_MAGIC | WTSCR_WT | WTSCR_TME |
WTSCR_CKS ( priv - > cks ) , priv - > base + WTCSR ) ;
2017-03-05 01:37:35 +03:00
return 0 ;
}
static int rza_wdt_stop ( struct watchdog_device * wdev )
{
struct rza_wdt * priv = watchdog_get_drvdata ( wdev ) ;
writew ( WTCSR_MAGIC | 0 , priv - > base + WTCSR ) ;
return 0 ;
}
static int rza_wdt_ping ( struct watchdog_device * wdev )
{
struct rza_wdt * priv = watchdog_get_drvdata ( wdev ) ;
2018-09-24 16:58:15 +03:00
writew ( WTCNT_MAGIC | priv - > count , priv - > base + WTCNT ) ;
2017-03-05 01:37:35 +03:00
2018-09-24 16:58:15 +03:00
pr_debug ( " %s: timeout = %u \n " , __func__ , wdev - > timeout ) ;
return 0 ;
}
static int rza_set_timeout ( struct watchdog_device * wdev , unsigned int timeout )
{
wdev - > timeout = timeout ;
rza_wdt_start ( wdev ) ;
2017-03-05 01:37:35 +03:00
return 0 ;
}
static int rza_wdt_restart ( struct watchdog_device * wdev , unsigned long action ,
void * data )
{
struct rza_wdt * priv = watchdog_get_drvdata ( wdev ) ;
/* Stop timer */
writew ( WTCSR_MAGIC | 0 , priv - > base + WTCSR ) ;
/* Must dummy read WRCSR:WOVF at least once before clearing */
readb ( priv - > base + WRCSR ) ;
writew ( WRCSR_CLEAR_WOVF , priv - > base + WRCSR ) ;
/*
* Start timer with fastest clock source and only 1 clock left before
* overflow with reset option enabled .
*/
writew ( WRCSR_MAGIC | WRCSR_RSTE , priv - > base + WRCSR ) ;
writew ( WTCNT_MAGIC | 255 , priv - > base + WTCNT ) ;
writew ( WTCSR_MAGIC | WTSCR_WT | WTSCR_TME , priv - > base + WTCSR ) ;
/*
* Actually make sure the above sequence hits hardware before sleeping .
*/
wmb ( ) ;
/* Wait for WDT overflow (reset) */
udelay ( 20 ) ;
return 0 ;
}
static const struct watchdog_info rza_wdt_ident = {
. options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT ,
. identity = " Renesas RZ/A WDT Watchdog " ,
} ;
static const struct watchdog_ops rza_wdt_ops = {
. owner = THIS_MODULE ,
. start = rza_wdt_start ,
. stop = rza_wdt_stop ,
. ping = rza_wdt_ping ,
2018-09-24 16:58:15 +03:00
. set_timeout = rza_set_timeout ,
2017-03-05 01:37:35 +03:00
. restart = rza_wdt_restart ,
} ;
static int rza_wdt_probe ( struct platform_device * pdev )
{
2019-04-09 20:23:53 +03:00
struct device * dev = & pdev - > dev ;
2017-03-05 01:37:35 +03:00
struct rza_wdt * priv ;
unsigned long rate ;
int ret ;
2019-04-09 20:23:53 +03:00
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
2017-03-05 01:37:35 +03:00
if ( ! priv )
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
priv - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2017-03-05 01:37:35 +03:00
if ( IS_ERR ( priv - > base ) )
return PTR_ERR ( priv - > base ) ;
2019-04-09 20:23:53 +03:00
priv - > clk = devm_clk_get ( dev , NULL ) ;
2017-03-05 01:37:35 +03:00
if ( IS_ERR ( priv - > clk ) )
return PTR_ERR ( priv - > clk ) ;
rate = clk_get_rate ( priv - > clk ) ;
if ( rate < 16384 ) {
2019-04-09 20:23:53 +03:00
dev_err ( dev , " invalid clock rate (%ld) \n " , rate ) ;
2017-03-05 01:37:35 +03:00
return - ENOENT ;
}
2021-09-15 12:48:23 +03:00
priv - > wdev . info = & rza_wdt_ident ;
priv - > wdev . ops = & rza_wdt_ops ;
2019-04-09 20:23:53 +03:00
priv - > wdev . parent = dev ;
2017-03-05 01:37:35 +03:00
2019-04-09 20:23:53 +03:00
priv - > cks = ( u8 ) ( uintptr_t ) of_device_get_match_data ( dev ) ;
2018-09-24 16:58:15 +03:00
if ( priv - > cks = = CKS_4BIT ) {
/* Assume slowest clock rate possible (CKS=0xF) */
priv - > wdev . max_timeout = ( DIVIDER_4BIT * U8_MAX ) / rate ;
} else if ( priv - > cks = = CKS_3BIT ) {
/* Assume slowest clock rate possible (CKS=7) */
rate / = DIVIDER_3BIT ;
/*
* Since the max possible timeout of our 8 - bit count
* register is less than a second , we must use
* max_hw_heartbeat_ms .
*/
priv - > wdev . max_hw_heartbeat_ms = ( 1000 * U8_MAX ) / rate ;
2019-04-09 20:23:53 +03:00
dev_dbg ( dev , " max hw timeout of %dms \n " ,
priv - > wdev . max_hw_heartbeat_ms ) ;
2018-09-24 16:58:15 +03:00
}
2017-03-05 01:37:35 +03:00
priv - > wdev . min_timeout = 1 ;
priv - > wdev . timeout = DEFAULT_TIMEOUT ;
2019-04-09 20:23:53 +03:00
watchdog_init_timeout ( & priv - > wdev , 0 , dev ) ;
2017-03-05 01:37:35 +03:00
watchdog_set_drvdata ( & priv - > wdev , priv ) ;
2019-04-09 20:23:53 +03:00
ret = devm_watchdog_register_device ( dev , & priv - > wdev ) ;
2017-03-05 01:37:35 +03:00
if ( ret )
2019-04-09 20:23:53 +03:00
dev_err ( dev , " Cannot register watchdog device \n " ) ;
2017-03-05 01:37:35 +03:00
return ret ;
}
static const struct of_device_id rza_wdt_of_match [ ] = {
2018-09-24 16:58:15 +03:00
{ . compatible = " renesas,r7s9210-wdt " , . data = ( void * ) CKS_4BIT , } ,
{ . compatible = " renesas,rza-wdt " , . data = ( void * ) CKS_3BIT , } ,
2017-03-05 01:37:35 +03:00
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , rza_wdt_of_match ) ;
static struct platform_driver rza_wdt_driver = {
. probe = rza_wdt_probe ,
. driver = {
. name = " rza_wdt " ,
. of_match_table = rza_wdt_of_match ,
} ,
} ;
module_platform_driver ( rza_wdt_driver ) ;
MODULE_DESCRIPTION ( " Renesas RZ/A WDT Driver " ) ;
MODULE_AUTHOR ( " Chris Brandt <chris.brandt@renesas.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;