2019-08-26 17:38:14 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2005-04-17 02:20:36 +04:00
/*
2006-10-04 01:01:26 +04:00
* drivers / char / watchdog / ixp4xx_wdt . c
2005-04-17 02:20:36 +04:00
*
* Watchdog driver for Intel IXP4xx network processors
*
* Author : Deepak Saxena < dsaxena @ plexity . net >
2019-08-26 17:38:14 +03:00
* Author : Linus Walleij < linus . walleij @ linaro . org >
2005-04-17 02:20:36 +04:00
*
* Copyright 2004 ( c ) MontaVista , Software , Inc .
* Based on sa1100 driver , Copyright ( C ) 2000 Oleg Drokin < green @ crimea . edu >
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/watchdog.h>
2019-08-26 17:38:14 +03:00
# include <linux/bits.h>
# include <linux/platform_device.h>
# include <linux/clk.h>
# include <linux/soc/ixp4xx/cpu.h>
struct ixp4xx_wdt {
struct watchdog_device wdd ;
void __iomem * base ;
unsigned long rate ;
} ;
2005-04-17 02:20:36 +04:00
2019-08-26 17:38:14 +03:00
/* Fallback if we do not have a clock for this */
# define IXP4XX_TIMER_FREQ 66666000
2005-04-17 02:20:36 +04:00
2019-08-26 17:38:14 +03:00
/* Registers after the timer registers */
# define IXP4XX_OSWT_OFFSET 0x14 /* Watchdog Timer */
# define IXP4XX_OSWE_OFFSET 0x18 /* Watchdog Enable */
# define IXP4XX_OSWK_OFFSET 0x1C /* Watchdog Key */
# define IXP4XX_OSST_OFFSET 0x20 /* Timer Status */
2005-04-17 02:20:36 +04:00
2019-08-26 17:38:14 +03:00
# define IXP4XX_OSST_TIMER_WDOG_PEND 0x00000008
# define IXP4XX_OSST_TIMER_WARM_RESET 0x00000010
# define IXP4XX_WDT_KEY 0x0000482E
# define IXP4XX_WDT_RESET_ENABLE 0x00000001
# define IXP4XX_WDT_IRQ_ENABLE 0x00000002
# define IXP4XX_WDT_COUNT_ENABLE 0x00000004
2005-04-17 02:20:36 +04:00
2019-08-26 17:38:14 +03:00
static inline
struct ixp4xx_wdt * to_ixp4xx_wdt ( struct watchdog_device * wdd )
2005-04-17 02:20:36 +04:00
{
2019-08-26 17:38:14 +03:00
return container_of ( wdd , struct ixp4xx_wdt , wdd ) ;
2005-04-17 02:20:36 +04:00
}
2019-08-26 17:38:14 +03:00
static int ixp4xx_wdt_start ( struct watchdog_device * wdd )
2005-04-17 02:20:36 +04:00
{
2019-08-26 17:38:14 +03:00
struct ixp4xx_wdt * iwdt = to_ixp4xx_wdt ( wdd ) ;
2005-04-17 02:20:36 +04:00
2019-08-26 17:38:14 +03:00
__raw_writel ( IXP4XX_WDT_KEY , iwdt - > base + IXP4XX_OSWK_OFFSET ) ;
__raw_writel ( 0 , iwdt - > base + IXP4XX_OSWE_OFFSET ) ;
__raw_writel ( wdd - > timeout * iwdt - > rate ,
iwdt - > base + IXP4XX_OSWT_OFFSET ) ;
__raw_writel ( IXP4XX_WDT_COUNT_ENABLE | IXP4XX_WDT_RESET_ENABLE ,
iwdt - > base + IXP4XX_OSWE_OFFSET ) ;
__raw_writel ( 0 , iwdt - > base + IXP4XX_OSWK_OFFSET ) ;
2005-04-17 02:20:36 +04:00
2019-08-26 17:38:14 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2019-08-26 17:38:14 +03:00
static int ixp4xx_wdt_stop ( struct watchdog_device * wdd )
2005-04-17 02:20:36 +04:00
{
2019-08-26 17:38:14 +03:00
struct ixp4xx_wdt * iwdt = to_ixp4xx_wdt ( wdd ) ;
2005-04-17 02:20:36 +04:00
2019-08-26 17:38:14 +03:00
__raw_writel ( IXP4XX_WDT_KEY , iwdt - > base + IXP4XX_OSWK_OFFSET ) ;
__raw_writel ( 0 , iwdt - > base + IXP4XX_OSWE_OFFSET ) ;
__raw_writel ( 0 , iwdt - > base + IXP4XX_OSWK_OFFSET ) ;
2005-04-17 02:20:36 +04:00
2019-08-26 17:38:14 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2019-08-26 17:38:14 +03:00
static int ixp4xx_wdt_set_timeout ( struct watchdog_device * wdd ,
unsigned int timeout )
2005-04-17 02:20:36 +04:00
{
2019-08-26 17:38:14 +03:00
wdd - > timeout = timeout ;
if ( watchdog_active ( wdd ) )
ixp4xx_wdt_start ( wdd ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2019-08-26 17:38:14 +03:00
static const struct watchdog_ops ixp4xx_wdt_ops = {
. start = ixp4xx_wdt_start ,
. stop = ixp4xx_wdt_stop ,
. set_timeout = ixp4xx_wdt_set_timeout ,
. owner = THIS_MODULE ,
2005-04-17 02:20:36 +04:00
} ;
2019-08-26 17:38:14 +03:00
static const struct watchdog_info ixp4xx_wdt_info = {
. options = WDIOF_KEEPALIVEPING
| WDIOF_MAGICCLOSE
| WDIOF_SETTIMEOUT ,
. identity = KBUILD_MODNAME ,
2005-04-17 02:20:36 +04:00
} ;
2019-08-26 17:38:14 +03:00
/* Devres-handled clock disablement */
static void ixp4xx_clock_action ( void * d )
{
clk_disable_unprepare ( d ) ;
}
static int ixp4xx_wdt_probe ( struct platform_device * pdev )
2005-04-17 02:20:36 +04:00
{
2019-08-26 17:38:14 +03:00
struct device * dev = & pdev - > dev ;
struct ixp4xx_wdt * iwdt ;
struct clk * clk ;
2005-04-17 02:20:36 +04:00
int ret ;
2008-08-10 21:08:10 +04:00
if ( ! ( read_cpuid_id ( ) & 0xf ) & & ! cpu_is_ixp46x ( ) ) {
2019-08-26 17:38:14 +03:00
dev_err ( dev , " Rev. A0 IXP42x CPU detected - watchdog disabled \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
2019-08-26 17:38:14 +03:00
iwdt = devm_kzalloc ( dev , sizeof ( * iwdt ) , GFP_KERNEL ) ;
if ( ! iwdt )
return - ENOMEM ;
2021-09-11 07:29:25 +03:00
iwdt - > base = ( void __iomem * ) dev - > platform_data ;
2005-04-17 02:20:36 +04:00
2019-08-26 17:38:14 +03:00
/*
* Retrieve rate from a fixed clock from the device tree if
* the parent has that , else use the default clock rate .
*/
clk = devm_clk_get ( dev - > parent , NULL ) ;
if ( ! IS_ERR ( clk ) ) {
ret = clk_prepare_enable ( clk ) ;
if ( ret )
return ret ;
ret = devm_add_action_or_reset ( dev , ixp4xx_clock_action , clk ) ;
if ( ret )
return ret ;
iwdt - > rate = clk_get_rate ( clk ) ;
}
if ( ! iwdt - > rate )
iwdt - > rate = IXP4XX_TIMER_FREQ ;
2005-04-17 02:20:36 +04:00
2019-08-26 17:38:14 +03:00
iwdt - > wdd . info = & ixp4xx_wdt_info ;
iwdt - > wdd . ops = & ixp4xx_wdt_ops ;
iwdt - > wdd . min_timeout = 1 ;
iwdt - > wdd . max_timeout = U32_MAX / iwdt - > rate ;
iwdt - > wdd . parent = dev ;
/* Default to 60 seconds */
iwdt - > wdd . timeout = 60U ;
watchdog_init_timeout ( & iwdt - > wdd , 0 , dev ) ;
2005-04-17 02:20:36 +04:00
2019-08-26 17:38:14 +03:00
if ( __raw_readl ( iwdt - > base + IXP4XX_OSST_OFFSET ) &
IXP4XX_OSST_TIMER_WARM_RESET )
iwdt - > wdd . bootstatus = WDIOF_CARDRESET ;
ret = devm_watchdog_register_device ( dev , & iwdt - > wdd ) ;
if ( ret )
return ret ;
dev_info ( dev , " IXP4xx watchdog available \n " ) ;
2005-04-17 02:20:36 +04:00
2019-08-26 17:38:14 +03:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
2019-08-26 17:38:14 +03:00
static struct platform_driver ixp4xx_wdt_driver = {
. probe = ixp4xx_wdt_probe ,
. driver = {
. name = " ixp4xx-watchdog " ,
} ,
} ;
module_platform_driver ( ixp4xx_wdt_driver ) ;
2005-04-17 02:20:36 +04:00
2019-08-26 17:38:14 +03:00
MODULE_AUTHOR ( " Deepak Saxena <dsaxena@plexity.net> " ) ;
MODULE_DESCRIPTION ( " IXP4xx Network Processor Watchdog " ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;