[coreboot-gerrit] Patch set updated for coreboot: nb/amd/amdmct: Select max_lanes based on ECC presence or absence

Damien Zammit (damien@zamaudio.com) gerrit at coreboot.org
Fri Feb 26 03:12:10 CET 2016


Damien Zammit (damien at zamaudio.com) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/13725

-gerrit

commit a3d1837dd99ada2dbd7acc2e03801859e95d52f4
Author: Damien Zammit <damien at zamaudio.com>
Date:   Wed Feb 17 14:14:47 2016 +1100

    nb/amd/amdmct: Select max_lanes based on ECC presence or absence
    
    Change-Id: Ic5482dc13ab7b53ec4df408bbe32d20888ae2e12
    Signed-off-by: Damien Zammit <damien at zamaudio.com>
---
 src/northbridge/amd/amdmct/mct_ddr3/mct_d.c    |  46 ++++----
 src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c |  69 ++++++++----
 src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c   | 108 ++++++++++--------
 src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c  | 150 ++++++++++++++-----------
 4 files changed, 221 insertions(+), 152 deletions(-)

diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
index 4615dd2..23c60b8 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
@@ -183,11 +183,13 @@ static void mct_ExtMCTConfig_Bx(struct DCTStatStruc *pDCTstat);
 static void mct_ExtMCTConfig_Cx(struct DCTStatStruc *pDCTstat);
 static void mct_ExtMCTConfig_Dx(struct DCTStatStruc *pDCTstat);
 
+uint8_t is_ecc_enabled(struct MCTStatStruc *pMCTstat);
+
 static void read_dqs_receiver_enable_control_registers(uint16_t* current_total_delay,
-			uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg);
+			uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg, u8 max_lane);
 
 static void read_dqs_write_timing_control_registers(uint16_t* current_total_delay,
-			uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg);
+			uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg, u8 max_lane);
 
 /*See mctAutoInitMCT header for index relationships to CL and T*/
 static const u16 Table_F_k[]	= {00,200,266,333,400,533 };
@@ -329,6 +331,11 @@ static inline uint8_t is_model10_1f(void)
 	return model101f;
 }
 
+uint8_t is_ecc_enabled(struct MCTStatStruc *pMCTstat)
+{
+	return !!(pMCTstat->GStatus & (1 << GSB_ECCDIMMs));
+}
+
 static uint16_t mhz_to_memclk_config(uint16_t freq)
 {
 	if (is_fam15h())
@@ -2827,12 +2834,14 @@ restartinit:
 		InterleaveNodes_D(pMCTstat, pDCTstatA);
 		InterleaveChannels_D(pMCTstat, pDCTstatA);
 
-		printk(BIOS_DEBUG, "mctAutoInitMCT_D: ECCInit_D\n");
-		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);
+		if (is_ecc_enabled(pMCTstat)) {
+			printk(BIOS_DEBUG, "mctAutoInitMCT_D: ECCInit_D\n");
+			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);
+			}
 		}
 
 		if (is_fam15h()) {
@@ -3031,7 +3040,6 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
 		uint16_t cdd_trwtto_we_delta;
 		uint8_t receiver;
 		uint8_t max_lane;
-		uint8_t ecc_enabled;
 		uint8_t x4_present = 0;
 		uint8_t x8_present = 0;
 		uint8_t memclk_index;
@@ -3056,8 +3064,6 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
 		uint8_t buffer_data_delay;
 		int16_t latency_difference;
 		uint16_t difference;
-		uint16_t current_total_delay_1[MAX_BYTE_LANES];
-		uint16_t current_total_delay_2[MAX_BYTE_LANES];
 		uint8_t ddr_voltage_index;
 		uint8_t max_dimms_installable;
 
@@ -3074,12 +3080,14 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
 		ddr_voltage_index = dct_ddr_voltage_index(pDCTstat, dct);
 		max_dimms_installable = mctGet_NVbits(NV_MAX_DIMMS_PER_CH);
 
-		ecc_enabled = !!(pMCTstat->GStatus & 1 << GSB_ECCDIMMs);
-		if (ecc_enabled)
+		if (is_ecc_enabled(pMCTstat))
 			max_lane = 9;
 		else
 			max_lane = 8;
 
+		uint16_t current_total_delay_1[max_lane];
+		uint16_t current_total_delay_2[max_lane];
+
 		if (pDCTstat->Dimmx4Present & ((dct)?0xaa:0x55))
 			x4_present = 1;
 		if (pDCTstat->Dimmx8Present & ((dct)?0xaa:0x55))
@@ -3141,7 +3149,7 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
 			if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, receiver))
 				continue;
 
-			read_dqs_receiver_enable_control_registers(current_total_delay_2, dev, dct, dimm, index_reg);
+			read_dqs_receiver_enable_control_registers(current_total_delay_2, dev, dct, dimm, index_reg, max_lane);
 
 			if (first_dimm) {
 				memcpy(current_total_delay_1, current_total_delay_2, sizeof(current_total_delay_1));
@@ -3187,7 +3195,7 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
 			if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, receiver))
 				continue;
 
-			read_dqs_write_timing_control_registers(current_total_delay_2, dev, dct, dimm, index_reg);
+			read_dqs_write_timing_control_registers(current_total_delay_2, dev, dct, dimm, index_reg, max_lane);
 
 			if (first_dimm) {
 				memcpy(current_total_delay_1, current_total_delay_2, sizeof(current_total_delay_1));
@@ -3382,8 +3390,8 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
 			if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, receiver))
 				continue;
 
-			read_dqs_write_timing_control_registers(current_total_delay_1, dev, dct, dimm, index_reg);
-			read_dqs_receiver_enable_control_registers(current_total_delay_2, dev, dct, dimm, index_reg);
+			read_dqs_write_timing_control_registers(current_total_delay_1, dev, dct, dimm, index_reg, max_lane);
+			read_dqs_receiver_enable_control_registers(current_total_delay_2, dev, dct, dimm, index_reg, max_lane);
 
 			for (lane = 0; lane < max_lane; lane++) {
 				if (current_total_delay_1[lane] > current_total_delay_2[lane])
@@ -3436,8 +3444,8 @@ static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat,
 			if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, receiver))
 				continue;
 
