aboutsummaryrefslogtreecommitdiff
path: root/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
diff options
context:
space:
mode:
authorTimothy Pearson <tpearson@raptorengineeringinc.com>2015-06-25 18:08:53 -0500
committerStefan Reinauer <stefan.reinauer@coreboot.org>2015-11-15 02:43:54 +0100
commit9426e4fcf5f49c46fa1e5cbe9fc38d2575cfdb62 (patch)
tree0067b1af0ec8037a8b93855b867d569e0564bf26 /src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
parent5288cedac1a3f92876990c5693bda0db11debc0c (diff)
northbridge/amd/amdmct/mct_ddr3: Attempt to recover from phy training errors
AMD's automatic phy phase detection hardware is very fragile and often produces incorrect results. Attempt to recover from obvious phase locking errors by retrying phy training on the failing link. Change-Id: Ia2c3022534c9ad44714eef6e118869f054bd9f6b Signed-off-by: Timothy Pearson <tpearson@raptorengineeringinc.com> Reviewed-on: http://review.coreboot.org/12006 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org> Reviewed-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Diffstat (limited to 'src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c')
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c68
1 files changed, 53 insertions, 15 deletions
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c b/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
index 5107fee63d..b3572b1941 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
@@ -14,11 +14,11 @@
* GNU General Public License for more details.
*/
-static void AgesaHwWlPhase1(struct MCTStatStruc *pMCTstat,
+static uint8_t AgesaHwWlPhase1(struct MCTStatStruc *pMCTstat,
struct DCTStatStruc *pDCTstat, u8 dct, u8 dimm, u8 pass);
-static void AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat,
+static uint8_t AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat,
struct DCTStatStruc *pDCTstat, u8 dct, u8 dimm, u8 pass);
-static void AgesaHwWlPhase3(struct MCTStatStruc *pMCTstat,
+static uint8_t AgesaHwWlPhase3(struct MCTStatStruc *pMCTstat,
struct DCTStatStruc *pDCTstat, u8 dct, u8 dimm, u8 pass);
static void EnableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
static void DisableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
@@ -96,11 +96,12 @@ static void DisableAutoRefresh_D(struct MCTStatStruc *pMCTstat,
}
-static void PhyWLPass1(struct MCTStatStruc *pMCTstat,
+static uint8_t PhyWLPass1(struct MCTStatStruc *pMCTstat,
struct DCTStatStruc *pDCTstat, u8 dct)
{
u8 dimm;
u16 DIMMValid;
+ uint8_t status = 0;
void *DCTPtr;
dct &= 1;
@@ -117,19 +118,22 @@ static void PhyWLPass1(struct MCTStatStruc *pMCTstat,
PrepareC_DCT(pMCTstat, pDCTstat, dct);
for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm ++) {
if (DIMMValid & (1 << (dimm << 1))) {
- AgesaHwWlPhase1(pMCTstat, pDCTstat, dct, dimm, FirstPass);
- AgesaHwWlPhase2(pMCTstat, pDCTstat, dct, dimm, FirstPass);
- AgesaHwWlPhase3(pMCTstat, pDCTstat, dct, dimm, FirstPass);
+ status |= AgesaHwWlPhase1(pMCTstat, pDCTstat, dct, dimm, FirstPass);
+ status |= AgesaHwWlPhase2(pMCTstat, pDCTstat, dct, dimm, FirstPass);
+ status |= AgesaHwWlPhase3(pMCTstat, pDCTstat, dct, dimm, FirstPass);
}
}
}
+
+ return status;
}
-static void PhyWLPass2(struct MCTStatStruc *pMCTstat,
+static uint8_t PhyWLPass2(struct MCTStatStruc *pMCTstat,
struct DCTStatStruc *pDCTstat, u8 dct)
{
u8 dimm;
u16 DIMMValid;
+ uint8_t status = 0;
void *DCTPtr;
dct &= 1;
@@ -159,12 +163,14 @@ static void PhyWLPass2(struct MCTStatStruc *pMCTstat,
DisableAutoRefresh_D(pMCTstat, pDCTstat);
for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm ++) {
if (DIMMValid & (1 << (dimm << 1))) {
- AgesaHwWlPhase1(pMCTstat, pDCTstat, dct, dimm, SecondPass);
- AgesaHwWlPhase2(pMCTstat, pDCTstat, dct, dimm, SecondPass);
- AgesaHwWlPhase3(pMCTstat, pDCTstat, dct, dimm, SecondPass);
+ status |= AgesaHwWlPhase1(pMCTstat, pDCTstat, dct, dimm, SecondPass);
+ status |= AgesaHwWlPhase2(pMCTstat, pDCTstat, dct, dimm, SecondPass);
+ status |= AgesaHwWlPhase3(pMCTstat, pDCTstat, dct, dimm, SecondPass);
}
}
}
+
+ return status;
}
static uint16_t fam15h_next_highest_memclk_freq(uint16_t memclk_freq)
@@ -179,6 +185,8 @@ static uint16_t fam15h_next_highest_memclk_freq(uint16_t memclk_freq)
static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
struct DCTStatStruc *pDCTstat, uint8_t Pass)
{
+ uint8_t status;
+ uint8_t timeout;
uint16_t final_target_freq;
pDCTstat->C_MCTPtr = &(pDCTstat->s_C_MCTPtr);
@@ -197,8 +205,21 @@ static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
}
if (Pass == FirstPass) {
- PhyWLPass1(pMCTstat, pDCTstat, 0);
- PhyWLPass1(pMCTstat, pDCTstat, 1);
+ timeout = 0;
+ do {
+ status = 0;
+ timeout++;
+ status |= PhyWLPass1(pMCTstat, pDCTstat, 0);
+ status |= PhyWLPass1(pMCTstat, pDCTstat, 1);
+ if (status)
+ printk(BIOS_INFO,
+ "%s: Retrying write levelling due to invalid value(s) detected in first phase\n",
+ __func__);
+ } while (status && (timeout < 8));
+ if (status)
+ printk(BIOS_INFO,
+ "%s: Uncorrectable invalid value(s) detected in first phase of write levelling\n",
+ __func__);
}
if (Pass == SecondPass) {
@@ -207,6 +228,7 @@ static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
* NOTE: BIOS must program both DCTs to the same frequency.
* NOTE: Fam15h steps the frequency, Fam10h slams the frequency.
*/
+ uint8_t global_phy_training_status = 0;
final_target_freq = pDCTstat->TargetFreq;
while (pDCTstat->Speed != final_target_freq) {
@@ -215,12 +237,28 @@ static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
else
pDCTstat->TargetFreq = final_target_freq;
SetTargetFreq(pMCTstat, pDCTstat);
- PhyWLPass2(pMCTstat, pDCTstat, 0);
- PhyWLPass2(pMCTstat, pDCTstat, 1);
+ timeout = 0;
+ do {
+ status = 0;
+ timeout++;
+ status |= PhyWLPass2(pMCTstat, pDCTstat, 0);
+ status |= PhyWLPass2(pMCTstat, pDCTstat, 1);
+ if (status)
+ printk(BIOS_INFO,
+ "%s: Retrying write levelling due to invalid value(s) detected in last phase\n",
+ __func__);
+ } while (status && (timeout < 8));
+ global_phy_training_status |= status;
}
pDCTstat->TargetFreq = final_target_freq;
+ if (global_phy_training_status)
+ printk(BIOS_WARNING,
+ "%s: Uncorrectable invalid value(s) detected in second phase of write levelling; "
+ "continuing but system may be unstable!\n",
+ __func__);
+
uint8_t dct;
for (dct = 0; dct < 2; dct++) {
sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];