nb/amd/mct_ddr3: Fix x4 DIMM receiver enable training on Fam15h
The existing Family 15h receiver enable training code stored temporary delay values in the wrong variables, leading to the requisite averaging of delays across nibbles not being applied. This in turn made x4 DIMMs less stable than they should have been. Store temporary nibble delay values in a dedicated array. Change-Id: Ic5da898af7d689db4110211f89b886ccdbb5f78f Signed-off-by: Timothy Pearson <tpearson@raptorengineeringinc.com> Reviewed-on: https://review.coreboot.org/14541 Tested-by: build bot (Jenkins) Tested-by: Raptor Engineering Automated Test Stand <noreply@raptorengineeringinc.com> Reviewed-by: Martin Roth <martinroth@google.com>
This commit is contained in:
parent
efcee9fadd
commit
7f731f8d4f
|
@ -1194,6 +1194,7 @@ static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
|
||||||
uint16_t initial_seed;
|
uint16_t initial_seed;
|
||||||
uint8_t train_both_nibbles;
|
uint8_t train_both_nibbles;
|
||||||
uint16_t current_total_delay[MAX_BYTE_LANES];
|
uint16_t current_total_delay[MAX_BYTE_LANES];
|
||||||
|
uint16_t nibble0_current_total_delay[MAX_BYTE_LANES];
|
||||||
uint16_t dqs_ret_pass1_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 rank0_current_total_delay[MAX_BYTE_LANES];
|
||||||
uint16_t phase_recovery_delays[MAX_BYTE_LANES];
|
uint16_t phase_recovery_delays[MAX_BYTE_LANES];
|
||||||
|
@ -1419,19 +1420,11 @@ static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
|
||||||
for (lane = 0; lane < lane_count; lane++) {
|
for (lane = 0; lane < lane_count; lane++) {
|
||||||
current_total_delay[lane] = (phase_recovery_delays[lane] & 0x1f);
|
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);
|
current_total_delay[lane] |= ((seed_gross[lane] + ((phase_recovery_delays[lane] >> 5) & 0x1f) - seed_pre_gross[lane] + 1) << 5);
|
||||||
if (nibble == 0) {
|
if (nibble == 1) {
|
||||||
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];
|
|
||||||
} else {
|
|
||||||
/* 2.10.5.8.2 (1)
|
/* 2.10.5.8.2 (1)
|
||||||
* Average the trained values of both nibbles on x4 DIMMs
|
* Average the trained values of both nibbles on x4 DIMMs
|
||||||
*/
|
*/
|
||||||
if (lane == 8)
|
current_total_delay[lane] = (nibble0_current_total_delay[lane] + current_total_delay[lane]) / 2;
|
||||||
pDCTstat->CH_D_BC_RCVRDLY[Channel][dimm] = (pDCTstat->CH_D_BC_RCVRDLY[Channel][dimm] + current_total_delay[lane]) / 2;
|
|
||||||
else
|
|
||||||
pDCTstat->CH_D_B_RCVRDLY[Channel][dimm][lane] = (pDCTstat->CH_D_B_RCVRDLY[Channel][dimm][lane] + current_total_delay[lane]) / 2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1441,26 +1434,40 @@ static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
|
||||||
Channel, dimm, nibble, lane, current_total_delay[lane], pDCTstat->CH_D_B_RCVRDLY[Channel][dimm][lane]);
|
Channel, dimm, nibble, lane, current_total_delay[lane], pDCTstat->CH_D_B_RCVRDLY[Channel][dimm][lane]);
|
||||||
#endif
|
#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);
|
||||||
|
|
||||||
|
if (nibble == 0) {
|
||||||
|
/* Back up the Nibble 0 delays for later use */
|
||||||
|
memcpy(nibble0_current_total_delay, current_total_delay, sizeof(current_total_delay));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rank == 0) {
|
if (_2Ranks) {
|
||||||
/* Back up the Rank 0 delays for later use */
|
if (rank == 0) {
|
||||||
memcpy(rank0_current_total_delay, current_total_delay, sizeof(current_total_delay));
|
/* Back up the Rank 0 delays for later use */
|
||||||
}
|
memcpy(rank0_current_total_delay, current_total_delay, sizeof(current_total_delay));
|
||||||
|
}
|
||||||
if (rank == 1) {
|
if (rank == 1) {
|
||||||
/* 2.10.5.8.2 (8)
|
/* 2.10.5.8.2 (8)
|
||||||
* Compute the average delay across both ranks and program the result into
|
* Compute the average delay across both ranks and program the result into
|
||||||
* the DQS Receiver Enable delay registers
|
* the DQS Receiver Enable delay registers
|
||||||
*/
|
*/
|
||||||
|
for (lane = 0; lane < lane_count; 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);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Save the current delay for later use by other routines */
|
||||||
for (lane = 0; lane < lane_count; lane++) {
|
for (lane = 0; lane < lane_count; lane++) {
|
||||||
current_total_delay[lane] = (rank0_current_total_delay[lane] + current_total_delay[lane]) / 2;
|
|
||||||
if (lane == 8)
|
if (lane == 8)
|
||||||
pDCTstat->CH_D_BC_RCVRDLY[Channel][dimm] = current_total_delay[lane];
|
pDCTstat->CH_D_BC_RCVRDLY[Channel][dimm] = current_total_delay[lane];
|
||||||
else
|
else
|
||||||
pDCTstat->CH_D_B_RCVRDLY[Channel][dimm][lane] = current_total_delay[lane];
|
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue