From e37c51fbf56695813ee545da5507eb465c688241 Mon Sep 17 00:00:00 2001
From: Timothy Pearson <tpearson@raptorengineeringinc.com>
Date: Wed, 24 Jun 2015 19:15:09 -0500
Subject: [PATCH 070/143] northbridge/amd/amdmct/mct_ddr3: Fix lockups and
 wasted time during ECC init

Change-Id: I09a8ea83024186b7ece7d78a4bef1201ab34ff8a
Signed-off-by: Timothy Pearson <tpearson@raptorengineeringinc.com>
---
 src/northbridge/amd/amdmct/mct_ddr3/mct_d.c    |  144 +++++++++++++++---------
 src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c |   39 ++++++-
 src/northbridge/amd/amdmct/mct_ddr3/mctproc.c  |   22 +++-
 src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c  |    3 +-
 4 files changed, 147 insertions(+), 61 deletions(-)

diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
index d8a09f0..78bc8b3 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
@@ -1498,11 +1498,12 @@ restartinit:
 		InterleaveChannels_D(pMCTstat, pDCTstatA);
 
 		printk(BIOS_DEBUG, "mctAutoInitMCT_D: ECCInit_D\n");
-		ECCInit_D(pMCTstat, pDCTstatA);			/* Setup ECC control and ECC check-bits*/
-
-		/* mctDoWarmResetMemClr_D(); */
-		printk(BIOS_DEBUG, "mctAutoInitMCT_D: MCTMemClr_D\n");
-		MCTMemClr_D(pMCTstat,pDCTstatA);
+		if (!ECCInit_D(pMCTstat, pDCTstatA)) {			/* Setup ECC control and ECC check-bits*/
+			/* Memory was not cleared during ECC setup */
+			/* mctDoWarmResetMemClr_D(); */
+			printk(BIOS_DEBUG, "mctAutoInitMCT_D: MCTMemClr_D\n");
+			MCTMemClr_D(pMCTstat,pDCTstatA);
+		}
 
 		printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_ForceNBPState0_Dis_Fam15\n");
 		for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
@@ -1695,7 +1696,6 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
 		uint8_t x8_present = 0;
 		uint8_t memclk_index;
 		uint8_t interleave_channels = 0;
-		uint8_t redirect_ecc_scrub = 0;
 		uint16_t trdrdsddc;
 		uint16_t trdrddd;
 		uint16_t cdd_trdrddd;
@@ -1733,9 +1733,6 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
 		if (pDCTstat->DIMMValidDCT[0] && pDCTstat->DIMMValidDCT[1] && mctGet_NVbits(NV_Unganged))
 			interleave_channels = 1;
 
-		if ((pMCTstat->GStatus & 1 << GSB_ECCDIMMs) && mctGet_NVbits(NV_ECCRedir))
-			redirect_ecc_scrub = 1;
-
 		dword = (Get_NB32_DCT(dev, dct, 0x240) >> 4) & 0xf;
 		if (dword > 6)
 			read_odt_delay = dword - 6;
@@ -1927,21 +1924,10 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
 		dword |= (interleave_channels & 0x1) << 2;
 		Set_NB32_DCT(dev, dct, 0x110, dword);			/* DRAM Controller Select Low */
 
-		dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x58);	/* Scrub Rate Control */
-		dword &= ~(0x1f << 24);					/* L3Scrub = NV_L3BKScrub */
-		dword |= (mctGet_NVbits(NV_L3BKScrub) & 0x1f) << 24;
-		dword &= ~(0x1f);					/* DramScrub = NV_DramBKScrub */
-		dword |= mctGet_NVbits(NV_DramBKScrub) & 0x1f;
-		Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x58, dword);	/* Scrub Rate Control */
-
-		dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c);	/* DRAM Scrub Address Low */
-		dword &= ~(0x1);					/* ScrubReDirEn = redirect_ecc_scrub */
-		dword |= redirect_ecc_scrub & 0x1;
-		Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c, dword);	/* DRAM Scrub Address Low */
-
-		dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8);	/* L3 Control 1 */
-		dword &= ~(0x1 << 4);					/* L3ScrbRedirDis = 0 */
-		Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8, dword);	/* L3 Control 1 */
+		/* NOTE
+		 * ECC-related setup is performed as part of ECCInit_D and must not be located here,
+		 * otherwise semi-random lockups will occur due to misconfigured scrubbing hardware!
+		 */
 
 		/* FIXME
 		 * The BKDG-recommended settings cause memory corruption on the ASUS KGPE-D16.
@@ -1983,11 +1969,17 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
 		dword |= ((((dword >> 8) & 0x1f) + 1) << 16);
 		Set_NB32_DCT(dev, dct, 0x21c, dword);			/* DRAM Timing 6 */
 
