2011-06-03 01:13:11 +04:00
/*
2013-05-31 09:56:33 +04:00
* Watchdog Device Driver for Xilinx axi / xps_timebase_wdt
*
2014-02-12 17:34:32 +04:00
* ( C ) Copyright 2013 - 2014 Xilinx , Inc .
2013-05-31 09:56:33 +04:00
* ( C ) Copyright 2011 ( Alejandro Cabrera < aldaya @ gmail . com > )
*
* 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 ; either version
* 2 of the License , or ( at your option ) any later version .
*/
2011-06-03 01:13:11 +04:00
2014-02-12 17:34:34 +04:00
# include <linux/err.h>
2011-06-03 01:13:11 +04:00
# include <linux/module.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/ioport.h>
# include <linux/watchdog.h>
# include <linux/io.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/of_address.h>
/* Register offsets for the Wdt device */
# define XWT_TWCSR0_OFFSET 0x0 /* Control/Status Register0 */
# define XWT_TWCSR1_OFFSET 0x4 /* Control/Status Register1 */
# define XWT_TBR_OFFSET 0x8 /* Timebase Register Offset */
/* Control/Status Register Masks */
# define XWT_CSR0_WRS_MASK 0x00000008 /* Reset status */
# define XWT_CSR0_WDS_MASK 0x00000004 /* Timer state */
# define XWT_CSR0_EWDT1_MASK 0x00000002 /* Enable bit 1 */
/* Control/Status Register 0/1 bits */
# define XWT_CSRX_EWDT2_MASK 0x00000001 /* Enable bit 2 */
/* SelfTest constants */
# define XWT_MAX_SELFTEST_LOOP_COUNT 0x00010000
# define XWT_TIMER_FAILED 0xFFFFFFFF
# define WATCHDOG_NAME "Xilinx Watchdog"
struct xwdt_device {
void __iomem * base ;
u32 wdt_interval ;
2014-02-12 17:41:19 +04:00
spinlock_t spinlock ;
struct watchdog_device xilinx_wdt_wdd ;
2011-06-03 01:13:11 +04:00
} ;
2014-02-12 17:34:32 +04:00
static int xilinx_wdt_start ( struct watchdog_device * wdd )
2011-06-03 01:13:11 +04:00
{
2014-02-12 17:34:33 +04:00
u32 control_status_reg ;
2014-02-12 17:41:19 +04:00
struct xwdt_device * xdev = watchdog_get_drvdata ( wdd ) ;
2014-02-12 17:34:33 +04:00
2014-02-12 17:41:19 +04:00
spin_lock ( & xdev - > spinlock ) ;
2011-06-03 01:13:11 +04:00
/* Clean previous status and enable the watchdog timer */
2014-02-12 17:41:19 +04:00
control_status_reg = ioread32 ( xdev - > base + XWT_TWCSR0_OFFSET ) ;
2011-06-03 01:13:11 +04:00
control_status_reg | = ( XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK ) ;
iowrite32 ( ( control_status_reg | XWT_CSR0_EWDT1_MASK ) ,
2014-02-12 17:41:19 +04:00
xdev - > base + XWT_TWCSR0_OFFSET ) ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:41:19 +04:00
iowrite32 ( XWT_CSRX_EWDT2_MASK , xdev - > base + XWT_TWCSR1_OFFSET ) ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:41:19 +04:00
spin_unlock ( & xdev - > spinlock ) ;
2014-02-12 17:34:32 +04:00
return 0 ;
2011-06-03 01:13:11 +04:00
}
2014-02-12 17:34:32 +04:00
static int xilinx_wdt_stop ( struct watchdog_device * wdd )
2011-06-03 01:13:11 +04:00
{
2014-02-12 17:34:33 +04:00
u32 control_status_reg ;
2014-02-12 17:41:19 +04:00
struct xwdt_device * xdev = watchdog_get_drvdata ( wdd ) ;
2014-02-12 17:34:33 +04:00
2014-02-12 17:41:19 +04:00
spin_lock ( & xdev - > spinlock ) ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:41:19 +04:00
control_status_reg = ioread32 ( xdev - > base + XWT_TWCSR0_OFFSET ) ;
2011-06-03 01:13:11 +04:00
iowrite32 ( ( control_status_reg & ~ XWT_CSR0_EWDT1_MASK ) ,
2014-02-12 17:41:19 +04:00
xdev - > base + XWT_TWCSR0_OFFSET ) ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:41:19 +04:00
iowrite32 ( 0 , xdev - > base + XWT_TWCSR1_OFFSET ) ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:41:19 +04:00
spin_unlock ( & xdev - > spinlock ) ;
2012-02-16 03:06:19 +04:00
pr_info ( " Stopped! \n " ) ;
2014-02-12 17:34:32 +04:00
return 0 ;
2011-06-03 01:13:11 +04:00
}
2014-02-12 17:34:32 +04:00
static int xilinx_wdt_keepalive ( struct watchdog_device * wdd )
2011-06-03 01:13:11 +04:00
{
2014-02-12 17:34:33 +04:00
u32 control_status_reg ;
2014-02-12 17:41:19 +04:00
struct xwdt_device * xdev = watchdog_get_drvdata ( wdd ) ;
2014-02-12 17:34:33 +04:00
2014-02-12 17:41:19 +04:00
spin_lock ( & xdev - > spinlock ) ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:41:19 +04:00
control_status_reg = ioread32 ( xdev - > base + XWT_TWCSR0_OFFSET ) ;
2011-06-03 01:13:11 +04:00
control_status_reg | = ( XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK ) ;
2014-02-12 17:41:19 +04:00
iowrite32 ( control_status_reg , xdev - > base + XWT_TWCSR0_OFFSET ) ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:41:19 +04:00
spin_unlock ( & xdev - > spinlock ) ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:34:32 +04:00
return 0 ;
}
2011-06-03 01:13:11 +04:00
2014-02-12 17:34:32 +04:00
static const struct watchdog_info xilinx_wdt_ident = {
. options = WDIOF_MAGICCLOSE |
WDIOF_KEEPALIVEPING ,
. firmware_version = 1 ,
. identity = WATCHDOG_NAME ,
} ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:34:32 +04:00
static const struct watchdog_ops xilinx_wdt_ops = {
. owner = THIS_MODULE ,
. start = xilinx_wdt_start ,
. stop = xilinx_wdt_stop ,
. ping = xilinx_wdt_keepalive ,
} ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:41:19 +04:00
static u32 xwdt_selftest ( struct xwdt_device * xdev )
2011-06-03 01:13:11 +04:00
{
int i ;
u32 timer_value1 ;
u32 timer_value2 ;
2014-02-12 17:41:19 +04:00
spin_lock ( & xdev - > spinlock ) ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:41:19 +04:00
timer_value1 = ioread32 ( xdev - > base + XWT_TBR_OFFSET ) ;
timer_value2 = ioread32 ( xdev - > base + XWT_TBR_OFFSET ) ;
2011-06-03 01:13:11 +04:00
for ( i = 0 ;
( ( i < = XWT_MAX_SELFTEST_LOOP_COUNT ) & &
( timer_value2 = = timer_value1 ) ) ; i + + ) {
2014-02-12 17:41:19 +04:00
timer_value2 = ioread32 ( xdev - > base + XWT_TBR_OFFSET ) ;
2011-06-03 01:13:11 +04:00
}
2014-02-12 17:41:19 +04:00
spin_unlock ( & xdev - > spinlock ) ;
2011-06-03 01:13:11 +04:00
if ( timer_value2 ! = timer_value1 )
return ~ XWT_TIMER_FAILED ;
else
return XWT_TIMER_FAILED ;
}
2012-11-19 22:21:41 +04:00
static int xwdt_probe ( struct platform_device * pdev )
2011-06-03 01:13:11 +04:00
{
int rc ;
2014-02-12 17:41:25 +04:00
u32 pfreq = 0 , enable_once = 0 ;
2014-02-12 17:34:34 +04:00
struct resource * res ;
2014-02-12 17:41:19 +04:00
struct xwdt_device * xdev ;
struct watchdog_device * xilinx_wdt_wdd ;
xdev = devm_kzalloc ( & pdev - > dev , sizeof ( * xdev ) , GFP_KERNEL ) ;
if ( ! xdev )
return - ENOMEM ;
xilinx_wdt_wdd = & xdev - > xilinx_wdt_wdd ;
xilinx_wdt_wdd - > info = & xilinx_wdt_ident ;
xilinx_wdt_wdd - > ops = & xilinx_wdt_ops ;
xilinx_wdt_wdd - > parent = & pdev - > dev ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:34:34 +04:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2014-02-12 17:41:19 +04:00
xdev - > base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( xdev - > base ) )
return PTR_ERR ( xdev - > base ) ;
2014-02-12 17:34:34 +04:00
2014-02-12 17:41:21 +04:00
rc = of_property_read_u32 ( pdev - > dev . of_node , " clock-frequency " , & pfreq ) ;
2014-02-12 17:41:25 +04:00
if ( rc )
2014-02-12 17:41:20 +04:00
dev_warn ( & pdev - > dev ,
" The watchdog clock frequency cannot be obtained \n " ) ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:41:21 +04:00
rc = of_property_read_u32 ( pdev - > dev . of_node , " xlnx,wdt-interval " ,
& xdev - > wdt_interval ) ;
2014-02-12 17:41:25 +04:00
if ( rc )
2014-02-12 17:41:20 +04:00
dev_warn ( & pdev - > dev ,
" Parameter \" xlnx,wdt-interval \" not found \n " ) ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:41:21 +04:00
rc = of_property_read_u32 ( pdev - > dev . of_node , " xlnx,wdt-enable-once " ,
& enable_once ) ;
if ( rc )
2014-02-12 17:41:20 +04:00
dev_warn ( & pdev - > dev ,
" Parameter \" xlnx,wdt-enable-once \" not found \n " ) ;
2014-02-12 17:41:21 +04:00
watchdog_set_nowayout ( xilinx_wdt_wdd , enable_once ) ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:41:22 +04:00
/*
* Twice of the 2 ^ wdt_interval / freq because the first wdt overflow is
* ignored ( interrupt ) , reset is only generated at second wdt overflow
*/
2014-02-12 17:41:25 +04:00
if ( pfreq & & xdev - > wdt_interval )
2014-02-12 17:41:19 +04:00
xilinx_wdt_wdd - > timeout = 2 * ( ( 1 < < xdev - > wdt_interval ) /
2014-02-12 17:41:21 +04:00
pfreq ) ;
2014-02-12 17:41:19 +04:00
spin_lock_init ( & xdev - > spinlock ) ;
watchdog_set_drvdata ( xilinx_wdt_wdd , xdev ) ;
2011-06-03 01:13:11 +04:00
2014-02-12 17:41:19 +04:00
rc = xwdt_selftest ( xdev ) ;
2011-06-03 01:13:11 +04:00
if ( rc = = XWT_TIMER_FAILED ) {
2014-02-12 17:41:20 +04:00
dev_err ( & pdev - > dev , " SelfTest routine error \n " ) ;
2014-02-12 17:34:34 +04:00
return rc ;
2011-06-03 01:13:11 +04:00
}
2014-02-12 17:41:19 +04:00
rc = watchdog_register_device ( xilinx_wdt_wdd ) ;
2011-06-03 01:13:11 +04:00
if ( rc ) {
2014-02-12 17:41:20 +04:00
dev_err ( & pdev - > dev , " Cannot register watchdog (err=%d) \n " , rc ) ;
2014-02-12 17:34:34 +04:00
return rc ;
2011-06-03 01:13:11 +04:00
}
2014-02-12 17:34:32 +04:00
dev_info ( & pdev - > dev , " Xilinx Watchdog Timer at %p with timeout %ds \n " ,
2014-02-12 17:41:19 +04:00
xdev - > base , xilinx_wdt_wdd - > timeout ) ;
platform_set_drvdata ( pdev , xdev ) ;
2011-06-03 01:13:11 +04:00
return 0 ;
}
2014-02-12 17:41:19 +04:00
static int xwdt_remove ( struct platform_device * pdev )
2011-06-03 01:13:11 +04:00
{
2014-02-12 17:41:19 +04:00
struct xwdt_device * xdev = platform_get_drvdata ( pdev ) ;
watchdog_unregister_device ( & xdev - > xilinx_wdt_wdd ) ;
2011-06-03 01:13:11 +04:00
return 0 ;
}
/* Match table for of_platform binding */
2014-05-07 12:42:22 +04:00
static const struct of_device_id xwdt_of_match [ ] = {
2013-05-31 09:56:34 +04:00
{ . compatible = " xlnx,xps-timebase-wdt-1.00.a " , } ,
2011-06-03 01:13:11 +04:00
{ . compatible = " xlnx,xps-timebase-wdt-1.01.a " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , xwdt_of_match ) ;
static struct platform_driver xwdt_driver = {
. probe = xwdt_probe ,
2012-11-19 22:21:12 +04:00
. remove = xwdt_remove ,
2011-06-03 01:13:11 +04:00
. driver = {
. name = WATCHDOG_NAME ,
. of_match_table = xwdt_of_match ,
} ,
} ;
2011-11-29 09:56:27 +04:00
module_platform_driver ( xwdt_driver ) ;
2011-06-03 01:13:11 +04:00
MODULE_AUTHOR ( " Alejandro Cabrera <aldaya@gmail.com> " ) ;
MODULE_DESCRIPTION ( " Xilinx Watchdog driver " ) ;
2013-05-31 09:56:33 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;