7 * Northbridge common DCT support for Recovery
9 * @xrefitem bom "File Content Label" "Release Content"
11 * @e sub-project: (Proc/Recovery/Mem/NB)
12 * @e \$Revision: 50454 $ @e \$Date: 2011-04-10 21:20:37 -0600 (Sun, 10 Apr 2011) $
15 /*****************************************************************************
17 * Copyright (C) 2012 Advanced Micro Devices, Inc.
18 * All rights reserved.
20 * Redistribution and use in source and binary forms, with or without
21 * modification, are permitted provided that the following conditions are met:
22 * * Redistributions of source code must retain the above copyright
23 * notice, this list of conditions and the following disclaimer.
24 * * Redistributions in binary form must reproduce the above copyright
25 * notice, this list of conditions and the following disclaimer in the
26 * documentation and/or other materials provided with the distribution.
27 * * Neither the name of Advanced Micro Devices, Inc. nor the names of
28 * its contributors may be used to endorse or promote products derived
29 * from this software without specific prior written permission.
31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
32 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
33 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
34 * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY
35 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
36 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
38 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
40 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 * ***************************************************************************
48 *----------------------------------------------------------------------------
51 *----------------------------------------------------------------------------
57 #include "OptionMemory.h"
58 #include "PlatformMemoryConfiguration.h"
61 #include "cpuFamRegisters.h"
68 #define FILECODE PROC_RECOVERY_MEM_NB_MRNDCT_FILECODE
69 /*----------------------------------------------------------------------------
70 * DEFINITIONS AND MACROS
72 *----------------------------------------------------------------------------
74 #define RECDEF_CSMASK_REG 0x00083FE0
75 #define RECDEF_DRAM_BASE_REG 0x00000003
78 /*----------------------------------------------------------------------------
79 * TYPEDEFS AND STRUCTURES
81 *----------------------------------------------------------------------------
83 /// Type of an entry for processing phy init compensation for client NB
85 BIT_FIELD_NAME IndexBitField; ///< Bit field on which the value is decided
86 BIT_FIELD_NAME StartTargetBitField; ///< First bit field to be modified
87 BIT_FIELD_NAME EndTargetBitField; ///< Last bit field to be modified
88 UINT16 ExtraValue; ///< Extra value needed to be written to bit field
89 CONST UINT16 (*TxPrePN)[4]; ///< Pointer to slew rate table
90 } REC_PHY_COMP_INIT_CLIENTNB;
92 /*----------------------------------------------------------------------------
93 * PROTOTYPES OF LOCAL FUNCTIONS
95 *----------------------------------------------------------------------------
99 MemRecTCtlOnDimmMirrorNb (
100 IN OUT MEM_NB_BLOCK *NBPtr,
107 IN OUT MEM_NB_BLOCK *NBPtr
112 MemRecNProgNbPstateDependentRegClientNb (
113 IN OUT MEM_NB_BLOCK *NBPtr
118 MemRecNTrainPhyFenceNb (
119 IN OUT MEM_NB_BLOCK *NBPtr
124 MemRecNCommonReadWritePatternUnb (
125 IN OUT MEM_NB_BLOCK *NBPtr,
130 /*----------------------------------------------------------------------------
133 *----------------------------------------------------------------------------
136 /* -----------------------------------------------------------------------------*/
139 * This function programs the memory controller with configuration parameters
142 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
144 * @return TRUE - An Error value lower than AGESA_ERROR may have occurred
145 * @return FALSE - An Error value greater than or equal to AGESA_ERROR may have occurred
149 MemRecNAutoConfigNb (
150 IN OUT MEM_NB_BLOCK *NBPtr
158 CH_DEF_STRUCT *ChannelPtr;
162 DCTPtr = NBPtr->DCTPtr;
163 ChannelPtr = NBPtr->ChannelPtr;
165 //Prepare variables for future usage.
166 for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
167 if ((ChannelPtr->ChDimmValid & (UINT8) 1 << Dimm) != 0) {
168 DCTPtr->Timings.CsPresent |= (UINT16) 1 << (Dimm * 2);
169 if (((ChannelPtr->DimmDrPresent & (UINT8) 1 << Dimm) == 0) && ((ChannelPtr->DimmQrPresent & (UINT8) 1 << Dimm) == 0)) {
172 DCTPtr->Timings.CsPresent |= (UINT16) 1 << (Dimm * 2 + 1);
177 Dimm = NBPtr->DimmToBeUsed;
179 //Temporarily set all CS Base/Limit registers (corresponding to Dimms exist on a channel) with 256MB size for WL training.
181 for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel++) {
182 if (DCTPtr->Timings.CsPresent & (UINT8) 1 << ChipSel) {
184 CSBase &= (UINT32) ~0x08; //Clear OnDimmMirror bit.
185 if (((ChipSel & 1) != 0) && ((ChannelPtr->DimmMirrorPresent & (UINT8) 1 << (ChipSel >> 1)) != 0)) {
186 CSBase |= (UINT32) 0x08; //Set OnDimmMirror bit.
188 MemRecNSetBitFieldNb (NBPtr, (BFCSBaseAddr0Reg + ChipSel), (CSBase | 0x01));
190 if ((ChipSel & 1) == 0) {
191 MemRecNSetBitFieldNb (NBPtr, (BFCSMask0Reg + (ChipSel >> 1)), RECDEF_CSMASK_REG);
195 MemRecNSetBitFieldNb (NBPtr, BFDramBaseReg0, RECDEF_DRAM_BASE_REG);
196 MemRecNSetBitFieldNb (NBPtr, BFDramLimitReg0, 0x70000);
198 // Disable the other DCT
199 NBPtr->MemRecNSwitchDctNb (NBPtr, Dct ^ 0x01);
200 MemRecNSetBitFieldNb (NBPtr, BFDisDramInterface, 1);
201 NBPtr->MemRecNSwitchDctNb (NBPtr, Dct);
203 // If DCT 1, set DctSelBase registers
204 MemRecNSetBitFieldNb (NBPtr, BFDctSelBaseAddrReg, 0x00000003);
205 MemRecNSetBitFieldNb (NBPtr, BFDctSelBaseOffsetReg, 0x00000000);
208 // Use default values for common registers
210 while (NBPtr->RecModeDefRegArray[i] != NULL) {
211 MemRecNSetBitFieldNb (NBPtr, NBPtr->RecModeDefRegArray[i], NBPtr->RecModeDefRegArray[i + 1]);
215 // Other specific settings
216 MemRecNSetBitFieldNb (NBPtr, BFX4Dimm, ChannelPtr->Dimmx4Present );
218 if ((ChannelPtr->RegDimmPresent == 0) && (ChannelPtr->SODimmPresent == 0)) {
219 MemRecNSetBitFieldNb (NBPtr, BFUnBuffDimm, 1);
222 if ((NBPtr->ChannelPtr->RegDimmPresent != 0) && (NBPtr->ChannelPtr->TechType == DDR3_TECHNOLOGY)) {
223 MemRecNSetBitFieldNb (NBPtr, BFSubMemclkRegDly, 1);
225 MemRecNSetBitFieldNb (NBPtr, BFOdtSwizzle, 1);
231 /* -----------------------------------------------------------------------------*/
234 * This function gets platform specific config/timing values from the interface layer and
235 * programs them into DCT.
238 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
240 * @return TRUE - An Error value lower than AGESA_ERROR may have occurred
241 * @return FALSE - An Error value greater than or equal to AGESA_ERROR may have occurred
245 MemRecNPlatformSpecNb (
246 IN OUT MEM_NB_BLOCK *NBPtr
252 for (p = 0; p < MAX_PLATFORM_TYPES; p++) {
253 if (NBPtr->MemPtr->GetPlatformCfg[p] (NBPtr->MemPtr, NBPtr->MCTPtr->SocketId, NBPtr->ChannelPtr) == AGESA_SUCCESS) {
254 MemRecNSetBitFieldNb (NBPtr, BFODCControl, NBPtr->ChannelPtr->DctOdcCtl);
255 MemRecNSetBitFieldNb (NBPtr, BFAddrTmgControl, NBPtr->ChannelPtr->DctAddrTmg);
263 /* -----------------------------------------------------------------------------*/
266 * This function reads MemClkFreqVal bit to see if the DIMMs are present in this node.
267 * If the DIMMs are present then set the DRAM Enable bit for this node.
269 * Setting dram init starts up the DCT state machine, initializes the
270 * dram devices with MRS commands, and kicks off any
271 * HW memory clear process that the chip is capable of. The sooner
272 * that dram init is set for all nodes, the faster the memory system
273 * initialization can complete. Thus, the init loop is unrolled into
274 * two loops so as to start the processes for non BSP nodes sooner.
275 * This procedure will not wait for the process to finish. Synchronization is
278 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
283 MemRecNStartupDCTNb (
284 IN OUT MEM_NB_BLOCK *NBPtr
287 // 1. Ensure F2x[1, 0]9C_x08[DisAutoComp] = 1.
288 // 2. BIOS waits 5 us for the disabling of the compensation engine to complete.
289 // ------- Done in InitPhyComp_Nb -------
291 MemRecNSetBitFieldNb (NBPtr, BFDisAutoComp, 1);
292 MemRecUWait10ns (500, NBPtr->MemPtr);
294 //MemRecNSetBitFieldNb (NBPtr, BFInitDram, 1); // HW Dram init
295 AGESA_TESTPOINT (TpProcMemDramInit, &(NBPtr->MemPtr->StdHeader));
296 NBPtr->TechPtr->DramInit (NBPtr->TechPtr);
298 // 7. Program F2x[1, 0]9C_x08[DisAutoComp] = 0.
299 // 8. BIOS must wait 750 us for the phy compensation engine
302 MemRecNSetBitFieldNb (NBPtr, BFDisAutoComp, 0);
303 MemRecUWait10ns (75000, NBPtr->MemPtr);
305 while (MemRecNGetBitFieldNb (NBPtr, BFDramEnabled) == 0);
308 /* -----------------------------------------------------------------------------*/
311 * This function initializes the DRAM devices on all DCTs at the same time
313 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
318 MemRecNStartupDCTClientNb (
319 IN OUT MEM_NB_BLOCK *NBPtr
322 IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", NBPtr->Dct);
324 // Program D18F2x[1,0]9C_x0000_000B = 80000000h. #109999.
325 MemRecNSetBitFieldNb (NBPtr, BFDramPhyStatusReg, 0x80000000);
327 // Program D18F2x[1,0]9C_x0D0F_E013[PllRegWaitTime] = 0118h. #193770.
328 MemRecNSetBitFieldNb (NBPtr, BFPllRegWaitTime, 0x118);
330 // Phy Voltage Level Programming
331 MemRecNPhyVoltageLevelNb (NBPtr);
333 // Run frequency change sequence
334 MemRecNSetBitFieldNb (NBPtr, BFPllLockTime, NBPtr->FreqChangeParam->PllLockTimeDefault);
335 MemRecNSetBitFieldNb (NBPtr, BFMemClkFreq, 6);
336 MemRecNProgNbPstateDependentRegClientNb (NBPtr);
337 MemRecNSetBitFieldNb (NBPtr, BFMemClkFreqVal, 1);
338 MemRecNSetBitFieldNb (NBPtr, BFPllLockTime, 0x000F);
340 IDS_HDT_CONSOLE (MEM_FLOW, "\tMemClkAlign=0\n");
341 IDS_HDT_CONSOLE (MEM_FLOW, "\tEnDramInit = 1 for DCT%d\n", NBPtr->Dct);
342 MemRecNSetBitFieldNb (NBPtr, BFDbeGskMemClkAlignMode, 0);
343 MemRecNSetBitFieldNb (NBPtr, BFEnDramInit, 1);
345 // Run DramInit sequence
346 AGESA_TESTPOINT (TpProcMemDramInit, &(NBPtr->MemPtr->StdHeader));
347 NBPtr->TechPtr->DramInit (NBPtr->TechPtr);
348 IDS_HDT_CONSOLE (MEM_FLOW, "\nMemClkFreq: %d MHz\n", DDR800_FREQUENCY);
351 /* -----------------------------------------------------------------------------*/
354 * This function sets the maximum round-trip latency in the system from the processor to the DRAM
358 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
359 * @param[in] MaxRcvEnDly - Maximum receiver enable delay value
364 MemRecNSetMaxLatencyNb (
365 IN OUT MEM_NB_BLOCK *NBPtr,
366 IN UINT16 MaxRcvEnDly
371 AGESA_TESTPOINT (TpProcMemRcvrCalcLatency, &(NBPtr->MemPtr->StdHeader));
373 // Multiply the CAS Latency by two to get a number of 1/2 MEMCLKs UINTs.
376 // If registered DIMMs are being used then add 1 MEMCLK to the sub-total.
377 if (MemRecNGetBitFieldNb (NBPtr, BFUnBuffDimm) == 0) {
381 // if (AddrCmdSetup || CsOdtSetup || CkeSetup) then K := K + 2;
384 // If the F2x[1, 0]78[RdPtrInit] field is 4, 5, 6 or 7 MEMCLKs,
385 // then add 4, 3, 2, or 1 MEMCLKs, respectively to the sub-total.
389 // Add the maximum (worst case) delay value of DqsRcvEnGrossDelay
390 // that exists across all DIMMs and byte lanes.
392 SubTotal += MaxRcvEnDly >> 5;
394 // Add 5.5 to the sub-total. 5.5 represents part of the processor
395 // specific constant delay value in the DRAM clock domain.
397 SubTotal += 5; // add 5.5 1/2MemClk
399 // Convert the sub-total (in 1/2 MEMCLKs) to northbridge clocks (NCLKs)
400 // as follows (assuming DDR400 and assuming that no P-state or link speed
401 // changes have occurred).
403 // Simplified formula:
404 // SubTotal *= (Fn2xD4[NBFid]+4)/4
406 SubTotal = SubTotal * ((UINT16) MemRecNGetBitFieldNb (NBPtr, BFNbFid) + 4);
409 // Add 5 NCLKs to the sub-total. 5 represents part of the processor
410 // specific constant value in the northbridge clock domain.
414 // Program the F2x[1, 0]78[MaxRdLatency] register with the total delay value
415 MemRecNSetBitFieldNb (NBPtr, BFMaxLatency, SubTotal);
419 /* -----------------------------------------------------------------------------*/
422 * Set Dram ODT for mission mode and write leveling mode.
424 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
425 * @param[in] OdtMode - Mission mode or write leveling mode
426 * @param[in] ChipSelect - Chip select number
427 * @param[in] TargetCS - Chip select number that is being trained
432 MemRecNSetDramOdtNb (
433 IN OUT MEM_NB_BLOCK *NBPtr,
442 DramTerm = NBPtr->ChannelPtr->Reserved[0];
443 DramTermDyn = NBPtr->ChannelPtr->Reserved[1];
445 if (OdtMode == WRITE_LEVELING_MODE) {
446 if (ChipSelect == TargetCS) {
447 DramTerm = DramTermDyn;
448 MemRecNSetBitFieldNb (NBPtr, BFWrLvOdt, NBPtr->ChannelPtr->PhyWLODT[TargetCS >> 1]);
451 MemRecNSetBitFieldNb (NBPtr, BFDramTerm, DramTerm);
452 MemRecNSetBitFieldNb (NBPtr, BFDramTermDyn, DramTermDyn);
455 /*----------------------------------------------------------------------------
458 *----------------------------------------------------------------------------
461 /* -----------------------------------------------------------------------------*/
464 * This function sends an MRS command
466 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
471 MemRecNSendMrsCmdNb (
472 IN OUT MEM_NB_BLOCK *NBPtr
477 if (NBPtr->IsSupported[CheckClearOnDimmMirror]) {
479 if ((NBPtr->MCTPtr->LogicalCpuid.Revision & AMD_F10_C0) != 0) {
480 if (NBPtr->IsSupported[CheckClearOnDimmMirror]) {
481 if (MemRecNGetBitFieldNb (NBPtr, BFEnDramInit) == 0) {
482 // For C0, if EnDramInit bit is cleared, ODM needs to be cleared before sending MRS
483 MemRecTCtlOnDimmMirrorNb (NBPtr, FALSE);
490 MemRecNSwapBitsNb (NBPtr);
492 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tCS%d MR%d %04x\n",
493 MemRecNGetBitFieldNb (NBPtr, BFMrsChipSel),
494 MemRecNGetBitFieldNb (NBPtr, BFMrsBank),
495 MemRecNGetBitFieldNb (NBPtr, BFMrsAddress));
497 // 1.Set SendMrsCmd=1
498 MemRecNSetBitFieldNb (NBPtr, BFSendMrsCmd, 1);
500 // 2.Wait for SendMrsCmd=0
501 while (MemRecNGetBitFieldNb (NBPtr, BFSendMrsCmd)) {}
503 if (NBPtr->IsSupported[CheckClearOnDimmMirror]) {
505 // Restore ODM if necessary
506 MemRecTCtlOnDimmMirrorNb (NBPtr, TRUE);
511 /* -----------------------------------------------------------------------------*/
514 * This function sends the ZQCL command
516 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
522 IN OUT MEM_NB_BLOCK *NBPtr
525 // 1.Program MrsAddress[10]=1
526 MemRecNSetBitFieldNb (NBPtr, BFMrsAddress, (UINT32) 1 << 10);
529 MemRecNSetBitFieldNb (NBPtr, BFSendZQCmd, 1);
531 // 3.Wait for SendZQCmd=0
532 while (MemRecNGetBitFieldNb (NBPtr, BFSendZQCmd)) {}
534 // 4.Wait 512 MEMCLKs
535 MemRecUWait10ns (128, NBPtr->MemPtr); // 512*2.5ns=1280, wait 1280ns
539 /* -----------------------------------------------------------------------------*/
542 * This function disables/enables F2x[1, 0][5C:40][OnDimmMirror]
544 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
545 * @param[in] SetFlag - Enable or disable flag - TRUE - Enable, FALSE - DISABLE
551 MemRecTCtlOnDimmMirrorNb (
552 IN OUT MEM_NB_BLOCK *NBPtr,
557 UINT32 CSBaseAddrReg;
559 for (Chipsel = 0; Chipsel < MAX_CS_PER_CHANNEL; Chipsel += 2) {
560 CSBaseAddrReg = MemRecNGetBitFieldNb (NBPtr, BFCSBaseAddr1Reg + Chipsel);
561 if ((CSBaseAddrReg & 1) == 1) {
562 if (SetFlag && ((NBPtr->ChannelPtr->DimmMirrorPresent & ((UINT8) 1 << (Chipsel >> 1))) != 0)) {
563 CSBaseAddrReg |= ((UINT32) 1 << BFOnDimmMirror);
565 CSBaseAddrReg &= ~((UINT32) 1 << BFOnDimmMirror);
567 MemRecNSetBitFieldNb (NBPtr, BFCSBaseAddr1Reg + Chipsel, CSBaseAddrReg);
571 /* -----------------------------------------------------------------------------*/
575 * This function swaps bits for OnDimmMirror support
577 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
584 IN OUT MEM_NB_BLOCK *NBPtr
590 ChipSel = (UINT8) MemRecNGetBitFieldNb (NBPtr, BFMrsChipSel);
591 if ((ChipSel & 1) != 0) {
592 MRSReg = MemRecNGetBitFieldNb (NBPtr, BFDramInitRegReg);
593 if ((NBPtr->ChannelPtr->DimmMirrorPresent & (UINT8) 1 << (ChipSel >> 1)) != 0) {
594 MRSReg = (MRSReg & 0xFFFCFE07) | ((MRSReg&0x100A8) << 1) | ((MRSReg&0x20150) >> 1);
595 MemRecNSetBitFieldNb (NBPtr, BFDramInitRegReg, MRSReg);
600 /* -----------------------------------------------------------------------------*/
604 * This function gets the total of sync components for Max Read Latency calculation
606 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
608 * @return Total in 1/2 MEMCLKs
612 MemRecNTotalSyncComponentsClientNb (
613 IN OUT MEM_NB_BLOCK *NBPtr
621 AGESA_TESTPOINT (TpProcMemRcvrCalcLatency , &(NBPtr->MemPtr->StdHeader));
623 // P = P + ((16 + RdPtrInitMin - D18F2x[1,0]78[RdPtrInit]) MOD 16) where RdPtrInitMin = RdPtrInit
626 AddrTmgCtl = MemRecNGetBitFieldNb (NBPtr, BFAddrTmgControl);
627 if (((AddrTmgCtl >> 16) & 0x20) != (AddrTmgCtl & 0x20)) {
631 // IF (DbeGskMemClkAlignMode==01b || (DbeGskMemClkAlignMode==00b && !(AddrCmdSetup==CsOdtSetup==CkeSetup)))
634 // IF (SlowAccessMode==1) THEN P = P + 2
636 // T = T + (0.5 * MemClkPeriod) - 786 ps
637 MemClkPeriod = 1000000 / DDR800_FREQUENCY;
638 T = MemClkPeriod / 2 - 768;
640 // If (AddrCmdSetup==0 && CsOdtSetup==0 && CkeSetup==0)
643 if ((AddrTmgCtl & 0x0202020) == 0) {
649 // P = P + (2 * (D18F2x[1,0]88[Tcl] clocks - 1))
650 P += 2 * 5; // Tcl = 6 clocks
652 // (DisCutThroughMode = 0), so P = P + 3
655 return ((P * MemClkPeriod + 1) / 2) + T;
658 /* -----------------------------------------------------------------------------*/
661 * This function programs the phy registers according to the desired phy VDDIO voltage level
663 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
668 MemRecNPhyVoltageLevelNb (
669 IN OUT MEM_NB_BLOCK *NBPtr
672 BIT_FIELD_NAME BitField;
677 Value = (UINT16) CONVERT_VDDIO_TO_ENCODED (NBPtr->RefPtr->DDR3Voltage) << 3;
679 for (BitField = BFDataRxVioLvl; BitField <= BFCmpVioLvl; BitField++) {
680 if (BitField == BFCmpVioLvl) {
684 MemRecNSetBitFieldNb (NBPtr, BitField, ((MemRecNGetBitFieldNb (NBPtr, BitField) & Mask)) | Value);
688 /* -----------------------------------------------------------------------------*/
692 * This function executes Phy fence training
694 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
700 MemRecNTrainPhyFenceNb (
701 IN OUT MEM_NB_BLOCK *NBPtr
708 if (MemRecNGetBitFieldNb (NBPtr, BFDisDramInterface)) {
712 // 1. BIOS first programs a seed value to the phase recovery
715 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tSeeds: ");
716 for (Byte = 0; Byte < 9; Byte++) {
717 // This includes ECC as byte 8
718 MemRecNSetTrainDlyNb (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (0, Byte), 19);
719 IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", 19);
722 IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tPhyFenceTrEn = 1");
723 // 2. Set F2x[1, 0]9C_x08[PhyFenceTrEn]=1.
724 MemRecNSetBitFieldNb (NBPtr, BFPhyFenceTrEn, 1);
726 MemRecUWait10ns (5000, NBPtr->MemPtr);
728 // 4. Clear F2x[1, 0]9C_x08[PhyFenceTrEn]=0.
729 MemRecNSetBitFieldNb (NBPtr, BFPhyFenceTrEn, 0);
731 // 5. BIOS reads the phase recovery engine registers
732 // F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52.
733 // 6. Calculate the average value of the fine delay and subtract 8.
736 for (Byte = 0; Byte < 9; Byte++) {
737 // This includes ECC as byte 8
738 PREvalue = (UINT8) (0x1F & MemRecNGetTrainDlyNb (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (0, Byte)));
739 Avg = Avg + ((UINT16) PREvalue);
741 Avg = ((Avg + 8) / 9); // round up
744 // 7. Write the value to F2x[1, 0]9C_x0C[PhyFence].
745 MemRecNSetBitFieldNb (NBPtr, BFPhyFence, Avg);
747 // 8. BIOS rewrites F2x[1, 0]9C_x04, DRAM Address/Command Timing Control
748 // Register delays for both channels. This forces the phy to recompute
751 MemRecNSetBitFieldNb (NBPtr, BFAddrTmgControl, MemRecNGetBitFieldNb (NBPtr, BFAddrTmgControl));
754 /* -----------------------------------------------------------------------------*/
757 * This function calculates and programs NB P-state dependent registers
759 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
765 MemRecNProgNbPstateDependentRegClientNb (
766 IN OUT MEM_NB_BLOCK *NBPtr
778 INT32 PartialSumSlotI2x;
779 INT32 RdPtrInitRmdr2x;
781 NclkFid = (UINT8) (MemRecNGetBitFieldNb (NBPtr, BFMainPllOpFreqId) + 0x10);
782 MemClkDid = 2; //BKDG recommended value for DDR800
783 PllMult = 16; //BKDG recommended value for DDR800
784 NclkDiv = (UINT8) MemRecNGetBitFieldNb (NBPtr, BFNbPs0NclkDiv);
786 NclkPeriod = (2500 * NclkDiv) / NclkFid;
787 MemClkPeriod = 1000000 / DDR800_FREQUENCY;
788 NBPtr->NBClkFreq = ((UINT32) NclkFid * 400) / NclkDiv;
790 IDS_HDT_CONSOLE (MEM_FLOW, "\n\tNB P%d Freq: %dMHz\n", 0, NBPtr->NBClkFreq);
791 IDS_HDT_CONSOLE (MEM_FLOW, "\tMemClk Freq: %dMHz\n", DDR800_FREQUENCY);
793 // D18F2x[1,0]78[RdPtrInit] = IF (D18F2x[1,0]94[MemClkFreq] >= 667 MHz) THEN 7 ELSE 8 ENDIF (Llano)
794 // THEN 2 ELSE 3 ENDIF (Ontario)
795 RdPtrInit = NBPtr->FreqChangeParam->RdPtrInitLower667;
796 MemRecNSetBitFieldNb (NBPtr, BFRdPtrInit, RdPtrInit);
797 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tRdPtr: %d\n", RdPtrInit);
799 // Program D18F2x[1,0]F4_x30[DbeGskFifoNumerator] and D18F2x[1,0]F4_x31[DbeGskFifoDenominator].
800 MemRecNSetBitFieldNb (NBPtr, BFDbeGskFifoNumerator, NclkFid * MemClkDid * 16);
801 MemRecNSetBitFieldNb (NBPtr, BFDbeGskFifoDenominator, PllMult * NclkDiv);
803 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDbeGskFifoNumerator: %d\n", NclkFid * MemClkDid * 16);
804 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDbeGskFifoDenominator: %d\n", PllMult * NclkDiv);
806 // Program D18F2x[1,0]F4_x32[DataTxFifoSchedDlyNegSlot1, DataTxFifoSchedDlySlot1,
807 // DataTxFifoSchedDlyNegSlot0, DataTxFifoSchedDlySlot0].
808 // PartialSum = ((7 * NclkPeriod) + (1.5 * MemClkPeriod) + 520ps)*MemClkFrequency - tCWL -
809 // CmdSetup - PtrSeparation - 1. (Llano)
810 // PartialSum = ((5 * NclkPeriod) + MemClkPeriod) + 520ps)*MemClkFrequency - tCWL -
811 // CmdSetup - PtrSeparation - 1. (Ontario)
812 PartialSum2x = NBPtr->FreqChangeParam->NclkPeriodMul2x * NclkPeriod;
813 PartialSum2x += NBPtr->FreqChangeParam->MemClkPeriodMul2x * MemClkPeriod;
814 PartialSum2x += 520 * 2;
816 // PtrSeparation = ((16 + RdPtrInitMin - D18F2x[1,0]78[RdPtrInit]) MOD 16)/2 + RdPtrInitRmdr
817 // RdPtrInitRmdr = (((2.25 * MemClkPeriod) - 1520ps) MOD MemClkPeriod)/MemClkPeriod
818 RdPtrInitRmdr2x = ((NBPtr->FreqChangeParam->SyncTimeMul4x * MemClkPeriod) / 2) - 2 * (NBPtr->FreqChangeParam->TDataPropLower800 + 520);
819 RdPtrInitRmdr2x %= MemClkPeriod;
820 PartialSum2x -= RdPtrInitRmdr2x;
821 PartialSum2x = (PartialSum2x + MemClkPeriod - 1) / MemClkPeriod; // round-up here
822 PartialSum2x -= 2 * 5; //Tcwl + 5
824 if ((MemRecNGetBitFieldNb (NBPtr, BFAddrTmgControl) & 0x0202020) == 0) {
831 // If PartialSumSlotN is positive:
832 // DataTxFifoSchedDlySlotN=CEIL(PartialSumSlotN).
833 // DataTxFifoSchedDlyNegSlotN=0.
834 // Else if PartialSumSlotN is negative:
835 // DataTxFifoSchedDlySlotN=ABS(CEIL(PartialSumSlotN*MemClkPeriod/NclkPeriod)).
836 // DataTxFifoSchedDlyNegSlotN=1.
837 for (i = 0; i < 2; i++) {
838 PartialSumSlotI2x = PartialSum2x;
840 PartialSumSlotI2x += 2;
842 if (PartialSumSlotI2x > 0) {
843 MemRecNSetBitFieldNb (NBPtr, BFDataTxFifoSchedDlyNegSlot0 + i, 0);
844 MemRecNSetBitFieldNb (NBPtr, BFDataTxFifoSchedDlySlot0 + i, (PartialSumSlotI2x + 1) / 2);
845 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDataTxFifoSchedDlySlot%d: %d\n", i, (PartialSumSlotI2x + 1) / 2);
847 MemRecNSetBitFieldNb (NBPtr, BFDataTxFifoSchedDlyNegSlot0 + i, 1);
848 PartialSumSlotI2x = ((-PartialSumSlotI2x) * MemClkPeriod) / (2 * NclkPeriod);
849 MemRecNSetBitFieldNb (NBPtr, BFDataTxFifoSchedDlySlot0 + i, PartialSumSlotI2x);
850 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDataTxFifoSchedDlySlot%d: -%d\n", i, PartialSumSlotI2x);
853 // Program ProcOdtAdv
854 MemRecNSetBitFieldNb (NBPtr, BFProcOdtAdv, 0);
857 /* -----------------------------------------------------------------------------*/
861 * This function reads cache lines continuously using TCB CPG engine
863 * @param[in,out] NBPtr - Pointer to the MEM_NB_BLOCK
864 * @param[in,out] Buffer - Array of bytes to be filled with data read from DRAM
865 * @param[in] Address - System Address [47:16]
866 * @param[in] ClCount - Number of cache lines
871 MemRecNContReadPatternClientNb (
872 IN OUT MEM_NB_BLOCK *NBPtr,
878 // 1. Program D18F2x1C0[RdDramTrainMode]=1.
879 MemRecNSetBitFieldNb (NBPtr, BFRdDramTrainMode, 1);
881 // 2. Program D18F2x1C0[TrainLength] to the appropriate number of cache lines.
882 MemRecNSetBitFieldNb (NBPtr, BFTrainLength, ClCount);
884 // 3. Program the DRAM training address as follows:
885 MemRecNSetBitFieldNb (NBPtr, BFWrTrainAdrPtrLo, (Address >> 6));
887 // 4. Program D18F2x1D0[WrTrainBufAddr]=000h
888 MemRecNSetBitFieldNb (NBPtr, BFWrTrainBufAddr, 0);
890 // 5. Program D18F2x1C0[RdTrainGo]=1.
891 MemRecNSetBitFieldNb (NBPtr, BFRdTrainGo, 1);
893 // 6. Wait for D18F2x1C0[RdTrainGo]=0.
894 while (MemRecNGetBitFieldNb (NBPtr, BFRdTrainGo) != 0) {}
896 // 7. Read D18F2x1E8[TrainCmpSts] and D18F2x1E8[TrainCmpSts2].
898 // 8. Program D18F2x1C0[RdDramTrainMode]=0.
899 MemRecNSetBitFieldNb (NBPtr, BFRdDramTrainMode, 0);
902 /* -----------------------------------------------------------------------------*/
905 * This is function sets the platform specific settings for the systems with UDIMMs configuration
907 * @param[in,out] *MemData Pointer to MEM_DATA_STRUCTURE
908 * @param[in] SocketID Socket number
909 * @param[in] *CurrentChannel Pointer to CH_DEF_STRUCT
911 * @return AGESA_SUCCESS
912 * @return CurrentChannel->DctAddrTmg Address Command Timing Settings for specified channel
913 * @return CurrentChannel->DctOdcCtl Drive Strength settings for specified channel
914 * @return CurrentChannel->Reserved[0] Dram Term for specified channel
915 * @return CurrentChannel->Reserved[1] Dynamic Dram Term for specified channel
916 * @return CurrentChannel->PhyWLODT[0] WL ODT for DIMM0
917 * @return CurrentChannel->PhyWLODT[1] WL ODT for DIMM1
918 * @return CurrentChannel->PhyWLODT[2] WL ODT for DIMM2
919 * @return CurrentChannel->PhyWLODT[3] WL ODT for DIMM3
923 MemRecNGetPsCfgUDIMM3Nb (
924 IN OUT MEM_DATA_STRUCT *MemData,
926 IN OUT CH_DEF_STRUCT *CurrentChannel
936 if ((CurrentChannel->RegDimmPresent != 0) || (CurrentChannel->SODimmPresent != 0)) {
937 return AGESA_UNSUPPORTED;
940 Dimms = CurrentChannel->Dimms;
941 MaxDimmPerCH = RecGetMaxDimmsPerChannel (MemData->ParameterListPtr->PlatformMemoryConfiguration, 0, CurrentChannel->ChannelID);
943 if (MaxDimmPerCH == 1) {
944 return AGESA_UNSUPPORTED;
946 DctOdcCtl = 0x20223323;
947 AddrTmgCTL = 0x00390039;
949 DctOdcCtl = 0x20113222;
950 AddrTmgCTL = 0x00390039;
951 if (CurrentChannel->Loads == 16) {
952 AddrTmgCTL = 0x003B0000;
956 CurrentChannel->DctAddrTmg = AddrTmgCTL;
957 CurrentChannel->DctOdcCtl = DctOdcCtl;
961 DramTerm = 1; // 60 ohms
962 DramTermDyn = 0; // Disable
963 if ((MaxDimmPerCH == 3) && (CurrentChannel->DimmDrPresent != 0)) {
964 DramTermDyn = 1; // 60 ohms
967 DramTerm = 3; // 40 ohms
968 DramTermDyn = 2; // 120 ohms
970 CurrentChannel->Reserved[0] = DramTerm;
971 CurrentChannel->Reserved[1] = DramTermDyn;
975 CurrentChannel->PhyWLODT[0] = 0;
976 CurrentChannel->PhyWLODT[1] = (CurrentChannel->DimmDrPresent != 0) ? 8 : 2;
978 CurrentChannel->PhyWLODT[0] = 3;
979 CurrentChannel->PhyWLODT[1] = 3;
981 CurrentChannel->PhyWLODT[2] = 0;
982 CurrentChannel->PhyWLODT[3] = 0;
984 return AGESA_SUCCESS;
987 /* -----------------------------------------------------------------------------*/
990 * This is function sets the platform specific settings for the systems with SODIMMs configuration
992 * @param[in,out] *MemData Pointer to MEM_DATA_STRUCTURE
993 * @param[in] SocketID Socket number
994 * @param[in] *CurrentChannel Pointer to CH_DEF_STRUCT
996 * @return AGESA_SUCCESS
997 * @return CurrentChannel->DctAddrTmg Address Command Timing Settings for specified channel
998 * @return CurrentChannel->DctOdcCtl Drive Strength settings for specified channel
999 * @return CurrentChannel->Reserved[0] Dram Term for specified channel
1000 * @return CurrentChannel->Reserved[1] Dynamic Dram Term for specified channel
1001 * @return CurrentChannel->PhyWLODT[0] WL ODT for DIMM0
1002 * @return CurrentChannel->PhyWLODT[1] WL ODT for DIMM1
1003 * @return CurrentChannel->PhyWLODT[2] WL ODT for DIMM2
1004 * @return CurrentChannel->PhyWLODT[3] WL ODT for DIMM3
1008 MemRecNGetPsCfgSODIMM3Nb (
1009 IN OUT MEM_DATA_STRUCT *MemData,
1011 IN OUT CH_DEF_STRUCT *CurrentChannel
1021 if (CurrentChannel->SODimmPresent != CurrentChannel->ChDimmValid) {
1022 return AGESA_UNSUPPORTED;
1025 Dimms = CurrentChannel->Dimms;
1026 MaxDimmPerCH = RecGetMaxDimmsPerChannel (MemData->ParameterListPtr->PlatformMemoryConfiguration, 0, CurrentChannel->ChannelID);
1028 if (MaxDimmPerCH == 1) {
1029 DctOdcCtl = 0x00113222;
1032 DctOdcCtl = 0x00223323;
1033 AddrTmgCTL = 0x00000039;
1035 DctOdcCtl = 0x00113222;
1039 CurrentChannel->DctAddrTmg = AddrTmgCTL;
1040 CurrentChannel->DctOdcCtl = DctOdcCtl;
1044 DramTerm = 2; // 120 ohms
1045 DramTermDyn = 0; // Disable
1046 if (MaxDimmPerCH == 2) {
1047 DramTerm = 1; // 60 ohms
1050 DramTerm = 3; // 40 ohms
1051 DramTermDyn = 2; // 120 ohms
1053 CurrentChannel->Reserved[0] = DramTerm;
1054 CurrentChannel->Reserved[1] = DramTermDyn;
1058 if (MaxDimmPerCH == 1) {
1059 CurrentChannel->PhyWLODT[0] = (CurrentChannel->DimmDrPresent != 0) ? 4 : 1;
1060 CurrentChannel->PhyWLODT[1] = 0;
1062 CurrentChannel->PhyWLODT[0] = 0;
1063 CurrentChannel->PhyWLODT[1] = (CurrentChannel->DimmDrPresent != 0) ? 8 : 2;
1066 CurrentChannel->PhyWLODT[0] = 3;
1067 CurrentChannel->PhyWLODT[1] = 3;
1069 CurrentChannel->PhyWLODT[2] = 0;
1070 CurrentChannel->PhyWLODT[3] = 0;
1072 return AGESA_SUCCESS;
1075 /* -----------------------------------------------------------------------------*/
1078 * This is function sets the platform specific settings for the systems with RDIMMs configuration
1080 * @param[in,out] *MemData Pointer to MEM_DATA_STRUCTURE
1081 * @param[in] SocketID Socket number
1082 * @param[in] *CurrentChannel Pointer to CH_DEF_STRUCT
1084 * @return AGESA_SUCCESS
1085 * @return CurrentChannel->DctAddrTmg Address Command Timing Settings for specified channel
1086 * @return CurrentChannel->DctOdcCtl Drive Strength settings for specified channel
1087 * @return CurrentChannel->Reserved[0] Dram Term for specified channel
1088 * @return CurrentChannel->Reserved[1] Dynamic Dram Term for specified channel
1089 * @return CurrentChannel->PhyWLODT[0] WL ODT for DIMM0
1090 * @return CurrentChannel->PhyWLODT[1] WL ODT for DIMM1
1091 * @return CurrentChannel->PhyWLODT[2] WL ODT for DIMM2
1092 * @return CurrentChannel->PhyWLODT[3] WL ODT for DIMM3
1097 MemRecNGetPsCfgRDIMM3Nb (
1098 IN OUT MEM_DATA_STRUCT *MemData,
1100 IN OUT CH_DEF_STRUCT *CurrentChannel
1103 STATIC CONST ADV_R_PSCFG_WL_ODT_ENTRY RecPSCfg2DIMMsWlODT[] = {
1104 {SR_DIMM0, {0x01, 0x00, 0x00, 0x00}, 1},
1105 {DR_DIMM0, {0x04, 0x00, 0x00, 0x00}, 1},
1106 {QR_DIMM0, {0x05, 0x00, 0x00, 0x00}, 1},
1107 {SR_DIMM1, {0x00, 0x02, 0x00, 0x00}, 1},
1108 {DR_DIMM1, {0x00, 0x08, 0x00, 0x00}, 1},
1109 {QR_DIMM1, {0x00, 0x0A, 0x00, 0x00}, 1},
1110 {SR_DIMM0 + DR_DIMM0 + SR_DIMM1 + DR_DIMM1, {0x03, 0x03, 0x00, 0x00}, 2},
1111 {SR_DIMM0 + QR_DIMM1, {0x0B, 0x03, 0x00, 0x09}, 2},
1112 {DR_DIMM0 + QR_DIMM1, {0x0B, 0x03, 0x00, 0x09}, 2},
1113 {QR_DIMM0 + SR_DIMM1, {0x03, 0x07, 0x06, 0x00}, 2},
1114 {QR_DIMM0 + DR_DIMM1, {0x03, 0x07, 0x06, 0x00}, 2},
1115 {QR_DIMM0 + QR_DIMM1, {0x0B, 0x07, 0x0E, 0x0D}, 2}
1117 STATIC CONST ADV_R_PSCFG_WL_ODT_ENTRY RecPSCfg3DIMMsWlODT[] = {
1118 {SR_DIMM2 + DR_DIMM2, {0x00, 0x00, 0x04, 0x00}, 1},
1119 {SR_DIMM0 + DR_DIMM0, {0x01, 0x02, 0x00, 0x00}, 1},
1120 {SR_DIMM0 + DR_DIMM0 + SR_DIMM2 + DR_DIMM2, {0x05, 0x00, 0x05, 0x00}, 2},
1121 {SR_DIMM0 + DR_DIMM0 + SR_DIMM1 + DR_DIMM1 + SR_DIMM2 + DR_DIMM2, {0x07, 0x07, 0x07, 0x00}, 3},
1122 {QR_DIMM1, {0x00, 0x0A, 0x00, 0x0A}, 1},
1123 {QR_DIMM1 + SR_DIMM2 + DR_DIMM2, {0x00, 0x06, 0x0E, 0x0C}, 2},
1124 {SR_DIMM0 + DR_DIMM0 + QR_DIMM1, {0x0B, 0x03, 0x00, 0x09}, 2},
1125 {SR_DIMM0 + DR_DIMM0 + QR_DIMM1 + SR_DIMM2 + DR_DIMM2, {0x0F, 0x07, 0x0F, 0x0D}, 3}
1127 STATIC CONST ADV_R_PSCFG_WL_ODT_ENTRY RecPSCfg4DIMMsWlODT[] = {
1128 {ANY_DIMM3, {0x00, 0x00, 0x00, 0x08}, 1},
1129 {ANY_DIMM2 + ANY_DIMM3, {0x00, 0x00, 0x0C, 0x0C}, 2},
1130 {ANY_DIMM1 + ANY_DIMM2 + ANY_DIMM3, {0x00, 0x0E, 0x0E, 0x0E}, 3},
1131 {ANY_DIMM0 + ANY_DIMM1 + ANY_DIMM2 + ANY_DIMM3, {0x0F, 0x0F, 0x0F, 0x0F}, 4}
1137 UINT8 DimmQrPresent;
1143 UINT16 DIMMRankType;
1144 UINT16 _DIMMRankType_;
1147 UINT8 PSCfgWlODTSize;
1148 CONST ADV_R_PSCFG_WL_ODT_ENTRY *PSCfgWlODTPtr;
1150 if (CurrentChannel->RegDimmPresent != CurrentChannel->ChDimmValid) {
1151 return AGESA_UNSUPPORTED;
1154 DIMMRankType = MemRecNGetPsRankType (CurrentChannel);
1155 MaxDimmPerCH = RecGetMaxDimmsPerChannel (MemData->ParameterListPtr->PlatformMemoryConfiguration, 0, CurrentChannel->ChannelID);
1156 Dimms = CurrentChannel->Dimms;
1157 PSCfgWlODTPtr = RecPSCfg2DIMMsWlODT;
1158 PSCfgWlODTSize = GET_SIZE_OF (RecPSCfg2DIMMsWlODT);
1159 PhyWLODT[0] = PhyWLODT[1] = PhyWLODT[2] = PhyWLODT[3] = 0xFF;
1160 DimmQrPresent = CurrentChannel->DimmQrPresent;
1162 if (MaxDimmPerCH == 4) {
1163 AddrTmgCTL = (Dimms > 2) ? 0x002F0000 : 0;
1164 DctOdcCtl = (Dimms == 1) ? 0x20113222 : 0x20223222;
1165 PSCfgWlODTPtr = RecPSCfg4DIMMsWlODT;
1166 PSCfgWlODTSize = GET_SIZE_OF (RecPSCfg4DIMMsWlODT);
1167 } else if (MaxDimmPerCH == 3) {
1169 DctOdcCtl = 0x20223222;
1171 AddrTmgCTL = 0x00380038;
1172 DctOdcCtl = 0x20113222;
1175 DctOdcCtl = 0x20113222;
1177 PSCfgWlODTPtr = RecPSCfg3DIMMsWlODT;
1178 PSCfgWlODTSize = GET_SIZE_OF (RecPSCfg3DIMMsWlODT);
1179 } else if (MaxDimmPerCH == 2) {
1181 DctOdcCtl = 0x20223222;
1182 if ((Dimms == 1) && (DimmQrPresent == 0)) {
1183 DctOdcCtl = 0x20113222;
1187 DctOdcCtl = (DimmQrPresent == 0) ? 0x20113222 : 0x20223222;
1189 CurrentChannel->DctAddrTmg = AddrTmgCTL;
1190 CurrentChannel->DctOdcCtl = DctOdcCtl;
1194 DramTerm = 1; // 60 ohms
1195 DramTermDyn = 0; // Disable
1196 if (DimmQrPresent != 0) {
1197 DramTermDyn = 2; // 120 ohms
1200 DramTerm = 3; // 40 ohms
1201 DramTermDyn = 2; // 120 ohms
1202 if (DimmQrPresent != 0) {
1203 DramTerm = 1; // 60 ohms
1206 CurrentChannel->Reserved[0] = DramTerm;
1207 CurrentChannel->Reserved[1] = DramTermDyn;
1210 for (i = 0; i < PSCfgWlODTSize; i++, PSCfgWlODTPtr++) {
1211 if (Dimms != PSCfgWlODTPtr->Dimms) {
1215 _DIMMRankType_ = DIMMRankType & PSCfgWlODTPtr->DIMMRankType;
1216 for (j = 0; j < MAX_DIMMS_PER_CHANNEL; j++) {
1217 if ((_DIMMRankType_ & (UINT16) 0x0F << (j << 2)) != 0) {
1221 if (DimmTpMatch == PSCfgWlODTPtr->Dimms) {
1222 PhyWLODT[0] = PSCfgWlODTPtr->PhyWrLvOdt[0];
1223 PhyWLODT[1] = PSCfgWlODTPtr->PhyWrLvOdt[1];
1224 PhyWLODT[2] = PSCfgWlODTPtr->PhyWrLvOdt[2];
1225 PhyWLODT[3] = PSCfgWlODTPtr->PhyWrLvOdt[3];
1229 CurrentChannel->PhyWLODT[0] = PhyWLODT[0];
1230 CurrentChannel->PhyWLODT[1] = PhyWLODT[1];
1231 CurrentChannel->PhyWLODT[2] = PhyWLODT[2];
1232 CurrentChannel->PhyWLODT[3] = PhyWLODT[3];
1234 return AGESA_SUCCESS;
1238 /* -----------------------------------------------------------------------------*/
1242 * This function returns the max dimms for a given memory channel on a given
1243 * processor. It first searches the platform override table for the max dimms
1244 * value. If it is not provided, the AGESA default value is returned. The target
1245 * socket must be a valid present socket.
1247 * @param[in] PlatformMemoryConfiguration - Platform config table
1248 * @param[in] SocketID - ID of the processor that owns the channel
1249 * @param[in] ChannelID - Channel to get max dimms for
1252 * @return UINT8 - Max Number of Dimms for that channel
1255 RecGetMaxDimmsPerChannel (
1256 IN PSO_TABLE *PlatformMemoryConfiguration,
1261 UINT8 *DimmsPerChPtr;
1264 DimmsPerChPtr = MemRecFindPSOverrideEntry (PlatformMemoryConfiguration, PSO_MAX_DIMMS, SocketID, ChannelID, 0);
1265 if (DimmsPerChPtr != NULL) {
1266 MaxDimmPerCH = *DimmsPerChPtr;
1271 return MaxDimmPerCH;
1274 /* -----------------------------------------------------------------------------*/
1277 * This is the default return function of the ARDK block. The function always
1278 * returns AGESA_UNSUPPORTED
1280 * @param[in,out] *MemData Pointer to MEM_DATA_STRUCTURE
1281 * @param[in] SocketID Socket number
1282 * @param[in] *CurrentChannel Pointer to CH_DEF_STRUCT
1284 * @return AGESA_UNSUPPORTED AGESA status indicating that default is unsupported
1289 MemRecNGetPsCfgDef (
1290 IN OUT MEM_DATA_STRUCT *MemData,
1292 IN OUT CH_DEF_STRUCT *CurrentChannel
1295 return AGESA_UNSUPPORTED;
1298 /* -----------------------------------------------------------------------------*/
1301 * This function returns the rank type map of a channel.
1303 * @param[in] *CurrentChannel Pointer to CH_DEF_STRUCT
1305 * @return UINT16 - The map of rank type.
1309 MemRecNGetPsRankType (
1310 IN CH_DEF_STRUCT *CurrentChannel
1314 UINT16 DIMMRankType;
1317 for (i = 0; i < MAX_DIMMS_PER_CHANNEL; i++) {
1318 if ((CurrentChannel->DimmQrPresent & (UINT8) 1 << i) != 0) {
1320 DIMMRankType |= (UINT16) 4 << (i << 2);
1322 } else if ((CurrentChannel->DimmDrPresent & (UINT8) 1 << i) != 0) {
1323 DIMMRankType |= (UINT16) 2 << (i << 2);
1324 } else if ((CurrentChannel->DimmSRPresent & (UINT8) 1 << i) != 0) {
1325 DIMMRankType |= (UINT16) 1 << (i << 2);
1328 return DIMMRankType;
1332 MemRecNcmnGetSetTrainDlyClientNb (
1333 IN OUT MEM_NB_BLOCK *NBPtr,
1335 IN TRN_DLY_TYPE TrnDly,
1347 Dimm = DRBN_DIMM (DrbnVar);
1348 Byte = DRBN_BYTE (DrbnVar);
1351 ASSERT (Byte <= ECC_DLY);
1354 // LN and ON do not support ECC delay, so:
1359 // On read, redirect to byte 0 to correct fence averaging
1365 case AccessRcvEnDly:
1368 case AccessWrDqsDly:
1371 case AccessWrDatDly:
1374 case AccessRdDqsDly:
1377 case AccessPhRecDly:
1386 case AccessRcvEnDly:
1387 case AccessWrDqsDly:
1388 Index += (Dimm * 3);
1397 Offset = 16 * (Byte % 2);
1400 case AccessRdDqsDly:
1401 case AccessWrDatDly:
1402 Index += (Dimm * 0x100);
1403 // break is not being used here because AccessRdDqsDly and AccessWrDatDly also need
1404 // to run AccessPhRecDly sequence.
1405 case AccessPhRecDly:
1406 Index += (Byte / 4);
1407 Offset = 8 * (Byte % 4);
1415 MemRecNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address);
1416 Value = MemRecNGetBitFieldNb (NBPtr, BFDctAddlDataReg);
1419 if (TrnDly == AccessPhRecDly) {
1420 Value = NBPtr->DctCachePtr->PhRecReg[Index & 0x03];
1423 Value = ((UINT32)Field << Offset) | (Value & (~((UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF) << Offset)));
1424 MemRecNSetBitFieldNb (NBPtr, BFDctAddlDataReg, Value);
1425 Address |= DCT_ACCESS_WRITE;
1426 MemRecNSetBitFieldNb (NBPtr, BFDctAddlOffsetReg, Address);
1428 if (TrnDly == AccessPhRecDly) {
1429 NBPtr->DctCachePtr->PhRecReg[Index & 0x03] = Value;
1431 // Gross WrDatDly and WrDqsDly cannot be larger than 4
1432 ASSERT (((TrnDly == AccessWrDatDly) || (TrnDly == AccessWrDqsDly)) ? (NBPtr->IsSupported[WLNegativeDelay] || (Field < 0xA0)) : TRUE);
1434 Value = (Value >> Offset) & (UINT32) ((TrnDly == AccessRcvEnDly) ? 0x1FF : 0xFF);
1441 /* -----------------------------------------------------------------------------*/
1445 * This function reads cache lines continuously using PRBS engine
1447 * @param[in,out] NBPtr - Pointer to the MEM_NB_BLOCK
1448 * @param[in,out] Buffer - Array of bytes to be filled with data read from DRAM
1449 * @param[in] Address - System Address [47:16]
1450 * @param[in] ClCount - Number of cache lines
1455 MemRecNContReadPatternUnb (
1456 IN OUT MEM_NB_BLOCK *NBPtr,
1462 MemRecNCommonReadWritePatternUnb (NBPtr, CMD_TYPE_READ, ClCount);
1465 /* -----------------------------------------------------------------------------*/
1468 * This function generates a continuous stream of writes to DRAM using the
1469 * Unified Northbridge Reliable Read/Write Engine.
1471 * @param[in,out] NBPtr - Pointer to the MEM_NB_BLOCK
1472 * @param[in,out] Address - Unused by this function
1473 * @param[in] Pattern - Unused by this function
1474 * @param[in] ClCount - Number of cache lines to write
1479 MemRecNContWritePatternUnb (
1480 IN OUT MEM_NB_BLOCK *NBPtr,
1486 MemRecNCommonReadWritePatternUnb (NBPtr, CMD_TYPE_WRITE, ClCount);
1489 /* -----------------------------------------------------------------------------*/
1492 * This function generates either read or write DRAM cycles for training
1495 * @param[in,out] NBPtr - Pointer to the MEM_NB_BLOCK
1496 * @param[in] CmdType - Read/Write
1497 * @param[in] ClCount - Number of cache lines to write
1503 MemRecNCommonReadWritePatternUnb (
1504 IN OUT MEM_NB_BLOCK *NBPtr,
1509 MEM_TECH_BLOCK *TechPtr;
1511 TechPtr = NBPtr->TechPtr;
1513 // Enable PRBS, also set ResetAllErr
1514 MemRecNSetBitFieldNb (NBPtr, BFCmdTestEnable, 3);
1516 // Send activate command
1517 MemRecNSetBitFieldNb (NBPtr, BFDramCmd2Reg, (UINT32) 1 << (TechPtr->ChipSel + 22) | ((UINT32) 1 << 31));
1518 MemRecUWait10ns (750, NBPtr->MemPtr);
1520 // Setup test address
1521 MemRecNSetBitFieldNb (NBPtr, BFTgtChipSelectA, TechPtr->ChipSel);
1522 MemRecNSetBitFieldNb (NBPtr, BFDataPrbsSeed, PRBS_SEED_256);
1523 MemRecNSetBitFieldNb (NBPtr, BFCmdCount, ClCount);
1525 // Select read or write command
1526 MemRecNSetBitFieldNb (NBPtr, BFCmdType, CmdType);
1528 // Send command and wait for completion
1529 MemRecNSetBitFieldNb (NBPtr, BFSendCmd, 1);
1530 while (MemRecNGetBitFieldNb (NBPtr, BFTestStatus) == 0) {}
1531 while (MemRecNGetBitFieldNb (NBPtr, BFCmdSendInProg) != 0) {}
1532 MemRecNSetBitFieldNb (NBPtr, BFSendCmd, 0);
1534 // Send precharge all command
1535 MemRecNSetBitFieldNb (NBPtr, BFDramCmd2Reg, (UINT32) 1 << (TechPtr->ChipSel + 22) | ((UINT32) 1 << 30));
1536 MemRecUWait10ns (750, NBPtr->MemPtr);
1539 MemRecNSetBitFieldNb (NBPtr, BFCmdTestEnable, 0);
1542 /* -----------------------------------------------------------------------------*/
1545 * This function checks the Error status bits for comparison results using PRBS
1547 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1548 * @param[in] Buffer[] - Not used in this implementation
1549 * @param[in] Pattern[] - Not used in this implementation
1550 * @param[in] ByteCount - Not used in this implementation
1552 * @return PASS - Bitmap of results of comparison
1556 MemRecNCompareTestPatternUnb (
1557 IN OUT MEM_NB_BLOCK *NBPtr,
1565 UINT32 NibbleErrSts;
1567 NibbleErrSts = MemRecNGetBitFieldNb (NBPtr, BFNibbleErrSts);
1570 for (i = 0; i < 8; i++) {
1571 Pass |= ((NibbleErrSts & 0x03) > 0 ) ? (1 << i) : 0;