soc/intel/common: Introduce API to get the FSP Reset Status

This patch creates a function to read the FSP API Reset Status. This
function relies on the FSP Scheduled Reset HOB which holds the reset
type (warm/cold/shutdown) information along with any platform specific
reset need (like global reset).

Ideally FSP API should be able to return the status (both success and
error code) upon exiting the FSP API but unfortunately there are some
scenarios in ADL/RPL FSP where MultiPhaseSiInit API is unable to return
any ERROR status. Hence, this function provides an additional hook to
read the FSP reset status by reading the dedicated HOB without relying
on the FSP API exit status code.

Additionally, create FSP_MULTIPHASE_SI_INIT_RETURN_BROKEN config option
to handle broken FSP API return status issue.

Any SoC platform that selects the `FSP_MULTIPHASE_SI_INIT_RETURN_BROKEN`
config will call into this newly added API to get the FSP return status
from MultiPhaseSiInit.

BUG=b:278665768
TEST=Able to select FSP_MULTIPHASE_SI_INIT_RETURN_BROKEN for ADL/RPL SoC
code and call into this API to know the return status from
MultiPhaseSiInit FSP API.

Signed-off-by: Subrata Banik <subratabanik@google.com>
Change-Id: Ief5d79736cc11a0a31ca2889128285795f8b5aae
Reviewed-on: https://review.coreboot.org/c/coreboot/+/74783
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Eric Lai <eric_lai@quanta.corp-partner.google.com>
Reviewed-by: Kapil Porwal <kapilporwal@google.com>
diff --git a/src/drivers/intel/fsp2_0/Kconfig b/src/drivers/intel/fsp2_0/Kconfig
index 2132737..378f4fb 100644
--- a/src/drivers/intel/fsp2_0/Kconfig
+++ b/src/drivers/intel/fsp2_0/Kconfig
@@ -389,4 +389,17 @@
 	  Save MRC training data after FSP-S. Select this on platforms that generate MRC
 	  cache HOB data as part of FSP-S rather than FSP-M.
 
+config FSP_MULTIPHASE_SI_INIT_RETURN_BROKEN
+	bool
+	default n
+	depends on PLATFORM_USES_FSP2_2
+	help
+	  Select this config for Intel SoC platform where FSP MultiPhaseSiInit API is unable
+	  to return ERROR status properly.
+
+	  The config option will be selected based on the target SoC platform and if the
+	  problem existed inside the FSP MultiPhaseSiInit. At present the problem has only
+	  reported with Alder Lake and Raptor Lake FSP where MultiPhaseSiInit API is unable
+	  to return any ERROR status.
+
 endif
diff --git a/src/soc/intel/common/fsp_reset.c b/src/soc/intel/common/fsp_reset.c
index bf26551..3a34aad 100644
--- a/src/soc/intel/common/fsp_reset.c
+++ b/src/soc/intel/common/fsp_reset.c
@@ -1,10 +1,35 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 
 #include <console/console.h>
+#include <fsp/api.h>
 #include <fsp/util.h>
 #include <soc/intel/common/reset.h>
 #include <stdint.h>
 
+static const uint8_t fsp_reset_guid[16] = {
+	0xff, 0x97, 0x05, 0xea, 0x58, 0x88, 0xca, 0x41,
+	0xbb, 0xc1, 0xfe, 0x18, 0xfc, 0xd2, 0x8e, 0x22
+};
+
+static const uint8_t fsp_global_reset_guid[16] = {
+	0x4c, 0x1b, 0xb3, 0x9d, 0xef, 0xf5, 0xbb, 0x48,
+	0x94, 0x2b, 0x18, 0x1f, 0x7e, 0x3a, 0x3e, 0x40
+};
+
+/* Platform Reset String as per Intel FSP is "PCH RESET" in unicode */
+#define PLATFORM_RESET_STRING_LENGTH  20
+
+struct pch_reset_data {
+	char reserved[PLATFORM_RESET_STRING_LENGTH];
+	efi_guid_t global_reset_uid;
+};
+
+/* This structure is used to provide information about PCH Reset */
+struct fsp_reset_hob {
+	EFI_RESET_TYPE   reset_type;
+	struct pch_reset_data   reset_data;
+};
+
 void chipset_handle_reset(uint32_t status)
 {
 	if (status == CONFIG_FSP_STATUS_GLOBAL_RESET) {
@@ -15,3 +40,47 @@
 	printk(BIOS_ERR, "unhandled reset type %x\n", status);
 	die("unknown reset type");
 }
+
+static uint32_t fsp_reset_type_to_status(EFI_RESET_TYPE reset_type)
+{
+	uint32_t status;
+
+	switch (reset_type) {
+	case EfiResetCold:
+		status = FSP_STATUS_RESET_REQUIRED_COLD;
+		break;
+	case EfiResetWarm:
+		status = FSP_STATUS_RESET_REQUIRED_WARM;
+		break;
+	default:
+		printk(BIOS_ERR, "unhandled reset type %x\n", reset_type);
+		die("unknown reset type");
+	}
+
+	return status;
+}
+
+/*
+ * Return PCH Reset Status
+ * The return status can be between EfiResetCold, EfiResetWarm, EfiResetShutdown
+ * or EfiResetPlatformSpecific.
+ *
+ * If reset type is `EfiResetPlatformSpecific` then relying on pch_reset_data structure
+ * to know if the reset type is a global reset.
+ */
+uint32_t fsp_get_pch_reset_status(void)
+{
+	assert(CONFIG(FSP_MULTIPHASE_SI_INIT_RETURN_BROKEN));
+
+	size_t size;
+	const struct fsp_reset_hob *hob = fsp_find_extension_hob_by_guid(fsp_reset_guid, &size);
+	if (!hob)
+		return 0;
+
+	if ((hob->reset_type == EfiResetPlatformSpecific) &&
+		 fsp_guid_compare((void *)&(hob->reset_data.global_reset_uid),
+			 fsp_global_reset_guid))
+		return CONFIG_FSP_STATUS_GLOBAL_RESET;
+
+	return fsp_reset_type_to_status(hob->reset_type);
+}
diff --git a/src/soc/intel/common/reset.h b/src/soc/intel/common/reset.h
index e1f6aab..d9c6ac6 100644
--- a/src/soc/intel/common/reset.h
+++ b/src/soc/intel/common/reset.h
@@ -13,4 +13,14 @@
 /* Prepare for reset, run do_global_reset(), halt. */
 __noreturn void global_reset(void);
 
+/*
+ * Return PCH Reset Status
+ * The return status can be between EfiResetCold, EfiResetWarm, EfiResetShutdown
+ * or EfiResetPlatformSpecific.
+ *
+ * If reset type if `EfiResetPlatformSpecific` then relying on pch_reset_data structure
+ * to know if the reset type is a global reset.
+ */
+uint32_t fsp_get_pch_reset_status(void);
+
 #endif	/* _INTEL_COMMON_RESET_H_ */