/** * @file * * mnphy.c * * Common Northbridge Phy support * * @xrefitem bom "File Content Label" "Release Content" * @e project: AGESA * @e sub-project: (Mem/NB) * @e \$Revision: 6789 $ @e \$Date: 2008-07-17 15:56:25 -0500 (Thu, 17 Jul 2008) $ * **/ /***************************************************************************** * * Copyright (c) 2011, 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 "amdlib.h" #include "Ids.h" #include "mport.h" #include "mm.h" #include "mn.h" #include "mt.h" #include "mu.h" #include "PlatformMemoryConfiguration.h" #include "heapManager.h" #include "merrhdl.h" #include "Filecode.h" #define FILECODE PROC_MEM_NB_MNPHY_FILECODE /*---------------------------------------------------------------------------- * DEFINITIONS AND MACROS * *---------------------------------------------------------------------------- */ #define UNUSED_CLK 4 /*---------------------------------------------------------------------------- * TYPEDEFS AND STRUCTURES * *---------------------------------------------------------------------------- */ /// Type of an entry for processing phy init compensation for client NB typedef struct { BIT_FIELD_NAME IndexBitField; ///< Bit field on which the value is decided BIT_FIELD_NAME StartTargetBitField; ///< First bit field to be modified BIT_FIELD_NAME EndTargetBitField; ///< Last bit field to be modified UINT16 ExtraValue; ///< Extra value needed to be written to bit field CONST UINT16 (*TxPrePN)[3][5]; ///< Pointer to slew rate table } PHY_COMP_INIT_CLIENTNB; /*---------------------------------------------------------------------------- * PROTOTYPES OF LOCAL FUNCTIONS * *---------------------------------------------------------------------------- */ /*---------------------------------------------------------------------------- * EXPORTED FUNCTIONS * *---------------------------------------------------------------------------- */ /* -----------------------------------------------------------------------------*/ /** * * * This function gets a delay value a PCI register during training * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in] TrnDly - type of delay to be set * @param[in] DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed * (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding * * @return Value read */ UINT32 MemNGetTrainDlyNb ( IN OUT MEM_NB_BLOCK *NBPtr, IN TRN_DLY_TYPE TrnDly, IN DRBN DrbnVar ) { return NBPtr->MemNcmnGetSetTrainDly (NBPtr, 0, TrnDly, DrbnVar, 0); } /* -----------------------------------------------------------------------------*/ /** * * * This function sets a delay value a PCI register during training * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in] TrnDly - type of delay to be set * @param[in] DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed * (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding * @param[in] Field - Value to be programmed * */ VOID MemNSetTrainDlyNb ( IN OUT MEM_NB_BLOCK *NBPtr, IN TRN_DLY_TYPE TrnDly, IN DRBN DrbnVar, IN UINT16 Field ) { NBPtr->MemNcmnGetSetTrainDly (NBPtr, 1, TrnDly, DrbnVar, Field); } /* -----------------------------------------------------------------------------*/ /** * * * This function executes prototypical Phy fence training function. * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * */ VOID MemNPhyFenceTrainingNb ( IN OUT MEM_NB_BLOCK *NBPtr ) { NBPtr->MemPPhyFenceTrainingNb (NBPtr); } /* -----------------------------------------------------------------------------*/ /** * * * This function executes prototypical Phy fence training function. * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * */ VOID MemNPhyFenceTrainingClientNb ( IN OUT MEM_NB_BLOCK *NBPtr ) { UINT8 FenceThresholdTxDll; UINT8 FenceThresholdRxDll; UINT8 FenceThresholdTxPad; UINT16 Fence2Data; // 1. Program D18F2x[1,0]9C_x0000_0008[FenceTrSel]=10b. // 2. Perform phy fence training. See 2.10.3.2.3.1 [Phy Fence Training]. // 3. Write the calculated fence value to D18F2x[1,0]9C_x0000_000C[FenceThresholdTxDll]. MemNSetBitFieldNb (NBPtr, BFFenceTrSel, 2); MAKE_TSEFO (NBPtr->NBRegTable, DCT_PHY_ACCESS, 0x0C, 30, 26, BFPhyFence); IDS_HDT_CONSOLE ("\t\tFenceThresholdTxDll\n"); MemNTrainPhyFenceNb (NBPtr); FenceThresholdTxDll = (UINT8) MemNGetBitFieldNb (NBPtr, BFPhyFence); // 4. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]0F[AlwaysEnDllClks]=001b. MemNSetBitFieldNb (NBPtr, BFAlwaysEnDllClks, 0x1000); // 5. Program D18F2x[1,0]9C_x0000_0008[FenceTrSel]=01b. // 6. Perform phy fence training. See 2.10.3.2.3.1 [Phy Fence Training]. // 7. Write the calculated fence value to D18F2x[1,0]9C_x0000_000C[FenceThresholdRxDll]. MemNSetBitFieldNb (NBPtr, BFFenceTrSel, 1); MAKE_TSEFO (NBPtr->NBRegTable, DCT_PHY_ACCESS, 0x0C, 25, 21, BFPhyFence); IDS_HDT_CONSOLE ("\n\t\tFenceThresholdRxDll\n"); MemNTrainPhyFenceNb (NBPtr); FenceThresholdRxDll = (UINT8) MemNGetBitFieldNb (NBPtr, BFPhyFence); // 8. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]0F[AlwaysEnDllClks]=000b. MemNSetBitFieldNb (NBPtr, BFAlwaysEnDllClks, 0x0000); // 9. Program D18F2x[1,0]9C_x0000_0008[FenceTrSel]=11b. // 10. Perform phy fence training. See 2.10.3.2.3.1 [Phy Fence Training]. // 11. Write the calculated fence value to D18F2x[1,0]9C_x0000_000C[FenceThresholdTxPad]. MemNSetBitFieldNb (NBPtr, BFFenceTrSel, 3); MAKE_TSEFO (NBPtr->NBRegTable, DCT_PHY_ACCESS, 0x0C, 20, 16, BFPhyFence); IDS_HDT_CONSOLE ("\n\t\tFenceThresholdTxPad\n"); MemNTrainPhyFenceNb (NBPtr); FenceThresholdTxPad = (UINT8) MemNGetBitFieldNb (NBPtr, BFPhyFence); // Program Fence2 threshold for Clk, Cmd, and Addr if (FenceThresholdTxPad < 16) { MemNSetBitFieldNb (NBPtr, BFClkFence2, FenceThresholdTxPad | 0x10); MemNSetBitFieldNb (NBPtr, BFCmdFence2, FenceThresholdTxPad | 0x10); MemNSetBitFieldNb (NBPtr, BFAddrFence2, FenceThresholdTxPad | 0x10); } else { MemNSetBitFieldNb (NBPtr, BFClkFence2, 0); MemNSetBitFieldNb (NBPtr, BFCmdFence2, 0); MemNSetBitFieldNb (NBPtr, BFAddrFence2, 0); } // Program Fence2 threshold for data Fence2Data = 0; if (FenceThresholdTxPad < 16) { Fence2Data |= FenceThresholdTxPad | 0x10; } if (FenceThresholdRxDll < 16) { Fence2Data |= (FenceThresholdRxDll | 0x10) << 10; } if (FenceThresholdTxDll < 16) { Fence2Data |= (FenceThresholdTxDll | 0x10) << 5; } MemNSetBitFieldNb (NBPtr, BFDataFence2, Fence2Data); // Reprogram F2x9C_04. MemNSetBitFieldNb (NBPtr, BFAddrTmgControl, MemNGetBitFieldNb (NBPtr, BFAddrTmgControl)); } /* -----------------------------------------------------------------------------*/ /** * * * This function executes Phy fence training * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * */ VOID MemNTrainPhyFenceNb ( IN OUT MEM_NB_BLOCK *NBPtr ) { UINT8 Byte; UINT16 Avg; UINT8 PREvalue; if (MemNGetBitFieldNb (NBPtr, BFDisDramInterface)) { return; } // 1. BIOS first programs a seed value to the phase recovery // engine registers. // IDS_HDT_CONSOLE ("\t\tSeeds: "); for (Byte = 0; Byte < 9; Byte++) { // This includes ECC as byte 8 MemNSetTrainDlyNb (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (0, Byte), 19); IDS_HDT_CONSOLE ("%02x ", 19); } IDS_HDT_CONSOLE ("\n\t\tPhyFenceTrEn = 1"); // 2. Set F2x[1, 0]9C_x08[PhyFenceTrEn]=1. MemNSetBitFieldNb (NBPtr, BFPhyFenceTrEn, 1); if (!NBPtr->IsSupported[ClientNbFence]) { // 3. Wait 200 MEMCLKs. MemUWait10ns (500, NBPtr->MemPtr); } else { // 3. Wait 2000 MEMCLKs. MemUWait10ns (5000, NBPtr->MemPtr); } // 4. Clear F2x[1, 0]9C_x08[PhyFenceTrEn]=0. MemNSetBitFieldNb (NBPtr, BFPhyFenceTrEn, 0); // 5. BIOS reads the phase recovery engine registers // F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52. // 6. Calculate the average value of the fine delay and subtract 8. // Avg = 0; IDS_HDT_CONSOLE ("\n\t\t PRE: "); for (Byte = 0; Byte < 9; Byte++) { // This includes ECC as byte 8 PREvalue = (UINT8) (0x1F & MemNGetTrainDlyNb (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (0, Byte))); Avg = Avg + ((UINT16) PREvalue); IDS_HDT_CONSOLE ("%02x ", PREvalue); } Avg = ((Avg + 8) / 9); // round up Avg -= 8; NBPtr->MemNPFenceAdjustNb (NBPtr, &Avg); IDS_HDT_CONSOLE ("\n\t\tFence: %02x\n", Avg); // 7. Write the value to F2x[1, 0]9C_x0C[PhyFence]. MemNSetBitFieldNb (NBPtr, BFPhyFence, Avg); // 8. BIOS rewrites F2x[1, 0]9C_x04, DRAM Address/Command Timing Control // Register delays for both channels. This forces the phy to recompute // the fence. // MemNSetBitFieldNb (NBPtr, BFAddrTmgControl, MemNGetBitFieldNb (NBPtr, BFAddrTmgControl)); } /* -----------------------------------------------------------------------------*/ /** * * * This function initializes the DDR phy compensation logic * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * */ VOID MemNInitPhyCompNb ( IN OUT MEM_NB_BLOCK *NBPtr ) { CONST UINT8 TableCompRiseSlew20x[] = {7, 3, 2, 2}; CONST UINT8 TableCompRiseSlew15x[] = {7, 7, 3, 2}; CONST UINT8 TableCompFallSlew20x[] = {7, 5, 3, 2}; CONST UINT8 TableCompFallSlew15x[] = {7, 7, 5, 3}; UINT8 i; UINT8 j; UINT8 CurrDct; UINT8 CurrChannel; BOOLEAN MarginImprv; MarginImprv = FALSE; CurrDct = NBPtr->Dct; CurrChannel = NBPtr->Channel; if (NBPtr->IsSupported[CheckSlewWithMarginImprv]) { if (NBPtr->MCTPtr->GangedMode == FALSE) { for (i = 0; i < NBPtr->DctCount; i++) { MemNSwitchDCTNb (NBPtr, i); for (j = 0; j < NBPtr->ChannelCount; j++) { NBPtr->SwitchChannel (NBPtr, j); if ((NBPtr->ChannelPtr->Dimms == 4) && ((NBPtr->DCTPtr->Timings.Speed == DDR533_FREQUENCY) || (NBPtr->DCTPtr->Timings.Speed == DDR667_FREQUENCY))) { MarginImprv = TRUE; } } } MemNSwitchDCTNb (NBPtr, CurrDct); NBPtr->SwitchChannel (NBPtr, CurrChannel); } } // 1. BIOS disables the phy compensation register by programming F2x9C_x08[DisAutoComp]=1 // 2. BIOS waits 5 us for the disabling of the compensation engine to complete. // DisAutoComp will be cleared after Dram init has completed // MemNSwitchDCTNb (NBPtr, 0); MemNSetBitFieldNb (NBPtr, BFDisAutoComp, 1); MemUWait10ns (500, NBPtr->MemPtr); MemNSwitchDCTNb (NBPtr, CurrDct); // 3. For each normalized driver strength code read from // F2x[1, 0]9C_x00[AddrCmdDrvStren], program the // corresponding 3 bit predriver code in F2x9C_x0A[D3Cmp1NCal, D3Cmp1PCal]. // // 4. For each normalized driver strength code read from // F2x[1, 0]9C_x00[DataDrvStren], program the corresponding // 3 bit predriver code in F2x9C_x0A[D3Cmp0NCal, D3Cmp0PCal, D3Cmp2NCal, // D3Cmp2PCal]. // j = (UINT8) MemNGetBitFieldNb (NBPtr, BFAddrCmdDrvStren); i = (UINT8) MemNGetBitFieldNb (NBPtr, BFDataDrvStren); MemNSwitchDCTNb (NBPtr, 0); MemNSetBitFieldNb (NBPtr, BFD3Cmp1NCal, TableCompRiseSlew20x[j]); MemNSetBitFieldNb (NBPtr, BFD3Cmp1PCal, TableCompFallSlew20x[j]); if (NBPtr->IsSupported[CheckSlewWithMarginImprv]) { MemNSetBitFieldNb (NBPtr, BFD3Cmp0NCal, (MarginImprv) ? 0 : TableCompRiseSlew15x[i]); MemNSetBitFieldNb (NBPtr, BFD3Cmp0PCal, (MarginImprv) ? 0 : TableCompFallSlew15x[i]); MemNSetBitFieldNb (NBPtr, BFD3Cmp2NCal, (MarginImprv) ? 0 : TableCompRiseSlew15x[i]); MemNSetBitFieldNb (NBPtr, BFD3Cmp2PCal, (MarginImprv) ? 0 : TableCompFallSlew15x[i]); } if (NBPtr->IsSupported[CheckSlewWithoutMarginImprv]) { ASSERT (i <= 3); MemNSetBitFieldNb (NBPtr, BFD3Cmp0NCal, TableCompRiseSlew15x[i]); MemNSetBitFieldNb (NBPtr, BFD3Cmp0PCal, TableCompFallSlew15x[i]); MemNSetBitFieldNb (NBPtr, BFD3Cmp2NCal, TableCompRiseSlew15x[i]); MemNSetBitFieldNb (NBPtr, BFD3Cmp2PCal, TableCompFallSlew15x[i]); } MemNSwitchDCTNb (NBPtr, CurrDct); } /* -----------------------------------------------------------------------------*/ /** * * * This is a general purpose function that executes before DRAM training * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * */ VOID MemNBeforeDQSTrainingNb ( IN OUT MEM_NB_BLOCK *NBPtr ) { UINT8 Dct; UINT8 ChipSel; UINT32 TestAddrRJ16; UINT32 RealAddr; MemTBeginTraining (NBPtr->TechPtr); for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { MemNSwitchDCTNb (NBPtr, Dct); if (NBPtr->DCTPtr->Timings.DctMemSize != 0) { for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel += 2) { if (MemNGetMCTSysAddrNb (NBPtr, ChipSel, &TestAddrRJ16)) { RealAddr = MemUSetUpperFSbase (TestAddrRJ16, NBPtr->MemPtr); MemUDummyCLRead (RealAddr); MemNSetBitFieldNb (NBPtr, BFErr350, 0x8000); MemUWait10ns (60, NBPtr->MemPtr); // Wait 300ns MemNSetBitFieldNb (NBPtr, BFErr350, 0x0000); MemUWait10ns (400, NBPtr->MemPtr); // Wait 2us MemUProcIOClFlush (TestAddrRJ16, 1, NBPtr->MemPtr); break; } } } if (NBPtr->IsSupported[CheckEccDLLPwrDnConfig]) { if (!NBPtr->MCTPtr->Status[SbEccDimms]) { MemNSetBitFieldNb (NBPtr, BFEccDLLPwrDnConf, 0x0010); } if (NBPtr->DCTPtr->Timings.Dimmx4Present == 0) { MemNSetBitFieldNb (NBPtr, BFEccDLLConf, 0x0080); } } } MemTEndTraining (NBPtr->TechPtr); } /*-----------------------------------------------------------------------------*/ /** * * Returns the parameters for a requested delay value to be used in training * The correct Min, Max and Mask are determined based on the type of Delay, * and the frequency * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in] TrnDly - Type of delay * @param[in,out] *Parms - Pointer to the TRN_DLY-PARMS struct * */ VOID MemNGetTrainDlyParmsNb ( IN OUT MEM_NB_BLOCK *NBPtr, IN TRN_DLY_TYPE TrnDly, IN OUT TRN_DLY_PARMS *Parms ) { Parms->Min = 0; if (TrnDly == AccessWrDatDly) { Parms->Max = 0x1F; Parms->Mask = 0x01F; } else if (TrnDly == AccessRdDqsDly) { if ( (NBPtr->IsSupported[CheckMaxRdDqsDlyPtr]) && (NBPtr->DCTPtr->Timings.Speed > DDR667_FREQUENCY) ) { Parms->Max = 0x3E; Parms->Mask = 0x03E; } else { Parms->Max = 0x1F; Parms->Mask = 0x01F; } } } /*---------------------------------------------------------------------------- * LOCAL FUNCTIONS * *---------------------------------------------------------------------------- */ /* -----------------------------------------------------------------------------*/ /** * * * This function gets or set DQS timing during training. * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in] TrnDly - type of delay to be set * @param[in] DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed * (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding * @param[in] Field - Value to be programmed * @param[in] IsSet - Indicates if the function will set or get * * @return value read, if the function is used as a "get" */ UINT32 MemNcmnGetSetTrainDlyNb ( IN OUT MEM_NB_BLOCK *NBPtr, IN UINT8 IsSet, IN TRN_DLY_TYPE TrnDly, IN DRBN DrbnVar, IN UINT16 Field ) { UINT16 Index; UINT16 Offset; UINT32 Value; UINT32 Address; UINT8 Dimm; UINT8 Rank; UINT8 Byte; UINT8 Nibble; Dimm = DRBN_DIMM (DrbnVar); Rank = DRBN_RANK (DrbnVar); Byte = DRBN_BYTE (DrbnVar); Nibble = DRBN_NBBL (DrbnVar); ASSERT (Dimm < 4); ASSERT (Byte <= ECC_DLY); switch (TrnDly) { case AccessRcvEnDly: Index = 0x10; break; case AccessWrDqsDly: Index = 0x30; break; case AccessWrDatDly: Index = 0x01; break; case AccessRdDqsDly: Index = 0x05; break; case AccessPhRecDly: Index = 0x50; break; default: Index = 0; IDS_ERROR_TRAP; } switch (TrnDly) { case AccessRcvEnDly: case AccessWrDqsDly: Index += (Dimm * 3); if (Byte & 0x04) { // if byte 4,5,6,7 Index += 0x10; } if (Byte & 0x02) { // if byte 2,3,6,7 Index++; } if (Byte > 7) { Index += 2; } Offset = 16 * (Byte % 2); Index |= (Rank << 8); Index |= (Nibble << 9); break; case AccessRdDqsDly: case AccessWrDatDly: if (NBPtr->IsSupported[DimmBasedOnSpeed]) { if (NBPtr->DCTPtr->Timings.Speed < DDR800_FREQUENCY) { // if DDR speed is below 800, use DIMM 0 delays for all DIMMs. Dimm = 0; } } Index += (Dimm * 0x100); if (Nibble) { if (Rank) { Index += 0xA0; } else { Index += 0x70; } } else if (Rank) { Index += 0x60; } // break is not being used here because AccessRdDqsDly and AccessWrDatDly also need // to run AccessPhRecDly sequence. case AccessPhRecDly: Index += (Byte / 4); Offset = 8 * (Byte % 4); break; default: Offset = 0; IDS_ERROR_TRAP; } Address = Index; MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address); MemNPollBitFieldNb (NBPtr, BFDctAccessDone, 1, PCI_ACCESS_TIMEOUT, FALSE); Value = MemNGetBitFieldNb (NBPtr, BFDctAddlDataReg); if (IsSet) { if (TrnDly == AccessPhRecDly) { Value = NBPtr->DctCachePtr->PhRecReg[Index & 0x03]; } Value = ((UINT32)Field << Offset) | (Value & (~((UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF) << Offset))); MemNSetBitFieldNb (NBPtr, BFDctAddlDataReg, Value); Address |= DCT_ACCESS_WRITE; MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address); MemNPollBitFieldNb (NBPtr, BFDctAccessDone, 1, PCI_ACCESS_TIMEOUT, FALSE); if (TrnDly == AccessPhRecDly) { NBPtr->DctCachePtr->PhRecReg[Index & 0x03] = Value; } } else { Value = (Value >> Offset) & (UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF); } return Value; } /* -----------------------------------------------------------------------------*/ /** * * This function gets or set DQS timing during training. * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in] IsSet - Indicates if the function will set or get * @param[in] TrnDly - type of delay to be set * @param[in] DrbnVar - encoding of Dimm-Rank-Byte-Nibble to be accessed * (use either DIMM_BYTE_ACCESS(dimm,byte) or CS_NBBL_ACCESS(cs,nibble) to use this encoding * @param[in] Field - Value to be programmed * * @return value read, if the function is used as a "get" */ UINT32 MemNcmnGetSetTrainDlyClientNb ( IN OUT MEM_NB_BLOCK *NBPtr, IN UINT8 IsSet, IN TRN_DLY_TYPE TrnDly, IN DRBN DrbnVar, IN UINT16 Field ) { UINT16 Index; UINT16 Offset; UINT32 Value; UINT32 Address; UINT8 Dimm; UINT8 Byte; Dimm = DRBN_DIMM (DrbnVar); Byte = DRBN_BYTE (DrbnVar); if ((Dimm > 1) || (Byte > 7)) { // LN only support DIMM 0 and 8 byte lanes. return 0; } switch (TrnDly) { case AccessRcvEnDly: Index = 0x10; break; case AccessWrDqsDly: Index = 0x30; break; case AccessWrDatDly: Index = 0x01; break; case AccessRdDqsDly: Index = 0x05; break; case AccessPhRecDly: Index = 0x50; break; default: Index = 0; IDS_ERROR_TRAP; } switch (TrnDly) { case AccessRcvEnDly: case AccessWrDqsDly: Index += (Dimm * 3); if (Byte & 0x04) { // if byte 4,5,6,7 Index += 0x10; } if (Byte & 0x02) { // if byte 2,3,6,7 Index++; } Offset = 16 * (Byte % 2); break; case AccessRdDqsDly: case AccessWrDatDly: Index += (Dimm * 0x100); // break is not being used here because AccessRdDqsDly and AccessWrDatDly also need // to run AccessPhRecDly sequence. case AccessPhRecDly: Index += (Byte / 4); Offset = 8 * (Byte % 4); break; default: Offset = 0; IDS_ERROR_TRAP; } Address = Index; MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address); Value = MemNGetBitFieldNb (NBPtr, BFDctAddlDataReg); if (IsSet) { if (TrnDly == AccessPhRecDly) { Value = NBPtr->DctCachePtr->PhRecReg[Index & 0x03]; } Value = ((UINT32)Field << Offset) | (Value & (~((UINT32)0xFF << Offset))); MemNSetBitFieldNb (NBPtr, BFDctAddlDataReg, Value); Address |= DCT_ACCESS_WRITE; MemNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address); if (TrnDly == AccessPhRecDly) { NBPtr->DctCachePtr->PhRecReg[Index & 0x03] = Value; } } else { Value = (Value >> Offset) & 0xFF; } return Value; } /* -----------------------------------------------------------------------------*/ /** * * This function initializes the training pattern. * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * * @return AGESA_STATUS - Result * AGESA_SUCCESS - Training pattern is ready to use * AGESA_ERROR - Unable to initialize the pattern. */ AGESA_STATUS MemNTrainingPatternInitNb ( IN OUT MEM_NB_BLOCK *NBPtr ) { MEM_TECH_BLOCK *TechPtr; ALLOCATE_HEAP_PARAMS AllocHeapParams; POS_TRN_PATTERN_TYPE TrainPattern; AGESA_STATUS Status; TechPtr = NBPtr->TechPtr; // // Determine pattern to be used // if (NBPtr->PosTrnPattern == POS_PATTERN_256B) { if (NBPtr->MCTPtr->Status[Sb128bitmode]) { TrainPattern = TestPatternJD256B; TechPtr->PatternLength = 64; } else { TrainPattern = TestPatternJD256A; TechPtr->PatternLength = 32; } } else { // // 72 bit pattern will be used if PosTrnPattern is not specified // if (NBPtr->MCTPtr->Status[Sb128bitmode]) { TrainPattern = TestPatternJD1B; TechPtr->PatternLength = 18; } else { TrainPattern = TestPatternJD1A; TechPtr->PatternLength = 9; } } // // Allocate training buffer // AllocHeapParams.RequestedBufferSize = (TechPtr->PatternLength * 64 * 2) + 16; AllocHeapParams.BufferHandle = AMD_MEM_TRAIN_BUFFER_HANDLE; AllocHeapParams.Persist = HEAP_LOCAL_CACHE; Status = HeapAllocateBuffer (&AllocHeapParams, &NBPtr->MemPtr->StdHeader); ASSERT (Status == AGESA_SUCCESS); if (Status != AGESA_SUCCESS) { return Status; } TechPtr->PatternBufPtr = AllocHeapParams.BufferPtr; AlignPointerTo16Byte (&TechPtr->PatternBufPtr); TechPtr->TestBufPtr = TechPtr->PatternBufPtr + (TechPtr->PatternLength * 64); // Prepare training pattern MemUFillTrainPattern (TrainPattern, TechPtr->PatternBufPtr, TechPtr->PatternLength * 64); return Status; } /* -----------------------------------------------------------------------------*/ /** * * This function finalizes the training pattern. * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in] Index - Index of Write Data Delay Value * @param[in,out] *Value - Write Data Delay Value * @return BOOLEAN - TRUE - Use the value returned. * FALSE - No more values in table. */ BOOLEAN MemNGetApproximateWriteDatDelayNb ( IN OUT MEM_NB_BLOCK *NBPtr, IN UINT8 Index, IN OUT UINT8 *Value ) { CONST UINT8 WriteDatDelayValue[] = {0x10, 0x4, 0x8, 0xC, 0x14, 0x18, 0x1C, 0x1F}; if (Index < GET_SIZE_OF (WriteDatDelayValue)) { *Value = WriteDatDelayValue[Index]; return TRUE; } return FALSE; } /* -----------------------------------------------------------------------------*/ /** * * This function finalizes the training pattern. * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * * @return AGESA_STATUS - Result * AGESA_SUCCESS - Training pattern has been finalized. * AGESA_ERROR - Unable to initialize the pattern. */ AGESA_STATUS MemNTrainingPatternFinalizeNb ( IN OUT MEM_NB_BLOCK *NBPtr ) { AGESA_STATUS Status; // // Deallocate training buffer // Status = HeapDeallocateBuffer (AMD_MEM_TRAIN_BUFFER_HANDLE, &NBPtr->MemPtr->StdHeader); ASSERT (Status == AGESA_SUCCESS); return Status; } /* -----------------------------------------------------------------------------*/ /** * * This function returns the number of chipselects per channel. * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * * @return */ UINT8 MemNCSPerChannelNb ( IN OUT MEM_NB_BLOCK *NBPtr ) { return MAX_CS_PER_CHANNEL; } /* -----------------------------------------------------------------------------*/ /** * * This function returns the number of Chipselects controlled by each set * of Delay registers under current conditions. * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * * @return */ UINT8 MemNCSPerDelayNb ( IN OUT MEM_NB_BLOCK *NBPtr ) { return MAX_CS_PER_DELAY; } /* -----------------------------------------------------------------------------*/ /** * * This function returns the minimum data eye width in 32nds of a UI for * the type of data eye(Rd/Wr) that is being trained. This value will * be the minimum number of consecutive delays that yield valid data. * Uses TechPtr->Direction to determine read or write. * * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * * @return */ UINT8 MemNMinDataEyeWidthNb ( IN OUT MEM_NB_BLOCK *NBPtr ) { if (NBPtr->TechPtr->Direction == DQS_READ_DIR) { return MIN_RD_DATAEYE_WIDTH_NB; } else { return MIN_WR_DATAEYE_WIDTH_NB; } } /* -----------------------------------------------------------------------------*/ /** * * This function programs the phy registers according to the desired phy VDDIO voltage level * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * */ VOID MemNPhyVoltageLevelClientNb ( IN OUT MEM_NB_BLOCK *NBPtr ) { BIT_FIELD_NAME BitField; UINT16 Value; UINT16 Mask; Mask = 0xFFE7; Value = (UINT16) NBPtr->RefPtr->DDR3Voltage << 3; /// @todo: Need to verify if the following logic can work on real hardware or not for (BitField = BFDataRxVioLvl; BitField <= BFCmpVioLvl; BitField++) { if (BitField == BFCmpVioLvl) { Mask = 0x3FFF; Value <<= (14 - 3); } MemNBrdcstSetNb (NBPtr, BitField, ((MemNGetBitFieldNb (NBPtr, BitField) & Mask)) | Value); } } /* -----------------------------------------------------------------------------*/ /** * * * This function adjusts Avg PRE value of Phy fence training according to specific CPU family. * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * @param[in,out] *Value16 - Pointer to the value that we want to adjust * */ VOID MemNPFenceAdjustClientNb ( IN OUT MEM_NB_BLOCK *NBPtr, IN OUT UINT16 *Value16 ) { *Value16 += 2; //for LN, the Avg PRE value is subtracted by 6 only. } /* -----------------------------------------------------------------------------*/ /** * * * This function initializes the DDR phy compensation logic * * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK * */ VOID MemNInitPhyCompClientNb ( IN OUT MEM_NB_BLOCK *NBPtr ) { // Slew rate table array [x][y][z] // array[0]: slew rate for VDDIO 1.5V // array[1]: slew rate for VDDIO 1.35V // array[x][y]: slew rate for a certain frequency // array[x][y][0]: frequency mask for current entry CONST STATIC UINT16 TxPrePNDataDqs[2][3][5] = { {{ (UINT16) DDR800, 0xFF6, 0xDAD, 0xDAD, 0x924}, { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xFF6}, { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xFF6}}, {{ (UINT16) DDR800, 0xFF6, 0xB6D, 0xB6D, 0x924}, { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xFF6}, { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xFF6}} }; CONST STATIC UINT16 TxPrePNCmdAddr[2][3][5] = { {{ (UINT16) DDR800, 0x492, 0x492, 0x492, 0x492}, { (UINT16) (DDR1066 + DDR1333), 0x6DB, 0x6DB, 0x6DB, 0x6DB}, { (UINT16) (DDR1600 + DDR1866), 0xB6D, 0xB6D, 0xB6D, 0xB6D}}, {{ (UINT16) DDR800, 0x492, 0x492, 0x492, 0x492}, { (UINT16) (DDR1066 + DDR1333), 0x924, 0x6DB, 0x6DB, 0x6DB}, { (UINT16) (DDR1600 + DDR1866), 0xB6D, 0xB6D, 0x924, 0x924}} }; CONST STATIC UINT16 TxPrePNClock[2][3][5] = { {{ (UINT16) DDR800, 0xDAD, 0xDAD, 0x924, 0x924}, { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xB6D}, { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xFF6}}, {{ (UINT16) DDR800, 0xDAD, 0xDAD, 0x924, 0x924}, { (UINT16) (DDR1066 + DDR1333), 0xFF6, 0xFF6, 0xFF6, 0xDAD}, { (UINT16) (DDR1600 + DDR1866), 0xFF6, 0xFF6, 0xFF6, 0xDAD}} }; CONST PHY_COMP_INIT_CLIENTNB PhyCompInitBitField[] = { // 3. Program TxPreP/TxPreN for Data and DQS according toTable 14 if VDDIO is 1.5V or Table 15 if 1.35V. // A. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]0[A,6]={0000b, TxPreP, TxPreN}. // B. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]02={1000b, TxPreP, TxPreN}. {BFDqsDrvStren, BFDataByteTxPreDriverCal2Pad1, BFDataByteTxPreDriverCal2Pad1, 0, TxPrePNDataDqs}, {BFDataDrvStren, BFDataByteTxPreDriverCal2Pad2, BFDataByteTxPreDriverCal2Pad2, 0, TxPrePNDataDqs}, {BFDataDrvStren, BFDataByteTxPreDriverCal, BFDataByteTxPreDriverCal, 8, TxPrePNDataDqs}, // 4. Program TxPreP/TxPreN for Cmd/Addr according toTable 16 if VDDIO is 1.5V or Table 17 if 1.35V. // A. Program D18F2x[1,0]9C_x0D0F_[C,8][1:0][12,0E,0A,06]={0000b, TxPreP, TxPreN}. // B. Program D18F2x[1,0]9C_x0D0F_[C,8][1:0]02={1000b, TxPreP, TxPreN}. {BFCsOdtDrvStren, BFCmdAddr0TxPreDriverCal2Pad1, BFCmdAddr0TxPreDriverCal2Pad2, 0, TxPrePNCmdAddr}, {BFAddrCmdDrvStren, BFCmdAddr1TxPreDriverCal2Pad1, BFAddrTxPreDriverCal2Pad4, 0, TxPrePNCmdAddr}, {BFCsOdtDrvStren, BFCmdAddr0TxPreDriverCalPad0, BFCmdAddr0TxPreDriverCalPad0, 8, TxPrePNCmdAddr}, {BFCkeDrvStren, BFAddrTxPreDriverCalPad0, BFAddrTxPreDriverCalPad0, 8, TxPrePNCmdAddr}, {BFAddrCmdDrvStren, BFCmdAddr1TxPreDriverCalPad0, BFCmdAddr1TxPreDriverCalPad0, 8, TxPrePNCmdAddr}, // 5. Program TxPreP/TxPreN for Clock according toTable 18 if VDDIO is 1.5V or Table 19 if 1.35V. // A. Program D18F2x[1,0]9C_x0D0F_2[1:0]02={1000b, TxPreP, TxPreN}. {BFClkDrvStren, BFClock0TxPreDriverCalPad0, BFClock1TxPreDriverCalPad0, 8, TxPrePNClock} }; BIT_FIELD_NAME CurrentBitField; UINT16 SpeedMask; CONST UINT16 (*TxPrePNArray)[5]; UINT8 Voltage; UINT8 i; UINT8 j; UINT8 k; // 1. Program D18F2x[1,0]9C_x0000_0008[DisAutoComp] = 1. MemNSetBitFieldNb (NBPtr, BFDisAutoComp, 1); // 2. Program D18F2x[1,0]9C_x0D0F_E003[DisalbePredriverCal]=1 MemNSetBitFieldNb (NBPtr, BFDisablePredriverCal, (1 << 13)); SpeedMask = (UINT16) 1 << (NBPtr->DCTPtr->Timings.Speed / 66); Voltage = (UINT8) NBPtr->RefPtr->DDR3Voltage; for (j = 0; j < GET_SIZE_OF (PhyCompInitBitField); j ++) { i = (UINT8) MemNGetBitFieldNb (NBPtr, PhyCompInitBitField[j].IndexBitField); TxPrePNArray = PhyCompInitBitField[j].TxPrePN[Voltage]; for (k = 0; k < 3; k ++) { if ((TxPrePNArray[k][0] & SpeedMask) != 0) { for (CurrentBitField = PhyCompInitBitField[j].StartTargetBitField; CurrentBitField <= PhyCompInitBitField[j].EndTargetBitField; CurrentBitField ++) { MemNSetBitFieldNb (NBPtr, CurrentBitField, ((PhyCompInitBitField[j].ExtraValue << 12) | TxPrePNArray[k][i + 1])); } break; } } ASSERT (k < 3); } // 6. Program D18F2x[1,0]9C_x0000_0008[DisAutoComp] = 0. MemNSetBitFieldNb (NBPtr, BFDisAutoComp, 0); }