mt8173: Add support for new watchdog SMC

This patch adds support for a new SMC that can be used to control the
watchdog. This allows for a cleaner separation of responsibilities where
all watchdog operations have to go through Trusted Firmware and we could
no longer have kernel and firmware poking concurrently at the same
register block.

Signed-off-by: Julius Werner <jwerner@chromium.org>
Signed-off-by: Evan Benn <evanbenn@chromium.org>
Change-Id: I4844a3559d5c956a53a74a61dd5bc2956f0cce7b
diff --git a/plat/mediatek/mt8173/drivers/wdt/wdt.c b/plat/mediatek/mt8173/drivers/wdt/wdt.c
new file mode 100644
index 0000000..40f57ee
--- /dev/null
+++ b/plat/mediatek/mt8173/drivers/wdt/wdt.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2020, Google LLC. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <common/debug.h>
+#include <lib/mmio.h>
+#include <mt8173_def.h>
+#include <plat_sip_calls.h>
+#include <lib/psci/psci.h>
+#include <smccc_helpers.h>
+#include <wdt.h>
+
+#define WDT_BASE		(RGU_BASE + 0)
+#define WDT_MODE		(WDT_BASE + 0x00)
+#define WDT_LENGTH		(WDT_BASE + 0x04)
+#define WDT_RESTART		(WDT_BASE + 0x08)
+#define WDT_SWRST		(WDT_BASE + 0x14)
+
+#define WDT_MODE_DUAL_MODE	0x40
+#define WDT_MODE_IRQ		0x8
+#define WDT_MODE_KEY		0x22000000
+#define WDT_MODE_EXTEN		0x4
+#define WDT_MODE_EN		0x1
+#define WDT_LENGTH_KEY		0x8
+#define WDT_RESTART_KEY		0x1971
+#define WDT_SWRST_KEY		0x1209
+
+
+#define WDT_MIN_TIMEOUT 1
+#define WDT_MAX_TIMEOUT 31
+
+enum smcwd_call {
+	SMCWD_INFO		= 0,
+	SMCWD_SET_TIMEOUT	= 1,
+	SMCWD_ENABLE		= 2,
+	SMCWD_PET		= 3,
+};
+
+static int wdt_enabled_before_suspend;
+
+/*
+ * We expect the WDT registers to be correctly initialized by BL2 firmware
+ * (which may be board specific), so we do not reinitialize them here.
+ */
+
+void wdt_trigger_reset(void)
+{
+	mmio_write_32(WDT_SWRST, WDT_SWRST_KEY);
+}
+
+void wdt_pet(void)
+{
+	mmio_write_32(WDT_RESTART, WDT_RESTART_KEY);
+}
+
+int wdt_set_timeout(uint32_t timeout)
+{
+	/* One tick here equals 512 32KHz ticks. 512 / 32000 * 125 / 2 = 1 */
+	uint32_t ticks = timeout * 125 / 2;
+
+	if (timeout < WDT_MIN_TIMEOUT || timeout > WDT_MAX_TIMEOUT)
+		return PSCI_E_INVALID_PARAMS;
+
+	mmio_write_32(WDT_LENGTH, ticks << 5 | WDT_LENGTH_KEY);
+
+	return PSCI_E_SUCCESS;
+}
+
+void wdt_set_enable(int enable)
+{
+	if (enable)
+		wdt_pet();
+	mmio_clrsetbits_32(WDT_MODE, WDT_MODE_EN,
+			   WDT_MODE_KEY | (enable ? WDT_MODE_EN : 0));
+}
+
+void wdt_suspend(void)
+{
+	wdt_enabled_before_suspend = mmio_read_32(WDT_MODE) & WDT_MODE_EN;
+	if (wdt_enabled_before_suspend)
+		wdt_set_enable(0);
+}
+
+void wdt_resume(void)
+{
+	if (wdt_enabled_before_suspend)
+		wdt_set_enable(1);
+}
+
+uint64_t wdt_smc_handler(uint32_t x1,
+			uint32_t x2,
+			void *handle)
+{
+	int ret;
+
+	switch (x1) {
+	case SMCWD_INFO:
+		SMC_RET3(handle, PSCI_E_SUCCESS,
+			 WDT_MIN_TIMEOUT, WDT_MAX_TIMEOUT);
+	case SMCWD_SET_TIMEOUT:
+		ret = wdt_set_timeout(x2);
+		SMC_RET1(handle, ret);
+	case SMCWD_ENABLE:
+		wdt_set_enable(x2 > 0);
+		SMC_RET1(handle, PSCI_E_SUCCESS);
+	case SMCWD_PET:
+		wdt_pet();
+		SMC_RET1(handle, PSCI_E_SUCCESS);
+	default:
+		ERROR("Unimplemented SMCWD call (%d)\n", x1);
+		SMC_RET1(handle, PSCI_E_NOT_SUPPORTED);
+	}
+}
diff --git a/plat/mediatek/mt8173/drivers/wdt/wdt.h b/plat/mediatek/mt8173/drivers/wdt/wdt.h
new file mode 100644
index 0000000..7262a57
--- /dev/null
+++ b/plat/mediatek/mt8173/drivers/wdt/wdt.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2020, Google LLC. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef WDT_H
+#define WDT_H
+
+#include "stdint.h"
+
+void wdt_pet(void);
+void wdt_resume(void);
+void wdt_set_enable(int enable);
+int wdt_set_timeout(uint32_t timeout);
+uint64_t wdt_smc_handler(uint32_t x1, uint32_t x2, void *handle);
+void wdt_suspend(void);
+void wdt_trigger_reset(void);
+
+#endif /* WDT_H */
diff --git a/plat/mediatek/mt8173/include/mt8173_def.h b/plat/mediatek/mt8173/include/mt8173_def.h
index 58962f0..378b4da 100644
--- a/plat/mediatek/mt8173/include/mt8173_def.h
+++ b/plat/mediatek/mt8173/include/mt8173_def.h
@@ -80,18 +80,6 @@
 #define PLAT_MT_CCI_CLUSTER0_SL_IFACE_IX	4
 #define PLAT_MT_CCI_CLUSTER1_SL_IFACE_IX	3
 