+		/* Configure partial power down delay */
+		dword = Get_NB32(dev, 0x244);				/* DRAM Controller Miscellaneous 3 */
+		dword &= ~0xf;						/* PrtlChPDDynDly = 0x2 */
+		dword |= 0x2;
+		Set_NB32(dev, 0x244, dword);				/* DRAM Controller Miscellaneous 3 */
+
 		/* Enable prefetchers */
-		dword = Get_NB32_DCT(dev, dct, 0x110);			/* Memory Controller Configuration High */
+		dword = Get_NB32(dev, 0x11c);				/* Memory Controller Configuration High */
 		dword &= ~(0x1 << 13);					/* PrefIoDis = 0 */
 		dword &= ~(0x1 << 12);					/* PrefCpuDis = 0 */
-		Set_NB32_DCT(dev, dct, 0x110, dword);			/* Memory Controller Configuration High */
+		Set_NB32(dev, 0x11c, dword);				/* Memory Controller Configuration High */
 	}
 }
 
@@ -2091,6 +2083,19 @@ static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
 		pMCTstat->GStatus |= 1 << GSB_ConfigRestored;
 	}
 
+	if (is_fam15h()) {
+		uint8_t Node;
+		struct DCTStatStruc *pDCTstat;
+
+		/* Switch DCT control register to DCT 0 per Erratum 505 */
+		for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+			pDCTstat = pDCTstatA + Node;
+			if (pDCTstat->NodePresent) {
+				fam15h_switch_dct(pDCTstat->dev_map, 0);
+			}
+		}
+	}
+
 	/* FIXME - currently uses calculated value	TrainMaxReadLatency_D(pMCTstat, pDCTstatA); */
 	mctHookAfterAnyTraining();
 }
@@ -2337,6 +2342,7 @@ static void MCTMemClr_D(struct MCTStatStruc *pMCTstat,
 	 * status are checked to ensure that memclr has completed.
 	 */
 	u8 Node;
+	uint32_t dword;
 	struct DCTStatStruc *pDCTstat;
 
 	if (!mctGet_NVbits(NV_DQSTrainCTL)){
@@ -2357,6 +2363,18 @@ static void MCTMemClr_D(struct MCTStatStruc *pMCTstat,
 			}
 		}
 	}
