2018-12-19 18:18:20 +03:00
// SPDX-License-Identifier: GPL-2.0+
/*
* Watchdog driver for TQMx86 PLD .
*
* The watchdog supports power of 2 timeouts from 1 to 4096 sec .
* Once started , it cannot be stopped .
*
* Based on the vendor code written by Vadim V . Vlasov
* < vvlasov @ dev . rtsoft . ru >
*/
# include <linux/io.h>
# include <linux/log2.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/timer.h>
# include <linux/watchdog.h>
/* default timeout (secs) */
# define WDT_TIMEOUT 32
static unsigned int timeout ;
module_param ( timeout , uint , 0 ) ;
MODULE_PARM_DESC ( timeout ,
" Watchdog timeout in seconds. (1<=timeout<=4096, default= "
__MODULE_STRING ( WDT_TIMEOUT ) " ) " ) ;
struct tqmx86_wdt {
struct watchdog_device wdd ;
void __iomem * io_base ;
} ;
# define TQMX86_WDCFG 0x00 /* Watchdog Configuration Register */
# define TQMX86_WDCS 0x01 /* Watchdog Config/Status Register */
static int tqmx86_wdt_start ( struct watchdog_device * wdd )
{
struct tqmx86_wdt * priv = watchdog_get_drvdata ( wdd ) ;
iowrite8 ( 0x81 , priv - > io_base + TQMX86_WDCS ) ;
return 0 ;
}
static int tqmx86_wdt_set_timeout ( struct watchdog_device * wdd , unsigned int t )
{
struct tqmx86_wdt * priv = watchdog_get_drvdata ( wdd ) ;
u8 val ;
t = roundup_pow_of_two ( t ) ;
val = ilog2 ( t ) | 0x90 ;
val + = 3 ; /* values 0,1,2 correspond to 0.125,0.25,0.5s timeouts */
iowrite8 ( val , priv - > io_base + TQMX86_WDCFG ) ;
wdd - > timeout = t ;
return 0 ;
}
static const struct watchdog_info tqmx86_wdt_info = {
. options = WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING ,
. identity = " TQMx86 Watchdog " ,
} ;
static struct watchdog_ops tqmx86_wdt_ops = {
. owner = THIS_MODULE ,
. start = tqmx86_wdt_start ,
. set_timeout = tqmx86_wdt_set_timeout ,
} ;
static int tqmx86_wdt_probe ( struct platform_device * pdev )
{
2019-04-10 19:27:43 +03:00
struct device * dev = & pdev - > dev ;
2018-12-19 18:18:20 +03:00
struct tqmx86_wdt * priv ;
struct resource * res ;
int err ;
2019-04-10 19:27:43 +03:00
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
2018-12-19 18:18:20 +03:00
if ( ! priv )
return - ENOMEM ;
res = platform_get_resource ( pdev , IORESOURCE_IO , 0 ) ;
2019-01-04 09:47:25 +03:00
if ( ! res )
return - ENODEV ;
2018-12-19 18:18:20 +03:00
2019-04-10 19:27:43 +03:00
priv - > io_base = devm_ioport_map ( dev , res - > start , resource_size ( res ) ) ;
2019-01-04 09:47:25 +03:00
if ( ! priv - > io_base )
return - ENOMEM ;
2018-12-19 18:18:20 +03:00
watchdog_set_drvdata ( & priv - > wdd , priv ) ;
2019-04-10 19:27:43 +03:00
priv - > wdd . parent = dev ;
2018-12-19 18:18:20 +03:00
priv - > wdd . info = & tqmx86_wdt_info ;
priv - > wdd . ops = & tqmx86_wdt_ops ;
priv - > wdd . min_timeout = 1 ;
priv - > wdd . max_timeout = 4096 ;
priv - > wdd . max_hw_heartbeat_ms = 4096 * 1000 ;
priv - > wdd . timeout = WDT_TIMEOUT ;
2019-04-10 19:27:43 +03:00
watchdog_init_timeout ( & priv - > wdd , timeout , dev ) ;
2018-12-19 18:18:20 +03:00
watchdog_set_nowayout ( & priv - > wdd , WATCHDOG_NOWAYOUT ) ;
tqmx86_wdt_set_timeout ( & priv - > wdd , priv - > wdd . timeout ) ;
2019-04-10 19:27:43 +03:00
err = devm_watchdog_register_device ( dev , & priv - > wdd ) ;
2018-12-19 18:18:20 +03:00
if ( err )
return err ;
2019-04-10 19:27:43 +03:00
dev_info ( dev , " TQMx86 watchdog \n " ) ;
2018-12-19 18:18:20 +03:00
return 0 ;
}
static struct platform_driver tqmx86_wdt_driver = {
. driver = {
. name = " tqmx86-wdt " ,
} ,
. probe = tqmx86_wdt_probe ,
} ;
module_platform_driver ( tqmx86_wdt_driver ) ;
MODULE_AUTHOR ( " Andrew Lunn <andrew@lunn.ch> " ) ;
MODULE_DESCRIPTION ( " TQMx86 Watchdog " ) ;
MODULE_ALIAS ( " platform:tqmx86-wdt " ) ;
MODULE_LICENSE ( " GPL " ) ;