2008-06-26 12:47:45 +04:00
/*
2009-06-01 21:56:02 +04:00
* drivers / watchdog / orion_wdt . c
2008-06-26 12:47:45 +04:00
*
2009-06-01 21:56:02 +04:00
* Watchdog driver for Orion / Kirkwood processors
2008-06-26 12:47:45 +04:00
*
* Author : Sylver Bruneau < sylver . bruneau @ googlemail . com >
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
2012-02-16 03:06:19 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2008-06-26 12:47:45 +04:00
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/miscdevice.h>
2009-02-25 01:59:22 +03:00
# include <linux/platform_device.h>
2008-06-26 12:47:45 +04:00
# include <linux/watchdog.h>
# include <linux/init.h>
# include <linux/io.h>
2008-10-02 13:32:45 +04:00
# include <linux/spinlock.h>
2012-03-04 19:57:31 +04:00
# include <linux/clk.h>
2012-03-26 07:14:29 +04:00
# include <linux/err.h>
2012-06-10 17:20:06 +04:00
# include <linux/of.h>
2009-04-22 23:08:17 +04:00
# include <mach/bridge-regs.h>
2008-06-26 12:47:45 +04:00
/*
* Watchdog timer block registers .
*/
2012-03-15 04:33:26 +04:00
# define TIMER_CTRL 0x0000
2012-03-26 07:14:29 +04:00
# define WDT_EN 0x0010
2012-03-15 04:33:26 +04:00
# define WDT_VAL 0x0024
2008-06-26 12:47:45 +04:00
2009-02-25 01:59:22 +03:00
# define WDT_MAX_CYCLE_COUNT 0xffffffff
2008-06-26 12:47:45 +04:00
# define WDT_IN_USE 0
# define WDT_OK_TO_CLOSE 1
2012-03-05 19:51:11 +04:00
static bool nowayout = WATCHDOG_NOWAYOUT ;
2009-02-25 01:59:22 +03:00
static int heartbeat = - 1 ; /* module parameter (seconds) */
static unsigned int wdt_max_duration ; /* (seconds) */
2012-03-04 19:57:31 +04:00
static struct clk * clk ;
2009-02-25 01:59:22 +03:00
static unsigned int wdt_tclk ;
2012-03-15 04:33:26 +04:00
static void __iomem * wdt_reg ;
2011-11-29 09:54:01 +04:00
static DEFINE_SPINLOCK ( wdt_lock ) ;
2008-06-26 12:47:45 +04:00
2012-03-26 07:14:29 +04:00
static int orion_wdt_ping ( struct watchdog_device * wdt_dev )
2009-02-20 21:44:59 +03:00
{
spin_lock ( & wdt_lock ) ;
/* Reload watchdog duration */
2012-03-26 07:14:29 +04:00
writel ( wdt_tclk * wdt_dev - > timeout , wdt_reg + WDT_VAL ) ;
2009-02-20 21:44:59 +03:00
spin_unlock ( & wdt_lock ) ;
2012-03-26 07:14:29 +04:00
return 0 ;
2009-02-20 21:44:59 +03:00
}
2012-03-26 07:14:29 +04:00
static int orion_wdt_start ( struct watchdog_device * wdt_dev )
2008-06-26 12:47:45 +04:00
{
u32 reg ;
2008-10-02 13:32:45 +04:00
spin_lock ( & wdt_lock ) ;
2008-06-26 12:47:45 +04:00
/* Set watchdog duration */
2012-03-26 07:14:29 +04:00
writel ( wdt_tclk * wdt_dev - > timeout , wdt_reg + WDT_VAL ) ;
2008-06-26 12:47:45 +04:00
/* Clear watchdog timer interrupt */
reg = readl ( BRIDGE_CAUSE ) ;
reg & = ~ WDT_INT_REQ ;
writel ( reg , BRIDGE_CAUSE ) ;
/* Enable watchdog timer */
2012-03-15 04:33:26 +04:00
reg = readl ( wdt_reg + TIMER_CTRL ) ;
2008-06-26 12:47:45 +04:00
reg | = WDT_EN ;
2012-03-15 04:33:26 +04:00
writel ( reg , wdt_reg + TIMER_CTRL ) ;
2008-06-26 12:47:45 +04:00
/* Enable reset on watchdog */
2009-06-01 15:38:33 +04:00
reg = readl ( RSTOUTn_MASK ) ;
reg | = WDT_RESET_OUT_EN ;
writel ( reg , RSTOUTn_MASK ) ;
2008-10-02 13:32:45 +04:00
spin_unlock ( & wdt_lock ) ;
2012-03-26 07:14:29 +04:00
return 0 ;
2008-06-26 12:47:45 +04:00
}
2012-03-26 07:14:29 +04:00
static int orion_wdt_stop ( struct watchdog_device * wdt_dev )
2008-06-26 12:47:45 +04:00
{
u32 reg ;
2008-10-02 13:32:45 +04:00
spin_lock ( & wdt_lock ) ;
2008-06-26 12:47:45 +04:00
/* Disable reset on watchdog */
2009-06-01 15:38:33 +04:00
reg = readl ( RSTOUTn_MASK ) ;
reg & = ~ WDT_RESET_OUT_EN ;
writel ( reg , RSTOUTn_MASK ) ;
2008-06-26 12:47:45 +04:00
/* Disable watchdog timer */
2012-03-15 04:33:26 +04:00
reg = readl ( wdt_reg + TIMER_CTRL ) ;
2008-06-26 12:47:45 +04:00
reg & = ~ WDT_EN ;
2012-03-15 04:33:26 +04:00
writel ( reg , wdt_reg + TIMER_CTRL ) ;
2008-10-02 13:32:45 +04:00
spin_unlock ( & wdt_lock ) ;
2012-03-26 07:14:29 +04:00
return 0 ;
2008-10-02 13:32:45 +04:00
}
2012-03-26 07:14:29 +04:00
static unsigned int orion_wdt_get_timeleft ( struct watchdog_device * wdt_dev )
2008-10-02 13:32:45 +04:00
{
2012-03-26 07:14:29 +04:00
unsigned int time_left ;
2008-10-02 13:32:45 +04:00
spin_lock ( & wdt_lock ) ;
2012-03-26 07:14:29 +04:00
time_left = readl ( wdt_reg + WDT_VAL ) / wdt_tclk ;
2008-10-02 13:32:45 +04:00
spin_unlock ( & wdt_lock ) ;
2008-06-26 12:47:45 +04:00
2012-03-26 07:14:29 +04:00
return time_left ;
2008-06-26 12:47:45 +04:00
}
2012-03-26 07:14:29 +04:00
static int orion_wdt_set_timeout ( struct watchdog_device * wdt_dev ,
unsigned int timeout )
2008-06-26 12:47:45 +04:00
{
2012-03-26 07:14:29 +04:00
wdt_dev - > timeout = timeout ;
2009-02-20 21:44:59 +03:00
return 0 ;
}
2012-03-26 07:14:29 +04:00
static const struct watchdog_info orion_wdt_info = {
. options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE ,
. identity = " Orion Watchdog " ,
2008-06-26 12:47:45 +04:00
} ;
2012-03-26 07:14:29 +04:00
static const struct watchdog_ops orion_wdt_ops = {
. owner = THIS_MODULE ,
. start = orion_wdt_start ,
. stop = orion_wdt_stop ,
. ping = orion_wdt_ping ,
. set_timeout = orion_wdt_set_timeout ,
. get_timeleft = orion_wdt_get_timeleft ,
2008-06-26 12:47:45 +04:00
} ;
2012-03-26 07:14:29 +04:00
static struct watchdog_device orion_wdt = {
. info = & orion_wdt_info ,
. ops = & orion_wdt_ops ,
2008-06-26 12:47:45 +04:00
} ;
2012-11-19 22:21:41 +04:00
static int orion_wdt_probe ( struct platform_device * pdev )
2008-06-26 12:47:45 +04:00
{
2012-03-15 04:33:26 +04:00
struct resource * res ;
2008-06-26 12:47:45 +04:00
int ret ;
2012-03-26 07:14:29 +04:00
clk = devm_clk_get ( & pdev - > dev , NULL ) ;
2012-03-04 19:57:31 +04:00
if ( IS_ERR ( clk ) ) {
2012-03-26 07:14:29 +04:00
dev_err ( & pdev - > dev , " Orion Watchdog missing clock \n " ) ;
2009-02-25 01:59:22 +03:00
return - ENODEV ;
}
2012-03-04 19:57:31 +04:00
clk_prepare_enable ( clk ) ;
wdt_tclk = clk_get_rate ( clk ) ;
2009-02-25 01:59:22 +03:00
2012-03-15 04:33:26 +04:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2012-03-26 07:14:29 +04:00
wdt_reg = devm_ioremap ( & pdev - > dev , res - > start , resource_size ( res ) ) ;
if ( ! wdt_reg )
return - ENOMEM ;
2009-02-25 01:59:22 +03:00
wdt_max_duration = WDT_MAX_CYCLE_COUNT / wdt_tclk ;
2012-03-26 07:14:29 +04:00
if ( ( heartbeat < 1 ) | | ( heartbeat > wdt_max_duration ) )
2009-02-25 01:59:22 +03:00
heartbeat = wdt_max_duration ;
2008-10-02 13:32:45 +04:00
2012-03-26 07:14:29 +04:00
orion_wdt . timeout = heartbeat ;
orion_wdt . min_timeout = 1 ;
orion_wdt . max_timeout = wdt_max_duration ;
watchdog_set_nowayout ( & orion_wdt , nowayout ) ;
ret = watchdog_register_device ( & orion_wdt ) ;
if ( ret ) {
clk_disable_unprepare ( clk ) ;
2009-02-25 01:59:22 +03:00
return ret ;
2012-03-26 07:14:29 +04:00
}
2009-02-25 01:59:22 +03:00
2012-02-16 03:06:19 +04:00
pr_info ( " Initial timeout %d sec%s \n " ,
heartbeat , nowayout ? " , nowayout " : " " ) ;
2009-02-25 01:59:22 +03:00
return 0 ;
}
2012-11-19 22:26:24 +04:00
static int orion_wdt_remove ( struct platform_device * pdev )
2009-02-25 01:59:22 +03:00
{
2012-03-26 07:14:29 +04:00
watchdog_unregister_device ( & orion_wdt ) ;
2012-03-04 19:57:31 +04:00
clk_disable_unprepare ( clk ) ;
2012-03-26 07:14:29 +04:00
return 0 ;
2008-06-26 12:47:45 +04:00
}
2009-06-01 21:56:02 +04:00
static void orion_wdt_shutdown ( struct platform_device * pdev )
2009-02-20 21:44:59 +03:00
{
2012-03-26 07:14:29 +04:00
orion_wdt_stop ( & orion_wdt ) ;
2009-02-20 21:44:59 +03:00
}
2012-11-19 22:24:05 +04:00
static const struct of_device_id orion_wdt_of_match_table [ ] = {
2012-06-10 17:20:06 +04:00
{ . compatible = " marvell,orion-wdt " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , orion_wdt_of_match_table ) ;
2009-06-01 21:56:02 +04:00
static struct platform_driver orion_wdt_driver = {
. probe = orion_wdt_probe ,
2012-11-19 22:21:12 +04:00
. remove = orion_wdt_remove ,
2009-06-01 21:56:02 +04:00
. shutdown = orion_wdt_shutdown ,
2009-02-25 01:59:22 +03:00
. driver = {
. owner = THIS_MODULE ,
2009-06-01 21:56:02 +04:00
. name = " orion_wdt " ,
2012-06-10 17:20:06 +04:00
. of_match_table = of_match_ptr ( orion_wdt_of_match_table ) ,
2009-02-25 01:59:22 +03:00
} ,
} ;
2011-11-29 09:56:27 +04:00
module_platform_driver ( orion_wdt_driver ) ;
2008-06-26 12:47:45 +04:00
MODULE_AUTHOR ( " Sylver Bruneau <sylver.bruneau@googlemail.com> " ) ;
2009-06-01 21:56:02 +04:00
MODULE_DESCRIPTION ( " Orion Processor Watchdog " ) ;
2008-06-26 12:47:45 +04:00
module_param ( heartbeat , int , 0 ) ;
2009-02-20 21:44:59 +03:00
MODULE_PARM_DESC ( heartbeat , " Initial watchdog heartbeat in seconds " ) ;
2008-06-26 12:47:45 +04:00
2012-03-05 19:51:11 +04:00
module_param ( nowayout , bool , 0 ) ;
2009-02-20 21:44:59 +03:00
MODULE_PARM_DESC ( nowayout , " Watchdog cannot be stopped once started (default= "
__MODULE_STRING ( WATCHDOG_NOWAYOUT ) " ) " ) ;
2008-06-26 12:47:45 +04:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS_MISCDEV ( WATCHDOG_MINOR ) ;