summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2024-12-05 10:03:43 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2024-12-05 10:03:43 -0800
commit42d52acfb10cfc53139a1139e1c401a52c3f924c (patch)
tree9d61fcf2d45cfe2a14e8b835bdfc65fffba63873
parent5076001689e4f8b6b178c40d268ed2ecd2eac29d (diff)
parent4962ee045d8f06638714d801ab0fb72f89c16690 (diff)
Merge tag 'linux-watchdog-6.13-rc1' of git://www.linux-watchdog.org/linux-watchdog
Pull watchdog updates from Wim Van Sebroeck: - Add support for exynosautov920 SoC - Add support for Airoha EN7851 watchdog - Add support for MT6735 TOPRGU/WDT - Delete the cpu5wdt driver - Always print when registering watchdog fails - Several other small fixes and improvements * tag 'linux-watchdog-6.13-rc1' of git://www.linux-watchdog.org/linux-watchdog: (36 commits) watchdog: rti: of: honor timeout-sec property watchdog: s3c2410_wdt: add support for exynosautov920 SoC dt-bindings: watchdog: Document ExynosAutoV920 watchdog bindings watchdog: mediatek: Add support for MT6735 TOPRGU/WDT watchdog: mediatek: Make sure system reset gets asserted in mtk_wdt_restart() dt-bindings: watchdog: fsl-imx-wdt: Add missing 'big-endian' property dt-bindings: watchdog: Document Qualcomm QCS8300 docs: ABI: Fix spelling mistake in pretimeout_avaialable_governors Revert "watchdog: s3c2410_wdt: use exynos_get_pmu_regmap_by_phandle() for PMU regs" watchdog: rzg2l_wdt: Power on the watchdog domain in the restart handler watchdog: Switch back to struct platform_driver::remove() watchdog: it87_wdt: add PWRGD enable quirk for Qotom QCML04 watchdog: da9063: Remove __maybe_unused notations watchdog: da9063: Do not use a global variable watchdog: Delete the cpu5wdt driver watchdog: Add support for Airoha EN7851 watchdog dt-bindings: watchdog: airoha: document watchdog for Airoha EN7581 watchdog: sl28cpld_wdt: don't print out if registering watchdog fails watchdog: rza_wdt: don't print out if registering watchdog fails watchdog: rti_wdt: don't print out if registering watchdog fails ...
-rw-r--r--Documentation/ABI/testing/sysfs-class-watchdog2
-rw-r--r--Documentation/devicetree/bindings/watchdog/airoha,en7581-wdt.yaml47
-rw-r--r--Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.yaml14
-rw-r--r--Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml2
-rw-r--r--Documentation/devicetree/bindings/watchdog/samsung-wdt.yaml3
-rw-r--r--Documentation/watchdog/watchdog-parameters.rst10
-rw-r--r--MAINTAINERS2
-rw-r--r--drivers/watchdog/Kconfig17
-rw-r--r--drivers/watchdog/Makefile2
-rw-r--r--drivers/watchdog/airoha_wdt.c216
-rw-r--r--drivers/watchdog/apple_wdt.c8
-rw-r--r--drivers/watchdog/armada_37xx_wdt.c10
-rw-r--r--drivers/watchdog/cpu5wdt.c284
-rw-r--r--drivers/watchdog/da9055_wdt.c7
-rw-r--r--drivers/watchdog/da9063_wdt.c19
-rw-r--r--drivers/watchdog/gxp-wdt.c4
-rw-r--r--drivers/watchdog/iTCO_wdt.c25
-rw-r--r--drivers/watchdog/it87_wdt.c43
-rw-r--r--drivers/watchdog/mtk_wdt.c12
-rw-r--r--drivers/watchdog/octeon-wdt-main.c4
-rw-r--r--drivers/watchdog/pcwd.c2
-rw-r--r--drivers/watchdog/rti_wdt.c7
-rw-r--r--drivers/watchdog/rza_wdt.c7
-rw-r--r--drivers/watchdog/rzg2l_wdt.c20
-rw-r--r--drivers/watchdog/rzn1_wdt.c2
-rw-r--r--drivers/watchdog/s3c2410_wdt.c45
-rw-r--r--drivers/watchdog/sa1100_wdt.c4
-rw-r--r--drivers/watchdog/sl28cpld_wdt.c4
-rw-r--r--drivers/watchdog/smsc37b787_wdt.c2
-rw-r--r--drivers/watchdog/starfive-wdt.c2
-rw-r--r--drivers/watchdog/stm32_iwdg.c95
-rw-r--r--drivers/watchdog/watchdog_core.c26
-rw-r--r--drivers/watchdog/xilinx_wwdt.c75
-rw-r--r--drivers/watchdog/ziirave_wdt.c2
-rw-r--r--include/linux/mfd/da9063/core.h1
35 files changed, 631 insertions, 394 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-watchdog b/Documentation/ABI/testing/sysfs-class-watchdog
index 94fb74615951..70eabccf0557 100644
--- a/Documentation/ABI/testing/sysfs-class-watchdog
+++ b/Documentation/ABI/testing/sysfs-class-watchdog
@@ -76,7 +76,7 @@ Description:
timeout when the pretimeout interrupt is delivered. Pretimeout
is an optional feature.
-What: /sys/class/watchdog/watchdogn/pretimeout_avaialable_governors
+What: /sys/class/watchdog/watchdogn/pretimeout_available_governors
Date: February 2017
Contact: Wim Van Sebroeck <wim@iguana.be>
Description:
diff --git a/Documentation/devicetree/bindings/watchdog/airoha,en7581-wdt.yaml b/Documentation/devicetree/bindings/watchdog/airoha,en7581-wdt.yaml
new file mode 100644
index 000000000000..6bbab3cb28e5
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/airoha,en7581-wdt.yaml
@@ -0,0 +1,47 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/watchdog/airoha,en7581-wdt.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Airoha EN7581 Watchdog Timer
+
+maintainers:
+ - Christian Marangi <ansuelsmth@gmail.com>
+
+allOf:
+ - $ref: watchdog.yaml#
+
+properties:
+ compatible:
+ const: airoha,en7581-wdt
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ description: BUS clock (timer ticks at half the BUS clock)
+ maxItems: 1
+
+ clock-names:
+ const: bus
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/en7523-clk.h>
+
+ watchdog@1fbf0100 {
+ compatible = "airoha,en7581-wdt";
+ reg = <0x1fbf0100 0x3c>;
+
+ clocks = <&scuclk EN7523_CLK_BUS>;
+ clock-names = "bus";
+ };
diff --git a/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.yaml b/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.yaml
index 36b836d0620c..0da953cb7127 100644
--- a/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.yaml
+++ b/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.yaml
@@ -48,6 +48,8 @@ properties:
clocks:
maxItems: 1
+ big-endian: true
+
fsl,ext-reset-output:
$ref: /schemas/types.yaml#/definitions/flag
description: |
@@ -93,6 +95,18 @@ allOf:
properties:
fsl,suspend-in-wait: false
+ - if:
+ not:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - fsl,ls1012a-wdt
+ - fsl,ls1043a-wdt
+ then:
+ properties:
+ big-endian: false
+
unevaluatedProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml
index 932393f8c649..34896a39fa91 100644
--- a/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml
+++ b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml
@@ -26,6 +26,8 @@ properties:
- qcom,apss-wdt-msm8994
- qcom,apss-wdt-qcm2290
- qcom,apss-wdt-qcs404
+ - qcom,apss-wdt-qcs615
+ - qcom,apss-wdt-qcs8300
- qcom,apss-wdt-sa8255p
- qcom,apss-wdt-sa8775p
- qcom,apss-wdt-sc7180
diff --git a/Documentation/devicetree/bindings/watchdog/samsung-wdt.yaml b/Documentation/devicetree/bindings/watchdog/samsung-wdt.yaml
index 77a5ddd0426e..d175ae968336 100644
--- a/Documentation/devicetree/bindings/watchdog/samsung-wdt.yaml
+++ b/Documentation/devicetree/bindings/watchdog/samsung-wdt.yaml
@@ -26,6 +26,7 @@ properties:
- samsung,exynos7-wdt # for Exynos7
- samsung,exynos850-wdt # for Exynos850
- samsung,exynosautov9-wdt # for Exynosautov9
+ - samsung,exynosautov920-wdt # for Exynosautov920
- items:
- enum:
- tesla,fsd-wdt
@@ -77,6 +78,7 @@ allOf:
- samsung,exynos7-wdt
- samsung,exynos850-wdt
- samsung,exynosautov9-wdt
+ - samsung,exynosautov920-wdt
then:
required:
- samsung,syscon-phandle
@@ -88,6 +90,7 @@ allOf:
- google,gs101-wdt
- samsung,exynos850-wdt
- samsung,exynosautov9-wdt
+ - samsung,exynosautov920-wdt
then:
properties:
clocks:
diff --git a/Documentation/watchdog/watchdog-parameters.rst b/Documentation/watchdog/watchdog-parameters.rst
index 29153eed6689..0a0119edfa82 100644
--- a/Documentation/watchdog/watchdog-parameters.rst
+++ b/Documentation/watchdog/watchdog-parameters.rst
@@ -120,16 +120,6 @@ coh901327_wdt:
-------------------------------------------------
-cpu5wdt:
- port:
- base address of watchdog card, default is 0x91
- verbose:
- be verbose, default is 0 (no)
- ticks:
- count down ticks, default is 10000
-
--------------------------------------------------
-
cpwd:
wd0_timeout:
Default watchdog0 timeout in 1/10secs
diff --git a/MAINTAINERS b/MAINTAINERS
index 1e930c7a58b1..f4d652f310e8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22407,7 +22407,7 @@ F: drivers/char/hw_random/jh7110-trng.c
STARFIVE WATCHDOG DRIVER
M: Xingyu Wu <xingyu.wu@starfivetech.com>
-M: Samin Guo <samin.guo@starfivetech.com>
+M: Ziv Xu <ziv.xu@starfivetech.com>
S: Supported
F: Documentation/devicetree/bindings/watchdog/starfive*
F: drivers/watchdog/starfive-wdt.c
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 2333476a42c0..f81705f8539a 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -408,6 +408,14 @@ config SL28CPLD_WATCHDOG
# ARM Architecture
+config AIROHA_WATCHDOG
+ tristate "Airoha EN7581 Watchdog"
+ depends on ARCH_AIROHA || COMPILE_TEST
+ select WATCHDOG_CORE
+ help
+ Watchdog timer embedded into Airoha SoC. This will reboot your
+ system when the timeout is reached.
+
config ARM_SP805_WATCHDOG
tristate "ARM SP805 Watchdog"
depends on (ARM || ARM64 || COMPILE_TEST) && ARM_AMBA
@@ -549,6 +557,7 @@ config S3C2410_WATCHDOG
tristate "S3C6410/S5Pv210/Exynos Watchdog"
depends on ARCH_S3C64XX || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
select WATCHDOG_CORE
+ select MFD_SYSCON if ARCH_EXYNOS
help
Watchdog timer block in the Samsung S3C64xx, S5Pv210 and Exynos
SoCs. This will reboot the system when the timer expires with
@@ -1543,14 +1552,6 @@ config SBC7240_WDT
To compile this driver as a module, choose M here: the
module will be called sbc7240_wdt.
-config CPU5_WDT
- tristate "SMA CPU5 Watchdog"
- depends on (X86 || COMPILE_TEST) && HAS_IOPORT
- help
- TBD.
- To compile this driver as a module, choose M here: the
- module will be called cpu5wdt.
-
config SMSC_SCH311X_WDT
tristate "SMSC SCH311X Watchdog Timer"
depends on (X86 || COMPILE_TEST) && HAS_IOPORT
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 386d88d89fe5..8411626fa162 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
obj-$(CONFIG_ARM_SBSA_WATCHDOG) += sbsa_gwdt.o
obj-$(CONFIG_ARMADA_37XX_WATCHDOG) += armada_37xx_wdt.o
+obj-$(CONFIG_AIROHA_WATCHDOG) += airoha_wdt.o
obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
@@ -138,7 +139,6 @@ obj-$(CONFIG_RDC321X_WDT) += rdc321x_wdt.o
obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o
obj-$(CONFIG_SBC8360_WDT) += sbc8360.o
obj-$(CONFIG_SBC7240_WDT) += sbc7240_wdt.o
-obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o
obj-$(CONFIG_SMSC_SCH311X_WDT) += sch311x_wdt.o
obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o
obj-$(CONFIG_TQMX86_WDT) += tqmx86_wdt.o
diff --git a/drivers/watchdog/airoha_wdt.c b/drivers/watchdog/airoha_wdt.c
new file mode 100644
index 000000000000..dc8ca11c14d8
--- /dev/null
+++ b/drivers/watchdog/airoha_wdt.c
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Airoha Watchdog Driver
+ *
+ * Copyright (c) 2024, AIROHA All rights reserved.
+ *
+ * Mayur Kumar <mayur.kumar@airoha.com>
+ * Christian Marangi <ansuelsmth@gmail.com>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/math.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+
+/* Base address of timer and watchdog registers */
+#define TIMER_CTRL 0x0
+#define WDT_ENABLE BIT(25)
+#define WDT_TIMER_INTERRUPT BIT(21)
+/* Timer3 is used as Watchdog Timer */
+#define WDT_TIMER_ENABLE BIT(5)
+#define WDT_TIMER_LOAD_VALUE 0x2c
+#define WDT_TIMER_CUR_VALUE 0x30
+#define WDT_TIMER_VAL GENMASK(31, 0)
+#define WDT_RELOAD 0x38
+#define WDT_RLD BIT(0)
+
+/* Airoha watchdog structure description */
+struct airoha_wdt_desc {
+ struct watchdog_device wdog_dev;
+ unsigned int wdt_freq;
+ void __iomem *base;
+};
+
+#define WDT_HEARTBEAT 24
+static int heartbeat = WDT_HEARTBEAT;
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. (default="
+ __MODULE_STRING(WDT_HEARTBEAT) ")");
+
+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) ")");
+
+static int airoha_wdt_start(struct watchdog_device *wdog_dev)
+{
+ struct airoha_wdt_desc *airoha_wdt = watchdog_get_drvdata(wdog_dev);
+ u32 val;
+
+ val = readl(airoha_wdt->base + TIMER_CTRL);
+ val |= (WDT_TIMER_ENABLE | WDT_ENABLE | WDT_TIMER_INTERRUPT);
+ writel(val, airoha_wdt->base + TIMER_CTRL);
+ val = wdog_dev->timeout * airoha_wdt->wdt_freq;
+ writel(val, airoha_wdt->base + WDT_TIMER_LOAD_VALUE);
+
+ return 0;
+}
+
+static int airoha_wdt_stop(struct watchdog_device *wdog_dev)
+{
+ struct airoha_wdt_desc *airoha_wdt = watchdog_get_drvdata(wdog_dev);
+ u32 val;
+
+ val = readl(airoha_wdt->base + TIMER_CTRL);
+ val &= (~WDT_ENABLE & ~WDT_TIMER_ENABLE);
+ writel(val, airoha_wdt->base + TIMER_CTRL);
+
+ return 0;
+}
+
+static int airoha_wdt_ping(struct watchdog_device *wdog_dev)
+{
+ struct airoha_wdt_desc *airoha_wdt = watchdog_get_drvdata(wdog_dev);
+ u32 val;
+
+ val = readl(airoha_wdt->base + WDT_RELOAD);
+ val |= WDT_RLD;
+ writel(val, airoha_wdt->base + WDT_RELOAD);
+
+ return 0;
+}
+
+static int airoha_wdt_set_timeout(struct watchdog_device *wdog_dev, unsigned int timeout)
+{
+ wdog_dev->timeout = timeout;
+
+ if (watchdog_active(wdog_dev)) {
+ airoha_wdt_stop(wdog_dev);
+ return airoha_wdt_start(wdog_dev);
+ }
+
+ return 0;
+}
+
+static unsigned int airoha_wdt_get_timeleft(struct watchdog_device *wdog_dev)
+{
+ struct airoha_wdt_desc *airoha_wdt = watchdog_get_drvdata(wdog_dev);
+ u32 val;
+
+ val = readl(airoha_wdt->base + WDT_TIMER_CUR_VALUE);
+ return DIV_ROUND_UP(val, airoha_wdt->wdt_freq);
+}
+
+static const struct watchdog_info airoha_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
+ .identity = "Airoha Watchdog",
+};
+
+static const struct watchdog_ops airoha_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = airoha_wdt_start,
+ .stop = airoha_wdt_stop,
+ .ping = airoha_wdt_ping,
+ .set_timeout = airoha_wdt_set_timeout,
+ .get_timeleft = airoha_wdt_get_timeleft,
+};
+
+static int airoha_wdt_probe(struct platform_device *pdev)
+{
+ struct airoha_wdt_desc *airoha_wdt;
+ struct watchdog_device *wdog_dev;
+ struct device *dev = &pdev->dev;
+ struct clk *bus_clk;
+ int ret;
+
+ airoha_wdt = devm_kzalloc(dev, sizeof(*airoha_wdt), GFP_KERNEL);
+ if (!airoha_wdt)
+ return -ENOMEM;
+
+ airoha_wdt->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(airoha_wdt->base))
+ return PTR_ERR(airoha_wdt->base);
+
+ bus_clk = devm_clk_get_enabled(dev, "bus");
+ if (IS_ERR(bus_clk))
+ return dev_err_probe(dev, PTR_ERR(bus_clk),
+ "failed to enable bus clock\n");
+
+ /* Watchdog ticks at half the bus rate */
+ airoha_wdt->wdt_freq = clk_get_rate(bus_clk) / 2;
+
+ /* Initialize struct watchdog device */
+ wdog_dev = &airoha_wdt->wdog_dev;
+ wdog_dev->timeout = heartbeat;
+ wdog_dev->info = &airoha_wdt_info;
+ wdog_dev->ops = &airoha_wdt_ops;
+ /* Bus 300MHz, watchdog 150MHz, 28 seconds */
+ wdog_dev->max_timeout = FIELD_MAX(WDT_TIMER_VAL) / airoha_wdt->wdt_freq;
+ wdog_dev->parent = dev;
+
+ watchdog_set_drvdata(wdog_dev, airoha_wdt);
+ watchdog_set_nowayout(wdog_dev, nowayout);
+ watchdog_stop_on_unregister(wdog_dev);
+
+ ret = devm_watchdog_register_device(dev, wdog_dev);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, airoha_wdt);
+ return 0;
+}
+
+static int airoha_wdt_suspend(struct device *dev)
+{
+ struct airoha_wdt_desc *airoha_wdt = dev_get_drvdata(dev);
+
+ if (watchdog_active(&airoha_wdt->wdog_dev))
+ airoha_wdt_stop(&airoha_wdt->wdog_dev);
+
+ return 0;
+}
+
+static int airoha_wdt_resume(struct device *dev)
+{
+ struct airoha_wdt_desc *airoha_wdt = dev_get_drvdata(dev);
+
+ if (watchdog_active(&airoha_wdt->wdog_dev)) {
+ airoha_wdt_start(&airoha_wdt->wdog_dev);
+ airoha_wdt_ping(&airoha_wdt->wdog_dev);
+ }
+ return 0;
+}
+
+static const struct of_device_id airoha_wdt_of_match[] = {
+ { .compatible = "airoha,en7581-wdt", },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, airoha_wdt_of_match);
+
+static DEFINE_SIMPLE_DEV_PM_OPS(airoha_wdt_pm_ops, airoha_wdt_suspend, airoha_wdt_resume);
+
+static struct platform_driver airoha_wdt_driver = {
+ .probe = airoha_wdt_probe,
+ .driver = {
+ .name = "airoha-wdt",
+ .pm = pm_sleep_ptr(&airoha_wdt_pm_ops),
+ .of_match_table = airoha_wdt_of_match,
+ },
+};
+
+module_platform_driver(airoha_wdt_driver);
+
+MODULE_AUTHOR("Mayur Kumar <mayur.kumar@airoha.com>");
+MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
+MODULE_DESCRIPTION("Airoha EN7581 Watchdog Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/apple_wdt.c b/drivers/watchdog/apple_wdt.c
index d4f739932f0b..95d9e37df41c 100644
--- a/drivers/watchdog/apple_wdt.c
+++ b/drivers/watchdog/apple_wdt.c
@@ -127,11 +127,11 @@ static int apple_wdt_restart(struct watchdog_device *wdd, unsigned long mode,
/*
* Flush writes and then wait for the SoC to reset. Even though the
* reset is queued almost immediately experiments have shown that it
- * can take up to ~20-25ms until the SoC is actually reset. Just wait
- * 50ms here to be safe.
+ * can take up to ~120-125ms until the SoC is actually reset. Just
+ * wait 150ms here to be safe.
*/
- (void)readl_relaxed(wdt->regs + APPLE_WDT_WD1_CUR_TIME);
- mdelay(50);
+ (void)readl(wdt->regs + APPLE_WDT_WD1_CUR_TIME);
+ mdelay(150);
return 0;
}
diff --git a/drivers/watchdog/armada_37xx_wdt.c b/drivers/watchdog/armada_37xx_wdt.c
index 8133a5d05647..a17a7911771a 100644
--- a/drivers/watchdog/armada_37xx_wdt.c
+++ b/drivers/watchdog/armada_37xx_wdt.c
@@ -248,7 +248,6 @@ static const struct watchdog_ops armada_37xx_wdt_ops = {
static int armada_37xx_wdt_probe(struct platform_device *pdev)
{
struct armada_37xx_watchdog *dev;
- struct resource *res;
struct regmap *regmap;
int ret;
@@ -266,12 +265,9 @@ static int armada_37xx_wdt_probe(struct platform_device *pdev)
return PTR_ERR(regmap);
dev->cpu_misc = regmap;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENODEV;
- dev->reg = devm_ioremap(&pdev->dev, res->start, resource_size(res));
- if (!dev->reg)
- return -ENOMEM;
+ dev->reg = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(dev->reg))
+ return PTR_ERR(dev->reg);
/* init clock */
dev->clk = devm_clk_get_enabled(&pdev->dev, NULL);
diff --git a/drivers/watchdog/cpu5wdt.c b/drivers/watchdog/cpu5wdt.c
deleted file mode 100644
index f94b84048612..000000000000
--- a/drivers/watchdog/cpu5wdt.c
+++ /dev/null
@@ -1,284 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * sma cpu5 watchdog driver
- *
- * Copyright (C) 2003 Heiko Ronsdorf <hero@ihg.uni-duisburg.de>
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/miscdevice.h>
-#include <linux/fs.h>
-#include <linux/ioport.h>
-#include <linux/timer.h>
-#include <linux/completion.h>
-#include <linux/jiffies.h>
-#include <linux/io.h>
-#include <linux/uaccess.h>
-#include <linux/watchdog.h>
-
-/* adjustable parameters */
-
-static int verbose;
-static int port = 0x91;
-static int ticks = 10000;
-static DEFINE_SPINLOCK(cpu5wdt_lock);
-
-#define PFX "cpu5wdt: "
-
-#define CPU5WDT_EXTENT 0x0A
-
-#define CPU5WDT_STATUS_REG 0x00
-#define CPU5WDT_TIME_A_REG 0x02
-#define CPU5WDT_TIME_B_REG 0x03
-#define CPU5WDT_MODE_REG 0x04
-#define CPU5WDT_TRIGGER_REG 0x07
-#define CPU5WDT_ENABLE_REG 0x08
-#define CPU5WDT_RESET_REG 0x09
-
-#define CPU5WDT_INTERVAL (HZ/10+1)
-
-/* some device data */
-
-static struct {
- struct completion stop;
- int running;
- struct timer_list timer;
- int queue;
- int default_ticks;
- unsigned long inuse;
-} cpu5wdt_device;
-
-/* generic helper functions */
-
-static void cpu5wdt_trigger(struct timer_list *unused)
-{
- if (verbose > 2)
- pr_debug("trigger at %i ticks\n", ticks);
-
- if (cpu5wdt_device.running)
- ticks--;
-
- spin_lock(&cpu5wdt_lock);
- /* keep watchdog alive */
- outb(1, port + CPU5WDT_TRIGGER_REG);
-
- /* requeue?? */
- if (cpu5wdt_device.queue && ticks)
- mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL);
- else {
- /* ticks doesn't matter anyway */
- complete(&cpu5wdt_device.stop);
- }
- spin_unlock(&cpu5wdt_lock);
-
-}
-
-static void cpu5wdt_reset(void)
-{
- ticks = cpu5wdt_device.default_ticks;
-
- if (verbose)
- pr_debug("reset (%i ticks)\n", (int) ticks);
-
-}
-
-static void cpu5wdt_start(void)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&cpu5wdt_lock, flags);
- if (!cpu5wdt_device.queue) {
- cpu5wdt_device.queue = 1;
- outb(0, port + CPU5WDT_TIME_A_REG);
- outb(0, port + CPU5WDT_TIME_B_REG);
- outb(1, port + CPU5WDT_MODE_REG);
- outb(0, port + CPU5WDT_RESET_REG);
- outb(0, port + CPU5WDT_ENABLE_REG);
- mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL);
- }
- /* if process dies, counter is not decremented */
- cpu5wdt_device.running++;
- spin_unlock_irqrestore(&cpu5wdt_lock, flags);
-}
-
-static int cpu5wdt_stop(void)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&cpu5wdt_lock, flags);
- if (cpu5wdt_device.running)
- cpu5wdt_device.running = 0;
- ticks = cpu5wdt_device.default_ticks;
- spin_unlock_irqrestore(&cpu5wdt_lock, flags);
- if (verbose)
- pr_crit("stop not possible\n");
- return -EIO;
-}
-
-/* filesystem operations */
-
-static int cpu5wdt_open(struct inode *inode, struct file *file)
-{
- if (test_and_set_bit(0, &cpu5wdt_device.inuse))
- return -EBUSY;
- return stream_open(inode, file);
-}
-
-static int cpu5wdt_release(struct inode *inode, struct file *file)
-{
- clear_bit(0, &cpu5wdt_device.inuse);
- return 0;
-}
-
-static long cpu5wdt_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- void __user *argp = (void __user *)arg;
- int __user *p = argp;
- unsigned int value;
- static const struct watchdog_info ident = {
- .options = WDIOF_CARDRESET,
- .identity = "CPU5 WDT",
- };
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- if (copy_to_user(argp, &ident, sizeof(ident)))
- return -EFAULT;
- break;
- case WDIOC_GETSTATUS:
- value = inb(port + CPU5WDT_STATUS_REG);
- value = (value >> 2) & 1;
- return put_user(value, p);
- case WDIOC_GETBOOTSTATUS:
- return put_user(0, p);
- case WDIOC_SETOPTIONS:
- if (get_user(value, p))
- return -EFAULT;
- if (value & WDIOS_ENABLECARD)
- cpu5wdt_start();
- if (value & WDIOS_DISABLECARD)
- cpu5wdt_stop();
- break;
- case WDIOC_KEEPALIVE:
- cpu5wdt_reset();
- break;
- default:
- return -ENOTTY;
- }
- return 0;
-}
-
-static ssize_t cpu5wdt_write(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- if (!count)
- return -EIO;
- cpu5wdt_reset();
- return count;
-}
-
-static const struct file_operations cpu5wdt_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = cpu5wdt_ioctl,
- .compat_ioctl = compat_ptr_ioctl,
- .open = cpu5wdt_open,
- .write = cpu5wdt_write,
- .release = cpu5wdt_release,
-};
-
-static struct miscdevice cpu5wdt_misc = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &cpu5wdt_fops,
-};
-
-/* init/exit function */
-
-static int cpu5wdt_init(void)
-{
- unsigned int val;
- int err;
-
- if (verbose)
- pr_debug("port=0x%x, verbose=%i\n", port, verbose);
-
- init_completion(&cpu5wdt_device.stop);
- cpu5wdt_device.queue = 0;
- timer_setup(&cpu5wdt_device.timer, cpu5wdt_trigger, 0);
- cpu5wdt_device.default_ticks = ticks;
-
- if (!request_region(port, CPU5WDT_EXTENT, PFX)) {
- pr_err("request_region failed\n");
- err = -EBUSY;
- goto no_port;
- }
-
- /* watchdog reboot? */
- val = inb(port + CPU5WDT_STATUS_REG);
- val = (val >> 2) & 1;
- if (!val)
- pr_info("sorry, was my fault\n");
-
- err = misc_register(&cpu5wdt_misc);
- if (err < 0) {
- pr_err("misc_register failed\n");
- goto no_misc;
- }
-
-
- pr_info("init success\n");
- return 0;
-
-no_misc:
- release_region(port, CPU5WDT_EXTENT);
-no_port:
- return err;
-}
-
-static int cpu5wdt_init_module(void)
-{
- return cpu5wdt_init();
-}
-
-static void cpu5wdt_exit(void)
-{
- if (cpu5wdt_device.queue) {
- cpu5wdt_device.queue = 0;
- wait_for_completion(&cpu5wdt_device.stop);
- timer_shutdown_sync(&cpu5wdt_device.timer);
- }
-
- misc_deregister(&cpu5wdt_misc);
-
- release_region(port, CPU5WDT_EXTENT);
-
-}
-
-static void cpu5wdt_exit_module(void)
-{
- cpu5wdt_exit();
-}
-
-/* module entry points */
-
-module_init(cpu5wdt_init_module);
-module_exit(cpu5wdt_exit_module);
-
-MODULE_AUTHOR("Heiko Ronsdorf <hero@ihg.uni-duisburg.de>");
-MODULE_DESCRIPTION("sma cpu5 watchdog driver");
-MODULE_LICENSE("GPL");
-
-module_param_hw(port, int, ioport, 0);
-MODULE_PARM_DESC(port, "base address of watchdog card, default is 0x91");
-
-module_param(verbose, int, 0);
-MODULE_PARM_DESC(verbose, "be verbose, default is 0 (no)");
-
-module_param(ticks, int, 0);
-MODULE_PARM_DESC(ticks, "count down ticks, default is 10000");
diff --git a/drivers/watchdog/da9055_wdt.c b/drivers/watchdog/da9055_wdt.c
index 389a4bdd208c..9d5a2009466f 100644
--- a/drivers/watchdog/da9055_wdt.c
+++ b/drivers/watchdog/da9055_wdt.c
@@ -146,12 +146,7 @@ static int da9055_wdt_probe(struct platform_device *pdev)
return ret;
}
- ret = devm_watchdog_register_device(dev, &driver_data->wdt);
- if (ret != 0)
- dev_err(da9055->dev, "watchdog_register_device() failed: %d\n",
- ret);
-
- return ret;
+ return devm_watchdog_register_device(dev, &driver_data->wdt);
}
static struct platform_driver da9055_wdt_driver = {
diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c
index 684667469b10..92e1b78ff481 100644
--- a/drivers/watchdog/da9063_wdt.c
+++ b/drivers/watchdog/da9063_wdt.c
@@ -27,7 +27,6 @@
* 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
@@ -230,7 +229,7 @@ static int da9063_wdt_probe(struct platform_device *pdev)
if (!wdd)
return -ENOMEM;
- use_sw_pm = device_property_present(dev, "dlg,use-sw-pm");
+ da9063->use_sw_pm = device_property_present(dev, "dlg,use-sw-pm");
wdd->info = &da9063_watchdog_info;
wdd->ops = &da9063_watchdog_ops;
@@ -264,11 +263,12 @@ 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)
+static int da9063_wdt_suspend(struct device *dev)
{
struct watchdog_device *wdd = dev_get_drvdata(dev);
+ struct da9063 *da9063 = watchdog_get_drvdata(wdd);
- if (!use_sw_pm)
+ if (!da9063->use_sw_pm)
return 0;
if (watchdog_active(wdd))
@@ -277,11 +277,12 @@ static int __maybe_unused da9063_wdt_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused da9063_wdt_resume(struct device *dev)
+static int da9063_wdt_resume(struct device *dev)
{
struct watchdog_device *wdd = dev_get_drvdata(dev);
+ struct da9063 *da9063 = watchdog_get_drvdata(wdd);
- if (!use_sw_pm)
+ if (!da9063->use_sw_pm)
return 0;
if (watchdog_active(wdd))
@@ -290,14 +291,14 @@ static int __maybe_unused da9063_wdt_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(da9063_wdt_pm_ops,
- da9063_wdt_suspend, da9063_wdt_resume);
+static DEFINE_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,
+ .pm = pm_sleep_ptr(&da9063_wdt_pm_ops),
},
};
module_platform_driver(da9063_wdt_driver);
diff --git a/drivers/watchdog/gxp-wdt.c b/drivers/watchdog/gxp-wdt.c
index 2fd85be88278..f2c236160266 100644
--- a/drivers/watchdog/gxp-wdt.c
+++ b/drivers/watchdog/gxp-wdt.c
@@ -151,10 +151,8 @@ static int gxp_wdt_probe(struct platform_device *pdev)
watchdog_stop_on_reboot(&drvdata->wdd);
err = devm_watchdog_register_device(dev, &drvdata->wdd);
- if (err) {
- dev_err(dev, "Failed to register watchdog device");
+ if (err)
return err;
- }
dev_info(dev, "HPE GXP watchdog timer");
diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c
index 35b358bcf94c..7672582fa407 100644
--- a/drivers/watchdog/iTCO_wdt.c
+++ b/drivers/watchdog/iTCO_wdt.c
@@ -82,6 +82,13 @@
#define TCO2_CNT(p) (TCOBASE(p) + 0x0a) /* TCO2 Control Register */
#define TCOv2_TMR(p) (TCOBASE(p) + 0x12) /* TCOv2 Timer Initial Value*/
+/*
+ * NMI_NOW is bit 8 of TCO1_CNT register
+ * Read/Write
+ * This bit is implemented as RW but has no effect on HW.
+ */
+#define NMI_NOW BIT(8)
+
/* internal variables */
struct iTCO_wdt_private {
struct watchdog_device wddev;
@@ -219,13 +226,23 @@ static int update_no_reboot_bit_cnt(void *priv, bool set)
struct iTCO_wdt_private *p = priv;
u16 val, newval;
- val = inw(TCO1_CNT(p));
+ /*
+ * writing back 1b1 to NMI_NOW of TCO1_CNT register
+ * causes NMI_NOW bit inversion what consequently does
+ * not allow to perform the register's value comparison
+ * properly.
+ *
+ * NMI_NOW bit masking for TCO1_CNT register values
+ * helps to avoid possible NMI_NOW bit inversions on
+ * following write operation.
+ */
+ val = inw(TCO1_CNT(p)) & ~NMI_NOW;
if (set)
val |= BIT(0);
else
val &= ~BIT(0);
outw(val, TCO1_CNT(p));
- newval = inw(TCO1_CNT(p));
+ newval = inw(TCO1_CNT(p)) & ~NMI_NOW;
/* make sure the update is successful */
return val != newval ? -EIO : 0;
@@ -592,10 +609,8 @@ static int iTCO_wdt_probe(struct platform_device *pdev)
watchdog_stop_on_reboot(&p->wddev);
watchdog_stop_on_unregister(&p->wddev);
ret = devm_watchdog_register_device(dev, &p->wddev);
- if (ret != 0) {
- dev_err(dev, "cannot register watchdog device (err=%d)\n", ret);
+ if (ret != 0)
return ret;
- }
dev_info(dev, "initialized. heartbeat=%d sec (nowayout=%d)\n",
heartbeat, nowayout);
diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c
index 3e8c15138edd..a1e23dce8810 100644
--- a/drivers/watchdog/it87_wdt.c
+++ b/drivers/watchdog/it87_wdt.c
@@ -20,6 +20,8 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/bits.h>
+#include <linux/dmi.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
@@ -40,6 +42,7 @@
#define VAL 0x2f
/* Logical device Numbers LDN */
+#define EC 0x04
#define GPIO 0x07
/* Configuration Registers and Functions */
@@ -73,6 +76,12 @@
#define IT8784_ID 0x8784
#define IT8786_ID 0x8786
+/* Environment Controller Configuration Registers LDN=0x04 */
+#define SCR1 0xfa
+
+/* Environment Controller Bits SCR1 */
+#define WDT_PWRGD 0x20
+
/* GPIO Configuration Registers LDN=0x07 */
#define WDTCTRL 0x71
#define WDTCFG 0x72
@@ -240,6 +249,21 @@ static int wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
return ret;
}
+enum {
+ IT87_WDT_OUTPUT_THROUGH_PWRGD = BIT(0),
+};
+
+static const struct dmi_system_id it87_quirks[] = {
+ {
+ /* Qotom Q30900P (IT8786) */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "QCML04"),
+ },
+ .driver_data = (void *)IT87_WDT_OUTPUT_THROUGH_PWRGD,
+ },
+ {}
+};
+
static const struct watchdog_info ident = {
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
.firmware_version = 1,
@@ -261,8 +285,10 @@ static struct watchdog_device wdt_dev = {
static int __init it87_wdt_init(void)
{
+ const struct dmi_system_id *dmi_id;
u8 chip_rev;
u8 ctrl;
+ int quirks = 0;
int rc;
rc = superio_enter();
@@ -273,6 +299,10 @@ static int __init it87_wdt_init(void)
chip_rev = superio_inb(CHIPREV) & 0x0f;
superio_exit();
+ dmi_id = dmi_first_match(it87_quirks);
+ if (dmi_id)
+ quirks = (long)dmi_id->driver_data;
+
switch (chip_type) {
case IT8702_ID:
max_units = 255;
@@ -333,6 +363,15 @@ static int __init it87_wdt_init(void)
superio_outb(0x00, WDTCTRL);
}
+ if (quirks & IT87_WDT_OUTPUT_THROUGH_PWRGD) {
+ superio_select(EC);
+ ctrl = superio_inb(SCR1);
+ if (!(ctrl & WDT_PWRGD)) {
+ ctrl |= WDT_PWRGD;
+ superio_outb(ctrl, SCR1);
+ }
+ }
+
superio_exit();
if (timeout < 1 || timeout > max_units * 60) {
@@ -349,10 +388,8 @@ static int __init it87_wdt_init(void)
watchdog_stop_on_reboot(&wdt_dev);
rc = watchdog_register_device(&wdt_dev);
- if (rc) {
- pr_err("Cannot register watchdog device (err=%d)\n", rc);
+ if (rc)
return rc;
- }
pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d)\n",
chip_type, chip_rev, timeout, nowayout, testmode);
diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c
index c35f85ce8d69..91d110646e16 100644
--- a/drivers/watchdog/mtk_wdt.c
+++ b/drivers/watchdog/mtk_wdt.c
@@ -10,6 +10,7 @@
*/
#include <dt-bindings/reset/mt2712-resets.h>
+#include <dt-bindings/reset/mediatek,mt6735-wdt.h>
#include <dt-bindings/reset/mediatek,mt6795-resets.h>
#include <dt-bindings/reset/mt7986-resets.h>
#include <dt-bindings/reset/mt8183-resets.h>
@@ -87,6 +88,10 @@ static const struct mtk_wdt_data mt2712_data = {
.toprgu_sw_rst_num = MT2712_TOPRGU_SW_RST_NUM,
};
+static const struct mtk_wdt_data mt6735_data = {
+ .toprgu_sw_rst_num = MT6735_TOPRGU_RST_NUM,
+};
+
static const struct mtk_wdt_data mt6795_data = {
.toprgu_sw_rst_num = MT6795_TOPRGU_SW_RST_NUM,
};
@@ -225,9 +230,15 @@ static int mtk_wdt_restart(struct watchdog_device *wdt_dev,
{
struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev);
void __iomem *wdt_base;
+ u32 reg;
wdt_base = mtk_wdt->wdt_base;
+ /* Enable reset in order to issue a system reset instead of an IRQ */
+ reg = readl(wdt_base + WDT_MODE);
+ reg &= ~WDT_MODE_IRQ_EN;
+ writel(reg | WDT_MODE_KEY, wdt_base + WDT_MODE);
+
while (1) {
writel(WDT_SWRST_KEY, wdt_base + WDT_SWRST);
mdelay(5);
@@ -483,6 +494,7 @@ 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,mt6735-wdt", .data = &mt6735_data },
{ .compatible = "mediatek,mt6795-wdt", .data = &mt6795_data },
{ .compatible = "mediatek,mt7986-wdt", .data = &mt7986_data },
{ .compatible = "mediatek,mt7988-wdt", .data = &mt7988_data },
diff --git a/drivers/watchdog/octeon-wdt-main.c b/drivers/watchdog/octeon-wdt-main.c
index 52d49e4e35a0..0615bb816082 100644
--- a/drivers/watchdog/octeon-wdt-main.c
+++ b/drivers/watchdog/octeon-wdt-main.c
@@ -559,10 +559,8 @@ static int __init octeon_wdt_init(void)
watchdog_set_nowayout(&octeon_wdt, nowayout);
ret = watchdog_register_device(&octeon_wdt);
- if (ret) {
- pr_err("watchdog_register_device() failed: %d\n", ret);
+ if (ret)
return ret;
- }
if (disable) {
pr_notice("disabled\n");
diff --git a/drivers/watchdog/pcwd.c b/drivers/watchdog/pcwd.c
index 1a4282235aac..31d3dcbf815e 100644
--- a/drivers/watchdog/pcwd.c
+++ b/drivers/watchdog/pcwd.c
@@ -833,7 +833,7 @@ static int pcwd_isa_match(struct device *dev, unsigned int id)
port0 = inb_p(base_addr);
port1 = inb_p(base_addr + 1);
- /* Has either hearbeat bit changed? */
+ /* Has either heartbeat bit changed? */
if ((port0 ^ last_port0) & WD_HRTBT ||
(port1 ^ last_port1) & WD_REVC_HRBT) {
retval = 1;
diff --git a/drivers/watchdog/rti_wdt.c b/drivers/watchdog/rti_wdt.c
index bfad78eb94a3..58c9445c0f88 100644
--- a/drivers/watchdog/rti_wdt.c
+++ b/drivers/watchdog/rti_wdt.c
@@ -61,7 +61,7 @@
#define MAX_HW_ERROR 250
-static int heartbeat = DEFAULT_HEARTBEAT;
+static int heartbeat;
/*
* struct to hold data for each WDT device
@@ -252,6 +252,7 @@ static int rti_wdt_probe(struct platform_device *pdev)
wdd->min_timeout = 1;
wdd->max_hw_heartbeat_ms = (WDT_PRELOAD_MAX << WDT_PRELOAD_SHIFT) /
wdt->freq * 1000;
+ wdd->timeout = DEFAULT_HEARTBEAT;
wdd->parent = dev;
watchdog_set_drvdata(wdd, wdt);
@@ -336,10 +337,8 @@ static int rti_wdt_probe(struct platform_device *pdev)
watchdog_init_timeout(wdd, heartbeat, dev);
ret = watchdog_register_device(wdd);
- if (ret) {
- dev_err(dev, "cannot register watchdog device\n");
+ if (ret)
goto err_iomap;
- }
if (last_ping)
watchdog_set_last_hw_keepalive(wdd, last_ping);
diff --git a/drivers/watchdog/rza_wdt.c b/drivers/watchdog/rza_wdt.c
index cb4901b3f777..9334255a37e9 100644
--- a/drivers/watchdog/rza_wdt.c
+++ b/drivers/watchdog/rza_wdt.c
@@ -169,7 +169,6 @@ static int rza_wdt_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct rza_wdt *priv;
unsigned long rate;
- int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -218,11 +217,7 @@ static int rza_wdt_probe(struct platform_device *pdev)
watchdog_init_timeout(&priv->wdev, 0, dev);
watchdog_set_drvdata(&priv->wdev, priv);
- ret = devm_watchdog_register_device(dev, &priv->wdev);
- if (ret)
- dev_err(dev, "Cannot register watchdog device\n");
-
- return ret;
+ return devm_watchdog_register_device(dev, &priv->wdev);
}
static const struct of_device_id rza_wdt_of_match[] = {
diff --git a/drivers/watchdog/rzg2l_wdt.c b/drivers/watchdog/rzg2l_wdt.c
index 2a35f890a288..11bbe48160ec 100644
--- a/drivers/watchdog/rzg2l_wdt.c
+++ b/drivers/watchdog/rzg2l_wdt.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/units.h>
@@ -166,8 +167,22 @@ static int rzg2l_wdt_restart(struct watchdog_device *wdev,
struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);
int ret;
- clk_prepare_enable(priv->pclk);
- clk_prepare_enable(priv->osc_clk);
+ /*
+ * In case of RZ/G3S the watchdog device may be part of an IRQ safe power
+ * domain that is currently powered off. In this case we need to power
+ * it on before accessing registers. Along with this the clocks will be
+ * enabled. We don't undo the pm_runtime_resume_and_get() as the device
+ * need to be on for the reboot to happen.
+ *
+ * For the rest of SoCs not registering a watchdog IRQ safe power
+ * domain it is safe to call pm_runtime_resume_and_get() as the
+ * irq_safe_dev_in_sleep_domain() call in genpd_runtime_resume()
+ * returns non zero value and the genpd_lock() is avoided, thus, there
+ * will be no invalid wait context reported by lockdep.
+ */
+ ret = pm_runtime_resume_and_get(wdev->parent);
+ if (ret)
+ return ret;
if (priv->devtype == WDT_RZG2L) {
ret = reset_control_deassert(priv->rstc);
@@ -275,6 +290,7 @@ static int rzg2l_wdt_probe(struct platform_device *pdev)
priv->devtype = (uintptr_t)of_device_get_match_data(dev);
+ pm_runtime_irq_safe(&pdev->dev);
pm_runtime_enable(&pdev->dev);
priv->wdev.info = &rzg2l_wdt_ident;
diff --git a/drivers/watchdog/rzn1_wdt.c b/drivers/watchdog/rzn1_wdt.c
index 7d3192d34afd..96fd04fbc2a2 100644
--- a/drivers/watchdog/rzn1_wdt.c
+++ b/drivers/watchdog/rzn1_wdt.c
@@ -52,7 +52,7 @@ static int rzn1_wdt_ping(struct watchdog_device *w)
{
struct rzn1_watchdog *wdt = watchdog_get_drvdata(w);
- /* Any value retrigggers the watchdog */
+ /* Any value retriggers the watchdog */
writel(0, wdt->base + RZN1_WDT_RETRIGGER);
return 0;
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index 686cf544d0ae..30450e99e5e9 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -24,9 +24,9 @@
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/of.h>
+#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/delay.h>
-#include <linux/soc/samsung/exynos-pmu.h>
#define S3C2410_WTCON 0x00
#define S3C2410_WTDAT 0x04
@@ -63,6 +63,10 @@
#define EXYNOS850_CLUSTER1_NONCPU_INT_EN 0x1644
#define EXYNOSAUTOV9_CLUSTER1_NONCPU_OUT 0x1520
#define EXYNOSAUTOV9_CLUSTER1_NONCPU_INT_EN 0x1544
+#define EXYNOSAUTOV920_CLUSTER0_NONCPU_OUT 0x1420
+#define EXYNOSAUTOV920_CLUSTER0_NONCPU_INT_EN 0x1444
+#define EXYNOSAUTOV920_CLUSTER1_NONCPU_OUT 0x1720
+#define EXYNOSAUTOV920_CLUSTER1_NONCPU_INT_EN 0x1744
#define EXYNOS850_CLUSTER0_WDTRESET_BIT 24
#define EXYNOS850_CLUSTER1_WDTRESET_BIT 23
@@ -303,6 +307,32 @@ static const struct s3c2410_wdt_variant drv_data_gs101_cl1 = {
QUIRK_HAS_DBGACK_BIT,
};
+static const struct s3c2410_wdt_variant drv_data_exynosautov920_cl0 = {
+ .mask_reset_reg = EXYNOSAUTOV920_CLUSTER0_NONCPU_INT_EN,
+ .mask_bit = 2,
+ .mask_reset_inv = true,
+ .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
+ .rst_stat_bit = EXYNOSAUTOV9_CLUSTER0_WDTRESET_BIT,
+ .cnt_en_reg = EXYNOSAUTOV920_CLUSTER0_NONCPU_OUT,
+ .cnt_en_bit = 7,
+ .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET |
+ QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN |
+ QUIRK_HAS_DBGACK_BIT,
+};
+
+static const struct s3c2410_wdt_variant drv_data_exynosautov920_cl1 = {
+ .mask_reset_reg = EXYNOSAUTOV920_CLUSTER1_NONCPU_INT_EN,
+ .mask_bit = 2,
+ .mask_reset_inv = true,
+ .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
+ .rst_stat_bit = EXYNOSAUTOV9_CLUSTER1_WDTRESET_BIT,
+ .cnt_en_reg = EXYNOSAUTOV920_CLUSTER1_NONCPU_OUT,
+ .cnt_en_bit = 7,
+ .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET |
+ QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN |
+ QUIRK_HAS_DBGACK_BIT,
+};
+
static const struct of_device_id s3c2410_wdt_match[] = {
{ .compatible = "google,gs101-wdt",
.data = &drv_data_gs101_cl0 },
@@ -320,6 +350,8 @@ static const struct of_device_id s3c2410_wdt_match[] = {
.data = &drv_data_exynos850_cl0 },
{ .compatible = "samsung,exynosautov9-wdt",
.data = &drv_data_exynosautov9_cl0 },
+ { .compatible = "samsung,exynosautov920-wdt",
+ .data = &drv_data_exynosautov920_cl0 },
{},
};
MODULE_DEVICE_TABLE(of, s3c2410_wdt_match);
@@ -643,7 +675,8 @@ s3c2410_get_wdt_drv_data(struct platform_device *pdev, struct s3c2410_wdt *wdt)
/* Choose Exynos850/ExynosAutov9 driver data w.r.t. cluster index */
if (variant == &drv_data_exynos850_cl0 ||
variant == &drv_data_exynosautov9_cl0 ||
- variant == &drv_data_gs101_cl0) {
+ variant == &drv_data_gs101_cl0 ||
+ variant == &drv_data_exynosautov920_cl0) {
u32 index;
int err;
@@ -662,6 +695,8 @@ s3c2410_get_wdt_drv_data(struct platform_device *pdev, struct s3c2410_wdt *wdt)
variant = &drv_data_exynosautov9_cl1;
else if (variant == &drv_data_gs101_cl0)
variant = &drv_data_gs101_cl1;
+ else if (variant == &drv_data_exynosautov920_cl0)
+ variant = &drv_data_exynosautov920_cl1;
break;
default:
return dev_err_probe(dev, -EINVAL, "wrong cluster index: %u\n", index);
@@ -699,11 +734,11 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
return ret;
if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) {
- wdt->pmureg = exynos_get_pmu_regmap_by_phandle(dev->of_node,
- "samsung,syscon-phandle");
+ wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "samsung,syscon-phandle");
if (IS_ERR(wdt->pmureg))
return dev_err_probe(dev, PTR_ERR(wdt->pmureg),
- "PMU regmap lookup failed.\n");
+ "syscon regmap lookup failed.\n");
}
wdt_irq = platform_get_irq(pdev, 0);
diff --git a/drivers/watchdog/sa1100_wdt.c b/drivers/watchdog/sa1100_wdt.c
index 1ee2e6b1de42..729a8508b31d 100644
--- a/drivers/watchdog/sa1100_wdt.c
+++ b/drivers/watchdog/sa1100_wdt.c
@@ -236,8 +236,8 @@ static void sa1100dog_remove(struct platform_device *pdev)
static struct platform_driver sa1100dog_driver = {
.driver.name = "sa1100_wdt",
- .probe = sa1100dog_probe,
- .remove = sa1100dog_remove,
+ .probe = sa1100dog_probe,
+ .remove = sa1100dog_remove,
};
module_platform_driver(sa1100dog_driver);
diff --git a/drivers/watchdog/sl28cpld_wdt.c b/drivers/watchdog/sl28cpld_wdt.c
index 9ce456f09f73..8630c29818f2 100644
--- a/drivers/watchdog/sl28cpld_wdt.c
+++ b/drivers/watchdog/sl28cpld_wdt.c
@@ -198,10 +198,8 @@ static int sl28cpld_wdt_probe(struct platform_device *pdev)
}
ret = devm_watchdog_register_device(&pdev->dev, wdd);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to register watchdog device\n");
+ if (ret < 0)
return ret;
- }
dev_info(&pdev->dev, "initial timeout %d sec%s\n",
wdd->timeout, nowayout ? ", nowayout" : "");
diff --git a/drivers/watchdog/smsc37b787_wdt.c b/drivers/watchdog/smsc37b787_wdt.c
index 97ca500ec8a8..3011e1af00f9 100644
--- a/drivers/watchdog/smsc37b787_wdt.c
+++ b/drivers/watchdog/smsc37b787_wdt.c
@@ -485,7 +485,7 @@ static long wb_smsc_wdt_ioctl(struct file *file,
}
}
-/* -- Notifier funtions -----------------------------------------*/
+/* -- Notifier functions -----------------------------------------*/
static int wb_smsc_wdt_notify_sys(struct notifier_block *this,
unsigned long code, void *unused)
diff --git a/drivers/watchdog/starfive-wdt.c b/drivers/watchdog/starfive-wdt.c
index 5e8ad576073d..355918d62f63 100644
--- a/drivers/watchdog/starfive-wdt.c
+++ b/drivers/watchdog/starfive-wdt.c
@@ -80,7 +80,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
struct starfive_wdt_variant {
- unsigned int control; /* Watchdog Control Resgister for reset enable */
+ unsigned int control; /* Watchdog Control Register for reset enable */
unsigned int load; /* Watchdog Load register */
unsigned int reload; /* Watchdog Reload Control register */
unsigned int enable; /* Watchdog Enable Register */
diff --git a/drivers/watchdog/stm32_iwdg.c b/drivers/watchdog/stm32_iwdg.c
index 5404e0387620..d700e0d49bb9 100644
--- a/drivers/watchdog/stm32_iwdg.c
+++ b/drivers/watchdog/stm32_iwdg.c
@@ -18,6 +18,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/pm_wakeirq.h>
#include <linux/watchdog.h>
#define DEFAULT_TIMEOUT 10
@@ -28,6 +29,7 @@
#define IWDG_RLR 0x08 /* ReLoad Register */
#define IWDG_SR 0x0C /* Status Register */
#define IWDG_WINR 0x10 /* Windows Register */
+#define IWDG_EWCR 0x14 /* Early Wake-up Register */
/* IWDG_KR register bit mask */
#define KR_KEY_RELOAD 0xAAAA /* reload counter enable */
@@ -47,22 +49,29 @@
#define SR_PVU BIT(0) /* Watchdog prescaler value update */
#define SR_RVU BIT(1) /* Watchdog counter reload value update */
+#define EWCR_EWIT GENMASK(11, 0) /* Watchdog counter window value */
+#define EWCR_EWIC BIT(14) /* Watchdog early interrupt acknowledge */
+#define EWCR_EWIE BIT(15) /* Watchdog early interrupt enable */
+
/* set timeout to 100000 us */
#define TIMEOUT_US 100000
#define SLEEP_US 1000
struct stm32_iwdg_data {
bool has_pclk;
+ bool has_early_wakeup;
u32 max_prescaler;
};
static const struct stm32_iwdg_data stm32_iwdg_data = {
.has_pclk = false,
+ .has_early_wakeup = false,
.max_prescaler = 256,
};
static const struct stm32_iwdg_data stm32mp1_iwdg_data = {
.has_pclk = true,
+ .has_early_wakeup = true,
.max_prescaler = 1024,
};
@@ -88,13 +97,18 @@ static inline void reg_write(void __iomem *base, u32 reg, u32 val)
static int stm32_iwdg_start(struct watchdog_device *wdd)
{
struct stm32_iwdg *wdt = watchdog_get_drvdata(wdd);
- u32 tout, presc, iwdg_rlr, iwdg_pr, iwdg_sr;
+ u32 tout, ptot, presc, iwdg_rlr, iwdg_ewcr, iwdg_pr, iwdg_sr;
int ret;
dev_dbg(wdd->parent, "%s\n", __func__);
+ if (!wdd->pretimeout)
+ wdd->pretimeout = 3 * wdd->timeout / 4;
+
tout = clamp_t(unsigned int, wdd->timeout,
wdd->min_timeout, wdd->max_hw_heartbeat_ms / 1000);
+ ptot = clamp_t(unsigned int, tout - wdd->pretimeout,
+ wdd->min_timeout, tout);
presc = DIV_ROUND_UP(tout * wdt->rate, RLR_MAX + 1);
@@ -102,6 +116,7 @@ static int stm32_iwdg_start(struct watchdog_device *wdd)
presc = roundup_pow_of_two(presc);
iwdg_pr = presc <= 1 << PR_SHIFT ? 0 : ilog2(presc) - PR_SHIFT;
iwdg_rlr = ((tout * wdt->rate) / presc) - 1;
+ iwdg_ewcr = ((ptot * wdt->rate) / presc) - 1;
/* enable write access */
reg_write(wdt->regs, IWDG_KR, KR_KEY_EWA);
@@ -109,6 +124,8 @@ static int stm32_iwdg_start(struct watchdog_device *wdd)
/* set prescaler & reload registers */
reg_write(wdt->regs, IWDG_PR, iwdg_pr);
reg_write(wdt->regs, IWDG_RLR, iwdg_rlr);
+ if (wdt->data->has_early_wakeup)
+ reg_write(wdt->regs, IWDG_EWCR, iwdg_ewcr | EWCR_EWIE);
reg_write(wdt->regs, IWDG_KR, KR_KEY_ENABLE);
/* wait for the registers to be updated (max 100ms) */
@@ -151,6 +168,34 @@ static int stm32_iwdg_set_timeout(struct watchdog_device *wdd,
return 0;
}
+static int stm32_iwdg_set_pretimeout(struct watchdog_device *wdd,
+ unsigned int pretimeout)
+{
+ dev_dbg(wdd->parent, "%s pretimeout: %d sec\n", __func__, pretimeout);
+
+ wdd->pretimeout = pretimeout;
+
+ if (watchdog_active(wdd))
+ return stm32_iwdg_start(wdd);
+
+ return 0;
+}
+
+static irqreturn_t stm32_iwdg_isr(int irq, void *wdog_arg)
+{
+ struct watchdog_device *wdd = wdog_arg;
+ struct stm32_iwdg *wdt = watchdog_get_drvdata(wdd);
+ u32 reg;
+
+ reg = reg_read(wdt->regs, IWDG_EWCR);
+ reg |= EWCR_EWIC;
+ reg_write(wdt->regs, IWDG_EWCR, reg);
+
+ watchdog_notify_pretimeout(wdd);
+
+ return IRQ_HANDLED;
+}
+
static void stm32_clk_disable_unprepare(void *data)
{
clk_disable_unprepare(data);
@@ -207,11 +252,20 @@ static const struct watchdog_info stm32_iwdg_info = {
.identity = "STM32 Independent Watchdog",
};
+static const struct watchdog_info stm32_iwdg_preinfo = {
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_PRETIMEOUT,
+ .identity = "STM32 Independent Watchdog",
+};
+
static const struct watchdog_ops stm32_iwdg_ops = {
.owner = THIS_MODULE,
.start = stm32_iwdg_start,
.ping = stm32_iwdg_ping,
.set_timeout = stm32_iwdg_set_timeout,
+ .set_pretimeout = stm32_iwdg_set_pretimeout,
};
static const struct of_device_id stm32_iwdg_of_match[] = {
@@ -221,6 +275,40 @@ static const struct of_device_id stm32_iwdg_of_match[] = {
};
MODULE_DEVICE_TABLE(of, stm32_iwdg_of_match);
+static int stm32_iwdg_irq_init(struct platform_device *pdev,
+ struct stm32_iwdg *wdt)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct watchdog_device *wdd = &wdt->wdd;
+ struct device *dev = &pdev->dev;
+ int irq, ret;
+
+ if (!wdt->data->has_early_wakeup)
+ return 0;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0)
+ return 0;
+
+ if (of_property_read_bool(np, "wakeup-source")) {
+ ret = device_init_wakeup(dev, true);
+ if (ret)
+ return ret;
+
+ ret = dev_pm_set_wake_irq(dev, irq);
+ if (ret)
+ return ret;
+ }
+
+ ret = devm_request_irq(dev, irq, stm32_iwdg_isr, 0,
+ dev_name(dev), wdd);
+ if (ret)
+ return ret;
+
+ wdd->info = &stm32_iwdg_preinfo;
+ return 0;
+}
+
static int stm32_iwdg_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -255,6 +343,11 @@ static int stm32_iwdg_probe(struct platform_device *pdev)
wdd->max_hw_heartbeat_ms = ((RLR_MAX + 1) * wdt->data->max_prescaler *
1000) / wdt->rate;
+ /* Initialize IRQ, this might override wdd->info, hence it is here. */
+ ret = stm32_iwdg_irq_init(pdev, wdt);
+ if (ret)
+ return ret;
+
watchdog_set_drvdata(wdd, wdt);
watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT);
watchdog_init_timeout(wdd, 0, dev);
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
index aff2c3912ead..d46d8c8c01f2 100644
--- a/drivers/watchdog/watchdog_core.c
+++ b/drivers/watchdog/watchdog_core.c
@@ -237,7 +237,7 @@ void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority)
}
EXPORT_SYMBOL_GPL(watchdog_set_restart_priority);
-static int __watchdog_register_device(struct watchdog_device *wdd)
+static int ___watchdog_register_device(struct watchdog_device *wdd)
{
int ret, id = -1;
@@ -337,6 +337,22 @@ static int __watchdog_register_device(struct watchdog_device *wdd)
return 0;
}
+static int __watchdog_register_device(struct watchdog_device *wdd)
+{
+ const char *dev_str;
+ int ret;
+
+ ret = ___watchdog_register_device(wdd);
+ if (ret) {
+ dev_str = wdd->parent ? dev_name(wdd->parent) :
+ (const char *)wdd->info->identity;
+ pr_err("%s: failed to register watchdog device (err = %d)\n",
+ dev_str, ret);
+ }
+
+ return ret;
+}
+
/**
* watchdog_register_device() - register a watchdog device
* @wdd: watchdog device
@@ -350,7 +366,6 @@ static int __watchdog_register_device(struct watchdog_device *wdd)
int watchdog_register_device(struct watchdog_device *wdd)
{
- const char *dev_str;
int ret = 0;
mutex_lock(&wtd_deferred_reg_mutex);
@@ -360,13 +375,6 @@ int watchdog_register_device(struct watchdog_device *wdd)
watchdog_deferred_registration_add(wdd);
mutex_unlock(&wtd_deferred_reg_mutex);
- if (ret) {
- dev_str = wdd->parent ? dev_name(wdd->parent) :
- (const char *)wdd->info->identity;
- pr_err("%s: failed to register watchdog device (err = %d)\n",
- dev_str, ret);
- }
-
return ret;
}
EXPORT_SYMBOL_GPL(watchdog_register_device);
diff --git a/drivers/watchdog/xilinx_wwdt.c b/drivers/watchdog/xilinx_wwdt.c
index d271e2e8d6e2..3d2a156f7180 100644
--- a/drivers/watchdog/xilinx_wwdt.c
+++ b/drivers/watchdog/xilinx_wwdt.c
@@ -2,7 +2,7 @@
/*
* Window watchdog device driver for Xilinx Versal WWDT
*
- * Copyright (C) 2022 - 2023, Advanced Micro Devices, Inc.
+ * Copyright (C) 2022 - 2024, Advanced Micro Devices, Inc.
*/
#include <linux/clk.h>
@@ -36,6 +36,12 @@
#define XWWDT_CLOSE_WINDOW_PERCENT 50
+/* Maximum count value of each 32 bit window */
+#define XWWDT_MAX_COUNT_WINDOW GENMASK(31, 0)
+
+/* Maximum count value of closed and open window combined */
+#define XWWDT_MAX_COUNT_WINDOW_COMBINED GENMASK_ULL(32, 1)
+
static int wwdt_timeout;
static int closed_window_percent;
@@ -54,6 +60,8 @@ MODULE_PARM_DESC(closed_window_percent,
* @xilinx_wwdt_wdd: watchdog device structure
* @freq: source clock frequency of WWDT
* @close_percent: Closed window percent
+ * @closed_timeout: Closed window timeout in ticks
+ * @open_timeout: Open window timeout in ticks
*/
struct xwwdt_device {
void __iomem *base;
@@ -61,27 +69,22 @@ struct xwwdt_device {
struct watchdog_device xilinx_wwdt_wdd;
unsigned long freq;
u32 close_percent;
+ u64 closed_timeout;
+ u64 open_timeout;
};
static int xilinx_wwdt_start(struct watchdog_device *wdd)
{
struct xwwdt_device *xdev = watchdog_get_drvdata(wdd);
struct watchdog_device *xilinx_wwdt_wdd = &xdev->xilinx_wwdt_wdd;
- u64 time_out, closed_timeout, open_timeout;
u32 control_status_reg;
- /* Calculate timeout count */
- time_out = xdev->freq * wdd->timeout;
- closed_timeout = div_u64(time_out * xdev->close_percent, 100);
- open_timeout = time_out - closed_timeout;
- wdd->min_hw_heartbeat_ms = xdev->close_percent * 10 * wdd->timeout;
-
spin_lock(&xdev->spinlock);
iowrite32(XWWDT_MWR_MASK, xdev->base + XWWDT_MWR_OFFSET);
iowrite32(~(u32)XWWDT_ESR_WEN_MASK, xdev->base + XWWDT_ESR_OFFSET);
- iowrite32((u32)closed_timeout, xdev->base + XWWDT_FWR_OFFSET);
- iowrite32((u32)open_timeout, xdev->base + XWWDT_SWR_OFFSET);
+ iowrite32((u32)xdev->closed_timeout, xdev->base + XWWDT_FWR_OFFSET);
+ iowrite32((u32)xdev->open_timeout, xdev->base + XWWDT_SWR_OFFSET);
/* Enable the window watchdog timer */
control_status_reg = ioread32(xdev->base + XWWDT_ESR_OFFSET);
@@ -133,7 +136,12 @@ static int xwwdt_probe(struct platform_device *pdev)
struct watchdog_device *xilinx_wwdt_wdd;
struct device *dev = &pdev->dev;
struct xwwdt_device *xdev;
+ u64 max_per_window_ms;
+ u64 min_per_window_ms;
+ u64 timeout_count;
struct clk *clk;
+ u32 timeout_ms;
+ u64 ms_count;
int ret;
xdev = devm_kzalloc(dev, sizeof(*xdev), GFP_KERNEL);
@@ -154,12 +162,13 @@ static int xwwdt_probe(struct platform_device *pdev)
return PTR_ERR(clk);
xdev->freq = clk_get_rate(clk);
- if (!xdev->freq)
+ if (xdev->freq < 1000000)
return -EINVAL;
xilinx_wwdt_wdd->min_timeout = XWWDT_MIN_TIMEOUT;
xilinx_wwdt_wdd->timeout = XWWDT_DEFAULT_TIMEOUT;
- xilinx_wwdt_wdd->max_hw_heartbeat_ms = 1000 * xilinx_wwdt_wdd->timeout;
+ xilinx_wwdt_wdd->max_hw_heartbeat_ms =
+ div64_u64(XWWDT_MAX_COUNT_WINDOW_COMBINED, xdev->freq) * 1000;
if (closed_window_percent == 0 || closed_window_percent >= 100)
xdev->close_percent = XWWDT_CLOSE_WINDOW_PERCENT;
@@ -167,6 +176,48 @@ static int xwwdt_probe(struct platform_device *pdev)
xdev->close_percent = closed_window_percent;
watchdog_init_timeout(xilinx_wwdt_wdd, wwdt_timeout, &pdev->dev);
+
+ /* Calculate ticks for 1 milli-second */
+ ms_count = div_u64(xdev->freq, 1000);
+ timeout_ms = xilinx_wwdt_wdd->timeout * 1000;
+ timeout_count = timeout_ms * ms_count;
+
+ if (timeout_ms > xilinx_wwdt_wdd->max_hw_heartbeat_ms) {
+ /*
+ * To avoid ping restrictions until the minimum hardware heartbeat,
+ * we will solely rely on the open window and
+ * adjust the minimum hardware heartbeat to 0.
+ */
+ xdev->closed_timeout = 0;
+ xdev->open_timeout = XWWDT_MAX_COUNT_WINDOW;
+ xilinx_wwdt_wdd->min_hw_heartbeat_ms = 0;
+ xilinx_wwdt_wdd->max_hw_heartbeat_ms = xilinx_wwdt_wdd->max_hw_heartbeat_ms / 2;
+ } else {
+ xdev->closed_timeout = div64_u64(timeout_count * xdev->close_percent, 100);
+ xilinx_wwdt_wdd->min_hw_heartbeat_ms =
+ div64_u64(timeout_ms * xdev->close_percent, 100);
+
+ if (timeout_ms > xilinx_wwdt_wdd->max_hw_heartbeat_ms / 2) {
+ max_per_window_ms = xilinx_wwdt_wdd->max_hw_heartbeat_ms / 2;
+ min_per_window_ms = timeout_ms - max_per_window_ms;
+
+ if (xilinx_wwdt_wdd->min_hw_heartbeat_ms > max_per_window_ms) {
+ dev_info(xilinx_wwdt_wdd->parent,
+ "Closed window cannot be set to %d%%. Using maximum supported value.\n",
+ xdev->close_percent);
+ xdev->closed_timeout = max_per_window_ms * ms_count;
+ xilinx_wwdt_wdd->min_hw_heartbeat_ms = max_per_window_ms;
+ } else if (xilinx_wwdt_wdd->min_hw_heartbeat_ms < min_per_window_ms) {
+ dev_info(xilinx_wwdt_wdd->parent,
+ "Closed window cannot be set to %d%%. Using minimum supported value.\n",
+ xdev->close_percent);
+ xdev->closed_timeout = min_per_window_ms * ms_count;
+ xilinx_wwdt_wdd->min_hw_heartbeat_ms = min_per_window_ms;
+ }
+ }
+ xdev->open_timeout = timeout_count - xdev->closed_timeout;
+ }
+
spin_lock_init(&xdev->spinlock);
watchdog_set_drvdata(xilinx_wwdt_wdd, xdev);
watchdog_set_nowayout(xilinx_wwdt_wdd, 1);
diff --git a/drivers/watchdog/ziirave_wdt.c b/drivers/watchdog/ziirave_wdt.c
index 775838346bb5..fcc1ba02e75b 100644
--- a/drivers/watchdog/ziirave_wdt.c
+++ b/drivers/watchdog/ziirave_wdt.c
@@ -715,7 +715,7 @@ static void ziirave_wdt_remove(struct i2c_client *client)
}
static const struct i2c_device_id ziirave_wdt_id[] = {
- { "rave-wdt", 0 },
+ { "rave-wdt" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ziirave_wdt_id);
diff --git a/include/linux/mfd/da9063/core.h b/include/linux/mfd/da9063/core.h
index 8db52324f416..eae82f421414 100644
--- a/include/linux/mfd/da9063/core.h
+++ b/include/linux/mfd/da9063/core.h
@@ -78,6 +78,7 @@ struct da9063 {
enum da9063_type type;
unsigned char variant_code;
unsigned int flags;
+ bool use_sw_pm;
/* Control interface */
struct regmap *regmap;