i.MX8QXP is an ARMv8 SoC which has a Cortex-M4 system controller
inside, the system controller is in charge of controlling power,
clock and watchdog etc..

This patch adds i.MX system controller watchdog driver support,
watchdog operation needs to be done in secure EL3 mode via
ARM-Trusted-Firmware, using SMC call, CPU will trap into
ARM-Trusted-Firmware and then it will request system controller
to do watchdog operation via IPC.

Signed-off-by: Stefano Manni <[email protected]>
---
 drivers/watchdog/Kconfig           |  12 +++
 drivers/watchdog/Makefile          |   1 +
 drivers/watchdog/imx_sc_watchdog.c | 113 +++++++++++++++++++++++++++++
 3 files changed, 126 insertions(+)
 create mode 100644 drivers/watchdog/imx_sc_watchdog.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 9ea617f1e43..af1d913251f 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -51,6 +51,18 @@ config IMX_WATCHDOG
          Select this to enable the IMX and LSCH2 of Layerscape watchdog
          driver.
 
+config IMX_SC_WATCHDOG
+       bool "IMX SC Watchdog"
+       depends on ARM_SMCCC && WDT
+       help
+         This is the driver for the system controller watchdog
+         on the NXP i.MX SoCs with system controller inside, the
+         watchdog driver will call ARM SMC API and trap into
+         ARM-Trusted-Firmware for operations, ARM-Trusted-Firmware
+         will request system controller to execute the operations.
+         If you have one of these processors and wish to have
+         watchdog support enabled, say Y, otherwise say N.
+
 config WATCHDOG_RESET_DISABLE
        bool "Disable reset watchdog"
        depends on IMX_WATCHDOG
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 84faefdb4c2..0fb3c6a6733 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -8,6 +8,7 @@ ifneq (,$(filter $(SOC), mx25 mx31 mx35 mx5 mx6 mx7 vf610))
 obj-y += imx_watchdog.o
 else
 obj-$(CONFIG_IMX_WATCHDOG) += imx_watchdog.o
+obj-$(CONFIG_IMX_SC_WATCHDOG) += imx_sc_watchdog.o
 endif
 obj-$(CONFIG_S5P)               += s5p_wdt.o
 obj-$(CONFIG_XILINX_TB_WATCHDOG) += xilinx_tb_wdt.o
diff --git a/drivers/watchdog/imx_sc_watchdog.c 
b/drivers/watchdog/imx_sc_watchdog.c
new file mode 100644
index 00000000000..ccd5f63e6ca
--- /dev/null
+++ b/drivers/watchdog/imx_sc_watchdog.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * imx_sc_watchdog.c - driver for i.mx SCU watchdog
+ *
+ * Author: Stefano Manni <[email protected]>
+ */
+
+#include <cpu_func.h>
+#include <dm.h>
+#include <hang.h>
+#include <imx_sip.h>
+#include <firmware/imx/sci/sci.h>
+#include <linux/arm-smccc.h>
+#include <wdt.h>
+
+#define SC_TIMER_ERR_BUSY       10
+
+static int imx_sc_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
+{
+       struct arm_smccc_res res;
+       unsigned long action, lock;
+
+       /* first LSB byte is used for action SC_TIMER_WDOG_ACTION_*
+        *  see include/firmware/imx/sci/svc/timer/api.h
+        */
+       action = (unsigned long)flags & 0x0F;
+
+       /* second LSB byte is used for lock */
+       lock = (unsigned long)flags & 0xF0 ? 1 : 0;
+
+       arm_smccc_smc(IMX_SIP_TIMER, IMX_SIP_TIMER_SET_TIMEOUT_WDOG,
+                     (unsigned long)timeout, 0, 0, 0, 0, 0, &res);
+
+       if (res.a0)
+               return -EACCES;
+
+       arm_smccc_smc(IMX_SIP_TIMER, IMX_SIP_TIMER_SET_WDOG_ACT,
+                     action,
+                       0, 0, 0, 0, 0, &res);
+
+       if (res.a0)
+               return -EACCES;
+
+       arm_smccc_smc(IMX_SIP_TIMER, IMX_SIP_TIMER_START_WDOG,
+                     lock,
+                       0, 0, 0, 0, 0, &res);
+
+       if (res.a0 && res.a0 != SC_TIMER_ERR_BUSY)
+               return -EACCES;
+
+       return 0;
+}
+
+static int imx_sc_wdt_stop(struct udevice *dev)
+{
+       struct arm_smccc_res res;
+
+       arm_smccc_smc(IMX_SIP_TIMER, IMX_SIP_TIMER_STOP_WDOG,
+                     0, 0, 0, 0, 0, 0, &res);
+
+       if (res.a0)
+               return -EACCES;
+
+       return 0;
+}
+
+static int imx_sc_wdt_reset(struct udevice *dev)
+{
+       struct arm_smccc_res res;
+
+       arm_smccc_smc(IMX_SIP_TIMER, IMX_SIP_TIMER_PING_WDOG,
+                     0, 0, 0, 0, 0, 0, &res);
+
+       if (res.a0)
+               return -EACCES;
+
+       return 0;
+}
+
+static int imx_sc_wdt_expire_now(struct udevice *dev, ulong flags)
+{
+       imx_sc_wdt_start(dev, 0, flags);
+       hang();
+
+       /* never get here */
+       return 0;
+}
+
+static int imx_sc_wdt_probe(struct udevice *dev)
+{
+       return 0;
+}
+
+static const struct wdt_ops imx_sc_wdt_ops = {
+       .start          = imx_sc_wdt_start,
+       .stop           = imx_sc_wdt_stop,
+       .reset          = imx_sc_wdt_reset,
+       .expire_now     = imx_sc_wdt_expire_now,
+};
+
+static const struct udevice_id imx_sc_wdt_ids[] = {
+       { .compatible = "fsl,imx-sc-wdt" },
+       {}
+};
+
+U_BOOT_DRIVER(imx_sc_wdt) = {
+       .name           = "imx_sc_wdt",
+       .id                     = UCLASS_WDT,
+       .of_match       = imx_sc_wdt_ids,
+       .probe          = imx_sc_wdt_probe,
+       .ops            = &imx_sc_wdt_ops,
+       .flags          = DM_FLAG_PRE_RELOC,
+};
-- 
2.49.0

Reply via email to