+
+	for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+		pDCTstat = pDCTstatA + Node;
+
+		/* Configure and enable prefetchers */
+		if (is_fam15h())
+			dword = 0x0ce00f41;	/* BKDG recommended */
+		else
+			dword = 0x0fe40fc0;	/* BKDG recommended */
+		dword |= MCCH_FlushWrOnStpGnt;	/* Set for S3 */
+		Set_NB32(pDCTstat->dev_dct, 0x11c, dword);
+	}
 }
 
 static void DCTMemClr_Init_D(struct MCTStatStruc *pMCTstat,
@@ -2364,48 +2382,59 @@ static void DCTMemClr_Init_D(struct MCTStatStruc *pMCTstat,
 {
 	u32 val;
 	u32 dev;
-	u32 reg;
+	uint32_t dword;
 
 	/* Initiates a memory clear operation on one node */
 	if (pDCTstat->DCTSysLimit) {
 		dev = pDCTstat->dev_dct;
-		reg = 0x110;
+
+		/* Disable prefetchers */
+		dword = Get_NB32(dev, 0x11c);		/* Memory Controller Configuration High */
+		dword |= 0x1 << 13;			/* PrefIoDis = 1 */
+		dword |= 0x1 << 12;			/* PrefCpuDis = 1 */
+		Set_NB32(dev, 0x11c, dword);		/* Memory Controller Configuration High */
 
 		do {
-			val = Get_NB32(dev, reg);
+			val = Get_NB32(dev, 0x110);
 		} while (val & (1 << MemClrBusy));
 
 		val |= (1 << MemClrInit);
-		Set_NB32(dev, reg, val);
+		Set_NB32(dev, 0x110, val);
 	}
 }
 
 static void DCTMemClr_Sync_D(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat)
 {
-	u32 val;
-	u32 dev = pDCTstat->dev_dct;
-	u32 reg;
+	uint32_t dword;
+	uint32_t dev = pDCTstat->dev_dct;
+
+	printk(BIOS_DEBUG, "%s: Start\n", __func__);
 
 	/* Ensure that a memory clear operation has completed on one node */
 	if (pDCTstat->DCTSysLimit){
-		reg = 0x110;
-
+		printk(BIOS_DEBUG, "%s: Waiting for memory clear to complete", __func__);
 		do {
-			val = Get_NB32(dev, reg);
-		} while (val & (1 << MemClrBusy));
+			dword = Get_NB32(dev, 0x110);
+
+			printk(BIOS_DEBUG, ".");
+		} while (dword & (1 << MemClrBusy));
 
+		printk(BIOS_DEBUG, "\n");
 		do {
-			val = Get_NB32(dev, reg);
-		} while (!(val & (1 << Dr_MemClrStatus)));
+			printk(BIOS_DEBUG, ".");
+			dword = Get_NB32(dev, 0x110);
+		} while (!(dword & (1 << Dr_MemClrStatus)));
+		printk(BIOS_DEBUG, "\n");
 	}
 
-	if (is_fam15h())
-		val = 0x0ce00f41;	/* BKDG recommended */
-	else
-		val = 0x0fe40fc0;	/* BKDG recommended */
-	val |= MCCH_FlushWrOnStpGnt;	/* Set for S3 */
-	Set_NB32(dev, 0x11c, val);
+	/* Enable prefetchers */
+	dword = Get_NB32(dev, 0x11c);		/* Memory Controller Configuration High */
+	dword &= ~(0x1 << 13);			/* PrefIoDis = 0 */
+	dword &= ~(0x1 << 12);			/* PrefCpuDis = 0 */
+	Set_NB32(dev, 0x11c, dword);		/* Memory Controller Configuration High */
+
+	printk(BIOS_DEBUG, "%s: Done\n", __func__);
 }
 
 static u8 NodePresent_D(u8 Node)
@@ -3346,8 +3375,8 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
 	dev = pDCTstat->dev_dct;
 
 	/* Build Dram Control Register Value */
-	DramConfigMisc2 = Get_NB32_DCT(dev, dct, 0xA8);		/* Dram Control*/
-	DramControl = Get_NB32_DCT(dev, dct, 0x78);		/* Dram Control*/
+	DramConfigMisc2 = Get_NB32_DCT(dev, dct, 0xa8);		/* Dram Miscellaneous 2 */
+	DramControl = Get_NB32_DCT(dev, dct, 0x78);		/* Dram Control */
 
 	/* FIXME: Skip mct_checkForDxSupport */
 	/* REV_CALL mct_DoRdPtrInit if not Dx */
@@ -3402,9 +3431,15 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
 			/* set only if x8 Registered DIMMs in System*/
 			DramConfigHi |= 1 << RDqsEn;
 
-	if (mctGet_NVbits(NV_CKE_CTL))
-		/*Chip Select control of CKE*/
-		DramConfigHi |= 1 << 16;
+	if (pDCTstat->LogicalCPUID & AMD_FAM15_ALL) {
+		DramConfigLo |= 1 << 25;	/* PendRefPaybackS3En = 1 */
+		DramConfigLo |= 1 << 24;	/* StagRefEn = 1 */
+		DramConfigHi |= 1 << 16;	/* PowerDownMode = 1 */
+	} else {
+		if (mctGet_NVbits(NV_CKE_CTL))
+			/*Chip Select control of CKE*/
+			DramConfigHi |= 1 << 16;
+	}
 
 	/* Control Bank Swizzle */
 	if (0) /* call back not needed mctBankSwizzleControl_D()) */
