vc/amd/agesa/f.../Proc/Mem/Tech/DDR3: Support a custom memory profile

The ability to set up a custom memory profile is useful if you don't
like the XMP memory profiles (if they exist) of your RAM sticks, or
want to try some overclocking. Read SPD data will be overriden by your
custom values. Tested on Crucial BLT8G3D1869DT1TX0 (1866MHz 9-9-9-27).

Signed-off-by: Mike Banon <mikebdp2@gmail.com>
Change-Id: I1238ff00ef0efd11ea807794827476c30ac98065
Reviewed-on: https://review.coreboot.org/c/coreboot/+/40489
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
Reviewed-by: Frans Hendriks <fhendriks@eltan.com>
diff --git a/src/vendorcode/amd/agesa/f14/Config/PlatformInstall.h b/src/vendorcode/amd/agesa/f14/Config/PlatformInstall.h
index 31472b2..4103961 100644
--- a/src/vendorcode/amd/agesa/f14/Config/PlatformInstall.h
+++ b/src/vendorcode/amd/agesa/f14/Config/PlatformInstall.h
@@ -817,6 +817,11 @@
   #define CFG_DQS_TRAINING_CONTROL              TRUE
 #endif
 
+#if CONFIG(CPU_AMD_AGESA_OPENSOURCE_MEM_CUSTOM)
+  #undef BLDCFG_IGNORE_SPD_CHECKSUM
+  #define BLDCFG_IGNORE_SPD_CHECKSUM            TRUE
+#endif
+
 #ifdef BLDCFG_IGNORE_SPD_CHECKSUM
   #define CFG_IGNORE_SPD_CHECKSUM               BLDCFG_IGNORE_SPD_CHECKSUM
 #else
diff --git a/src/vendorcode/amd/agesa/f14/Proc/Mem/Main/mmflow.c b/src/vendorcode/amd/agesa/f14/Proc/Mem/Main/mmflow.c
index 392b7fc..cf3942b 100644
--- a/src/vendorcode/amd/agesa/f14/Proc/Mem/Main/mmflow.c
+++ b/src/vendorcode/amd/agesa/f14/Proc/Mem/Main/mmflow.c
@@ -64,6 +64,7 @@
 #include "mm.h"
 #include "mn.h"
 #include "mt.h"
+#include "mtspd3.h"
 #include "mu.h"
 #include "heapManager.h"
 #include "Filecode.h"
@@ -101,6 +102,16 @@
   IN OUT   MEM_DATA_STRUCT *MemPtr
   );
 
+#if (CONFIG(CPU_AMD_AGESA_OPENSOURCE_MEM_CUSTOM))
+
+VOID
+STATIC
+AgesaCustomMemoryProfileSPD (
+  IN OUT   UINT8 *Buffer
+  );
+
+#endif
+
 /*----------------------------------------------------------------------------
  *                            EXPORTED FUNCTIONS
  *
@@ -293,6 +304,60 @@
   return Retval;
 }
 
+#if (CONFIG(CPU_AMD_AGESA_OPENSOURCE_MEM_CUSTOM))
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *
+ *  This function modifies a SPD buffer with the custom SPD values set up in coreboot's config.
+ *
+ *     @param[in,out]   *Buffer   - Pointer to the UINT8
+ *
+ */
+
+VOID
+STATIC
+AgesaCustomMemoryProfileSPD (
+  IN OUT   UINT8 *Buffer
+  )
+{
+  UINT16 Offset;
+  //
+  // Modify a SPD buffer.
+  //
+  Buffer[SPD_DIVIDENT]   = CONFIG_CUSTOM_SPD_DIVIDENT;
+  Buffer[SPD_DIVISOR]    = CONFIG_CUSTOM_SPD_DIVISOR;
+  Buffer[SPD_TCK]        = CONFIG_CUSTOM_SPD_TCK;
+  Buffer[SPD_CASLO]      = CONFIG_CUSTOM_SPD_CASLO;
+  Buffer[SPD_CASHI]      = CONFIG_CUSTOM_SPD_CASHI;
+  Buffer[SPD_TAA]        = CONFIG_CUSTOM_SPD_TAA;
+  Buffer[SPD_TWR]        = CONFIG_CUSTOM_SPD_TWR;
+  Buffer[SPD_TRCD]       = CONFIG_CUSTOM_SPD_TRCD;
+  Buffer[SPD_TRRD]       = CONFIG_CUSTOM_SPD_TRRD;
+  Buffer[SPD_TRP]        = CONFIG_CUSTOM_SPD_TRP;
+  //
+  // SPD_UPPER_TRC and SPD_UPPER_TRAS are the same index but different bits:
+  // SPD_UPPER_TRC - [7:4], SPD_UPPER_TRAS - [3:0].
+  //
+  Buffer[SPD_UPPER_TRAS] = (CONFIG_CUSTOM_SPD_UPPER_TRC << 4) + CONFIG_CUSTOM_SPD_UPPER_TRAS;
+  Buffer[SPD_TRAS]       = CONFIG_CUSTOM_SPD_TRAS;
+  Buffer[SPD_TRC]        = CONFIG_CUSTOM_SPD_TRC;
+  Buffer[SPD_TWTR]       = CONFIG_CUSTOM_SPD_TWTR;
+  Buffer[SPD_TRTP]       = CONFIG_CUSTOM_SPD_TRTP;
+  Buffer[SPD_UPPER_TFAW] = CONFIG_CUSTOM_SPD_UPPER_TFAW;
+  Buffer[SPD_TFAW]       = CONFIG_CUSTOM_SPD_TFAW;
+  //
+  // Print a SPD buffer.
+  //
+  printk(BIOS_SPEW, "***** SPD BUFFER *****\n");
+  for (Offset = 0; Offset < 256; Offset++) {
+    printk(BIOS_SPEW, "Buffer[%d] = 0x%02X\n", Offset, Buffer[Offset]);
+  }
+  printk(BIOS_SPEW, "**********************\n");
+}
+
+#endif
 
 /* -----------------------------------------------------------------------------*/
 /**
@@ -369,6 +434,9 @@
           if (AgesaStatus == AGESA_SUCCESS) {
             DimmSPDPtr->DimmPresent = TRUE;
             IDS_HDT_CONSOLE (MEM_FLOW, "SPD Socket %d Channel %d Dimm %d: %08x\n", Socket, Channel, Dimm, SpdParam.Buffer);
+            #if (CONFIG(CPU_AMD_AGESA_OPENSOURCE_MEM_CUSTOM))
+                AgesaCustomMemoryProfileSPD(SpdParam.Buffer);
+            #endif
           } else {
             DimmSPDPtr->DimmPresent = FALSE;
           }
diff --git a/src/vendorcode/amd/agesa/f14/Proc/Mem/Tech/DDR3/mtspd3.h b/src/vendorcode/amd/agesa/f14/Proc/Mem/Tech/DDR3/mtspd3.h
index c7f490d..2797f80 100644
--- a/src/vendorcode/amd/agesa/f14/Proc/Mem/Tech/DDR3/mtspd3.h
+++ b/src/vendorcode/amd/agesa/f14/Proc/Mem/Tech/DDR3/mtspd3.h
@@ -95,7 +95,7 @@
 
 #define SPD_FTB          9
 
-#if CONFIG(CPU_AMD_AGESA_OPENSOURCE_MEM_JEDEC)
+#if CONFIG(CPU_AMD_AGESA_OPENSOURCE_MEM_JEDEC) || CONFIG(CPU_AMD_AGESA_OPENSOURCE_MEM_CUSTOM)
 
 #define SPD_DIVIDENT    10
 #define SPD_DIVISOR     11