Tegra194: introduce watchdog timer
This patch adds support for WDT0 that is connected to CPU0 to act as the
watchdog timer for Tegra194 platforms. The watchdog timer uses TMR0 as the
source and fires if the CPU is hung for more than 10 seconds.
Signed-off-by: Varun Wadekar <vwadekar@nvidia.com>
Change-Id: Ibe20c1130f86718b919c89436d7f5f49b74d9cc9
diff --git a/plat/nvidia/tegra194/watchdog.c b/plat/nvidia/tegra194/watchdog.c
new file mode 100644
index 0000000..3773a3b
--- /dev/null
+++ b/plat/nvidia/tegra194/watchdog.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2020, NVIDIA Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <debug.h>
+#include <mmio.h>
+#include <platform.h>
+
+/* Timer registers */
+#define TIMER_PTV U(0)
+ #define TIMER_EN_BIT BIT_32(31)
+ #define TIMER_PERIODIC_BIT BIT_32(30)
+#define TIMER_PCR U(0x4)
+ #define TIMER_PCR_INTR_BIT BIT_32(30)
+
+/* WDT registers */
+#define WDT_CFG U(0)
+ #define WDT_CFG_TMR_SRC U(0) /* for TMR0. */
+ #define WDT_CFG_PERIOD_BIT BIT_32(4)
+ #define WDT_CFG_INT_EN_BIT BIT_32(12)
+ #define WDT_CFG_SYS_RST_EN_BIT BIT_32(14)
+ #define WDT_CFG_PMC2CAR_RST_EN_BIT BIT_32(15)
+#define WDT_CMD U(8)
+ #define WDT_CMD_START_COUNTER_BIT BIT_32(0)
+ #define WDT_CMD_DISABLE_COUNTER_BIT BIT_32(1)
+#define WDT_UNLOCK U(0xC)
+ #define WDT_UNLOCK_PATTERN U(0xC45A)
+
+/* watchdog will fire after this timeout value is reached */
+#define WDT_TIMEOUT_SECONDS U(10)
+#define WDT_TIMEOUT_MULTIPLIER UL(125000)
+
+static inline void tegra194_wdt_write(uint32_t offset, uint32_t val)
+{
+ mmio_write_32(TEGRA194_WDT0_BASE + offset, val);
+}
+
+static inline uint32_t tegra194_wdt_read(uint32_t offset)
+{
+ return mmio_read_32(TEGRA194_WDT0_BASE + offset);
+}
+
+static inline void tegra194_tmr_write(uint32_t offset, uint32_t val)
+{
+ mmio_write_32(TEGRA194_TMR0_BASE + offset, val);
+}
+
+static inline uint32_t tegra194_tmr_read(uint32_t offset)
+{
+ return mmio_read_32(TEGRA194_TMR0_BASE + offset);
+}
+
+/*
+ * Start the watchdog timer
+ */
+void tftf_platform_watchdog_set(void)
+{
+ uint32_t val;
+
+ /* Clear pending interrupts first */
+ tegra194_tmr_write(TIMER_PCR, TIMER_PCR_INTR_BIT);
+
+ /*
+ * Normally, we would set the period to 1 second by writing 125000ul,
+ * but the watchdog system reset actually occurs on the 4th expiration
+ * of this counter, so we set the period to 1/4 of this amount.
+ */
+ val = (WDT_TIMEOUT_SECONDS * WDT_TIMEOUT_MULTIPLIER) / 4;
+ val |= (TIMER_EN_BIT | TIMER_PERIODIC_BIT);
+ tegra194_tmr_write(TIMER_PTV, val);
+
+ /*
+ * Set number of periods and start counter.
+ */
+ val = WDT_CFG_TMR_SRC | WDT_CFG_SYS_RST_EN_BIT |
+ WDT_CFG_PMC2CAR_RST_EN_BIT;
+ tegra194_wdt_write(WDT_CFG, val);
+ tegra194_wdt_write(WDT_CMD, WDT_CMD_START_COUNTER_BIT);
+}
+
+/*
+ * Stop the watchdog timer
+ */
+void tftf_platform_watchdog_reset(void)
+{
+ tegra194_tmr_write(TIMER_PCR, TIMER_PCR_INTR_BIT);
+ tegra194_wdt_write(WDT_UNLOCK, WDT_UNLOCK_PATTERN);
+ tegra194_wdt_write(WDT_CMD, WDT_CMD_DISABLE_COUNTER_BIT);
+ tegra194_tmr_write(TIMER_PTV, 0);
+}