-			read_dqs_receiver_enable_control_registers(current_total_delay_1, dev, dct, dimm, index_reg);
-			read_dqs_write_timing_control_registers(current_total_delay_2, dev, dct, dimm, index_reg);
+			read_dqs_receiver_enable_control_registers(current_total_delay_1, dev, dct, dimm, index_reg, max_lane);
+			read_dqs_write_timing_control_registers(current_total_delay_2, dev, dct, dimm, index_reg, max_lane);
 
 			for (lane = 0; lane < max_lane; lane++) {
 				if (current_total_delay_1[lane] > current_total_delay_2[lane])
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
index 19a7acb..9f9d95f 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
@@ -15,10 +15,10 @@
  */
 
 static void write_dqs_receiver_enable_control_registers(uint16_t* current_total_delay,
-			uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg);
+			uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg, u8 max_lane);
 
 static void read_read_dqs_timing_control_registers(uint16_t* current_total_delay,
-			uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg);
+			uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg, u8 max_lane);
 
 static void dqsTrainMaxRdLatency_SW_Fam15(struct MCTStatStruc *pMCTstat,
 				struct DCTStatStruc *pDCTstat);
@@ -931,8 +931,6 @@ static void Calc_SetMaxRdLatency_D_Fam15(struct MCTStatStruc *pMCTstat,
 	uint32_t p = 0;
 	uint32_t n = 0;
 	uint32_t t = 0;
-	uint16_t current_phy_phase_delay[MAX_BYTE_LANES];
-	uint16_t current_read_dqs_delay[MAX_BYTE_LANES];
 
 	uint32_t index_reg = 0x98;
 	uint32_t dev = pDCTstat->dev_dct;
@@ -942,6 +940,15 @@ static void Calc_SetMaxRdLatency_D_Fam15(struct MCTStatStruc *pMCTstat,
 	printk(BIOS_DEBUG, "%s: Start\n", __func__);
 #endif
 
+	u8 max_lane;
+	if (is_ecc_enabled(pMCTstat))
+		max_lane = 9;
+	else
+		max_lane = 8;
+
+	uint16_t current_phy_phase_delay[max_lane];
+	uint16_t current_read_dqs_delay[max_lane];
+
 	mem_clk = Get_NB32_DCT(dev, dct, 0x94) & 0x1f;
 	if (fam15h_freq_tab[mem_clk] == 0) {
 		pDCTstat->CH_MaxRdLat[dct] = 0x55;
@@ -981,9 +988,9 @@ static void Calc_SetMaxRdLatency_D_Fam15(struct MCTStatStruc *pMCTstat,
 			if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, dimm * 2))
 				continue;
 
-			read_dqs_receiver_enable_control_registers(current_phy_phase_delay, dev, dct, dimm, index_reg);
-			read_read_dqs_timing_control_registers(current_read_dqs_delay, dev, dct, dimm, index_reg);
-			for (lane = 0; lane < MAX_BYTE_LANES; lane++)
+			read_dqs_receiver_enable_control_registers(current_phy_phase_delay, dev, dct, dimm, index_reg, max_lane);
+			read_read_dqs_timing_control_registers(current_read_dqs_delay, dev, dct, dimm, index_reg, max_lane);
+			for (lane = 0; lane < max_lane; lane++)
 				if ((current_phy_phase_delay[lane] + current_read_dqs_delay[lane]) > max_delay)
 					max_delay = (current_phy_phase_delay[lane] + current_read_dqs_delay[lane]);
 		}
@@ -1318,13 +1325,6 @@ static uint8_t TrainDQSRdWrPos_D_Fam15(struct MCTStatStruc *pMCTstat,
 	uint8_t write_iter;
 	uint8_t read_iter;
 	uint8_t check_antiphase;
-	uint16_t initial_write_dqs_delay[MAX_BYTE_LANES];
-	uint16_t initial_read_dqs_delay[MAX_BYTE_LANES];
-	uint16_t initial_write_data_timing[MAX_BYTE_LANES];
-	uint16_t current_write_data_delay[MAX_BYTE_LANES];
-	uint16_t current_read_dqs_delay[MAX_BYTE_LANES];
-	uint16_t current_write_dqs_delay[MAX_BYTE_LANES];
-	uint8_t passing_dqs_delay_found[MAX_BYTE_LANES];
 	uint8_t dqs_results_array[2][(lane_end - lane_start)][32][48];		/* [rank][lane][write step][read step + 16] */
 
 	uint8_t last_pos = 0;
@@ -1335,6 +1335,20 @@ static uint8_t TrainDQSRdWrPos_D_Fam15(struct MCTStatStruc *pMCTstat,
 	uint32_t index_reg = 0x98;
 	uint32_t dev = pDCTstat->dev_dct;
 
+	u8 max_lane;
+	if (is_ecc_enabled(pMCTstat))
+		max_lane = 9;
+	else
+		max_lane = 8;
+
+	uint16_t initial_write_dqs_delay[max_lane];
+	uint16_t initial_read_dqs_delay[max_lane];
+	uint16_t initial_write_data_timing[max_lane];
+	uint16_t current_write_data_delay[max_lane];
+	uint16_t current_read_dqs_delay[max_lane];
+	uint16_t current_write_dqs_delay[max_lane];
+	uint8_t passing_dqs_delay_found[max_lane];
+
 	/* Calculate and program MaxRdLatency */
 	Calc_SetMaxRdLatency_D_Fam15(pMCTstat, pDCTstat, dct, 0);
 
@@ -1374,7 +1388,7 @@ static uint8_t TrainDQSRdWrPos_D_Fam15(struct MCTStatStruc *pMCTstat,
 			memset(dqs_results_array, 0, sizeof(dqs_results_array));
 
 			/* Read initial read / write DQS delays */
-			read_dqs_write_timing_control_registers(initial_write_dqs_delay, dev, dct, dimm, index_reg);
+			read_dqs_write_timing_control_registers(initial_write_dqs_delay, dev, dct, dimm, index_reg, max_lane);
 			read_dqs_read_data_timing_registers(initial_read_dqs_delay, dev, dct, dimm, index_reg);
 
 			/* Read current settings of other (previously trained) lanes */
@@ -1665,15 +1679,22 @@ static void TrainDQSReceiverEnCyc_D_Fam15(struct MCTStatStruc *pMCTstat,
 	uint32_t dword;
 	uint32_t rx_en_offset;
 	uint8_t dct_training_success;
-	uint16_t initial_phy_phase_delay[MAX_BYTE_LANES];
-	uint16_t current_phy_phase_delay[MAX_BYTE_LANES];
-	uint8_t lane_training_success[MAX_BYTE_LANES];
 	uint8_t dqs_results_array[1024];
 
  	uint16_t ren_step = 0x40;
 	uint32_t index_reg = 0x98;
 	uint32_t dev = pDCTstat->dev_dct;
 
+	u8 max_lane;
+	if (is_ecc_enabled(pMCTstat))
+		max_lane = 9;
+	else
+		max_lane = 8;
+
+	uint16_t initial_phy_phase_delay[max_lane];
+	uint16_t current_phy_phase_delay[max_lane];
+	uint8_t lane_training_success[max_lane];
+
 	print_debug_dqs("\nTrainDQSReceiverEnCyc: Node_ID ", pDCTstat->Node_ID, 0);
 	cr4 = read_cr4();
 	if (cr4 & (1<<9)) {
@@ -1723,13 +1744,13 @@ static void TrainDQSReceiverEnCyc_D_Fam15(struct MCTStatStruc *pMCTstat,
 				continue;
 			}
 
-			for (lane = 0; lane < MAX_BYTE_LANES; lane++)
+			for (lane = 0; lane < max_lane; lane++)
 				lane_training_success[lane] = 0;
 
 			/* 2.10.5.8.3 (2) */
-			read_dqs_receiver_enable_control_registers(initial_phy_phase_delay, dev, dct, dimm, index_reg);
+			read_dqs_receiver_enable_control_registers(initial_phy_phase_delay, dev, dct, dimm, index_reg, max_lane);
 
-			for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+			for (lane = 0; lane < max_lane; lane++) {
 				/* Initialize variables */
 				memset(dqs_results_array, 0, sizeof(dqs_results_array));
 
@@ -1751,7 +1772,7 @@ static void TrainDQSReceiverEnCyc_D_Fam15(struct MCTStatStruc *pMCTstat,
 #endif
 
 					/* 2.10.5.8.3 (4 A) */
-					write_dqs_receiver_enable_control_registers(current_phy_phase_delay, dev, dct, dimm, index_reg);
+					write_dqs_receiver_enable_control_registers(current_phy_phase_delay, dev, dct, dimm, index_reg, max_lane);
 
 					/* Calculate and program MaxRdLatency */
 					Calc_SetMaxRdLatency_D_Fam15(pMCTstat, pDCTstat, dct, 0);
@@ -1796,7 +1817,7 @@ static void TrainDQSReceiverEnCyc_D_Fam15(struct MCTStatStruc *pMCTstat,
 						current_phy_phase_delay[lane] -= 0x10;
 
 						/* Update hardware registers with final values */
-						write_dqs_receiver_enable_control_registers(current_phy_phase_delay, dev, dct, dimm, index_reg);
+						write_dqs_receiver_enable_control_registers(current_phy_phase_delay, dev, dct, dimm, index_reg, max_lane);
 						break;
 					}
 					prev = dqs_results_array[current_phy_phase_delay[lane]];
@@ -1817,7 +1838,7 @@ static void TrainDQSReceiverEnCyc_D_Fam15(struct MCTStatStruc *pMCTstat,
 
 #if DQS_TRAIN_DEBUG > 0
 			printk(BIOS_DEBUG, "TrainDQSReceiverEnCyc_D_Fam15 DQS receiver enable timing: ");
-			for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+			for (lane = 0; lane < max_lane; lane++) {
 				printk(BIOS_DEBUG, " %03x", current_phy_phase_delay[lane]);
 			}
 			printk(BIOS_DEBUG, "\n");
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
index b8d89fe..9e3bed8 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
@@ -236,12 +236,12 @@ static uint16_t fam15_receiver_enable_training_seed(struct DCTStatStruc *pDCTsta
 	return seed;
 }
 
-static void read_dqs_write_timing_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
+static void read_dqs_write_timing_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg, u8 max_lane)
 {
 	uint8_t lane;
 	uint32_t dword;
 
-	for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+	for (lane = 0; lane < max_lane; lane++) {
 		uint32_t wdt_reg;
 		if ((lane == 0) || (lane == 1))
 			wdt_reg = 0x30;
@@ -295,12 +295,12 @@ static void write_dqs_write_timing_control_registers(uint16_t* current_total_del
 }
 #endif
 
-static void write_write_data_timing_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
+static void write_write_data_timing_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg, u8 max_lane)
 {
 	uint8_t lane;
 	uint32_t dword;
 
-	for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+	for (lane = 0; lane < max_lane; lane++) {
 		uint32_t wdt_reg;
 
 		/* Calculate Write Data Timing register location */
@@ -334,7 +334,7 @@ static void write_write_data_timing_control_registers(uint16_t* current_total_de
 	}
 }
 
-static void read_dqs_receiver_enable_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
+static void read_dqs_receiver_enable_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg, u8 max_lane)
 {
 	uint8_t lane;
 	uint32_t mask;
@@ -345,7 +345,7 @@ static void read_dqs_receiver_enable_control_registers(uint16_t* current_total_d
 	else
 		mask = 0x1ff;
 
-	for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+	for (lane = 0; lane < max_lane; lane++) {
 		uint32_t ret_reg;
 		if ((lane == 0) || (lane == 1))
 			ret_reg = 0x10;
@@ -368,7 +368,7 @@ static void read_dqs_receiver_enable_control_registers(uint16_t* current_total_d
 	}
 }
 
-static void write_dqs_receiver_enable_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
+static void write_dqs_receiver_enable_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg, u8 max_lane)
 {
 	uint8_t lane;
 	uint32_t mask;
@@ -379,7 +379,7 @@ static void write_dqs_receiver_enable_control_registers(uint16_t* current_total_
 	else
 		mask = 0x1ff;
 
-	for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+	for (lane = 0; lane < max_lane; lane++) {
 		uint32_t ret_reg;
 		if ((lane == 0) || (lane == 1))
 			ret_reg = 0x10;
@@ -405,12 +405,12 @@ static void write_dqs_receiver_enable_control_registers(uint16_t* current_total_
 	}
 }
 
-static void read_dram_phase_recovery_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
+static void read_dram_phase_recovery_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg, u8 max_lane)
 {
 	uint8_t lane;
 	uint32_t dword;
 
-	for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+	for (lane = 0; lane < max_lane; lane++) {
 		uint32_t prc_reg;
 
 		/* Calculate DRAM Phase Recovery Control register location */
@@ -437,12 +437,12 @@ static void read_dram_phase_recovery_control_registers(uint16_t* current_total_d
 	}
 }
 
-static void write_dram_phase_recovery_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
+static void write_dram_phase_recovery_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg, u8 max_lane)
 {
 	uint8_t lane;
 	uint32_t dword;
 
-	for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+	for (lane = 0; lane < max_lane; lane++) {
 		uint32_t prc_reg;
 
 		/* Calculate DRAM Phase Recovery Control register location */
@@ -475,12 +475,12 @@ static void write_dram_phase_recovery_control_registers(uint16_t* current_total_
 	}
 }
 
-static void read_read_dqs_timing_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg)
+static void read_read_dqs_timing_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg, u8 max_lane)
 {
 	uint8_t lane;
 	uint32_t dword;
 
-	for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+	for (lane = 0; lane < max_lane; lane++) {
 		uint32_t rdt_reg;
 
 		/* Calculate DRAM Read DQS Timing register location */
@@ -646,14 +646,14 @@ static void dqsTrainRcvrEn_SW_Fam10(struct MCTStatStruc *pMCTstat,
 			/* 2.8.9.9.2 (1, 6)
 			 * Retrieve gross and fine timing fields from write DQS registers
 			 */
-			read_dqs_write_timing_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+			read_dqs_write_timing_control_registers(current_total_delay, dev, Channel, dimm, index_reg, MAX_BYTE_LANES);
 
 			/* 2.8.9.9.2 (1)
 			 * Program the Write Data Timing and Write ECC Timing register to
 			 * the values stored in the DQS Write Timing Control register
 			 * for each lane
 			 */
-			write_write_data_timing_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+			write_write_data_timing_control_registers(current_total_delay, dev, Channel, dimm, index_reg, MAX_BYTE_LANES);
 
 			/* 2.8.9.9.2 (2)
 			 * Program the Read DQS Timing Control and the Read DQS ECC Timing Control registers
@@ -721,7 +721,7 @@ static void dqsTrainRcvrEn_SW_Fam10(struct MCTStatStruc *pMCTstat,
 			/* 2.8.9.9.2 (6)
 			 * Write gross and fine timing fields to read DQS registers
 			 */
-			write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+			write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg, MAX_BYTE_LANES);
 
 			/* 2.8.9.9.2 (7)
 			 * Loop over all delay values up to 1 MEMCLK (0x40 delay steps) from the initial delay values
@@ -765,17 +765,17 @@ static void dqsTrainRcvrEn_SW_Fam10(struct MCTStatStruc *pMCTstat,
 						 */
 						proc_IOCLFLUSH_D((rank == 0)?TestAddr0B:TestAddr1B);
 						result_qword2 = read64_fs(convert_testaddr_and_channel_to_address(pDCTstat, (rank == 0)?TestAddr0B:TestAddr1B, Channel));
-						write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+						write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg, MAX_BYTE_LANES);
 						proc_IOCLFLUSH_D((rank == 0)?TestAddr0:TestAddr1);
 						result_qword1 = read64_fs(convert_testaddr_and_channel_to_address(pDCTstat, (rank == 0)?TestAddr0:TestAddr1, Channel));
-						write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+						write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg, MAX_BYTE_LANES);
 					} else {
 						proc_IOCLFLUSH_D((rank == 0)?TestAddr0:TestAddr1);
 						result_qword1 = read64_fs(convert_testaddr_and_channel_to_address(pDCTstat, (rank == 0)?TestAddr0:TestAddr1, Channel));
-						write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+						write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg, MAX_BYTE_LANES);
 						proc_IOCLFLUSH_D((rank == 0)?TestAddr0B:TestAddr1B);
 						result_qword2 = read64_fs(convert_testaddr_and_channel_to_address(pDCTstat, (rank == 0)?TestAddr0B:TestAddr1B, Channel));
-						write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+						write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg, MAX_BYTE_LANES);
 					}
 					/* 2.8.9.9.2 (7 A e)
 					 * Compare both read patterns and flag passing ranks/lanes
@@ -864,7 +864,7 @@ static void dqsTrainRcvrEn_SW_Fam10(struct MCTStatStruc *pMCTstat,
 				}
 
 				/* Update delays in hardware */
-				write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+				write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg, MAX_BYTE_LANES);
 
 				/* Save previous results for comparison in the next iteration */
 				for (lane = 0; lane < 8; lane++)
@@ -1148,18 +1148,26 @@ static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
 	uint8_t mem_clk;
 	uint16_t initial_seed;
 	uint8_t train_both_nibbles;
-	uint16_t current_total_delay[MAX_BYTE_LANES];
-	uint16_t dqs_ret_pass1_total_delay[MAX_BYTE_LANES];
-	uint16_t rank0_current_total_delay[MAX_BYTE_LANES];
-	uint16_t phase_recovery_delays[MAX_BYTE_LANES];
-	uint16_t seed[MAX_BYTE_LANES];
-	uint16_t seed_gross[MAX_BYTE_LANES];
-	uint16_t seed_fine[MAX_BYTE_LANES];
-	uint16_t seed_pre_gross[MAX_BYTE_LANES];
 
 	uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
 	uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933};
 
+	u8 max_lane;
+	u8 ecc_enabled = !!(pMCTstat->GStatus & 1 << GSB_ECCDIMMs);
+	if (ecc_enabled)
+		max_lane = 9;
+	else
+		max_lane = 8;
+
+	uint16_t current_total_delay[max_lane];
+	uint16_t dqs_ret_pass1_total_delay[max_lane];
+	uint16_t rank0_current_total_delay[max_lane];
+	uint16_t phase_recovery_delays[max_lane];
+	uint16_t seed[max_lane];
+	uint16_t seed_gross[max_lane];
+	uint16_t seed_fine[max_lane];
+	uint16_t seed_pre_gross[max_lane];
+
 	print_debug_dqs("\nTrainRcvEn: Node", pDCTstat->Node_ID, 0);
 	print_debug_dqs("TrainRcvEn: Pass", Pass, 0);
 
@@ -1239,7 +1247,7 @@ static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
 
 			/* Retrieve the total delay values from pass 1 of DQS receiver enable training */
 			if (Pass != FirstPass) {
-				read_dqs_receiver_enable_control_registers(dqs_ret_pass1_total_delay, dev, Channel, dimm, index_reg);
+				read_dqs_receiver_enable_control_registers(dqs_ret_pass1_total_delay, dev, Channel, dimm, index_reg, max_lane);
 			}
 
 			/* 2.10.5.8.2
@@ -1264,7 +1272,7 @@ static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
 					/* 2.10.5.8.2 (2)
 					 * Retrieve gross and fine timing fields from write DQS registers
 					 */
-					read_dqs_write_timing_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+					read_dqs_write_timing_control_registers(current_total_delay, dev, Channel, dimm, index_reg, max_lane);
 
 					/* 2.10.5.8.2.1
 					 * Generate the DQS Receiver Enable Training Seed Values
@@ -1276,7 +1284,7 @@ static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
 						initial_seed = (uint16_t) (((((uint64_t) initial_seed) *
 							fam15h_freq_tab[mem_clk] * 100) / (mctGet_NVbits(NV_MIN_MEMCLK) * 100)));
 
-						for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+						for (lane = 0; lane < max_lane; lane++) {
 							uint16_t wl_pass1_delay;
 							wl_pass1_delay = current_total_delay[lane];
 
@@ -1302,13 +1310,13 @@ static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
 							register_delay = 0x0;
 						}
 
-						for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+						for (lane = 0; lane < max_lane; lane++) {
 							seed_prescaling = current_total_delay[lane] - register_delay - 0x20;
 							seed[lane] = (uint16_t) (register_delay + ((((uint64_t) seed_prescaling) * fam15h_freq_tab[mem_clk] * 100) / (mctGet_NVbits(NV_MIN_MEMCLK) * 100)));
 						}
 					}
 
-					for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+					for (lane = 0; lane < max_lane; lane++) {
 						seed_gross[lane] = (seed[lane] >> 5) & 0x1f;
 						seed_fine[lane] = seed[lane] & 0x1f;
 
@@ -1332,12 +1340,12 @@ static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
 					/* 2.10.5.8.2 (2) / 2.10.5.8.2.1 (5 6)
 					 * Program PhRecFineDly and PhRecGrossDly
 					 */
-					write_dram_phase_recovery_control_registers(phase_recovery_delays, dev, Channel, dimm, index_reg);
+					write_dram_phase_recovery_control_registers(phase_recovery_delays, dev, Channel, dimm, index_reg, max_lane);
 
 					/* 2.10.5.8.2 (2) / 2.10.5.8.2.1 (7)
 					 * Program the DQS Receiver Enable delay values for each lane
 					 */
-					write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+					write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg, max_lane);
 
 					/* 2.10.5.8.2 (3)
 					 * Program DqsRcvTrEn = 1
@@ -1361,12 +1369,12 @@ static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
 					/* 2.10.5.8.2 (6)
 					 * Read PhRecGrossDly, PhRecFineDly
 					 */
-					read_dram_phase_recovery_control_registers(phase_recovery_delays, dev, Channel, dimm, index_reg);
+					read_dram_phase_recovery_control_registers(phase_recovery_delays, dev, Channel, dimm, index_reg, max_lane);
 
 					/* 2.10.5.8.2 (7)
 					 * Calculate and program the DQS Receiver Enable delay values
 					 */
-					for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+					for (lane = 0; lane < max_lane; lane++) {
 						current_total_delay[lane] = (phase_recovery_delays[lane] & 0x1f);
 						current_total_delay[lane] |= ((seed_gross[lane] + ((phase_recovery_delays[lane] >> 5) & 0x1f) - seed_pre_gross[lane] + 1) << 5);
 						if (nibble == 0) {
@@ -1390,7 +1398,7 @@ static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
 						printk(BIOS_DEBUG, "\t\tTrainRcvEn55: Channel: %d dimm: %d nibble: %d lane %d current_total_delay: %04x CH_D_B_RCVRDLY: %04x\n",
 							Channel, dimm, nibble, lane, current_total_delay[lane], pDCTstat->CH_D_B_RCVRDLY[Channel][dimm][lane]);
 #endif
-					write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+					write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg, max_lane);
 				}
 
 				if (rank == 0) {
@@ -1403,14 +1411,14 @@ static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
 					 * Compute the average delay across both ranks and program the result into
 					 * the DQS Receiver Enable delay registers
 					 */
-					for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+					for (lane = 0; lane < max_lane; lane++) {
 						current_total_delay[lane] = (rank0_current_total_delay[lane] + current_total_delay[lane]) / 2;
 						if (lane == 8)
 							pDCTstat->CH_D_BC_RCVRDLY[Channel][dimm] = current_total_delay[lane];
 						else
 							pDCTstat->CH_D_B_RCVRDLY[Channel][dimm][lane] = current_total_delay[lane];
 					}
-					write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
+					write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg, max_lane);
 				}
 			}
 
@@ -1526,11 +1534,19 @@ static void dqsTrainMaxRdLatency_SW_Fam15(struct MCTStatStruc *pMCTstat,
 	uint8_t mem_clk;
 	uint32_t nb_clk;
 	uint8_t nb_pstate;
-	uint16_t current_total_delay[MAX_BYTE_LANES];
-	uint16_t current_rdqs_total_delay[MAX_BYTE_LANES];
 	uint8_t current_worst_case_total_delay_dimm;
 	uint16_t current_worst_case_total_delay_value;
 
+	u8 max_lane;
+	u8 ecc_enabled = !!(pMCTstat->GStatus & 1 << GSB_ECCDIMMs);
+	if (ecc_enabled)
+		max_lane = 9;
+	else
+		max_lane = 8;
+
+	uint16_t current_total_delay[max_lane];
+	uint16_t current_rdqs_total_delay[max_lane];
+
 	uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933};
 
 	print_debug_dqs("\nTrainMaxRdLatency: Node", pDCTstat->Node_ID, 0);
@@ -1592,8 +1608,8 @@ static void dqsTrainMaxRdLatency_SW_Fam15(struct MCTStatStruc *pMCTstat,
 			}
 
 			/* Retrieve the total delay values from pass 1 of DQS receiver enable training */
-			read_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg);
-			read_read_dqs_timing_control_registers(current_rdqs_total_delay, dev, Channel, dimm, index_reg);
+			read_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg, max_lane);
+			read_read_dqs_timing_control_registers(current_rdqs_total_delay, dev, Channel, dimm, index_reg, max_lane);
 
 			for (lane = 0; lane < 8; lane++) {
 				current_total_delay[lane] += current_rdqs_total_delay[lane];
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
index 2b8a997..258da21 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
@@ -32,8 +32,8 @@ void prepareDimms(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat,
 	u8 dct, u8 dimm, BOOL wl);
 void programODT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, u8 dimm);
 void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t dimm, uint8_t pass, uint8_t nibble);
