2019-05-28 20:10:09 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2014-04-16 00:06:05 +04:00
/*
* intel - mid_wdt : generic Intel MID SCU watchdog driver
*
* Platforms supported so far :
* - Merrifield only
*
* Copyright ( C ) 2014 Intel Corporation . All rights reserved .
* Contact : David Cohen < david . a . cohen @ linux . intel . com >
*/
# include <linux/interrupt.h>
# include <linux/module.h>
# include <linux/nmi.h>
# include <linux/platform_device.h>
# include <linux/watchdog.h>
# include <linux/platform_data/intel-mid_wdt.h>
# include <asm/intel_scu_ipc.h>
# include <asm/intel-mid.h>
# define IPC_WATCHDOG 0xf8
# define MID_WDT_PRETIMEOUT 15
# define MID_WDT_TIMEOUT_MIN (1 + MID_WDT_PRETIMEOUT)
# define MID_WDT_TIMEOUT_MAX 170
# define MID_WDT_DEFAULT_TIMEOUT 90
/* SCU watchdog messages */
enum {
SCU_WATCHDOG_START = 0 ,
SCU_WATCHDOG_STOP ,
SCU_WATCHDOG_KEEPALIVE ,
} ;
static inline int wdt_command ( int sub , u32 * in , int inlen )
{
return intel_scu_ipc_command ( IPC_WATCHDOG , sub , in , inlen , NULL , 0 ) ;
}
static int wdt_start ( struct watchdog_device * wd )
{
2016-11-18 18:24:41 +03:00
struct device * dev = watchdog_get_drvdata ( wd ) ;
2014-04-16 00:06:05 +04:00
int ret , in_size ;
int timeout = wd - > timeout ;
struct ipc_wd_start {
u32 pretimeout ;
u32 timeout ;
} ipc_wd_start = { timeout - MID_WDT_PRETIMEOUT , timeout } ;
/*
* SCU expects the input size for watchdog IPC to
* be based on 4 bytes
*/
in_size = DIV_ROUND_UP ( sizeof ( ipc_wd_start ) , 4 ) ;
ret = wdt_command ( SCU_WATCHDOG_START , ( u32 * ) & ipc_wd_start , in_size ) ;
2016-11-18 18:24:41 +03:00
if ( ret )
2014-04-16 00:06:05 +04:00
dev_crit ( dev , " error starting watchdog: %d \n " , ret ) ;
return ret ;
}
static int wdt_ping ( struct watchdog_device * wd )
{
2016-11-18 18:24:41 +03:00
struct device * dev = watchdog_get_drvdata ( wd ) ;
2014-04-16 00:06:05 +04:00
int ret ;
ret = wdt_command ( SCU_WATCHDOG_KEEPALIVE , NULL , 0 ) ;
2016-11-18 18:24:41 +03:00
if ( ret )
dev_crit ( dev , " Error executing keepalive: %d \n " , ret ) ;
2014-04-16 00:06:05 +04:00
return ret ;
}
static int wdt_stop ( struct watchdog_device * wd )
{
2016-11-18 18:24:41 +03:00
struct device * dev = watchdog_get_drvdata ( wd ) ;
2014-04-16 00:06:05 +04:00
int ret ;
ret = wdt_command ( SCU_WATCHDOG_STOP , NULL , 0 ) ;
2016-11-18 18:24:41 +03:00
if ( ret )
dev_crit ( dev , " Error stopping watchdog: %d \n " , ret ) ;
2014-04-16 00:06:05 +04:00
return ret ;
}
static irqreturn_t mid_wdt_irq ( int irq , void * dev_id )
{
panic ( " Kernel Watchdog " ) ;
/* This code should not be reached */
return IRQ_HANDLED ;
}
static const struct watchdog_info mid_wdt_info = {
. identity = " Intel MID SCU watchdog " ,
2015-10-06 02:49:58 +03:00
. options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE ,
2014-04-16 00:06:05 +04:00
} ;
static const struct watchdog_ops mid_wdt_ops = {
. owner = THIS_MODULE ,
. start = wdt_start ,
. stop = wdt_stop ,
. ping = wdt_ping ,
} ;
static int mid_wdt_probe ( struct platform_device * pdev )
{
2019-04-08 22:38:43 +03:00
struct device * dev = & pdev - > dev ;
2014-04-16 00:06:05 +04:00
struct watchdog_device * wdt_dev ;
2019-04-08 22:38:43 +03:00
struct intel_mid_wdt_pdata * pdata = dev - > platform_data ;
2014-04-16 00:06:05 +04:00
int ret ;
if ( ! pdata ) {
2019-04-08 22:38:43 +03:00
dev_err ( dev , " missing platform data \n " ) ;
2014-04-16 00:06:05 +04:00
return - EINVAL ;
}
if ( pdata - > probe ) {
ret = pdata - > probe ( pdev ) ;
if ( ret )
return ret ;
}
2019-04-08 22:38:43 +03:00
wdt_dev = devm_kzalloc ( dev , sizeof ( * wdt_dev ) , GFP_KERNEL ) ;
2014-04-16 00:06:05 +04:00
if ( ! wdt_dev )
return - ENOMEM ;
wdt_dev - > info = & mid_wdt_info ;
wdt_dev - > ops = & mid_wdt_ops ;
wdt_dev - > min_timeout = MID_WDT_TIMEOUT_MIN ;
wdt_dev - > max_timeout = MID_WDT_TIMEOUT_MAX ;
wdt_dev - > timeout = MID_WDT_DEFAULT_TIMEOUT ;
2019-04-08 22:38:43 +03:00
wdt_dev - > parent = dev ;
2014-04-16 00:06:05 +04:00
2019-09-24 17:31:16 +03:00
watchdog_set_nowayout ( wdt_dev , WATCHDOG_NOWAYOUT ) ;
2019-04-08 22:38:43 +03:00
watchdog_set_drvdata ( wdt_dev , dev ) ;
2014-04-16 00:06:05 +04:00
2019-04-08 22:38:43 +03:00
ret = devm_request_irq ( dev , pdata - > irq , mid_wdt_irq ,
2014-04-16 00:06:05 +04:00
IRQF_SHARED | IRQF_NO_SUSPEND , " watchdog " ,
wdt_dev ) ;
if ( ret ) {
2019-04-08 22:38:43 +03:00
dev_err ( dev , " error requesting warning irq %d \n " , pdata - > irq ) ;
2014-04-16 00:06:05 +04:00
return ret ;
}
2017-03-11 01:22:17 +03:00
/*
* The firmware followed by U - Boot leaves the watchdog running
* with the default threshold which may vary . When we get here
* we should make a decision to prevent any side effects before
* user space daemon will take care of it . The best option ,
* taking into consideration that there is no way to read values
* back from hardware , is to enforce watchdog being run with
* deterministic values .
*/
ret = wdt_start ( wdt_dev ) ;
if ( ret )
return ret ;
/* Make sure the watchdog is serviced */
set_bit ( WDOG_HW_RUNNING , & wdt_dev - > status ) ;
2016-11-18 17:50:02 +03:00
2019-04-08 22:38:43 +03:00
ret = devm_watchdog_register_device ( dev , wdt_dev ) ;
2019-05-19 00:27:33 +03:00
if ( ret )
2014-04-16 00:06:05 +04:00
return ret ;
2019-04-08 22:38:43 +03:00
dev_info ( dev , " Intel MID watchdog device probed \n " ) ;
2014-04-16 00:06:05 +04:00
return 0 ;
}
static struct platform_driver mid_wdt_driver = {
. probe = mid_wdt_probe ,
. driver = {
. name = " intel_mid_wdt " ,
} ,
} ;
module_platform_driver ( mid_wdt_driver ) ;
MODULE_AUTHOR ( " David Cohen <david.a.cohen@linux.intel.com> " ) ;
MODULE_DESCRIPTION ( " Watchdog Driver for Intel MID platform " ) ;
MODULE_LICENSE ( " GPL " ) ;