2018-03-16 18:14:11 +03:00
// SPDX-License-Identifier: GPL-2.0+
2015-04-09 17:47:31 +03:00
/*
* ST ' s LPC Watchdog
*
* Copyright ( C ) 2014 STMicroelectronics - - All Rights Reserved
*
* Author : David Paris < david . paris @ st . com > for STMicroelectronics
* Lee Jones < lee . jones @ linaro . org > for STMicroelectronics
*/
# include <linux/clk.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/mfd/syscon.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_platform.h>
# include <linux/platform_device.h>
# include <linux/regmap.h>
# include <linux/watchdog.h>
# include <dt-bindings/mfd/st-lpc.h>
/* Low Power Alarm */
# define LPC_LPA_LSB_OFF 0x410
# define LPC_LPA_START_OFF 0x418
/* LPC as WDT */
# define LPC_WDT_OFF 0x510
static struct watchdog_device st_wdog_dev ;
struct st_wdog_syscfg {
unsigned int reset_type_reg ;
unsigned int reset_type_mask ;
unsigned int enable_reg ;
unsigned int enable_mask ;
} ;
struct st_wdog {
void __iomem * base ;
struct device * dev ;
struct regmap * regmap ;
struct st_wdog_syscfg * syscfg ;
struct clk * clk ;
unsigned long clkrate ;
bool warm_reset ;
} ;
static struct st_wdog_syscfg stih407_syscfg = {
. enable_reg = 0x204 ,
. enable_mask = BIT ( 19 ) ,
} ;
static const struct of_device_id st_wdog_match [ ] = {
{
. compatible = " st,stih407-lpc " ,
. data = & stih407_syscfg ,
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , st_wdog_match ) ;
static void st_wdog_setup ( struct st_wdog * st_wdog , bool enable )
{
/* Type of watchdog reset - 0: Cold 1: Warm */
if ( st_wdog - > syscfg - > reset_type_reg )
regmap_update_bits ( st_wdog - > regmap ,
st_wdog - > syscfg - > reset_type_reg ,
st_wdog - > syscfg - > reset_type_mask ,
st_wdog - > warm_reset ) ;
/* Mask/unmask watchdog reset */
regmap_update_bits ( st_wdog - > regmap ,
st_wdog - > syscfg - > enable_reg ,
st_wdog - > syscfg - > enable_mask ,
enable ? 0 : st_wdog - > syscfg - > enable_mask ) ;
}
static void st_wdog_load_timer ( struct st_wdog * st_wdog , unsigned int timeout )
{
unsigned long clkrate = st_wdog - > clkrate ;
writel_relaxed ( timeout * clkrate , st_wdog - > base + LPC_LPA_LSB_OFF ) ;
writel_relaxed ( 1 , st_wdog - > base + LPC_LPA_START_OFF ) ;
}
static int st_wdog_start ( struct watchdog_device * wdd )
{
struct st_wdog * st_wdog = watchdog_get_drvdata ( wdd ) ;
writel_relaxed ( 1 , st_wdog - > base + LPC_WDT_OFF ) ;
return 0 ;
}
static int st_wdog_stop ( struct watchdog_device * wdd )
{
struct st_wdog * st_wdog = watchdog_get_drvdata ( wdd ) ;
writel_relaxed ( 0 , st_wdog - > base + LPC_WDT_OFF ) ;
return 0 ;
}
static int st_wdog_set_timeout ( struct watchdog_device * wdd ,
unsigned int timeout )
{
struct st_wdog * st_wdog = watchdog_get_drvdata ( wdd ) ;
wdd - > timeout = timeout ;
st_wdog_load_timer ( st_wdog , timeout ) ;
return 0 ;
}
static int st_wdog_keepalive ( struct watchdog_device * wdd )
{
struct st_wdog * st_wdog = watchdog_get_drvdata ( wdd ) ;
st_wdog_load_timer ( st_wdog , wdd - > timeout ) ;
return 0 ;
}
static const struct watchdog_info st_wdog_info = {
. options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE ,
. identity = " ST LPC WDT " ,
} ;
static const struct watchdog_ops st_wdog_ops = {
. owner = THIS_MODULE ,
. start = st_wdog_start ,
. stop = st_wdog_stop ,
. ping = st_wdog_keepalive ,
. set_timeout = st_wdog_set_timeout ,
} ;
static struct watchdog_device st_wdog_dev = {
. info = & st_wdog_info ,
. ops = & st_wdog_ops ,
} ;
2019-04-09 20:23:57 +03:00
static void st_clk_disable_unprepare ( void * data )
{
clk_disable_unprepare ( data ) ;
}
2015-04-09 17:47:31 +03:00
static int st_wdog_probe ( struct platform_device * pdev )
{
2019-04-09 20:23:57 +03:00
struct device * dev = & pdev - > dev ;
2015-04-09 17:47:31 +03:00
const struct of_device_id * match ;
2019-04-09 20:23:57 +03:00
struct device_node * np = dev - > of_node ;
2015-04-09 17:47:31 +03:00
struct st_wdog * st_wdog ;
struct regmap * regmap ;
struct clk * clk ;
void __iomem * base ;
uint32_t mode ;
int ret ;
ret = of_property_read_u32 ( np , " st,lpc-mode " , & mode ) ;
if ( ret ) {
2019-04-09 20:23:57 +03:00
dev_err ( dev , " An LPC mode must be provided \n " ) ;
2015-04-09 17:47:31 +03:00
return - EINVAL ;
}
2015-05-12 15:58:13 +03:00
/* LPC can either run as a Clocksource or in RTC or WDT mode */
2015-04-09 17:47:31 +03:00
if ( mode ! = ST_LPC_MODE_WDT )
return - ENODEV ;
2019-04-09 20:23:57 +03:00
st_wdog = devm_kzalloc ( dev , sizeof ( * st_wdog ) , GFP_KERNEL ) ;
2015-04-09 17:47:31 +03:00
if ( ! st_wdog )
return - ENOMEM ;
2019-04-09 20:23:57 +03:00
match = of_match_device ( st_wdog_match , dev ) ;
2015-04-09 17:47:31 +03:00
if ( ! match ) {
2019-04-09 20:23:57 +03:00
dev_err ( dev , " Couldn't match device \n " ) ;
2015-04-09 17:47:31 +03:00
return - ENODEV ;
}
st_wdog - > syscfg = ( struct st_wdog_syscfg * ) match - > data ;
watchdog: Convert to use devm_platform_ioremap_resource
Use devm_platform_ioremap_resource to reduce source code size,
improve readability, and reduce the likelyhood of bugs.
The conversion was done automatically with coccinelle using the
following semantic patch.
@r@
identifier res, pdev;
expression a;
expression index;
expression e;
@@
<+...
- res = platform_get_resource(pdev, IORESOURCE_MEM, index);
- a = devm_ioremap_resource(e, res);
+ a = devm_platform_ioremap_resource(pdev, index);
...+>
@depends on r@
identifier r.res;
@@
- struct resource *res;
... when != res
@@
identifier res, pdev;
expression index;
expression a;
@@
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, index);
- a = devm_ioremap_resource(&pdev->dev, res);
+ a = devm_platform_ioremap_resource(pdev, index);
Cc: Joel Stanley <joel@jms.id.au>
Cc: Nicolas Ferre <nicolas.ferre@microchip.com>
Cc: Alexandre Belloni <alexandre.belloni@bootlin.com>
Cc: Florian Fainelli <f.fainelli@gmail.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Baruch Siach <baruch@tkos.co.il>
Cc: Keguang Zhang <keguang.zhang@gmail.com>
Cc: Vladimir Zapolskiy <vz@mleia.com>
Cc: Kevin Hilman <khilman@baylibre.com>
Cc: Matthias Brugger <matthias.bgg@gmail.com>
Cc: Avi Fishman <avifishman70@gmail.com>
Cc: Nancy Yuen <yuenn@google.com>
Cc: Brendan Higgins <brendanhiggins@google.com>
Cc: Wan ZongShun <mcuos.com@gmail.com>
Cc: Michal Simek <michal.simek@xilinx.com>
Cc: Sylvain Lemieux <slemieux.tyco@gmail.com>
Cc: Kukjin Kim <kgene@kernel.org>
Cc: Barry Song <baohua@kernel.org>
Cc: Orson Zhai <orsonzhai@gmail.com>
Cc: Patrice Chotard <patrice.chotard@st.com>
Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com>
Cc: Maxime Ripard <maxime.ripard@bootlin.com>
Cc: Chen-Yu Tsai <wens@csie.org>
Cc: Marc Gonzalez <marc.w.gonzalez@free.fr>
Cc: Thierry Reding <thierry.reding@gmail.com>
Cc: Shawn Guo <shawnguo@kernel.org>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Acked-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Tested-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Acked-by: Joel Stanley <joel@jms.id.au>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Acked-by: Maxime Ripard <maxime.ripard@bootlin.com>
Acked-by: Michal Simek <michal.simek@xilinx.com> (cadence/xilinx wdts)
Acked-by: Thierry Reding <treding@nvidia.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Acked-by: Patrice Chotard <patrice.chotard@st.com>
Acked-by: Vladimir Zapolskiy <vz@mleia.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
2019-04-02 22:01:53 +03:00
base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2015-04-09 17:47:31 +03:00
if ( IS_ERR ( base ) )
return PTR_ERR ( base ) ;
regmap = syscon_regmap_lookup_by_phandle ( np , " st,syscfg " ) ;
if ( IS_ERR ( regmap ) ) {
2019-04-09 20:23:57 +03:00
dev_err ( dev , " No syscfg phandle specified \n " ) ;
2015-04-09 17:47:31 +03:00
return PTR_ERR ( regmap ) ;
}
2019-04-09 20:23:57 +03:00
clk = devm_clk_get ( dev , NULL ) ;
2015-04-09 17:47:31 +03:00
if ( IS_ERR ( clk ) ) {
2019-04-09 20:23:57 +03:00
dev_err ( dev , " Unable to request clock \n " ) ;
2015-04-09 17:47:31 +03:00
return PTR_ERR ( clk ) ;
}
2019-04-09 20:23:57 +03:00
st_wdog - > dev = dev ;
2015-04-09 17:47:31 +03:00
st_wdog - > base = base ;
st_wdog - > clk = clk ;
st_wdog - > regmap = regmap ;
st_wdog - > warm_reset = of_property_read_bool ( np , " st,warm_reset " ) ;
st_wdog - > clkrate = clk_get_rate ( st_wdog - > clk ) ;
if ( ! st_wdog - > clkrate ) {
2019-04-09 20:23:57 +03:00
dev_err ( dev , " Unable to fetch clock rate \n " ) ;
2015-04-09 17:47:31 +03:00
return - EINVAL ;
}
st_wdog_dev . max_timeout = 0xFFFFFFFF / st_wdog - > clkrate ;
2019-04-09 20:23:57 +03:00
st_wdog_dev . parent = dev ;
2015-04-09 17:47:31 +03:00
ret = clk_prepare_enable ( clk ) ;
if ( ret ) {
2019-04-09 20:23:57 +03:00
dev_err ( dev , " Unable to enable clock \n " ) ;
2015-04-09 17:47:31 +03:00
return ret ;
}
2019-04-09 20:23:57 +03:00
ret = devm_add_action_or_reset ( dev , st_clk_disable_unprepare , clk ) ;
if ( ret )
return ret ;
2015-04-09 17:47:31 +03:00
watchdog_set_drvdata ( & st_wdog_dev , st_wdog ) ;
watchdog_set_nowayout ( & st_wdog_dev , WATCHDOG_NOWAYOUT ) ;
/* Init Watchdog timeout with value in DT */
2019-04-09 20:23:57 +03:00
ret = watchdog_init_timeout ( & st_wdog_dev , 0 , dev ) ;
2019-04-19 21:15:58 +03:00
if ( ret )
2015-04-09 17:47:31 +03:00
return ret ;
2019-04-09 20:23:57 +03:00
ret = devm_watchdog_register_device ( dev , & st_wdog_dev ) ;
2019-05-19 00:27:55 +03:00
if ( ret )
2015-04-09 17:47:31 +03:00
return ret ;
st_wdog_setup ( st_wdog , true ) ;
2019-04-09 20:23:57 +03:00
dev_info ( dev , " LPC Watchdog driver registered, reset type is %s " ,
2015-04-09 17:47:31 +03:00
st_wdog - > warm_reset ? " warm " : " cold " ) ;
return ret ;
}
static int st_wdog_remove ( struct platform_device * pdev )
{
struct st_wdog * st_wdog = watchdog_get_drvdata ( & st_wdog_dev ) ;
st_wdog_setup ( st_wdog , false ) ;
return 0 ;
}
# ifdef CONFIG_PM_SLEEP
static int st_wdog_suspend ( struct device * dev )
{
struct st_wdog * st_wdog = watchdog_get_drvdata ( & st_wdog_dev ) ;
if ( watchdog_active ( & st_wdog_dev ) )
st_wdog_stop ( & st_wdog_dev ) ;
st_wdog_setup ( st_wdog , false ) ;
clk_disable ( st_wdog - > clk ) ;
return 0 ;
}
static int st_wdog_resume ( struct device * dev )
{
struct st_wdog * st_wdog = watchdog_get_drvdata ( & st_wdog_dev ) ;
int ret ;
ret = clk_enable ( st_wdog - > clk ) ;
if ( ret ) {
dev_err ( dev , " Unable to re-enable clock \n " ) ;
watchdog_unregister_device ( & st_wdog_dev ) ;
clk_unprepare ( st_wdog - > clk ) ;
return ret ;
}
st_wdog_setup ( st_wdog , true ) ;
if ( watchdog_active ( & st_wdog_dev ) ) {
st_wdog_load_timer ( st_wdog , st_wdog_dev . timeout ) ;
st_wdog_start ( & st_wdog_dev ) ;
}
return 0 ;
}
# endif
static SIMPLE_DEV_PM_OPS ( st_wdog_pm_ops ,
st_wdog_suspend ,
st_wdog_resume ) ;
static struct platform_driver st_wdog_driver = {
. driver = {
. name = " st-lpc-wdt " ,
. pm = & st_wdog_pm_ops ,
. of_match_table = st_wdog_match ,
} ,
. probe = st_wdog_probe ,
. remove = st_wdog_remove ,
} ;
module_platform_driver ( st_wdog_driver ) ;
MODULE_AUTHOR ( " David Paris <david.paris@st.com> " ) ;
MODULE_DESCRIPTION ( " ST LPC Watchdog Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;