2006-01-14 13:20:50 -08:00
/*
2008-07-03 23:51:36 -07:00
* mpc8xxx_wdt . c - MPC8xx / MPC83xx / MPC86xx watchdog userspace interface
2006-01-14 13:20:50 -08:00
*
* Authors : Dave Updegraff < dave @ cray . org >
2011-02-23 20:04:38 +00:00
* Kumar Gala < galak @ kernel . crashing . org >
* Attribution : from 83 xx_wst : Florian Schirmer < jolt @ tuxbox . org >
* . . and from sc520_wdt
2008-07-03 23:51:34 -07:00
* Copyright ( c ) 2008 MontaVista Software , Inc .
* Anton Vorontsov < avorontsov @ ru . mvista . com >
2006-01-14 13:20:50 -08: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 .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*/
2012-02-15 15:06:19 -08:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2006-01-14 13:20:50 -08:00
# include <linux/fs.h>
# include <linux/init.h>
# include <linux/kernel.h>
2013-09-17 14:28:33 -05:00
# include <linux/of_address.h>
2008-07-03 23:51:32 -07:00
# include <linux/of_platform.h>
2006-01-14 13:20:50 -08:00
# include <linux/module.h>
# include <linux/watchdog.h>
2008-05-19 14:07:09 +01:00
# include <linux/io.h>
# include <linux/uaccess.h>
2008-07-03 23:51:32 -07:00
# include <sysdev/fsl_soc.h>
2006-01-14 13:20:50 -08:00
2017-11-08 15:39:44 +01:00
# define WATCHDOG_TIMEOUT 10
2008-07-03 23:51:35 -07:00
struct mpc8xxx_wdt {
2006-01-14 13:20:50 -08:00
__be32 res0 ;
__be32 swcrr ; /* System watchdog control register */
# define SWCRR_SWTC 0xFFFF0000 /* Software Watchdog Time Count. */
2017-11-08 15:39:44 +01:00
# define SWCRR_SWF 0x00000008 /* Software Watchdog Freeze (mpc8xx). */
2006-01-14 13:20:50 -08: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-03 23:51:35 -07:00
struct mpc8xxx_wdt_type {
2008-07-03 23:51:34 -07:00
int prescaler ;
bool hw_enabled ;
} ;
2015-08-12 10:15:56 +02:00
struct mpc8xxx_wdt_ddata {
struct mpc8xxx_wdt __iomem * base ;
struct watchdog_device wdd ;
spinlock_t lock ;
2017-11-08 15:39:44 +01:00
u16 swtc ;
2015-08-12 10:15:56 +02:00
} ;
2006-01-14 13:20:50 -08:00
2017-11-08 15:39:44 +01:00
static u16 timeout ;
2006-01-14 13:20:50 -08:00
module_param ( timeout , ushort , 0 ) ;
2008-05-19 14:07:09 +01:00
MODULE_PARM_DESC ( timeout ,
2017-11-08 15:39:44 +01:00
" Watchdog timeout in seconds. (1<timeout<65535, default= "
__MODULE_STRING ( WATCHDOG_TIMEOUT ) " ) " ) ;
2006-01-14 13:20:50 -08:00
2012-01-13 09:32:20 +10:30
static bool reset = 1 ;
2006-01-14 13:20:50 -08:00
module_param ( reset , bool , 0 ) ;
2008-05-19 14:07:09 +01:00
MODULE_PARM_DESC ( reset ,
" Watchdog Interrupt/Reset Mode. 0 = interrupt, 1 = reset " ) ;
2006-01-14 13:20:50 -08:00
2012-03-05 16:51:11 +01:00
static bool nowayout = WATCHDOG_NOWAYOUT ;
module_param ( nowayout , bool , 0 ) ;
2008-07-03 23:51:34 -07:00
MODULE_PARM_DESC ( nowayout , " Watchdog cannot be stopped once started "
" (default= " __MODULE_STRING ( WATCHDOG_NOWAYOUT ) " ) " ) ;
2015-08-12 10:15:56 +02:00
static void mpc8xxx_wdt_keepalive ( struct mpc8xxx_wdt_ddata * ddata )
2006-01-14 13:20:50 -08:00
{
/* Ping the WDT */
2015-08-12 10:15:56 +02:00
spin_lock ( & ddata - > lock ) ;
out_be16 ( & ddata - > base - > swsrr , 0x556c ) ;
out_be16 ( & ddata - > base - > swsrr , 0xaa39 ) ;
spin_unlock ( & ddata - > lock ) ;
2006-01-14 13:20:50 -08:00
}
2013-12-04 07:32:14 +01:00
static int mpc8xxx_wdt_start ( struct watchdog_device * w )
2006-01-14 13:20:50 -08:00
{
2015-08-12 10:15:56 +02:00
struct mpc8xxx_wdt_ddata * ddata =
container_of ( w , struct mpc8xxx_wdt_ddata , wdd ) ;
2017-11-08 15:39:44 +01:00
u32 tmp = in_be32 ( & ddata - > base - > swcrr ) ;
2006-01-14 13:20:50 -08:00
/* Good, fire up the show */
2017-11-08 15:39:44 +01:00
tmp & = ~ ( SWCRR_SWTC | SWCRR_SWF | SWCRR_SWEN | SWCRR_SWRI | SWCRR_SWPR ) ;
tmp | = SWCRR_SWEN | SWCRR_SWPR | ( ddata - > swtc < < 16 ) ;
2006-01-14 13:20:50 -08:00
if ( reset )
tmp | = SWCRR_SWRI ;
2015-08-12 10:15:56 +02:00
out_be32 ( & ddata - > base - > swcrr , tmp ) ;
2006-01-14 13:20:50 -08:00
2017-11-08 15:39:44 +01: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-03 23:51:34 -07:00
2013-12-04 07:32:14 +01:00
return 0 ;
2006-01-14 13:20:50 -08:00
}
2013-12-04 07:32:14 +01:00
static int mpc8xxx_wdt_ping ( struct watchdog_device * w )
2006-01-14 13:20:50 -08:00
{
2015-08-12 10:15:56 +02:00
struct mpc8xxx_wdt_ddata * ddata =
container_of ( w , struct mpc8xxx_wdt_ddata , wdd ) ;
mpc8xxx_wdt_keepalive ( ddata ) ;
2006-01-14 13:20:50 -08:00
return 0 ;
}
2013-12-04 07:32:14 +01:00
static struct watchdog_info mpc8xxx_wdt_info = {
2017-11-08 15:39:44 +01:00
. options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT ,
2013-12-04 07:32:14 +01:00
. firmware_version = 1 ,
. identity = " MPC8xxx " ,
2006-01-14 13:20:50 -08:00
} ;
2013-12-04 07:32:14 +01:00
static struct watchdog_ops mpc8xxx_wdt_ops = {
. owner = THIS_MODULE ,
. start = mpc8xxx_wdt_start ,
. ping = mpc8xxx_wdt_ping ,
} ;
2012-11-19 13:21:41 -05:00
static int mpc8xxx_wdt_probe ( struct platform_device * ofdev )
2006-01-14 13:20:50 -08:00
{
int ret ;
2015-08-12 10:15:55 +02:00
struct resource * res ;
2012-05-21 21:57:39 +02:00
const struct mpc8xxx_wdt_type * wdt_type ;
2015-08-12 10:15:56 +02:00
struct mpc8xxx_wdt_ddata * ddata ;
2008-07-03 23:51:32 -07:00
u32 freq = fsl_get_sys_freq ( ) ;
2008-07-03 23:51:34 -07:00
bool enabled ;
2006-01-14 13:20:50 -08:00
2015-08-12 10:15:54 +02:00
wdt_type = of_device_get_match_data ( & ofdev - > dev ) ;
if ( ! wdt_type )
2011-02-17 02:43:24 -07:00
return - EINVAL ;
2008-07-03 23:51:32 -07:00
if ( ! freq | | freq = = - 1 )
return - EINVAL ;
2006-01-14 13:20:50 -08:00
2015-08-12 10:15:56 +02:00
ddata = devm_kzalloc ( & ofdev - > dev , sizeof ( * ddata ) , GFP_KERNEL ) ;
if ( ! ddata )
return - ENOMEM ;
2015-08-12 10:15:55 +02:00
res = platform_get_resource ( ofdev , IORESOURCE_MEM , 0 ) ;
2015-08-12 10:15:56 +02:00
ddata - > base = devm_ioremap_resource ( & ofdev - > dev , res ) ;
if ( IS_ERR ( ddata - > base ) )
return PTR_ERR ( ddata - > base ) ;
2006-01-14 13:20:50 -08:00
2015-08-12 10:15:56 +02:00
enabled = in_be32 ( & ddata - > base - > swcrr ) & SWCRR_SWEN ;
2008-07-03 23:51:34 -07:00
if ( ! enabled & & wdt_type - > hw_enabled ) {
2012-02-15 15:06:19 -08:00
pr_info ( " could not be enabled in software \n " ) ;
2015-08-12 10:15:57 +02:00
return - ENODEV ;
2008-07-03 23:51:34 -07:00
}
2015-08-12 10:15:56 +02:00
spin_lock_init ( & ddata - > lock ) ;
ddata - > wdd . info = & mpc8xxx_wdt_info ,
ddata - > wdd . ops = & mpc8xxx_wdt_ops ,
2017-11-08 15:39:44 +01:00
ddata - > wdd . timeout = WATCHDOG_TIMEOUT ;
watchdog_init_timeout ( & ddata - > wdd , timeout , & ofdev - > dev ) ;
2015-08-12 10:15:53 +02:00
2015-08-12 10:15:56 +02:00
watchdog_set_nowayout ( & ddata - > wdd , nowayout ) ;
2015-08-12 10:15:53 +02:00
2017-11-08 15:39:44 +01:00
ddata - > swtc = min ( ddata - > wdd . timeout * freq / wdt_type - > prescaler ,
0xffffU ) ;
2008-07-03 23:51:34 -07:00
/*
* If the watchdog was previously enabled or we ' re running on
2008-07-03 23:51:35 -07:00
* MPC8xxx , we should ping the wdt from the kernel until the
2008-07-03 23:51:34 -07:00
* userspace handles it .
*/
if ( enabled )
2017-11-08 15:39:44 +01: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 ;
ret = watchdog_register_device ( & ddata - > wdd ) ;
if ( ret ) {
pr_err ( " cannot register watchdog device (err=%d) \n " , ret ) ;
return ret ;
}
pr_info ( " WDT driver for MPC8xxx initialized. mode:%s timeout=%d sec \n " ,
reset ? " reset " : " interrupt " , ddata - > wdd . timeout ) ;
2015-08-12 10:15:56 +02:00
platform_set_drvdata ( ofdev , ddata ) ;
2006-01-14 13:20:50 -08:00
return 0 ;
}
2012-11-19 13:26:24 -05:00
static int mpc8xxx_wdt_remove ( struct platform_device * ofdev )
2006-01-14 13:20:50 -08:00
{
2015-08-12 10:15:56 +02:00
struct mpc8xxx_wdt_ddata * ddata = platform_get_drvdata ( ofdev ) ;
2013-12-04 07:32:14 +01:00
pr_crit ( " Watchdog removed, expect the %s soon! \n " ,
reset ? " reset " : " machine check exception " ) ;
2015-08-12 10:15:56 +02:00
watchdog_unregister_device ( & ddata - > wdd ) ;
2006-01-14 13:20:50 -08:00
return 0 ;
}
2008-07-03 23:51:35 -07:00
static const struct of_device_id mpc8xxx_wdt_match [ ] = {
2008-07-03 23:51:32 -07:00
{
. compatible = " mpc83xx_wdt " ,
2008-07-03 23:51:35 -07:00
. data = & ( struct mpc8xxx_wdt_type ) {
2008-07-03 23:51:34 -07:00
. prescaler = 0x10000 ,
} ,
} ,
{
. compatible = " fsl,mpc8610-wdt " ,
2008-07-03 23:51:35 -07:00
. data = & ( struct mpc8xxx_wdt_type ) {
2008-07-03 23:51:34 -07:00
. prescaler = 0x10000 ,
. hw_enabled = true ,
} ,
2008-07-03 23:51:32 -07:00
} ,
2008-07-03 23:51:36 -07:00
{
. compatible = " fsl,mpc823-wdt " ,
. data = & ( struct mpc8xxx_wdt_type ) {
. prescaler = 0x800 ,
2013-11-30 16:45:40 +01:00
. hw_enabled = true ,
2008-07-03 23:51:36 -07:00
} ,
} ,
2008-07-03 23:51:32 -07:00
{ } ,
} ;
2008-07-03 23:51:35 -07:00
MODULE_DEVICE_TABLE ( of , mpc8xxx_wdt_match ) ;
2008-07-03 23:51:32 -07:00
2011-02-17 02:43:24 -07:00
static struct platform_driver mpc8xxx_wdt_driver = {
2008-07-03 23:51:35 -07:00
. probe = mpc8xxx_wdt_probe ,
2012-11-19 13:21:12 -05:00
. remove = mpc8xxx_wdt_remove ,
2010-04-13 16:13:02 -07:00
. driver = {
. name = " mpc8xxx_wdt " ,
. of_match_table = mpc8xxx_wdt_match ,
2006-01-14 13:20:50 -08:00
} ,
} ;
2008-07-03 23:51:35 -07:00
static int __init mpc8xxx_wdt_init ( void )
2006-01-14 13:20:50 -08:00
{
2011-02-17 02:43:24 -07:00
return platform_driver_register ( & mpc8xxx_wdt_driver ) ;
2006-01-14 13:20:50 -08:00
}
2008-07-03 23:51:36 -07:00
arch_initcall ( mpc8xxx_wdt_init ) ;
2006-01-14 13:20:50 -08:00
2008-07-03 23:51:35 -07:00
static void __exit mpc8xxx_wdt_exit ( void )
2006-01-14 13:20:50 -08:00
{
2011-02-17 02:43:24 -07:00
platform_driver_unregister ( & mpc8xxx_wdt_driver ) ;
2006-01-14 13:20:50 -08:00
}
2008-07-03 23:51:35 -07:00
module_exit ( mpc8xxx_wdt_exit ) ;
2006-01-14 13:20:50 -08:00
MODULE_AUTHOR ( " Dave Updegraff, Kumar Gala " ) ;
2008-07-03 23:51:36 -07:00
MODULE_DESCRIPTION ( " Driver for watchdog timer in MPC8xx/MPC83xx/MPC86xx "
" uProcessors " ) ;
2006-01-14 13:20:50 -08:00
MODULE_LICENSE ( " GPL " ) ;