From: Alexey Charkov <alchark@gmail.com>
To: Alexey Charkov <alchark@gmail.com>,
Krzysztof Kozlowski <krzk@kernel.org>,
Daniel Lezcano <daniel.lezcano@kernel.org>,
Thomas Gleixner <tglx@kernel.org>,
Wim Van Sebroeck <wim@linux-watchdog.org>,
Guenter Roeck <linux@roeck-us.net>
Cc: linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-watchdog@vger.kernel.org
Subject: [PATCH v6 3/4] watchdog: Add support for VIA/WonderMedia SoC watchdog functionality
Date: Thu, 12 Nov 2026 22:55:10 +0400 [thread overview]
Message-ID: <20261112-vt8500-timer-updates-v6-3-fd1cea645d1f@gmail.com> (raw)
In-Reply-To: <20261112-vt8500-timer-updates-v6-0-fd1cea645d1f@gmail.com>
VIA/WonderMedia SoCs can use their system timer's first channel as a
watchdog device which will reset the system if the clocksource counter
matches the value given in its match register 0 and if the watchdog
function is enabled.
Since the watchdog function is tightly coupled to the timer itself, it
is implemented as an auxiliary device of the timer device. Build it in
rather than as a module, as the timer it depends on is always built in
and the driver is tiny.
Report a watchdog found running at probe, as one armed before a kexec
would be, so that the core keeps it fed until userspace takes over.
Signed-off-by: Alexey Charkov <alchark@gmail.com>
---
Documentation/watchdog/watchdog-parameters.rst | 7 ++
MAINTAINERS | 1 +
drivers/watchdog/Kconfig | 12 +++
drivers/watchdog/Makefile | 1 +
drivers/watchdog/vt8500-wdt.c | 132 +++++++++++++++++++++++++
5 files changed, 153 insertions(+)
diff --git a/Documentation/watchdog/watchdog-parameters.rst b/Documentation/watchdog/watchdog-parameters.rst
index 348229ffcb43..27453b659545 100644
--- a/Documentation/watchdog/watchdog-parameters.rst
+++ b/Documentation/watchdog/watchdog-parameters.rst
@@ -637,6 +637,13 @@ uniphier_wdt:
-------------------------------------------------
+vt8500_wdt:
+ nowayout:
+ Watchdog cannot be stopped once started
+ (default=kernel config parameter)
+
+-------------------------------------------------
+
w83627hf_wdt:
wdt_io:
w83627hf/thf WDT io port (default 0x2E)
diff --git a/MAINTAINERS b/MAINTAINERS
index acc4d2915460..80aefe2f5481 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4010,6 +4010,7 @@ F: drivers/tty/serial/vt8500_serial.c
F: drivers/video/fbdev/vt8500lcdfb.*
F: drivers/video/fbdev/wm8505fb*
F: drivers/video/fbdev/wmt_ge_rops.*
+F: drivers/watchdog/vt8500-wdt.c
F: include/clocksource/vt8500-timer.h
ARM/ZTE ZX29 SOC SUPPORT
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index c884f86be4e3..a252f9c9c2c8 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1183,6 +1183,18 @@ config SUNPLUS_WATCHDOG
To compile this driver as a module, choose M here: the
module will be called sunplus_wdt.
+config VT8500_WATCHDOG
+ bool "VIA/WonderMedia VT8500 watchdog support"
+ depends on ARCH_VT8500 || COMPILE_TEST
+ select WATCHDOG_CORE
+ select AUXILIARY_BUS
+ help
+ VIA/WonderMedia SoCs can use their system timer as a hardware
+ watchdog, as long as the first timer channel is free from other
+ uses and respective function is enabled in its registers. To
+ make use of it, say Y here and ensure that the device tree
+ lists at least two interrupts for the VT8500 timer device.
+
# X86 (i386 + ia64 + x86_64) Architecture
config ACQUIRE_WDT
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 19cb25f51624..5a5a111b4815 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -106,6 +106,7 @@ obj-$(CONFIG_SUNPLUS_WATCHDOG) += sunplus_wdt.o
obj-$(CONFIG_MARVELL_GTI_WDT) += marvell_gti_wdt.o
obj-$(CONFIG_GUNYAH_WATCHDOG) += gunyah_wdt.o
obj-$(CONFIG_MA35D1_WDT) += ma35d1_wdt.o
+obj-$(CONFIG_VT8500_WATCHDOG) += vt8500-wdt.o
# X86 (i386 + ia64 + x86_64) Architecture
obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
diff --git a/drivers/watchdog/vt8500-wdt.c b/drivers/watchdog/vt8500-wdt.c
new file mode 100644
index 000000000000..e0f838fd1d50
--- /dev/null
+++ b/drivers/watchdog/vt8500-wdt.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: 2026 Alexey Charkov <alchark@gmail.com>
+
+#include <linux/auxiliary_bus.h>
+#include <linux/bits.h>
+#include <linux/container_of.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/limits.h>
+#include <linux/minmax.h>
+#include <linux/moduleparam.h>
+#include <linux/stringify.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+
+#include <clocksource/vt8500-timer.h>
+
+/* Offsets within the timer register block */
+#define VT8500_WDT_MATCH_REG 0x0000 /* timer match register 0 */
+#define VT8500_WDT_EN_REG 0x0018
+#define VT8500_WDT_EN BIT(0)
+#define VT8500_WDT_ACC_STS_REG 0x0024 /* timer access status */
+#define VT8500_WDT_ACC_WR_MATCH BIT(0) /* match register 0 write pending */
+
+#define VT8500_WDT_DEFAULT_TIMEOUT 30 /* seconds */
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __stringify(WATCHDOG_NOWAYOUT) ")");
+
+/* Arm the watchdog to reset the SoC @cycles counter ticks from now */
+static int vt8500_watchdog_arm(struct vt8500_wdt_info *info, u64 cycles)
+{
+ u32 deadline, sts;
+ int ret;
+
+ /* Let a previous match write settle, as the timer driver does */
+ ret = readl_poll_timeout_atomic(info->regbase + VT8500_WDT_ACC_STS_REG,
+ sts, !(sts & VT8500_WDT_ACC_WR_MATCH),
+ 1, 10000);
+ if (ret)
+ return ret;
+
+ /*
+ * The deadline is matched against the hardware clocksource counter,
+ * which is a u32 value incrementing at VT8500_TIMER_HZ and continuing
+ * past wraparound. When the return value of timer_next is greater than
+ * U32_MAX then the match should occur after the hardware counter wraps
+ * around, thus we take only the lower 32 bits of timer_next return val
+ */
+ deadline = info->timer_next(cycles);
+ writel(deadline, info->regbase + VT8500_WDT_MATCH_REG);
+ writel(VT8500_WDT_EN, info->regbase + VT8500_WDT_EN_REG);
+
+ return 0;
+}
+
+static int vt8500_watchdog_start(struct watchdog_device *wdd)
+{
+ struct vt8500_wdt_info *info = watchdog_get_drvdata(wdd);
+ u32 timeout_ms = min(wdd->timeout * 1000, wdd->max_hw_heartbeat_ms);
+
+ return vt8500_watchdog_arm(info,
+ (u64)timeout_ms * (VT8500_TIMER_HZ / 1000));
+}
+
+static int vt8500_watchdog_stop(struct watchdog_device *wdd)
+{
+ struct vt8500_wdt_info *info = watchdog_get_drvdata(wdd);
+
+ writel(0, info->regbase + VT8500_WDT_EN_REG);
+
+ return 0;
+}
+
+static const struct watchdog_ops vt8500_watchdog_ops = {
+ .start = vt8500_watchdog_start,
+ .stop = vt8500_watchdog_stop,
+};
+
+static const struct watchdog_info vt8500_watchdog_info = {
+ .identity = "VIA VT8500 watchdog",
+ .options = WDIOF_MAGICCLOSE |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_SETTIMEOUT,
+};
+
+static int vt8500_wdt_probe(struct auxiliary_device *auxdev,
+ const struct auxiliary_device_id *id)
+{
+ struct vt8500_wdt_info *info;
+ struct watchdog_device *wdd;
+
+ wdd = devm_kzalloc_obj(&auxdev->dev, *wdd);
+ if (!wdd)
+ return -ENOMEM;
+
+ wdd->info = &vt8500_watchdog_info;
+ wdd->ops = &vt8500_watchdog_ops;
+ wdd->min_timeout = 1;
+ wdd->timeout = VT8500_WDT_DEFAULT_TIMEOUT;
+ wdd->max_hw_heartbeat_ms = U32_MAX / (VT8500_TIMER_HZ / 1000);
+ wdd->parent = &auxdev->dev;
+
+ info = container_of(auxdev, struct vt8500_wdt_info, auxdev);
+ watchdog_set_drvdata(wdd, info);
+ watchdog_set_nowayout(wdd, nowayout);
+
+ /* Let the core feed a watchdog left running, e.g. across kexec */
+ if (readl(info->regbase + VT8500_WDT_EN_REG) & VT8500_WDT_EN)
+ set_bit(WDOG_HW_RUNNING, &wdd->status);
+
+ return devm_watchdog_register_device(&auxdev->dev, wdd);
+}
+
+static const struct auxiliary_device_id vt8500_wdt_ids[] = {
+ { .name = "timer_vt8500.vt8500-wdt" },
+ {}
+};
+
+static struct auxiliary_driver vt8500_wdt_driver = {
+ .name = "vt8500-wdt",
+ .probe = vt8500_wdt_probe,
+ .id_table = vt8500_wdt_ids,
+ .driver = {
+ /* Unbinding would leave the hardware armed with no one to ping */
+ .suppress_bind_attrs = true,
+ },
+};
+builtin_driver(vt8500_wdt_driver, auxiliary_driver_register);
--
2.56.0
next prev parent reply other threads:[~2026-11-12 18:55 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-12 18:55 [PATCH v6 0/4] clocksource/drivers/timer-vt8500: clean up and add watchdog function Alexey Charkov
2026-11-12 18:55 ` [PATCH v6 1/4] clocksource/drivers/timer-vt8500: Add defines for magic constants Alexey Charkov
2026-11-12 18:55 ` [PATCH v6 2/4] clocksource/drivers/timer-vt8500: Prepare for watchdog functionality Alexey Charkov
2026-11-12 18:55 ` Alexey Charkov [this message]
2026-11-12 18:55 ` [PATCH v6 4/4] watchdog: vt8500: Add a restart handler Alexey Charkov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261112-vt8500-timer-updates-v6-3-fd1cea645d1f@gmail.com \
--to=alchark@gmail.com \
--cc=daniel.lezcano@kernel.org \
--cc=krzk@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-watchdog@vger.kernel.org \
--cc=linux@roeck-us.net \
--cc=tglx@kernel.org \
--cc=wim@linux-watchdog.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®