summaryrefslogtreecommitdiff
path: root/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
diff options
context:
space:
mode:
authorTimothy Pearson <tpearson@raptorengineeringinc.com>2016-01-30 23:34:51 -0600
committerMartin Roth <martinroth@google.com>2016-02-05 22:26:31 +0100
commit31682364ba062fb3cbf4ff3b0ad7cbdb7b5daae1 (patch)
tree0824c0e09657c7fbbdc71c83e9bba0969826f7a9 /src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
parent606b6ec686e07930008c5c1710efaaf3d097f465 (diff)
downloadcoreboot-31682364ba062fb3cbf4ff3b0ad7cbdb7b5daae1.tar.xz
nb/amd/mct_ddr3: Work around RDIMM training failure
Under certain conditions, not elucidated in the BKDG, an extra memclock of CAS write latency is required. The only reliable way I have found to detect when this is required is to try training without the delay, and if DQS position training fails, adding the delay and retraining. This is probably related in some form or another to the badly broken DQS Write Early algorithm given in the BKDG. Change-Id: Idfaca1b3da3f45793d210980e952ccdfc9ba1410 Signed-off-by: Timothy Pearson <tpearson@raptorengineeringinc.com> Reviewed-on: https://review.coreboot.org/13531 Tested-by: build bot (Jenkins) Tested-by: Raptor Engineering Automated Test Stand <noreply@raptorengineeringinc.com> Reviewed-by: Martin Roth <martinroth@google.com>
Diffstat (limited to 'src/northbridge/amd/amdmct/mct_ddr3/mct_d.c')
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mct_d.c70
1 files changed, 68 insertions, 2 deletions
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
index e1c0d4f563..cbe75b60d4 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
@@ -3332,12 +3332,24 @@ static void exit_training_mode_fam15(struct MCTStatStruc *pMCTstat,
static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
struct DCTStatStruc *pDCTstatA, uint8_t allow_config_restore)
{
+ uint8_t Node;
u8 nv_DQSTrainCTL;
+ uint8_t retry_requested;
if (pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW)) {
return;
}
+ /* Set initial TCWL offset to zero */
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ uint8_t dct;
+ struct DCTStatStruc *pDCTstat;
+ pDCTstat = pDCTstatA + Node;
+ for (dct = 0; dct < 2; dct++)
+ pDCTstat->tcwl_delay[dct] = 0;
+ }
+
+retry_dqs_training_and_levelization:
// nv_DQSTrainCTL = mctGet_NVbits(NV_DQSTrainCTL);
nv_DQSTrainCTL = !allow_config_restore;
@@ -3345,7 +3357,6 @@ static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
phyAssistedMemFnceTraining(pMCTstat, pDCTstatA, -1);
if (is_fam15h()) {
- uint8_t Node;
struct DCTStatStruc *pDCTstat;
for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
pDCTstat = pDCTstatA + Node;
@@ -3393,6 +3404,59 @@ static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
mct_TrainDQSPos_D(pMCTstat, pDCTstatA);
+ /* Determine if DQS training requested a retrain attempt */
+ retry_requested = 0;
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ struct DCTStatStruc *pDCTstat;
+ pDCTstat = pDCTstatA + Node;
+
+ if (pDCTstat->NodePresent) {
+ if (pDCTstat->TrainErrors & (1 << SB_FatalError)) {
+ die("DIMM training FAILED! Halting system.");
+ }
+ if (pDCTstat->TrainErrors & (1 << SB_RetryConfigTrain)) {
+ retry_requested = 1;
+
+ /* Clear previous errors */
+ pDCTstat->TrainErrors &= ~(1 << SB_RetryConfigTrain);
+ pDCTstat->TrainErrors &= ~(1 << SB_NODQSPOS);
+ pDCTstat->ErrStatus &= ~(1 << SB_RetryConfigTrain);
+ pDCTstat->ErrStatus &= ~(1 << SB_NODQSPOS);
+ }
+ }
+ }
+
+ /* Retry training and levelization if requested */
+ if (retry_requested) {
+ printk(BIOS_DEBUG, "%s: Restarting training on algorithm request\n", __func__);
+ /* Reset frequency to minimum */
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ struct DCTStatStruc *pDCTstat;
+ pDCTstat = pDCTstatA + Node;
+ if (pDCTstat->NodePresent) {
+ uint8_t original_target_freq = pDCTstat->TargetFreq;
+ uint8_t original_auto_speed = pDCTstat->DIMMAutoSpeed;
+ pDCTstat->TargetFreq = mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK));
+ pDCTstat->Speed = pDCTstat->DIMMAutoSpeed = pDCTstat->TargetFreq;
+ SetTargetFreq(pMCTstat, pDCTstatA, Node);
+ pDCTstat->TargetFreq = original_target_freq;
+ pDCTstat->DIMMAutoSpeed = original_auto_speed;
+ }
+ }
+ /* Apply any DIMM timing changes */
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ struct DCTStatStruc *pDCTstat;
+ pDCTstat = pDCTstatA + Node;
+ if (pDCTstat->NodePresent) {
+ AutoCycTiming_D(pMCTstat, pDCTstat, 0);
+ if (!pDCTstat->GangedMode)
+ if (pDCTstat->DIMMValidDCT[1] > 0)
+ AutoCycTiming_D(pMCTstat, pDCTstat, 1);
+ }
+ }
+ goto retry_dqs_training_and_levelization;
+ }
+
TrainMaxRdLatency_En_D(pMCTstat, pDCTstatA);
if (is_fam15h())
@@ -3417,7 +3481,6 @@ static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
}
if (is_fam15h()) {
- uint8_t Node;
struct DCTStatStruc *pDCTstat;
/* Switch DCT control register to DCT 0 per Erratum 505 */
@@ -4268,6 +4331,9 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat,
Tcwl = 0x9;
else
Tcwl = 0x5; /* Power-on default */
+
+ /* Apply offset */
+ Tcwl += pDCTstat->tcwl_delay[dct];
}
/* Program DRAM Timing values */