northbridge/amd/amdmct: Fix broken AMD K10 DDR3 memory initalization
The native AMD DDR3 memory initialization code was riddled with numerous errors and was missing critical configuration code segments; this made it so that DDR3 memory did not function on most AMD boards. This patch corrects enough of the DDR3 initialization such that UDIMMs can be used on most channels of G34 Opteron boards. Further work is needed to fix the broken RDIMM code and remaining UDIMM issues. Change-Id: Iab690db769e820600693ad1170085623b177b94e Signed-off-by: Timothy Pearson <tpearson@raptorengineeringinc.com> Reviewed-on: http://review.coreboot.org/11941 Tested-by: build bot (Jenkins) Reviewed-by: Aaron Durbin <adurbin@chromium.org> Tested-by: Raptor Engineering Automated Test Stand <noreply@raptorengineeringinc.com>
This commit is contained in:
parent
7a5413a81c
commit
b8a355dcdf
|
@ -26,7 +26,6 @@ static void print_tx(const char *strval, u32 val)
|
|||
printk(BIOS_DEBUG, "%s%08x\n", strval, val);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static void print_t(const char *strval)
|
||||
{
|
||||
|
@ -34,6 +33,7 @@ static void print_t(const char *strval)
|
|||
printk(BIOS_DEBUG, "%s", strval);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static void print_tf(const char *func, const char *strval)
|
||||
{
|
||||
|
|
|
@ -542,7 +542,6 @@ static void HTMemMapInit_D(struct MCTStatStruc *pMCTstat,
|
|||
pDCTstat = pDCTstatA + Node;
|
||||
devx = pDCTstat->dev_map;
|
||||
DramSelBaseAddr = 0;
|
||||
pDCTstat = pDCTstatA + Node;
|
||||
if (!pDCTstat->GangedMode) {
|
||||
DramSelBaseAddr = pDCTstat->NodeSysLimit - pDCTstat->DCTSysLimit;
|
||||
/*In unganged mode, we must add DCT0 and DCT1 to DCTSysLimit */
|
||||
|
|
|
@ -209,12 +209,24 @@ static const u8 Table_DQSRcvEn_Offset[] = {0x00,0x01,0x10,0x11,0x2};
|
|||
MEMCLK_MAPPING EQU 00010000b, 00000100b, 00001000b, 00100000b, 00000000b, 00000000b, 00000000b, 00000000b
|
||||
*/
|
||||
|
||||
/* Note: If you are not sure about the pin mappings at initial stage, we dont have to disable MemClk.
|
||||
* Set entries in the tables all 0xFF. */
|
||||
/* ==========================================================================================
|
||||
* Set up clock pin to DIMM mappings,
|
||||
* NOTE: If you are not sure about the pin mappings, you can keep all MemClk signals active,
|
||||
* just set all entries in the relevant table(s) to 0xff.
|
||||
* ==========================================================================================
|
||||
*/
|
||||
static const u8 Tab_L1CLKDis[] = {0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04};
|
||||
static const u8 Tab_AM3CLKDis[] = {0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00};
|
||||
static const u8 Tab_S1CLKDis[] = {0xA2, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
/* C32: Enable CS0 - CS3 clocks (DIMM0 - DIMM1) */
|
||||
static const u8 Tab_C32CLKDis[] = {0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
/* G34: Enable CS0 - CS3 clocks (DIMM0 - DIMM1) */
|
||||
static const u8 Tab_G34CLKDis[] = {0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
static const u8 Tab_ManualCLKDis[]= {0x10, 0x04, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00};
|
||||
/* ========================================================================================== */
|
||||
|
||||
static const u8 Table_Comp_Rise_Slew_20x[] = {7, 3, 2, 2, 0xFF};
|
||||
static const u8 Table_Comp_Rise_Slew_15x[] = {7, 7, 3, 2, 0xFF};
|
||||
|
@ -277,6 +289,11 @@ restartinit:
|
|||
for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
|
||||
struct DCTStatStruc *pDCTstat;
|
||||
pDCTstat = pDCTstatA + Node;
|
||||
|
||||
/* Zero out data structures to avoid false detection of DIMMs */
|
||||
memset(pDCTstat, 0, sizeof(struct DCTStatStruc));
|
||||
|
||||
/* Initialize data structures */
|
||||
pDCTstat->Node_ID = Node;
|
||||
pDCTstat->dev_host = PA_HOST(Node);
|
||||
pDCTstat->dev_map = PA_MAP(Node);
|
||||
|
@ -284,17 +301,22 @@ restartinit:
|
|||
pDCTstat->dev_nbmisc = PA_NBMISC(Node);
|
||||
pDCTstat->NodeSysBase = node_sys_base;
|
||||
|
||||
printk(BIOS_DEBUG, "%s: mct_init Node %d\n", __func__, Node);
|
||||
mct_init(pMCTstat, pDCTstat);
|
||||
mctNodeIDDebugPort_D();
|
||||
pDCTstat->NodePresent = NodePresent_D(Node);
|
||||
if (pDCTstat->NodePresent) { /* See if Node is there*/
|
||||
printk(BIOS_DEBUG, "%s: clear_legacy_Mode\n", __func__);
|
||||
clear_legacy_Mode(pMCTstat, pDCTstat);
|
||||
pDCTstat->LogicalCPUID = mctGetLogicalCPUID_D(Node);
|
||||
|
||||
printk(BIOS_DEBUG, "%s: mct_InitialMCT_D\n", __func__);
|
||||
mct_InitialMCT_D(pMCTstat, pDCTstat);
|
||||
|
||||
printk(BIOS_DEBUG, "%s: mctSMBhub_Init\n", __func__);
|
||||
mctSMBhub_Init(Node); /* Switch SMBUS crossbar to proper node*/
|
||||
|
||||
printk(BIOS_DEBUG, "%s: mct_initDCT\n", __func__);
|
||||
mct_initDCT(pMCTstat, pDCTstat);
|
||||
if (pDCTstat->ErrCode == SC_FatalErr) {
|
||||
goto fatalexit; /* any fatal errors?*/
|
||||
|
@ -345,6 +367,7 @@ restartinit:
|
|||
|
||||
mct_FinalMCT_D(pMCTstat, pDCTstatA);
|
||||
printk(BIOS_DEBUG, "mctAutoInitMCT_D Done: Global Status: %x\n", pMCTstat->GStatus);
|
||||
|
||||
return;
|
||||
|
||||
fatalexit:
|
||||
|
@ -560,7 +583,6 @@ static void HTMemMapInit_D(struct MCTStatStruc *pMCTstat,
|
|||
pDCTstat = pDCTstatA + Node;
|
||||
devx = pDCTstat->dev_map;
|
||||
DramSelBaseAddr = 0;
|
||||
pDCTstat = pDCTstatA + Node; /* ??? */
|
||||
if (!pDCTstat->GangedMode) {
|
||||
DramSelBaseAddr = pDCTstat->NodeSysLimit - pDCTstat->DCTSysLimit;
|
||||
/*In unganged mode, we must add DCT0 and DCT1 to DCTSysLimit */
|
||||
|
@ -645,6 +667,7 @@ static void HTMemMapInit_D(struct MCTStatStruc *pMCTstat,
|
|||
devx = pDCTstat->dev_map;
|
||||
|
||||
if (pDCTstat->NodePresent) {
|
||||
printk(BIOS_DEBUG, " Copy dram map from Node 0 to Node %02x \n", Node);
|
||||
reg = 0x40; /*Dram Base 0*/
|
||||
do {
|
||||
val = Get_NB32(dev, reg);
|
||||
|
@ -1162,7 +1185,7 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat,
|
|||
|
||||
/* Program DRAM Timing values */
|
||||
DramTimingLo = 0; /* Dram Timing Low init */
|
||||
val = pDCTstat->CASL - 2; /* pDCTstat.CASL to reg. definition */
|
||||
val = pDCTstat->CASL - 4; /* pDCTstat.CASL to reg. definition */
|
||||
DramTimingLo |= val;
|
||||
|
||||
val = pDCTstat->Trcd - Bias_TrcdT;
|
||||
|
@ -1406,18 +1429,16 @@ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
|
|||
else if (tCKproposed16x <= 24) {
|
||||
pDCTstat->TargetFreq = 6;
|
||||
tCKproposed16x = 24;
|
||||
}
|
||||
else if (tCKproposed16x <= 30) {
|
||||
} else if (tCKproposed16x <= 30) {
|
||||
pDCTstat->TargetFreq = 5;
|
||||
tCKproposed16x = 30;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
pDCTstat->TargetFreq = 4;
|
||||
tCKproposed16x = 40;
|
||||
}
|
||||
/* Running through this loop twice:
|
||||
- First time find tCL at target frequency
|
||||
- Second tim find tCL at 400MHz */
|
||||
- Second time find tCL at 400MHz */
|
||||
|
||||
for (;;) {
|
||||
CLT_Fail = 0;
|
||||
|
@ -1451,7 +1472,7 @@ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
|
|||
CLT_Fail = 1;
|
||||
/* get CL and T */
|
||||
if (!CLT_Fail) {
|
||||
bytex = CLactual - 2;
|
||||
bytex = CLactual;
|
||||
if (tCKproposed16x == 20)
|
||||
byte = 7;
|
||||
else if (tCKproposed16x == 24)
|
||||
|
@ -1632,7 +1653,7 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
|
|||
val = 0x0f; /* recommended setting (default) */
|
||||
DramConfigHi |= val << 24;
|
||||
|
||||
if (pDCTstat->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Bx))
|
||||
if (pDCTstat->LogicalCPUID & (AMD_DR_Dx | AMD_DR_Cx | AMD_DR_Bx))
|
||||
DramConfigHi |= 1 << DcqArbBypassEn;
|
||||
|
||||
/* Build MemClkDis Value from Dram Timing Lo and
|
||||
|
@ -1657,6 +1678,10 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
|
|||
p = Tab_L1CLKDis;
|
||||
else if (byte == PT_M2 || byte == PT_AS)
|
||||
p = Tab_AM3CLKDis;
|
||||
else if (byte == PT_C3)
|
||||
p = Tab_C32CLKDis;
|
||||
else if (byte == PT_GR)
|
||||
p = Tab_G34CLKDis;
|
||||
else
|
||||
p = Tab_S1CLKDis;
|
||||
|
||||
|
@ -2102,8 +2127,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
|
|||
if (byte == JED_RDIMM || byte == JED_MiniRDIMM) {
|
||||
RegDIMMPresent |= 1 << i;
|
||||
pDCTstat->DimmRegistered[i] = 1;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
pDCTstat->DimmRegistered[i] = 0;
|
||||
}
|
||||
/* Check ECC capable */
|
||||
|
@ -2977,9 +3001,9 @@ static void mct_FinalMCT_D(struct MCTStatStruc *pMCTstat,
|
|||
} else { /* For Dx CPU */
|
||||
val = 0x0CE00F00 | 1 << 29/* FlushWrOnStpGnt */;
|
||||
if (!(pDCTstat->GangedMode))
|
||||
val |= 0x20; /* MctWrLimit = 8 for Unganed mode */
|
||||
val |= 0x20; /* MctWrLimit = 8 for Unganged mode */
|
||||
else
|
||||
val |= 0x40; /* MctWrLimit = 16 for ganed mode */
|
||||
val |= 0x40; /* MctWrLimit = 16 for ganged mode */
|
||||
Set_NB32(pDCTstat->dev_dct, 0x11C, val);
|
||||
|
||||
val = Get_NB32(pDCTstat->dev_dct, 0x1B0);
|
||||
|
@ -3414,6 +3438,138 @@ static void mct_BeforeDramInit_Prod_D(struct MCTStatStruc *pMCTstat,
|
|||
Set_NB32(dev, 0x98 + reg_off, 0x0D000030);
|
||||
Set_NB32(dev, 0x9C + reg_off, dword);
|
||||
Set_NB32(dev, 0x98 + reg_off, 0x4D040F30);
|
||||
|
||||
/* FIXME
|
||||
* Mainboards need to be able to specify the maximum number of DIMMs installable per channel
|
||||
* For now assume a maximum of 2 DIMMs per channel can be installed
|
||||
*/
|
||||
uint8_t MaxDimmsInstallable = 2;
|
||||
|
||||
/* Obtain number of DIMMs on channel */
|
||||
uint8_t dimm_count = pDCTstat->MAdimms[i];
|
||||
uint8_t rank_count_dimm0;
|
||||
uint8_t rank_count_dimm1;
|
||||
uint32_t odt_pattern_0;
|
||||
uint32_t odt_pattern_1;
|
||||
uint32_t odt_pattern_2;
|
||||
uint32_t odt_pattern_3;
|
||||
|
||||
/* Select appropriate ODT pattern for installed DIMMs
|
||||
* Refer to the BKDG Rev. 3.62, page 120 onwards
|
||||
*/
|
||||
if (pDCTstat->C_DCTPtr[i]->Status[DCT_STATUS_REGISTERED]) {
|
||||
if (MaxDimmsInstallable == 2) {
|
||||
if (dimm_count == 1) {
|
||||
/* 1 DIMM detected */
|
||||
rank_count_dimm1 = pDCTstat->C_DCTPtr[i]->DimmRanks[1];
|
||||
if (rank_count_dimm1 == 1) {
|
||||
odt_pattern_0 = 0x00000000;
|
||||
odt_pattern_1 = 0x00000000;
|
||||
odt_pattern_2 = 0x00000000;
|
||||
odt_pattern_3 = 0x00020000;
|
||||
} else if (rank_count_dimm1 == 2) {
|
||||
odt_pattern_0 = 0x00000000;
|
||||
odt_pattern_1 = 0x00000000;
|
||||
odt_pattern_2 = 0x00000000;
|
||||
odt_pattern_3 = 0x02080000;
|
||||
} else if (rank_count_dimm1 == 4) {
|
||||
odt_pattern_0 = 0x00000000;
|
||||
odt_pattern_1 = 0x00000000;
|
||||
odt_pattern_2 = 0x020a0000;
|
||||
odt_pattern_3 = 0x080a0000;
|
||||
} else {
|
||||
/* Fallback */
|
||||
odt_pattern_0 = 0x00000000;
|
||||
odt_pattern_1 = 0x00000000;
|
||||
odt_pattern_2 = 0x00000000;
|
||||
odt_pattern_3 = 0x00000000;
|
||||
}
|
||||
} else {
|
||||
/* 2 DIMMs detected */
|
||||
rank_count_dimm0 = pDCTstat->C_DCTPtr[i]->DimmRanks[0];
|
||||
rank_count_dimm1 = pDCTstat->C_DCTPtr[i]->DimmRanks[1];
|
||||
if ((rank_count_dimm0 < 4) && (rank_count_dimm1 < 4)) {
|
||||
odt_pattern_0 = 0x00000000;
|
||||
odt_pattern_1 = 0x01010202;
|
||||
odt_pattern_2 = 0x00000000;
|
||||
odt_pattern_3 = 0x09030603;
|
||||
} else if ((rank_count_dimm0 < 4) && (rank_count_dimm1 == 4)) {
|
||||
odt_pattern_0 = 0x01010000;
|
||||
odt_pattern_1 = 0x01010a0a;
|
||||
odt_pattern_2 = 0x01090000;
|
||||
odt_pattern_3 = 0x01030e0b;
|
||||
} else if ((rank_count_dimm0 == 4) && (rank_count_dimm1 < 4)) {
|
||||
odt_pattern_0 = 0x00000202;
|
||||
odt_pattern_1 = 0x05050202;
|
||||
odt_pattern_2 = 0x00000206;
|
||||
odt_pattern_3 = 0x0d070203;
|
||||
} else if ((rank_count_dimm0 == 4) && (rank_count_dimm1 == 4)) {
|
||||
odt_pattern_0 = 0x05050a0a;
|
||||
odt_pattern_1 = 0x05050a0a;
|
||||
odt_pattern_2 = 0x050d0a0e;
|
||||
odt_pattern_3 = 0x05070a0b;
|
||||
} else {
|
||||
/* Fallback */
|
||||
odt_pattern_0 = 0x00000000;
|
||||
odt_pattern_1 = 0x00000000;
|
||||
odt_pattern_2 = 0x00000000;
|
||||
odt_pattern_3 = 0x00000000;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* FIXME
|
||||
* 3 DIMMs per channel UNIMPLEMENTED
|
||||
*/
|
||||
odt_pattern_0 = 0x00000000;
|
||||
odt_pattern_1 = 0x00000000;
|
||||
odt_pattern_2 = 0x00000000;
|
||||
odt_pattern_3 = 0x00000000;
|
||||
}
|
||||
} else {
|
||||
if (MaxDimmsInstallable == 2) {
|
||||
if (dimm_count == 1) {
|
||||
/* 1 DIMM detected */
|
||||
rank_count_dimm1 = pDCTstat->C_DCTPtr[i]->DimmRanks[1];
|
||||
if (rank_count_dimm1 == 1) {
|
||||
odt_pattern_0 = 0x00000000;
|
||||
odt_pattern_1 = 0x00000000;
|
||||
odt_pattern_2 = 0x00000000;
|
||||
odt_pattern_3 = 0x00020000;
|
||||
} else if (rank_count_dimm1 == 2) {
|
||||
odt_pattern_0 = 0x00000000;
|
||||
odt_pattern_1 = 0x00000000;
|
||||
odt_pattern_2 = 0x00000000;
|
||||
odt_pattern_3 = 0x02080000;
|
||||
} else {
|
||||
/* Fallback */
|
||||
odt_pattern_0 = 0x00000000;
|
||||
odt_pattern_1 = 0x00000000;
|
||||
odt_pattern_2 = 0x00000000;
|
||||
odt_pattern_3 = 0x00000000;
|
||||
}
|
||||
} else {
|
||||
/* 2 DIMMs detected */
|
||||
odt_pattern_0 = 0x00000000;
|
||||
odt_pattern_1 = 0x01010202;
|
||||
odt_pattern_2 = 0x00000000;
|
||||
odt_pattern_3 = 0x09030603;
|
||||
}
|
||||
} else {
|
||||
/* FIXME
|
||||
* 3 DIMMs per channel UNIMPLEMENTED
|
||||
*/
|
||||
odt_pattern_0 = 0x00000000;
|
||||
odt_pattern_1 = 0x00000000;
|
||||
odt_pattern_2 = 0x00000000;
|
||||
odt_pattern_3 = 0x00000000;
|
||||
}
|
||||
}
|
||||
|
||||
/* Program ODT pattern */
|
||||
Set_NB32_index_wait(dev, 0xf0 + reg_off, 0x180, odt_pattern_1);
|
||||
Set_NB32_index_wait(dev, 0xf0 + reg_off, 0x181, odt_pattern_0);
|
||||
Set_NB32_index_wait(dev, 0xf0 + reg_off, 0x182, odt_pattern_3);
|
||||
Set_NB32_index_wait(dev, 0xf0 + reg_off, 0x183, odt_pattern_2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3657,6 +3813,7 @@ static void mct_BeforeDQSTrain_D(struct MCTStatStruc *pMCTstat,
|
|||
}
|
||||
}
|
||||
|
||||
/* Erratum 350 */
|
||||
static void mct_ResetDLL_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat, u8 dct)
|
||||
{
|
||||
|
@ -3692,11 +3849,11 @@ static void mct_ResetDLL_D(struct MCTStatStruc *pMCTstat,
|
|||
mct_Read1LTestPattern_D(pMCTstat, pDCTstat, addr); /* cache fills */
|
||||
|
||||
/* Write 0000_8000h to register F2x[1,0]9C_xD080F0C */
|
||||
Set_NB32_index_wait(dev, 0x98 + reg_off, 0x4D080F0C, 0x00008000);
|
||||
Set_NB32_index_wait(dev, 0x98 + reg_off, 0xD080F0C, 0x00008000);
|
||||
mct_Wait(80); /* wait >= 300ns */
|
||||
|
||||
/* Write 0000_0000h to register F2x[1,0]9C_xD080F0C */
|
||||
Set_NB32_index_wait(dev, 0x98 + reg_off, 0x4D080F0C, 0x00000000);
|
||||
Set_NB32_index_wait(dev, 0x98 + reg_off, 0xD080F0C, 0x00000000);
|
||||
mct_Wait(800); /* wait >= 2us */
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -499,7 +499,7 @@ struct DCTStatStruc { /* A per Node structure*/
|
|||
/* CHB DIMM0 Byte 0 - 7 TxDqs */
|
||||
/* CHB DIMM1 Byte 0 - 7 TxDqs */
|
||||
/* CHB DIMM1 Byte 0 - 7 TxDqs */
|
||||
u8 CH_D_B_RCVRDLY[2][4][8]; /* [A/B] [DIMM0-3] [DQS] */
|
||||
u16 CH_D_B_RCVRDLY[2][4][8]; /* [A/B] [DIMM0-3] [DQS] */
|
||||
/* CHA DIMM 0 Receiver Enable Delay*/
|
||||
/* CHA DIMM 1 Receiver Enable Delay*/
|
||||
/* CHA DIMM 2 Receiver Enable Delay*/
|
||||
|
@ -509,7 +509,7 @@ struct DCTStatStruc { /* A per Node structure*/
|
|||
/* CHB DIMM 1 Receiver Enable Delay*/
|
||||
/* CHB DIMM 2 Receiver Enable Delay*/
|
||||
/* CHB DIMM 3 Receiver Enable Delay*/
|
||||
u8 CH_D_BC_RCVRDLY[2][4];
|
||||
u16 CH_D_BC_RCVRDLY[2][4];
|
||||
/* CHA DIMM 0 - 4 Check Byte Receiver Enable Delay*/
|
||||
/* CHB DIMM 0 - 4 Check Byte Receiver Enable Delay*/
|
||||
u8 DIMMValidDCT[2]; /* DIMM# in DCT0*/
|
||||
|
@ -769,7 +769,7 @@ u8 mct_checkNumberOfDqsRcvEn_1Pass(u8 pass);
|
|||
u32 SetupDqsPattern_1PassA(u8 Pass);
|
||||
u32 SetupDqsPattern_1PassB(u8 Pass);
|
||||
u8 mct_Get_Start_RcvrEnDly_1Pass(u8 Pass);
|
||||
u8 mct_Average_RcvrEnDly_Pass(struct DCTStatStruc *pDCTstat, u8 RcvrEnDly, u8 RcvrEnDlyLimit, u8 Channel, u8 Receiver, u8 Pass);
|
||||
u16 mct_Average_RcvrEnDly_Pass(struct DCTStatStruc *pDCTstat, u16 RcvrEnDly, u16 RcvrEnDlyLimit, u8 Channel, u8 Receiver, u8 Pass);
|
||||
void CPUMemTyping_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
|
||||
void UMAMemTyping_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
|
||||
uint64_t mctGetLogicalCPUID(u32 Node);
|
||||
|
@ -779,7 +779,7 @@ void mct_TrainDQSPos_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTs
|
|||
void mctSetEccDQSRcvrEn_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
|
||||
void TrainMaxReadLatency_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
|
||||
void mct_EndDQSTraining_D(struct MCTStatStruc *pMCTstat,struct DCTStatStruc *pDCTstatA);
|
||||
void mct_SetRcvrEnDly_D(struct DCTStatStruc *pDCTstat, u8 RcvrEnDly, u8 FinalValue, u8 Channel, u8 Receiver, u32 dev, u32 index_reg, u8 Addl_Index, u8 Pass);
|
||||
void mct_SetRcvrEnDly_D(struct DCTStatStruc *pDCTstat, u16 RcvrEnDly, u8 FinalValue, u8 Channel, u8 Receiver, u32 dev, u32 index_reg, u8 Addl_Index, u8 Pass);
|
||||
void SetEccDQSRcvrEn_D(struct DCTStatStruc *pDCTstat, u8 Channel);
|
||||
void mctGet_PS_Cfg_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u32 dct);
|
||||
void InterleaveBanks_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* This file is part of the coreboot project.
|
||||
*
|
||||
* Copyright (C) 2010 Advanced Micro Devices, Inc.
|
||||
* Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -103,10 +104,10 @@ static void proc_CLFLUSH(u32 addr_hi)
|
|||
|
||||
__asm__ volatile (
|
||||
/* clflush fs:[eax] */
|
||||
"outb %%al, $0xed\n\t" /* _EXECFENCE */
|
||||
"clflush %%fs:(%0)\n\t"
|
||||
"outb %%al, $0xed\n\t" /* _EXECFENCE */
|
||||
"clflush %%fs:(%0)\n\t"
|
||||
"mfence\n\t"
|
||||
::"a" (addr_hi<<8)
|
||||
::"a" (addr_hi<<8)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -141,6 +142,24 @@ static u32 read32_fs(u32 addr_lo)
|
|||
return value;
|
||||
}
|
||||
|
||||
static uint64_t read64_fs(uint32_t addr_lo)
|
||||
{
|
||||
uint64_t value = 0;
|
||||
uint32_t value_lo;
|
||||
uint32_t value_hi;
|
||||
|
||||
__asm__ volatile (
|
||||
"outb %%al, $0xed\n\t" /* _EXECFENCE */
|
||||
"mfence\n\t"
|
||||
"movl %%fs:(%2), %0\n\t"
|
||||
"movl %%fs:(%3), %1\n\t"
|
||||
:"=c"(value_lo), "=d"(value_hi): "a" (addr_lo), "b" (addr_lo + 4) : "memory"
|
||||
);
|
||||
value |= value_lo;
|
||||
value |= ((uint64_t)value_hi) << 32;
|
||||
return value;
|
||||
}
|
||||
|
||||
#ifdef UNUSED_CODE
|
||||
static u8 read8_fs(u32 addr_lo)
|
||||
{
|
||||
|
@ -210,68 +229,6 @@ static __attribute__((noinline)) void FlushDQSTestPattern_L18(u32 addr_lo)
|
|||
);
|
||||
}
|
||||
|
||||
static void ReadL18TestPattern(u32 addr_lo)
|
||||
{
|
||||
/* set fs and use fs prefix to access the mem */
|
||||
__asm__ volatile (
|
||||
"outb %%al, $0xed\n\t" /* _EXECFENCE */
|
||||
"movl %%fs:-128(%%esi), %%eax\n\t" /* TestAddr cache line */
|
||||
"movl %%fs:-64(%%esi), %%eax\n\t" /* +1 */
|
||||
"movl %%fs:(%%esi), %%eax\n\t" /* +2 */
|
||||
"movl %%fs:64(%%esi), %%eax\n\t" /* +3 */
|
||||
|
||||
"movl %%fs:-128(%%edi), %%eax\n\t" /* +4 */
|
||||
"movl %%fs:-64(%%edi), %%eax\n\t" /* +5 */
|
||||
"movl %%fs:(%%edi), %%eax\n\t" /* +6 */
|
||||
"movl %%fs:64(%%edi), %%eax\n\t" /* +7 */
|
||||
|
||||
"movl %%fs:-128(%%ebx), %%eax\n\t" /* +8 */
|
||||
"movl %%fs:-64(%%ebx), %%eax\n\t" /* +9 */
|
||||
"movl %%fs:(%%ebx), %%eax\n\t" /* +10 */
|
||||
"movl %%fs:64(%%ebx), %%eax\n\t" /* +11 */
|
||||
|
||||
"movl %%fs:-128(%%ecx), %%eax\n\t" /* +12 */
|
||||
"movl %%fs:-64(%%ecx), %%eax\n\t" /* +13 */
|
||||
"movl %%fs:(%%ecx), %%eax\n\t" /* +14 */
|
||||
"movl %%fs:64(%%ecx), %%eax\n\t" /* +15 */
|
||||
|
||||
"movl %%fs:-128(%%edx), %%eax\n\t" /* +16 */
|
||||
"movl %%fs:-64(%%edx), %%eax\n\t" /* +17 */
|
||||
"mfence\n\t"
|
||||
|
||||
:: "a"(0), "b" (addr_lo+128+8*64), "c" (addr_lo+128+12*64),
|
||||
"d" (addr_lo +128+16*64), "S"(addr_lo+128),
|
||||
"D"(addr_lo+128+4*64)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
static void ReadL9TestPattern(u32 addr_lo)
|
||||
{
|
||||
|
||||
/* set fs and use fs prefix to access the mem */
|
||||
__asm__ volatile (
|
||||
"outb %%al, $0xed\n\t" /* _EXECFENCE */
|
||||
|
||||
"movl %%fs:-128(%%ecx), %%eax\n\t" /* TestAddr cache line */
|
||||
"movl %%fs:-64(%%ecx), %%eax\n\t" /* +1 */
|
||||
"movl %%fs:(%%ecx), %%eax\n\t" /* +2 */
|
||||
"movl %%fs:64(%%ecx), %%eax\n\t" /* +3 */
|
||||
|
||||
"movl %%fs:-128(%%edx), %%eax\n\t" /* +4 */
|
||||
"movl %%fs:-64(%%edx), %%eax\n\t" /* +5 */
|
||||
"movl %%fs:(%%edx), %%eax\n\t" /* +6 */
|
||||
"movl %%fs:64(%%edx), %%eax\n\t" /* +7 */
|
||||
|
||||
"movl %%fs:-128(%%ebx), %%eax\n\t" /* +8 */
|
||||
"mfence\n\t"
|
||||
|
||||
:: "a"(0), "b" (addr_lo+128+8*64), "c"(addr_lo+128),
|
||||
"d"(addr_lo+128+4*64)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
static void ReadMaxRdLat1CLTestPattern_D(u32 addr)
|
||||
{
|
||||
SetUpperFSbase(addr);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* This file is part of the coreboot project.
|
||||
*
|
||||
* Copyright (C) 2010 Advanced Micro Devices, Inc.
|
||||
* Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -17,7 +18,7 @@
|
|||
* Foundation, Inc.
|
||||
*/
|
||||
|
||||
/* The socket type F (1207), Fr2, G (1207) are not tested.
|
||||
/* The socket type Fr2, G (1207) are not tested.
|
||||
*/
|
||||
|
||||
static void Get_ChannelPS_Cfg0_D(u8 MAAdimms, u8 Speed, u8 MAAload,
|
||||
|
@ -79,8 +80,7 @@ static void Get_ChannelPS_Cfg0_D( u8 MAAdimms, u8 Speed, u8 MAAload,
|
|||
else
|
||||
*AddrTmgCTL = 0x00353935;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if(Speed == 4) {
|
||||
*AddrTmgCTL = 0x00000000;
|
||||
if (MAAdimms == 3)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* This file is part of the coreboot project.
|
||||
*
|
||||
* Copyright (C) 2010 Advanced Micro Devices, Inc.
|
||||
* Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -22,13 +23,6 @@ static void CalcEccDQSPos_D(struct MCTStatStruc *pMCTstat,
|
|||
u8 scale, u8 ChipSel);
|
||||
static void GetDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat, u8 ChipSel);
|
||||
static u8 MiddleDQS_D(u8 min, u8 max);
|
||||
static void TrainReadDQS_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat,
|
||||
u8 cs_start);
|
||||
static void TrainWriteDQS_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat,
|
||||
u8 cs_start);
|
||||
static void WriteDQSTestPattern_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat,
|
||||
u32 TestAddr_lo);
|
||||
|
@ -43,31 +37,19 @@ static void FlushDQSTestPattern_D(struct DCTStatStruc *pDCTstat,
|
|||
u32 addr_lo);
|
||||
static void SetTargetWTIO_D(u32 TestAddr);
|
||||
static void ResetTargetWTIO_D(void);
|
||||
static void ReadDQSTestPattern_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat,
|
||||
u32 TestAddr_lo);
|
||||
static void mctEngDQSwindow_Save_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat, u8 ChipSel,
|
||||
u8 RnkDlyFilterMin, u8 RnkDlyFilterMax);
|
||||
void ResetDCTWrPtr_D(u32 dev, u32 index_reg, u32 index);
|
||||
u8 mct_DisableDimmEccEn_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat);
|
||||
static void mct_SetDQSDelayCSR_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat,
|
||||
u8 ChipSel);
|
||||
static void mct_SetDQSDelayAllCSR_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat,
|
||||
u8 cs_start);
|
||||
u32 mct_GetMCTSysAddr_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat, u8 Channel,
|
||||
u8 receiver, u8 *valid);
|
||||
static void SetupDqsPattern_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat,
|
||||
u32 *buffer);
|
||||
|
||||
static void StoreWrRdDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat, u8 ChipSel,
|
||||
u8 RnkDlyFilterMin, u8 RnkDlyFilterMax);
|
||||
static void proc_IOCLFLUSH_D(u32 addr_hi);
|
||||
|
||||
static void StoreDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 ChipSel);
|
||||
|
||||
|
@ -286,20 +268,99 @@ static void CalcEccDQSPos_D(struct MCTStatStruc *pMCTstat,
|
|||
pDCTstat->DQSDelay = (u8)DQSDelay;
|
||||
}
|
||||
|
||||
static void write_dqs_write_data_timing_registers(uint16_t* delay, uint32_t dev, uint8_t dimm, uint32_t index_reg)
|
||||
{
|
||||
uint32_t dword;
|
||||
|
||||
/* Lanes 0 - 3 */
|
||||
dword = Get_NB32_index_wait(dev, index_reg, 0x1 | (dimm << 8));
|
||||
dword &= ~0x7f7f7f7f;
|
||||
dword |= (delay[3] & 0x7f) << 24;
|
||||
dword |= (delay[2] & 0x7f) << 16;
|
||||
dword |= (delay[1] & 0x7f) << 8;
|
||||
dword |= delay[0] & 0x7f;
|
||||
Set_NB32_index_wait(dev, index_reg, 0x1 | (dimm << 8), dword);
|
||||
|
||||
/* Lanes 4 - 7 */
|
||||
dword = Get_NB32_index_wait(dev, index_reg, 0x2 | (dimm << 8));
|
||||
dword &= ~0x7f7f7f7f;
|
||||
dword |= (delay[7] & 0x7f) << 24;
|
||||
dword |= (delay[6] & 0x7f) << 16;
|
||||
dword |= (delay[5] & 0x7f) << 8;
|
||||
dword |= delay[4] & 0x7f;
|
||||
Set_NB32_index_wait(dev, index_reg, 0x2 | (dimm << 8), dword);
|
||||
|
||||
/* Lane 8 (ECC) */
|
||||
dword = Get_NB32_index_wait(dev, index_reg, 0x3 | (dimm << 8));
|
||||
dword &= ~0x0000007f;
|
||||
dword |= delay[8] & 0x7f;
|
||||
Set_NB32_index_wait(dev, index_reg, 0x3 | (dimm << 8), dword);
|
||||
}
|
||||
|
||||
static void write_dqs_read_data_timing_registers(uint16_t* delay, uint32_t dev, uint8_t dimm, uint32_t index_reg)
|
||||
{
|
||||
uint32_t dword;
|
||||
|
||||
/* Lanes 0 - 3 */
|
||||
dword = Get_NB32_index_wait(dev, index_reg, 0x5 | (dimm << 8));
|
||||
dword &= ~0x3f3f3f3f;
|
||||
dword |= (delay[3] & 0x3f) << 24;
|
||||
dword |= (delay[2] & 0x3f) << 16;
|
||||
dword |= (delay[1] & 0x3f) << 8;
|
||||
dword |= delay[0] & 0x3f;
|
||||
Set_NB32_index_wait(dev, index_reg, 0x5 | (dimm << 8), dword);
|
||||
|
||||
/* Lanes 4 - 7 */
|
||||
dword = Get_NB32_index_wait(dev, index_reg, 0x6 | (dimm << 8));
|
||||
dword &= ~0x3f3f3f3f;
|
||||
dword |= (delay[7] & 0x3f) << 24;
|
||||
dword |= (delay[6] & 0x3f) << 16;
|
||||
dword |= (delay[5] & 0x3f) << 8;
|
||||
dword |= delay[4] & 0x3f;
|
||||
Set_NB32_index_wait(dev, index_reg, 0x6 | (dimm << 8), dword);
|
||||
|
||||
/* Lane 8 (ECC) */
|
||||
dword = Get_NB32_index_wait(dev, index_reg, 0x7 | (dimm << 8));
|
||||
dword &= ~0x0000003f;
|
||||
dword |= delay[8] & 0x3f;
|
||||
Set_NB32_index_wait(dev, index_reg, 0x7 | (dimm << 8), dword);
|
||||
}
|
||||
|
||||
/* DQS Position Training
|
||||
* Algorithm detailed in the Fam10h BKDG Rev. 3.62 section 2.8.9.9.3
|
||||
*/
|
||||
static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat,
|
||||
u8 cs_start)
|
||||
struct DCTStatStruc *pDCTstat)
|
||||
{
|
||||
u32 Errors;
|
||||
u8 Channel, DQSWrDelay;
|
||||
u8 Channel;
|
||||
u8 Receiver;
|
||||
u8 _DisableDramECC = 0;
|
||||
u32 PatternBuffer[292];
|
||||
u32 PatternBuffer[304]; /* 288 + 16 */
|
||||
u8 _Wrap32Dis = 0, _SSE2 = 0;
|
||||
u8 dqsWrDelay_end;
|
||||
|
||||
u32 dev;
|
||||
u32 addr;
|
||||
u8 valid;
|
||||
u32 cr4;
|
||||
u32 lo, hi;
|
||||
u32 index_reg;
|
||||
uint32_t TestAddr;
|
||||
|
||||
uint8_t dual_rank;
|
||||
uint8_t iter;
|
||||
uint8_t lane;
|
||||
uint16_t bytelane_test_results;
|
||||
uint16_t current_write_dqs_delay[MAX_BYTE_LANES];
|
||||
uint16_t current_read_dqs_delay[MAX_BYTE_LANES];
|
||||
uint16_t write_dqs_delay_stepping_done[MAX_BYTE_LANES];
|
||||
uint8_t dqs_read_results_array[2][MAX_BYTE_LANES][64]; /* [rank][lane][step] */
|
||||
uint8_t dqs_write_results_array[2][MAX_BYTE_LANES][128]; /* [rank][lane][step] */
|
||||
|
||||
uint8_t last_pos = 0;
|
||||
uint8_t cur_count = 0;
|
||||
uint8_t best_pos = 0;
|
||||
uint8_t best_count = 0;
|
||||
|
||||
print_debug_dqs("\nTrainDQSRdWrPos: Node_ID ", pDCTstat->Node_ID, 0);
|
||||
cr4 = read_cr4();
|
||||
|
@ -323,50 +384,363 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
|
|||
SetupDqsPattern_D(pMCTstat, pDCTstat, PatternBuffer);
|
||||
|
||||
/* mct_BeforeTrainDQSRdWrPos_D */
|
||||
dqsWrDelay_end = 0x20;
|
||||
|
||||
dev = pDCTstat->dev_dct;
|
||||
pDCTstat->Direction = DQS_READDIR;
|
||||
|
||||
/* 2.8.9.9.3 (2)
|
||||
* Loop over each channel, lane, and rank
|
||||
*/
|
||||
|
||||
/* NOTE
|
||||
* The BKDG originally stated to iterate over lane, then rank, however this process is quite slow
|
||||
* compared to an equivalent loop over rank, then lane as the latter allows multiple lanes to be
|
||||
* tested simultaneously, thus improving performance by around 8x.
|
||||
*/
|
||||
|
||||
Errors = 0;
|
||||
for (Channel = 0; Channel < 2; Channel++) {
|
||||
print_debug_dqs("\tTrainDQSRdWrPos: 1 Channel ",Channel, 1);
|
||||
print_debug_dqs("\tTrainDQSRdWrPos: 1 Channel ", Channel, 1);
|
||||
pDCTstat->Channel = Channel;
|
||||
|
||||
if (pDCTstat->DIMMValidDCT[Channel] == 0) /* mct_BeforeTrainDQSRdWrPos_D */
|
||||
continue;
|
||||
pDCTstat->DqsRdWrPos_Saved = 0;
|
||||
for ( DQSWrDelay = 0; DQSWrDelay < dqsWrDelay_end; DQSWrDelay++) {
|
||||
pDCTstat->DQSDelay = DQSWrDelay;
|
||||
pDCTstat->Direction = DQS_WRITEDIR;
|
||||
mct_SetDQSDelayAllCSR_D(pMCTstat, pDCTstat, cs_start);
|
||||
|
||||
print_debug_dqs("\t\tTrainDQSRdWrPos: 21 DQSWrDelay ", DQSWrDelay, 2);
|
||||
TrainReadDQS_D(pMCTstat, pDCTstat, cs_start);
|
||||
print_debug_dqs("\t\tTrainDQSRdWrPos: 21 DqsRdWrPos_Saved ", pDCTstat->DqsRdWrPos_Saved, 2);
|
||||
if (pDCTstat->DqsRdWrPos_Saved == 0xFF)
|
||||
break;
|
||||
index_reg = 0x98 + 0x100 * Channel;
|
||||
|
||||
print_debug_dqs("\t\tTrainDQSRdWrPos: 22 TrainErrors ",pDCTstat->TrainErrors, 2);
|
||||
if (pDCTstat->TrainErrors == 0) {
|
||||
break;
|
||||
dual_rank = 0;
|
||||
Receiver = mct_InitReceiver_D(pDCTstat, Channel);
|
||||
/* There are four receiver pairs, loosely associated with chipselects.
|
||||
* This is essentially looping over each rank of each DIMM.
|
||||
*/
|
||||
for (; Receiver < 8; Receiver++) {
|
||||
if ((Receiver & 0x1) == 0) {
|
||||
/* Even rank of DIMM */
|
||||
if(mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, Channel, Receiver+1))
|
||||
dual_rank = 1;
|
||||
else
|
||||
dual_rank = 0;
|
||||
}
|
||||
Errors |= pDCTstat->TrainErrors;
|
||||
}
|
||||
|
||||
pDCTstat->DqsRdWrPos_Saved = 0;
|
||||
if (DQSWrDelay < dqsWrDelay_end) {
|
||||
Errors = 0;
|
||||
if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, Channel, Receiver)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Select the base test address for the current rank */
|
||||
TestAddr = mct_GetMCTSysAddr_D(pMCTstat, pDCTstat, Channel, Receiver, &valid);
|
||||
if (!valid) { /* Address not supported on current CS */
|
||||
continue;
|
||||
}
|
||||
|
||||
print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 14 TestAddr ", TestAddr, 4);
|
||||
SetUpperFSbase(TestAddr); /* fs:eax=far ptr to target */
|
||||
|
||||
print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 12 Receiver ", Receiver, 2);
|
||||
|
||||
/* 2.8.9.9.3 (DRAM Write Data Timing Loop)
|
||||
* Iterate over all possible DQS delay values (0x0 - 0x7f)
|
||||
*/
|
||||
uint8_t test_write_dqs_delay = 0;
|
||||
uint8_t test_read_dqs_delay = 0;
|
||||
uint8_t passing_dqs_delay_found[MAX_BYTE_LANES];
|
||||
|
||||
/* Initialize variables */
|
||||
for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
|
||||
current_write_dqs_delay[lane] = 0;
|
||||
passing_dqs_delay_found[lane] = 0;
|
||||
write_dqs_delay_stepping_done[lane] = 0;
|
||||
}
|
||||
|
||||
for (test_write_dqs_delay = 0; test_write_dqs_delay < 128; test_write_dqs_delay++) {
|
||||
print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 16 test_write_dqs_delay ", test_write_dqs_delay, 6);
|
||||
|
||||
/* Break out of loop if passing window already found, */
|
||||
if (write_dqs_delay_stepping_done[0] && write_dqs_delay_stepping_done[1]
|
||||
&& write_dqs_delay_stepping_done[2] && write_dqs_delay_stepping_done[3]
|
||||
&& write_dqs_delay_stepping_done[4] && write_dqs_delay_stepping_done[5]
|
||||
&& write_dqs_delay_stepping_done[6] && write_dqs_delay_stepping_done[7])
|
||||
break;
|
||||
|
||||
/* Commit the current Write Data Timing settings to the hardware registers */
|
||||
write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, (Receiver >> 1), index_reg);
|
||||
|
||||
/* Write the DRAM training pattern to the base test address */
|
||||
WriteDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr << 8);
|
||||
|
||||
/* 2.8.9.9.3 (DRAM Read DQS Timing Control Loop)
|
||||
* Iterate over all possible DQS delay values (0x0 - 0x3f)
|
||||
*/
|
||||
for (test_read_dqs_delay = 0; test_read_dqs_delay < 64; test_read_dqs_delay++) {
|
||||
print_debug_dqs("\t\t\t\t\tTrainDQSRdWrPos: 161 test_read_dqs_delay ", test_read_dqs_delay, 6);
|
||||
|
||||
/* Initialize Read DQS Timing Control settings for this iteration */
|
||||
for (lane = 0; lane < MAX_BYTE_LANES; lane++)
|
||||
if (!write_dqs_delay_stepping_done[lane])
|
||||
current_read_dqs_delay[lane] = test_read_dqs_delay;
|
||||
|
||||
/* Commit the current Read DQS Timing Control settings to the hardware registers */
|
||||
write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, (Receiver >> 1), index_reg);
|
||||
|
||||
/* Initialize test result variable */
|
||||
bytelane_test_results = 0xff;
|
||||
|
||||
/* Read the DRAM training pattern from the base test address three times
|
||||
* NOTE
|
||||
* While the BKDG states to read three times this is probably excessive!
|
||||
* Decrease training time by only reading the test pattern once per iteration
|
||||
*/
|
||||
for (iter = 0; iter < 1; iter++) {
|
||||
/* Flush caches */
|
||||
SetTargetWTIO_D(TestAddr);
|
||||
FlushDQSTestPattern_D(pDCTstat, TestAddr << 8);
|
||||
ResetTargetWTIO_D();
|
||||
|
||||
/* Read and compare pattern */
|
||||
bytelane_test_results &= (CompareDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr << 8) & 0xff); /* [Lane 7 :: Lane 0] 0=fail, 1=pass */
|
||||
|
||||
/* If all lanes have already failed testing bypass remaining re-read attempt(s) */
|
||||
if (bytelane_test_results == 0x0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Store any lanes that passed testing for later use */
|
||||
for (lane = 0; lane < 8; lane++)
|
||||
if (!write_dqs_delay_stepping_done[lane])
|
||||
dqs_read_results_array[Receiver & 0x1][lane][test_read_dqs_delay] = (!!(bytelane_test_results & (1 << lane)));
|
||||
|
||||
print_debug_dqs("\t\t\t\t\tTrainDQSRdWrPos: 162 bytelane_test_results ", bytelane_test_results, 6);
|
||||
}
|
||||
|
||||
for (lane = 0; lane < MAX_BYTE_LANES; lane++) {
|
||||
if (write_dqs_delay_stepping_done[lane])
|
||||
continue;
|
||||
|
||||
/* Determine location and length of longest consecutive string of passing values
|
||||
* Output is stored in best_pos and best_count
|
||||
*/
|
||||
last_pos = 0;
|
||||
cur_count = 0;
|
||||
best_pos = 0;
|
||||
best_count = 0;
|
||||
for (iter = 0; iter < 64; iter++) {
|
||||
if ((dqs_read_results_array[Receiver & 0x1][lane][iter]) && (iter < 63)) {
|
||||
/* Pass */
|
||||
cur_count++;
|
||||
} else {
|
||||
/* Failure or end of loop */
|
||||
if (cur_count > best_count) {
|
||||
best_count = cur_count;
|
||||
best_pos = last_pos;
|
||||
}
|
||||
cur_count = 0;
|
||||
last_pos = iter;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_count > 2) {
|
||||
/* Exit the DRAM Write Data Timing Loop after programming the Read DQS Timing Control
|
||||
* register with the center of the passing window
|
||||
*/
|
||||
current_read_dqs_delay[lane] = (best_pos + (best_count / 2));
|
||||
passing_dqs_delay_found[lane] = 1;
|
||||
|
||||
/* Commit the current Read DQS Timing Control settings to the hardware registers */
|
||||
write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, (Receiver >> 1), index_reg);
|
||||
|
||||
/* Exit the DRAM Write Data Timing Loop */
|
||||
write_dqs_delay_stepping_done[lane] = 1;
|
||||
|
||||
print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 142 largest passing region ", best_count, 4);
|
||||
print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 143 largest passing region start ", best_pos, 4);
|
||||
}
|
||||
|
||||
/* Increment the DQS Write Delay value if needed for the next DRAM Write Data Timing Loop iteration */
|
||||
if (!write_dqs_delay_stepping_done[lane])
|
||||
current_write_dqs_delay[lane]++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Flag failure(s) if present */
|
||||
for (lane = 0; lane < 8; lane++) {
|
||||
if (!passing_dqs_delay_found[lane]) {
|
||||
print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 121 Unable to find passing region for lane ", lane, 2);
|
||||
|
||||
/* Flag absence of passing window */
|
||||
Errors |= 1 << SB_NODQSPOS;
|
||||
}
|
||||
}
|
||||
|
||||
/* Iterate over all possible Write Data Timing values (0x0 - 0x7f)
|
||||
* Note that the Read DQS Timing Control was calibrated / centered in the prior nested loop
|
||||
*/
|
||||
for (test_write_dqs_delay = 0; test_write_dqs_delay < 128; test_write_dqs_delay++) {
|
||||
/* Initialize Write Data Timing settings for this iteration */
|
||||
for (lane = 0; lane < MAX_BYTE_LANES; lane++)
|
||||
current_write_dqs_delay[lane] = test_write_dqs_delay;
|
||||
|
||||
/* Commit the current Write Data Timing settings to the hardware registers */
|
||||
write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, (Receiver >> 1), index_reg);
|
||||
|
||||
/* Write the DRAM training pattern to the base test address */
|
||||
WriteDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr << 8);
|
||||
|
||||
/* Flush caches */
|
||||
SetTargetWTIO_D(TestAddr);
|
||||
FlushDQSTestPattern_D(pDCTstat, TestAddr << 8);
|
||||
ResetTargetWTIO_D();
|
||||
|
||||
/* Read and compare pattern from the base test address */
|
||||
bytelane_test_results = (CompareDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr << 8) & 0xff); /* [Lane 7 :: Lane 0] 0=fail, 1=pass */
|
||||
|
||||
/* Store any lanes that passed testing for later use */
|
||||
for (lane = 0; lane < 8; lane++)
|
||||
dqs_write_results_array[Receiver & 0x1][lane][test_write_dqs_delay] = (!!(bytelane_test_results & (1 << lane)));
|
||||
}
|
||||
|
||||
for (lane = 0; lane < 8; lane++) {
|
||||
if ((!dual_rank) || (dual_rank && (Receiver & 0x1))) {
|
||||
|
||||
#ifdef PRINT_PASS_FAIL_BITMAPS
|
||||
for (iter = 0; iter < 64; iter++) {
|
||||
if (dqs_read_results_array[0][lane][iter])
|
||||
printk(BIOS_DEBUG, "+");
|
||||
else
|
||||
printk(BIOS_DEBUG, ".");
|
||||
}
|
||||
printk(BIOS_DEBUG, "\n");
|
||||
for (iter = 0; iter < 64; iter++) {
|
||||
if (dqs_read_results_array[1][lane][iter])
|
||||
printk(BIOS_DEBUG, "+");
|
||||
else
|
||||
printk(BIOS_DEBUG, ".");
|
||||
}
|
||||
printk(BIOS_DEBUG, "\n\n");
|
||||
for (iter = 0; iter < 128; iter++) {
|
||||
if (dqs_write_results_array[0][lane][iter])
|
||||
printk(BIOS_DEBUG, "+");
|
||||
else
|
||||
printk(BIOS_DEBUG, ".");
|
||||
}
|
||||
printk(BIOS_DEBUG, "\n");
|
||||
for (iter = 0; iter < 128; iter++) {
|
||||
if (dqs_write_results_array[1][lane][iter])
|
||||
printk(BIOS_DEBUG, "+");
|
||||
else
|
||||
printk(BIOS_DEBUG, ".");
|
||||
}
|
||||
printk(BIOS_DEBUG, "\n\n");
|
||||
#endif
|
||||
|
||||
/* Base rank of single-rank DIMM, or odd rank of dual-rank DIMM */
|
||||
if (dual_rank) {
|
||||
/* Intersect the passing windows of both ranks */
|
||||
for (iter = 0; iter < 64; iter++)
|
||||
if (!dqs_read_results_array[1][lane][iter])
|
||||
dqs_read_results_array[0][lane][iter] = 0;
|
||||
for (iter = 0; iter < 128; iter++)
|
||||
if (!dqs_write_results_array[1][lane][iter])
|
||||
dqs_write_results_array[0][lane][iter] = 0;
|
||||
}
|
||||
|
||||
/* Determine location and length of longest consecutive string of passing values for read DQS timing
|
||||
* Output is stored in best_pos and best_count
|
||||
*/
|
||||
last_pos = 0;
|
||||
cur_count = 0;
|
||||
best_pos = 0;
|
||||
best_count = 0;
|
||||
for (iter = 0; iter < 64; iter++) {
|
||||
if ((dqs_read_results_array[0][lane][iter]) && (iter < 63)) {
|
||||
/* Pass */
|
||||
cur_count++;
|
||||
} else {
|
||||
/* Failure or end of loop */
|
||||
if (cur_count > best_count) {
|
||||
best_count = cur_count;
|
||||
best_pos = last_pos;
|
||||
}
|
||||
cur_count = 0;
|
||||
last_pos = iter;
|
||||
}
|
||||
}
|
||||
print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 144 largest read passing region ", best_count, 4);
|
||||
if (best_count > 0) {
|
||||
if (best_count < MIN_DQS_WNDW) {
|
||||
/* Flag excessively small passing window */
|
||||
Errors |= 1 << SB_SMALLDQS;
|
||||
}
|
||||
|
||||
/* Find the center of the passing window */
|
||||
current_read_dqs_delay[lane] = (best_pos + (best_count / 2));
|
||||
|
||||
/* Commit the current Read DQS Timing Control settings to the hardware registers */
|
||||
write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, (Receiver >> 1), index_reg);
|
||||
|
||||
/* Save the final Read DQS Timing Control settings for later use */
|
||||
pDCTstat->CH_D_DIR_B_DQS[Channel][Receiver >> 1][DQS_READDIR][lane] = current_read_dqs_delay[lane];
|
||||
} else {
|
||||
print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 122 Unable to find read passing region for lane ", lane, 2);
|
||||
|
||||
/* Flag absence of passing window */
|
||||
Errors |= 1 << SB_NODQSPOS;
|
||||
}
|
||||
|
||||
/* Determine location and length of longest consecutive string of passing values for write DQS timing
|
||||
* Output is stored in best_pos and best_count
|
||||
*/
|
||||
last_pos = 0;
|
||||
cur_count = 0;
|
||||
best_pos = 0;
|
||||
best_count = 0;
|
||||
for (iter = 0; iter < 128; iter++) {
|
||||
if ((dqs_write_results_array[0][lane][iter]) && (iter < 127)) {
|
||||
/* Pass */
|
||||
cur_count++;
|
||||
} else {
|
||||
/* Failure or end of loop */
|
||||
if (cur_count > best_count) {
|
||||
best_count = cur_count;
|
||||
best_pos = last_pos;
|
||||
}
|
||||
cur_count = 0;
|
||||
last_pos = iter;
|
||||
}
|
||||
}
|
||||
print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 145 largest write passing region ", best_count, 4);
|
||||
if (best_count > 0) {
|
||||
if (best_count < MIN_DQS_WNDW) {
|
||||
/* Flag excessively small passing window */
|
||||
Errors |= 1 << SB_SMALLDQS;
|
||||
}
|
||||
|
||||
/* Find the center of the passing window */
|
||||
current_write_dqs_delay[lane] = (best_pos + (best_count / 2));
|
||||
|
||||
/* Commit the current Write Data Timing settings to the hardware registers */
|
||||
write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, (Receiver >> 1), index_reg);
|
||||
|
||||
/* Save the final Write Data Timing settings for later use */
|
||||
pDCTstat->CH_D_DIR_B_DQS[Channel][Receiver >> 1][DQS_WRITEDIR][lane] = current_write_dqs_delay[lane];
|
||||
} else {
|
||||
print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 123 Unable to find write passing region for lane ", lane, 2);
|
||||
|
||||
/* Flag absence of passing window */
|
||||
Errors |= 1 << SB_NODQSPOS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print_debug_dqs("\tTrainDQSRdWrPos: 231 DQSWrDelay ", DQSWrDelay, 1);
|
||||
TrainWriteDQS_D(pMCTstat, pDCTstat, cs_start);
|
||||
}
|
||||
print_debug_dqs("\tTrainDQSRdWrPos: 232 Errors ", Errors, 1);
|
||||
pDCTstat->ErrStatus |= Errors;
|
||||
}
|
||||
|
||||
pDCTstat->TrainErrors |= Errors;
|
||||
pDCTstat->ErrStatus |= Errors;
|
||||
|
||||
#if DQS_TRAIN_DEBUG > 0
|
||||
{
|
||||
u8 val;
|
||||
u8 i;
|
||||
u8 Channel, Receiver, Dir;
|
||||
u8 ChannelDTD, ReceiverDTD, Dir;
|
||||
u8 *p;
|
||||
|
||||
for (Dir = 0; Dir < 2; Dir++) {
|
||||
|
@ -375,14 +749,14 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
|
|||
} else {
|
||||
printk(BIOS_DEBUG, "TrainDQSRdWrPos: CH_D_DIR_B_DQS RD:\n");
|
||||
}
|
||||
for (Channel = 0; Channel < 2; Channel++) {
|
||||
printk(BIOS_DEBUG, "Channel: %02x\n", Channel);
|
||||
for (Receiver = cs_start; Receiver < (cs_start + 2); Receiver += 2) {
|
||||
printk(BIOS_DEBUG, "\t\tReceiver: %02x: ", Receiver);
|
||||
p = pDCTstat->CH_D_DIR_B_DQS[Channel][Receiver >> 1][Dir];
|
||||
for (ChannelDTD = 0; ChannelDTD < 2; ChannelDTD++) {
|
||||
printk(BIOS_DEBUG, "Channel: %02x\n", ChannelDTD);
|
||||
for (ReceiverDTD = 0; ReceiverDTD < MAX_CS_SUPPORTED; ReceiverDTD += 2) {
|
||||
printk(BIOS_DEBUG, "\t\tReceiver: %02x:", ReceiverDTD);
|
||||
p = pDCTstat->CH_D_DIR_B_DQS[ChannelDTD][ReceiverDTD >> 1][Dir];
|
||||
for (i=0;i<8; i++) {
|
||||
val = p[i];
|
||||
printk(BIOS_DEBUG, "%02x ", val);
|
||||
printk(BIOS_DEBUG, " %02x", val);
|
||||
}
|
||||
printk(BIOS_DEBUG, "\n");
|
||||
}
|
||||
|
@ -437,225 +811,6 @@ static void SetupDqsPattern_D(struct MCTStatStruc *pMCTstat,
|
|||
pDCTstat->PtrPatternBufA = (u32)buf;
|
||||
}
|
||||
|
||||
static void TrainDQSPos_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat,
|
||||
u8 cs_start)
|
||||
{
|
||||
u32 Errors;
|
||||
u8 ChipSel, DQSDelay;
|
||||
u8 RnkDlySeqPassMin=0, RnkDlySeqPassMax=0xFF, RnkDlyFilterMin=0, RnkDlyFilterMax=0xFF;
|
||||
u8 RnkDlySeqPassMinTot=0, RnkDlySeqPassMaxTot=0xFF, RnkDlyFilterMinTot=0, RnkDlyFilterMaxTot=0xFF;
|
||||
u8 LastTest ,LastTestTot;
|
||||
u32 TestAddr;
|
||||
u8 ByteLane;
|
||||
u8 MutualCSPassW[128];
|
||||
u8 BanksPresent;
|
||||
u8 dqsDelay_end;
|
||||
u8 tmp, valid, tmp1;
|
||||
u16 word;
|
||||
|
||||
/* MutualCSPassW: each byte represents a bitmap of pass/fail per
|
||||
* ByteLane. The indext within MutualCSPassW is the delay value
|
||||
* given the results.
|
||||
*/
|
||||
print_debug_dqs("\t\t\tTrainDQSPos begin ", 0, 3);
|
||||
|
||||
Errors = 0;
|
||||
BanksPresent = 0;
|
||||
|
||||
dqsDelay_end = 32;
|
||||
/* Bitmapped status per delay setting, 0xff=All positions
|
||||
* passing (1= PASS). Set the entire array.
|
||||
*/
|
||||
for (DQSDelay=0; DQSDelay<128; DQSDelay++) {
|
||||
MutualCSPassW[DQSDelay] = 0xFF;
|
||||
}
|
||||
|
||||
for (ChipSel = cs_start; ChipSel < (cs_start + 2); ChipSel++) { /* logical register chipselects 0..7 */
|
||||
print_debug_dqs("\t\t\t\tTrainDQSPos: 11 ChipSel ", ChipSel, 4);
|
||||
|
||||
if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, pDCTstat->Channel, ChipSel)) {
|
||||
print_debug_dqs("\t\t\t\tmct_RcvrRankEnabled_D CS not enabled ", ChipSel, 4);
|
||||
continue;
|
||||
}
|
||||
|
||||
BanksPresent = 1; /* flag for at least one bank is present */
|
||||
TestAddr = mct_GetMCTSysAddr_D(pMCTstat, pDCTstat, pDCTstat->Channel, ChipSel, &valid);
|
||||
if (!valid) {
|
||||
print_debug_dqs("\t\t\t\tAddress not supported on current CS ", TestAddr, 4);
|
||||
continue;
|
||||
}
|
||||
|
||||
print_debug_dqs("\t\t\t\tTrainDQSPos: 12 TestAddr ", TestAddr, 4);
|
||||
SetUpperFSbase(TestAddr); /* fs:eax=far ptr to target */
|
||||
|
||||
if (pDCTstat->Direction == DQS_READDIR) {
|
||||
print_debug_dqs("\t\t\t\tTrainDQSPos: 13 for read ", 0, 4);
|
||||
WriteDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr << 8);
|
||||
}
|
||||
|
||||
for (DQSDelay = 0; DQSDelay < dqsDelay_end; DQSDelay++) {
|
||||
print_debug_dqs("\t\t\t\t\tTrainDQSPos: 141 DQSDelay ", DQSDelay, 5);
|
||||
|
||||
tmp = 0xFF;
|
||||
tmp1 = DQSDelay;
|
||||
if (pDCTstat->Direction == DQS_READDIR) {
|
||||
tmp &= MutualCSPassW[DQSDelay];
|
||||
tmp1 += dqsDelay_end;
|
||||
}
|
||||
tmp &= MutualCSPassW[tmp1];
|
||||
|
||||
if (tmp == 0) {
|
||||
continue;/* skip current delay value if other chipselects have failed all 8 bytelanes */
|
||||
}
|
||||
|
||||
pDCTstat->DQSDelay = DQSDelay;
|
||||
mct_SetDQSDelayAllCSR_D(pMCTstat, pDCTstat, cs_start);
|
||||
print_debug_dqs("\t\t\t\t\tTrainDQSPos: 142 MutualCSPassW ", MutualCSPassW[DQSDelay], 5);
|
||||
|
||||
if (pDCTstat->Direction == DQS_WRITEDIR) {
|
||||
print_debug_dqs("\t\t\t\t\tTrainDQSPos: 143 for write", 0, 5);
|
||||
WriteDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr << 8);
|
||||
}
|
||||
|
||||
print_debug_dqs("\t\t\t\t\tTrainDQSPos: 144 Pattern ", pDCTstat->Pattern, 5);
|
||||
ReadDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr << 8);
|
||||
/* print_debug_dqs("\t\t\t\t\tTrainDQSPos: 145 MutualCSPassW ", MutualCSPassW[DQSDelay], 5); */
|
||||
word = CompareDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr << 8); /* 0=fail, 1=pass */
|
||||
print_debug_dqs("\t\t\t\t\tTrainDQSPos: 144 compare 1 ", word, 3);
|
||||
|
||||
print_debug_dqs("\t\t\t\t\tTrainDQSPos: 144 DqsRdWrPos_Saved ", pDCTstat->DqsRdWrPos_Saved, 3);
|
||||
word &= ~(pDCTstat->DqsRdWrPos_Saved); /* mask out bytelanes that already passed */
|
||||
word &= ~(pDCTstat->DqsRdWrPos_Saved << 8);
|
||||
print_debug_dqs("\t\t\t\t\tTrainDQSPos: 144 compare 2 ", word, 3);
|
||||
|
||||
tmp = DQSDelay;
|
||||
if (pDCTstat->Direction == DQS_READDIR) {
|
||||
MutualCSPassW[tmp] &= word >> 8;
|
||||
tmp += dqsDelay_end;
|
||||
}
|
||||
MutualCSPassW[tmp] &= word & 0xFF;
|
||||
|
||||
print_debug_dqs("\t\t\t\t\tTrainDQSPos: 146 \tMutualCSPassW ", MutualCSPassW[DQSDelay], 5);
|
||||
|
||||
SetTargetWTIO_D(TestAddr);
|
||||
FlushDQSTestPattern_D(pDCTstat, TestAddr << 8);
|
||||
ResetTargetWTIO_D();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (pDCTstat->Direction == DQS_READDIR) {
|
||||
dqsDelay_end <<= 1;
|
||||
}
|
||||
|
||||
if (BanksPresent) {
|
||||
#if 0 /* show the bitmap */
|
||||
for (ByteLane = 0; ByteLane < 8; ByteLane++) { /* just print ByteLane 0 */
|
||||
for (DQSDelay = 0; DQSDelay < dqsDelay_end; DQSDelay++) {
|
||||
if (!(MutualCSPassW[DQSDelay] &(1 << ByteLane))) {
|
||||
printk(BIOS_DEBUG, ".");
|
||||
} else {
|
||||
printk(BIOS_DEBUG, "*");
|
||||
}
|
||||
}
|
||||
printk(BIOS_DEBUG, "\n");
|
||||
}
|
||||
#endif
|
||||
for (ByteLane = 0; ByteLane < 8; ByteLane++) {
|
||||
print_debug_dqs("\t\t\t\tTrainDQSPos: 31 ByteLane ",ByteLane, 4);
|
||||
if (!(pDCTstat->DqsRdWrPos_Saved &(1 << ByteLane))) {
|
||||
pDCTstat->ByteLane = ByteLane;
|
||||
LastTest = DQS_FAIL; /* Analyze the results */
|
||||
LastTestTot = DQS_FAIL;
|
||||
/* RnkDlySeqPassMin = 0; */
|
||||
/* RnkDlySeqPassMax = 0; */
|
||||
RnkDlyFilterMax = 0;
|
||||
RnkDlyFilterMin = 0;
|
||||
RnkDlyFilterMaxTot = 0;
|
||||
RnkDlyFilterMinTot = 0;
|
||||
for (DQSDelay = 0; DQSDelay < dqsDelay_end; DQSDelay++) {
|
||||
if (MutualCSPassW[DQSDelay] & (1 << ByteLane)) {
|
||||
print_debug_dqs("\t\t\t\t\tTrainDQSPos: 321 DQSDelay ", DQSDelay, 5);
|
||||
print_debug_dqs("\t\t\t\t\tTrainDQSPos: 322 MutualCSPassW ", MutualCSPassW[DQSDelay], 5);
|
||||
if (pDCTstat->Direction == DQS_READDIR)
|
||||
tmp = 0x20;
|
||||
else
|
||||
tmp = 0;
|
||||
if (DQSDelay >= tmp) {
|
||||
RnkDlySeqPassMax = DQSDelay;
|
||||
if (LastTest == DQS_FAIL) {
|
||||
RnkDlySeqPassMin = DQSDelay; /* start sequential run */
|
||||
}
|
||||
if ((RnkDlySeqPassMax - RnkDlySeqPassMin)>(RnkDlyFilterMax-RnkDlyFilterMin)){
|
||||
RnkDlyFilterMin = RnkDlySeqPassMin;
|
||||
RnkDlyFilterMax = RnkDlySeqPassMax;
|
||||
}
|
||||
LastTest = DQS_PASS;
|
||||
}
|
||||
|
||||
if (pDCTstat->Direction == DQS_READDIR) {
|
||||
RnkDlySeqPassMaxTot = DQSDelay;
|
||||
if (LastTestTot == DQS_FAIL)
|
||||
RnkDlySeqPassMinTot = DQSDelay;
|
||||
if ((RnkDlySeqPassMaxTot - RnkDlySeqPassMinTot)>(RnkDlyFilterMaxTot-RnkDlyFilterMinTot)){
|
||||
RnkDlyFilterMinTot = RnkDlySeqPassMinTot;
|
||||
RnkDlyFilterMaxTot = RnkDlySeqPassMaxTot;
|
||||
}
|
||||
LastTestTot = DQS_PASS;
|
||||
}
|
||||
} else {
|
||||
LastTest = DQS_FAIL;
|
||||
LastTestTot = DQS_FAIL;
|
||||
}
|
||||
}
|
||||
print_debug_dqs("\t\t\t\tTrainDQSPos: 33 RnkDlySeqPassMax ", RnkDlySeqPassMax, 4);
|
||||
if (RnkDlySeqPassMax == 0) {
|
||||
Errors |= 1 << SB_NODQSPOS; /* no passing window */
|
||||
} else {
|
||||
print_debug_dqs_pair("\t\t\t\tTrainDQSPos: 34 RnkDlyFilter: ", RnkDlyFilterMin, " ", RnkDlyFilterMax, 4);
|
||||
if (((RnkDlyFilterMax - RnkDlyFilterMin) < MIN_DQS_WNDW)){
|
||||
Errors |= 1 << SB_SMALLDQS;
|
||||
} else {
|
||||
u8 middle_dqs;
|
||||
/* mctEngDQSwindow_Save_D Not required for arrays */
|
||||
if (pDCTstat->Direction == DQS_READDIR)
|
||||
middle_dqs = MiddleDQS_D(RnkDlyFilterMinTot, RnkDlyFilterMaxTot);
|
||||
else
|
||||
middle_dqs = MiddleDQS_D(RnkDlyFilterMin, RnkDlyFilterMax);
|
||||
pDCTstat->DQSDelay = middle_dqs;
|
||||
mct_SetDQSDelayCSR_D(pMCTstat, pDCTstat, cs_start); /* load the register with the value */
|
||||
if (pDCTstat->Direction == DQS_READDIR)
|
||||
StoreWrRdDQSDatStrucVal_D(pMCTstat, pDCTstat, cs_start, RnkDlyFilterMinTot, RnkDlyFilterMaxTot); /* store the value into the data structure */
|
||||
else
|
||||
StoreWrRdDQSDatStrucVal_D(pMCTstat, pDCTstat, cs_start, RnkDlyFilterMin, RnkDlyFilterMax); /* store the value into the data structure */
|
||||
print_debug_dqs("\t\t\t\tTrainDQSPos: 42 middle_dqs : ",middle_dqs, 4);
|
||||
pDCTstat->DqsRdWrPos_Saved |= 1 << ByteLane;
|
||||
}
|
||||
}
|
||||
}
|
||||
} /* if (pDCTstat->DqsRdWrPos_Saved &(1 << ByteLane)) */
|
||||
}
|
||||
/* skipLocMiddle: */
|
||||
pDCTstat->TrainErrors = Errors;
|
||||
|
||||
print_debug_dqs("\t\t\tTrainDQSPos: Errors ", Errors, 3);
|
||||
}
|
||||
|
||||
static void mctEngDQSwindow_Save_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat, u8 ChipSel,
|
||||
u8 RnkDlyFilterMin, u8 RnkDlyFilterMax)
|
||||
{
|
||||
pDCTstat->CH_D_DIR_MaxMin_B_Dly[pDCTstat->Channel]
|
||||
[pDCTstat->Direction]
|
||||
[0]
|
||||
[pDCTstat->ByteLane] = RnkDlyFilterMin;
|
||||
pDCTstat->CH_D_DIR_MaxMin_B_Dly[pDCTstat->Channel]
|
||||
[pDCTstat->Direction]
|
||||
[1]
|
||||
[pDCTstat->ByteLane] = RnkDlyFilterMax;
|
||||
}
|
||||
|
||||
static void StoreDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat, u8 ChipSel)
|
||||
{
|
||||
|
@ -679,26 +834,6 @@ static void StoreDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat,
|
|||
pDCTstat->DQSDelay;
|
||||
}
|
||||
|
||||
static void StoreWrRdDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat, u8 ChipSel,
|
||||
u8 RnkDlyFilterMin, u8 RnkDlyFilterMax)
|
||||
{
|
||||
u8 dn;
|
||||
|
||||
if (pDCTstat->Direction == DQS_WRITEDIR) {
|
||||
dn = ChipSel >> 1;
|
||||
RnkDlyFilterMin += pDCTstat->CH_D_B_TxDqs[pDCTstat->Channel][dn][pDCTstat->ByteLane];
|
||||
RnkDlyFilterMax += pDCTstat->CH_D_B_TxDqs[pDCTstat->Channel][dn][pDCTstat->ByteLane];
|
||||
pDCTstat->DQSDelay += pDCTstat->CH_D_B_TxDqs[pDCTstat->Channel][dn][pDCTstat->ByteLane];
|
||||
} else {
|
||||
RnkDlyFilterMin <<= 1;
|
||||
RnkDlyFilterMax <<= 1;
|
||||
pDCTstat->DQSDelay <<= 1;
|
||||
}
|
||||
mctEngDQSwindow_Save_D(pMCTstat, pDCTstat, ChipSel, RnkDlyFilterMin, RnkDlyFilterMax);
|
||||
StoreDQSDatStrucVal_D(pMCTstat, pDCTstat, ChipSel);
|
||||
}
|
||||
|
||||
static void GetDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat, u8 ChipSel)
|
||||
{
|
||||
|
@ -720,33 +855,6 @@ static void GetDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat,
|
|||
|
||||
/* FindDQSDatDimmVal_D is not required since we use an array */
|
||||
|
||||
static u8 MiddleDQS_D(u8 min, u8 max)
|
||||
{
|
||||
u8 size;
|
||||
size = max-min;
|
||||
if (size % 2)
|
||||
size++; /* round up if the size isn't even. */
|
||||
return ( min + (size >> 1));
|
||||
}
|
||||
|
||||
static void TrainReadDQS_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat,
|
||||
u8 cs_start)
|
||||
{
|
||||
print_debug_dqs("\t\tTrainReadPos ", 0, 2);
|
||||
pDCTstat->Direction = DQS_READDIR;
|
||||
TrainDQSPos_D(pMCTstat, pDCTstat, cs_start);
|
||||
}
|
||||
|
||||
static void TrainWriteDQS_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat,
|
||||
u8 cs_start)
|
||||
{
|
||||
pDCTstat->Direction = DQS_WRITEDIR;
|
||||
print_debug_dqs("\t\tTrainWritePos", 0, 2);
|
||||
TrainDQSPos_D(pMCTstat, pDCTstat, cs_start);
|
||||
}
|
||||
|
||||
static void proc_IOCLFLUSH_D(u32 addr_hi)
|
||||
{
|
||||
SetTargetWTIO_D(addr_hi);
|
||||
|
@ -963,30 +1071,6 @@ static void ResetTargetWTIO_D(void)
|
|||
_WRMSR(0xc0010017, lo, hi); /* IORR0 Mask */
|
||||
}
|
||||
|
||||
static void ReadDQSTestPattern_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat,
|
||||
u32 TestAddr_lo)
|
||||
{
|
||||
/* Read a pattern of 72 bit times (per DQ), to test dram functionality.
|
||||
* The pattern is a stress pattern which exercises both ISI and
|
||||
* crosstalk. The number of cache lines to fill is dependent on DCT
|
||||
* width mode and burstlength.
|
||||
* Mode BL Lines Pattern no.
|
||||
* ----+---+-------------------
|
||||
* 64 4 9 0
|
||||
* 64 8 9 0
|
||||
* 64M 4 9 0
|
||||
* 64M 8 9 0
|
||||
* 128 4 18 1
|
||||
* 128 8 N/A -
|
||||
*/
|
||||
if (pDCTstat->Pattern == 0)
|
||||
ReadL9TestPattern(TestAddr_lo);
|
||||
else
|
||||
ReadL18TestPattern(TestAddr_lo);
|
||||
_MFENCE;
|
||||
}
|
||||
|
||||
u32 SetUpperFSbase(u32 addr_hi)
|
||||
{
|
||||
/* Set the upper 32-bits of the Base address, 4GB aligned) for the
|
||||
|
@ -1009,8 +1093,6 @@ void ResetDCTWrPtr_D(u32 dev, u32 index_reg, u32 index)
|
|||
Set_NB32_index_wait(dev, index_reg, index, val);
|
||||
}
|
||||
|
||||
/* mctEngDQSwindow_Save_D not required with arrays */
|
||||
|
||||
void mct_TrainDQSPos_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstatA)
|
||||
{
|
||||
|
@ -1021,8 +1103,8 @@ void mct_TrainDQSPos_D(struct MCTStatStruc *pMCTstat,
|
|||
for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
|
||||
pDCTstat = pDCTstatA + Node;
|
||||
if (pDCTstat->DCTSysLimit) {
|
||||
TrainDQSRdWrPos_D(pMCTstat, pDCTstat);
|
||||
for (ChipSel = 0; ChipSel < MAX_CS_SUPPORTED; ChipSel += 2) {
|
||||
TrainDQSRdWrPos_D(pMCTstat, pDCTstat, ChipSel);
|
||||
SetEccDQSRdWrPos_D(pMCTstat, pDCTstat, ChipSel);
|
||||
}
|
||||
}
|
||||
|
@ -1137,27 +1219,6 @@ static void mct_SetDQSDelayCSR_D(struct MCTStatStruc *pMCTstat,
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* mct_SetDQSDelayAllCSR_D:
|
||||
* Write the Delay value to all eight byte lanes.
|
||||
*/
|
||||
static void mct_SetDQSDelayAllCSR_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat,
|
||||
u8 cs_start)
|
||||
{
|
||||
u8 ByteLane;
|
||||
u8 ChipSel = cs_start;
|
||||
|
||||
for (ChipSel = cs_start; ChipSel < (cs_start + 2); ChipSel++) {
|
||||
if ( mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, pDCTstat->Channel, ChipSel)) {
|
||||
for (ByteLane = 0; ByteLane < 8; ByteLane++) {
|
||||
pDCTstat->ByteLane = ByteLane;
|
||||
mct_SetDQSDelayCSR_D(pMCTstat, pDCTstat, ChipSel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u8 mct_RcvrRankEnabled_D(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat,
|
||||
u8 Channel, u8 ChipSel)
|
||||
|
@ -1196,7 +1257,7 @@ u32 mct_GetMCTSysAddr_D(struct MCTStatStruc *pMCTstat,
|
|||
reg = 0x40 + (receiver << 2) + reg_off;
|
||||
val = Get_NB32(dev, reg);
|
||||
|
||||
val &= ~0x0F;
|
||||
val &= ~0xe007c01f;
|
||||
|
||||
/* unganged mode DCT0+DCT1, sys addr of DCT1=node
|
||||
* base+DctSelBaseAddr+local ca base*/
|
||||
|
@ -1277,6 +1338,7 @@ exitGetAddrWNoError:
|
|||
print_debug_dqs("mct_GetMCTSysAddr_D: base_addr ", val, 2);
|
||||
print_debug_dqs("mct_GetMCTSysAddr_D: valid ", *valid, 2);
|
||||
print_debug_dqs("mct_GetMCTSysAddr_D: status ", pDCTstat->Status, 2);
|
||||
print_debug_dqs("mct_GetMCTSysAddr_D: SysBase ", pDCTstat->DCTSysBase, 2);
|
||||
print_debug_dqs("mct_GetMCTSysAddr_D: HoleBase ", pDCTstat->DCTHoleBase, 2);
|
||||
print_debug_dqs("mct_GetMCTSysAddr_D: Cachetop ", pMCTstat->Sub4GCacheTop, 2);
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* This file is part of the coreboot project.
|
||||
*
|
||||
* Copyright (C) 2010 Advanced Micro Devices, Inc.
|
||||
* Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -25,7 +26,6 @@ static void EnableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStr
|
|||
static void DisableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
|
||||
static void PrepareC_MCT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
|
||||
static void PrepareC_DCT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct);
|
||||
static void MultiplyDelay(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct);
|
||||
static void Restore_OnDimmMirror(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
|
||||
static void Clear_OnDimmMirror(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
|
||||
|
||||
|
@ -154,7 +154,6 @@ static void PhyWLPass2(struct MCTStatStruc *pMCTstat,
|
|||
Clear_OnDimmMirror(pMCTstat, pDCTstat);
|
||||
SetDllSpeedUp_D(pMCTstat, pDCTstat, dct);
|
||||
DisableAutoRefresh_D(pMCTstat, pDCTstat);
|
||||
MultiplyDelay(pMCTstat, pDCTstat, dct);
|
||||
for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm ++) {
|
||||
if (DIMMValid & (1 << (dimm << 1)))
|
||||
AgesaHwWlPhase1(pDCTstat->C_MCTPtr, pDCTstat->C_DCTPtr[dct], dimm, SecondPass);
|
||||
|
@ -162,6 +161,9 @@ static void PhyWLPass2(struct MCTStatStruc *pMCTstat,
|
|||
}
|
||||
}
|
||||
|
||||
/* Write Levelization Training
|
||||
* 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)
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* This file is part of the coreboot project.
|
||||
*
|
||||
* Copyright (C) 2010 Advanced Micro Devices, Inc.
|
||||
* Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -201,12 +202,13 @@ static void SetMTRRrange_D(u32 Base, u32 *pLimit, u32 *pMtrrAddr, u16 MtrrType)
|
|||
|
||||
void UMAMemTyping_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
|
||||
{
|
||||
/* UMA memory size may need splitting the MTRR configuration into two
|
||||
Before training use NB_BottomIO or the physical memory size to set the MTRRs.
|
||||
After training, add UMAMemTyping function to reconfigure the MTRRs based on
|
||||
NV_BottomUMA (for UMA systems only).
|
||||
This two-step process allows all memory to be cached for training
|
||||
*/
|
||||
/* UMA memory size may need splitting the MTRR configuration into two
|
||||
* Before training use NB_BottomIO or the physical memory size to set the MTRRs.
|
||||
* After training, add UMAMemTyping function to reconfigure the MTRRs based on
|
||||
* NV_BottomUMA (for UMA systems only).
|
||||
* This two-step process allows all memory to be cached for training
|
||||
*/
|
||||
|
||||
u32 Bottom32bIO, Cache32bTOP;
|
||||
u32 val;
|
||||
u32 addr;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* This file is part of the coreboot project.
|
||||
*
|
||||
* Copyright (C) 2010 Advanced Micro Devices, Inc.
|
||||
* Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -140,7 +141,7 @@ void InterleaveNodes_D(struct MCTStatStruc *pMCTstat,
|
|||
}
|
||||
|
||||
if (DoIntlv) {
|
||||
MCTMemClr_D(pMCTstat,pDCTstatA);
|
||||
MCTMemClr_D(pMCTstat, pDCTstatA);
|
||||
/* Program Interleaving enabled on Node 0 map only.*/
|
||||
MemSize0 <<= bsf(Nodes); /* MemSize=MemSize*2 (or 4, or 8) */
|
||||
Dct0MemSize <<= bsf(Nodes);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* This file is part of the coreboot project.
|
||||
*
|
||||
* Copyright (C) 2010 Advanced Micro Devices, Inc.
|
||||
* Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -36,10 +37,10 @@ u32 mct_SetDramConfigMisc2(struct DCTStatStruc *pDCTstat, u8 dct, u32 misc2)
|
|||
val = Get_NB32(pDCTstat->dev_dct, dct * 0x100 + 0x78);
|
||||
|
||||
val &= 7;
|
||||
val = ((~val) & 0xFF) + 1;
|
||||
val = ((~val) & 0xff) + 1;
|
||||
val += 6;
|
||||
val &= 0xFF;
|
||||
misc2 &= 0xFFF8FFFF;
|
||||
val &= 0x7;
|
||||
misc2 &= 0xfff8ffff;
|
||||
misc2 |= val << 16; /* DataTxFifoWrDly */
|
||||
if (pDCTstat->LogicalCPUID & AMD_DR_Dx)
|
||||
misc2 |= 1 << 7; /* ProgOdtEn */
|
||||
|
@ -52,11 +53,13 @@ void mct_ExtMCTConfig_Cx(struct DCTStatStruc *pDCTstat)
|
|||
u32 val;
|
||||
|
||||
if (pDCTstat->LogicalCPUID & (AMD_DR_Cx)) {
|
||||
Set_NB32(pDCTstat->dev_dct, 0x11C, 0x0CE00FC0 | 1 << 29/* FlushWrOnStpGnt */);
|
||||
/* Revision C */
|
||||
Set_NB32(pDCTstat->dev_dct, 0x11c, 0x0ce00fc0 | 1 << 29/* FlushWrOnStpGnt */);
|
||||
|
||||
val = Get_NB32(pDCTstat->dev_dct, 0x1B0);
|
||||
val &= 0xFFFFF8C0;
|
||||
val = Get_NB32(pDCTstat->dev_dct, 0x1b0);
|
||||
val &= ~0x73f;
|
||||
val |= 0x101; /* BKDG recommended settings */
|
||||
Set_NB32(pDCTstat->dev_dct, 0x1B0, val);
|
||||
|
||||
Set_NB32(pDCTstat->dev_dct, 0x1b0, val);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* This file is part of the coreboot project.
|
||||
*
|
||||
* Copyright (C) 2010 Advanced Micro Devices, Inc.
|
||||
* Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -172,6 +173,7 @@ static u32 mct_MR1(struct MCTStatStruc *pMCTstat,
|
|||
ret |= 1 << 11;
|
||||
}
|
||||
|
||||
/* program MrsAddress[12]=QOFF: based on F2x[1,0]84[Qoff] */
|
||||
if (dword & (1 << 13))
|
||||
ret |= 1 << 12;
|
||||
|
||||
|
@ -199,7 +201,8 @@ static u32 mct_MR0(struct MCTStatStruc *pMCTstat,
|
|||
/* program MrsAddress[6:4,2]=read CAS latency
|
||||
(CL):based on F2x[1,0]88[Tcl] */
|
||||
dword2 = Get_NB32(dev, reg_off + 0x88);
|
||||
ret |= (dword2 & 0xF) << 4; /* F2x88[3:0] to MrsAddress[6:4,2]=xxx0b */
|
||||
ret |= (dword2 & 0x7) << 4; /* F2x88[2:0] to MrsAddress[6:4] */
|
||||
ret |= ((dword2 & 0x8) >> 3) << 2; /* F2x88[3] to MrsAddress[2] */
|
||||
|
||||
/* program MrsAddress[12]=0 (PPD):slow exit */
|
||||
if (dword & (1 << 23))
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,6 +2,7 @@
|
|||
* This file is part of the coreboot project.
|
||||
*
|
||||
* Copyright (C) 2010 Advanced Micro Devices, Inc.
|
||||
* Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -36,17 +37,12 @@ u32 SetupDqsPattern_1PassB(u8 pass)
|
|||
return (u32) TestPattern0_D;
|
||||
}
|
||||
|
||||
u8 mct_Get_Start_RcvrEnDly_1Pass(u8 pass)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 mct_Average_RcvrEnDly_1Pass(struct DCTStatStruc *pDCTstat, u8 Channel, u8 Receiver,
|
||||
static u16 mct_Average_RcvrEnDly_1Pass(struct DCTStatStruc *pDCTstat, u8 Channel, u8 Receiver,
|
||||
u8 Pass)
|
||||
{
|
||||
u8 i, MaxValue;
|
||||
u8 *p;
|
||||
u8 val;
|
||||
u16 i, MaxValue;
|
||||
u16 *p;
|
||||
u16 val;
|
||||
|
||||
MaxValue = 0;
|
||||
p = pDCTstat->CH_D_B_RCVRDLY[Channel][Receiver >> 1];
|
||||
|
@ -76,8 +72,8 @@ u8 mct_SaveRcvEnDly_D_1Pass(struct DCTStatStruc *pDCTstat, u8 pass)
|
|||
return ret;
|
||||
}
|
||||
|
||||
u8 mct_Average_RcvrEnDly_Pass(struct DCTStatStruc *pDCTstat,
|
||||
u8 RcvrEnDly, u8 RcvrEnDlyLimit,
|
||||
u16 mct_Average_RcvrEnDly_Pass(struct DCTStatStruc *pDCTstat,
|
||||
u16 RcvrEnDly, u16 RcvrEnDlyLimit,
|
||||
u8 Channel, u8 Receiver, u8 Pass)
|
||||
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* This file is part of the coreboot project.
|
||||
*
|
||||
* Copyright (C) 2010 Advanced Micro Devices, Inc.
|
||||
* Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -74,15 +75,15 @@ u8 mct_Get_Start_RcvrEnDly_Pass(struct DCTStatStruc *pDCTstat,
|
|||
return RcvrEnDly;
|
||||
}
|
||||
|
||||
u8 mct_Average_RcvrEnDly_Pass(struct DCTStatStruc *pDCTstat,
|
||||
u8 RcvrEnDly, u8 RcvrEnDlyLimit,
|
||||
u16 mct_Average_RcvrEnDly_Pass(struct DCTStatStruc *pDCTstat,
|
||||
u16 RcvrEnDly, u16 RcvrEnDlyLimit,
|
||||
u8 Channel, u8 Receiver, u8 Pass)
|
||||
{
|
||||
u8 i;
|
||||
u8 *p;
|
||||
u8 *p_1;
|
||||
u8 val;
|
||||
u8 val_1;
|
||||
u16 *p;
|
||||
u16 *p_1;
|
||||
u16 val;
|
||||
u16 val_1;
|
||||
u8 valid = 1;
|
||||
u8 bn;
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* This file is part of the coreboot project.
|
||||
*
|
||||
* Copyright (C) 2010 Advanced Micro Devices, Inc.
|
||||
* Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -191,10 +192,10 @@ static void maxRdLatencyTrain_D(struct MCTStatStruc *pMCTstat,
|
|||
|
||||
#if DQS_TRAIN_DEBUG > 0
|
||||
{
|
||||
u8 Channel;
|
||||
u8 ChannelDTD;
|
||||
printk(BIOS_DEBUG, "maxRdLatencyTrain: CH_MaxRdLat:\n");
|
||||
for(Channel = 0; Channel<2; Channel++) {
|
||||
printk(BIOS_DEBUG, "Channel: %02x: %02x\n", Channel, pDCTstat->CH_MaxRdLat[Channel]);
|
||||
for(ChannelDTD = 0; ChannelDTD<2; ChannelDTD++) {
|
||||
printk(BIOS_DEBUG, "Channel: %02x: %02x\n", ChannelDTD, pDCTstat->CH_MaxRdLat[ChannelDTD]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* This file is part of the coreboot project.
|
||||
*
|
||||
* Copyright (C) 2010 Advanced Micro Devices, Inc.
|
||||
* Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -58,9 +59,9 @@ void PrepareC_DCT(struct MCTStatStruc *pMCTstat,
|
|||
pDCTstat->C_DCTPtr[dct]->LogicalCPUID = pDCTstat->LogicalCPUID;
|
||||
|
||||
for (dimm = 0; dimm < MAX_DIMMS; dimm++) {
|
||||
if (DimmValid & (1 << dimm))
|
||||
if (DimmValid & (1 << (dimm << 1)))
|
||||
pDCTstat->C_DCTPtr[dct]->DimmPresent[dimm] = 1;
|
||||
if (Dimmx8Present & (1 << dimm))
|
||||
if (Dimmx8Present & (1 << (dimm << 1)))
|
||||
pDCTstat->C_DCTPtr[dct]->DimmX8Present[dimm] = 1;
|
||||
}
|
||||
|
||||
|
@ -88,9 +89,9 @@ void PrepareC_DCT(struct MCTStatStruc *pMCTstat,
|
|||
u8 DimmRanks;
|
||||
if (DimmValid & (1 << (dimm << 1))) {
|
||||
DimmRanks = 1;
|
||||
if (pDCTstat->DimmDRPresent & (1 << (dimm+dct)))
|
||||
if (pDCTstat->DimmDRPresent & (1 << ((dimm << 1) + dct)))
|
||||
DimmRanks = 2;
|
||||
else if (pDCTstat->DimmQRPresent & (1 << (dimm+dct)))
|
||||
else if (pDCTstat->DimmQRPresent & (1 << ((dimm << 1) + dct)))
|
||||
DimmRanks = 4;
|
||||
} else
|
||||
DimmRanks = 0;
|
||||
|
@ -249,35 +250,6 @@ static void ChangeMemClk(struct MCTStatStruc *pMCTstat,
|
|||
}
|
||||
}
|
||||
|
||||
/* Multiply the previously saved delay values in Pass 1, step #5 by
|
||||
(target frequency)/400 to find the gross and fine delay initialization
|
||||
values at the target frequency.
|
||||
*/
|
||||
void MultiplyDelay(struct MCTStatStruc *pMCTstat,
|
||||
struct DCTStatStruc *pDCTstat, u8 dct)
|
||||
{
|
||||
u16 index;
|
||||
u8 Multiplier;
|
||||
u8 gross, fine;
|
||||
u16 total;
|
||||
|
||||
Multiplier = pDCTstat->TargetFreq;
|
||||
|
||||
for (index=0; index < MAX_BYTE_LANES*MAX_LDIMMS; index ++) {
|
||||
gross = pDCTstat->C_DCTPtr[dct]->WLGrossDelay[index];
|
||||
fine = pDCTstat->C_DCTPtr[dct]->WLFineDelay[index];
|
||||
|
||||
total = gross << 5 | fine;
|
||||
total *= Multiplier;
|
||||
if (total % 3)
|
||||
total = total / 3 + 1;
|
||||
else
|
||||
total = total / 3;
|
||||
pDCTstat->C_DCTPtr[dct]->WLGrossDelay[index] = (total & 0xFF) >> 5;
|
||||
pDCTstat->C_DCTPtr[dct]->WLFineDelay[index] = total & 0x1F;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* the DRAM controller to bring the DRAMs out of self refresh mode.
|
||||
*/
|
||||
|
@ -352,9 +324,9 @@ void SetTargetFreq(struct MCTStatStruc *pMCTstat,
|
|||
|
||||
if (!DCT1Present)
|
||||
pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[0];
|
||||
else if (pDCTstat->GangedMode) {
|
||||
else if (pDCTstat->GangedMode)
|
||||
pDCTstat->CSPresent = 0;
|
||||
} else
|
||||
else
|
||||
pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[1];
|
||||
|
||||
FreqChgCtrlWrd(pMCTstat, pDCTstat);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* This file is part of the coreboot project.
|
||||
*
|
||||
* Copyright (C) 2010 Advanced Micro Devices, Inc.
|
||||
* Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -235,6 +236,65 @@ u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue)
|
|||
return MRSValue;
|
||||
}
|
||||
|
||||
static uint16_t unbuffered_dimm_nominal_termination_emrs(uint8_t number_of_dimms, uint8_t frequency_index, uint8_t rank_count, uint8_t rank)
|
||||
{
|
||||
uint16_t term;
|
||||
|
||||
/* FIXME
|
||||
* Mainboards need to be able to specify the maximum number of DIMMs installable per channel
|
||||
* For now assume a maximum of 2 DIMMs per channel can be installed
|
||||
*/
|
||||
uint8_t MaxDimmsInstallable = 2;
|
||||
|
||||
if (number_of_dimms == 1) {
|
||||
if (MaxDimmsInstallable < 3) {
|
||||
term = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
|
||||
} else {
|
||||
if (rank_count == 1) {
|
||||
term = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
|
||||
} else {
|
||||
if (rank == 0)
|
||||
term = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
|
||||
else
|
||||
term = 0x00; /* Rtt_Nom=OFF */
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (frequency_index < 5)
|
||||
term = 0x0044; /* Rtt_Nom=RZQ/6=40 Ohm */
|
||||
else
|
||||
term = 0x0204; /* Rtt_Nom=RZQ/8=30 Ohm */
|
||||
}
|
||||
|
||||
return term;
|
||||
}
|
||||
|
||||
static uint16_t unbuffered_dimm_dynamic_termination_emrs(uint8_t number_of_dimms, uint8_t frequency_index, uint8_t rank_count, uint8_t rank)
|
||||
{
|
||||
uint16_t term;
|
||||
|
||||
/* FIXME
|
||||
* Mainboards need to be able to specify the maximum number of DIMMs installable per channel
|
||||
* For now assume a maximum of 2 DIMMs per channel can be installed
|
||||
*/
|
||||
uint8_t MaxDimmsInstallable = 2;
|
||||
|
||||
if (number_of_dimms == 1) {
|
||||
if (MaxDimmsInstallable < 3) {
|
||||
term = 0x00; /* Rtt_WR=off */
|
||||
} else {
|
||||
if (rank_count == 1)
|
||||
term = 0x00; /* Rtt_WR=off */
|
||||
else
|
||||
term = 0x200; /* Rtt_WR=RZQ/4=60 Ohm */
|
||||
}
|
||||
} else {
|
||||
term = 0x400; /* Rtt_WR=RZQ/2=120 Ohm */
|
||||
}
|
||||
|
||||
return term;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *DCTData, u8 Dimm, BOOL WL)
|
||||
*
|
||||
|
@ -295,48 +355,23 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl)
|
|||
if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
|
||||
tempW1 = RttNomTargetRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank);
|
||||
} else {
|
||||
if (wl)
|
||||
{
|
||||
if (pDCTData->MaxDimmsInstalled == 1)
|
||||
{
|
||||
if ((pDCTData->DimmRanks[dimm] == 2) && (rank == 0))
|
||||
{
|
||||
tempW1 = 0x00; /* Rtt_Nom=OFF */
|
||||
}
|
||||
if (wl) {
|
||||
if (rank == 0) {
|
||||
/* Get Rtt_WR for the current DIMM and rank */
|
||||
uint16_t dynamic_term = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank);
|
||||
|
||||
/* Convert dynamic termination code to corresponding nominal termination code */
|
||||
if (dynamic_term == 0x200)
|
||||
tempW1 = 0x04;
|
||||
else if (dynamic_term == 0x400)
|
||||
tempW1 = 0x40;
|
||||
else
|
||||
{
|
||||
tempW1 = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
|
||||
}
|
||||
}
|
||||
else /* 2 Dimms or more per channel */
|
||||
{
|
||||
if ((pDCTData->DimmRanks[dimm] == 2) && (rank == 1))
|
||||
{
|
||||
tempW1 = 0x00; /* Rtt_Nom=OFF */
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MemClkFreq == 6) {
|
||||
tempW1 = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
|
||||
} else {
|
||||
tempW1 = 0x40;/* Rtt_Nom=RZQ/2=120 Ohm */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else { /* 1 or 4 Dimms per channel */
|
||||
if ((pDCTData->MaxDimmsInstalled == 1) || (pDCTData->MaxDimmsInstalled == 4))
|
||||
{
|
||||
tempW1 = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
|
||||
}
|
||||
else /* 2 or 3 Dimms per channel */
|
||||
{
|
||||
if (MemClkFreq < 5) {
|
||||
tempW1 = 0x0044; /* Rtt_Nom=RZQ/6=40 Ohm */
|
||||
} else {
|
||||
tempW1 = 0x0204; /* Rtt_Nom=RZQ/8=30 Ohm */
|
||||
}
|
||||
tempW1 = 0x0;
|
||||
} else {
|
||||
tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank);
|
||||
}
|
||||
} else {
|
||||
tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank);
|
||||
}
|
||||
}
|
||||
tempW=tempW|tempW1;
|
||||
|
@ -353,20 +388,22 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl)
|
|||
else
|
||||
{
|
||||
/* Disable the output drivers of all other ranks for
|
||||
* the target DIMM. */
|
||||
* the target DIMM.
|
||||
*/
|
||||
tempW = bitTestSet(tempW1, Qoff);
|
||||
}
|
||||
}
|
||||
/* program MrsAddress[5,1]=output driver impedance control (DIC):
|
||||
* based on F2x[1,0]84[DrvImpCtrl] */
|
||||
/* Program MrsAddress[5,1]=output driver impedance control (DIC):
|
||||
* based on F2x[1,0]84[DrvImpCtrl]
|
||||
*/
|
||||
tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
|
||||
FUN_DCT, DRAM_MRS_REGISTER, DrvImpCtrlStart, DrvImpCtrlEnd);
|
||||
if (bitTest(tempW1,1))
|
||||
{tempW = bitTestSet(tempW, 5);}
|
||||
if (bitTest(tempW1,0))
|
||||
{tempW = bitTestSet(tempW, 1);}
|
||||
if (bitTest(tempW1, 1))
|
||||
tempW = bitTestSet(tempW, 5);
|
||||
if (bitTest(tempW1, 0))
|
||||
tempW = bitTestSet(tempW, 1);
|
||||
|
||||
tempW = swapAddrBits_wl(pDCTData,tempW);
|
||||
tempW = swapAddrBits_wl(pDCTData, tempW);
|
||||
|
||||
set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
|
||||
DRAM_INIT, MrsAddressStart, MrsAddressEnd, tempW);
|
||||
|
@ -404,29 +441,10 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl)
|
|||
if ((pDCTData->LogicalCPUID & AMD_DR_Bx) && (pDCTData->Status[DCT_STATUS_REGISTERED]))
|
||||
tempW+=0x8;
|
||||
/* determine Rtt_WR for WL & Normal mode */
|
||||
if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
|
||||
if (pDCTData->Status[DCT_STATUS_REGISTERED])
|
||||
tempW1 = RttWrRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank);
|
||||
} else {
|
||||
if (wl)
|
||||
{
|
||||
tempW1 = 0x00; /* Rtt_WR=off */
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pDCTData->MaxDimmsInstalled == 1)
|
||||
{
|
||||
tempW1 = 0x00; /* Rtt_WR=off */
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MemClkFreq == 6) {
|
||||
tempW1 = 0x200; /* Rtt_WR=RZQ/4=60 Ohm */
|
||||
} else {
|
||||
tempW1 = 0x400; /* Rtt_WR=RZQ/2 */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
tempW1 = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank);
|
||||
tempW=tempW|tempW1;
|
||||
tempW = swapAddrBits_wl(pDCTData,tempW);
|
||||
set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
|
||||
|
@ -483,38 +501,10 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl)
|
|||
}
|
||||
|
||||
/* determine Rtt_Nom for WL & Normal mode */
|
||||
if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
|
||||
if (pDCTData->Status[DCT_STATUS_REGISTERED])
|
||||
tempW1 = RttNomNonTargetRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank);
|
||||
} else {
|
||||
if (wl)
|
||||
{
|
||||
if ((pDCTData->DimmRanks[currDimm] == 2) && (rank == 1))
|
||||
{
|
||||
tempW1 = 0x00; /* Rtt_Nom=OFF */
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MemClkFreq < 5) {
|
||||
tempW1 = 0x0044;/* Rtt_Nom=RZQ/6=40 Ohm */
|
||||
} else {
|
||||
tempW1 = 0x0204;/* Rtt_Nom=RZQ/8=30 Ohm */
|
||||
}
|
||||
}
|
||||
}
|
||||
else { /* 1 or 4 Dimms per channel */
|
||||
if (pDCTData->MaxDimmsInstalled == 4)
|
||||
{
|
||||
tempW1 = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
|
||||
}
|
||||
else { /* 2 or 3 Dimms per channel */
|
||||
if (MemClkFreq < 5) {
|
||||
tempW1 = 0x0044; /* Rtt_Nom=RZQ/6=40 Ohm */
|
||||
} else {
|
||||
tempW1 = 0x0204; /* Rtt_Nom=RZQ/8=30 Ohm */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank);
|
||||
tempW=tempW|tempW1;
|
||||
/* program MrsAddress[5,1]=output driver impedance control (DIC):
|
||||
* based on F2x[1,0]84[DrvImpCtrl] */
|
||||
|
@ -560,22 +550,10 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl)
|
|||
if ((pDCTData->LogicalCPUID & AMD_DR_Bx) && (pDCTData->Status[DCT_STATUS_REGISTERED]))
|
||||
tempW+=0x8;
|
||||
/* determine Rtt_WR for WL & Normal mode */
|
||||
if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
|
||||
if (pDCTData->Status[DCT_STATUS_REGISTERED])
|
||||
tempW1 = RttWrRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank);
|
||||
} else {
|
||||
if (wl)
|
||||
{
|
||||
tempW1 = 0x00; /* Rtt_WR=off */
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MemClkFreq == 6) {
|
||||
tempW1 = 0x200; /* Rtt_WR=RZQ/4=60 Ohm */
|
||||
} else {
|
||||
tempW1 = 0x400; /* Rtt_WR=RZQ/2 */
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
tempW1 = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank);
|
||||
tempW=tempW|tempW1;
|
||||
tempW = swapAddrBits_wl(pDCTData,tempW);
|
||||
set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT,
|
||||
|
@ -646,9 +624,14 @@ void programODT(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm)
|
|||
*/
|
||||
void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass)
|
||||
{
|
||||
u8 ByteLane, Seed_Gross, Seed_Fine;
|
||||
u8 ByteLane, Seed_Gross, Seed_Fine, MemClkFreq;
|
||||
u32 Value, Addr;
|
||||
u16 Addl_Data_Offset, Addl_Data_Port;
|
||||
u16 freq_tab[] = {400, 533, 667, 800};
|
||||
|
||||
/* MemClkFreq: 3: 400MHz; 4: 533MHz; 5: 667MHz; 6: 800MHz */
|
||||
MemClkFreq = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
|
||||
FUN_DCT, DRAM_CONFIG_HIGH, 0, 2);
|
||||
|
||||
/* Program F2x[1, 0]9C_x08[WrLvOdt[3:0]] to the proper ODT settings for the
|
||||
* current memory subsystem configuration.
|
||||
|
@ -656,12 +639,13 @@ void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass)
|
|||
programODT(pMCTData, pDCTData, dimm);
|
||||
|
||||
/* Program F2x[1,0]9C_x08[WrLvOdtEn]=1 */
|
||||
if (pDCTData->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx))
|
||||
if (pDCTData->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx)) {
|
||||
set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
|
||||
DRAM_ADD_DCT_PHY_CONTROL_REG, WrLvOdtEn, WrLvOdtEn, (u32)1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Program WrLvOdtEn=1 through set bit 12 of D3CSODT reg offset 0 for Rev.B*/
|
||||
/* Program WrLvOdtEn=1 through set bit 12 of D3CSODT reg offset 0 for Rev.B */
|
||||
if (pDCTData->DctTrain)
|
||||
{
|
||||
Addl_Data_Offset=0x198;
|
||||
|
@ -687,7 +671,6 @@ void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass)
|
|||
|
||||
/* Wait 10 MEMCLKs to allow for ODT signal settling. */
|
||||
pMCTData->AgesaDelay(10);
|
||||
ByteLane = 0;
|
||||
if (pass == 1)
|
||||
{
|
||||
if (pDCTData->Status[DCT_STATUS_REGISTERED])
|
||||
|
@ -705,10 +688,17 @@ void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass)
|
|||
}
|
||||
else
|
||||
{
|
||||
Seed_Gross = 0x00;
|
||||
Seed_Fine = 0x1A;
|
||||
if (MemClkFreq == 6) {
|
||||
/* DDR-800 */
|
||||
Seed_Gross = 0x00;
|
||||
Seed_Fine = 0x1a;
|
||||
} else {
|
||||
/* Use settings for DDR-400 (interpolated from BKDG) */
|
||||
Seed_Gross = 0x00;
|
||||
Seed_Fine = 0x0d;
|
||||
}
|
||||
}
|
||||
while(ByteLane < MAX_BYTE_LANES)
|
||||
for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; 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
|
||||
|
@ -720,35 +710,32 @@ void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass)
|
|||
*/
|
||||
pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
|
||||
pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
|
||||
ByteLane++;
|
||||
}
|
||||
} else if (pDCTData->Status[DCT_STATUS_REGISTERED]) { /* For Pass 2 */
|
||||
} else { /* Pass 2 */
|
||||
/* From BKDG, Write Leveling Seed Value. */
|
||||
/* TODO: The unbuffered DIMMs are unstable on the code below. So temporarily it is
|
||||
* only for registered DIMMs. */
|
||||
u32 RegisterDelay, SeedTotal;
|
||||
u8 MemClkFreq;
|
||||
u16 freq_tab[] = {400, 533, 667, 800};
|
||||
while(ByteLane < MAX_BYTE_LANES)
|
||||
for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++)
|
||||
{
|
||||
MemClkFreq = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
|
||||
FUN_DCT, DRAM_CONFIG_HIGH, 0, 2);
|
||||
if (pDCTData->Status[DCT_STATUS_REGISTERED])
|
||||
RegisterDelay = 0x20; /* TODO: ((RCW2 & BIT0) == 0) ? 0x20 : 0x30; */
|
||||
else
|
||||
RegisterDelay = 0;
|
||||
SeedTotal = (pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1F) |
|
||||
pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] << 5;
|
||||
SeedTotal = (pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) |
|
||||
(pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] << 5);
|
||||
/* SeedTotalPreScaling = (the total delay value in F2x[1, 0]9C_x[4A:30] from pass 1 of write levelization
|
||||
training) - RegisterDelay. */
|
||||
/* MemClkFreq: 3: 400MHz; 4: 533MHz; 5: 667MHz; 6: 800MHz */
|
||||
SeedTotal = (u16) (RegisterDelay + ((((u32) SeedTotal - RegisterDelay) *
|
||||
freq_tab[MemClkFreq-3]) / 400));
|
||||
Seed_Gross = (SeedTotal & 0x20) != 0 ? 1 : 2;
|
||||
Seed_Fine = SeedTotal & 0x1F;
|
||||
SeedTotal = (uint16_t) (RegisterDelay + ((((uint64_t) SeedTotal - RegisterDelay) *
|
||||
freq_tab[MemClkFreq-3] * 100) / (freq_tab[0] * 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;
|
||||
pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
|
||||
pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
|
||||
ByteLane ++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -59,6 +59,10 @@ static u16 mctGet_NVbits(u8 index)
|
|||
val = 1;
|
||||
#elif CONFIG_CPU_SOCKET_TYPE == 0x13 /* ASB2 */
|
||||
val = 4;
|
||||
#elif CONFIG_CPU_SOCKET_TYPE == 0x14 /* C32 */
|
||||
val = 5;
|
||||
#elif CONFIG_CPU_SOCKET_TYPE == 0x15 /* G34 */
|
||||
val = 3;
|
||||
//#elif SYSTEM_TYPE == MOBILE
|
||||
// val = 2;
|
||||
#endif
|
||||
|
@ -413,101 +417,6 @@ static void mctHookAfterDramInit(void)
|
|||
}
|
||||
|
||||
#if (CONFIG_DIMM_SUPPORT & 0x000F)==0x0005 /* AMD_FAM10_DDR3 */
|
||||
static void coreDelay(u32 microseconds)
|
||||
{
|
||||
msr_t now;
|
||||
msr_t end;
|
||||
u32 cycles;
|
||||
|
||||
/* delay ~40us
|
||||
This seems like a hack to me...
|
||||
It would be nice to have a central delay function. */
|
||||
|
||||
cycles = (microseconds * 100) << 3; /* x8 (number of 1.25ns ticks) */
|
||||
|
||||
if (!(rdmsr(HWCR).lo & TSC_FREQ_SEL_MASK)) {
|
||||
msr_t pstate_msr = rdmsr(CUR_PSTATE_MSR);
|
||||
if (!(rdmsr(0xC0010064+pstate_msr.lo).lo & NB_DID_M_ON)) {
|
||||
cycles = cycles <<1; // half freq, double cycles
|
||||
}
|
||||
} // else should we keep p0 freq at the time of setting TSC_FREQ_SEL_MASK somewhere and check it here ?
|
||||
|
||||
now = rdmsr(TSC_MSR);
|
||||
// avoid overflow when called near 2^32 ticks ~ 5.3 s boundaries
|
||||
if (0xffffffff - cycles >= now.lo ) {
|
||||
end.hi = now.hi;
|
||||
end.lo = now.lo + cycles;
|
||||
} else {
|
||||
end.hi = now.hi +1; //
|
||||
end.lo = cycles - (1+(0xffffffff - now.lo));
|
||||
}
|
||||
do {
|
||||
now = rdmsr(TSC_MSR);
|
||||
} while ((now.hi < end.hi) || ((now.hi == end.hi) && (now.lo < end.lo)));
|
||||
}
|
||||
|
||||
/* Erratum 350 */
|
||||
static void vErrata350(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat)
|
||||
{
|
||||
u8 u8Channel;
|
||||
u8 u8Receiver;
|
||||
u32 u32Addr;
|
||||
u8 u8Valid;
|
||||
u32 u32DctDev;
|
||||
|
||||
// 1. dummy read for each installed DIMM */
|
||||
for (u8Channel = 0; u8Channel < 2; u8Channel++) {
|
||||
// This will be 0 for vaild DIMMS, eles 8
|
||||
u8Receiver = mct_InitReceiver_D(pDCTstat, u8Channel);
|
||||
|
||||
for (; u8Receiver < 8; u8Receiver += 2) {
|
||||
u32Addr = mct_GetRcvrSysAddr_D(pMCTstat, pDCTstat, u8Channel, u8Receiver, &u8Valid);
|
||||
|
||||
if(!u8Valid) { /* Address not supported on current CS */
|
||||
print_t("vErrata350: Address not supported on current CS\n");
|
||||
continue;
|
||||
}
|
||||
print_t("vErrata350: dummy read \n");
|
||||
read32_fs(u32Addr);
|
||||
}
|
||||
}
|
||||
|
||||
print_t("vErrata350: step 2a\n");
|
||||
|
||||
/* 2. Write 0000_8000h to register F2x[1, 0]9C_xD080F0C. */
|
||||
u32DctDev = pDCTstat->dev_dct;
|
||||
Set_NB32_index_wait(u32DctDev, 0x098, 0xD080F0C, 0x00008000);
|
||||
/* ^--- value
|
||||
^---F2x[1, 0]9C_x0D080F0C, No description in BKDG.
|
||||
^----F2x[1, 0]98 DRAM Controller Additional Data Offset Register */
|
||||
|
||||
if(!pDCTstat->GangedMode) {
|
||||
print_t("vErrata350: step 2b\n");
|
||||
Set_NB32_index_wait(u32DctDev, 0x198, 0xD080F0C, 0x00008000);
|
||||
/* ^--- value
|
||||
^---F2x[1, 0]9C_x0D080F0C, No description in BKDG
|
||||
^----F2x[1, 0]98 DRAM Controller Additional Data Offset Register */
|
||||
}
|
||||
|
||||
print_t("vErrata350: step 3\n");
|
||||
/* 3. Wait at least 300 nanoseconds. */
|
||||
coreDelay(1);
|
||||
|
||||
print_t("vErrata350: step 4\n");
|
||||
/* 4. Write 0000_0000h to register F2x[1, 0]9C_xD080F0C. */
|
||||
Set_NB32_index_wait(u32DctDev, 0x098, 0xD080F0C, 0x00000000);
|
||||
|
||||
if(!pDCTstat->GangedMode) {
|
||||
print_t("vErrata350: step 4b\n");
|
||||
Set_NB32_index_wait(u32DctDev, 0x198, 0xD080F0C, 0x00000000);
|
||||
}
|
||||
|
||||
print_t("vErrata350: step 5\n");
|
||||
/* 5. Wait at least 2 microseconds. */
|
||||
coreDelay(2);
|
||||
|
||||
}
|
||||
|
||||
static void vErratum372(struct DCTStatStruc *pDCTstat)
|
||||
{
|
||||
msr_t msr = rdmsr(NB_CFG_MSR);
|
||||
|
@ -546,8 +455,7 @@ static void mctHookBeforeAnyTraining(struct MCTStatStruc *pMCTstat, struct DCTSt
|
|||
{
|
||||
#if (CONFIG_DIMM_SUPPORT & 0x000F)==0x0005 /* AMD_FAM10_DDR3 */
|
||||
/* FIXME : as of 25.6.2010 errata 350 and 372 should apply to ((RB|BL|DA)-C[23])|(HY-D[01])|(PH-E0) but I don't find constants for all of them */
|
||||
if (pDCTstatA->LogicalCPUID & AMD_DRBH_Cx) {
|
||||
vErrata350(pMCTstat, pDCTstatA);
|
||||
if (pDCTstatA->LogicalCPUID & (AMD_DRBH_Cx | AMD_DR_Dx)) {
|
||||
vErratum372(pDCTstatA);
|
||||
vErratum414(pDCTstatA);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue