summaryrefslogtreecommitdiff
path: root/drivers/watchdog
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/Kconfig19
-rw-r--r--drivers/watchdog/Makefile2
-rw-r--r--drivers/watchdog/bcm7038_wdt.c1
-rw-r--r--drivers/watchdog/da9063_wdt.c36
-rw-r--r--drivers/watchdog/iTCO_wdt.c20
-rw-r--r--drivers/watchdog/mtk_wdt.c12
-rw-r--r--drivers/watchdog/rti_wdt.c10
-rw-r--r--drivers/watchdog/rzg2l_wdt.c83
-rw-r--r--drivers/watchdog/rzn1_wdt.c203
-rw-r--r--drivers/watchdog/sp805_wdt.c1
-rw-r--r--drivers/watchdog/sunplus_wdt.c220
-rw-r--r--drivers/watchdog/ts4800_wdt.c5
-rw-r--r--drivers/watchdog/wdat_wdt.c6
13 files changed, 562 insertions, 56 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index c4e82a8d863f..001ae1be9b61 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -883,6 +883,14 @@ config RENESAS_RZAWDT
This driver adds watchdog support for the integrated watchdogs in the
Renesas RZ/A SoCs. These watchdogs can be used to reset a system.
+config RENESAS_RZN1WDT
+ tristate "Renesas RZ/N1 watchdog"
+ depends on ARCH_RENESAS || COMPILE_TEST
+ select WATCHDOG_CORE
+ help
+ This driver adds watchdog support for the integrated watchdogs in the
+ Renesas RZ/N1 SoCs. These watchdogs can be used to reset a system.
+
config RENESAS_RZG2LWDT
tristate "Renesas RZ/G2L WDT Watchdog"
depends on ARCH_RENESAS || COMPILE_TEST
@@ -1011,6 +1019,17 @@ config APPLE_WATCHDOG
To compile this driver as a module, choose M here: the
module will be called apple_wdt.
+config SUNPLUS_WATCHDOG
+ tristate "Sunplus watchdog support"
+ depends on ARCH_SUNPLUS || COMPILE_TEST
+ select WATCHDOG_CORE
+ help
+ Say Y here to include support for the watchdog timer
+ in Sunplus SoCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sunplus_wdt.
+
# X86 (i386 + ia64 + x86_64) Architecture
config ACQUIRE_WDT
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index f7da867e8782..5f88a6237f7c 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -84,6 +84,7 @@ obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o
obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o
obj-$(CONFIG_RENESAS_WDT) += renesas_wdt.o
obj-$(CONFIG_RENESAS_RZAWDT) += rza_wdt.o
+obj-$(CONFIG_RENESAS_RZN1WDT) += rzn1_wdt.o
obj-$(CONFIG_RENESAS_RZG2LWDT) += rzg2l_wdt.o
obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o
obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o
@@ -95,6 +96,7 @@ obj-$(CONFIG_ARM_SMC_WATCHDOG) += arm_smc_wdt.o
obj-$(CONFIG_VISCONTI_WATCHDOG) += visconti_wdt.o
obj-$(CONFIG_MSC313E_WATCHDOG) += msc313e_wdt.o
obj-$(CONFIG_APPLE_WATCHDOG) += apple_wdt.o
+obj-$(CONFIG_SUNPLUS_WATCHDOG) += sunplus_wdt.o
# X86 (i386 + ia64 + x86_64) Architecture
obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
diff --git a/drivers/watchdog/bcm7038_wdt.c b/drivers/watchdog/bcm7038_wdt.c
index 8656a137e9a4..1ffcf6aca6ae 100644
--- a/drivers/watchdog/bcm7038_wdt.c
+++ b/drivers/watchdog/bcm7038_wdt.c
@@ -218,6 +218,7 @@ static SIMPLE_DEV_PM_OPS(bcm7038_wdt_pm_ops, bcm7038_wdt_suspend,
bcm7038_wdt_resume);
static const struct of_device_id bcm7038_wdt_match[] = {
+ { .compatible = "brcm,bcm6345-wdt" },
{ .compatible = "brcm,bcm7038-wdt" },
{},
};
diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c
index 9adad1862bbd..09a4af4c58fc 100644
--- a/drivers/watchdog/da9063_wdt.c
+++ b/drivers/watchdog/da9063_wdt.c
@@ -18,6 +18,7 @@
#include <linux/delay.h>
#include <linux/mfd/da9063/registers.h>
#include <linux/mfd/da9063/core.h>
+#include <linux/property.h>
#include <linux/regmap.h>
/*
@@ -26,6 +27,8 @@
* others: timeout = 2048 ms * 2^(TWDSCALE-1).
*/
static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 };
+static bool use_sw_pm;
+
#define DA9063_TWDSCALE_DISABLE 0
#define DA9063_TWDSCALE_MIN 1
#define DA9063_TWDSCALE_MAX (ARRAY_SIZE(wdt_timeout) - 1)
@@ -218,6 +221,8 @@ static int da9063_wdt_probe(struct platform_device *pdev)
if (!wdd)
return -ENOMEM;
+ use_sw_pm = device_property_present(dev, "dlg,use-sw-pm");
+
wdd->info = &da9063_watchdog_info;
wdd->ops = &da9063_watchdog_ops;
wdd->min_timeout = DA9063_WDT_MIN_TIMEOUT;
@@ -228,6 +233,7 @@ static int da9063_wdt_probe(struct platform_device *pdev)
watchdog_set_restart_priority(wdd, 128);
watchdog_set_drvdata(wdd, da9063);
+ dev_set_drvdata(dev, wdd);
wdd->timeout = DA9063_WDG_TIMEOUT;
@@ -249,10 +255,40 @@ static int da9063_wdt_probe(struct platform_device *pdev)
return devm_watchdog_register_device(dev, wdd);
}
+static int __maybe_unused da9063_wdt_suspend(struct device *dev)
+{
+ struct watchdog_device *wdd = dev_get_drvdata(dev);
+
+ if (!use_sw_pm)
+ return 0;
+
+ if (watchdog_active(wdd))
+ return da9063_wdt_stop(wdd);
+
+ return 0;
+}
+
+static int __maybe_unused da9063_wdt_resume(struct device *dev)
+{
+ struct watchdog_device *wdd = dev_get_drvdata(dev);
+
+ if (!use_sw_pm)
+ return 0;
+
+ if (watchdog_active(wdd))
+ return da9063_wdt_start(wdd);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(da9063_wdt_pm_ops,
+ da9063_wdt_suspend, da9063_wdt_resume);
+
static struct platform_driver da9063_wdt_driver = {
.probe = da9063_wdt_probe,
.driver = {
.name = DA9063_DRVNAME_WATCHDOG,
+ .pm = &da9063_wdt_pm_ops,
},
};
module_platform_driver(da9063_wdt_driver);
diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c
index 3f2f4343644f..34693f11385f 100644
--- a/drivers/watchdog/iTCO_wdt.c
+++ b/drivers/watchdog/iTCO_wdt.c
@@ -596,7 +596,6 @@ static int iTCO_wdt_probe(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
/*
* Suspend-to-idle requires this, because it stops the ticks and timekeeping, so
* the watchdog cannot be pinged while in that state. In ACPI sleep states the
@@ -604,15 +603,15 @@ static int iTCO_wdt_probe(struct platform_device *pdev)
*/
#ifdef CONFIG_ACPI
-static inline bool need_suspend(void)
+static inline bool __maybe_unused need_suspend(void)
{
return acpi_target_system_state() == ACPI_STATE_S0;
}
#else
-static inline bool need_suspend(void) { return true; }
+static inline bool __maybe_unused need_suspend(void) { return true; }
#endif
-static int iTCO_wdt_suspend_noirq(struct device *dev)
+static int __maybe_unused iTCO_wdt_suspend_noirq(struct device *dev)
{
struct iTCO_wdt_private *p = dev_get_drvdata(dev);
int ret = 0;
@@ -626,7 +625,7 @@ static int iTCO_wdt_suspend_noirq(struct device *dev)
return ret;
}
-static int iTCO_wdt_resume_noirq(struct device *dev)
+static int __maybe_unused iTCO_wdt_resume_noirq(struct device *dev)
{
struct iTCO_wdt_private *p = dev_get_drvdata(dev);
@@ -637,20 +636,15 @@ static int iTCO_wdt_resume_noirq(struct device *dev)
}
static const struct dev_pm_ops iTCO_wdt_pm = {
- .suspend_noirq = iTCO_wdt_suspend_noirq,
- .resume_noirq = iTCO_wdt_resume_noirq,
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(iTCO_wdt_suspend_noirq,
+ iTCO_wdt_resume_noirq)
};
-#define ITCO_WDT_PM_OPS (&iTCO_wdt_pm)
-#else
-#define ITCO_WDT_PM_OPS NULL
-#endif /* CONFIG_PM_SLEEP */
-
static struct platform_driver iTCO_wdt_driver = {
.probe = iTCO_wdt_probe,
.driver = {
.name = DRV_NAME,
- .pm = ITCO_WDT_PM_OPS,
+ .pm = &iTCO_wdt_pm,
},
};
diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c
index 4577a76dd464..f0d4e3cc7459 100644
--- a/drivers/watchdog/mtk_wdt.c
+++ b/drivers/watchdog/mtk_wdt.c
@@ -10,7 +10,9 @@
*/
#include <dt-bindings/reset/mt2712-resets.h>
+#include <dt-bindings/reset/mt7986-resets.h>
#include <dt-bindings/reset/mt8183-resets.h>
+#include <dt-bindings/reset/mt8186-resets.h>
#include <dt-bindings/reset/mt8192-resets.h>
#include <dt-bindings/reset/mt8195-resets.h>
#include <linux/delay.h>
@@ -76,10 +78,18 @@ static const struct mtk_wdt_data mt2712_data = {
.toprgu_sw_rst_num = MT2712_TOPRGU_SW_RST_NUM,
};
+static const struct mtk_wdt_data mt7986_data = {
+ .toprgu_sw_rst_num = MT7986_TOPRGU_SW_RST_NUM,
+};
+
static const struct mtk_wdt_data mt8183_data = {
.toprgu_sw_rst_num = MT8183_TOPRGU_SW_RST_NUM,
};
+static const struct mtk_wdt_data mt8186_data = {
+ .toprgu_sw_rst_num = MT8186_TOPRGU_SW_RST_NUM,
+};
+
static const struct mtk_wdt_data mt8192_data = {
.toprgu_sw_rst_num = MT8192_TOPRGU_SW_RST_NUM,
};
@@ -418,7 +428,9 @@ static int mtk_wdt_resume(struct device *dev)
static const struct of_device_id mtk_wdt_dt_ids[] = {
{ .compatible = "mediatek,mt2712-wdt", .data = &mt2712_data },
{ .compatible = "mediatek,mt6589-wdt" },
+ { .compatible = "mediatek,mt7986-wdt", .data = &mt7986_data },
{ .compatible = "mediatek,mt8183-wdt", .data = &mt8183_data },
+ { .compatible = "mediatek,mt8186-wdt", .data = &mt8186_data },
{ .compatible = "mediatek,mt8192-wdt", .data = &mt8192_data },
{ .compatible = "mediatek,mt8195-wdt", .data = &mt8195_data },
{ /* sentinel */ }
diff --git a/drivers/watchdog/rti_wdt.c b/drivers/watchdog/rti_wdt.c
index db843f825860..053ef3bde12d 100644
--- a/drivers/watchdog/rti_wdt.c
+++ b/drivers/watchdog/rti_wdt.c
@@ -226,7 +226,7 @@ static int rti_wdt_probe(struct platform_device *pdev)
pm_runtime_enable(dev);
ret = pm_runtime_get_sync(dev);
- if (ret) {
+ if (ret < 0) {
pm_runtime_put_noidle(dev);
pm_runtime_disable(&pdev->dev);
return dev_err_probe(dev, ret, "runtime pm failed\n");
@@ -253,6 +253,7 @@ static int rti_wdt_probe(struct platform_device *pdev)
}
if (readl(wdt->base + RTIDWDCTRL) == WDENABLE_KEY) {
+ int preset_heartbeat;
u32 time_left_ms;
u64 heartbeat_ms;
u32 wsize;
@@ -263,11 +264,12 @@ static int rti_wdt_probe(struct platform_device *pdev)
heartbeat_ms <<= WDT_PRELOAD_SHIFT;
heartbeat_ms *= 1000;
do_div(heartbeat_ms, wdt->freq);
- if (heartbeat_ms != heartbeat * 1000)
+ preset_heartbeat = heartbeat_ms + 500;
+ preset_heartbeat /= 1000;
+ if (preset_heartbeat != heartbeat)
dev_warn(dev, "watchdog already running, ignoring heartbeat config!\n");
- heartbeat = heartbeat_ms;
- heartbeat /= 1000;
+ heartbeat = preset_heartbeat;
wsize = readl(wdt->base + RTIWWDSIZECTRL);
ret = rti_wdt_setup_hw_hb(wdd, wsize);
diff --git a/drivers/watchdog/rzg2l_wdt.c b/drivers/watchdog/rzg2l_wdt.c
index 6b426df34fd6..6eea0ee4af49 100644
--- a/drivers/watchdog/rzg2l_wdt.c
+++ b/drivers/watchdog/rzg2l_wdt.c
@@ -21,8 +21,11 @@
#define WDTSET 0x04
#define WDTTIM 0x08
#define WDTINT 0x0C
+#define PECR 0x10
+#define PEEN 0x14
#define WDTCNT_WDTEN BIT(0)
#define WDTINT_INTDISP BIT(0)
+#define PEEN_FORCE BIT(0)
#define WDT_DEFAULT_TIMEOUT 60U
@@ -43,6 +46,8 @@ struct rzg2l_wdt_priv {
struct reset_control *rstc;
unsigned long osc_clk_rate;
unsigned long delay;
+ struct clk *pclk;
+ struct clk *osc_clk;
};
static void rzg2l_wdt_wait_delay(struct rzg2l_wdt_priv *priv)
@@ -53,7 +58,7 @@ static void rzg2l_wdt_wait_delay(struct rzg2l_wdt_priv *priv)
static u32 rzg2l_wdt_get_cycle_usec(unsigned long cycle, u32 wdttime)
{
- u64 timer_cycle_us = 1024 * 1024 * (wdttime + 1) * MICRO;
+ u64 timer_cycle_us = 1024 * 1024ULL * (wdttime + 1) * MICRO;
return div64_ul(timer_cycle_us, cycle);
}
@@ -86,7 +91,6 @@ static int rzg2l_wdt_start(struct watchdog_device *wdev)
{
struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
- reset_control_deassert(priv->rstc);
pm_runtime_get_sync(wdev->parent);
/* Initialize time out */
@@ -106,7 +110,26 @@ static int rzg2l_wdt_stop(struct watchdog_device *wdev)
struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
pm_runtime_put(wdev->parent);
- reset_control_assert(priv->rstc);
+ reset_control_reset(priv->rstc);
+
+ return 0;
+}
+
+static int rzg2l_wdt_set_timeout(struct watchdog_device *wdev, unsigned int timeout)
+{
+ struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
+
+ wdev->timeout = timeout;
+
+ /*
+ * If the watchdog is active, reset the module for updating the WDTSET
+ * register so that it is updated with new timeout values.
+ */
+ if (watchdog_active(wdev)) {
+ pm_runtime_put(wdev->parent);
+ reset_control_reset(priv->rstc);
+ rzg2l_wdt_start(wdev);
+ }
return 0;
}
@@ -116,15 +139,14 @@ static int rzg2l_wdt_restart(struct watchdog_device *wdev,
{
struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
- /* Reset the module before we modify any register */
- reset_control_reset(priv->rstc);
- pm_runtime_get_sync(wdev->parent);
+ clk_prepare_enable(priv->pclk);
+ clk_prepare_enable(priv->osc_clk);
- /* smallest counter value to reboot soon */
- rzg2l_wdt_write(priv, WDTSET_COUNTER_VAL(1), WDTSET);
+ /* Generate Reset (WDTRSTB) Signal on parity error */
+ rzg2l_wdt_write(priv, 0, PECR);
- /* Enable watchdog timer*/
- rzg2l_wdt_write(priv, WDTCNT_WDTEN, WDTCNT);
+ /* Force parity error */
+ rzg2l_wdt_write(priv, PEEN_FORCE, PEEN);
return 0;
}
@@ -148,15 +170,15 @@ static const struct watchdog_ops rzg2l_wdt_ops = {
.start = rzg2l_wdt_start,
.stop = rzg2l_wdt_stop,
.ping = rzg2l_wdt_ping,
+ .set_timeout = rzg2l_wdt_set_timeout,
.restart = rzg2l_wdt_restart,
};
-static void rzg2l_wdt_reset_assert_pm_disable_put(void *data)
+static void rzg2l_wdt_reset_assert_pm_disable(void *data)
{
struct watchdog_device *wdev = data;
struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
- pm_runtime_put(wdev->parent);
pm_runtime_disable(wdev->parent);
reset_control_assert(priv->rstc);
}
@@ -166,7 +188,6 @@ static int rzg2l_wdt_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct rzg2l_wdt_priv *priv;
unsigned long pclk_rate;
- struct clk *wdt_clk;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -178,22 +199,20 @@ static int rzg2l_wdt_probe(struct platform_device *pdev)
return PTR_ERR(priv->base);
/* Get watchdog main clock */
- wdt_clk = clk_get(&pdev->dev, "oscclk");
- if (IS_ERR(wdt_clk))
- return dev_err_probe(&pdev->dev, PTR_ERR(wdt_clk), "no oscclk");
+ priv->osc_clk = devm_clk_get(&pdev->dev, "oscclk");
+ if (IS_ERR(priv->osc_clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(priv->osc_clk), "no oscclk");
- priv->osc_clk_rate = clk_get_rate(wdt_clk);
- clk_put(wdt_clk);
+ priv->osc_clk_rate = clk_get_rate(priv->osc_clk);
if (!priv->osc_clk_rate)
return dev_err_probe(&pdev->dev, -EINVAL, "oscclk rate is 0");
/* Get Peripheral clock */
- wdt_clk = clk_get(&pdev->dev, "pclk");
- if (IS_ERR(wdt_clk))
- return dev_err_probe(&pdev->dev, PTR_ERR(wdt_clk), "no pclk");
+ priv->pclk = devm_clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(priv->pclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(priv->pclk), "no pclk");
- pclk_rate = clk_get_rate(wdt_clk);
- clk_put(wdt_clk);
+ pclk_rate = clk_get_rate(priv->pclk);
if (!pclk_rate)
return dev_err_probe(&pdev->dev, -EINVAL, "pclk rate is 0");
@@ -204,13 +223,11 @@ static int rzg2l_wdt_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, PTR_ERR(priv->rstc),
"failed to get cpg reset");
- reset_control_deassert(priv->rstc);
+ ret = reset_control_deassert(priv->rstc);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to deassert");
+
pm_runtime_enable(&pdev->dev);
- ret = pm_runtime_resume_and_get(&pdev->dev);
- if (ret < 0) {
- dev_err(dev, "pm_runtime_resume_and_get failed ret=%pe", ERR_PTR(ret));
- goto out_pm_get;
- }
priv->wdev.info = &rzg2l_wdt_ident;
priv->wdev.ops = &rzg2l_wdt_ops;
@@ -222,7 +239,7 @@ static int rzg2l_wdt_probe(struct platform_device *pdev)
watchdog_set_drvdata(&priv->wdev, priv);
ret = devm_add_action_or_reset(&pdev->dev,
- rzg2l_wdt_reset_assert_pm_disable_put,
+ rzg2l_wdt_reset_assert_pm_disable,
&priv->wdev);
if (ret < 0)
return ret;
@@ -235,12 +252,6 @@ static int rzg2l_wdt_probe(struct platform_device *pdev)
dev_warn(dev, "Specified timeout invalid, using default");
return devm_watchdog_register_device(&pdev->dev, &priv->wdev);
-
-out_pm_get:
- pm_runtime_disable(dev);
- reset_control_assert(priv->rstc);
-
- return ret;
}
static const struct of_device_id rzg2l_wdt_ids[] = {
diff --git a/drivers/watchdog/rzn1_wdt.c b/drivers/watchdog/rzn1_wdt.c
new file mode 100644
index 000000000000..55ab384b9965
--- /dev/null
+++ b/drivers/watchdog/rzn1_wdt.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas RZ/N1 Watchdog timer.
+ * This is a 12-bit timer driver from a (62.5/16384) MHz clock. It can't even
+ * cope with 2 seconds.
+ *
+ * Copyright 2018 Renesas Electronics Europe Ltd.
+ *
+ * Derived from Ralink RT288x watchdog timer.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/watchdog.h>
+
+#define DEFAULT_TIMEOUT 60
+
+#define RZN1_WDT_RETRIGGER 0x0
+#define RZN1_WDT_RETRIGGER_RELOAD_VAL 0
+#define RZN1_WDT_RETRIGGER_RELOAD_VAL_MASK 0xfff
+#define RZN1_WDT_RETRIGGER_PRESCALE BIT(12)
+#define RZN1_WDT_RETRIGGER_ENABLE BIT(13)
+#define RZN1_WDT_RETRIGGER_WDSI (0x2 << 14)
+
+#define RZN1_WDT_PRESCALER 16384
+#define RZN1_WDT_MAX 4095
+
+struct rzn1_watchdog {
+ struct watchdog_device wdtdev;
+ void __iomem *base;
+ unsigned long clk_rate_khz;
+};
+
+static inline uint32_t max_heart_beat_ms(unsigned long clk_rate_khz)
+{
+ return (RZN1_WDT_MAX * RZN1_WDT_PRESCALER) / clk_rate_khz;
+}
+
+static inline uint32_t compute_reload_value(uint32_t tick_ms,
+ unsigned long clk_rate_khz)
+{
+ return (tick_ms * clk_rate_khz) / RZN1_WDT_PRESCALER;
+}
+
+static int rzn1_wdt_ping(struct watchdog_device *w)
+{
+ struct rzn1_watchdog *wdt = watchdog_get_drvdata(w);
+
+ /* Any value retrigggers the watchdog */
+ writel(0, wdt->base + RZN1_WDT_RETRIGGER);
+
+ return 0;
+}
+
+static int rzn1_wdt_start(struct watchdog_device *w)
+{
+ struct rzn1_watchdog *wdt = watchdog_get_drvdata(w);
+ u32 val;
+
+ /*
+ * The hardware allows you to write to this reg only once.
+ * Since this includes the reload value, there is no way to change the
+ * timeout once started. Also note that the WDT clock is half the bus
+ * fabric clock rate, so if the bus fabric clock rate is changed after
+ * the WDT is started, the WDT interval will be wrong.
+ */
+ val = RZN1_WDT_RETRIGGER_WDSI;
+ val |= RZN1_WDT_RETRIGGER_ENABLE;
+ val |= RZN1_WDT_RETRIGGER_PRESCALE;
+ val |= compute_reload_value(w->max_hw_heartbeat_ms, wdt->clk_rate_khz);
+ writel(val, wdt->base + RZN1_WDT_RETRIGGER);
+
+ return 0;
+}
+
+static irqreturn_t rzn1_wdt_irq(int irq, void *_wdt)
+{
+ pr_crit("RZN1 Watchdog. Initiating system reboot\n");
+ emergency_restart();
+
+ return IRQ_HANDLED;
+}
+
+static struct watchdog_info rzn1_wdt_info = {
+ .identity = "RZ/N1 Watchdog",
+ .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+};
+
+static const struct watchdog_ops rzn1_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = rzn1_wdt_start,
+ .ping = rzn1_wdt_ping,
+};
+
+static void rzn1_wdt_clk_disable_unprepare(void *data)
+{
+ clk_disable_unprepare(data);
+}
+
+static int rzn1_wdt_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rzn1_watchdog *wdt;
+ struct device_node *np = dev->of_node;
+ struct clk *clk;
+ unsigned long clk_rate;
+ int ret;
+ int irq;
+
+ wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
+ if (!wdt)
+ return -ENOMEM;
+
+ wdt->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(wdt->base))
+ return PTR_ERR(wdt->base);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(dev, irq, rzn1_wdt_irq, 0,
+ np->name, wdt);
+ if (ret) {
+ dev_err(dev, "failed to request irq %d\n", irq);
+ return ret;
+ }
+
+ clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(dev, "failed to get the clock\n");
+ return PTR_ERR(clk);
+ }
+
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ dev_err(dev, "failed to prepare/enable the clock\n");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(dev, rzn1_wdt_clk_disable_unprepare,
+ clk);
+ if (ret)
+ return ret;
+
+ clk_rate = clk_get_rate(clk);
+ if (!clk_rate) {
+ dev_err(dev, "failed to get the clock rate\n");
+ return -EINVAL;
+ }
+
+ wdt->clk_rate_khz = clk_rate / 1000;
+ wdt->wdtdev.info = &rzn1_wdt_info,
+ wdt->wdtdev.ops = &rzn1_wdt_ops,
+ wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS,
+ wdt->wdtdev.parent = dev;
+ /*
+ * The period of the watchdog cannot be changed once set
+ * and is limited to a very short period.
+ * Configure it for a 1s period once and for all, and
+ * rely on the heart-beat provided by the watchdog core
+ * to make this usable by the user-space.
+ */
+ wdt->wdtdev.max_hw_heartbeat_ms = max_heart_beat_ms(wdt->clk_rate_khz);
+ if (wdt->wdtdev.max_hw_heartbeat_ms > 1000)
+ wdt->wdtdev.max_hw_heartbeat_ms = 1000;
+
+ wdt->wdtdev.timeout = DEFAULT_TIMEOUT;
+ ret = watchdog_init_timeout(&wdt->wdtdev, 0, dev);
+ if (ret)
+ return ret;
+
+ watchdog_set_drvdata(&wdt->wdtdev, wdt);
+
+ return devm_watchdog_register_device(dev, &wdt->wdtdev);
+}
+
+
+static const struct of_device_id rzn1_wdt_match[] = {
+ { .compatible = "renesas,rzn1-wdt" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rzn1_wdt_match);
+
+static struct platform_driver rzn1_wdt_driver = {
+ .probe = rzn1_wdt_probe,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = rzn1_wdt_match,
+ },
+};
+
+module_platform_driver(rzn1_wdt_driver);
+
+MODULE_DESCRIPTION("Renesas RZ/N1 hardware watchdog");
+MODULE_AUTHOR("Phil Edworthy <phil.edworthy@renesas.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c
index dbeb2146c968..f9479a3fe2a6 100644
--- a/drivers/watchdog/sp805_wdt.c
+++ b/drivers/watchdog/sp805_wdt.c
@@ -272,6 +272,7 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
watchdog_set_nowayout(&wdt->wdd, nowayout);
watchdog_set_drvdata(&wdt->wdd, wdt);
watchdog_set_restart_priority(&wdt->wdd, 128);
+ watchdog_stop_on_unregister(&wdt->wdd);
/*
* If 'timeout-sec' devicetree property is specified, use that.
diff --git a/drivers/watchdog/sunplus_wdt.c b/drivers/watchdog/sunplus_wdt.c
new file mode 100644
index 000000000000..e2d8c532bcb1
--- /dev/null
+++ b/drivers/watchdog/sunplus_wdt.c
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * sunplus Watchdog Driver
+ *
+ * Copyright (C) 2021 Sunplus Technology Co., Ltd.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/watchdog.h>
+
+#define WDT_CTRL 0x00
+#define WDT_CNT 0x04
+
+#define WDT_STOP 0x3877
+#define WDT_RESUME 0x4A4B
+#define WDT_CLRIRQ 0x7482
+#define WDT_UNLOCK 0xAB00
+#define WDT_LOCK 0xAB01
+#define WDT_CONMAX 0xDEAF
+
+/* TIMEOUT_MAX = ffff0/90kHz =11.65, so longer than 11 seconds will time out. */
+#define SP_WDT_MAX_TIMEOUT 11U
+#define SP_WDT_DEFAULT_TIMEOUT 10
+
+#define STC_CLK 90000
+
+#define DEVICE_NAME "sunplus-wdt"
+
+static unsigned int timeout;
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+struct sp_wdt_priv {
+ struct watchdog_device wdev;
+ void __iomem *base;
+ struct clk *clk;
+ struct reset_control *rstc;
+};
+
+static int sp_wdt_restart(struct watchdog_device *wdev,
+ unsigned long action, void *data)
+{
+ struct sp_wdt_priv *priv = watchdog_get_drvdata(wdev);
+ void __iomem *base = priv->base;
+
+ writel(WDT_STOP, base + WDT_CTRL);
+ writel(WDT_UNLOCK, base + WDT_CTRL);
+ writel(0x0001, base + WDT_CNT);
+ writel(WDT_LOCK, base + WDT_CTRL);
+ writel(WDT_RESUME, base + WDT_CTRL);
+
+ return 0;
+}
+
+static int sp_wdt_ping(struct watchdog_device *wdev)
+{
+ struct sp_wdt_priv *priv = watchdog_get_drvdata(wdev);
+ void __iomem *base = priv->base;
+ u32 count;
+
+ if (wdev->timeout > SP_WDT_MAX_TIMEOUT) {
+ /* WDT_CONMAX sets the count to the maximum (down-counting). */
+ writel(WDT_CONMAX, base + WDT_CTRL);
+ } else {
+ writel(WDT_UNLOCK, base + WDT_CTRL);
+ /*
+ * Watchdog timer is a 20-bit down-counting based on STC_CLK.
+ * This register bits[16:0] is from bit[19:4] of the watchdog
+ * timer counter.
+ */
+ count = (wdev->timeout * STC_CLK) >> 4;
+ writel(count, base + WDT_CNT);
+ writel(WDT_LOCK, base + WDT_CTRL);
+ }
+
+ return 0;
+}
+
+static int sp_wdt_stop(struct watchdog_device *wdev)
+{
+ struct sp_wdt_priv *priv = watchdog_get_drvdata(wdev);
+ void __iomem *base = priv->base;
+
+ writel(WDT_STOP, base + WDT_CTRL);
+
+ return 0;
+}
+
+static int sp_wdt_start(struct watchdog_device *wdev)
+{
+ struct sp_wdt_priv *priv = watchdog_get_drvdata(wdev);
+ void __iomem *base = priv->base;
+
+ writel(WDT_RESUME, base + WDT_CTRL);
+
+ return 0;
+}
+
+static unsigned int sp_wdt_get_timeleft(struct watchdog_device *wdev)
+{
+ struct sp_wdt_priv *priv = watchdog_get_drvdata(wdev);
+ void __iomem *base = priv->base;
+ u32 val;
+
+ val = readl(base + WDT_CNT);
+ val &= 0xffff;
+ val = val << 4;
+
+ return val;
+}
+
+static const struct watchdog_info sp_wdt_info = {
+ .identity = DEVICE_NAME,
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE |
+ WDIOF_KEEPALIVEPING,
+};
+
+static const struct watchdog_ops sp_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = sp_wdt_start,
+ .stop = sp_wdt_stop,
+ .ping = sp_wdt_ping,
+ .get_timeleft = sp_wdt_get_timeleft,
+ .restart = sp_wdt_restart,
+};
+
+static void sp_clk_disable_unprepare(void *data)
+{
+ clk_disable_unprepare(data);
+}
+
+static void sp_reset_control_assert(void *data)
+{
+ reset_control_assert(data);
+}
+
+static int sp_wdt_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct sp_wdt_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->clk))
+ return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to get clock\n");
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable clock\n");
+
+ ret = devm_add_action_or_reset(dev, sp_clk_disable_unprepare, priv->clk);
+ if (ret)
+ return ret;
+
+ /* The timer and watchdog shared the STC reset */
+ priv->rstc = devm_reset_control_get_shared(dev, NULL);
+ if (IS_ERR(priv->rstc))
+ return dev_err_probe(dev, PTR_ERR(priv->rstc), "Failed to get reset\n");
+
+ reset_control_deassert(priv->rstc);
+
+ ret = devm_add_action_or_reset(dev, sp_reset_control_assert, priv->rstc);
+ if (ret)
+ return ret;
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ priv->wdev.info = &sp_wdt_info;
+ priv->wdev.ops = &sp_wdt_ops;
+ priv->wdev.timeout = SP_WDT_DEFAULT_TIMEOUT;
+ priv->wdev.max_hw_heartbeat_ms = SP_WDT_MAX_TIMEOUT * 1000;
+ priv->wdev.min_timeout = 1;
+ priv->wdev.parent = dev;
+
+ watchdog_set_drvdata(&priv->wdev, priv);
+ watchdog_init_timeout(&priv->wdev, timeout, dev);
+ watchdog_set_nowayout(&priv->wdev, nowayout);
+ watchdog_stop_on_reboot(&priv->wdev);
+ watchdog_set_restart_priority(&priv->wdev, 128);
+
+ return devm_watchdog_register_device(dev, &priv->wdev);
+}
+
+static const struct of_device_id sp_wdt_of_match[] = {
+ {.compatible = "sunplus,sp7021-wdt", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sp_wdt_of_match);
+
+static struct platform_driver sp_wdt_driver = {
+ .probe = sp_wdt_probe,
+ .driver = {
+ .name = DEVICE_NAME,
+ .of_match_table = sp_wdt_of_match,
+ },
+};
+
+module_platform_driver(sp_wdt_driver);
+
+MODULE_AUTHOR("Xiantao Hu <xt.hu@cqplus1.com>");
+MODULE_DESCRIPTION("Sunplus Watchdog Timer Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/ts4800_wdt.c b/drivers/watchdog/ts4800_wdt.c
index c137ad2bd5c3..0ea554c7cda5 100644
--- a/drivers/watchdog/ts4800_wdt.c
+++ b/drivers/watchdog/ts4800_wdt.c
@@ -125,13 +125,16 @@ static int ts4800_wdt_probe(struct platform_device *pdev)
ret = of_property_read_u32_index(np, "syscon", 1, &reg);
if (ret < 0) {
dev_err(dev, "no offset in syscon\n");
+ of_node_put(syscon_np);
return ret;
}
/* allocate memory for watchdog struct */
wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
- if (!wdt)
+ if (!wdt) {
+ of_node_put(syscon_np);
return -ENOMEM;
+ }
/* set regmap and offset to know where to write */
wdt->feed_offset = reg;
diff --git a/drivers/watchdog/wdat_wdt.c b/drivers/watchdog/wdat_wdt.c
index 195c8c004b69..e6f95e99156d 100644
--- a/drivers/watchdog/wdat_wdt.c
+++ b/drivers/watchdog/wdat_wdt.c
@@ -344,6 +344,7 @@ static int wdat_wdt_probe(struct platform_device *pdev)
wdat->period = tbl->timer_period;
wdat->wdd.min_hw_heartbeat_ms = wdat->period * tbl->min_count;
wdat->wdd.max_hw_heartbeat_ms = wdat->period * tbl->max_count;
+ wdat->wdd.min_timeout = 1;
wdat->stopped_in_sleep = tbl->flags & ACPI_WDAT_STOPPED;
wdat->wdd.info = &wdat_wdt_info;
wdat->wdd.ops = &wdat_wdt_ops;
@@ -450,8 +451,7 @@ static int wdat_wdt_probe(struct platform_device *pdev)
* watchdog properly after it has opened the device. In some cases
* the BIOS default is too short and causes immediate reboot.
*/
- if (timeout * 1000 < wdat->wdd.min_hw_heartbeat_ms ||
- timeout * 1000 > wdat->wdd.max_hw_heartbeat_ms) {
+ if (watchdog_timeout_invalid(&wdat->wdd, timeout)) {
dev_warn(dev, "Invalid timeout %d given, using %d\n",
timeout, WDAT_DEFAULT_TIMEOUT);
timeout = WDAT_DEFAULT_TIMEOUT;
@@ -462,6 +462,8 @@ static int wdat_wdt_probe(struct platform_device *pdev)
return ret;
watchdog_set_nowayout(&wdat->wdd, nowayout);
+ watchdog_stop_on_reboot(&wdat->wdd);
+ watchdog_stop_on_unregister(&wdat->wdd);
return devm_watchdog_register_device(dev, &wdat->wdd);
}