2009-07-27 17:46:12 +04:00
/*
* Watchdog driver for the wm831x PMICs
*
* Copyright ( C ) 2009 Wolfson Microelectronics
*
* 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
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/types.h>
# include <linux/kernel.h>
2011-10-04 19:43:40 +04:00
# include <linux/slab.h>
2009-07-27 17:46:12 +04:00
# include <linux/platform_device.h>
# include <linux/watchdog.h>
# include <linux/uaccess.h>
# include <linux/gpio.h>
# include <linux/mfd/wm831x/core.h>
# include <linux/mfd/wm831x/pdata.h>
# include <linux/mfd/wm831x/watchdog.h>
2012-03-05 19:51:11 +04:00
static bool nowayout = WATCHDOG_NOWAYOUT ;
module_param ( nowayout , bool , 0 ) ;
2009-07-27 17:46:12 +04:00
MODULE_PARM_DESC ( nowayout ,
" Watchdog cannot be stopped once started (default= "
__MODULE_STRING ( WATCHDOG_NOWAYOUT ) " ) " ) ;
2011-10-04 19:43:40 +04:00
struct wm831x_wdt_drvdata {
struct watchdog_device wdt ;
struct wm831x * wm831x ;
struct mutex lock ;
int update_gpio ;
int update_state ;
} ;
2009-07-27 17:46:12 +04:00
/* We can't use the sub-second values here but they're included
* for completeness . */
static struct {
2011-10-04 19:43:40 +04:00
unsigned int time ; /* Seconds */
u16 val ; /* WDOG_TO value */
2009-07-27 17:46:12 +04:00
} wm831x_wdt_cfgs [ ] = {
{ 1 , 2 } ,
{ 2 , 3 } ,
{ 4 , 4 } ,
{ 8 , 5 } ,
{ 16 , 6 } ,
{ 32 , 7 } ,
{ 33 , 7 } , /* Actually 32.768s so include both, others round down */
} ;
2011-10-04 19:43:40 +04:00
static int wm831x_wdt_start ( struct watchdog_device * wdt_dev )
2009-07-27 17:46:12 +04:00
{
2011-10-04 19:43:40 +04:00
struct wm831x_wdt_drvdata * driver_data = watchdog_get_drvdata ( wdt_dev ) ;
struct wm831x * wm831x = driver_data - > wm831x ;
2009-07-27 17:46:12 +04:00
int ret ;
2011-10-04 19:43:40 +04:00
mutex_lock ( & driver_data - > lock ) ;
2009-07-27 17:46:12 +04:00
ret = wm831x_reg_unlock ( wm831x ) ;
if ( ret = = 0 ) {
ret = wm831x_set_bits ( wm831x , WM831X_WATCHDOG ,
WM831X_WDOG_ENA , WM831X_WDOG_ENA ) ;
wm831x_reg_lock ( wm831x ) ;
} else {
dev_err ( wm831x - > dev , " Failed to unlock security key: %d \n " ,
ret ) ;
}
2011-10-04 19:43:40 +04:00
mutex_unlock ( & driver_data - > lock ) ;
2009-07-27 17:46:12 +04:00
return ret ;
}
2011-10-04 19:43:40 +04:00
static int wm831x_wdt_stop ( struct watchdog_device * wdt_dev )
2009-07-27 17:46:12 +04:00
{
2011-10-04 19:43:40 +04:00
struct wm831x_wdt_drvdata * driver_data = watchdog_get_drvdata ( wdt_dev ) ;
struct wm831x * wm831x = driver_data - > wm831x ;
2009-07-27 17:46:12 +04:00
int ret ;
2011-10-04 19:43:40 +04:00
mutex_lock ( & driver_data - > lock ) ;
2009-07-27 17:46:12 +04:00
ret = wm831x_reg_unlock ( wm831x ) ;
if ( ret = = 0 ) {
ret = wm831x_set_bits ( wm831x , WM831X_WATCHDOG ,
WM831X_WDOG_ENA , 0 ) ;
wm831x_reg_lock ( wm831x ) ;
} else {
dev_err ( wm831x - > dev , " Failed to unlock security key: %d \n " ,
ret ) ;
}
2011-10-04 19:43:40 +04:00
mutex_unlock ( & driver_data - > lock ) ;
2009-07-27 17:46:12 +04:00
return ret ;
}
2011-10-04 19:43:40 +04:00
static int wm831x_wdt_ping ( struct watchdog_device * wdt_dev )
2009-07-27 17:46:12 +04:00
{
2011-10-04 19:43:40 +04:00
struct wm831x_wdt_drvdata * driver_data = watchdog_get_drvdata ( wdt_dev ) ;
struct wm831x * wm831x = driver_data - > wm831x ;
2009-07-27 17:46:12 +04:00
int ret ;
u16 reg ;
2011-10-04 19:43:40 +04:00
mutex_lock ( & driver_data - > lock ) ;
2009-07-27 17:46:12 +04:00
2011-10-04 19:43:40 +04:00
if ( driver_data - > update_gpio ) {
gpio_set_value_cansleep ( driver_data - > update_gpio ,
driver_data - > update_state ) ;
driver_data - > update_state = ! driver_data - > update_state ;
2009-07-27 17:46:12 +04:00
ret = 0 ;
goto out ;
}
reg = wm831x_reg_read ( wm831x , WM831X_WATCHDOG ) ;
if ( ! ( reg & WM831X_WDOG_RST_SRC ) ) {
dev_err ( wm831x - > dev , " Hardware watchdog update unsupported \n " ) ;
ret = - EINVAL ;
goto out ;
}
reg | = WM831X_WDOG_RESET ;
ret = wm831x_reg_unlock ( wm831x ) ;
if ( ret = = 0 ) {
ret = wm831x_reg_write ( wm831x , WM831X_WATCHDOG , reg ) ;
wm831x_reg_lock ( wm831x ) ;
} else {
dev_err ( wm831x - > dev , " Failed to unlock security key: %d \n " ,
ret ) ;
}
out :
2011-10-04 19:43:40 +04:00
mutex_unlock ( & driver_data - > lock ) ;
2009-07-27 17:46:12 +04:00
return ret ;
}
2011-10-04 19:43:40 +04:00
static int wm831x_wdt_set_timeout ( struct watchdog_device * wdt_dev ,
unsigned int timeout )
2009-07-27 17:46:12 +04:00
{
2011-10-04 19:43:40 +04:00
struct wm831x_wdt_drvdata * driver_data = watchdog_get_drvdata ( wdt_dev ) ;
struct wm831x * wm831x = driver_data - > wm831x ;
int ret , i ;
2009-07-27 17:46:12 +04:00
2011-10-04 19:43:40 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( wm831x_wdt_cfgs ) ; i + + )
if ( wm831x_wdt_cfgs [ i ] . time = = timeout )
break ;
if ( i = = ARRAY_SIZE ( wm831x_wdt_cfgs ) )
2011-11-16 15:52:12 +04:00
return - EINVAL ;
2009-07-27 17:46:12 +04:00
2011-10-04 19:43:40 +04:00
ret = wm831x_reg_unlock ( wm831x ) ;
if ( ret = = 0 ) {
ret = wm831x_set_bits ( wm831x , WM831X_WATCHDOG ,
WM831X_WDOG_TO_MASK ,
wm831x_wdt_cfgs [ i ] . val ) ;
wm831x_reg_lock ( wm831x ) ;
} else {
dev_err ( wm831x - > dev , " Failed to unlock security key: %d \n " ,
ret ) ;
2009-07-27 17:46:12 +04:00
}
2012-02-29 23:20:58 +04:00
wdt_dev - > timeout = timeout ;
2011-10-04 19:43:40 +04:00
return ret ;
2009-07-27 17:46:12 +04:00
}
2011-10-04 19:43:40 +04:00
static const struct watchdog_info wm831x_wdt_info = {
2009-07-27 17:46:12 +04:00
. options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE ,
. identity = " WM831x Watchdog " ,
} ;
2011-10-04 19:43:40 +04:00
static const struct watchdog_ops wm831x_wdt_ops = {
2009-07-27 17:46:12 +04:00
. owner = THIS_MODULE ,
2011-10-04 19:43:40 +04:00
. start = wm831x_wdt_start ,
. stop = wm831x_wdt_stop ,
. ping = wm831x_wdt_ping ,
. set_timeout = wm831x_wdt_set_timeout ,
2009-07-27 17:46:12 +04:00
} ;
2012-11-19 22:21:41 +04:00
static int wm831x_wdt_probe ( struct platform_device * pdev )
2009-07-27 17:46:12 +04:00
{
2011-10-04 19:43:40 +04:00
struct wm831x * wm831x = dev_get_drvdata ( pdev - > dev . parent ) ;
2013-07-30 14:58:51 +04:00
struct wm831x_pdata * chip_pdata = dev_get_platdata ( pdev - > dev . parent ) ;
2009-07-27 17:46:12 +04:00
struct wm831x_watchdog_pdata * pdata ;
2011-10-04 19:43:40 +04:00
struct wm831x_wdt_drvdata * driver_data ;
struct watchdog_device * wm831x_wdt ;
int reg , ret , i ;
2009-07-27 17:46:12 +04:00
ret = wm831x_reg_read ( wm831x , WM831X_WATCHDOG ) ;
if ( ret < 0 ) {
dev_err ( wm831x - > dev , " Failed to read watchdog status: %d \n " ,
ret ) ;
goto err ;
}
reg = ret ;
if ( reg & WM831X_WDOG_DEBUG )
dev_warn ( wm831x - > dev , " Watchdog is paused \n " ) ;
2011-12-14 22:23:19 +04:00
driver_data = devm_kzalloc ( & pdev - > dev , sizeof ( * driver_data ) ,
GFP_KERNEL ) ;
2011-10-04 19:43:40 +04:00
if ( ! driver_data ) {
ret = - ENOMEM ;
goto err ;
}
mutex_init ( & driver_data - > lock ) ;
driver_data - > wm831x = wm831x ;
wm831x_wdt = & driver_data - > wdt ;
wm831x_wdt - > info = & wm831x_wdt_info ;
wm831x_wdt - > ops = & wm831x_wdt_ops ;
2011-11-29 19:24:16 +04:00
watchdog_set_nowayout ( wm831x_wdt , nowayout ) ;
2011-10-04 19:43:40 +04:00
watchdog_set_drvdata ( wm831x_wdt , driver_data ) ;
reg = wm831x_reg_read ( wm831x , WM831X_WATCHDOG ) ;
reg & = WM831X_WDOG_TO_MASK ;
for ( i = 0 ; i < ARRAY_SIZE ( wm831x_wdt_cfgs ) ; i + + )
if ( wm831x_wdt_cfgs [ i ] . val = = reg )
break ;
if ( i = = ARRAY_SIZE ( wm831x_wdt_cfgs ) )
dev_warn ( wm831x - > dev ,
" Unknown watchdog timeout: %x \n " , reg ) ;
else
wm831x_wdt - > timeout = wm831x_wdt_cfgs [ i ] . time ;
2009-07-27 17:46:12 +04:00
/* Apply any configuration */
2013-07-30 14:58:51 +04:00
if ( chip_pdata )
2009-07-27 17:46:12 +04:00
pdata = chip_pdata - > watchdog ;
2013-07-30 14:58:51 +04:00
else
2009-07-27 17:46:12 +04:00
pdata = NULL ;
if ( pdata ) {
reg & = ~ ( WM831X_WDOG_SECACT_MASK | WM831X_WDOG_PRIMACT_MASK |
WM831X_WDOG_RST_SRC ) ;
reg | = pdata - > primary < < WM831X_WDOG_PRIMACT_SHIFT ;
reg | = pdata - > secondary < < WM831X_WDOG_SECACT_SHIFT ;
reg | = pdata - > software < < WM831X_WDOG_RST_SRC_SHIFT ;
if ( pdata - > update_gpio ) {
2013-04-29 13:31:20 +04:00
ret = devm_gpio_request_one ( & pdev - > dev ,
pdata - > update_gpio ,
GPIOF_OUT_INIT_LOW ,
" Watchdog update " ) ;
2009-07-27 17:46:12 +04:00
if ( ret < 0 ) {
dev_err ( wm831x - > dev ,
" Failed to request update GPIO: %d \n " ,
ret ) ;
2011-12-14 22:23:19 +04:00
goto err ;
2009-07-27 17:46:12 +04:00
}
2011-10-04 19:43:40 +04:00
driver_data - > update_gpio = pdata - > update_gpio ;
2009-07-27 17:46:12 +04:00
/* Make sure the watchdog takes hardware updates */
reg | = WM831X_WDOG_RST_SRC ;
}
ret = wm831x_reg_unlock ( wm831x ) ;
if ( ret = = 0 ) {
ret = wm831x_reg_write ( wm831x , WM831X_WATCHDOG , reg ) ;
wm831x_reg_lock ( wm831x ) ;
} else {
dev_err ( wm831x - > dev ,
" Failed to unlock security key: %d \n " , ret ) ;
2013-04-29 13:31:20 +04:00
goto err ;
2009-07-27 17:46:12 +04:00
}
}
2011-10-04 19:43:40 +04:00
ret = watchdog_register_device ( & driver_data - > wdt ) ;
2009-07-27 17:46:12 +04:00
if ( ret ! = 0 ) {
2011-10-04 19:43:40 +04:00
dev_err ( wm831x - > dev , " watchdog_register_device() failed: %d \n " ,
ret ) ;
2013-04-29 13:31:20 +04:00
goto err ;
2009-07-27 17:46:12 +04:00
}
2013-05-23 14:44:53 +04:00
platform_set_drvdata ( pdev , driver_data ) ;
2011-10-04 19:43:40 +04:00
2009-07-27 17:46:12 +04:00
return 0 ;
err :
return ret ;
}
2012-11-19 22:26:24 +04:00
static int wm831x_wdt_remove ( struct platform_device * pdev )
2009-07-27 17:46:12 +04:00
{
2013-05-23 14:44:53 +04:00
struct wm831x_wdt_drvdata * driver_data = platform_get_drvdata ( pdev ) ;
2011-10-04 19:43:40 +04:00
watchdog_unregister_device ( & driver_data - > wdt ) ;
2009-07-27 17:46:12 +04:00
return 0 ;
}
static struct platform_driver wm831x_wdt_driver = {
. probe = wm831x_wdt_probe ,
2012-11-19 22:21:12 +04:00
. remove = wm831x_wdt_remove ,
2009-07-27 17:46:12 +04:00
. driver = {
. name = " wm831x-watchdog " ,
} ,
} ;
2011-11-23 19:22:36 +04:00
module_platform_driver ( wm831x_wdt_driver ) ;
2009-07-27 17:46:12 +04:00
MODULE_AUTHOR ( " Mark Brown " ) ;
MODULE_DESCRIPTION ( " WM831x Watchdog " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:wm831x-watchdog " ) ;