-/*******************************************************************************
- * WDT related constants
- ******************************************************************************/
-#define MTK_WDT_BASE		(RGU_BASE + 0)
-#define MTK_WDT_SWRST		(MTK_WDT_BASE + 0x0014)
-
-#define MTK_WDT_MODE_DUAL_MODE	0x0040
-#define MTK_WDT_MODE_IRQ	0x0008
-#define MTK_WDT_MODE_KEY	0x22000000
-#define MTK_WDT_MODE_EXTEN	0x0004
-#define MTK_WDT_SWRST_KEY	0x1209
-
 /* FIQ platform related define */
 #define MT_IRQ_SEC_SGI_0	8
 #define MT_IRQ_SEC_SGI_1	9
diff --git a/plat/mediatek/mt8173/include/plat_sip_calls.h b/plat/mediatek/mt8173/include/plat_sip_calls.h
index 88202cc..ce9951a 100644
--- a/plat/mediatek/mt8173/include/plat_sip_calls.h
+++ b/plat/mediatek/mt8173/include/plat_sip_calls.h
@@ -10,7 +10,7 @@
 /*******************************************************************************
  * Plat SiP function constants
  ******************************************************************************/
-#define MTK_PLAT_SIP_NUM_CALLS	6
+#define MTK_PLAT_SIP_NUM_CALLS	7
 
 #define MTK_SIP_PWR_ON_MTCMOS			0x82000402
 #define MTK_SIP_PWR_OFF_MTCMOS			0x82000403
@@ -18,5 +18,6 @@
 #define MTK_SIP_SET_HDCP_KEY_NUM		0x82000405
 #define MTK_SIP_CLR_HDCP_KEY			0x82000406
 #define MTK_SIP_SET_HDCP_KEY_EX			0x82000407
+#define MTK_SIP_SMC_WATCHDOG			0x82003D06
 
 #endif /* PLAT_SIP_CALLS_H */