@@ -4112,8 +4147,7 @@ static void mct_preInitDCT(struct MCTStatStruc *pMCTstat,
 
 	if (load_spd_hashes_from_nvram(pMCTstat, pDCTstat) < 0) {
 		pDCTstat->spd_data.nvram_spd_match = 0;
-	}
-	else {
+	} else {
 		compare_nvram_spd_hashes(pMCTstat, pDCTstat);
 	}
 #else
@@ -4311,8 +4345,8 @@ static u8 mct_PlatformSpec(struct MCTStatStruc *pMCTstat,
 	}
 	for (i=i_start; i<i_end; i++) {
 		index_reg = 0x98;
-		Set_NB32_index_wait_DCT(dev, i, index_reg, 0x00, pDCTstat->CH_ODC_CTL[i]); /* Channel A Output Driver Compensation Control */
-		Set_NB32_index_wait_DCT(dev, i, index_reg, 0x04, pDCTstat->CH_ADDR_TMG[i]); /* Channel A Output Driver Compensation Control */
+		Set_NB32_index_wait_DCT(dev, i, index_reg, 0x00, pDCTstat->CH_ODC_CTL[i]); /* Channel A/B Output Driver Compensation Control */
+		Set_NB32_index_wait_DCT(dev, i, index_reg, 0x04, pDCTstat->CH_ADDR_TMG[i]); /* Channel A/B Output Driver Compensation Control */
 	}
 
 	return pDCTstat->ErrCode;
@@ -6102,11 +6136,11 @@ void ProgDramMRSReg_D(struct MCTStatStruc *pMCTstat,
 		DramMRS |= 1 << 1;
 
 	dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x84);
+	dword |= DramMRS;
 	if (is_fam15h())
 		dword &= ~0x00800003;
 	else
 		dword &= ~0x00fc2f8f;
-	dword |= DramMRS;
 	Set_NB32_DCT(pDCTstat->dev_dct, dct, 0x84, dword);
 }
 
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
index 918e91e..a0482e8 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
@@ -92,8 +92,13 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
 	uint8_t sync_flood_on_dram_err[MAX_NODES_SUPPORTED];
 	uint8_t sync_flood_on_any_uc_err[MAX_NODES_SUPPORTED];
 
+	uint8_t redirect_ecc_scrub = 0;
+
 	mctHookBeforeECC();
 
+	if ((pMCTstat->GStatus & 1 << GSB_ECCDIMMs) && mctGet_NVbits(NV_ECCRedir))
+		redirect_ecc_scrub = 1;
+
 	/* Construct these booleans, based on setup options, for easy handling
 	later in this procedure */
 	OB_NBECC = mctGet_NVbits(NV_NBECC);			/* MCA ECC (MCE) enable bit */
@@ -230,12 +235,12 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
 					}
 					dev = pDCTstat->dev_nbmisc;
 					val = curBase << 8;
-					if(OB_ECCRedir) {
-						val |= (1<<0); /* enable redirection */
+					if (OB_ECCRedir) {
+						val |= (1<<0);			/* enable redirection */
 					}
-					Set_NB32(dev, 0x5C, val); /* Dram Scrub Addr Low */
+					Set_NB32(dev, 0x5c, val);		/* Dram Scrub Addr Low */
 					val = curBase>>24;
-					Set_NB32(dev, 0x60, val); /* Dram Scrub Addr High */
+					Set_NB32(dev, 0x60, val);		/* Dram Scrub Addr High */
 					Set_NB32(dev, 0x58, OF_ScrubCTL);	/*Scrub Control */
 
 					if (!is_fam15h()) {
@@ -252,6 +257,32 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
 							}
 						}
 					}
