Re: [PATCH v8 2/2] watchdog: Add driver for Gunyah Watchdog
From: Guenter Roeck
Date: Wed Nov 19 2025 - 00:18:56 EST
On 11/18/25 02:40, Hrishabh Rajput via B4 Relay wrote:
From: Hrishabh Rajput <hrishabh.rajput@xxxxxxxxxxxxxxxx>
On Qualcomm SoCs running under the Gunyah hypervisor, access to watchdog
through MMIO is not available on all platforms. Depending on the
hypervisor configuration, the watchdog is either fully emulated or
exposed via ARM's SMC Calling Conventions (SMCCC) through the Vendor
Specific Hypervisor Service Calls space.
Add driver to support the SMC-based watchdog provided by the Gunyah
Hypervisor. Device registration is done in the QCOM SCM driver after
checks to restrict the watchdog initialization to Qualcomm devices
running under Gunyah.
Gunyah watchdog is not a hardware but an SMC-based vendor-specific
hypervisor interface provided by the Gunyah hypervisor. The design
involving QCOM SCM driver for registering the platform device has been
devised to avoid adding non-hardware nodes to devicetree.
Tested-by: Shivendra Pratap <shivendra.pratap@xxxxxxxxxxxxxxxx>
Tested-by: Neil Armstrong <neil.armstrong@xxxxxxxxxx>
Signed-off-by: Hrishabh Rajput <hrishabh.rajput@xxxxxxxxxxxxxxxx>
---
MAINTAINERS | 1 +
drivers/watchdog/Kconfig | 13 +++
drivers/watchdog/Makefile | 1 +
drivers/watchdog/gunyah_wdt.c | 261 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 276 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index e64b94e6b5a9..5d2212c3c917 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3216,6 +3216,7 @@ F: arch/arm64/boot/dts/qcom/
F: drivers/bus/qcom*
F: drivers/firmware/qcom/
F: drivers/soc/qcom/
+F: drivers/watchdog/gunyah_wdt.c
F: include/dt-bindings/arm/qcom,ids.h
F: include/dt-bindings/firmware/qcom,scm.h
F: include/dt-bindings/soc/qcom*
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 05008d937e40..bc6db9a1c116 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -2354,4 +2354,17 @@ config KEEMBAY_WATCHDOG
To compile this driver as a module, choose M here: the
module will be called keembay_wdt.
+config GUNYAH_WATCHDOG
+ tristate "Qualcomm Gunyah Watchdog"
+ depends on ARCH_QCOM || COMPILE_TEST
+ depends on HAVE_ARM_SMCCC
+ select WATCHDOG_CORE
+ help
+ Say Y here to include support for watchdog timer provided by the
+ Gunyah hypervisor. The driver uses ARM SMC Calling Convention (SMCCC)
+ to interact with Gunyah Watchdog.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gunyah_wdt.
+
endif # WATCHDOG
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index b680e4d3c1bc..1215efb7816d 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -102,6 +102,7 @@ obj-$(CONFIG_MSC313E_WATCHDOG) += msc313e_wdt.o
obj-$(CONFIG_APPLE_WATCHDOG) += apple_wdt.o
obj-$(CONFIG_SUNPLUS_WATCHDOG) += sunplus_wdt.o
obj-$(CONFIG_MARVELL_GTI_WDT) += marvell_gti_wdt.o
+obj-$(CONFIG_GUNYAH_WATCHDOG) += gunyah_wdt.o
# X86 (i386 + ia64 + x86_64) Architecture
obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
diff --git a/drivers/watchdog/gunyah_wdt.c b/drivers/watchdog/gunyah_wdt.c
new file mode 100644
index 000000000000..49dfef459e84
--- /dev/null
+++ b/drivers/watchdog/gunyah_wdt.c
@@ -0,0 +1,261 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+
+#define GUNYAH_WDT_SMCCC_CALL_VAL(func_id) \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32,\
+ ARM_SMCCC_OWNER_VENDOR_HYP, func_id)
+
+/* SMCCC function IDs for watchdog operations */
+#define GUNYAH_WDT_CONTROL GUNYAH_WDT_SMCCC_CALL_VAL(0x0005)
+#define GUNYAH_WDT_STATUS GUNYAH_WDT_SMCCC_CALL_VAL(0x0006)
+#define GUNYAH_WDT_PING GUNYAH_WDT_SMCCC_CALL_VAL(0x0007)
+#define GUNYAH_WDT_SET_TIME GUNYAH_WDT_SMCCC_CALL_VAL(0x0008)
+
+/*
+ * Control values for GUNYAH_WDT_CONTROL.
+ * Bit 0 is used to enable or disable the watchdog. If this bit is set,
+ * then the watchdog is enabled and vice versa.
+ * Bit 1 should always be set to 1 as this bit is reserved in Gunyah and
+ * it's expected to be 1.
+ */
+#define WDT_CTRL_ENABLE (BIT(1) | BIT(0))
+#define WDT_CTRL_DISABLE BIT(1)
+
+enum gunyah_error {
+ GUNYAH_ERROR_OK = 0,
+ GUNYAH_ERROR_UNIMPLEMENTED = -1,
+ GUNYAH_ERROR_ARG_INVAL = 1,
+};
+
+/**
+ * gunyah_error_remap() - Remap Gunyah hypervisor errors into a Linux error code
+ * @gunyah_error: Gunyah hypercall return value
+ */
+static inline int gunyah_error_remap(enum gunyah_error gunyah_error)
+{
+ switch (gunyah_error) {
+ case GUNYAH_ERROR_OK:
+ return 0;
+ case GUNYAH_ERROR_UNIMPLEMENTED:
+ return -EOPNOTSUPP;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int gunyah_wdt_call(unsigned long func_id, unsigned long arg1,
+ unsigned long arg2)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_1_1_smc(func_id, arg1, arg2, &res);
+ return gunyah_error_remap(res.a0);
+}
+
+static int gunyah_wdt_start(struct watchdog_device *wdd)
+{
+ unsigned int timeout_ms;
+ struct device *dev = wdd->parent;
+ int ret;
+
+ ret = gunyah_wdt_call(GUNYAH_WDT_CONTROL, WDT_CTRL_DISABLE, 0);
+ if (ret && watchdog_active(wdd)) {
+ dev_err(dev, "%s: Failed to stop gunyah wdt %d\n", __func__, ret);
I am not a friend of such logging noise (it will clog the kernel log if there
is a problem with the firmware), but then I figure that it isn't _my_ system
that gets flooded with noise, so
Reviewed-by: Guenter Roeck <linux@xxxxxxxxxxxx>
Guenter