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/misc_arria10.c b/arch/arm/mach-socfpga/misc_arria10.c
index bf97805..634e63e 100644
--- a/arch/arm/mach-socfpga/misc_arria10.c
+++ b/arch/arm/mach-socfpga/misc_arria10.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * Copyright (C) 2016-2017 Intel Corporation
+ * Copyright (C) 2016-2021 Intel Corporation
  */
 
 #include <altera.h>
@@ -11,6 +11,7 @@
 #include <miiphy.h>
 #include <netdev.h>
 #include <ns16550.h>
+#include <spi_flash.h>
 #include <watchdog.h>
 #include <asm/arch/misc.h>
 #include <asm/arch/pinmux.h>
@@ -21,6 +22,7 @@
 #include <asm/arch/nic301.h>
 #include <asm/io.h>
 #include <asm/pl310.h>
+#include <linux/sizes.h>
 
 #define PINMUX_UART0_TX_SHARED_IO_OFFSET_Q1_3	0x08
 #define PINMUX_UART0_TX_SHARED_IO_OFFSET_Q2_11	0x58
@@ -29,6 +31,12 @@
 #define PINMUX_UART1_TX_SHARED_IO_OFFSET_Q3_7	0x78
 #define PINMUX_UART1_TX_SHARED_IO_OFFSET_Q4_3	0x98
 
+#define REGULAR_BOOT_MAGIC	0xd15ea5e
+
+#define QSPI_S25FL_SOFT_RESET_COMMAND	0x00f0ff82
+#define QSPI_N25_SOFT_RESET_COMMAND	0x00000001
+#define QSPI_NO_SOFT_RESET		0x00000000
+
 /*
  * FPGA programming support for SoC FPGA Arria 10
  */
@@ -122,3 +130,83 @@
 	else
 		socfpga_bridges_reset();
 }
+
+/*
+ * This function set/unset magic number "0xd15ea5e" to
+ * handoff register isw_handoff[7] - 0xffd0624c
+ * This magic number is part of boot progress tracking
+ * and it's required for warm reset workaround on MPFE hang issue.
+ */
+void set_regular_boot(unsigned int status)
+{
+	if (status)
+		writel(REGULAR_BOOT_MAGIC, socfpga_get_sysmgr_addr() +
+		       SYSMGR_A10_ISW_HANDOFF_BASE + SYSMGR_A10_ISW_HANDOFF_7);
+	else
+		writel(0, socfpga_get_sysmgr_addr() +
+		       SYSMGR_A10_ISW_HANDOFF_BASE + SYSMGR_A10_ISW_HANDOFF_7);
+}
+
+/*
+ * This function is used to check whether
+ * handoff register isw_handoff[7] contains
+ * magic number "0xd15ea5e".
+ */
+bool is_regular_boot_valid(void)
+{
+	unsigned int status;
+
+	status = readl(socfpga_get_sysmgr_addr() +
+		       SYSMGR_A10_ISW_HANDOFF_BASE + SYSMGR_A10_ISW_HANDOFF_7);
+
+	if (status == REGULAR_BOOT_MAGIC)
+		return true;
+	else
+		return false;
+}
+
+#if IS_ENABLED(CONFIG_CADENCE_QSPI)
+/* This function is used to trigger software reset
+ * to the QSPI flash. On some boards, the QSPI flash reset may
+ * not be connected to the HPS warm reset.
+ */
+int qspi_flash_software_reset(void)
+{
+	struct udevice *flash;
+	int ret;
+
+	/* Get the flash info */
+	ret = spi_flash_probe_bus_cs(CONFIG_SF_DEFAULT_BUS,
+				     CONFIG_SF_DEFAULT_CS,
+				     CONFIG_SF_DEFAULT_SPEED,
+				     CONFIG_SF_DEFAULT_MODE,
+				     &flash);
+
+	if (ret) {
+		debug("Failed to initialize SPI flash at ");
+		debug("%u:%u (error %d)\n", CONFIG_SF_DEFAULT_BUS,
+		      CONFIG_SF_DEFAULT_CS, ret);
+		return -ENODEV;
+	}
+
+	if (!flash)
+		return -EINVAL;
+
+	/*
+	 * QSPI flash software reset command, for the case where
+	 * no HPS reset connected to QSPI flash reset
+	 */
+	if (!memcmp(flash->name, "N25", SZ_1 + SZ_2))
+		writel(QSPI_N25_SOFT_RESET_COMMAND, socfpga_get_sysmgr_addr() +
+		       SYSMGR_A10_ROMCODE_QSPIRESETCOMMAND);
+	else if (!memcmp(flash->name, "S25FL", SZ_1 + SZ_4))
+		writel(QSPI_S25FL_SOFT_RESET_COMMAND,
+		       socfpga_get_sysmgr_addr() +
+		       SYSMGR_A10_ROMCODE_QSPIRESETCOMMAND);
+	else /* No software reset */
+		writel(QSPI_NO_SOFT_RESET, socfpga_get_sysmgr_addr() +
+		       SYSMGR_A10_ROMCODE_QSPIRESETCOMMAND);
+
+	return 0;
+}
+#endif