soc/amd/stoneyridge: Create a HALT_THIS_AP callout

It was required for all cores use the same CAR teardown function
(exit_car.S and gcccar.inc). AGESA has already been modified to do the
AP to do the call out. Create assembly code to call chipset_teardown_car
and then enter an endless loop with halt instruction. Then create the
call out that will call this new assembly code.

BUG=b:70338633
AGESA COMMIT=3313d277
TEST=Created a debug version of AGESA that would print the returned
status of HALT_THIS_AP. Build code without the fix, see the return.
Build code with the fix, see that there's no return.

Change-Id: I05ee405812211d93dfdbdc5ee7d9978c2eb585e1
Signed-off-by: Richard Spiegel <richard.spiegel@silverbackltd.com>
Reviewed-on: https://review.coreboot.org/24999
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Martin Roth <martinroth@google.com>
diff --git a/src/soc/amd/common/block/cpu/Makefile.inc b/src/soc/amd/common/block/cpu/Makefile.inc
index ecc9afbc..fed208a 100644
--- a/src/soc/amd/common/block/cpu/Makefile.inc
+++ b/src/soc/amd/common/block/cpu/Makefile.inc
@@ -1,3 +1,7 @@
 bootblock-$(CONFIG_SOC_AMD_COMMON_BLOCK_CAR) += car/cache_as_ram.S
+bootblock-$(CONFIG_SOC_AMD_COMMON_BLOCK_CAR) += car/ap_exit_car.S
+bootblock-$(CONFIG_SOC_AMD_COMMON_BLOCK_CAR) += car/exit_car.S
+
 postcar-$(CONFIG_SOC_AMD_COMMON_BLOCK_CAR) += car/exit_car.S
+
 romstage-$(CONFIG_SOC_AMD_COMMON_BLOCK_CAR) += car/exit_car.S
diff --git a/src/soc/amd/common/block/cpu/car/ap_exit_car.S b/src/soc/amd/common/block/cpu/car/ap_exit_car.S
new file mode 100644
index 0000000..5d3e13b
--- /dev/null
+++ b/src/soc/amd/common/block/cpu/car/ap_exit_car.S
@@ -0,0 +1,61 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2018 Advanced Micro Devices, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+.code32
+
+#include <cpu/x86/cr.h>
+
+.globl ap_teardown_car
+ap_teardown_car:
+	pop	%esi		/* return address, don't care */
+	pop	%esi		/* flags */
+
+	/* chipset_teardown_car() is expected to disable cache-as-ram. */
+	call	chipset_teardown_car
+
+	/*
+	 * Check flags requirements (0 = FALSE, 1 = TRUE) :
+	 * bit 0 = ExecWbinvd
+	 * bit 1 = CacheEn
+	 */
+
+	/*
+	 * TODO: Either use or remove this code - we'll node if the code
+	 * is needed when 3 conditions happens:
+	 * 1) This code is in place
+	 * 2) AGESA code that calls HALT_THIS_AP is in place
+	 * 3) We boot to OS, go to S3 and resume.
+	 * If S3 resume fails, this code might be needed, if S3 resume
+	 * is successful then the code can be removed.
+	 */
+
+	/*
+	 * Commented out until defined if needed or not.
+	 * test	%esi, 1
+	 * jz	1f
+	 * wbinvd
+	 * 1:
+	 */
+
+	test	%esi, 2
+	jz	2f
+	/* Enable cache */
+	mov	%cr0, %eax
+	and	$(~(CR0_CD | CR0_NW)), %eax
+	mov	%eax, %cr0
+2:
+	cli
+	hlt
+	jmp	2b
diff --git a/src/soc/amd/common/block/include/amdblocks/BiosCallOuts.h b/src/soc/amd/common/block/include/amdblocks/BiosCallOuts.h
index e061c63..34131cf 100644
--- a/src/soc/amd/common/block/include/amdblocks/BiosCallOuts.h
+++ b/src/soc/amd/common/block/include/amdblocks/BiosCallOuts.h
@@ -57,6 +57,7 @@
 
 AGESA_STATUS agesa_fch_initreset(UINT32 Func, UINTN FchData, VOID *ConfigPtr);
 AGESA_STATUS agesa_fch_initenv(UINT32 Func, UINTN FchData, VOID *ConfigPtr);
+AGESA_STATUS agesa_HaltThisAp(UINT32 Func, UINTN Data, VOID *ConfigPtr);
 
 void platform_FchParams_reset(FCH_RESET_DATA_BLOCK *FchParams_reset);
 void platform_FchParams_env(FCH_DATA_BLOCK *FchParams_env);
diff --git a/src/soc/amd/common/block/include/amdblocks/car.h b/src/soc/amd/common/block/include/amdblocks/car.h
new file mode 100644
index 0000000..6c4049b
--- /dev/null
+++ b/src/soc/amd/common/block/include/amdblocks/car.h
@@ -0,0 +1,23 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2018 Advanced Micro Devices, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __AMD_CAR_H__
+#define __AMD_CAR_H__
+
+#include <stdint.h>
+
+void ap_teardown_car(uint32_t flags);
+
+#endif /* __AMD_CAR_H__ */
diff --git a/src/soc/amd/common/block/pi/def_callouts.c b/src/soc/amd/common/block/pi/def_callouts.c
index c05d4de..beb2d18 100644
--- a/src/soc/amd/common/block/pi/def_callouts.c
+++ b/src/soc/amd/common/block/pi/def_callouts.c
@@ -29,6 +29,7 @@
 const BIOS_CALLOUT_STRUCT BiosCallouts[] = {
 	{ AGESA_DO_RESET,                 agesa_Reset },
 	{ AGESA_FCH_OEM_CALLOUT,          agesa_fch_initreset },
+	{ AGESA_HALT_THIS_AP,             agesa_HaltThisAp },
 	{ AGESA_GNB_PCIE_SLOT_RESET,      agesa_PcieSlotResetControl }
 };
 #else
diff --git a/src/soc/amd/stoneyridge/BiosCallOuts.c b/src/soc/amd/stoneyridge/BiosCallOuts.c
index 4ddcedc..f30ed3c 100644
--- a/src/soc/amd/stoneyridge/BiosCallOuts.c
+++ b/src/soc/amd/stoneyridge/BiosCallOuts.c
@@ -26,6 +26,7 @@
 #include <amdlib.h>
 #include <amdblocks/dimm_spd.h>
 #include "chip.h"
+#include <amdblocks/car.h>
 
 void __attribute__((weak)) platform_FchParams_reset(
 				FCH_RESET_DATA_BLOCK *FchParams_reset) {}
@@ -139,6 +140,24 @@
 	return AGESA_SUCCESS;
 }
 
+AGESA_STATUS agesa_HaltThisAp(UINT32 Func, UINTN Data, VOID *ConfigPtr)
+{
+	AGESA_HALT_THIS_AP_PARAMS *info = ConfigPtr;
+	uint32_t flags = 0;
+
+	if (info->PrimaryCore == TRUE)
+		return AGESA_UNSUPPORTED; /* force normal path */
+	if (info->ExecWbinvd == TRUE)
+		flags |= 1;
+	if (info->CacheEn == TRUE)
+		flags |= 2;
+
+	ap_teardown_car(flags); /* does not return */
+
+	/* Should never reach here */
+	return AGESA_UNSUPPORTED;
+}
+
 /* Allow mainboards to fill the SPD buffer */
 __attribute__((weak)) int mainboard_read_spd(uint8_t spdAddress, char *buf,
 						size_t len)