diff --git a/plat/mediatek/mt8173/plat_pm.c b/plat/mediatek/mt8173/plat_pm.c
index 67f1c73..e72a343 100644
--- a/plat/mediatek/mt8173/plat_pm.c
+++ b/plat/mediatek/mt8173/plat_pm.c
@@ -27,6 +27,7 @@
 #include <spm_hotplug.h>
 #include <spm_mcdi.h>
 #include <spm_suspend.h>
+#include <wdt.h>
 
 #define MTK_PWR_LVL0	0
 #define MTK_PWR_LVL1	1
@@ -350,6 +351,7 @@
 	}
 
 	if (MTK_SYSTEM_PWR_STATE(state) == MTK_LOCAL_STATE_OFF) {
+		wdt_suspend();
 		disable_scu(mpidr);
 		generic_timer_backup();
 		spm_system_suspend();
@@ -409,6 +411,7 @@
 		plat_arm_gic_init();
 		spm_system_suspend_finish();
 		enable_scu(mpidr);
+		wdt_resume();
 	}
 
 	/* Perform the common cluster specific operations */
@@ -455,11 +458,7 @@
 	/* Write the System Configuration Control Register */
 	INFO("MTK System Reset\n");
 
-	mmio_clrsetbits_32(MTK_WDT_BASE,
-		(MTK_WDT_MODE_DUAL_MODE | MTK_WDT_MODE_IRQ),
-		MTK_WDT_MODE_KEY);
-	mmio_setbits_32(MTK_WDT_BASE, (MTK_WDT_MODE_KEY | MTK_WDT_MODE_EXTEN));
-	mmio_setbits_32(MTK_WDT_SWRST, MTK_WDT_SWRST_KEY);
+	wdt_trigger_reset();
 
 	wfi();
 	ERROR("MTK System Reset: operation not handled.\n");
diff --git a/plat/mediatek/mt8173/plat_sip_calls.c b/plat/mediatek/mt8173/plat_sip_calls.c
index 102feb2..da9b91d 100644
--- a/plat/mediatek/mt8173/plat_sip_calls.c
+++ b/plat/mediatek/mt8173/plat_sip_calls.c
@@ -12,6 +12,7 @@
 #include <mtcmos.h>
 #include <mtk_sip_svc.h>
 #include <plat_sip_calls.h>
+#include <wdt.h>
 
 /* Authorized secure register list */
 enum {
@@ -102,6 +103,9 @@
 		ret = crypt_clear_hdcp_key();
 		SMC_RET1(handle, ret);
 
+	case MTK_SIP_SMC_WATCHDOG:
+		return wdt_smc_handler(x1, x2, handle);
+
 	default:
 		ERROR("%s: unhandled SMC (0x%x)\n", __func__, smc_fid);
 		break;
diff --git a/plat/mediatek/mt8173/platform.mk b/plat/mediatek/mt8173/platform.mk
index a66c49b..f62802c 100644
--- a/plat/mediatek/mt8173/platform.mk
+++ b/plat/mediatek/mt8173/platform.mk
@@ -15,6 +15,7 @@
 				-I${MTK_PLAT_SOC}/drivers/rtc/			\
 				-I${MTK_PLAT_SOC}/drivers/spm/			\
 				-I${MTK_PLAT_SOC}/drivers/timer/		\
+				-I${MTK_PLAT_SOC}/drivers/wdt/			\
 				-I${MTK_PLAT_SOC}/include/
 
 PLAT_BL_COMMON_SOURCES	:=	lib/xlat_tables/xlat_tables_common.c		\
@@ -50,6 +51,7 @@
 				${MTK_PLAT_SOC}/drivers/spm/spm_mcdi.c		\
 				${MTK_PLAT_SOC}/drivers/spm/spm_suspend.c	\
 				${MTK_PLAT_SOC}/drivers/timer/mt_cpuxgpt.c	\
+				${MTK_PLAT_SOC}/drivers/wdt/wdt.c		\
 				${MTK_PLAT_SOC}/plat_pm.c			\
 				${MTK_PLAT_SOC}/plat_sip_calls.c		\
 				${MTK_PLAT_SOC}/plat_topology.c			\