aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mct_d.c186
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mct_d.h2
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c4
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c17
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mctrci.c191
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c42
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c253
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mctwl.c16
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c400
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h13
10 files changed, 698 insertions, 426 deletions
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
index 3338ae3f28..25c4042c23 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
@@ -162,7 +162,7 @@ static void mct_EnDllShutdownSR(struct MCTStatStruc *pMCTstat,
static void ChangeMemClk(struct MCTStatStruc *pMCTstat,
struct DCTStatStruc *pDCTstat);
void SetTargetFreq(struct MCTStatStruc *pMCTstat,
- struct DCTStatStruc *pDCTstat);
+ struct DCTStatStruc *pDCTstatA, uint8_t Node);
static u32 mct_MR1Odt_RDimm(struct MCTStatStruc *pMCTstat,
struct DCTStatStruc *pDCTstat, u8 dct, u32 MrsChipSel);
@@ -1410,6 +1410,10 @@ static void precise_memclk_delay_fam15(struct MCTStatStruc *pMCTstat, struct DCT
memclk_freq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
+ if (fam15h_freq_tab[memclk_freq] == 0) {
+ printk(BIOS_DEBUG, "ERROR: precise_memclk_delay_fam15 for DCT %d (delay %d clocks) failed to obtain valid memory frequency!"
+ " (pDCTstat: %p pDCTstat->dev_dct: %08x memclk_freq: %02x)\n", dct, clocks, pDCTstat, pDCTstat->dev_dct, memclk_freq);
+ }
delay_ns = (((uint64_t)clocks * 1000) / fam15h_freq_tab[memclk_freq]);
precise_ndelay_fam15(pMCTstat, delay_ns);
}
@@ -2327,7 +2331,7 @@ static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
nv_DQSTrainCTL = !allow_config_restore;
mct_BeforeDQSTrain_D(pMCTstat, pDCTstatA);
- phyAssistedMemFnceTraining(pMCTstat, pDCTstatA);
+ phyAssistedMemFnceTraining(pMCTstat, pDCTstatA, -1);
if (is_fam15h()) {
uint8_t Node;
@@ -3367,7 +3371,7 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat,
}
static u8 AutoCycTiming_D(struct MCTStatStruc *pMCTstat,
- struct DCTStatStruc *pDCTstat, u8 dct)
+ struct DCTStatStruc *pDCTstat, uint8_t dct)
{
/* Initialize DCT Timing registers as per DIMM SPD.
* For primary timing (T, CL) use best case T value.
@@ -3471,7 +3475,7 @@ static void GetPresetmaxF_D(struct MCTStatStruc *pMCTstat,
}
static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
- struct DCTStatStruc *pDCTstat, u8 dct)
+ struct DCTStatStruc *pDCTstat, uint8_t dct)
{
/* Find the best T and CL primary timing parameter pair, per Mfg.,
* for the given set of DIMMs, and store into DCTStatStruc
@@ -3750,10 +3754,15 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
dword++;
}
- if (Status & (1 << SB_Registered))
- DramConfigLo |= 1 << ParEn; /* Registered DIMMs */
- else
- DramConfigLo |= 1 << UnBuffDimm; /* Unbuffered DIMMs */
+ if (Status & (1 << SB_Registered)) {
+ /* Registered DIMMs */
+ if (!is_fam15h()) {
+ DramConfigLo |= 1 << ParEn;
+ }
+ } else {
+ /* Unbuffered DIMMs */
+ DramConfigLo |= 1 << UnBuffDimm;
+ }
if (mctGet_NVbits(NV_ECC_CAP))
if (Status & (1 << SB_ECCDIMMs))
@@ -3771,10 +3780,11 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
DramConfigHi |= dword - offset; /* get MemClk encoding */
DramConfigHi |= 1 << MemClkFreqVal;
- if (Status & (1 << SB_Registered))
- if ((pDCTstat->Dimmx4Present != 0) && (pDCTstat->Dimmx8Present != 0))
- /* set only if x8 Registered DIMMs in System*/
- DramConfigHi |= 1 << RDqsEn;
+ if (!is_fam15h())
+ if (Status & (1 << SB_Registered))
+ if ((pDCTstat->Dimmx4Present != 0) && (pDCTstat->Dimmx8Present != 0))
+ /* set only if x8 Registered DIMMs in System*/
+ DramConfigHi |= 1 << RDqsEn;
if (pDCTstat->LogicalCPUID & AMD_FAM15_ALL) {
DramConfigLo |= 1 << 25; /* PendRefPaybackS3En = 1 */
@@ -3786,14 +3796,16 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
DramConfigHi |= 1 << 16;
}
- /* Control Bank Swizzle */
- if (0) /* call back not needed mctBankSwizzleControl_D()) */
- DramConfigHi &= ~(1 << BankSwizzleMode);
- else
- DramConfigHi |= 1 << BankSwizzleMode; /* recommended setting (default) */
+ if (!is_fam15h()) {
+ /* Control Bank Swizzle */
+ if (0) /* call back not needed mctBankSwizzleControl_D()) */
+ DramConfigHi &= ~(1 << BankSwizzleMode);
+ else
+ DramConfigHi |= 1 << BankSwizzleMode; /* recommended setting (default) */
+ }
/* Check for Quadrank DIMM presence */
- if ( pDCTstat->DimmQRPresent != 0) {
+ if (pDCTstat->DimmQRPresent != 0) {
byte = mctGet_NVbits(NV_4RANKType);
if (byte == 2)
DramConfigHi |= 1 << 17; /* S4 (4-Rank SO-DIMMs) */
@@ -4598,8 +4610,9 @@ static u8 mct_setMode(struct MCTStatStruc *pMCTstat,
Set_NB32(pDCTstat->dev_dct, reg, val);
}
if (byte) /* NV_Unganged */
- pDCTstat->ErrStatus &= ~(1 << SB_DimmMismatchO); /* Clear so that there is no DIMM missmatch error */
+ pDCTstat->ErrStatus &= ~(1 << SB_DimmMismatchO); /* Clear so that there is no DIMM mismatch error */
}
+
return pDCTstat->ErrCode;
}
@@ -4660,6 +4673,8 @@ void Set_NB32_index_wait(u32 dev, u32 index_reg, u32 index, u32 data)
static u8 mct_BeforePlatformSpec(struct MCTStatStruc *pMCTstat,
struct DCTStatStruc *pDCTstat, u8 dct)
{
+ printk(BIOS_DEBUG, "%s: Start\n", __func__);
+
/* mct_checkForCxDxSupport_D */
if (pDCTstat->LogicalCPUID & AMD_DR_GT_Bx) {
/* Family 10h Errata 322: Address and Command Fine Delay Values May Be Incorrect */
@@ -4674,6 +4689,9 @@ static u8 mct_BeforePlatformSpec(struct MCTStatStruc *pMCTstat,
else
Set_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, 0x98, 0x0D02E001, 0x90);
}
+
+ printk(BIOS_DEBUG, "%s: Done\n", __func__);
+
return pDCTstat->ErrCode;
}
@@ -4684,6 +4702,8 @@ static u8 mct_PlatformSpec(struct MCTStatStruc *pMCTstat,
* and program them into DCT.
*/
+ printk(BIOS_DEBUG, "%s: Start\n", __func__);
+
u32 dev = pDCTstat->dev_dct;
u32 index_reg;
u8 i, i_start, i_end;
@@ -4704,6 +4724,8 @@ static u8 mct_PlatformSpec(struct MCTStatStruc *pMCTstat,
printk(BIOS_SPEW, "Programmed DCT %d timing/termination pattern %08x %08x\n", dct, pDCTstat->CH_ADDR_TMG[i], pDCTstat->CH_ODC_CTL[i]);
}
+ printk(BIOS_DEBUG, "%s: Done\n", __func__);
+
return pDCTstat->ErrCode;
}
@@ -4715,7 +4737,8 @@ static void mct_SyncDCTsReady(struct DCTStatStruc *pDCTstat)
if (pDCTstat->NodePresent) {
dev = pDCTstat->dev_dct;
- if ((pDCTstat->DIMMValidDCT[0] ) || (pDCTstat->DIMMValidDCT[1])) { /* This Node has dram */
+ if ((pDCTstat->DIMMValidDCT[0]) || (pDCTstat->DIMMValidDCT[1])) {
+ /* This Node has DRAM */
do {
val = Get_NB32(dev, 0x110);
} while (!(val & (1 << DramEnabled)));
@@ -5663,57 +5686,56 @@ static void InitDDRPhy(struct MCTStatStruc *pMCTstat,
/* Fam15h BKDG v3.14 section 2.10.5.3
* The remainder of the Phy Initialization algorithm picks up in phyAssistedMemFnceTraining
*/
- for (dct = 0; dct < 2; dct++) {
- Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000b, 0x80000000);
- Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe013, 0x00000118);
-
- /* Program desired VDDIO level */
- if (ddr_voltage_index & 0x4) {
- /* 1.25V */
- amd_voltage_level_index = 0x2;
- } else if (ddr_voltage_index & 0x2) {
- /* 1.35V */
- amd_voltage_level_index = 0x1;
- } else if (ddr_voltage_index & 0x1) {
- /* 1.50V */
- amd_voltage_level_index = 0x0;
- }
-
- /* D18F2x9C_x0D0F_0[F,8:0]1F_dct[1:0][RxVioLvl] */
- for (index = 0; index < 0x9; index++) {
- dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f001f | (index << 8));
- dword &= ~(0x3 << 3);
- dword |= (amd_voltage_level_index << 3);
- Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f001f | (index << 8), dword);
- }
-
- /* D18F2x9C_x0D0F_[C,8,2][2:0]1F_dct[1:0][RxVioLvl] */
- for (index = 0; index < 0x3; index++) {
- dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f201f | (index << 8));
- dword &= ~(0x3 << 3);
- dword |= (amd_voltage_level_index << 3);
- Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f201f | (index << 8), dword);
- }
- for (index = 0; index < 0x2; index++) {
- dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f801f | (index << 8));
- dword &= ~(0x3 << 3);
- dword |= (amd_voltage_level_index << 3);
- Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f801f | (index << 8), dword);
- }
- for (index = 0; index < 0x1; index++) {
- dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc01f | (index << 8));
- dword &= ~(0x3 << 3);
- dword |= (amd_voltage_level_index << 3);
- Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc01f | (index << 8), dword);
- }
+ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000b, 0x80000000);
+ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe013, 0x00000118);
- /* D18F2x9C_x0D0F_4009_dct[1:0][CmpVioLvl, ComparatorAdjust] */
- dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f4009);
- dword &= ~(0x0000c00c);
- dword |= (amd_voltage_level_index << 14);
- dword |= (amd_voltage_level_index << 2);
- Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f4009, dword);
- }
+ /* Program desired VDDIO level */
+ if (ddr_voltage_index & 0x4) {
+ /* 1.25V */
+ amd_voltage_level_index = 0x2;
+ } else if (ddr_voltage_index & 0x2) {
+ /* 1.35V */
+ amd_voltage_level_index = 0x1;
+ } else if (ddr_voltage_index & 0x1) {
+ /* 1.50V */
+ amd_voltage_level_index = 0x0;
+ }
+
+ /* D18F2x9C_x0D0F_0[F,8:0]1F_dct[1:0][RxVioLvl] */
+ for (index = 0; index < 0x9; index++) {
+ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f001f | (index << 8));
+ dword &= ~(0x3 << 3);
+ dword |= (amd_voltage_level_index << 3);
+ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f001f | (index << 8), dword);
+ }
+
+ /* D18F2x9C_x0D0F_[C,8,2][2:0]1F_dct[1:0][RxVioLvl] */
+ for (index = 0; index < 0x3; index++) {
+ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f201f | (index << 8));
+ dword &= ~(0x3 << 3);
+ dword |= (amd_voltage_level_index << 3);
+ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f201f | (index << 8), dword);
+ }
+ for (index = 0; index < 0x2; index++) {
+ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f801f | (index << 8));
+ dword &= ~(0x3 << 3);
+ dword |= (amd_voltage_level_index << 3);
+ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f801f | (index << 8), dword);
+ }
+ for (index = 0; index < 0x1; index++) {
+ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc01f | (index << 8));
+ dword &= ~(0x3 << 3);
+ dword |= (amd_voltage_level_index << 3);
+ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc01f | (index << 8), dword);
+ }
+
+ /* D18F2x9C_x0D0F_4009_dct[1:0][CmpVioLvl, ComparatorAdjust] */
+ /* NOTE: CmpVioLvl and ComparatorAdjust only take effect when set on DCT 0 */
+ dword = Get_NB32_index_wait_DCT(dev, 0, index_reg, 0x0d0f4009);
+ dword &= ~(0x0000c00c);
+ dword |= (amd_voltage_level_index << 14);
+ dword |= (amd_voltage_level_index << 2);
+ Set_NB32_index_wait_DCT(dev, 0, index_reg, 0x0d0f4009, dword);
printk(BIOS_DEBUG, "%s: Done\n", __func__);
}
@@ -5729,18 +5751,24 @@ static void InitPhyCompensation(struct MCTStatStruc *pMCTstat,
uint32_t dword;
const u8 *p;
- printk(BIOS_DEBUG, "%s: Start\n", __func__);
+ printk(BIOS_DEBUG, "%s: DCT %d: Start\n", __func__, dct);
if (is_fam15h()) {
/* Algorithm detailed in the Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 */
uint32_t tx_pre;
uint32_t drive_strength;
- /* Program D18F2x9C_x0D0F_E003_dct[1:0][DisAutoComp, DisablePredriverCal] */
+ /* Program D18F2x9C_x0D0F_E003_dct[1:0][DisAutoComp] */
dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe003);
- dword |= (0x3 << 13);
+ dword |= (0x1 << 14);
Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe003, dword);
+ /* Program D18F2x9C_x0D0F_E003_dct[1:0][DisablePredriverCal] */
+ /* NOTE: DisablePredriverCal only takes effect when set on DCT 0 */
+ dword = Get_NB32_index_wait_DCT(dev, 0, index_reg, 0x0d0fe003);
+ dword |= (0x1 << 13);
+ Set_NB32_index_wait_DCT(dev, 0, index_reg, 0x0d0fe003, dword);
+
/* Determine TxPreP/TxPreN for data lanes (Stage 1) */
dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000000);
drive_strength = (dword >> 20) & 0x7; /* DqsDrvStren */
@@ -5886,12 +5914,14 @@ static void InitPhyCompensation(struct MCTStatStruc *pMCTstat,
Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0a, dword);
}
- printk(BIOS_DEBUG, "%s: Done\n", __func__);
+ printk(BIOS_DEBUG, "%s: DCT %d: Done\n", __func__, dct);
}
static void mct_EarlyArbEn_D(struct MCTStatStruc *pMCTstat,
struct DCTStatStruc *pDCTstat, u8 dct)
{
+ printk(BIOS_DEBUG, "%s: Start\n", __func__);
+
if (!is_fam15h()) {
u32 reg;
u32 val;
@@ -5913,6 +5943,8 @@ static void mct_EarlyArbEn_D(struct MCTStatStruc *pMCTstat,
Set_NB32_DCT(dev, dct, reg, val);
}
+
+ printk(BIOS_DEBUG, "%s: Done\n", __func__);
}
static u8 CheckNBCOFEarlyArbEn(struct MCTStatStruc *pMCTstat,
@@ -6556,6 +6588,8 @@ void mct_SetDramConfigHi_D(struct MCTStatStruc *pMCTstat,
uint32_t dword;
+ printk(BIOS_DEBUG, "%s: Start\n", __func__);
+
if (is_fam15h()) {
/* Initial setup for frequency change
* 9C_x0000_0004 must be configured before MemClkFreqVal is set
@@ -6588,6 +6622,8 @@ void mct_SetDramConfigHi_D(struct MCTStatStruc *pMCTstat,
mct_Wait(100);
}
+ printk(BIOS_DEBUG, "mct_SetDramConfigHi_D: DramConfigHi: %08x\n", DramConfigHi);
+
/* Program the DRAM Configuration High register */
Set_NB32_DCT(dev, dct, 0x94, DramConfigHi);
@@ -6603,6 +6639,8 @@ void mct_SetDramConfigHi_D(struct MCTStatStruc *pMCTstat,
dword |= 0x0000000f;
Set_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, index_reg, 0x0d0fe006, dword);
}
+
+ printk(BIOS_DEBUG, "%s: Done\n", __func__);
}
static void mct_BeforeDQSTrain_D(struct MCTStatStruc *pMCTstat,
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
index 592b1e646e..eb4c74e309 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
@@ -1010,7 +1010,7 @@ void InterleaveNodes_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTs
void InterleaveChannels_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
void mct_BeforeDQSTrain_Samp_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
-void phyAssistedMemFnceTraining(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+void phyAssistedMemFnceTraining(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA, int16_t Node);
u8 mct_SaveRcvEnDly_D_1Pass(struct DCTStatStruc *pDCTstat, u8 pass);
u8 mct_InitReceiver_D(struct DCTStatStruc *pDCTstat, u8 dct);
void mct_Wait(u32 cycles);
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
index cdb93f9405..3615616cd5 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
@@ -1584,6 +1584,7 @@ static void TrainDQSReceiverEnCyc_D_Fam15(struct MCTStatStruc *pMCTstat,
for (dct = 0; dct < 2; dct++) {
/* Program D18F2x9C_x0D0F_E003_dct[1:0][DisAutoComp, DisablePredriverCal] */
+ /* NOTE: DisablePredriverCal only takes effect when set on DCT 0 */
dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe003);
dword &= ~(0x3 << 13);
dword |= (0x1 << 13);
@@ -1623,6 +1624,9 @@ static void TrainDQSReceiverEnCyc_D_Fam15(struct MCTStatStruc *pMCTstat,
rx_en_offset = (initial_phy_phase_delay[lane] + 0x10) % 0x40;
/* 2.10.5.8.3 (4) */
+#if DQS_TRAIN_DEBUG > 0
+ printk(BIOS_DEBUG, "TrainDQSReceiverEnCyc_D_Fam15 Receiver %d lane %d initial phy delay %04x: iterating from %04x to %04x\n", Receiver, lane, initial_phy_phase_delay[lane], rx_en_offset, 0x3ff);
+#endif
for (current_phy_phase_delay[lane] = rx_en_offset; current_phy_phase_delay[lane] < 0x3ff; current_phy_phase_delay[lane] += ren_step) {
/* 2.10.5.8.3 (4 A) */
write_dqs_receiver_enable_control_registers(current_phy_phase_delay, dev, dct, dimm, index_reg);
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c b/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
index b3572b1941..a92f9e5ee8 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
@@ -17,7 +17,7 @@
static uint8_t AgesaHwWlPhase1(struct MCTStatStruc *pMCTstat,
struct DCTStatStruc *pDCTstat, u8 dct, u8 dimm, u8 pass);
static uint8_t AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat,
- struct DCTStatStruc *pDCTstat, u8 dct, u8 dimm, u8 pass);
+ struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t dimm, uint8_t pass);
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);
@@ -129,7 +129,7 @@ static uint8_t PhyWLPass1(struct MCTStatStruc *pMCTstat,
}
static uint8_t PhyWLPass2(struct MCTStatStruc *pMCTstat,
- struct DCTStatStruc *pDCTstat, u8 dct)
+ struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t final)
{
u8 dimm;
u16 DIMMValid;
@@ -183,12 +183,15 @@ static uint16_t fam15h_next_highest_memclk_freq(uint16_t memclk_freq)
* Algorithm detailed in the Fam10h BKDG Rev. 3.62 section 2.8.9.9.1
*/
static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
- struct DCTStatStruc *pDCTstat, uint8_t Pass)
+ struct DCTStatStruc *pDCTstatA, uint8_t Node, uint8_t Pass)
{
uint8_t status;
uint8_t timeout;
uint16_t final_target_freq;
+ struct DCTStatStruc *pDCTstat;
+ pDCTstat = pDCTstatA + Node;
+
pDCTstat->C_MCTPtr = &(pDCTstat->s_C_MCTPtr);
pDCTstat->C_DCTPtr[0] = &(pDCTstat->s_C_DCTPtr[0]);
pDCTstat->C_DCTPtr[1] = &(pDCTstat->s_C_DCTPtr[1]);
@@ -236,13 +239,13 @@ static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
pDCTstat->TargetFreq = fam15h_next_highest_memclk_freq(pDCTstat->Speed);
else
pDCTstat->TargetFreq = final_target_freq;
- SetTargetFreq(pMCTstat, pDCTstat);
+ SetTargetFreq(pMCTstat, pDCTstatA, Node);
timeout = 0;
do {
status = 0;
timeout++;
- status |= PhyWLPass2(pMCTstat, pDCTstat, 0);
- status |= PhyWLPass2(pMCTstat, pDCTstat, 1);
+ status |= PhyWLPass2(pMCTstat, pDCTstat, 0, (pDCTstat->TargetFreq == final_target_freq));
+ status |= PhyWLPass2(pMCTstat, pDCTstat, 1, (pDCTstat->TargetFreq == final_target_freq));
if (status)
printk(BIOS_INFO,
"%s: Retrying write levelling due to invalid value(s) detected in last phase\n",
@@ -286,7 +289,7 @@ void mct_WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
if (pDCTstat->NodePresent) {
mctSMBhub_Init(Node);
Clear_OnDimmMirror(pMCTstat, pDCTstat);
- WriteLevelization_HW(pMCTstat, pDCTstat, Pass);
+ WriteLevelization_HW(pMCTstat, pDCTstatA, Node, Pass);
Restore_OnDimmMirror(pMCTstat, pDCTstat);
}
}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c b/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c
index 3f6c39d2c5..01061a7335 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c
@@ -14,6 +14,78 @@
* GNU General Public License for more details.
*/
+static uint8_t fam15h_rdimm_rc2_control_code(struct DCTStatStruc *pDCTstat, uint8_t dct)
+{
+ uint8_t MaxDimmsInstallable = mctGet_NVbits(NV_MAX_DIMMS_PER_CH);
+
+ uint8_t package_type;
+ uint8_t control_code = 0;
+
+ package_type = mctGet_NVbits(NV_PACK_TYPE);
+ uint16_t MemClkFreq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f;
+
+ /* Obtain number of DIMMs on channel */
+ uint8_t dimm_count = pDCTstat->MAdimms[dct];
+
+ /* FIXME
+ * Assume there is only one register on the RDIMM for now
+ */
+ uint8_t num_registers = 1;
+
+ if (package_type == PT_GR) {
+ /* Socket G34 */
+ /* Fam15h BKDG Rev. 3.14 section 2.10.5.7.1.2.1 Table 85 */
+ if (MaxDimmsInstallable == 1) {
+ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+ /* DDR3-667 - DDR3-800 */
+ control_code = 0x1;
+ } else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) {
+ /* DDR3-1066 - DDR3-1333 */
+ if (num_registers == 1) {
+ control_code = 0x0;
+ } else {
+ control_code = 0x1;
+ }
+ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) {
+ /* DDR3-1600 - DDR3-1866 */
+ control_code = 0x0;
+ }
+ } else if (MaxDimmsInstallable == 2) {
+ if (dimm_count == 1) {
+ /* 1 DIMM detected */
+ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) {
+ /* DDR3-667 - DDR3-800 */
+ control_code = 0x1;
+ } else if ((MemClkFreq >= 0xa) && (MemClkFreq <= 0x12)) {
+ /* DDR3-1066 - DDR3-1600 */
+ if (num_registers == 1) {
+ control_code = 0x0;
+ } else {
+ control_code = 0x1;
+ }
+ }
+ } else if (dimm_count == 2) {
+ /* 2 DIMMs detected */
+ if (num_registers == 1) {
+ control_code = 0x1;
+ } else {
+ control_code = 0x8;
+ }
+ }
+ } else if (MaxDimmsInstallable == 3) {
+ /* TODO
+ * 3 DIMM/channel support unimplemented
+ */
+ }
+ } else {
+ /* TODO
+ * Other socket support unimplemented
+ */
+ }
+
+ return control_code;
+}
+
static uint16_t memclk_to_freq(uint16_t memclk) {
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};
@@ -33,36 +105,46 @@ static uint16_t memclk_to_freq(uint16_t memclk) {
return mem_freq;
}
+static uint8_t rc_word_chip_select_lower_bit(void) {
+ if (is_fam15h()) {
+ return 21;
+ } else {
+ return 20;
+ }
+}
+
+static uint32_t rc_word_address_to_ctl_bits(uint32_t address) {
+ if (is_fam15h()) {
+ return (((address >> 3) & 0x1) << 2) << 18 | (address & 0x7);
+ } else {
+ return (((address >> 3) & 0x1) << 2) << 16 | (address & 0x7);
+ }
+}
+
static uint32_t rc_word_value_to_ctl_bits(uint32_t value) {
- return ((value >> 2) & 3) << 16 | ((value & 3) << 3);
+ if (is_fam15h()) {
+ return ((value >> 2) & 0x3) << 18 | ((value & 0x3) << 3);
+ } else {
+ return ((value >> 2) & 0x3) << 16 | ((value & 0x3) << 3);
+ }
}
static u32 mct_ControlRC(struct MCTStatStruc *pMCTstat,
- struct DCTStatStruc *pDCTstat, u32 MrsChipSel, u32 CtrlWordNum)
+ struct DCTStatStruc *pDCTstat, uint8_t dct, u32 MrsChipSel, u32 CtrlWordNum)
{
u8 Dimms, DimmNum;
u32 val;
- u32 dct = 0;
uint8_t ddr_voltage_index;
uint16_t mem_freq;
uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
uint8_t MaxDimmsInstallable = mctGet_NVbits(NV_MAX_DIMMS_PER_CH);
- DimmNum = (MrsChipSel >> 20) & 0xFE;
+ DimmNum = (MrsChipSel >> rc_word_chip_select_lower_bit()) & 0xfe;
- /* assume dct=0; */
- /* if (dct == 1) */
- /* DimmNum ++; */
- /* cl +=8; */
+ if (dct == 1)
+ DimmNum++;
mem_freq = memclk_to_freq(pDCTstat->DIMMAutoSpeed);
-
- if (pDCTstat->CSPresent_DCT[0] > 0) {
- dct = 0;
- } else if (pDCTstat->CSPresent_DCT[1] > 0 ) {
- dct = 1;
- DimmNum++;
- }
Dimms = pDCTstat->MAdimms[dct];
ddr_voltage_index = dct_ddr_voltage_index(pDCTstat, dct);
@@ -72,21 +154,25 @@ static u32 mct_ControlRC(struct MCTStatStruc *pMCTstat,
val = 0x2;
else if (CtrlWordNum == 1) {
if (!((pDCTstat->DimmDRPresent | pDCTstat->DimmQRPresent) & (1 << DimmNum)))
- val = 0xC; /* if single rank, set DBA1 and DBA0 */
+ val = 0xc; /* if single rank, set DBA1 and DBA0 */
} else if (CtrlWordNum == 2) {
- if (package_type == PT_GR) {
- /* Socket G34 */
- if (MaxDimmsInstallable == 2) {
- if (Dimms > 1)
- val = 0x4;
+ if (is_fam15h()) {
+ val = fam15h_rdimm_rc2_control_code(pDCTstat, dct);
+ } else {
+ if (package_type == PT_GR) {
+ /* Socket G34 */
+ if (MaxDimmsInstallable == 2) {
+ if (Dimms > 1)
+ val = 0x4;
+ }
}
}
} else if (CtrlWordNum == 3) {
- val = (pDCTstat->CtrlWrd3 >> (DimmNum << 2)) & 0xFF;
+ val = (pDCTstat->CtrlWrd3 >> (DimmNum << 2)) & 0xff;
} else if (CtrlWordNum == 4) {
- val = (pDCTstat->CtrlWrd4 >> (DimmNum << 2)) & 0xFF;
+ val = (pDCTstat->CtrlWrd4 >> (DimmNum << 2)) & 0xff;
} else if (CtrlWordNum == 5) {
- val = (pDCTstat->CtrlWrd5 >> (DimmNum << 2)) & 0xFF;
+ val = (pDCTstat->CtrlWrd5 >> (DimmNum << 2)) & 0xff;
} else if (CtrlWordNum == 8) {
if (package_type == PT_GR) {
/* Socket G34 */
@@ -95,7 +181,7 @@ static u32 mct_ControlRC(struct MCTStatStruc *pMCTstat,
}
}
} else if (CtrlWordNum == 9) {
- val = 0xD; /* DBA1, DBA0, DA3 = 0 */
+ val = 0xd; /* DBA1, DBA0, DA3 = 0 */
} else if (CtrlWordNum == 10) {
val = 0x0; /* Lowest operating frequency */
} else if (CtrlWordNum == 11) {
@@ -110,43 +196,30 @@ static u32 mct_ControlRC(struct MCTStatStruc *pMCTstat,
}
val &= 0xf;
- printk(BIOS_SPEW, "Preparing to send DIMM RC%d: %02x\n", CtrlWordNum, val);
+ printk(BIOS_SPEW, "Preparing to send DCT %d DIMM RC%d: %02x\n", dct, CtrlWordNum, val);
val = MrsChipSel | rc_word_value_to_ctl_bits(val);
-
- /* transfer Control word number to address [BA2,A2,A1,A0] */
- if (CtrlWordNum > 7) {
- val |= 1 << 18;
- CtrlWordNum &= 7;
- }
- val |= CtrlWordNum;
+ val |= rc_word_address_to_ctl_bits(CtrlWordNum);
return val;
}
static void mct_SendCtrlWrd(struct MCTStatStruc *pMCTstat,
- struct DCTStatStruc *pDCTstat, u32 val)
+ struct DCTStatStruc *pDCTstat, uint8_t dct, uint32_t val)
{
- uint8_t dct = 0;
u32 dev = pDCTstat->dev_dct;
- if (pDCTstat->CSPresent_DCT[0] > 0) {
- dct = 0;
- } else if (pDCTstat->CSPresent_DCT[1] > 0 ){
- dct = 1;
- }
-
- val |= Get_NB32_DCT(dev, dct, 0x7C) & ~0xFFFFFF;
+ val |= Get_NB32_DCT(dev, dct, 0x7c) & ~0xffffff;
val |= 1 << SendControlWord;
- Set_NB32_DCT(dev, dct, 0x7C, val);
+ Set_NB32_DCT(dev, dct, 0x7c, val);
do {
- val = Get_NB32_DCT(dev, dct, 0x7C);
+ val = Get_NB32_DCT(dev, dct, 0x7c);
} while (val & (1 << SendControlWord));
}
void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat,
- struct DCTStatStruc *pDCTstat, u8 dct)
+ struct DCTStatStruc *pDCTstat, uint8_t dct)
{
u8 MrsChipSel;
u32 dev = pDCTstat->dev_dct;
@@ -159,7 +232,7 @@ void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat,
for (MrsChipSel = 0; MrsChipSel < 8; MrsChipSel ++, MrsChipSel ++) {
if (pDCTstat->CSPresent & (1 << MrsChipSel)) {
val = Get_NB32_DCT(dev, dct, 0xa8);
- val &= ~(0xF << 8);
+ val &= ~(0xf << 8);
switch (MrsChipSel) {
case 0:
@@ -180,8 +253,8 @@ void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat,
for (cw=0; cw <=15; cw ++) {
mct_Wait(1600);
if (!(cw==6 || cw==7)) {
- val = mct_ControlRC(pMCTstat, pDCTstat, MrsChipSel << 20, cw);
- mct_SendCtrlWrd(pMCTstat, pDCTstat, val);
+ val = mct_ControlRC(pMCTstat, pDCTstat, dct, MrsChipSel << rc_word_chip_select_lower_bit(), cw);
+ mct_SendCtrlWrd(pMCTstat, pDCTstat, dct, val);
}
}
}
@@ -191,7 +264,7 @@ void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat,
}
void FreqChgCtrlWrd(struct MCTStatStruc *pMCTstat,
- struct DCTStatStruc *pDCTstat)
+ struct DCTStatStruc *pDCTstat, uint8_t dct)
{
u32 SaveSpeed = pDCTstat->DIMMAutoSpeed;
u32 MrsChipSel;
@@ -204,10 +277,10 @@ void FreqChgCtrlWrd(struct MCTStatStruc *pMCTstat,
for (MrsChipSel=0; MrsChipSel < 8; MrsChipSel++, MrsChipSel++) {
if (pDCTstat->CSPresent & (1 << MrsChipSel)) {
/* 2. Program F2x[1, 0]A8[CtrlWordCS]=bit mask for target chip selects. */
- val = Get_NB32_DCT(dev, 0, 0xA8); /* TODO: dct 0 / 1 select */
- val &= ~(0xFF << 8);
- val |= (0x3 << (MrsChipSel & 0xFE)) << 8;
- Set_NB32_DCT(dev, 0, 0xA8, val); /* TODO: dct 0 / 1 select */
+ val = Get_NB32_DCT(dev, dct, 0xa8);
+ val &= ~(0xff << 8);
+ val |= (0x3 << (MrsChipSel & 0xfe)) << 8;
+ Set_NB32_DCT(dev, dct, 0xa8, val);
/* Resend control word 10 */
uint8_t freq_ctl_val = 0;
@@ -231,21 +304,21 @@ void FreqChgCtrlWrd(struct MCTStatStruc *pMCTstat,
break;
}
- printk(BIOS_SPEW, "Preparing to send DIMM RC%d: %02x\n", 10, freq_ctl_val);
+ printk(BIOS_SPEW, "Preparing to send DCT %d DIMM RC%d: %02x\n", dct, 10, freq_ctl_val);
- mct_SendCtrlWrd(pMCTstat, pDCTstat, MrsChipSel << 20 | 0x40002 | rc_word_value_to_ctl_bits(freq_ctl_val));
+ mct_SendCtrlWrd(pMCTstat, pDCTstat, dct, MrsChipSel << rc_word_chip_select_lower_bit() | rc_word_address_to_ctl_bits(10) | rc_word_value_to_ctl_bits(freq_ctl_val));
mct_Wait(1600);
/* Resend control word 2 */
- val = mct_ControlRC(pMCTstat, pDCTstat, MrsChipSel << 20, 2);
- mct_SendCtrlWrd(pMCTstat, pDCTstat, val);
+ val = mct_ControlRC(pMCTstat, pDCTstat, dct, MrsChipSel << rc_word_chip_select_lower_bit(), 2);
+ mct_SendCtrlWrd(pMCTstat, pDCTstat, dct, val);
mct_Wait(1600);
/* Resend control word 8 */
- val = mct_ControlRC(pMCTstat, pDCTstat, MrsChipSel << 20, 8);
- mct_SendCtrlWrd(pMCTstat, pDCTstat, val);
+ val = mct_ControlRC(pMCTstat, pDCTstat, dct, MrsChipSel << rc_word_chip_select_lower_bit(), 8);
+ mct_SendCtrlWrd(pMCTstat, pDCTstat, dct, val);
mct_Wait(1600);
}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
index 718a61fac7..c75f2ea698 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
@@ -441,13 +441,13 @@ static u32 mct_MR2(struct MCTStatStruc *pMCTstat,
u32 dev = pDCTstat->dev_dct;
u32 dword, ret;
+ /* The formula for chip select number is: CS = dimm*2+rank */
+ uint8_t dimm = MrsChipSel / 2;
+ uint8_t rank = MrsChipSel % 2;
+
if (is_fam15h()) {
uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
- /* The formula for chip select number is: CS = dimm*2+rank */
- uint8_t dimm = MrsChipSel / 2;
- uint8_t rank = MrsChipSel % 2;
-
/* FIXME: These parameters should be configurable
* For now, err on the side of caution and enable automatic 2x refresh
* when the DDR temperature rises above the internal limits
@@ -492,7 +492,7 @@ static u32 mct_MR2(struct MCTStatStruc *pMCTstat,
ret |= ((dword >> 10) & 3) << 9;
}
- printk(BIOS_SPEW, "Going to send MR2 control word %08x\n", ret);
+ printk(BIOS_SPEW, "Going to send DCT %d DIMM %d rank %d MR2 control word %08x\n", dct, dimm, rank, ret);
return ret;
}
@@ -503,6 +503,10 @@ static u32 mct_MR3(struct MCTStatStruc *pMCTstat,
u32 dev = pDCTstat->dev_dct;
u32 dword, ret;
+ /* The formula for chip select number is: CS = dimm*2+rank */
+ uint8_t dimm = MrsChipSel / 2;
+ uint8_t rank = MrsChipSel % 2;
+
if (is_fam15h()) {
ret = 0xc0000;
ret |= (MrsChipSel << 21);
@@ -523,7 +527,7 @@ static u32 mct_MR3(struct MCTStatStruc *pMCTstat,
ret |= (dword >> 24) & 7;
}
- printk(BIOS_SPEW, "Going to send MR3 control word %08x\n", ret);
+ printk(BIOS_SPEW, "Going to send DCT %d DIMM %d rank %d MR3 control word %08x\n", dct, dimm, rank, ret);
return ret;
}
@@ -534,6 +538,10 @@ static u32 mct_MR1(struct MCTStatStruc *pMCTstat,
u32 dev = pDCTstat->dev_dct;
u32 dword, ret;
+ /* The formula for chip select number is: CS = dimm*2+rank */
+ uint8_t dimm = MrsChipSel / 2;
+ uint8_t rank = MrsChipSel % 2;
+
if (is_fam15h()) {
uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
@@ -549,10 +557,6 @@ static u32 mct_MR1(struct MCTStatStruc *pMCTstat,
ret = 0x40000;
ret |= (MrsChipSel << 21);
- /* The formula for chip select number is: CS = dimm*2+rank */
- uint8_t dimm = MrsChipSel / 2;
- uint8_t rank = MrsChipSel % 2;
-
/* Determine if TQDS should be set */
if ((pDCTstat->Dimmx8Present & (1 << dimm))
&& (((dimm & 0x1)?(pDCTstat->Dimmx4Present&0x55):(pDCTstat->Dimmx4Present&0xaa)) != 0x0)
@@ -619,7 +623,7 @@ static u32 mct_MR1(struct MCTStatStruc *pMCTstat,
ret |= 1 << 12;
}
- printk(BIOS_SPEW, "Going to send MR1 control word %08x\n", ret);
+ printk(BIOS_SPEW, "Going to send DCT %d DIMM %d rank %d MR1 control word %08x\n", dct, dimm, rank, ret);
return ret;
}
@@ -630,6 +634,10 @@ static u32 mct_MR0(struct MCTStatStruc *pMCTstat,
u32 dev = pDCTstat->dev_dct;
u32 dword, ret, dword2;
+ /* The formula for chip select number is: CS = dimm*2+rank */
+ uint8_t dimm = MrsChipSel / 2;
+ uint8_t rank = MrsChipSel % 2;
+
if (is_fam15h()) {
ret = 0x00000;
ret |= (MrsChipSel << 21);
@@ -740,7 +748,7 @@ static u32 mct_MR0(struct MCTStatStruc *pMCTstat,
ret |= 1 << 8;
}
- printk(BIOS_SPEW, "Going to send MR0 control word %08x\n", ret);
+ printk(BIOS_SPEW, "Going to send DCT %d DIMM %d rank %d MR0 control word %08x\n", dct, dimm, rank, ret);
return ret;
}
@@ -807,6 +815,16 @@ void mct_DramInit_Sw_D(struct MCTStatStruc *pMCTstat,
/* 8.wait 360ns */
mct_Wait(80);
+ /* Set up address parity */
+ if ((pDCTstat->Status & (1 << SB_Registered))
+ || (pDCTstat->Status & (1 << SB_LoadReduced))) {
+ if (is_fam15h()) {
+ dword = Get_NB32_DCT(dev, dct, 0x90);
+ dword |= 1 << ParEn;
+ Set_NB32_DCT(dev, dct, 0x90, dword);
+ }
+ }
+
/* The following steps are performed with registered DIMMs only and
* must be done for each chip select pair */
if (pDCTstat->Status & (1 << SB_Registered))
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
index c85dc2721f..19b1b8f1e7 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
@@ -1142,8 +1142,10 @@ static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
uint8_t dimm;
uint8_t rank;
uint8_t lane;
+ uint8_t nibble;
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];
@@ -1159,6 +1161,11 @@ static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
print_debug_dqs("\nTrainRcvEn: Node", pDCTstat->Node_ID, 0);
print_debug_dqs("TrainRcvEn: Pass", Pass, 0);
+ train_both_nibbles = 0;
+ if (pDCTstat->Dimmx4Present)
+ if (is_fam15h())
+ train_both_nibbles = 1;
+
dev = pDCTstat->dev_dct;
index_reg = 0x98;
ch_start = 0;
@@ -1241,132 +1248,148 @@ static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
else
_2Ranks = 0;
for (rank = 0; rank < (_2Ranks + 1); rank++) {
- /* 2.10.5.8.2 (1)
- * Specify the target DIMM to be trained
- * Set TrNibbleSel = 0
- *
- * TODO: Add support for x4 DIMMs
- */
- dword = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008);
- dword &= ~(0x3 << 4); /* TrDimmSel */
- dword |= ((dimm & 0x3) << 4);
- dword &= ~(0x1 << 2); /* TrNibbleSel */
- Set_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008, dword);
-
- /* 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);
+ for (nibble = 0; nibble < (train_both_nibbles + 1); nibble++) {
+ /* 2.10.5.8.2 (1)
+ * Specify the target DIMM and nibble to be trained
+ */
+ dword = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008);
+ dword &= ~(0x3 << 4); /* TrDimmSel = dimm */
+ dword |= ((dimm & 0x3) << 4);
+ dword &= ~(0x1 << 2); /* TrNibbleSel = nibble */
+ dword |= ((nibble & 0x1) << 2);
+ Set_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008, dword);
+
+ /* 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);
- /* 2.10.5.8.2.1
- * Generate the DQS Receiver Enable Training Seed Values
- */
- if (Pass == FirstPass) {
- initial_seed = fam15_receiver_enable_training_seed(pDCTstat, Channel, dimm, rank, package_type);
+ /* 2.10.5.8.2.1
+ * Generate the DQS Receiver Enable Training Seed Values
+ */
+ if (Pass == FirstPass) {
+ initial_seed = fam15_receiver_enable_training_seed(pDCTstat, Channel, dimm, rank, package_type);
- /* Adjust seed for the minimum platform supported frequency */
- initial_seed = (uint16_t) (((((uint64_t) initial_seed) *
- fam15h_freq_tab[mem_clk] * 100) / (mctGet_NVbits(NV_MIN_MEMCLK) * 100)));
+ /* Adjust seed for the minimum platform supported frequency */
+ 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++) {
- uint16_t wl_pass1_delay;
- wl_pass1_delay = current_total_delay[lane];
+ for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
+ uint16_t wl_pass1_delay;
+ wl_pass1_delay = current_total_delay[lane];
- seed[lane] = initial_seed + wl_pass1_delay;
- }
- } else {
- uint8_t addr_prelaunch = 0; /* TODO: Fetch the correct value from RC2[0] */
- uint16_t register_delay;
- int16_t seed_prescaling;
-
- memcpy(current_total_delay, dqs_ret_pass1_total_delay, sizeof(current_total_delay));
- if ((pDCTstat->Status & (1 << SB_Registered))) {
- if (addr_prelaunch)
- register_delay = 0x30;
- else
- register_delay = 0x20;
- } else if ((pDCTstat->Status & (1 << SB_LoadReduced))) {
- /* TODO
- * Load reduced DIMM support unimplemented
- */
- register_delay = 0x0;
+ seed[lane] = initial_seed + wl_pass1_delay;
+ }
} else {
- register_delay = 0x0;
+ uint8_t addr_prelaunch = 0; /* TODO: Fetch the correct value from RC2[0] */
+ uint16_t register_delay;
+ int16_t seed_prescaling;
+
+ memcpy(current_total_delay, dqs_ret_pass1_total_delay, sizeof(current_total_delay));
+ if ((pDCTstat->Status & (1 << SB_Registered))) {
+ if (addr_prelaunch)
+ register_delay = 0x30;
+ else
+ register_delay = 0x20;
+ } else if ((pDCTstat->Status & (1 << SB_LoadReduced))) {
+ /* TODO
+ * Load reduced DIMM support unimplemented
+ */
+ register_delay = 0x0;
+ } else {
+ register_delay = 0x0;
+ }
+
+ for (lane = 0; lane < MAX_BYTE_LANES; 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++) {
- 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)));
- }
- }
+ seed_gross[lane] = (seed[lane] >> 5) & 0x1f;
+ seed_fine[lane] = seed[lane] & 0x1f;
- for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
- seed_gross[lane] = (seed[lane] >> 5) & 0x1f;
- seed_fine[lane] = seed[lane] & 0x1f;
+ /*if (seed_gross[lane] == 0)
+ seed_pre_gross[lane] = 0;
+ else */if (seed_gross[lane] & 0x1)
+ seed_pre_gross[lane] = 1;
+ else
+ seed_pre_gross[lane] = 2;
- /*if (seed_gross[lane] == 0)
- seed_pre_gross[lane] = 0;
- else */if (seed_gross[lane] & 0x1)
- seed_pre_gross[lane] = 1;
- else
- seed_pre_gross[lane] = 2;
+ /* Calculate phase recovery delays */
+ phase_recovery_delays[lane] = ((seed_pre_gross[lane] & 0x1f) << 5) | (seed_fine[lane] & 0x1f);
- /* Calculate phase recovery delays */
- phase_recovery_delays[lane] = ((seed_pre_gross[lane] & 0x1f) << 5) | (seed_fine[lane] & 0x1f);
+ /* Set the gross delay.
+ * NOTE: While the BKDG states to only program DqsRcvEnGrossDelay, this appears
+ * to have been a misprint as DqsRcvEnFineDelay should be set to zero as well.
+ */
+ current_total_delay[lane] = ((seed_gross[lane] & 0x1f) << 5);
+ }
- /* Set the gross delay.
- * NOTE: While the BKDG states to only program DqsRcvEnGrossDelay, this appears
- * to have been a misprint as DqsRcvEnFineDelay should be set to zero as well.
+ /* 2.10.5.8.2 (2) / 2.10.5.8.2.1 (5 6)
+ * Program PhRecFineDly and PhRecGrossDly
*/
- current_total_delay[lane] = ((seed_gross[lane] & 0x1f) << 5);
- }
+ write_dram_phase_recovery_control_registers(phase_recovery_delays, dev, Channel, dimm, index_reg);
- /* 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);
+ /* 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);
- /* 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);
+ /* 2.10.5.8.2 (3)
+ * Program DqsRcvTrEn = 1
+ */
+ dword = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008);
+ dword |= (0x1 << 13);
+ Set_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008, dword);
- /* 2.10.5.8.2 (3)
- * Program DqsRcvTrEn = 1
- */
- dword = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008);
- dword |= (0x1 << 13);
- Set_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008, dword);
+ /* 2.10.5.8.2 (4)
+ * Issue 192 read requests to the target rank
+ */
+ generate_dram_receiver_enable_training_pattern_fam15(pMCTstat, pDCTstat, Channel, Receiver + (rank & 0x1));
- /* 2.10.5.8.2 (4)
- * Issue 192 read requests to the target rank
- */
- generate_dram_receiver_enable_training_pattern_fam15(pMCTstat, pDCTstat, Channel, Receiver + (rank & 0x1));
+ /* 2.10.5.8.2 (5)
+ * Program DqsRcvTrEn = 0
+ */
+ dword = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008);
+ dword &= ~(0x1 << 13);
+ Set_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008, dword);
- /* 2.10.5.8.2 (5)
- * Program DqsRcvTrEn = 0
- */
- dword = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008);
- dword &= ~(0x1 << 13);
- Set_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008, dword);
+ /* 2.10.5.8.2 (6)
+ * Read PhRecGrossDly, PhRecFineDly
+ */
+ read_dram_phase_recovery_control_registers(phase_recovery_delays, dev, Channel, dimm, index_reg);
- /* 2.10.5.8.2 (6)
- * Read PhRecGrossDly, PhRecFineDly
- */
- read_dram_phase_recovery_control_registers(phase_recovery_delays, dev, Channel, dimm, index_reg);
+ /* 2.10.5.8.2 (7)
+ * Calculate and program the DQS Receiver Enable delay values
+ */
+ for (lane = 0; lane < MAX_BYTE_LANES; 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) {
+ 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)
+ * Average the trained values of both nibbles on x4 DIMMs
+ */
+ if (lane == 8)
+ 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;
+ }
+ }
- /* 2.10.5.8.2 (7)
- * Calculate and program the DQS Receiver Enable delay values
- */
- for (lane = 0; lane < MAX_BYTE_LANES; 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 (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];
+#if DQS_TRAIN_DEBUG > 1
+ for (lane = 0; lane < 8; lane++)
+ 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);
if (rank == 0) {
/* Back up the Rank 0 delays for later use */
@@ -1391,7 +1414,7 @@ static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat,
#if DQS_TRAIN_DEBUG > 0
for (lane = 0; lane < 8; lane++)
- print_debug_dqs_pair("\t\tTrainRcvEn55: Lane ", lane, " current_total_delay ", current_total_delay[lane], 2);
+ print_debug_dqs_pair("\t\tTrainRcvEn56: Lane ", lane, " current_total_delay ", current_total_delay[lane], 2);
#endif
}
}
@@ -1811,15 +1834,23 @@ void mctSetEccDQSRcvrEn_D(struct MCTStatStruc *pMCTstat,
}
void phyAssistedMemFnceTraining(struct MCTStatStruc *pMCTstat,
- struct DCTStatStruc *pDCTstatA)
+ struct DCTStatStruc *pDCTstatA, int16_t single_node_number)
{
u8 Node = 0;
struct DCTStatStruc *pDCTstat;
printk(BIOS_DEBUG, "%s: Start\n", __func__);
+ uint8_t start_node = 0;
+ uint8_t end_node = MAX_NODES_SUPPORTED;
+
+ if (single_node_number >= 0) {
+ start_node = single_node_number;
+ end_node = single_node_number;
+ }
+
/* FIXME: skip for Ax */
- for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ for (Node = start_node; Node < end_node; Node++) {
pDCTstat = pDCTstatA + Node;
if (!pDCTstat->NodePresent)
continue;
@@ -1843,6 +1874,8 @@ void phyAssistedMemFnceTraining(struct MCTStatStruc *pMCTstat,
if (!pDCTstat->DIMMValidDCT[dct])
continue;
+ printk(BIOS_SPEW, "%s: training node %d DCT %d\n", __func__, Node, dct);
+
/* Back up D18F2x9C_x0000_0004_dct[1:0] */
datc_backup = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000004);
@@ -1981,6 +2014,8 @@ void phyAssistedMemFnceTraining(struct MCTStatStruc *pMCTstat,
/* Restore D18F2x9C_x0000_0004_dct[1:0] */
Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000004, datc_backup);
+
+ printk(BIOS_SPEW, "%s: done training node %d DCT %d\n", __func__, Node, dct);
}
} else {
fenceDynTraining_D(pMCTstat, pDCTstat, 0);
@@ -1993,7 +2028,7 @@ void phyAssistedMemFnceTraining(struct MCTStatStruc *pMCTstat,
}
static uint32_t fenceDynTraining_D(struct MCTStatStruc *pMCTstat,
- struct DCTStatStruc *pDCTstat, u8 dct)
+ struct DCTStatStruc *pDCTstat, uint8_t dct)
{
u16 avRecValue;
u32 val;
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c b/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
index e163227949..4bfcc401ff 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
@@ -15,7 +15,7 @@
*/
static void FreqChgCtrlWrd(struct MCTStatStruc *pMCTstat,
- struct DCTStatStruc *pDCTstat);
+ struct DCTStatStruc *pDCTstat, uint8_t dct);
static void AgesaDelay(u32 msec)
@@ -349,11 +349,14 @@ static void ExitSelfRefresh(struct MCTStatStruc *pMCTstat,
}
void SetTargetFreq(struct MCTStatStruc *pMCTstat,
- struct DCTStatStruc *pDCTstat)
+ struct DCTStatStruc *pDCTstatA, uint8_t Node)
{
uint32_t dword;
uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
+ struct DCTStatStruc *pDCTstat;
+ pDCTstat = pDCTstatA + Node;
+
if (is_fam15h()) {
/* Program F2x[1, 0]90[DisDllShutDownSR]=1. */
if (pDCTstat->DIMMValidDCT[0]) {
@@ -387,7 +390,7 @@ void SetTargetFreq(struct MCTStatStruc *pMCTstat,
uint8_t dct;
for (dct = 0; dct < 2; dct++) {
if (pDCTstat->DIMMValidDCT[dct]) {
- phyAssistedMemFnceTraining(pMCTstat, pDCTstat);
+ phyAssistedMemFnceTraining(pMCTstat, pDCTstatA, Node);
InitPhyCompensation(pMCTstat, pDCTstat, dct);
}
}
@@ -434,7 +437,12 @@ void SetTargetFreq(struct MCTStatStruc *pMCTstat,
else
pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[1];
- FreqChgCtrlWrd(pMCTstat, pDCTstat);
+ if (pDCTstat->DIMMValidDCT[0]) {
+ FreqChgCtrlWrd(pMCTstat, pDCTstat, 0);
+ }
+ if (pDCTstat->DIMMValidDCT[1]) {
+ FreqChgCtrlWrd(pMCTstat, pDCTstat, 1);
+ }
}
}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
index fab7bc6a73..5deaca5cf6 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
@@ -31,9 +31,9 @@ u32 swapBankBits(struct DCTStatStruc *pDCTstat, uint8_t dct, uint32_t MRSValue);
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, u8 dimm, u8 pass);
+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);
+void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, uint8_t pass, uint8_t nibble);
static int32_t abs(int32_t val) {
if (val < 0)
@@ -72,6 +72,8 @@ uint8_t AgesaHwWlPhase1(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCT
{
u8 ByteLane;
u32 Value, Addr;
+ uint8_t nibble = 0;
+ uint8_t train_both_nibbles;
u16 Addl_Data_Offset, Addl_Data_Port;
sMCTStruct *pMCTData = pDCTstat->C_MCTPtr;
sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
@@ -84,98 +86,108 @@ uint8_t AgesaHwWlPhase1(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCT
DRAM_ADD_DCT_PHY_CONTROL_REG, TrDimmSelStart,
TrDimmSelEnd, (u32)dimm);
- if (is_fam15h()) {
- /* Set TrNibbleSel = 0
- *
- * TODO: Add support for x4 DIMMs
- */
- set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
- DRAM_ADD_DCT_PHY_CONTROL_REG, 2,
- 2, (u32)0);
- }
+ train_both_nibbles = 0;
+ if (pDCTstat->Dimmx4Present)
+ if (is_fam15h())
+ train_both_nibbles = 1;
- /* 2. Prepare the DIMMs for write levelization using DDR3-defined
- * MR commands. */
- prepareDimms(pMCTstat, pDCTstat, dct, dimm, TRUE);
+ for (nibble = 0; nibble < (train_both_nibbles + 1); nibble++) {
+ printk(BIOS_SPEW, "AgesaHwWlPhase1: training nibble %d\n", nibble);
- /* 3. After the DIMMs are configured, BIOS waits 40 MEMCLKs to
- * satisfy DDR3-defined internal DRAM timing.
- */
- if (is_fam15h())
- precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 40);
- else
- pMCTData->AgesaDelay(40);
+ if (is_fam15h()) {
+ /* Program F2x[1, 0]9C_x08[WrtLvTrEn]=0 */
+ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+ DRAM_ADD_DCT_PHY_CONTROL_REG, WrtLvTrEn, WrtLvTrEn, 0);
+
+ /* Set TrNibbleSel */
+ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+ DRAM_ADD_DCT_PHY_CONTROL_REG, 2,
+ 2, (uint32_t)nibble);
+ }
- /* 4. Configure the processor's DDR phy for write levelization training: */
- procConfig(pMCTstat, pDCTstat, dct, dimm, pass);
+ /* 2. Prepare the DIMMs for write levelization using DDR3-defined
+ * MR commands. */
+ prepareDimms(pMCTstat, pDCTstat, dct, dimm, TRUE);
- /* 5. Begin write levelization training:
- * Program F2x[1, 0]9C_x08[WrtLvTrEn]=1. */
- if (pDCTData->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx | AMD_FAM15_ALL))
- {
- set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
- DRAM_ADD_DCT_PHY_CONTROL_REG, WrtLvTrEn, WrtLvTrEn, 1);
- }
- else
- {
- /* Broadcast write to all D3Dbyte chipset register offset 0xc
- * Set bit 0 (wrTrain)
- * Program bit 4 to nibble being trained (only matters for x4dimms)
- * retain value of 3:2 (Trdimmsel)
- * reset bit 5 (FrzPR)
+ /* 3. After the DIMMs are configured, BIOS waits 40 MEMCLKs to
+ * satisfy DDR3-defined internal DRAM timing.
*/
- if (dct)
+ if (is_fam15h())
+ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 40);
+ else
+ pMCTData->AgesaDelay(40);
+
+ /* 4. Configure the processor's DDR phy for write levelization training: */
+ procConfig(pMCTstat, pDCTstat, dct, dimm, pass, nibble);
+
+ /* 5. Begin write levelization training:
+ * Program F2x[1, 0]9C_x08[WrtLvTrEn]=1. */
+ if (pDCTData->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx | AMD_FAM15_ALL))
{
- Addl_Data_Offset=0x198;
- Addl_Data_Port=0x19C;
+ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+ DRAM_ADD_DCT_PHY_CONTROL_REG, WrtLvTrEn, WrtLvTrEn, 1);
}
else
{
- Addl_Data_Offset=0x98;
- Addl_Data_Port=0x9C;
+ /* Broadcast write to all D3Dbyte chipset register offset 0xc
+ * Set bit 0 (wrTrain)
+ * Program bit 4 to nibble being trained (only matters for x4dimms)
+ * retain value of 3:2 (Trdimmsel)
+ * reset bit 5 (FrzPR)
+ */
+ if (dct)
+ {
+ Addl_Data_Offset=0x198;
+ Addl_Data_Port=0x19C;
+ }
+ else
+ {
+ Addl_Data_Offset=0x98;
+ Addl_Data_Port=0x9C;
+ }
+ Addr=0x0D00000C;
+ AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Offset), 31, 0, &Addr);
+ while ((get_Bits(pDCTData,FUN_DCT,pDCTData->NodeId, FUN_DCT, Addl_Data_Offset,
+ DctAccessDone, DctAccessDone)) == 0);
+ AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Port), 31, 0, &Value);
+ Value = bitTestSet(Value, 0); /* enable WL training */
+ Value = bitTestReset(Value, 4); /* for x8 only */
+ Value = bitTestReset(Value, 5); /* for hardware WL training */
+ AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Port), 31, 0, &Value);
+ Addr=0x4D030F0C;
+ AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Offset), 31, 0, &Addr);
+ while ((get_Bits(pDCTData,FUN_DCT,pDCTData->NodeId, FUN_DCT, Addl_Data_Offset,
+ DctAccessDone, DctAccessDone)) == 0);
}
- Addr=0x0D00000C;
- AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Offset), 31, 0, &Addr);
- while ((get_Bits(pDCTData,FUN_DCT,pDCTData->NodeId, FUN_DCT, Addl_Data_Offset,
- DctAccessDone, DctAccessDone)) == 0);
- AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Port), 31, 0, &Value);
- Value = bitTestSet(Value, 0); /* enable WL training */
- Value = bitTestReset(Value, 4); /* for x8 only */
- Value = bitTestReset(Value, 5); /* for hardware WL training */
- AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Port), 31, 0, &Value);
- Addr=0x4D030F0C;
- AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Offset), 31, 0, &Addr);
- while ((get_Bits(pDCTData,FUN_DCT,pDCTData->NodeId, FUN_DCT, Addl_Data_Offset,
- DctAccessDone, DctAccessDone)) == 0);
- }
- if (is_fam15h())
- proc_MFENCE();
+ if (is_fam15h())
+ proc_MFENCE();
- /* Wait 200 MEMCLKs. If executing pass 2, wait 32 MEMCLKs. */
- if (is_fam15h())
- precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 200);
- else
- pMCTData->AgesaDelay(140);
+ /* Wait 200 MEMCLKs. If executing pass 2, wait 32 MEMCLKs. */
+ if (is_fam15h())
+ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 200);
+ else
+ pMCTData->AgesaDelay(140);
- /* Program F2x[1, 0]9C_x08[WrtLevelTrEn]=0. */
- set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
- DRAM_ADD_DCT_PHY_CONTROL_REG, WrtLvTrEn, WrtLvTrEn, 0);
+ /* Program F2x[1, 0]9C_x08[WrtLevelTrEn]=0. */
+ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT,
+ DRAM_ADD_DCT_PHY_CONTROL_REG, WrtLvTrEn, WrtLvTrEn, 0);
- /* 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);
- }
+ /* 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);
+ }
- pDCTData->WLCriticalGrossDelayPrevPass = 0x1f;
+ pDCTData->WLCriticalGrossDelayPrevPass = 0x0;
+ }
return 0;
}
uint8_t AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat,
- u8 dct, u8 dimm, u8 pass)
+ uint8_t dct, uint8_t dimm, uint8_t pass)
{
u8 ByteLane;
uint8_t status = 0;
@@ -186,6 +198,12 @@ uint8_t AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCT
int32_t cgd = pDCTData->WLCriticalGrossDelayPrevPass;
uint8_t index = (uint8_t)(MAX_BYTE_LANES * dimm);
+ printk(BIOS_SPEW, "\toriginal critical gross delay: %d\n", cgd);
+
+ /* FIXME
+ * For now, disable CGD adjustment as it seems to interfere with registered DIMM training
+ */
+
/* Calculate the Critical Gross Delay */
for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
/* Calculate the gross delay differential for this lane */
@@ -201,6 +219,8 @@ uint8_t AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCT
cgd = gross_diff[ByteLane];
}
+ printk(BIOS_SPEW, "\tnew critical gross delay: %d\n", cgd);
+
pDCTData->WLCriticalGrossDelayPrevPass = cgd;
if (pDCTstat->Speed != pDCTstat->TargetFreq) {
@@ -277,7 +297,7 @@ uint8_t AgesaHwWlPhase3(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCT
gross_diff[ByteLane] = pDCTData->WLSeedGrossDelay[index+ByteLane] + pDCTData->WLGrossDelay[index+ByteLane];
gross_diff[ByteLane] -= pDCTData->WLSeedPreGrossDelay[index+ByteLane];
- /* Prevent underflow in the presence of noise / instability*/
+ /* Prevent underflow in the presence of noise / instability */
if (gross_diff[ByteLane] < cgd)
gross_diff[ByteLane] = cgd;
@@ -285,7 +305,8 @@ uint8_t AgesaHwWlPhase3(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCT
}
} else {
dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0xa8);
- dword &= ~(0x3 << 24); /* WrDqDqsEarly = 0 */
+ dword &= ~(0x3 << 24); /* WrDqDqsEarly = pDCTData->WrDqsGrossDlyBaseOffset */
+ dword |= ((pDCTData->WrDqsGrossDlyBaseOffset & 0x3) << 24);
Set_NB32_DCT(pDCTstat->dev_dct, dct, 0xa8, dword);
}
}
@@ -955,7 +976,7 @@ static uint16_t fam15h_next_lowest_memclk_freq(uint16_t memclk_freq)
#endif
/*-----------------------------------------------------------------------------
- * void procConfig(MCTStruct *MCTData,DCTStruct *DCTData, u8 Dimm, u8 Pass)
+ * void procConfig(MCTStruct *MCTData,DCTStruct *DCTData, u8 Dimm, u8 Pass, u8 Nibble)
*
* Description:
* This function programs the ODT values for the NB
@@ -968,13 +989,14 @@ static uint16_t fam15h_next_lowest_memclk_freq(uint16_t memclk_freq)
* OUT
* ----------------------------------------------------------------------------
*/
-void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, u8 dimm, u8 pass)
+void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t dimm, uint8_t pass, uint8_t nibble)
{
u8 ByteLane, MemClkFreq;
int32_t Seed_Gross;
int32_t Seed_Fine;
uint8_t Seed_PreGross;
u32 Value, Addr;
+ uint32_t dword;
u16 Addl_Data_Offset, Addl_Data_Port;
sMCTStruct *pMCTData = pDCTstat->C_MCTPtr;
sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
@@ -1044,10 +1066,17 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
uint8_t AddrCmdPrelaunch = 0; /* TODO: Fetch the correct value from RC2[0] */
uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE);
uint16_t Seed_Total = 0;
+ pDCTData->WrDqsGrossDlyBaseOffset = 0x0;
if (package_type == PT_GR) {
/* Socket G34: Fam15h BKDG v3.14 Table 96 */
if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
+ /* TODO
+ * Implement mainboard-specific seed and
+ * WrDqsGrossDly base overrides.
+ * 0x41 and 0x0 are the "stock" values
+ */
Seed_Total = 0x41;
+ pDCTData->WrDqsGrossDlyBaseOffset = 0x2;
} else if (pDCTData->Status[DCT_STATUS_LOAD_REDUCED]) {
Seed_Total = 0x0;
} else {
@@ -1129,15 +1158,16 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
printk(BIOS_SPEW, "\tLane %02x initial seed: %04x\n", ByteLane, ((Seed_Gross & 0x1f) << 5) | (Seed_Fine & 0x1f));
}
} else {
- /* Pass 2 */
- /* From BKDG, Write Leveling Seed Value. */
- if (is_fam15h()) {
- uint32_t RegisterDelay;
- int32_t SeedTotal;
- int32_t SeedTotalPreScaling;
- uint8_t AddrCmdPrelaunch = 0; /* TODO: Fetch the correct value from RC2[0] */
+ if (nibble == 0) {
+ /* Pass 2 */
+ /* From BKDG, Write Leveling Seed Value. */
+ if (is_fam15h()) {
+ uint32_t RegisterDelay;
+ int32_t SeedTotal[MAX_BYTE_LANES];
+ int32_t SeedTotalPreScaling[MAX_BYTE_LANES];
+ uint32_t WrDqDqsEarly;
+ uint8_t AddrCmdPrelaunch = 0; /* TODO: Fetch the correct value from RC2[0] */
- for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
if (AddrCmdPrelaunch)
RegisterDelay = 0x30;
@@ -1146,84 +1176,133 @@ void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ui
} else {
RegisterDelay = 0;
}
+
/* Retrieve WrDqDqsEarly */
- AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId), FUN_DCT, 0xa8), 25, 24, &Value);
+ dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0xa8);
+ WrDqDqsEarly = (dword >> 24) & 0x3;
- /* Calculate adjusted seed values */
- SeedTotal = (pDCTData->WLFineDelayPrevPass[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) |
- ((pDCTData->WLGrossDelayPrevPass[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) << 5);
- SeedTotalPreScaling = (SeedTotal - RegisterDelay - (0x20 * Value));
- SeedTotal = (int32_t) (RegisterDelay + ((((int64_t) SeedTotalPreScaling) *
- fam15h_freq_tab[MemClkFreq] * 100) / (fam15h_freq_tab[pDCTData->WLPrevMemclkFreq] * 100)));
+ /* FIXME
+ * Ignore WrDqDqsEarly for now to work around training issues
+ */
+ WrDqDqsEarly = 0;
- if (SeedTotal >= 0) {
- Seed_Gross = SeedTotal / 32;
- Seed_Fine = SeedTotal % 32;
- } else {
- Seed_Gross = (SeedTotal / 32) - 1;
- Seed_Fine = (SeedTotal % 32) + 32;
+ /* Generate new seed values */
+ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
+ /* Calculate adjusted seed values */
+ SeedTotal[ByteLane] = (pDCTData->WLFineDelayPrevPass[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) |
+ ((pDCTData->WLGrossDelayPrevPass[MAX_BYTE_LANES*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)));
}
- if (Seed_Gross == 0)
- Seed_PreGross = 0;
- else if (Seed_Gross & 0x1)
- Seed_PreGross = 1;
- else
- Seed_PreGross = 2;
+ /* Generate register values from seeds */
+ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) {
+ printk(BIOS_SPEW, "\tLane %02x scaled delay: %04x\n", ByteLane, SeedTotal[ByteLane]);
- /* 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;
+ if (SeedTotal[ByteLane] >= 0) {
+ Seed_Gross = SeedTotal[ByteLane] / 32;
+ Seed_Fine = SeedTotal[ByteLane] % 32;
+ } else {
+ Seed_Gross = (SeedTotal[ByteLane] / 32) - 1;
+ Seed_Fine = (SeedTotal[ByteLane] % 32) + 32;
+ }
- pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross;
- pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
+ if (Seed_Gross == 0)
+ Seed_PreGross = 0;
+ else if (Seed_Gross & 0x1)
+ Seed_PreGross = 1;
+ else
+ Seed_PreGross = 2;
- printk(BIOS_SPEW, "\tLane %02x new seed: %04x\n", ByteLane, ((Seed_Gross & 0x1f) << 5) | (Seed_Fine & 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++)
- {
- if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
- if (AddrCmdPrelaunch == 0)
- RegisterDelay = 0x20;
+ /* The BKDG-recommended algorithm causes problems with registered DIMMs on some systems
+ * due to the long register delays causing premature total delay wrap-around.
+ * Attempt to work around this...
+ */
+ 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->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross;
+ pDCTData->WLFineDelay[MAX_BYTE_LANES*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));
+ }
+ } 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++)
+ {
+ if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
+ if (AddrCmdPrelaunch == 0)
+ RegisterDelay = 0x20;
+ else
+ RegisterDelay = 0x30;
+ } else {
+ RegisterDelay = 0;
+ }
+ SeedTotalPreScaling = ((pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) |
+ (pDCTData->WLGrossDelay[MAX_BYTE_LANES*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) *
+ fam10h_freq_tab[MemClkFreq] * 100) / (fam10h_freq_tab[3] * 100));
+ Seed_Gross = SeedTotal / 32;
+ Seed_Fine = SeedTotal & 0x1f;
+ if (Seed_Gross == 0)
+ Seed_Gross = 0;
+ else if (Seed_Gross & 0x1)
+ Seed_Gross = 1;
else
- RegisterDelay = 0x30;
- } else {
- RegisterDelay = 0;
+ Seed_Gross = 2;
+
+ /* The BKDG-recommended algorithm causes problems with registered DIMMs on some systems
+ * due to the long register delays causing premature total delay wrap-around.
+ * Attempt to work around this...
+ */
+ SeedTotal = ((Seed_Gross & 0x1f) << 5) | (Seed_Fine & 0x1f);
+ SeedTotal += RegisterDelay;
+ 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;
+
+ 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));
}
- SeedTotalPreScaling = ((pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) |
- (pDCTData->WLGrossDelay[MAX_BYTE_LANES*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) *
- fam10h_freq_tab[MemClkFreq] * 100) / (fam10h_freq_tab[3] * 100));
- Seed_Gross = SeedTotal / 32;
- Seed_Fine = SeedTotal & 0x1f;
- if (Seed_Gross == 0)
- Seed_Gross = 0;
- else if (Seed_Gross & 0x1)
- Seed_Gross = 1;
- else
- Seed_Gross = 2;
+ }
- /* The BKDG-recommended algorithm causes problems with registered DIMMs on some systems
- * due to the long register delays causing premature total delay wrap-around.
- * Attempt to work around this...
- */
- SeedTotal = ((Seed_Gross & 0x1f) << 5) | (Seed_Fine & 0x1f);
- SeedTotal += RegisterDelay;
- Seed_Gross = SeedTotal / 32;
- Seed_Fine = SeedTotal & 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];
+ }
+ } 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];
- pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
- pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
+ 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];
- printk(BIOS_SPEW, "\tLane %02x new seed: %04x\n", ByteLane, ((Seed_Gross & 0x1f) << 5) | (Seed_Fine & 0x1f));
+ 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));
+ }
+ } 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];
+
+ 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));
+ }
}
}
}
@@ -1354,7 +1433,7 @@ void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8
}
/*-----------------------------------------------------------------------------
- * void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 Dimm)
+ * void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 Dimm, u8 Nibble)
*
* Description:
* This function reads the write levelization byte delay from the Phase
@@ -1372,7 +1451,7 @@ 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)
+void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, uint8_t pass, uint8_t nibble)
{
sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct];
u8 fineStartLoc, fineEndLoc, grossStartLoc, grossEndLoc, tempB, tempB1, index;
@@ -1423,7 +1502,16 @@ void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8
fine = 0;
}
}
- pDCTData->WLFineDelay[index+ByteLane] = (u8)fine;
- pDCTData->WLGrossDelay[index+ByteLane] = (u8)gross;
- printk(BIOS_SPEW, "\tLane %02x final adjusted value: %04x\n", ByteLane, ((gross & 0x1f) << 5) | (fine & 0x1f));
+ if (nibble == 0) {
+ pDCTData->WLFineDelay[index+ByteLane] = (uint8_t)fine;
+ pDCTData->WLGrossDelay[index+ByteLane] = (uint8_t)gross;
+ } else {
+ uint32_t WLTotalDelay = ((pDCTData->WLGrossDelay[index+ByteLane] & 0x1f) << 5) | (pDCTData->WLFineDelay[index+ByteLane] & 0x1f);
+ WLTotalDelay += ((gross & 0x1f) << 5) | (fine & 0x1f);
+ WLTotalDelay /= 2;
+ pDCTData->WLFineDelay[index+ByteLane] = (uint8_t)(WLTotalDelay & 0x1f);
+ pDCTData->WLGrossDelay[index+ByteLane] = (uint8_t)((WLTotalDelay >> 5) & 0x1f);
+ }
+
+ printk(BIOS_SPEW, "\tLane %02x adjusted value: %04x\n", ByteLane, ((pDCTData->WLGrossDelay[index+ByteLane] & 0x1f) << 5) | (pDCTData->WLFineDelay[index+ByteLane] & 0x1f));
}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h
index e14c433734..4de7af0de6 100644
--- a/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h
@@ -115,16 +115,21 @@ typedef struct _sDCTStruct
u8 DctTrain; /* Current DCT being trained */
u8 CurrDct; /* Current DCT number (0 or 1) */
u8 DctCSPresent; /* Current DCT CS mapping */
+ uint8_t WrDqsGrossDlyBaseOffset;
int32_t WLSeedGrossDelay[MAX_BYTE_LANES*MAX_LDIMMS]; /* Write Levelization Seed Gross Delay */
/* per byte Lane Per Logical DIMM*/
int32_t WLSeedFineDelay[MAX_BYTE_LANES*MAX_LDIMMS]; /* Write Levelization Seed Fine Delay */
/* per byte Lane Per Logical DIMM*/
int32_t WLSeedPreGrossDelay[MAX_BYTE_LANES*MAX_LDIMMS]; /* Write Levelization Seed Pre-Gross Delay */
/* per byte Lane Per Logical DIMM*/
- u8 WLGrossDelay[MAX_BYTE_LANES*MAX_LDIMMS]; /* Write Levelization Gross Delay */
- /* per byte Lane Per Logical DIMM*/
- u8 WLFineDelay[MAX_BYTE_LANES*MAX_LDIMMS]; /* Write Levelization Fine Delay */
- /* per byte Lane Per Logical DIMM*/
+ uint8_t WLSeedPreGrossPrevNibble[MAX_BYTE_LANES*MAX_LDIMMS];
+ uint8_t WLSeedGrossPrevNibble[MAX_BYTE_LANES*MAX_LDIMMS];
+ uint8_t WLSeedFinePrevNibble[MAX_BYTE_LANES*MAX_LDIMMS];
+ /* per byte Lane Per Logical DIMM*/
+ u8 WLGrossDelay[MAX_BYTE_LANES*MAX_LDIMMS]; /* Write Levelization Gross Delay */
+ /* per byte Lane Per Logical DIMM*/
+ u8 WLFineDelay[MAX_BYTE_LANES*MAX_LDIMMS]; /* Write Levelization Fine Delay */
+ /* per byte Lane Per Logical DIMM*/
u8 WLGrossDelayFirstPass[MAX_BYTE_LANES*MAX_LDIMMS]; /* First-Pass Write Levelization Gross Delay */
/* per byte Lane Per Logical DIMM*/
u8 WLFineDelayFirstPass[MAX_BYTE_LANES*MAX_LDIMMS]; /* First-Pass Write Levelization Fine Delay */