| /* $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; |
| } |
| } |