+
+					if (is_fam15h()) {
+						uint8_t dct;
+
+						/* Disable training mode
+						 * See fam15EnableTrainingMode for the non-ECC training mode tear-down code
+						 */
+						for (dct = 0; dct < 2; dct++) {
+							/* NOTE: Reads use DCT 0 and writes use the current DCT per Erratum 505 */
+							dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, 0, 0x58);	/* Scrub Rate Control */
+							dword &= ~(0x1f << 24);					/* L3Scrub = NV_L3BKScrub */
+							dword |= (mctGet_NVbits(NV_L3BKScrub) & 0x1f) << 24;
+							dword &= ~(0x1f);					/* DramScrub = NV_DramBKScrub */
+							dword |= mctGet_NVbits(NV_DramBKScrub) & 0x1f;
+							Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x58, dword);	/* Scrub Rate Control */
+
+							dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c);	/* DRAM Scrub Address Low */
+							dword &= ~(0x1);					/* ScrubReDirEn = redirect_ecc_scrub */
+							dword |= redirect_ecc_scrub & 0x1;
+							Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c, dword);	/* DRAM Scrub Address Low */
+
+							dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8);	/* L3 Control 1 */
+							dword &= ~(0x1 << 4);					/* L3ScrbRedirDis = 0 */
+							Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8, dword);	/* L3 Control 1 */
+						}
+					}
 				}	/* this node has ECC enabled dram */
 			}	/*Node has Dram */
 		}	/*if Node present */
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c b/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
index 5ef4a2c..32b447f 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
@@ -23,7 +23,27 @@ u32 mct_SetDramConfigMisc2(struct DCTStatStruc *pDCTstat, u8 dct, u32 misc2)
 {
 	u32 val;
 
-	if (pDCTstat->LogicalCPUID & (AMD_DR_Dx | AMD_DR_Cx)) {
+	/* FIXME
+	 * Mainboards need to be able to specify the maximum number of DIMMs installable per channel
+	 * For now assume a maximum of 2 DIMMs per channel can be installed
+	 */
+	uint8_t MaxDimmsInstallable = 2;
+
+	if (pDCTstat->LogicalCPUID & AMD_FAM15_ALL) {
+		uint8_t cs_mux_45;
+		uint8_t cs_mux_67;
+
+		/* BKDG v3.14 Table 200 / Table 201 */
+		if (MaxDimmsInstallable < 3) {
+			cs_mux_45 = 1;
+			cs_mux_67 = 1;
+		} else {
+			cs_mux_45 = 0;
+			cs_mux_67 = 0;
+		}
+		misc2 |= (cs_mux_45 & 0x1) << 26;
+		misc2 |= (cs_mux_67 & 0x1) << 27;
+	} else if (pDCTstat->LogicalCPUID & (AMD_DR_Dx | AMD_DR_Cx)) {
 		if (pDCTstat->Status & (1 << SB_Registered)) {
 			misc2 |= 1 << SubMemclkRegDly;
 			if (mctGet_NVbits(NV_MAX_DIMMS) == 8)
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
index 35378c8..0e626fa 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
@@ -209,7 +209,8 @@ void AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTsta
 			uint16_t total_delay_seed = ((pDCTData->WLSeedGrossDelay[index+ByteLane] & 0x1f) << 5) | (pDCTData->WLSeedFineDelay[index+ByteLane] & 0x1f);
 			uint16_t total_delay_phy = ((pDCTData->WLGrossDelay[index+ByteLane] & 0x1f) << 5) | (pDCTData->WLFineDelay[index+ByteLane] & 0x1f);
 			if (abs(total_delay_phy - total_delay_seed) > 0x20) {
-				printk(BIOS_DEBUG, "%s: overriding faulty phy value\n", __func__);
+				printk(BIOS_DEBUG, "%s: overriding faulty phy value (seed: %04x phy: %04x step: %04x)\n", __func__,
+					total_delay_seed, total_delay_phy, abs(total_delay_phy - total_delay_seed));
 				pDCTData->WLGrossDelay[index+ByteLane] = pDCTData->WLSeedGrossDelay[index+ByteLane];
 				pDCTData->WLFineDelay[index+ByteLane] = pDCTData->WLSeedFineDelay[index+ByteLane];
 			}
-- 
1.7.9.5

