BOOLEAN MemTTrainDQSEdgeDetectSw ( IN OUT MEM_TECH_BLOCK *TechPtr ) { MEM_NB_BLOCK *NBPtr; BOOLEAN Status; Status = FALSE; NBPtr = TechPtr->NBPtr; TechPtr->TrainingType = TRN_DQS_POSITION; // // Initialize the Pattern // if (AGESA_SUCCESS == NBPtr->TrainingPatternInit (NBPtr)) { // // Setup hardware training engine (if applicable) // NBPtr->FamilySpecificHook[SetupHwTrainingEngine] (NBPtr, &TechPtr->TrainingType); // // Start Edge Detection // Status |= MemTTrainDQSRdWrEdgeDetect (TechPtr); // // Finalize the Pattern // Status &= (AGESA_SUCCESS == NBPtr->TrainingPatternFinalize (NBPtr)); } return Status; }
/** * * * This function determines if the PSP is present * * @param[in] *StdHeader - Config handle for library and services. * * @return AGESA_STATUS * - AGESA_FATAL * - AGESA_SUCCESS */ AGESA_STATUS AmdMemDoResume ( IN AMD_CONFIG_PARAMS *StdHeader ) { S3_MEM_NB_BLOCK *S3NBPtr; MEM_NB_BLOCK *NBPtr; LOCATE_HEAP_PTR LocHeap; LocHeap.BufferHandle = AMD_MEM_S3_NB_HANDLE; S3NBPtr = NULL; if (HeapLocateBuffer (&LocHeap, StdHeader) == AGESA_SUCCESS) { S3NBPtr = (S3_MEM_NB_BLOCK *)LocHeap.BufferPtr; NBPtr = ((S3_MEM_NB_BLOCK *)S3NBPtr)->NBPtr; } else { ASSERT (FALSE) ; // No match for heap status, but could not locate "AMD_MEM_S3_NB_HANDLE" in heap for S3GetMsr return AGESA_FATAL; } if (S3NBPtr[BSP_DIE].MemS3PspPlatformSecureBootEn (S3NBPtr[BSP_DIE].NBPtr) == NBPtr->MemRunningOnPsp (NBPtr)) { return AGESA_SUCCESS; } else { return AGESA_FATAL; } }
INT8 MemTGetLD3 ( IN OUT MEM_TECH_BLOCK *TechPtr ) { INT8 LD; MEM_NB_BLOCK *NBPtr; NBPtr = TechPtr->NBPtr; // // For DDR3, BIOS calculates the latency difference (Ld) as equal to read CAS latency minus write CAS // latency, in MEMCLKs (see F2x[1, 0]88[Tcl] and F2x[1, 0]84[Tcwl]) which can be a negative or positive // value. // LD = ((INT8) NBPtr->GetBitField (NBPtr, BFTcl) + 4) - ((INT8) NBPtr->GetBitField (NBPtr, BFTcwl) + 5); return LD; }
VOID MemRecTEMRS13 ( IN OUT MEM_TECH_BLOCK *TechPtr ) { UINT16 MrsAddress; UINT8 DramTerm; MEM_NB_BLOCK *NBPtr; NBPtr = TechPtr->NBPtr; // BA2=0,BA1=0,BA0=1 NBPtr->SetBitField (NBPtr, BFMrsBank, 1); MrsAddress = 0; // program MrsAddress[5,1]=output driver impedance control (DIC): // based on F2x[1,0]84[DrvImpCtrl], which is 2'b01 MrsAddress |= ((UINT16) 1 << 1); // program MrsAddress[9,6,2]=nominal termination resistance of ODT (RTT): // based on F2x[1,0]84[DramTerm], which is 3'b001 (60 Ohms) if (!(NBPtr->IsSupported[CheckDramTerm])) { DramTerm = (UINT8) NBPtr->GetBitField (NBPtr, BFDramTerm); } else { DramTerm = NBPtr->PsPtr->DramTerm; } if ((DramTerm & 1) != 0) { MrsAddress |= ((UINT16) 1 << 2); } if ((DramTerm & 2) != 0) { MrsAddress |= ((UINT16) 1 << 6); } if ((DramTerm & 4) != 0) { MrsAddress |= ((UINT16) 1 << 9); } // program MrsAddress[12]=output disable (QOFF): // based on F2x[1,0]84[Qoff], which is 1'b0 // program MrsAddress[11]=TDQS: // based on F2x[1,0]94[RDqsEn], which is 1'b0 NBPtr->SetBitField (NBPtr, BFMrsAddress, MrsAddress); }
VOID MemRecTEMRS33 ( IN OUT MEM_TECH_BLOCK *TechPtr ) { MEM_NB_BLOCK *NBPtr; NBPtr = TechPtr->NBPtr; // BA2=0,BA1=1,BA0=1 NBPtr->SetBitField (NBPtr, BFMrsBank, 3); // program MrsAddress[1:0]=multi purpose register address location // (MPR Location):based on F2x[1,0]84[MprLoc], which is 0 // program MrsAddress[2]=multi purpose register // (MPR):based on F2x[1,0]84[MprEn], which is also 0 // NBPtr->SetBitField (NBPtr, BFMrsAddress, 0); }
/** * * This function sets the TestFail bit for all CS that fail training. * * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK */ VOID MemTMarkTrainFail ( IN OUT MEM_TECH_BLOCK *TechPtr ) { MEM_NB_BLOCK *NBPtr; UINT8 Dct; UINT8 ChipSel; NBPtr = TechPtr->NBPtr; for (Dct = 0; Dct < NBPtr->DctCount; Dct ++) { NBPtr->SwitchDCT (NBPtr, Dct); NBPtr->DCTPtr->Timings.CsEnabled &= ~NBPtr->DCTPtr->Timings.CsTrainFail; for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel ++) { if ((NBPtr->DCTPtr->Timings.CsTrainFail & ((UINT16)1 << ChipSel)) != 0) { NBPtr->SetBitField (NBPtr, (BFCSBaseAddr0Reg + ChipSel), (UINT32)1 << BFTestFail); } } } }
VOID MemTEndTraining ( IN OUT MEM_TECH_BLOCK *TechPtr ) { S_UINT64 SMsr; MEM_DATA_STRUCT *MemPtr; MEM_NB_BLOCK *NBPtr; NBPtr = TechPtr->NBPtr; MemPtr = NBPtr->MemPtr; LibAmdWriteCpuReg (CR4_REG, TechPtr->CR4reg); LibAmdMsrRead (HWCR, (UINT64 *)&SMsr, &MemPtr->StdHeader); SMsr.lo = TechPtr->HwcrLo; LibAmdMsrWrite (HWCR, (UINT64 *)&SMsr, &MemPtr->StdHeader); NBPtr->SetBitField (NBPtr, BFDramEccEn, TechPtr->DramEcc); }
VOID MemTDramInitHw ( IN OUT MEM_TECH_BLOCK *TechPtr ) { UINT8 Dct; MEM_NB_BLOCK *NBPtr; NBPtr = TechPtr->NBPtr; NBPtr->BrdcstSet (NBPtr, BFInitDram, 1); // Phy fence training AGESA_TESTPOINT (TpProcMemPhyFenceTraining, &(NBPtr->MemPtr->StdHeader)); for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { NBPtr->SwitchDCT (NBPtr, Dct); if (NBPtr->DCTPtr->Timings.DctMemSize != 0) { IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct); NBPtr->PhyFenceTraining (NBPtr); } } }
/** * * This function is the interface to call the PCI register access function * defined in NB block. * * @param[in] *NBPtr - Pointer to the parameter structure MEM_NB_BLOCK * @param[in] NodeID - Node ID number of the target Northbridge * @param[in] DctNum - DCT number if applicable, otherwise, put 0 * @param[in] BitFieldName - targeted bitfield * * @retval UINT32 - 32 bits PCI register value * */ UINT32 MemFGetPCI ( IN MEM_NB_BLOCK *NBPtr, IN UINT8 NodeID, IN UINT8 DctNum, IN BIT_FIELD_NAME BitFieldName ) { MEM_NB_BLOCK *LocalNBPtr; UINT8 Die; // Find NBBlock that associates with node NodeID for (Die = 0; (Die < MAX_NODES_SUPPORTED) && (NBPtr[Die].Node != NodeID); Die ++); ASSERT (Die < MAX_NODES_SUPPORTED); // Get the northbridge pointer for the targeted node. LocalNBPtr = &NBPtr[Die]; LocalNBPtr->FamilySpecificHook[DCTSelectSwitch] (LocalNBPtr, &DctNum); LocalNBPtr->Dct = DctNum; // The caller of this function will take care of the ganged/unganged situation. // So Ganged is set to be false here, and do PCI read on the DCT specified by DctNum. return LocalNBPtr->GetBitField (LocalNBPtr, BitFieldName); }
VOID MemRecTEMRS23 ( IN OUT MEM_TECH_BLOCK *TechPtr ) { UINT16 MrsAddress; UINT8 DramTermDyn; MEM_NB_BLOCK *NBPtr; NBPtr = TechPtr->NBPtr; // BA2=0,BA1=1,BA0=0 NBPtr->SetBitField (NBPtr, BFMrsBank, 2); // program MrsAddress[5:3]=CAS write latency (CWL): // based on F2x[1,0]84[Tcwl], which is 3'b000 // MrsAddress = 0; // program MrsAddress[6]=auto self refresh method (ASR): // based on F2x[1,0]84[ASR], which is 1'b1 // program MrsAddress[7]=self refresh temperature range (SRT): // based on F2x[1,0]84[SRT], which is also 1'b0 // MrsAddress |= (UINT16) 1 << 6; // program MrsAddress[10:9]=dynamic termination during writes (RTT_WR): // based on F2x[1,0]84[DramTermDyn] // if (!(NBPtr->IsSupported[CheckDramTermDyn])) { DramTermDyn = (UINT8) NBPtr->GetBitField (NBPtr, BFDramTermDyn); } else { DramTermDyn = NBPtr->PsPtr->DynamicDramTerm; } MrsAddress |= (UINT16) DramTermDyn << 9; NBPtr->SetBitField (NBPtr, BFMrsAddress, MrsAddress); }
VOID MemRecTMRS3 ( IN OUT MEM_TECH_BLOCK *TechPtr ) { UINT16 MrsAddress; MEM_NB_BLOCK *NBPtr; NBPtr = TechPtr->NBPtr; // BA2=0,BA1=0,BA0=0 NBPtr->SetBitField (NBPtr, BFMrsBank, 0); // program MrsAddress[1:0]=burst length and control method // (BL):based on F2x[1,0]84[BurstCtrl], which is 1'b0 // MrsAddress = 0; // program MrsAddress[3]=1 (BT):interleaved MrsAddress |= (UINT16) 1 << 3; // program MrsAddress[6:4,2]=read CAS latency // (CL):based on F2x[1,0]88[Tcl], which is 4'b0010 MrsAddress |= (UINT16) 2 << 4; // program MrsAddress[11:9]=write recovery for auto-precharge // (WR):based on F2x[1,0]84[Twr], which is 3'b010 // MrsAddress |= (UINT16) 2 << 9; // program MrsAddress[12]=0 (PPD):slow exit // program MrsAddress[8]=1 (DLL):DLL reset MrsAddress |= (UINT16) 1 << 8; // just issue DLL reset at first time NBPtr->SetBitField (NBPtr, BFMrsAddress, MrsAddress); }
VOID MemTBeginTraining ( IN OUT MEM_TECH_BLOCK *TechPtr ) { S_UINT64 SMsr; MEM_DATA_STRUCT *MemPtr; MEM_NB_BLOCK *NBPtr; NBPtr = TechPtr->NBPtr; MemPtr = NBPtr->MemPtr; LibAmdReadCpuReg (CR4_REG, &TechPtr->CR4reg); LibAmdWriteCpuReg (CR4_REG, TechPtr->CR4reg | ((UINT32)1 << 9)); // enable SSE2 LibAmdMsrRead (HWCR, (UINT64 *) (&SMsr), &MemPtr->StdHeader); // HWCR TechPtr->HwcrLo = SMsr.lo; SMsr.lo |= 0x00020000; // turn on HWCR.wrap32dis SMsr.lo &= 0xFFFF7FFF; // turn off HWCR.SSEDIS LibAmdMsrWrite (HWCR, (UINT64 *) (&SMsr), &MemPtr->StdHeader); TechPtr->DramEcc = (UINT8) NBPtr->GetBitField (NBPtr, BFDramEccEn); NBPtr->SetBitField (NBPtr, BFDramEccEn, 0); // Disable ECC }
BOOLEAN STATIC MemTTrainDQSRdWrEdgeDetect ( IN OUT MEM_TECH_BLOCK *TechPtr ) { MEM_DATA_STRUCT *MemPtr; MEM_NB_BLOCK *NBPtr; UINT8 WrDqDelay; UINT8 Dct; UINT8 CSPerChannel; UINT8 CsPerDelay; UINT8 ChipSel; UINT8 i; BOOLEAN Status; UINT8 TimesFail; UINT8 TimesRetrain; NBPtr = TechPtr->NBPtr; MemPtr = NBPtr->MemPtr; TimesRetrain = DEFAULT_TRAINING_TIMES; IDS_OPTION_HOOK (IDS_MEM_RETRAIN_TIMES, &TimesRetrain, &MemPtr->StdHeader); // // Set environment settings before training // IDS_HDT_CONSOLE (MEM_STATUS, "\nStart Read/Write Data Eye Edge Detection.\n"); MemTBeginTraining (TechPtr); // // Do Rd DQS /Wr Data Position training for all Dcts/Chipselects // for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct); NBPtr->SwitchDCT (NBPtr, Dct); // // Chip Select Loop // CSPerChannel = NBPtr->CSPerChannel (NBPtr); CsPerDelay = NBPtr->CSPerDelay (NBPtr); for (ChipSel = 0; ChipSel < CSPerChannel; ChipSel = ChipSel + CsPerDelay ) { // // Init Bit Error Masks // LibAmdMemFill (&NBPtr->ChannelPtr->FailingBitMask[ (ChipSel * MAX_BYTELANES_PER_CHANNEL) ], 0xFF, (MAX_BYTELANES_PER_CHANNEL * CsPerDelay), &MemPtr->StdHeader); if ((NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16) 1 << ChipSel)) != 0) { TechPtr->ChipSel = ChipSel; IDS_HDT_CONSOLE (MEM_STATUS, "\t\tCS %d\n", ChipSel); IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tIncrease WrDat, Train RdDqs:\n"); TechPtr->DqsRdWrPosSaved = 0; // // Use a list of Approximate Write Data delay values and train Read DQS Position for // each until a valid Data eye is found. // Status = FALSE; TimesFail = 0; ERROR_HANDLE_RETRAIN_BEGIN (TimesFail, TimesRetrain) { i = 0; while (NBPtr->GetApproximateWriteDatDelay (NBPtr, i, &WrDqDelay)) { TechPtr->SmallDqsPosWindow = FALSE; // // Set Write Delay approximation // TechPtr->Direction = DQS_WRITE_DIR; IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\tWrite Delay: %02x", WrDqDelay); MemTSetDQSDelayAllCSR (TechPtr, WrDqDelay); // // Attempt Read Training // TechPtr->Direction = DQS_READ_DIR; if (MemTTrainDQSEdgeDetect (TechPtr)) { // // If Read DQS Training was successful, Train Write Data (DQ) Position // TechPtr->DqsRdWrPosSaved = 0; IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\tTrain WrDat:\n\n"); TechPtr->Direction = DQS_WRITE_DIR; Status = MemTTrainDQSEdgeDetect (TechPtr); break; } i++; } ERROR_HANDLE_RETRAIN_END ((Status == FALSE), TimesFail) } // // If we went through the table, Fail. // if (Status == FALSE) { // On training failure, check and record whether training fails due to small window or no window if (TechPtr->SmallDqsPosWindow) { NBPtr->MCTPtr->ErrStatus[EsbSmallDqs] = TRUE; } else { NBPtr->MCTPtr->ErrStatus[EsbNoDqsPos] = TRUE; } SetMemError (AGESA_ERROR, NBPtr->MCTPtr); if (TechPtr->Direction == DQS_READ_DIR) { PutEventLog (AGESA_ERROR, MEM_ERROR_NO_DQS_POS_RD_WINDOW, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader); } else { PutEventLog (AGESA_ERROR, MEM_ERROR_NO_DQS_POS_WR_WINDOW, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader); } NBPtr->DCTPtr->Timings.CsTrainFail |= (UINT16)1 << ChipSel; // If the even chip select failed training always fail the odd, if present. if ((ChipSel & 0x01) == 0) { if (NBPtr->DCTPtr->Timings.CsPresent & ((UINT16)1 << (ChipSel + 1))) { NBPtr->DCTPtr->Timings.CsTrainFail |= (UINT16)1 << (ChipSel + 1); } } NBPtr->MemPtr->ErrorHandling (NBPtr->MCTPtr, NBPtr->Dct, NBPtr->DCTPtr->Timings.CsTrainFail, &NBPtr->MemPtr->StdHeader); } } else {
/** * * * This is the main function to perform parallel training on all nodes. * This is the routine which will run on the remote AP. * * @param[in,out] *EnvPtr - Pointer to the Training Environment Data * @param[in,out] *StdHeader - Pointer to the Standard Header of the AP * * @return TRUE - This feature is enabled. * @return FALSE - This feature is not enabled. */ BOOLEAN MemFParallelTraining ( IN OUT REMOTE_TRAINING_ENV *EnvPtr, IN OUT AMD_CONFIG_PARAMS *StdHeader ) { MEM_PARAMETER_STRUCT ParameterList; MEM_NB_BLOCK NB; MEM_TECH_BLOCK TB; ALLOCATE_HEAP_PARAMS AllocHeapParams; MEM_DATA_STRUCT *MemPtr; DIE_STRUCT *MCTPtr; UINT8 p; UINT8 i; UINT8 Dct; UINT8 Channel; UINT8 *BufferPtr; UINT8 DctCount; UINT8 ChannelCount; UINT8 RowCount; UINT8 ColumnCount; UINT16 SizeOfNewBuffer; AP_DATA_TRANSFER ReturnData; // // Initialize Parameters // ReturnData.DataPtr = NULL; ReturnData.DataSizeInDwords = 0; ReturnData.DataTransferFlags = 0; ASSERT (EnvPtr != NULL); // // Replace Standard header of a AP // LibAmdMemCopy (StdHeader, &(EnvPtr->StdHeader), sizeof (AMD_CONFIG_PARAMS), &(EnvPtr->StdHeader)); // // Allocate buffer for training data // BufferPtr = (UINT8 *) (&EnvPtr->DieStruct); DctCount = EnvPtr->DieStruct.DctCount; BufferPtr += sizeof (DIE_STRUCT); ChannelCount = ((DCT_STRUCT *) BufferPtr)->ChannelCount; BufferPtr += DctCount * sizeof (DCT_STRUCT); RowCount = ((CH_DEF_STRUCT *) BufferPtr)->RowCount; ColumnCount = ((CH_DEF_STRUCT *) BufferPtr)->ColumnCount; SizeOfNewBuffer = sizeof (DIE_STRUCT) + DctCount * ( sizeof (DCT_STRUCT) + ( ChannelCount * ( sizeof (CH_DEF_STRUCT) + sizeof (MEM_PS_BLOCK) + ( RowCount * ColumnCount * NUMBER_OF_DELAY_TABLES + (MAX_BYTELANES_PER_CHANNEL * MAX_CS_PER_CHANNEL * NUMBER_OF_FAILURE_MASK_TABLES) + (MAX_DIMMS_PER_CHANNEL * MAX_NUMBER_LANES) ) ) ) ); AllocHeapParams.RequestedBufferSize = SizeOfNewBuffer; AllocHeapParams.BufferHandle = GENERATE_MEM_HANDLE (ALLOC_PAR_TRN_HANDLE, 0, 0, 0); AllocHeapParams.Persist = HEAP_LOCAL_CACHE; if (HeapAllocateBuffer (&AllocHeapParams, StdHeader) == AGESA_SUCCESS) { BufferPtr = AllocHeapParams.BufferPtr; LibAmdMemCopy ( BufferPtr, &(EnvPtr->DieStruct), sizeof (DIE_STRUCT) + DctCount * (sizeof (DCT_STRUCT) + ChannelCount * (sizeof (CH_DEF_STRUCT) + sizeof (MEM_PS_BLOCK))), StdHeader ); // // Fix up pointers // MCTPtr = (DIE_STRUCT *) BufferPtr; BufferPtr += sizeof (DIE_STRUCT); MCTPtr->DctData = (DCT_STRUCT *) BufferPtr; BufferPtr += MCTPtr->DctCount * sizeof (DCT_STRUCT); for (Dct = 0; Dct < MCTPtr->DctCount; Dct++) { MCTPtr->DctData[Dct].ChData = (CH_DEF_STRUCT *) BufferPtr; BufferPtr += MCTPtr->DctData[Dct].ChannelCount * sizeof (CH_DEF_STRUCT); for (Channel = 0; Channel < MCTPtr->DctData[Dct].ChannelCount; Channel++) { MCTPtr->DctData[Dct].ChData[Channel].MCTPtr = MCTPtr; MCTPtr->DctData[Dct].ChData[Channel].DCTPtr = &MCTPtr->DctData[Dct]; } } NB.PSBlock = (MEM_PS_BLOCK *) BufferPtr; BufferPtr += DctCount * ChannelCount * sizeof (MEM_PS_BLOCK); ReturnData.DataPtr = AllocHeapParams.BufferPtr; ReturnData.DataSizeInDwords = (SizeOfNewBuffer + 3) / 4; ReturnData.DataTransferFlags = 0; // // Allocate Memory for the MEM_DATA_STRUCT we will use // AllocHeapParams.RequestedBufferSize = sizeof (MEM_DATA_STRUCT); AllocHeapParams.BufferHandle = AMD_MEM_DATA_HANDLE; AllocHeapParams.Persist = HEAP_LOCAL_CACHE; if (HeapAllocateBuffer (&AllocHeapParams, StdHeader) == AGESA_SUCCESS) { MemPtr = (MEM_DATA_STRUCT *)AllocHeapParams.BufferPtr; LibAmdMemCopy (&(MemPtr->StdHeader), &(EnvPtr->StdHeader), sizeof (AMD_CONFIG_PARAMS), StdHeader); // // Copy Parameters from environment // ParameterList.HoleBase = EnvPtr->HoleBase; ParameterList.BottomIo = EnvPtr->BottomIo; ParameterList.UmaSize = EnvPtr->UmaSize; ParameterList.SysLimit = EnvPtr->SysLimit; ParameterList.TableBasedAlterations = EnvPtr->TableBasedAlterations; ParameterList.PlatformMemoryConfiguration = EnvPtr->PlatformMemoryConfiguration; MemPtr->ParameterListPtr = &ParameterList; for (p = 0; p < MAX_PLATFORM_TYPES; p++) { MemPtr->GetPlatformCfg[p] = EnvPtr->GetPlatformCfg[p]; } MemPtr->ErrorHandling = EnvPtr->ErrorHandling; // // Create Local NBBlock and Tech Block // EnvPtr->NBBlockCtor (&NB, MCTPtr, EnvPtr->FeatPtr); NB.RefPtr = &ParameterList; NB.MemPtr = MemPtr; i = 0; while (memTechInstalled[i] != NULL) { if (memTechInstalled[i] (&TB, &NB)) { break; } i++; } NB.TechPtr = &TB; NB.TechBlockSwitch (&NB); // // Setup CPU Mem Type MSRs on the AP // NB.CpuMemTyping (&NB); IDS_HDT_CONSOLE (MEM_STATUS, "Node %d\n", NB.Node); // // Call Technology Specific Training routine // NB.TrainingFlow (&NB); // // Copy training data to ReturnData buffer // LibAmdMemCopy ( BufferPtr, MCTPtr->DctData[0].ChData[0].RcvEnDlys, ((DctCount * ChannelCount) * ( (RowCount * ColumnCount * NUMBER_OF_DELAY_TABLES) + (MAX_BYTELANES_PER_CHANNEL * MAX_CS_PER_CHANNEL * NUMBER_OF_FAILURE_MASK_TABLES) + (MAX_DIMMS_PER_CHANNEL * MAX_NUMBER_LANES) ) ), StdHeader); HeapDeallocateBuffer (AMD_MEM_DATA_HANDLE, StdHeader); // // Restore pointers // for (Dct = 0; Dct < MCTPtr->DctCount; Dct++) { for (Channel = 0; Channel < MCTPtr->DctData[Dct].ChannelCount; Channel++) { MCTPtr->DctData[Dct].ChData[Channel].MCTPtr = &EnvPtr->DieStruct; MCTPtr->DctData[Dct].ChData[Channel].DCTPtr = &EnvPtr->DieStruct.DctData[Dct]; MCTPtr->DctData[Dct].ChData[Channel].RcvEnDlys = EnvPtr->DieStruct.DctData[Dct].ChData[Channel].RcvEnDlys; MCTPtr->DctData[Dct].ChData[Channel].WrDqsDlys = EnvPtr->DieStruct.DctData[Dct].ChData[Channel].WrDqsDlys; MCTPtr->DctData[Dct].ChData[Channel].RdDqsDlys = EnvPtr->DieStruct.DctData[Dct].ChData[Channel].RdDqsDlys; MCTPtr->DctData[Dct].ChData[Channel].RdDqsDlys = EnvPtr->DieStruct.DctData[Dct].ChData[Channel].RdDqsDlys; MCTPtr->DctData[Dct].ChData[Channel].WrDatDlys = EnvPtr->DieStruct.DctData[Dct].ChData[Channel].WrDatDlys; MCTPtr->DctData[Dct].ChData[Channel].RdDqs2dDlys = EnvPtr->DieStruct.DctData[Dct].ChData[Channel].RdDqs2dDlys; MCTPtr->DctData[Dct].ChData[Channel].RdDqsMinDlys = EnvPtr->DieStruct.DctData[Dct].ChData[Channel].RdDqsMinDlys; MCTPtr->DctData[Dct].ChData[Channel].RdDqsMaxDlys = EnvPtr->DieStruct.DctData[Dct].ChData[Channel].RdDqsMaxDlys; MCTPtr->DctData[Dct].ChData[Channel].WrDatMinDlys = EnvPtr->DieStruct.DctData[Dct].ChData[Channel].WrDatMinDlys; MCTPtr->DctData[Dct].ChData[Channel].WrDatMaxDlys = EnvPtr->DieStruct.DctData[Dct].ChData[Channel].WrDatMaxDlys; MCTPtr->DctData[Dct].ChData[Channel].FailingBitMask = EnvPtr->DieStruct.DctData[Dct].ChData[Channel].FailingBitMask; } MCTPtr->DctData[Dct].ChData = EnvPtr->DieStruct.DctData[Dct].ChData; } MCTPtr->DctData = EnvPtr->DieStruct.DctData; } // // Signal to BSP that training is complete and Send Results // ASSERT (ReturnData.DataPtr != NULL); ApUtilTransmitBuffer (EnvPtr->BspSocket, EnvPtr->BspCore, &ReturnData, StdHeader); // // Clean up and exit. // HeapDeallocateBuffer (GENERATE_MEM_HANDLE (ALLOC_PAR_TRN_HANDLE, 0, 0, 0), StdHeader); } else { MCTPtr = &EnvPtr->DieStruct; PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_ALLOCATE_FOR_TRAINING_DATA, MCTPtr->NodeId, 0, 0, 0, StdHeader); SetMemError (AGESA_FATAL, MCTPtr); ASSERT(FALSE); // Could not allocate heap for buffer for parallel training data } return TRUE; }
BOOLEAN MemTSetDQSEccTmgsRDdr3 ( IN OUT MEM_TECH_BLOCK *TechPtr ) { UINT8 Dct; UINT8 Dimm; UINT8 i; UINT8 *WrDqsDly; UINT16 *RcvEnDly; UINT8 *RdDqsDly; UINT8 *WrDatDly; UINT8 EccByte; INT16 TempValue; MEM_NB_BLOCK *NBPtr; CH_DEF_STRUCT *ChannelPtr; EccByte = TechPtr->MaxByteLanes (); NBPtr = TechPtr->NBPtr; if (NBPtr->MCTPtr->NodeMemSize) { for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { NBPtr->SwitchDCT (NBPtr, Dct); if (NBPtr->DCTPtr->Timings.DctMemSize != 0) { ChannelPtr = NBPtr->ChannelPtr; for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) { if (NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16)3 << (Dimm * 2))) { i = Dimm * TechPtr->DlyTableWidth (); WrDqsDly = &ChannelPtr->WrDqsDlys[i]; RcvEnDly = &ChannelPtr->RcvEnDlys[i]; RdDqsDly = &ChannelPtr->RdDqsDlys[i]; WrDatDly = &ChannelPtr->WrDatDlys[i]; // Receiver DQS Enable: // Receiver DQS enable for ECC bytelane = Receiver DQS enable for bytelane 3 - // [write DQS for bytelane 3 - write DQS for ECC] TempValue = (INT16) RcvEnDly[3] - (INT16) (WrDqsDly[3] - WrDqsDly[EccByte]); if (TempValue < 0) { TempValue = 0; } RcvEnDly[EccByte] = (UINT16) TempValue; // Read DQS: // Read DQS for ECC bytelane = read DQS of byte lane 3 // RdDqsDly[EccByte] = RdDqsDly[3]; // Write Data: // Write Data for ECC bytelane = Write DQS for ECC + // [write data for bytelane 3 - Write DQS for bytelane 3] TempValue = (INT16) (WrDqsDly[EccByte] + (INT8) (WrDatDly[3] - WrDqsDly[3])); if (TempValue < 0) { TempValue = 0; } WrDatDly[EccByte] = (UINT8) TempValue; NBPtr->SetTrainDly (NBPtr, AccessRcvEnDly, DIMM_BYTE_ACCESS (Dimm, EccByte), RcvEnDly[EccByte]); NBPtr->SetTrainDly (NBPtr, AccessRdDqsDly, DIMM_BYTE_ACCESS (Dimm, EccByte), RdDqsDly[EccByte]); NBPtr->SetTrainDly (NBPtr, AccessWrDatDly, DIMM_BYTE_ACCESS (Dimm, EccByte), WrDatDly[EccByte]); } } } } } return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL); }
/** * * This function executes receiver enable training for a specific die * * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK * @param[in] Pass - Pass of the receiver training * * @return TRUE - No fatal error occurs. * @return FALSE - Fatal error occurs. */ BOOLEAN STATIC MemTDqsTrainRcvrEnSw ( IN OUT MEM_TECH_BLOCK *TechPtr, IN UINT8 Pass ) { _16BYTE_ALIGN UINT8 PatternBuffer[3 * 64]; UINT8 TestBuffer[120]; UINT8 *PatternBufPtr[4]; UINT8 *TempPtr; UINT32 TestAddrRJ16[4]; UINT32 TempAddrRJ16; UINT32 RealAddr; UINT16 CurTest[4]; UINT8 Dct; UINT8 Receiver; UINT8 i; UINT8 TimesFail; UINT8 TimesRetrain; UINT16 RcvrEnDly; UINT16 MaxRcvrEnDly; UINT16 RcvrEnDlyLimit; UINT16 MaxDelayCha; BOOLEAN IsDualRank; BOOLEAN S0En; BOOLEAN S1En; UINT8 MaxFilterDly; MEM_DATA_STRUCT *MemPtr; DIE_STRUCT *MCTPtr; DCT_STRUCT *DCTPtr; MEM_NB_BLOCK *NBPtr; NBPtr = TechPtr->NBPtr; MemPtr = NBPtr->MemPtr; MCTPtr = NBPtr->MCTPtr; TempAddrRJ16 = 0; TempPtr = NULL; MaxDelayCha = 0; MaxFilterDly = TechPtr->MaxFilterDly; RcvrEnDlyLimit = NBPtr->RcvrEnDlyLimit; TimesRetrain = DEFAULT_TRAINING_TIMES; IDS_OPTION_HOOK (IDS_MEM_RETRAIN_TIMES, &TimesRetrain, &MemPtr->StdHeader); IDS_HDT_CONSOLE ("!\nStart SW RxEn training\n"); // Set environment settings before training MemTBeginTraining (TechPtr); PatternBufPtr[0] = PatternBufPtr[2] = PatternBuffer; MemUFillTrainPattern (TestPattern0, PatternBufPtr[0], 64); PatternBufPtr[1] = PatternBufPtr[3] = PatternBufPtr[0] + 128; MemUFillTrainPattern (TestPattern1, PatternBufPtr[1], 64); // Begin receiver enable training AGESA_TESTPOINT (TpProcMemReceiverEnableTraining, &(MemPtr->StdHeader)); MaxRcvrEnDly = 0; for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { IDS_HDT_CONSOLE ("!\tDct %d\n", Dct); NBPtr->SwitchDCT (NBPtr, Dct); DCTPtr = NBPtr->DCTPtr; // Set training bit NBPtr->SetBitField (NBPtr, BFDqsRcvEnTrain, 1); // Relax Max Latency before training NBPtr->SetMaxLatency (NBPtr, 0xFFFF); if (Pass == FIRST_PASS) { TechPtr->InitDQSPos4RcvrEn (TechPtr); } // there are four receiver pairs, loosely associated with chipselects. Receiver = DCTPtr->Timings.CsEnabled ? 0 : 8; for (; Receiver < 8; Receiver += 2) { TechPtr->DqsRcvEnSaved = 0; RcvrEnDly = RcvrEnDlyLimit; S0En = NBPtr->GetSysAddr (NBPtr, Receiver, &TestAddrRJ16[0]); S1En = NBPtr->GetSysAddr (NBPtr, Receiver + 1, &TestAddrRJ16[2]); if (S0En) { TestAddrRJ16[1] = TestAddrRJ16[0] + BIGPAGE_X8_RJ16; } if (S1En) { TestAddrRJ16[3] = TestAddrRJ16[2] + BIGPAGE_X8_RJ16; } if (S0En && S1En) { IsDualRank = TRUE; } else { IsDualRank = FALSE; } if (S0En || S1En) { IDS_HDT_CONSOLE ("!\t\tCS %d\n", Receiver); // Write the test patterns AGESA_TESTPOINT (TpProcMemRcvrWritePattern, &(MemPtr->StdHeader)); IDS_HDT_CONSOLE ("\t\t\tWrite to addresses: "); for (i = (S0En ? 0 : 2); i < (S1En ? 4 : 2); i++) { RealAddr = MemUSetUpperFSbase (TestAddrRJ16[i], MemPtr); MemUWriteCachelines (RealAddr, PatternBufPtr[i], 1); IDS_HDT_CONSOLE (" %04lx0000 ", TestAddrRJ16[i]); } IDS_HDT_CONSOLE ("\n"); // Initialize RcvrEnDly value and other DCT stored values // MCTPtr->DqsRcvEnPass = Pass ? 0xFF : 0; // Sweep receiver enable delays AGESA_TESTPOINT (TpProcMemRcvrStartSweep, &(MemPtr->StdHeader)); TimesFail = 0; ERROR_HANDLE_RETRAIN_BEGIN (TimesFail, TimesRetrain) { for (RcvrEnDly = 0; RcvrEnDly < RcvrEnDlyLimit; RcvrEnDly++) { AGESA_TESTPOINT (TpProcMemRcvrSetDelay, &(MemPtr->StdHeader)); TechPtr->SetRcvrEnDly (TechPtr, Receiver, RcvrEnDly); IDS_HDT_CONSOLE ("\t\t\tDly %3x", RcvrEnDly); // Read and compare the first beat of data for (i = (S0En ? 0 : 2); i < (S1En ? 4 : 2); i++) { AGESA_TESTPOINT (TpProcMemRcvrReadPattern, &(MemPtr->StdHeader)); RealAddr = MemUSetUpperFSbase (TestAddrRJ16[i], MemPtr); MemUReadCachelines (TestBuffer, RealAddr, 1); AGESA_TESTPOINT (TpProcMemRcvrTestPattern, &(MemPtr->StdHeader)); CurTest[i] = TechPtr->Compare1ClPattern (TechPtr, TestBuffer, PatternBufPtr[i]); // Due to speculative execution during MemUReadCachelines, we must // flush one more cache line than we read. MemUProcIOClFlush (TestAddrRJ16[i], 2, MemPtr); TechPtr->ResetDCTWrPtr (TechPtr, Receiver); // // Swap the test pointers such that even and odd steps alternate. // if ((i % 2) == 0) { TempPtr = PatternBufPtr[i]; PatternBufPtr[i] = PatternBufPtr[i + 1]; TempAddrRJ16 = TestAddrRJ16[i]; TestAddrRJ16[i] = TestAddrRJ16[i + 1]; } else { PatternBufPtr[i] = TempPtr; TestAddrRJ16[i] = TempAddrRJ16; } } if (TechPtr->SaveRcvrEnDly (TechPtr, Receiver, RcvrEnDly, S0En ? (CurTest[0] & CurTest[1]) : 0xFFFF, S1En ? (CurTest[2] & CurTest[3]) : 0xFFFF)) { // if all bytelanes pass if (MaxRcvrEnDly < (RcvrEnDly - MaxFilterDly)) { MaxRcvrEnDly = RcvrEnDly - MaxFilterDly; } break; } } // End of delay sweep ERROR_HANDLE_RETRAIN_END ((RcvrEnDly > (RcvrEnDlyLimit - 1)), TimesFail) } if (RcvrEnDly == RcvrEnDlyLimit) { // no passing window PutEventLog (AGESA_ERROR, MEM_ERROR_RCVR_EN_NO_PASSING_WINDOW_EQUAL_LIMIT, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader); SetMemError (AGESA_ERROR, MCTPtr); } if (RcvrEnDly > (RcvrEnDlyLimit - 1)) { // passing window too narrow, too far delayed PutEventLog (AGESA_ERROR, MEM_ERROR_RCVR_EN_VALUE_TOO_LARGE_LIMIT_LESS_ONE, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader); SetMemError (AGESA_ERROR, MCTPtr); DCTPtr->Timings.CsTrainFail |= DCTPtr->Timings.CsPresent & (UINT16) (3 << Receiver); MCTPtr->ChannelTrainFail |= (UINT32)1 << Dct; if (!NBPtr->MemPtr->ErrorHandling (MCTPtr, NBPtr->Dct, DCTPtr->Timings.CsTrainFail, &NBPtr->MemPtr->StdHeader)) { return FALSE; } } } TechPtr->LoadRcvrEnDly (TechPtr, Receiver); // set final delays } // End while Receiver < 8 // Clear training bit when done NBPtr->SetBitField (NBPtr, BFDqsRcvEnTrain, 0); // Set Max Latency for both channels MaxRcvrEnDly += 0x20; // @attention - IDS_HDT_CONSOLE ("\t\tMaxRcvrEnDly: %03x\n", MaxRcvrEnDly); if (MCTPtr->GangedMode) { if (Dct == 0) { MaxDelayCha = MaxRcvrEnDly; } else if (MaxRcvrEnDly > MaxDelayCha) { NBPtr->SwitchDCT (NBPtr, 0); NBPtr->SetMaxLatency (NBPtr, MaxRcvrEnDly); } } else { NBPtr->SetMaxLatency (NBPtr, MaxRcvrEnDly); } TechPtr->ResetDCTWrPtr (TechPtr, 6); }
VOID MemRecTDramInitSw3 ( IN OUT MEM_TECH_BLOCK *TechPtr ) { UINT8 ChipSel; MEM_DATA_STRUCT *MemPtr; MEM_NB_BLOCK *NBPtr; NBPtr = TechPtr->NBPtr; MemPtr = NBPtr->MemPtr; IDS_HDT_CONSOLE (MEM_STATUS, "\nStart Dram Init\n"); IDS_HDT_CONSOLE (MEM_FLOW, "\tEnDramInit = 1 for DCT%d\n", NBPtr->Dct); // 3.Program F2x[1,0]7C[EnDramInit]=1 NBPtr->SetBitField (NBPtr, BFEnDramInit, 1); // 4.wait 200us MemRecUWait10ns (20000, MemPtr); NBPtr->SetBitField (NBPtr, BFDeassertMemRstX, 1); // 6.wait 500us MemRecUWait10ns (50000, MemPtr); // 7.NOP or deselect & take CKE high NBPtr->SetBitField (NBPtr, BFAssertCke, 1); // 8.wait 360ns MemRecUWait10ns (36, MemPtr); // The following steps are performed with registered DIMMs only and // must be done for each chip select pair: // if (NBPtr->ChannelPtr->RegDimmPresent != 0) { MemRecTDramControlRegInit3 (TechPtr); } for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel++) { if ((NBPtr->DCTPtr->Timings.CsPresent & (UINT16) 1 << ChipSel) != 0) { // Set Dram ODT per ChipSel NBPtr->SetDramOdtRec (NBPtr, MISSION_MODE, ChipSel, (NBPtr->DimmToBeUsed << 1)); NBPtr->SetBitField (NBPtr, BFMrsChipSel, ChipSel); // 13.Send EMRS(2) MemRecTEMRS23 (TechPtr); NBPtr->SendMrsCmd (NBPtr); // 14.Send EMRS(3). Ordinarily at this time, MrsAddress[2:0]=000b MemRecTEMRS33 (TechPtr); NBPtr->SendMrsCmd (NBPtr); // 15.Send EMRS(1). MemRecTEMRS13 (TechPtr); NBPtr->SendMrsCmd (NBPtr); // 16.Send MRS with MrsAddress[8]=1(reset the DLL) MemRecTMRS3 (TechPtr); NBPtr->SendMrsCmd (NBPtr); //wait 500us MemRecUWait10ns (50000, MemPtr); if (NBPtr->ChannelPtr->RegDimmPresent == 0) { break; } } } // 17.Send two ZQCL commands (to even then odd chip select) NBPtr->sendZQCmd (NBPtr); NBPtr->sendZQCmd (NBPtr); // 18.Program F2x[1,0]7C[EnDramInit]=0 NBPtr->SetBitField (NBPtr, BFEnDramInit, 0); IDS_HDT_CONSOLE (MEM_FLOW, "End Dram Init\n\n"); }
AGESA_STATUS AmdMemRecovery ( IN OUT MEM_DATA_STRUCT *MemPtr ) { UINT8 Socket; UINT8 Module; UINT8 i; AGESA_STATUS AgesaStatus; PCI_ADDR Address; MEM_NB_BLOCK NBBlock; MEM_TECH_BLOCK TechBlock; LOCATE_HEAP_PTR SocketWithMem; ALLOCATE_HEAP_PARAMS AllocHeapParams; // // Read SPD data // MemRecSPDDataProcess (MemPtr); // // Get the socket id from heap. // SocketWithMem.BufferHandle = AMD_REC_MEM_SOCKET_HANDLE; if (HeapLocateBuffer (&SocketWithMem, &MemPtr->StdHeader) == AGESA_SUCCESS) { Socket = *(UINT8 *) SocketWithMem.BufferPtr; } else { ASSERT(FALSE); // Socket handle not found return AGESA_FATAL; } // // Allocate buffer for memory init structures // AllocHeapParams.RequestedBufferSize = MAX_DIES_PER_SOCKET * sizeof (DIE_STRUCT); AllocHeapParams.BufferHandle = GENERATE_MEM_HANDLE (ALLOC_DIE_STRUCT_HANDLE, 0, 0, 0); AllocHeapParams.Persist = HEAP_LOCAL_CACHE; if (HeapAllocateBuffer (&AllocHeapParams, &MemPtr->StdHeader) != AGESA_SUCCESS) { ASSERT(FALSE); // Heap allocation failed to allocate Die struct return AGESA_FATAL; } MemPtr->DiesPerSystem = (DIE_STRUCT *)AllocHeapParams.BufferPtr; // // Discover populated CPUs // for (Module = 0; Module < MAX_DIES_PER_SOCKET; Module++) { if (GetPciAddress ((VOID *)MemPtr, Socket, Module, &Address, &AgesaStatus)) { MemPtr->DiesPerSystem[Module].SocketId = Socket; MemPtr->DiesPerSystem[Module].DieId = Module; MemPtr->DiesPerSystem[Module].PciAddr.AddressValue = Address.AddressValue; } } i = 0; while (MemRecNBInstalled[i] != NULL) { if (MemRecNBInstalled[i] (&NBBlock, MemPtr, 0) == TRUE) { break; } i++; }; if (MemRecNBInstalled[i] == NULL) { ASSERT(FALSE); // No NB installed return AGESA_FATAL; } MemRecTechInstalled[0] (&TechBlock, &NBBlock); NBBlock.TechPtr = &TechBlock; return NBBlock.InitRecovery (&NBBlock); }
/** * * This function executes receiver enable training for a specific die * * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK * @param[in] Pass - Pass of the receiver training * * @return TRUE - No fatal error occurs. * @return FALSE - Fatal error occurs. */ BOOLEAN STATIC MemTDqsTrainOptRcvrEnSw ( IN OUT MEM_TECH_BLOCK *TechPtr, IN UINT8 Pass ) { _16BYTE_ALIGN UINT8 PatternBuffer[6 * 64]; UINT8 TestBuffer[256]; UINT8 *PatternBufPtr[6]; UINT8 *TempPtr; UINT32 TestAddrRJ16[4]; UINT32 TempAddrRJ16; UINT32 RealAddr; UINT16 CurTest[4]; UINT8 Dct; UINT8 Receiver; UINT8 i; UINT8 TimesFail; UINT8 TimesRetrain; UINT16 RcvrEnDly; UINT16 MaxRcvrEnDly; UINT16 RcvrEnDlyLimit; UINT16 MaxDelayCha; BOOLEAN IsDualRank; BOOLEAN S0En; BOOLEAN S1En; MEM_DATA_STRUCT *MemPtr; DIE_STRUCT *MCTPtr; DCT_STRUCT *DCTPtr; MEM_NB_BLOCK *NBPtr; NBPtr = TechPtr->NBPtr; MemPtr = NBPtr->MemPtr; MCTPtr = NBPtr->MCTPtr; TechPtr->TrainingType = TRN_RCVR_ENABLE; TempAddrRJ16 = 0; TempPtr = NULL; MaxDelayCha = 0; TimesRetrain = DEFAULT_TRAINING_TIMES; IDS_OPTION_HOOK (IDS_MEM_RETRAIN_TIMES, &TimesRetrain, &MemPtr->StdHeader); IDS_HDT_CONSOLE (MEM_STATUS, "\nStart Optimized SW RxEn training\n"); // Set environment settings before training MemTBeginTraining (TechPtr); PatternBufPtr[0] = PatternBufPtr[2] = PatternBuffer; // These two patterns used for first Test Address MemUFillTrainPattern (TestPattern0, PatternBufPtr[0], 64); // Second Cacheline used for Dummy Read is the inverse of // the first so that is is not mistaken for the real read MemUFillTrainPattern (TestPattern1, PatternBufPtr[0] + 64, 64); PatternBufPtr[1] = PatternBufPtr[3] = PatternBufPtr[0] + 128; // These two patterns used for second Test Address MemUFillTrainPattern (TestPattern1, PatternBufPtr[1], 64); // Second Cacheline used for Dummy Read is the inverse of // the first so that is is not mistaken for the real read MemUFillTrainPattern (TestPattern0, PatternBufPtr[1] + 64, 64); // Fill pattern for flush after every sweep PatternBufPtr[4] = PatternBufPtr[0] + 256; MemUFillTrainPattern (TestPattern3, PatternBufPtr[4], 64); // Fill pattern for initial dummy read PatternBufPtr[5] = PatternBufPtr[0] + 320; MemUFillTrainPattern (TestPattern4, PatternBufPtr[5], 64); // Begin receiver enable training AGESA_TESTPOINT (TpProcMemReceiverEnableTraining, &(MemPtr->StdHeader)); for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct); NBPtr->SwitchDCT (NBPtr, Dct); DCTPtr = NBPtr->DCTPtr; // Set training bit NBPtr->SetBitField (NBPtr, BFDqsRcvEnTrain, 1); // Relax Max Latency before training NBPtr->SetMaxLatency (NBPtr, 0xFFFF); if (Pass == FIRST_PASS) { TechPtr->InitDQSPos4RcvrEn (TechPtr); } // there are four receiver pairs, loosely associated with chipselects. Receiver = DCTPtr->Timings.CsEnabled ? 0 : 8; for (; Receiver < 8; Receiver += 2) { S0En = NBPtr->GetSysAddr (NBPtr, Receiver, &TestAddrRJ16[0]); S1En = NBPtr->GetSysAddr (NBPtr, Receiver + 1, &TestAddrRJ16[2]); if (S0En) { TestAddrRJ16[1] = TestAddrRJ16[0] + BIGPAGE_X8_RJ16; } if (S1En) { TestAddrRJ16[3] = TestAddrRJ16[2] + BIGPAGE_X8_RJ16; } if (S0En && S1En) { IsDualRank = TRUE; } else { IsDualRank = FALSE; } if (S0En || S1En) { IDS_HDT_CONSOLE (MEM_STATUS, "\t\tCS %d\n", Receiver); RcvrEnDlyLimit = 0x1FF; // @attention - limit depends on proc type TechPtr->DqsRcvEnSaved = 0; RcvrEnDly = RcvrEnDlyLimit; RealAddr = 0; TechPtr->GetFirstPassVal = FALSE; TechPtr->DqsRcvEnFirstPassVal = 0; TechPtr->RevertPassVal = FALSE; TechPtr->InitializeVariablesOpt (TechPtr); // Write the test patterns AGESA_TESTPOINT (TpProcMemRcvrWritePattern, &(MemPtr->StdHeader)); IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tWrite to addresses: "); for (i = (S0En ? 0 : 2); i < (S1En ? 4 : 2); i++) { RealAddr = MemUSetUpperFSbase (TestAddrRJ16[i], MemPtr); // One cacheline of data to be tested and one of dummy data MemUWriteCachelines (RealAddr, PatternBufPtr[i], 2); // This is dummy data with a different pattern used for the first dummy read. MemUWriteCachelines (RealAddr + 128, PatternBufPtr[5], 1); IDS_HDT_CONSOLE (MEM_FLOW, " %04x0000 ", TestAddrRJ16[i]); } IDS_HDT_CONSOLE (MEM_FLOW, "\n"); // Sweep receiver enable delays AGESA_TESTPOINT (TpProcMemRcvrStartSweep, &(MemPtr->StdHeader)); TimesFail = 0; ERROR_HANDLE_RETRAIN_BEGIN (TimesFail, TimesRetrain) { TechPtr->LoadInitialRcvrEnDlyOpt (TechPtr, Receiver); while (!TechPtr->CheckRcvrEnDlyLimitOpt (TechPtr)) { AGESA_TESTPOINT (TpProcMemRcvrSetDelay, &(MemPtr->StdHeader)); TechPtr->SetRcvrEnDlyOpt (TechPtr, Receiver, RcvrEnDly); // Read and compare the first beat of data for (i = (S0En ? 0 : 2); i < (S1En ? 4 : 2); i++) { AGESA_TESTPOINT (TpProcMemRcvrReadPattern, &(MemPtr->StdHeader)); RealAddr = MemUSetUpperFSbase (TestAddrRJ16[i], MemPtr); // // Issue dummy cacheline reads // MemUReadCachelines (TestBuffer + 128, RealAddr + 128, 1); MemUReadCachelines (TestBuffer, RealAddr, 1); MemUProcIOClFlush (TestAddrRJ16[i], 2, MemPtr); // // Perform actual read which will be compared // MemUReadCachelines (TestBuffer + 64, RealAddr + 64, 1); AGESA_TESTPOINT (TpProcMemRcvrTestPattern, &(MemPtr->StdHeader)); CurTest[i] = TechPtr->Compare1ClPatternOpt (TechPtr, TestBuffer + 64 , PatternBufPtr[i] + 64, i, Receiver, S1En); // Due to speculative execution during MemUReadCachelines, we must // flush one more cache line than we read. MemUProcIOClFlush (TestAddrRJ16[i], 4, MemPtr); TechPtr->ResetDCTWrPtr (TechPtr, Receiver); // // Swap the test pointers such that even and odd steps alternate. // if ((i % 2) == 0) { TempPtr = PatternBufPtr[i]; PatternBufPtr[i] = PatternBufPtr[i + 1]; TempAddrRJ16 = TestAddrRJ16[i]; TestAddrRJ16[i] = TestAddrRJ16[i + 1]; } else { PatternBufPtr[i] = TempPtr; TestAddrRJ16[i] = TempAddrRJ16; } } } // End of delay sweep ERROR_HANDLE_RETRAIN_END (!TechPtr->SetSweepErrorOpt (TechPtr, Receiver, Dct, TRUE), TimesFail) } if (!TechPtr->SetSweepErrorOpt (TechPtr, Receiver, Dct, FALSE)) { return FALSE; } TechPtr->LoadRcvrEnDlyOpt (TechPtr, Receiver); // set final delays // // Flush AA and 55 patterns by reading a dummy pattern to fill in FIFO // // Aquire a new FSBase, based on the last test address that we stored. RealAddr = MemUSetUpperFSbase (TempAddrRJ16, MemPtr); ASSERT (RealAddr != 0); MemUWriteCachelines (RealAddr, PatternBufPtr[4], 1); MemUWriteCachelines (RealAddr + 64, PatternBufPtr[4], 1); MemUReadCachelines (TestBuffer, RealAddr, 2); // Due to speculative execution during MemUReadCachelines, we must // flush one more cache line than we read. MemUProcIOClFlush (TempAddrRJ16, 3, MemPtr); } } // End while Receiver < 8
BOOLEAN MemTTrainMaxLatency ( IN OUT MEM_TECH_BLOCK *TechPtr ) { UINT32 TestAddrRJ16; UINT8 Dct; UINT8 ChipSel; UINT8 *PatternBufPtr; UINT8 *TestBufferPtr; UINT8 CurrentNbPstate; UINT16 CalcMaxLatDly; UINT16 MaxLatDly; UINT16 MaxLatLimit; UINT16 Margin; UINT16 CurTest; UINT16 _CL_; UINT8 TimesFail; UINT8 TimesRetrain; UINT16 i; MEM_DATA_STRUCT *MemPtr; DIE_STRUCT *MCTPtr; MEM_NB_BLOCK *NBPtr; NBPtr = TechPtr->NBPtr; MCTPtr = NBPtr->MCTPtr; MemPtr = NBPtr->MemPtr; TechPtr->TrainingType = TRN_MAX_READ_LATENCY; TimesRetrain = DEFAULT_TRAINING_TIMES; IDS_OPTION_HOOK (IDS_MEM_RETRAIN_TIMES, &TimesRetrain, &MemPtr->StdHeader); IDS_HDT_CONSOLE (MEM_STATUS, "\nStart MaxRdLat training\n"); // Set environment settings before training AGESA_TESTPOINT (TpProcMemMaxRdLatencyTraining, &(MemPtr->StdHeader)); MemTBeginTraining (TechPtr); // // Initialize the Training Pattern // if (AGESA_SUCCESS != NBPtr->TrainingPatternInit (NBPtr)) { return (BOOLEAN) (MCTPtr->ErrCode < AGESA_FATAL); } TechPtr->PatternLength = (MCTPtr->Status[Sb128bitmode]) ? 6 : 3; // // Setup hardware training engine (if applicable) // NBPtr->FamilySpecificHook[SetupHwTrainingEngine] (NBPtr, &TechPtr->TrainingType); MaxLatDly = 0; _CL_ = TechPtr->PatternLength; PatternBufPtr = TechPtr->PatternBufPtr; TestBufferPtr = TechPtr->TestBufPtr; // // Begin max latency training // for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { if (MCTPtr->Status[Sb128bitmode] && (Dct != 0)) { break; } IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct); NBPtr->SwitchDCT (NBPtr, Dct); if (NBPtr->DCTPtr->Timings.DctMemSize != 0) { if (TechPtr->FindMaxDlyForMaxRdLat (TechPtr, &ChipSel)) { TechPtr->ChipSel = ChipSel; if (NBPtr->GetSysAddr (NBPtr, ChipSel, &TestAddrRJ16)) { IDS_HDT_CONSOLE (MEM_STATUS, "\t\tCS %d\n", ChipSel); IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tWrite to address: %04x0000\n", TestAddrRJ16); // Write the test patterns AGESA_TESTPOINT (TpProcMemMaxRdLatWritePattern, &(MemPtr->StdHeader)); NBPtr->WritePattern (NBPtr, TestAddrRJ16, PatternBufPtr, _CL_); // Sweep max latency delays NBPtr->getMaxLatParams (NBPtr, TechPtr->MaxDlyForMaxRdLat, &CalcMaxLatDly, &MaxLatLimit, &Margin); AGESA_TESTPOINT (TpProcMemMaxRdLatStartSweep, &(MemPtr->StdHeader)); TimesFail = 0; ERROR_HANDLE_RETRAIN_BEGIN (TimesFail, TimesRetrain) { MaxLatDly = CalcMaxLatDly; for (i = 0; i < (MaxLatLimit - CalcMaxLatDly); i++) { NBPtr->SetBitField (NBPtr, BFMaxLatency, MaxLatDly); IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tDly %3x", MaxLatDly); TechPtr->ResetDCTWrPtr (TechPtr, 6); AGESA_TESTPOINT (TpProcMemMaxRdLatReadPattern, &(MemPtr->StdHeader)); NBPtr->ReadPattern (NBPtr, TestBufferPtr, TestAddrRJ16, _CL_); AGESA_TESTPOINT (TpProcMemMaxRdLatTestPattern, &(MemPtr->StdHeader)); CurTest = NBPtr->CompareTestPattern (NBPtr, TestBufferPtr, PatternBufPtr, _CL_ * 64); NBPtr->FlushPattern (NBPtr, TestAddrRJ16, _CL_); if (NBPtr->IsSupported[ReverseMaxRdLatTrain]) { // Reverse training decrements MaxLatDly whenever the test passes // and uses the last passing MaxLatDly as left edge if (CurTest == 0xFFFF) { IDS_HDT_CONSOLE (MEM_FLOW, " P"); if (MaxLatDly == 0) { break; } else { MaxLatDly--; } } } else { // Traditional training increments MaxLatDly until the test passes // and uses it as left edge if (CurTest == 0xFFFF) { IDS_HDT_CONSOLE (MEM_FLOW, " P"); break; } else { MaxLatDly++; } } IDS_HDT_CONSOLE (MEM_FLOW, "\n"); } // End of delay sweep ERROR_HANDLE_RETRAIN_END ((MaxLatDly >= MaxLatLimit), TimesFail) } AGESA_TESTPOINT (TpProcMemMaxRdLatSetDelay, &(MemPtr->StdHeader)); if (MaxLatDly >= MaxLatLimit) { PutEventLog (AGESA_ERROR, MEM_ERROR_MAX_LAT_NO_WINDOW, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader); SetMemError (AGESA_ERROR, MCTPtr); NBPtr->DCTPtr->Timings.CsTrainFail |= NBPtr->DCTPtr->Timings.CsPresent; MCTPtr->ChannelTrainFail |= (UINT32)1 << Dct; if (!NBPtr->MemPtr->ErrorHandling (MCTPtr, NBPtr->Dct, EXCLUDE_ALL_CHIPSEL, &NBPtr->MemPtr->StdHeader)) { ASSERT (FALSE); return FALSE; } } else { NBPtr->FamilySpecificHook[AddlMaxRdLatTrain] (NBPtr, &TestAddrRJ16); MaxLatDly = MaxLatDly + Margin; if (NBPtr->IsSupported[ReverseMaxRdLatTrain]) { MaxLatDly++; // Add 1 to get back to the last passing value } // Set final delays CurrentNbPstate = (UINT8) MemNGetBitFieldNb (NBPtr, BFCurNbPstate); ASSERT (CurrentNbPstate <= 3); NBPtr->ChannelPtr->DctMaxRdLat [CurrentNbPstate] = MaxLatDly; NBPtr->SetBitField (NBPtr, BFMaxLatency, MaxLatDly); IDS_HDT_CONSOLE (MEM_FLOW, "\t\tFinal MaxRdLat: %03x\n", MaxLatDly); } } }