/* $NoKeywords:$ */ /** * @file * * mrttsrc.c * * Technology Software based DQS receiver enable training Recovery * * @xrefitem bom "File Content Label" "Release Content" * @e project: AGESA * @e sub-project: (Proc/Recovery/Mem) * @e \$Revision: 52180 $ @e \$Date: 2011-05-03 05:17:25 -0600 (Tue, 03 May 2011) $ * **/ /***************************************************************************** * * Copyright (C) 2012 Advanced Micro Devices, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Advanced Micro Devices, Inc. nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * *************************************************************************** * */ /* *---------------------------------------------------------------------------- * MODULES USED * *---------------------------------------------------------------------------- */ #include "AGESA.h" #include "OptionMemory.h" #include "amdlib.h" #include "Ids.h" #include "mm.h" #include "mn.h" #include "mru.h" #include "mt.h" #include "Filecode.h" CODE_GROUP (G2_PEI) RDATA_GROUP (G2_PEI) #define FILECODE PROC_RECOVERY_MEM_TECH_MRTTSRC_FILECODE /*---------------------------------------------------------------------------- * DEFINITIONS AND MACROS * *---------------------------------------------------------------------------- */ #define MAX_BYTELANES 8 /* Max Bytelanes per channel */ /*---------------------------------------------------------------------------- * TYPEDEFS AND STRUCTURES * *---------------------------------------------------------------------------- */ /*---------------------------------------------------------------------------- * PROTOTYPES OF LOCAL FUNCTIONS * *---------------------------------------------------------------------------- */ VOID STATIC MemRecTSetRcvrEnDly ( IN OUT MEM_TECH_BLOCK *TechPtr, IN UINT8 Receiver, IN UINT16 RcvEnDly ); VOID STATIC MemRecTLoadRcvrEnDly ( IN OUT MEM_TECH_BLOCK *TechPtr, IN UINT8 Receiver ); BOOLEAN STATIC MemRecTSaveRcvrEnDly ( IN OUT MEM_TECH_BLOCK *TechPtr, IN UINT8 Receiver, IN UINT16 RcvEnDly, IN UINT8 CmpResult ); UINT8 STATIC MemRecTCompare1ClPattern ( IN UINT8 Buffer[], IN UINT8 Pattern[], IN OUT AMD_CONFIG_PARAMS *StdHeader ); /*---------------------------------------------------------------------------- * EXPORTED FUNCTIONS * *---------------------------------------------------------------------------- */ /* -----------------------------------------------------------------------------*/ /** * * This function executes receiver enable training for BSP * * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK * */ VOID MemRecTTrainRcvrEnSw ( IN OUT MEM_TECH_BLOCK *TechPtr ) { _16BYTE_ALIGN UINT8 PatternBuffer[3 * 64]; UINT8 TestBuffer[120]; UINT8 *PatternBufPtr[2]; UINT32 TestAddr[4]; UINT8 TestResult; UINT8 Receiver; UINT8 i; UINT8 j; UINT16 RcvrEnDly; MEM_DATA_STRUCT *MemPtr; DIE_STRUCT *MCTPtr; MEM_NB_BLOCK *NBPtr; NBPtr = TechPtr->NBPtr; MemPtr = NBPtr->MemPtr; MCTPtr = NBPtr->MCTPtr; AGESA_TESTPOINT (TpProcMemReceiverEnableTraining, &(MemPtr->StdHeader)); // Set environment settings before training MemRecTBeginTraining (TechPtr); PatternBufPtr[0] = PatternBuffer; MemRecUFillTrainPattern (TestPattern0, PatternBufPtr[0], 64, &(MemPtr->StdHeader)); PatternBufPtr[1] = PatternBufPtr[0] + 128; MemRecUFillTrainPattern (TestPattern1, PatternBufPtr[1], 64, &(MemPtr->StdHeader)); // Begin receiver enable training MemRecTSetWrDatRdDqs (TechPtr, 0); // there are four receiver pairs, loosely associated with chipselects. Receiver = NBPtr->DimmToBeUsed << 1; TechPtr->DqsRcvEnSaved = 0; TestAddr[0] = NBPtr->GetSysAddrRec (); TestAddr[1] = TestAddr[0] + BIGPAGE_X8; IDS_HDT_CONSOLE (MEM_FLOW, "\tDct %d\n", NBPtr->Dct); IDS_HDT_CONSOLE (MEM_FLOW, "\t\tCS %d\n", Receiver); IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tWrite to addresses: %04x0000\n", TestAddr[0]); // Sweep receiver enable delays AGESA_TESTPOINT (TpProcMemRcvrStartSweep, &(MemPtr->StdHeader)); for (RcvrEnDly = 0; RcvrEnDly < 0xFF; RcvrEnDly++) { TestResult = 0xFF; for (i = 0; i < 2; i++) { // Set RcvrEn delay for all byte lanes AGESA_TESTPOINT (TpProcMemRcvrSetDelay, &(MemPtr->StdHeader)); MemRecTSetRcvrEnDly (TechPtr, Receiver, RcvrEnDly); IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tDly %3x", RcvrEnDly); // Swap the test pointers such that even and odd steps alternate. j = ((RcvrEnDly & 1) != 0) ? (i ^ 1) : i; // Write, read and compare the first beat of data AGESA_TESTPOINT (TpProcMemRcvrWritePattern, &(MemPtr->StdHeader)); MemRecUWrite1CL (TestAddr[j], PatternBufPtr[j]); AGESA_TESTPOINT (TpProcMemRcvrReadPattern, &(MemPtr->StdHeader)); MemRecURead1CL (TestBuffer, TestAddr[j]); AGESA_TESTPOINT (TpProcMemRcvrTestPattern, &(MemPtr->StdHeader)); TestResult &= MemRecTCompare1ClPattern (TestBuffer, PatternBufPtr[j], &(MemPtr->StdHeader)); MemRecUProcIOClFlush (TestAddr[j], MemPtr); } if (MemRecTSaveRcvrEnDly (TechPtr, Receiver, RcvrEnDly, TestResult)) { // if all bytelanes pass break; } } // End of delay sweep if (RcvrEnDly == 0xFF) { // no passing window // LibAmdEventLog (AGESA_ERROR, MEM_ERROR_RCVR_EN_NO_PASSING_WINDOW, 0, NBPtr->Dct, NBPtr->Channel, 0); //@attention commented out since it is not defined in recovery code SetMemRecError (AGESA_ERROR, MCTPtr); } // set final delays MemRecTLoadRcvrEnDly (TechPtr, Receiver); // Clear training bit when done NBPtr->SetBitField (NBPtr, BFDqsRcvEnTrain, 0); IDS_HDT_CONSOLE (MEM_FLOW, "\t\tMaxRcvrEnDly: %03x\n", RcvrEnDly + 0x20); // Set Max Latency for both channels NBPtr->SetMaxLatency (NBPtr, RcvrEnDly + 0x20); // Restore environment settings after training MemRecTEndTraining (TechPtr); } /* -----------------------------------------------------------------------------*/ /** * * If WrDatDly is 0, this function sets the DQS Positions in preparation * for Receiver Enable Training. (Write Position is no delay, Read Position is 1.5 Memclock delay). * Otherwise it will set WrDat and RdDqs to center of data eye. * * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK * @param[in] WrDatDly - either 0 or 0x0F * */ VOID MemRecTSetWrDatRdDqs ( IN OUT MEM_TECH_BLOCK *TechPtr, IN UINT8 WrDatDly ) { UINT8 ByteLane; UINT8 Dimm; UINT8 WrDqs; UINT8 RdDqs; MEM_NB_BLOCK *NBPtr; NBPtr = TechPtr->NBPtr; Dimm = NBPtr->DimmToBeUsed; IDS_HDT_CONSOLE (MEM_FLOW, "\nWrDat RdDqs\n"); for (ByteLane = 0; ByteLane < 8; ByteLane++) { WrDqs = NBPtr->ChannelPtr->WrDqsDlys[(Dimm * MAX_BYTELANES) + ByteLane]; NBPtr->SetTrainDly (NBPtr, AccessWrDatDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), WrDqs + WrDatDly); RdDqs = (WrDatDly == 0) ? 0x2F : 0x012; NBPtr->SetTrainDly (NBPtr, AccessRdDqsDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), RdDqs); IDS_HDT_CONSOLE (MEM_FLOW, " %02x %02x\n\n", WrDqs + WrDatDly, RdDqs); } } /* -----------------------------------------------------------------------------*/ /** * * This function programs DqsRcvEnDly to additional index for DQS receiver enabled training * * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK * @param[in] Receiver - Current Chip select value * @param[in] RcvEnDly - receiver enable delay to be saved * */ VOID STATIC MemRecTSetRcvrEnDly ( IN OUT MEM_TECH_BLOCK *TechPtr, IN UINT8 Receiver, IN UINT16 RcvEnDly ) { UINT8 ByteLane; ASSERT (Receiver <= MAX_CS_PER_CHANNEL); for (ByteLane = 0; ByteLane < 8; ByteLane++) { TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessRcvEnDly, DIMM_BYTE_ACCESS (Receiver >> 1, ByteLane), RcvEnDly); } } /* -----------------------------------------------------------------------------*/ /** * * This function compares test pattern with data in buffer and return a pass/fail bitmap * for 8 Bytes * * @param[in] Buffer[] - Buffer data from DRAM (Measured data from DRAM) to compare * @param[in] Pattern[] - Pattern (Expected data in ROM/CACHE) to compare against * @param[in,out] StdHeader - The Pointer of AGESA Header. * * @return PASS - Bit map of results of comparison */ UINT8 STATIC MemRecTCompare1ClPattern ( IN UINT8 Buffer[], IN UINT8 Pattern[], IN OUT AMD_CONFIG_PARAMS *StdHeader ) { UINT8 i; UINT8 Pass; Pass = 0xFF; IDS_HDT_CONSOLE (MEM_FLOW, " -"); for (i = 0; i < 8; i++) { if (Buffer[i] != Pattern[i]) { // if bytelane n fails Pass &= ~((UINT16) 1 << (i % 8)); // clear bit n } IDS_HDT_CONSOLE (MEM_FLOW, " %c", (Buffer[i] == Pattern[i]) ? 'P' : '.'); } IDS_HDT_CONSOLE_DEBUG_CODE ( IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t -"); for (i = 0; i < 8; i++) { IDS_HDT_CONSOLE (MEM_FLOW, " %02x", Buffer[i]); } IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t -"); for (i = 0; i < 8; i++) { IDS_HDT_CONSOLE (MEM_FLOW, " %02x", Pattern[i]); } IDS_HDT_CONSOLE (MEM_FLOW, "\n\n"); ); return Pass; } /* -----------------------------------------------------------------------------*/ /** * * This function saves passing DqsRcvEnDly values to the stack * * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK * @param[in] Receiver - Current Chip select value * @param[in] RcvEnDly - receiver enable delay to be saved * @param[in] CmpResult - compare result for Rank 0 * * @return TRUE - All bytelanes pass * @return FALSE - Some bytelanes fail */ BOOLEAN STATIC MemRecTSaveRcvrEnDly ( IN OUT MEM_TECH_BLOCK *TechPtr, IN UINT8 Receiver, IN UINT16 RcvEnDly, IN UINT8 CmpResult ) { UINT8 i; UINT8 Passed; UINT8 Saved; UINT8 Mask; UINT8 Dimm; ASSERT (Receiver <= MAX_CS_PER_CHANNEL); Passed = CmpResult; Saved = (UINT8) (TechPtr->DqsRcvEnSaved & Passed); //@todo - false passes filter (subject to be replaced with a better solution) Dimm = Receiver >> 1; Mask = 1; for (i = 0; i < 8; i++) { if ((Passed & Mask) != 0) { if ((Saved & Mask) == 0) { TechPtr->NBPtr->ChannelPtr->RcvEnDlys[Dimm * MAX_BYTELANES + i] = (UINT8) (RcvEnDly + 0x20); IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tBL %d = %02x", i, RcvEnDly + 0x20); } Saved |= Mask; } Mask <<= 1; } TechPtr->DqsRcvEnSaved = Saved; if (Saved == 0xFF) { return TRUE; } else { return FALSE; } } /* -----------------------------------------------------------------------------*/ /** * * This function loads the DqsRcvEnDly from saved data and program to additional index * for DQS receiver enabled training * * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK * @param[in] Receiver - Current Chip select value * */ VOID STATIC MemRecTLoadRcvrEnDly ( IN OUT MEM_TECH_BLOCK *TechPtr, IN UINT8 Receiver ) { UINT8 i; UINT8 Dimm; UINT16 Saved; CH_DEF_STRUCT *ChannelPtr; ASSERT (Receiver <= MAX_CS_PER_CHANNEL); ChannelPtr = TechPtr->NBPtr->ChannelPtr; Dimm = Receiver >> 1; Saved = TechPtr->DqsRcvEnSaved; for (i = 0; i < 8; i++) { if ((Saved & 1) != 0) { TechPtr->NBPtr->SetTrainDly (TechPtr->NBPtr, AccessRcvEnDly, DIMM_BYTE_ACCESS (Receiver >> 1, i), ChannelPtr->RcvEnDlys[Dimm * MAX_BYTELANES + i]); } Saved >>= 1; } }