-void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, u8 targetAddr, uint8_t pass);
-void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, uint8_t pass, uint8_t nibble);
+void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, u8 targetAddr, uint8_t pass, u8 max_lane);
+void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, uint8_t pass, uint8_t nibble, u8 max_lane);
 
 static int32_t abs(int32_t val) {
 	if (val < 0)
@@ -77,6 +77,12 @@ uint8_t AgesaHwWlPhase1(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCT
 	u16 Addl_Data_Offset, Addl_Data_Port;
 	sMCTStruct *pMCTData = pDCTstat->C_MCTPtr;
 	sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+	uint8_t max_lane;
+
+	if (is_ecc_enabled(pMCTstat))
+		max_lane = 9;
+	else
+		max_lane = 8;
 
 	pDCTData->WLPass = pass;
 	/* 1. Specify the target DIMM that is to be trained by programming
@@ -176,8 +182,8 @@ uint8_t AgesaHwWlPhase1(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCT
 		/* Read from registers F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52
 		 * to get the gross and fine delay settings
 		 * for the target DIMM and save these values. */
-		for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
-			getWLByteDelay(pDCTstat, dct, ByteLane, dimm, pass, nibble);
+		for (ByteLane = 0; ByteLane < max_lane; ByteLane++) {
+			getWLByteDelay(pDCTstat, dct, ByteLane, dimm, pass, nibble, max_lane);
 		}
 
 		pDCTData->WLCriticalGrossDelayPrevPass = 0x0;
@@ -192,11 +198,17 @@ uint8_t AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCT
 	u8 ByteLane;
 	uint8_t status = 0;
 	sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+	uint8_t max_lane;
+
+	if (is_ecc_enabled(pMCTstat))
+		max_lane = 9;
+	else
+		max_lane = 8;
 
 	if (is_fam15h()) {
-		int32_t gross_diff[MAX_BYTE_LANES];
+		int32_t gross_diff[max_lane];
 		int32_t cgd = pDCTData->WLCriticalGrossDelayPrevPass;
-		uint8_t index = (uint8_t)(MAX_BYTE_LANES * dimm);
+		uint8_t index = (uint8_t)(max_lane * dimm);
 
 		printk(BIOS_SPEW, "\toriginal critical gross delay: %d\n", cgd);
 
@@ -205,7 +217,7 @@ uint8_t AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCT
 		 */
 
 		/* Calculate the Critical Gross Delay */
-		for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
+		for (ByteLane = 0; ByteLane < max_lane; ByteLane++) {
 			/* Calculate the gross delay differential for this lane */
 			gross_diff[ByteLane] = pDCTData->WLSeedGrossDelay[index+ByteLane] + pDCTData->WLGrossDelay[index+ByteLane];
 			gross_diff[ByteLane] -= pDCTData->WLSeedPreGrossDelay[index+ByteLane];
@@ -231,7 +243,7 @@ uint8_t AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCT
 			 * Figure out why this is and fix it, then remove the bypass code below...
 			 */
 			if (pass == FirstPass) {
-				for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
+				for (ByteLane = 0; ByteLane < max_lane; ByteLane++) {
 					pDCTData->WLGrossDelay[index+ByteLane] = pDCTData->WLSeedGrossDelay[index+ByteLane];
 					pDCTData->WLFineDelay[index+ByteLane] = pDCTData->WLSeedFineDelay[index+ByteLane];
 				}
@@ -240,7 +252,7 @@ uint8_t AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCT
 		}
 
 		/* Compensate for occasional noise/instability causing sporadic training failure */
-		for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
+		for (ByteLane = 0; ByteLane < max_lane; ByteLane++) {
 			uint8_t faulty_value_detected = 0;
 			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);
@@ -278,12 +290,18 @@ uint8_t AgesaHwWlPhase3(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCT
 	u8 ByteLane;
 	sMCTStruct *pMCTData = pDCTstat->C_MCTPtr;
 	sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
+	u8 max_lane;
+
+	if (is_ecc_enabled(pMCTstat))
+		max_lane = 9;
+	else
+		max_lane = 8;
 
 	if (is_fam15h()) {
 		uint32_t dword;
-		int32_t gross_diff[MAX_BYTE_LANES];
+		int32_t gross_diff[max_lane];
 		int32_t cgd = pDCTData->WLCriticalGrossDelayPrevPass;
-		uint8_t index = (uint8_t)(MAX_BYTE_LANES * dimm);
+		uint8_t index = (uint8_t)(max_lane * dimm);
 
 		/* Apply offset(s) if needed */
 		if (cgd < 0) {
@@ -292,7 +310,7 @@ uint8_t AgesaHwWlPhase3(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCT
 			dword |= ((abs(cgd) & 0x3) << 24);
 			Set_NB32_DCT(pDCTstat->dev_dct, dct, 0xa8, dword);
 
-			for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
+			for (ByteLane = 0; ByteLane < max_lane; ByteLane++) {
 				/* Calculate the gross delay differential for this lane */
 				gross_diff[ByteLane] = pDCTData->WLSeedGrossDelay[index+ByteLane] + pDCTData->WLGrossDelay[index+ByteLane];
 				gross_diff[ByteLane] -= pDCTData->WLSeedPreGrossDelay[index+ByteLane];
@@ -313,8 +331,8 @@ uint8_t AgesaHwWlPhase3(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCT
 
 	/* Write the adjusted gross and fine delay settings
 	 * to the target DIMM. */
-	for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
-		setWLByteDelay(pDCTstat, dct, ByteLane, dimm, 1, pass);
+	for (ByteLane = 0; ByteLane < max_lane; ByteLane++) {
+		setWLByteDelay(pDCTstat, dct, ByteLane, dimm, 1, pass, max_lane);
 	}
 
 	/* 6. Configure DRAM Phy Control Register so that the phy stops driving
@@ -1006,6 +1024,12 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
 	sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
 	uint16_t fam10h_freq_tab[] = {0, 0, 0, 400, 533, 667, 800};
 	uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933};
+	uint8_t max_lane;
+
+	if (is_ecc_enabled(pMCTstat))
+		max_lane = 9;
+	else
+		max_lane = 8;
 
 	if (is_fam15h()) {
 		/* MemClkFreq: 0x4: 333MHz; 0x6: 400MHz; 0xa: 533MHz; 0xe: 667MHz; 0x12: 800MHz; 0x16: 933MHz */
@@ -1113,9 +1137,9 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
 			Seed_Fine = Seed_Total & 0x1f;
 
 			/* Save seed values for later use */
-			for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
-				pDCTData->WLSeedGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
-				pDCTData->WLSeedFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
+			for (ByteLane = 0; ByteLane < max_lane; ByteLane++) {
+				pDCTData->WLSeedGrossDelay[max_lane*dimm+ByteLane] = Seed_Gross;
+				pDCTData->WLSeedFineDelay[max_lane*dimm+ByteLane] = Seed_Fine;
 
 				if (Seed_Gross == 0)
 					Seed_PreGross = 0;
@@ -1124,7 +1148,7 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
 				else
 					Seed_PreGross = 2;
 
-				pDCTData->WLSeedPreGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross;
+				pDCTData->WLSeedPreGrossDelay[max_lane*dimm+ByteLane] = Seed_PreGross;
 			}
 		} else {
 			if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
@@ -1150,7 +1174,7 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
 				}
 			}
 		}
-		for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++)
+		for (ByteLane = 0; ByteLane < max_lane; ByteLane++)
 		{
 			/* Program an initialization value to registers F2x[1, 0]9C_x[51:50] and
 			 * F2x[1, 0]9C_x52 to set the gross and fine delay for all the byte lane fields
@@ -1160,8 +1184,8 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
 			 * of 01Fh. This represents a 1UI (UI=.5MEMCLK) delay and is determined
 			 * by design.
 			 */
-			pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
-			pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
+			pDCTData->WLGrossDelay[max_lane*dimm+ByteLane] = Seed_Gross;
+			pDCTData->WLFineDelay[max_lane*dimm+ByteLane] = Seed_Fine;
 			printk(BIOS_SPEW, "\tLane %02x initial seed: %04x\n", ByteLane, ((Seed_Gross & 0x1f) << 5) | (Seed_Fine & 0x1f));
 		}
 	} else {
@@ -1170,8 +1194,8 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
 			/* From BKDG, Write Leveling Seed Value. */
 			if (is_fam15h()) {
 				uint32_t RegisterDelay;
-				int32_t SeedTotal[MAX_BYTE_LANES];
-				int32_t SeedTotalPreScaling[MAX_BYTE_LANES];
+				int32_t SeedTotal[max_lane];
+				int32_t SeedTotalPreScaling[max_lane];
 				uint32_t WrDqDqsEarly;
 				uint8_t AddrCmdPrelaunch = 0;		/* TODO: Fetch the correct value from RC2[0] */
 
@@ -1194,17 +1218,17 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
 				WrDqDqsEarly = 0;
 
 				/* Generate new seed values */
-				for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
+				for (ByteLane = 0; ByteLane < max_lane; ByteLane++) {
 					/* Calculate adjusted seed values */
-					SeedTotal[ByteLane] = (pDCTData->WLFineDelayPrevPass[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) |
-						((pDCTData->WLGrossDelayPrevPass[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) << 5);
+					SeedTotal[ByteLane] = (pDCTData->WLFineDelayPrevPass[max_lane*dimm+ByteLane] & 0x1f) |
+						((pDCTData->WLGrossDelayPrevPass[max_lane*dimm+ByteLane] & 0x1f) << 5);
 					SeedTotalPreScaling[ByteLane] = (SeedTotal[ByteLane] - RegisterDelay - (0x20 * WrDqDqsEarly));
 					SeedTotal[ByteLane] = (int32_t) (RegisterDelay + ((((int64_t) SeedTotalPreScaling[ByteLane]) *
 						fam15h_freq_tab[MemClkFreq] * 100) / (fam15h_freq_tab[pDCTData->WLPrevMemclkFreq] * 100)));
 				}
 
 				/* Generate register values from seeds */
-				for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
+				for (ByteLane = 0; ByteLane < max_lane; ByteLane++) {
 					printk(BIOS_SPEW, "\tLane %02x scaled delay: %04x\n", ByteLane, SeedTotal[ByteLane]);
 
 					if (SeedTotal[ByteLane] >= 0) {
@@ -1229,21 +1253,21 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
 					Seed_PreGross = Seed_Gross;
 
 					/* Save seed values for later use */
-					pDCTData->WLSeedGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
-					pDCTData->WLSeedFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
-					pDCTData->WLSeedPreGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross;
+					pDCTData->WLSeedGrossDelay[max_lane*dimm+ByteLane] = Seed_Gross;
+					pDCTData->WLSeedFineDelay[max_lane*dimm+ByteLane] = Seed_Fine;
+					pDCTData->WLSeedPreGrossDelay[max_lane*dimm+ByteLane] = Seed_PreGross;
 
-					pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross;
-					pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
+					pDCTData->WLGrossDelay[max_lane*dimm+ByteLane] = Seed_PreGross;
+					pDCTData->WLFineDelay[max_lane*dimm+ByteLane] = Seed_Fine;
 
-					printk(BIOS_SPEW, "\tLane %02x new seed: %04x\n", ByteLane, ((pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) << 5) | (pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f));
+					printk(BIOS_SPEW, "\tLane %02x new seed: %04x\n", ByteLane, ((pDCTData->WLGrossDelay[max_lane*dimm+ByteLane] & 0x1f) << 5) | (pDCTData->WLFineDelay[max_lane*dimm+ByteLane] & 0x1f));
 				}
 			} else {
 				uint32_t RegisterDelay;
 				uint32_t SeedTotalPreScaling;
 				uint32_t SeedTotal;
 				uint8_t AddrCmdPrelaunch = 0;		/* TODO: Fetch the correct value from RC2[0] */
-				for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++)
+				for (ByteLane = 0; ByteLane < max_lane; ByteLane++)
 				{
 					if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
 						if (AddrCmdPrelaunch == 0)
@@ -1253,8 +1277,8 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
 					} else {
 						RegisterDelay = 0;
 					}
-					SeedTotalPreScaling = ((pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) |
-						(pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] << 5)) - RegisterDelay;
+					SeedTotalPreScaling = ((pDCTData->WLFineDelay[max_lane*dimm+ByteLane] & 0x1f) |
+						(pDCTData->WLGrossDelay[max_lane*dimm+ByteLane] << 5)) - RegisterDelay;
 					/* SeedTotalPreScaling = (the total delay value in F2x[1, 0]9C_x[4A:30] from pass 1 of write levelization
 					training) - RegisterDelay. */
 					SeedTotal = (uint16_t) ((((uint64_t) SeedTotalPreScaling) *
@@ -1277,49 +1301,49 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
 					Seed_Gross = SeedTotal / 32;
 					Seed_Fine = SeedTotal & 0x1f;
 
-					pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
-					pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
+					pDCTData->WLGrossDelay[max_lane*dimm+ByteLane] = Seed_Gross;
+					pDCTData->WLFineDelay[max_lane*dimm+ByteLane] = Seed_Fine;
 
-					printk(BIOS_SPEW, "\tLane %02x new seed: %04x\n", ByteLane, ((pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) << 5) | (pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f));
+					printk(BIOS_SPEW, "\tLane %02x new seed: %04x\n", ByteLane, ((pDCTData->WLGrossDelay[max_lane*dimm+ByteLane] & 0x1f) << 5) | (pDCTData->WLFineDelay[max_lane*dimm+ByteLane] & 0x1f));
 				}
 			}
 
 			/* Save initial seeds for upper nibble pass */
-			for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
-				pDCTData->WLSeedPreGrossPrevNibble[MAX_BYTE_LANES*dimm+ByteLane] = pDCTData->WLSeedPreGrossDelay[MAX_BYTE_LANES*dimm+ByteLane];
-				pDCTData->WLSeedGrossPrevNibble[MAX_BYTE_LANES*dimm+ByteLane] = pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane];
-				pDCTData->WLSeedFinePrevNibble[MAX_BYTE_LANES*dimm+ByteLane] = pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane];
+			for (ByteLane = 0; ByteLane < max_lane; ByteLane++) {
+				pDCTData->WLSeedPreGrossPrevNibble[max_lane*dimm+ByteLane] = pDCTData->WLSeedPreGrossDelay[max_lane*dimm+ByteLane];
+				pDCTData->WLSeedGrossPrevNibble[max_lane*dimm+ByteLane] = pDCTData->WLGrossDelay[max_lane*dimm+ByteLane];
+				pDCTData->WLSeedFinePrevNibble[max_lane*dimm+ByteLane] = pDCTData->WLFineDelay[max_lane*dimm+ByteLane];
 			}
 		} else {
 			/* Restore seed values from lower nibble pass */
 			if (is_fam15h()) {
-				for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
-					pDCTData->WLSeedGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = pDCTData->WLSeedGrossPrevNibble[MAX_BYTE_LANES*dimm+ByteLane];
-					pDCTData->WLSeedFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = pDCTData->WLSeedFinePrevNibble[MAX_BYTE_LANES*dimm+ByteLane];
-					pDCTData->WLSeedPreGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = pDCTData->WLSeedPreGrossPrevNibble[MAX_BYTE_LANES*dimm+ByteLane];
+				for (ByteLane = 0; ByteLane < max_lane; ByteLane++) {
+					pDCTData->WLSeedGrossDelay[max_lane*dimm+ByteLane] = pDCTData->WLSeedGrossPrevNibble[max_lane*dimm+ByteLane];
+					pDCTData->WLSeedFineDelay[max_lane*dimm+ByteLane] = pDCTData->WLSeedFinePrevNibble[max_lane*dimm+ByteLane];
+					pDCTData->WLSeedPreGrossDelay[max_lane*dimm+ByteLane] = pDCTData->WLSeedPreGrossPrevNibble[max_lane*dimm+ByteLane];
 
-					pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = pDCTData->WLSeedPreGrossPrevNibble[MAX_BYTE_LANES*dimm+ByteLane];
-					pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = pDCTData->WLSeedFinePrevNibble[MAX_BYTE_LANES*dimm+ByteLane];
+					pDCTData->WLGrossDelay[max_lane*dimm+ByteLane] = pDCTData->WLSeedPreGrossPrevNibble[max_lane*dimm+ByteLane];
+					pDCTData->WLFineDelay[max_lane*dimm+ByteLane] = pDCTData->WLSeedFinePrevNibble[max_lane*dimm+ByteLane];
 
-					printk(BIOS_SPEW, "\tLane %02x new seed: %04x\n", ByteLane, ((pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) << 5) | (pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f));
+					printk(BIOS_SPEW, "\tLane %02x new seed: %04x\n", ByteLane, ((pDCTData->WLGrossDelay[max_lane*dimm+ByteLane] & 0x1f) << 5) | (pDCTData->WLFineDelay[max_lane*dimm+ByteLane] & 0x1f));
 				}
 			} else {
-				for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
-					pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = pDCTData->WLSeedGrossPrevNibble[MAX_BYTE_LANES*dimm+ByteLane];
-					pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = pDCTData->WLSeedFinePrevNibble[MAX_BYTE_LANES*dimm+ByteLane];
+				for (ByteLane = 0; ByteLane < max_lane; ByteLane++) {
+					pDCTData->WLGrossDelay[max_lane*dimm+ByteLane] = pDCTData->WLSeedGrossPrevNibble[max_lane*dimm+ByteLane];
+					pDCTData->WLFineDelay[max_lane*dimm+ByteLane] = pDCTData->WLSeedFinePrevNibble[max_lane*dimm+ByteLane];
 
-					printk(BIOS_SPEW, "\tLane %02x new seed: %04x\n", ByteLane, ((pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) << 5) | (pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f));
+					printk(BIOS_SPEW, "\tLane %02x new seed: %04x\n", ByteLane, ((pDCTData->WLGrossDelay[max_lane*dimm+ByteLane] & 0x1f) << 5) | (pDCTData->WLFineDelay[max_lane*dimm+ByteLane] & 0x1f));
 				}
 			}
 		}
 	}
 
 	pDCTData->WLPrevMemclkFreq = MemClkFreq;
-	setWLByteDelay(pDCTstat, dct, ByteLane, dimm, 0, pass);
+	setWLByteDelay(pDCTstat, dct, ByteLane, dimm, 0, pass, max_lane);
 }
 
 /*-----------------------------------------------------------------------------
- *  void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 Dimm){
+ *  void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 Dimm, u8 max_lane){
  *
  *  Description:
  *       This function writes the write levelization byte delay for the Phase
@@ -1339,7 +1363,7 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
  *
  *-----------------------------------------------------------------------------
  */
-void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, u8 targetAddr, uint8_t pass)
+void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, u8 targetAddr, uint8_t pass, u8 max_lane)
 {
 	sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
 	u8 fineStartLoc, fineEndLoc, grossStartLoc, grossEndLoc, tempB, index, offsetAddr;
@@ -1347,12 +1371,12 @@ void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8
 
 	if (targetAddr == 0)
 	{
-		index = (u8)(MAX_BYTE_LANES * dimm);
+		index = (u8)(max_lane * dimm);
 		ValueLow = 0;
 		ValueHigh = 0;
 		ByteLane = 0;
 		EccValue = 0;
-		while (ByteLane < MAX_BYTE_LANES)
+		while (ByteLane < max_lane)
 		{
 			if (is_fam15h()) {
 				grossDelayValue = pDCTData->WLGrossDelay[index+ByteLane];
@@ -1394,7 +1418,7 @@ void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8
 	else
 	{
 		/* Fam10h BKDG Rev. 3.62 2.8.9.9.1 (6) */
-		index = (u8)(MAX_BYTE_LANES * dimm);
+		index = (u8)(max_lane * dimm);
 		grossDelayValue = pDCTData->WLGrossDelay[index+ByteLane];
 		fineDelayValue = pDCTData->WLFineDelay[index+ByteLane];
 
@@ -1440,7 +1464,7 @@ void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8
 }
 
 /*-----------------------------------------------------------------------------
- *  void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 Dimm, u8 Nibble)
+ *  void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 Dimm, u8 Nibble, u8 max_lane)
  *
  *  Description:
  *       This function reads the write levelization byte delay from the Phase
@@ -1458,13 +1482,13 @@ void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8
  *
  *-----------------------------------------------------------------------------
  */
-void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, uint8_t pass, uint8_t nibble)
+void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, uint8_t pass, uint8_t nibble, u8 max_lane)
 {
 	sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
 	u8 fineStartLoc, fineEndLoc, grossStartLoc, grossEndLoc, tempB, tempB1, index;
 	u32 addr, fine, gross;
 	tempB = 0;
-	index = (u8)(MAX_BYTE_LANES*dimm);
+	index = (u8)(max_lane*dimm);
 	if (ByteLane < 4) {
 		tempB = (u8)(8 * ByteLane);
 		addr = DRAM_CONT_ADD_PHASE_REC_CTRL_LOW;



More information about the coreboot-gerrit mailing list