2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2006-01-15 00:20:50 +03:00
/*
2008-07-04 10:51:36 +04:00
* mpc8xxx_wdt . c - MPC8xx / MPC83xx / MPC86xx watchdog userspace interface
2006-01-15 00:20:50 +03:00
*
* Authors : Dave Updegraff < dave @ cray . org >
2011-02-23 23:04:38 +03:00
* Kumar Gala < galak @ kernel . crashing . org >
* Attribution : from 83 xx_wst : Florian Schirmer < jolt @ tuxbox . org >
* . . and from sc520_wdt
2008-07-04 10:51:34 +04:00
* Copyright ( c ) 2008 MontaVista Software , Inc .
* Anton Vorontsov < avorontsov @ ru . mvista . com >
2006-01-15 00:20:50 +03:00
*
* Note : it appears that you can only actually ENABLE or DISABLE the thing
* once after POR . Once enabled , you cannot disable , and vice versa .
*/
# include <linux/fs.h>
# include <linux/init.h>
# include <linux/kernel.h>
2013-09-17 23:28:33 +04:00
# include <linux/of_address.h>
2008-07-04 10:51:32 +04:00
# include <linux/of_platform.h>
2006-01-15 00:20:50 +03:00
# include <linux/module.h>
# include <linux/watchdog.h>
2008-05-19 17:07:09 +04:00
# include <linux/io.h>
# include <linux/uaccess.h>
2008-07-04 10:51:32 +04:00
# include <sysdev/fsl_soc.h>
2006-01-15 00:20:50 +03:00
2017-11-08 17:39:44 +03:00
# define WATCHDOG_TIMEOUT 10
2008-07-04 10:51:35 +04:00
struct mpc8xxx_wdt {
2006-01-15 00:20:50 +03:00
__be32 res0 ;
__be32 swcrr ; /* System watchdog control register */
# define SWCRR_SWTC 0xFFFF0000 /* Software Watchdog Time Count. */
2017-11-08 17:39:44 +03:00
# define SWCRR_SWF 0x00000008 /* Software Watchdog Freeze (mpc8xx). */
2006-01-15 00:20:50 +03:00
# define SWCRR_SWEN 0x00000004 /* Watchdog Enable bit. */
# define SWCRR_SWRI 0x00000002 /* Software Watchdog Reset/Interrupt Select bit.*/
# define SWCRR_SWPR 0x00000001 /* Software Watchdog Counter Prescale bit. */
__be32 swcnr ; /* System watchdog count register */
u8 res1 [ 2 ] ;
__be16 swsrr ; /* System watchdog service register */
u8 res2 [ 0xF0 ] ;
} ;
2008-07-04 10:51:35 +04:00
struct mpc8xxx_wdt_type {
2008-07-04 10:51:34 +04:00
int prescaler ;
bool hw_enabled ;
2018-09-17 09:22:50 +03:00
u32 rsr_mask ;
2008-07-04 10:51:34 +04:00
} ;
2015-08-12 11:15:56 +03:00
struct mpc8xxx_wdt_ddata {
struct mpc8xxx_wdt __iomem * base ;
struct watchdog_device wdd ;
spinlock_t lock ;
2017-11-08 17:39:44 +03:00
u16 swtc ;
2015-08-12 11:15:56 +03:00
} ;
2006-01-15 00:20:50 +03:00
2017-11-08 17:39:44 +03:00
static u16 timeout ;
2006-01-15 00:20:50 +03:00
module_param ( timeout , ushort , 0 ) ;
2008-05-19 17:07:09 +04:00
MODULE_PARM_DESC ( timeout ,
2017-11-08 17:39:44 +03:00
" Watchdog timeout in seconds. (1<timeout<65535, default= "
__MODULE_STRING ( WATCHDOG_TIMEOUT ) " ) " ) ;
2006-01-15 00:20:50 +03:00
2012-01-13 03:02:20 +04:00
static bool reset = 1 ;
2006-01-15 00:20:50 +03:00
module_param ( reset , bool , 0 ) ;
2008-05-19 17:07:09 +04:00
MODULE_PARM_DESC ( reset ,
" Watchdog Interrupt/Reset Mode. 0 = interrupt, 1 = reset " ) ;
2006-01-15 00:20:50 +03:00
2012-03-05 19:51:11 +04:00
static bool nowayout = WATCHDOG_NOWAYOUT ;
module_param ( nowayout , bool , 0 ) ;
2008-07-04 10:51:34 +04:00
MODULE_PARM_DESC ( nowayout , " Watchdog cannot be stopped once started "
" (default= " __MODULE_STRING ( WATCHDOG_NOWAYOUT ) " ) " ) ;
2015-08-12 11:15:56 +03:00
static void mpc8xxx_wdt_keepalive ( struct mpc8xxx_wdt_ddata * ddata )
2006-01-15 00:20:50 +03:00
{
/* Ping the WDT */
2015-08-12 11:15:56 +03:00
spin_lock ( & ddata - > lock ) ;
out_be16 ( & ddata - > base - > swsrr , 0x556c ) ;
out_be16 ( & ddata - > base - > swsrr , 0xaa39 ) ;
spin_unlock ( & ddata - > lock ) ;
2006-01-15 00:20:50 +03:00
}
2013-12-04 10:32:14 +04:00
static int mpc8xxx_wdt_start ( struct watchdog_device * w )
2006-01-15 00:20:50 +03:00
{
2015-08-12 11:15:56 +03:00
struct mpc8xxx_wdt_ddata * ddata =
container_of ( w , struct mpc8xxx_wdt_ddata , wdd ) ;
2017-11-08 17:39:44 +03:00
u32 tmp = in_be32 ( & ddata - > base - > swcrr ) ;
2006-01-15 00:20:50 +03:00
/* Good, fire up the show */
2017-11-08 17:39:44 +03:00
tmp & = ~ ( SWCRR_SWTC | SWCRR_SWF | SWCRR_SWEN | SWCRR_SWRI | SWCRR_SWPR ) ;
tmp | = SWCRR_SWEN | SWCRR_SWPR | ( ddata - > swtc < < 16 ) ;
2006-01-15 00:20:50 +03:00
if ( reset )
tmp | = SWCRR_SWRI ;
2015-08-12 11:15:56 +03:00
out_be32 ( & ddata - > base - > swcrr , tmp ) ;
2006-01-15 00:20:50 +03:00
2017-11-08 17:39:44 +03:00
tmp = in_be32 ( & ddata - > base - > swcrr ) ;
if ( ! ( tmp & SWCRR_SWEN ) )
return - EOPNOTSUPP ;
ddata - > swtc = tmp > > 16 ;
set_bit ( WDOG_HW_RUNNING , & ddata - > wdd . status ) ;
2008-07-04 10:51:34 +04:00
2013-12-04 10:32:14 +04:00
return 0 ;
2006-01-15 00:20:50 +03:00
}
2013-12-04 10:32:14 +04:00
static int mpc8xxx_wdt_ping ( struct watchdog_device * w )
2006-01-15 00:20:50 +03:00
{
2015-08-12 11:15:56 +03:00
struct mpc8xxx_wdt_ddata * ddata =
container_of ( w , struct mpc8xxx_wdt_ddata , wdd ) ;
mpc8xxx_wdt_keepalive ( ddata ) ;
2006-01-15 00:20:50 +03:00
return 0 ;
}
2013-12-04 10:32:14 +04:00
static struct watchdog_info mpc8xxx_wdt_info = {
2017-11-08 17:39:44 +03:00
. options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT ,
2013-12-04 10:32:14 +04:00
. firmware_version = 1 ,
. identity = " MPC8xxx " ,
2006-01-15 00:20:50 +03:00
} ;
2013-12-04 10:32:14 +04:00
static struct watchdog_ops mpc8xxx_wdt_ops = {
. owner = THIS_MODULE ,
. start = mpc8xxx_wdt_start ,
. ping = mpc8xxx_wdt_ping ,
} ;
2012-11-19 22:21:41 +04:00
static int mpc8xxx_wdt_probe ( struct platform_device * ofdev )
2006-01-15 00:20:50 +03:00
{
int ret ;
2015-08-12 11:15:55 +03:00
struct resource * res ;
2012-05-21 23:57:39 +04:00
const struct mpc8xxx_wdt_type * wdt_type ;
2015-08-12 11:15:56 +03:00
struct mpc8xxx_wdt_ddata * ddata ;
2008-07-04 10:51:32 +04:00
u32 freq = fsl_get_sys_freq ( ) ;
2008-07-04 10:51:34 +04:00
bool enabled ;
2018-09-17 09:22:47 +03:00
struct device * dev = & ofdev - > dev ;
2006-01-15 00:20:50 +03:00
2018-09-17 09:22:47 +03:00
wdt_type = of_device_get_match_data ( dev ) ;
2015-08-12 11:15:54 +03:00
if ( ! wdt_type )
2011-02-17 12:43:24 +03:00
return - EINVAL ;
2008-07-04 10:51:32 +04:00
if ( ! freq | | freq = = - 1 )
return - EINVAL ;
2006-01-15 00:20:50 +03:00
2018-09-17 09:22:47 +03:00
ddata = devm_kzalloc ( dev , sizeof ( * ddata ) , GFP_KERNEL ) ;
2015-08-12 11:15:56 +03:00
if ( ! ddata )
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
ddata - > base = devm_platform_ioremap_resource ( ofdev , 0 ) ;
2015-08-12 11:15:56 +03:00
if ( IS_ERR ( ddata - > base ) )
return PTR_ERR ( ddata - > base ) ;
2006-01-15 00:20:50 +03:00
2015-08-12 11:15:56 +03:00
enabled = in_be32 ( & ddata - > base - > swcrr ) & SWCRR_SWEN ;
2008-07-04 10:51:34 +04:00
if ( ! enabled & & wdt_type - > hw_enabled ) {
2018-09-17 09:22:47 +03:00
dev_info ( dev , " could not be enabled in software \n " ) ;
2015-08-12 11:15:57 +03:00
return - ENODEV ;
2008-07-04 10:51:34 +04:00
}
2018-09-17 09:22:50 +03:00
res = platform_get_resource ( ofdev , IORESOURCE_MEM , 1 ) ;
if ( res ) {
bool status ;
u32 __iomem * rsr = ioremap ( res - > start , resource_size ( res ) ) ;
if ( ! rsr )
return - ENOMEM ;
status = in_be32 ( rsr ) & wdt_type - > rsr_mask ;
ddata - > wdd . bootstatus = status ? WDIOF_CARDRESET : 0 ;
/* clear reset status bits related to watchdog timer */
out_be32 ( rsr , wdt_type - > rsr_mask ) ;
iounmap ( rsr ) ;
dev_info ( dev , " Last boot was %scaused by watchdog \n " ,
status ? " " : " not " ) ;
}
2015-08-12 11:15:56 +03:00
spin_lock_init ( & ddata - > lock ) ;
ddata - > wdd . info = & mpc8xxx_wdt_info ,
ddata - > wdd . ops = & mpc8xxx_wdt_ops ,
2017-11-08 17:39:44 +03:00
ddata - > wdd . timeout = WATCHDOG_TIMEOUT ;
2018-09-17 09:22:47 +03:00
watchdog_init_timeout ( & ddata - > wdd , timeout , dev ) ;
2015-08-12 11:15:53 +03:00
2015-08-12 11:15:56 +03:00
watchdog_set_nowayout ( & ddata - > wdd , nowayout ) ;
2015-08-12 11:15:53 +03:00
2017-11-08 17:39:44 +03:00
ddata - > swtc = min ( ddata - > wdd . timeout * freq / wdt_type - > prescaler ,
0xffffU ) ;
2008-07-04 10:51:34 +04:00
/*
* If the watchdog was previously enabled or we ' re running on
2008-07-04 10:51:35 +04:00
* MPC8xxx , we should ping the wdt from the kernel until the
2008-07-04 10:51:34 +04:00
* userspace handles it .
*/
if ( enabled )
2017-11-08 17:39:44 +03:00
mpc8xxx_wdt_start ( & ddata - > wdd ) ;
ddata - > wdd . max_hw_heartbeat_ms = ( ddata - > swtc * wdt_type - > prescaler ) /
( freq / 1000 ) ;
ddata - > wdd . min_timeout = ddata - > wdd . max_hw_heartbeat_ms / 1000 ;
if ( ddata - > wdd . timeout < ddata - > wdd . min_timeout )
ddata - > wdd . timeout = ddata - > wdd . min_timeout ;
2019-04-10 19:28:02 +03:00
ret = devm_watchdog_register_device ( dev , & ddata - > wdd ) ;
2019-05-19 00:27:40 +03:00
if ( ret )
2017-11-08 17:39:44 +03:00
return ret ;
2018-09-17 09:22:47 +03:00
dev_info ( dev ,
" WDT driver for MPC8xxx initialized. mode:%s timeout=%d sec \n " ,
reset ? " reset " : " interrupt " , ddata - > wdd . timeout ) ;
2015-08-12 11:15:56 +03:00
platform_set_drvdata ( ofdev , ddata ) ;
2006-01-15 00:20:50 +03:00
return 0 ;
}
2008-07-04 10:51:35 +04:00
static const struct of_device_id mpc8xxx_wdt_match [ ] = {
2008-07-04 10:51:32 +04:00
{
. compatible = " mpc83xx_wdt " ,
2008-07-04 10:51:35 +04:00
. data = & ( struct mpc8xxx_wdt_type ) {
2008-07-04 10:51:34 +04:00
. prescaler = 0x10000 ,
2018-09-17 09:22:50 +03:00
. rsr_mask = BIT ( 3 ) , /* RSR Bit SWRS */
2008-07-04 10:51:34 +04:00
} ,
} ,
{
. compatible = " fsl,mpc8610-wdt " ,
2008-07-04 10:51:35 +04:00
. data = & ( struct mpc8xxx_wdt_type ) {
2008-07-04 10:51:34 +04:00
. prescaler = 0x10000 ,
. hw_enabled = true ,
2018-09-17 09:22:50 +03:00
. rsr_mask = BIT ( 20 ) , /* RSTRSCR Bit WDT_RR */
2008-07-04 10:51:34 +04:00
} ,
2008-07-04 10:51:32 +04:00
} ,
2008-07-04 10:51:36 +04:00
{
. compatible = " fsl,mpc823-wdt " ,
. data = & ( struct mpc8xxx_wdt_type ) {
. prescaler = 0x800 ,
2013-11-30 19:45:40 +04:00
. hw_enabled = true ,
2018-09-17 09:22:50 +03:00
. rsr_mask = BIT ( 28 ) , /* RSR Bit SWRS */
2008-07-04 10:51:36 +04:00
} ,
} ,
2008-07-04 10:51:32 +04:00
{ } ,
} ;
2008-07-04 10:51:35 +04:00
MODULE_DEVICE_TABLE ( of , mpc8xxx_wdt_match ) ;
2008-07-04 10:51:32 +04:00
2011-02-17 12:43:24 +03:00
static struct platform_driver mpc8xxx_wdt_driver = {
2008-07-04 10:51:35 +04:00
. probe = mpc8xxx_wdt_probe ,
2010-04-14 03:13:02 +04:00
. driver = {
. name = " mpc8xxx_wdt " ,
. of_match_table = mpc8xxx_wdt_match ,
2006-01-15 00:20:50 +03:00
} ,
} ;
2008-07-04 10:51:35 +04:00
static int __init mpc8xxx_wdt_init ( void )
2006-01-15 00:20:50 +03:00
{
2011-02-17 12:43:24 +03:00
return platform_driver_register ( & mpc8xxx_wdt_driver ) ;
2006-01-15 00:20:50 +03:00
}
2008-07-04 10:51:36 +04:00
arch_initcall ( mpc8xxx_wdt_init ) ;
2006-01-15 00:20:50 +03:00
2008-07-04 10:51:35 +04:00
static void __exit mpc8xxx_wdt_exit ( void )
2006-01-15 00:20:50 +03:00
{
2011-02-17 12:43:24 +03:00
platform_driver_unregister ( & mpc8xxx_wdt_driver ) ;
2006-01-15 00:20:50 +03:00
}
2008-07-04 10:51:35 +04:00
module_exit ( mpc8xxx_wdt_exit ) ;
2006-01-15 00:20:50 +03:00
MODULE_AUTHOR ( " Dave Updegraff, Kumar Gala " ) ;
2008-07-04 10:51:36 +04:00
MODULE_DESCRIPTION ( " Driver for watchdog timer in MPC8xx/MPC83xx/MPC86xx "
" uProcessors " ) ;
2006-01-15 00:20:50 +03:00
MODULE_LICENSE ( " GPL " ) ;