arm: socfpga: arria10: Reset MPFE NoC after program periph / combined RBF

This patch triggers warm reset to recover the MPFE NoC from corruption
due to high frequency transient clock output from HPS EMIF IOPLL at
VCO startup after peripheral RBF is programmed.

Signed-off-by: Tien Fong Chee <tien.fong.chee@intel.com>
Signed-off-by: Sin Hui Kho <sin.hui.kho@intel.com>
Reviewed-by: Tien Fong Chee <tien.fong.chee@intel.com>
diff --git a/arch/arm/mach-socfpga/spl_a10.c b/arch/arm/mach-socfpga/spl_a10.c
index f6c4b57..6450f75 100644
--- a/arch/arm/mach-socfpga/spl_a10.c
+++ b/arch/arm/mach-socfpga/spl_a10.c
@@ -30,10 +30,14 @@
 #include <asm/arch/fpga_manager.h>
 #include <mmc.h>
 #include <memalign.h>
+#include <linux/delay.h>
 
 #define FPGA_BUFSIZ	16 * 1024
 #define FSBL_IMAGE_IS_VALID	0x49535756
 
+#define FSBL_IMAGE_IS_INVALID	0x0
+#define BOOTROM_CONFIGURES_IO_PINMUX	0x3
+
 DECLARE_GLOBAL_DATA_PTR;
 
 #define BOOTROM_SHARED_MEM_SIZE		0x800	/* 2KB */
@@ -107,6 +111,8 @@
 
 void spl_board_init(void)
 {
+	int ret;
+
 	ALLOC_CACHE_ALIGN_BUFFER(char, buf, FPGA_BUFSIZ);
 
 	/* enable console uart printing */
@@ -117,8 +123,7 @@
 
 	/* If the full FPGA is already loaded, ie.from EPCQ, config fpga pins */
 	if (is_fpgamgr_user_mode()) {
-		int ret = config_pins(gd->fdt_blob, "shared");
-
+		ret = config_pins(gd->fdt_blob, "shared");
 		if (ret)
 			return;
 
@@ -131,8 +136,93 @@
 	}
 
 	/* If the IOSSM/full FPGA is already loaded, start DDR */
-	if (is_fpgamgr_early_user_mode() || is_fpgamgr_user_mode())
+	if (is_fpgamgr_early_user_mode() || is_fpgamgr_user_mode()) {
+		if (!is_regular_boot_valid()) {
+			/*
+			 * Ensure all signals in stable state before triggering
+			 * warm reset. This value is recommended from stress
+			 * test.
+			 */
+			mdelay(10);
+
+#if IS_ENABLED(CONFIG_CADENCE_QSPI)
+			/*
+			 * Trigger software reset to QSPI flash.
+			 * On some boards, the QSPI flash reset may not be
+			 * connected to the HPS warm reset.
+			 */
+			qspi_flash_software_reset();
+#endif
+
+			ret = readl(socfpga_get_rstmgr_addr() +
+				    RSTMGR_A10_SYSWARMMASK);
+			/*
+			 * Masking s2f & FPGA manager module reset from warm
+			 * reset
+			 */
+			writel(ret & (~(ALT_RSTMGR_SYSWARMMASK_S2F_SET_MSK |
+			       ALT_RSTMGR_FPGAMGRWARMMASK_S2F_SET_MSK)),
+			       socfpga_get_rstmgr_addr() +
+			       RSTMGR_A10_SYSWARMMASK);
+
+			/*
+			 * BootROM will configure both IO and pin mux after a
+			 * warm reset
+			 */
+			ret = readl(socfpga_get_sysmgr_addr() +
+				    SYSMGR_A10_ROMCODE_CTRL);
+			writel(ret | BOOTROM_CONFIGURES_IO_PINMUX,
+			       socfpga_get_sysmgr_addr() +
+			       SYSMGR_A10_ROMCODE_CTRL);
+
+			/*
+			 * Up to here, image is considered valid and should be
+			 * set as valid before warm reset is triggered
+			 */
+			writel(FSBL_IMAGE_IS_VALID, socfpga_get_sysmgr_addr() +
+			       SYSMGR_A10_ROMCODE_INITSWSTATE);
+
+			/*
+			 * Set this flag to scratch register, so that a proper
+			 * boot progress before / after warm reset can be
+			 * tracked by FSBL
+			 */
+			set_regular_boot(true);
+
+			WATCHDOG_RESET();
+
+			reset_cpu();
+		}
+
+		/*
+		 * Reset this flag to scratch register, so that a proper
+		 * boot progress before / after warm reset can be
+		 * tracked by FSBL
+		 */
+		set_regular_boot(false);
+
+		ret = readl(socfpga_get_rstmgr_addr() +
+			    RSTMGR_A10_SYSWARMMASK);
+
+		/*
+		 * Unmasking s2f & FPGA manager module reset from warm
+		 * reset
+		 */
+		writel(ret | ALT_RSTMGR_SYSWARMMASK_S2F_SET_MSK |
+			ALT_RSTMGR_FPGAMGRWARMMASK_S2F_SET_MSK,
+			socfpga_get_rstmgr_addr() + RSTMGR_A10_SYSWARMMASK);
+
+		/*
+		 * Up to here, MPFE hang workaround is considered done and
+		 * should be reset as invalid until FSBL successfully loading
+		 * SSBL, and prepare jumping to SSBL, then only setting as
+		 * valid
+		 */
+		writel(FSBL_IMAGE_IS_INVALID, socfpga_get_sysmgr_addr() +
+		       SYSMGR_A10_ROMCODE_INITSWSTATE);
+
 		ddr_calibration_sequence();
+	}
 
 	if (!is_fpgamgr_user_mode())
 		fpgamgr_program(buf, FPGA_BUFSIZ, 0);