2018-07-16 10:25:10 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Watchdog driver for the MEN z069 IP - Core
*
* Copyright ( C ) 2018 Johannes Thumshirn < jth @ kernel . org >
*/
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/mcb.h>
# include <linux/module.h>
# include <linux/watchdog.h>
struct men_z069_drv {
struct watchdog_device wdt ;
void __iomem * base ;
struct resource * mem ;
} ;
# define MEN_Z069_WTR 0x10
# define MEN_Z069_WTR_WDEN BIT(15)
# define MEN_Z069_WTR_WDET_MASK 0x7fff
# define MEN_Z069_WVR 0x14
# define MEN_Z069_TIMER_FREQ 500 /* 500 Hz */
# define MEN_Z069_WDT_COUNTER_MIN 1
# define MEN_Z069_WDT_COUNTER_MAX 0x7fff
# define MEN_Z069_DEFAULT_TIMEOUT 30
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 ) " ) " ) ;
static int men_z069_wdt_start ( struct watchdog_device * wdt )
{
struct men_z069_drv * drv = watchdog_get_drvdata ( wdt ) ;
u16 val ;
val = readw ( drv - > base + MEN_Z069_WTR ) ;
val | = MEN_Z069_WTR_WDEN ;
writew ( val , drv - > base + MEN_Z069_WTR ) ;
return 0 ;
}
static int men_z069_wdt_stop ( struct watchdog_device * wdt )
{
struct men_z069_drv * drv = watchdog_get_drvdata ( wdt ) ;
u16 val ;
val = readw ( drv - > base + MEN_Z069_WTR ) ;
val & = ~ MEN_Z069_WTR_WDEN ;
writew ( val , drv - > base + MEN_Z069_WTR ) ;
return 0 ;
}
static int men_z069_wdt_ping ( struct watchdog_device * wdt )
{
struct men_z069_drv * drv = watchdog_get_drvdata ( wdt ) ;
u16 val ;
/* The watchdog trigger value toggles between 0x5555 and 0xaaaa */
val = readw ( drv - > base + MEN_Z069_WVR ) ;
val ^ = 0xffff ;
writew ( val , drv - > base + MEN_Z069_WVR ) ;
return 0 ;
}
static int men_z069_wdt_set_timeout ( struct watchdog_device * wdt ,
unsigned int timeout )
{
struct men_z069_drv * drv = watchdog_get_drvdata ( wdt ) ;
u16 reg , val , ena ;
wdt - > timeout = timeout ;
val = timeout * MEN_Z069_TIMER_FREQ ;
2023-04-18 20:25:31 +03:00
reg = readw ( drv - > base + MEN_Z069_WTR ) ;
2018-07-16 10:25:10 +03:00
ena = reg & MEN_Z069_WTR_WDEN ;
reg = ena | val ;
writew ( reg , drv - > base + MEN_Z069_WTR ) ;
return 0 ;
}
static const struct watchdog_info men_z069_info = {
. options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE ,
. identity = " MEN z069 Watchdog " ,
} ;
static const struct watchdog_ops men_z069_ops = {
. owner = THIS_MODULE ,
. start = men_z069_wdt_start ,
. stop = men_z069_wdt_stop ,
. ping = men_z069_wdt_ping ,
. set_timeout = men_z069_wdt_set_timeout ,
} ;
static int men_z069_probe ( struct mcb_device * dev ,
const struct mcb_device_id * id )
{
struct men_z069_drv * drv ;
struct resource * mem ;
drv = devm_kzalloc ( & dev - > dev , sizeof ( struct men_z069_drv ) , GFP_KERNEL ) ;
if ( ! drv )
return - ENOMEM ;
mem = mcb_request_mem ( dev , " z069-wdt " ) ;
if ( IS_ERR ( mem ) )
return PTR_ERR ( mem ) ;
drv - > base = devm_ioremap ( & dev - > dev , mem - > start , resource_size ( mem ) ) ;
if ( drv - > base = = NULL )
goto release_mem ;
drv - > mem = mem ;
2023-04-18 20:25:30 +03:00
drv - > wdt . info = & men_z069_info ;
drv - > wdt . ops = & men_z069_ops ;
drv - > wdt . timeout = MEN_Z069_DEFAULT_TIMEOUT ;
drv - > wdt . min_timeout = 1 ;
drv - > wdt . max_timeout = MEN_Z069_WDT_COUNTER_MAX / MEN_Z069_TIMER_FREQ ;
2018-07-16 10:25:10 +03:00
watchdog_init_timeout ( & drv - > wdt , 0 , & dev - > dev ) ;
watchdog_set_nowayout ( & drv - > wdt , nowayout ) ;
watchdog_set_drvdata ( & drv - > wdt , drv ) ;
drv - > wdt . parent = & dev - > dev ;
mcb_set_drvdata ( dev , drv ) ;
2023-04-18 20:25:30 +03:00
return watchdog_register_device ( & drv - > wdt ) ;
2018-07-16 10:25:10 +03:00
release_mem :
mcb_release_mem ( mem ) ;
return - ENOMEM ;
}
static void men_z069_remove ( struct mcb_device * dev )
{
struct men_z069_drv * drv = mcb_get_drvdata ( dev ) ;
watchdog_unregister_device ( & drv - > wdt ) ;
mcb_release_mem ( drv - > mem ) ;
}
static const struct mcb_device_id men_z069_ids [ ] = {
{ . device = 0x45 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( mcb , men_z069_ids ) ;
static struct mcb_driver men_z069_driver = {
. driver = {
. name = " z069-wdt " ,
} ,
. probe = men_z069_probe ,
. remove = men_z069_remove ,
. id_table = men_z069_ids ,
} ;
module_mcb_driver ( men_z069_driver ) ;
MODULE_AUTHOR ( " Johannes Thumshirn <jth@kernel.org> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " mcb:16z069 " ) ;
2019-10-16 13:01:58 +03:00
MODULE_IMPORT_NS ( MCB ) ;