blob: 6aa4868b7fee6a52779e99c84bceaa428c340e3f [file] [log] [blame]
zbao7d94cf92012-07-02 14:19:14 +08001/* $NoKeywords:$ */
2/**
3 * @file
4 *
5 * mndct.c
6 *
7 * Common Northbridge DCT support
8 *
9 * @xrefitem bom "File Content Label" "Release Content"
10 * @e project: AGESA
11 * @e sub-project: (Mem/NB)
12 * @e \$Revision: 64574 $ @e \$Date: 2012-01-25 01:01:51 -0600 (Wed, 25 Jan 2012) $
13 *
14 **/
15/*****************************************************************************
16*
Siyuan Wang641f00c2013-06-08 11:50:55 +080017 * Copyright (c) 2008 - 2012, Advanced Micro Devices, Inc.
18 * All rights reserved.
19 *
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.
30 *
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.
zbao7d94cf92012-07-02 14:19:14 +080041* ***************************************************************************
42*
43*/
44
45
46/*
47 *----------------------------------------------------------------------------
48 * MODULES USED
49 *
50 *----------------------------------------------------------------------------
51 */
52
53
54
55#include "AGESA.h"
56#include "amdlib.h"
57#include "Ids.h"
58#include "mport.h"
59#include "mm.h"
60#include "mn.h"
61#include "mt.h"
62#include "mu.h"
63#include "mftds.h"
64#include "merrhdl.h"
65#include "cpuFamilyTranslation.h"
66#include "OptionMemory.h"
67#include "PlatformMemoryConfiguration.h"
68#include "Filecode.h"
69CODE_GROUP (G1_PEICC)
70RDATA_GROUP (G1_PEICC)
71
72#define FILECODE PROC_MEM_NB_MNDCT_FILECODE
73/*----------------------------------------------------------------------------
74 * DEFINITIONS AND MACROS
75 *
76 *----------------------------------------------------------------------------
77 */
78#define UNUSED_CLK 4
79
80/*----------------------------------------------------------------------------
81 * TYPEDEFS AND STRUCTURES
82 *
83 *----------------------------------------------------------------------------
84 */
85
86/*----------------------------------------------------------------------------
87 * PROTOTYPES OF LOCAL FUNCTIONS
88 *
89 *----------------------------------------------------------------------------
90 */
91
92VOID
93STATIC
94MemNAfterStitchMemNb (
95 IN OUT MEM_NB_BLOCK *NBPtr
96 );
97
98UINT8
99MemNGet1KTFawTkNb (
100 IN UINT8 k
101 );
102
103UINT8
104MemNGet2KTFawTkNb (
105 IN UINT8 k
106 );
107
108VOID
109STATIC
110MemNQuarterMemClk2NClkNb (
111 IN OUT MEM_NB_BLOCK *NBPtr,
112 IN OUT UINT16 *SubTotalPtr
113 );
114
115/*----------------------------------------------------------------------------
116 * EXPORTED FUNCTIONS
117 *
118 *----------------------------------------------------------------------------
119 */
120
121extern BUILD_OPT_CFG UserOptions;
122
123/* -----------------------------------------------------------------------------*/
124/**
125 *
126 *
127 * This function combines all the memory into a contiguous map.
128 * Requires that Mask values for each bank be programmed first and that
129 * the chip-select population indicator is correctly set.
130 *
131 *
132 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
133 *
134 * @return TRUE - An Error value lower than AGESA_FATAL may have occurred
135 * @return FALSE - An Error value greater than or equal to AGESA_FATAL may have occurred
136 */
137
138BOOLEAN
139MemNStitchMemoryNb (
140 IN OUT MEM_NB_BLOCK *NBPtr
141 )
142{
143 BOOLEAN DSpareEn;
144 UINT32 NxtCSBase;
145 UINT32 CurCSBase;
146 UINT32 CsSize;
147 UINT32 BiggestBank;
148 UINT8 p;
149 UINT8 q;
150 UINT8 BiggestDimm;
151 MEM_PARAMETER_STRUCT *RefPtr;
152 DIE_STRUCT *MCTPtr;
153 DCT_STRUCT *DCTPtr;
154 RefPtr = NBPtr->RefPtr;
155 MCTPtr = NBPtr->MCTPtr;
156 DCTPtr = NBPtr->DCTPtr;
157 DSpareEn = FALSE;
158 if (NBPtr->IsSupported[SetSpareEn]) {
159 DSpareEn = FALSE;
160 if (RefPtr->GStatus[GsbEnDIMMSpareNW]) {
161 DSpareEn = TRUE;
162 }
163 }
164
165 DCTPtr->Timings.CsEnabled = 0;
166 NxtCSBase = 0;
167 for (p = 0; p < MAX_CS_PER_CHANNEL; p++) {
168 BiggestBank = 0;
169 BiggestDimm = 0;
170 for (q = 0; q < MAX_CS_PER_CHANNEL; q++) {
171 if (((DCTPtr->Timings.CsPresent & ~DCTPtr->Timings.CsTestFail) & ((UINT16)1 << q)) != 0) {
172 if ((MemNGetBitFieldNb (NBPtr, BFCSBaseAddr0Reg + q) & 7) == 0) {
173 // (CSEnable|Spare==1)bank is not enabled yet
174 CsSize = MemNGetBitFieldNb (NBPtr, BFCSMask0Reg + (q >> 1));
175 if (CsSize != 0) {
176 CsSize += ((UINT32)1 << 19);
177 CsSize &= 0xFFF80000;
178 }
179 if (CsSize > BiggestBank) {
180 BiggestBank = CsSize;
181 BiggestDimm = q;
182 }
183 }
184 }
185 }
186
187 if (BiggestBank != 0) {
188 CurCSBase = NxtCSBase;
189 if (NBPtr->IsSupported[CheckSpareEn]) {
190 if (DSpareEn) {
191 CurCSBase = ((UINT32)1 << BFSpare);
192 DSpareEn = FALSE;
193 } else {
194 CurCSBase |= ((UINT32)1 << BFCSEnable);
195 NxtCSBase += BiggestBank;
196 }
197 } else {
198 CurCSBase |= ((UINT32)1 << BFCSEnable);
199 NxtCSBase += BiggestBank;
200 }
201 if ((BiggestDimm & 1) != 0) {
202 if (!(MCTPtr->Status[SbLrdimms])) {
203 // For LRDIMMS, On Dimm Mirroring is enabled after SDI
204 if ((DCTPtr->Timings.DimmMirrorPresent & (1 << (BiggestDimm >> 1))) != 0) {
205 CurCSBase |= ((UINT32)1 << BFOnDimmMirror);
206 }
207 }
208 }
209 MemNSetBitFieldNb (NBPtr, BFCSBaseAddr0Reg + BiggestDimm, CurCSBase);
210 DCTPtr->Timings.CsEnabled |= (1 << BiggestDimm);
211 }
212 if ((DCTPtr->Timings.CsTestFail & ((UINT16)1 << p)) != 0) {
213 IDS_HDT_CONSOLE (MEM_FLOW, "Node %d Dct %d exclude CS %d\n", NBPtr->Node, NBPtr->Dct, p);
214 MemNSetBitFieldNb (NBPtr, (BFCSBaseAddr0Reg + p), (UINT32)1 << BFTestFail);
215 }
216 }
217
218 if (NxtCSBase != 0) {
219 DCTPtr->Timings.DctMemSize = NxtCSBase >> 8; // Scale base address from [39:8] to [47:16]
220 MemNAfterStitchMemNb (NBPtr);
221 }
222
223 return (BOOLEAN) (MCTPtr->ErrCode < AGESA_FATAL);
224}
225
226/* -----------------------------------------------------------------------------*/
227/**
228 *
229 *
230 * This function gets platform specific config/timing values from the interface layer and
231 * programs them into DCT.
232 *
233 *
234 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
235 *
236 * @return TRUE - An Error value lower than AGESA_FATAL may have occurred
237 * @return FALSE - An Error value greater than or equal to AGESA_FATAL may have occurred
238 */
239
240BOOLEAN
241MemNPlatformSpecNb (
242 IN OUT MEM_NB_BLOCK *NBPtr
243 )
244{
245 CONST BIT_FIELD_NAME ChipletPDRegs[] = {
246 BFPhyClkConfig0,
247 BFPhyClkConfig3,
248 BFPhyClkConfig1,
249 BFPhyClkConfig2
250 };
251 CONST UINT8 ChipletPDClkDisMap[][2] = {
252 //F2[1, 0]x9C_x0D0F2030 -> F2x[1, 0]88[MemClkDis[1:0]]
253 {0, 1},
254 //F2[1, 0]x9C_x0D0F2330 -> F2x[1, 0]88[MemClkDis[7:6]]
255 {6, 7},
256 //F2x09C_x0D0F2130 -> F2x88[MemClkDis[5:4]]
257 {4, 5},
258 //F2x09C_x0D0F2230 -> F2x88[MemClkDis[3:2]]
259 {2, 3},
260 //F2x19C_x0D0F2130 -> F2x188[MemClkDis[5:2]]
261 {2, 5},
262 //F2x19C_x0D0F2230 -> F2x188[MemClkDis[4:3]]
263 {3, 4}
264 };
265
266 UINT8 MemClkDis;
267 UINT8 i;
268 UINT8 MemoryAllClocks;
269 UINT8 *MemClkDisMap;
270 UINT16 CsPresent;
271 UINT8 RegIndex;
272 UINT8 Cs1;
273 UINT8 Cs2;
274
275 if (!MemNGetPlatformCfgNb (NBPtr)) {
276 IDS_ERROR_TRAP;
277 }
278
279 if (!NBPtr->PsPtr->MemPDoPs (NBPtr)) {
280 IDS_ERROR_TRAP;
281 }
282 MemNProgramPlatformSpecNb (NBPtr);
283
284 MemProcessConditionalOverrides (NBPtr->RefPtr->PlatformMemoryConfiguration, NBPtr, PSO_ACTION_ODT, ALL_DIMMS);
285
286 if (NBPtr->MCTPtr->GangedMode) {
287 MemNSwitchDCTNb (NBPtr, 1);
288 if (!MemNGetPlatformCfgNb (NBPtr)) {
289 IDS_ERROR_TRAP;
290 }
291 MemNProgramPlatformSpecNb (NBPtr);
292 MemNSwitchDCTNb (NBPtr, 0);
293 }
294
295 //======================================================================
296 // Disable unused MemClk to save power
297 //======================================================================
298 //
299 MemClkDis = 0;
300 MemoryAllClocks = UserOptions.CfgMemoryAllClocksOn;
301 IDS_OPTION_HOOK (IDS_ALL_MEMORY_CLOCK, &MemoryAllClocks, &(NBPtr->MemPtr->StdHeader));
302 if (!MemoryAllClocks) {
303 // Special Jedec SPD diagnostic bit - "enable all clocks"
304 if (!NBPtr->MCTPtr->Status[SbDiagClks]) {
305 MemClkDisMap = FindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PSO_MEMCLK_DIS, NBPtr->MCTPtr->SocketId, MemNGetSocketRelativeChannelNb (NBPtr, NBPtr->Dct, 0), 0,
306 &(NBPtr->MCTPtr->LogicalCpuid), &(NBPtr->MemPtr->StdHeader));
307 if (MemClkDisMap == NULL) {
308 MemClkDisMap = NBPtr->ChannelPtr->MemClkDisMap;
309 }
310
311 // Turn off the unused CS clocks
312 CsPresent = NBPtr->DCTPtr->Timings.CsPresent;
313
314 if (NBPtr->IsSupported[CheckMemClkCSPresent]) {
315 if (NBPtr->ChannelPtr->RegDimmPresent != 0) {
316 // All DDR3 RDIMM use only one MEMCLOCK from edge finger to the register
317 // regardless of how many Ranks are on the DIMM (Single, Dual or Quad)
318 CsPresent = (CsPresent | (CsPresent >> 1)) & 0x5555;
319 }
320 }
321 for (i = 0; i < 8; i++) {
322 if ((CsPresent & MemClkDisMap[i]) == 0) {
323 MemClkDis |= (UINT8) (1 << i);
324 }
325 }
326 //Chiplet power down
327 for (RegIndex = 0; RegIndex < GET_SIZE_OF (ChipletPDRegs); RegIndex++) {
328 if ((NBPtr->Dct == 1) && (RegIndex >= 2)) {
329 Cs1 = MemClkDisMap[ChipletPDClkDisMap[RegIndex + 2][0]];
330 Cs2 = MemClkDisMap[ChipletPDClkDisMap[RegIndex + 2][1]];
331 } else {
332 Cs1 = MemClkDisMap[ChipletPDClkDisMap[RegIndex][0]];
333 Cs2 = MemClkDisMap[ChipletPDClkDisMap[RegIndex][1]];
334 }
335 if ((CsPresent & (UINT16) (Cs1 | Cs2)) == 0) {
336 MemNSetBitFieldNb (NBPtr, ChipletPDRegs[RegIndex], (MemNGetBitFieldNb (NBPtr, ChipletPDRegs[RegIndex]) | 0x10));
337 }
338 }
339 }
340 }
341 MemNSetBitFieldNb (NBPtr, BFMemClkDis, MemClkDis);
342
343 AGESA_TESTPOINT (TPProcMemPhyCompensation, &(NBPtr->MemPtr->StdHeader));
344 NBPtr->MemNInitPhyComp (NBPtr);
345
346 MemProcessConditionalOverrides (NBPtr->RefPtr->PlatformMemoryConfiguration, NBPtr, PSO_ACTION_SLEWRATE, ALL_DIMMS);
347
348 // Program DramTerm for DDR2
349 if ((MemNGetBitFieldNb (NBPtr, BFDdr3Mode)) == 0) {
350 MemNSetBitFieldNb (NBPtr, BFDramTerm, NBPtr->PsPtr->DramTerm);
351 } else {
352 // Dynamic Dynamic DramTerm for DDR3
353 // Dram Term for DDR3 may vary based on chip selects
354 MemNSetBitFieldNb (NBPtr, BFDramTermDyn, NBPtr->PsPtr->DynamicDramTerm);
355 }
356
357 MemFInitTableDrive (NBPtr, MTAfterPlatformSpec);
358
359 return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL);
360}
361
362/* -----------------------------------------------------------------------------*/
363/**
364 *
365 *
366 * This function gets platform specific config/timing values from the interface layer and
367 * programs them into DCT.
368 *
369 *
370 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
371 *
372 * @return TRUE - An Error value lower than AGESA_FATAL may have occurred
373 * @return FALSE - An Error value greater than or equal to AGESA_FATAL may have occurred
374 */
375
376BOOLEAN
377MemNPlatformSpecUnb (
378 IN OUT MEM_NB_BLOCK *NBPtr
379 )
380{
381 UINT8 MemClkDis;
382 UINT8 i;
383 UINT8 MemoryAllClocks;
384 UINT8 *MemClkDisMap;
385 UINT16 CsPresent;
386
387 if (!MemNGetPlatformCfgNb (NBPtr)) {
388 IDS_ERROR_TRAP;
389 }
390
391 if (!NBPtr->PsPtr->MemPDoPs (NBPtr)) {
392 IDS_HDT_CONSOLE (MEM_FLOW, "\tDisable DCT%d due to unsupported DIMM configuration\n", NBPtr->Dct);
393 if (!NBPtr->MemPtr->ErrorHandling (NBPtr->MCTPtr, NBPtr->Dct, EXCLUDE_ALL_CHIPSEL, &NBPtr->MemPtr->StdHeader)) {
394 ASSERT (FALSE);
395 }
396 NBPtr->DisableDCT (NBPtr);
397 } else {
398
399 MemNProgramPlatformSpecNb (NBPtr);
400 MemProcessConditionalOverrides (NBPtr->RefPtr->PlatformMemoryConfiguration, NBPtr, PSO_ACTION_ODT, ALL_DIMMS);
401
402 //======================================================================
403 // Disable unused MemClk to save power
404 //======================================================================
405 //
406 MemClkDis = 0;
407 MemoryAllClocks = UserOptions.CfgMemoryAllClocksOn;
408 IDS_OPTION_HOOK (IDS_ALL_MEMORY_CLOCK, &MemoryAllClocks, &(NBPtr->MemPtr->StdHeader));
409 if (!MemoryAllClocks) {
410 // Special Jedec SPD diagnostic bit - "enable all clocks"
411 if (!NBPtr->MCTPtr->Status[SbDiagClks]) {
412 MemClkDisMap = FindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PSO_MEMCLK_DIS, NBPtr->MCTPtr->SocketId, NBPtr->Dct, 0,
413 &(NBPtr->MCTPtr->LogicalCpuid), &(NBPtr->MemPtr->StdHeader));
414 if (MemClkDisMap == NULL) {
415 MemClkDisMap = NBPtr->ChannelPtr->MemClkDisMap;
416 }
417
418 // Turn off unused clocks
419 CsPresent = NBPtr->DCTPtr->Timings.CsPresent;
420
421 for (i = 0; i < 8; i++) {
422 if ((CsPresent & MemClkDisMap[i]) == 0) {
423 MemClkDis |= (UINT8) (1 << i);
424 }
425 }
426
427 // Turn off unused chiplets
428 for (i = 0; i < 3; i++) {
429 if (((MemClkDis >> (i * 2)) & 0x3) == 0x3) {
430 MemNSetBitFieldNb (NBPtr, BFPhyClkConfig0 + i, 0x0010);
431 }
432 }
433 }
434 }
435 MemNSetBitFieldNb (NBPtr, BFMemClkDis, MemClkDis);
436 MemFInitTableDrive (NBPtr, MTAfterPlatformSpec);
437 }
438
439 return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL);
440}
441
442/* -----------------------------------------------------------------------------*/
443/**
444 *
445 *
446 * This function disables the DCT and mem clock
447 *
448 *
449 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
450 *
451 */
452
453VOID
454MemNDisableDCTNb (
455 IN OUT MEM_NB_BLOCK *NBPtr
456 )
457{
458 MemNSetBitFieldNb (NBPtr, BFCKETri, 0x03);
459 MemNSetBitFieldNb (NBPtr, BFODTTri, 0x0F);
460 MemNSetBitFieldNb (NBPtr, BFChipSelTri, 0xFF);
461
462 // To maximize power savings when DisDramInterface=1b,
463 // all of the MemClkDis bits should also be set.
464 //
465 MemNSetBitFieldNb (NBPtr, BFMemClkDis, 0xFF);
466 MemNSetBitFieldNb (NBPtr, BFDisDramInterface, 1);
467}
468
469/* -----------------------------------------------------------------------------*/
470/**
471 *
472 *
473 * This function disables the DCT and mem clock for client NB
474 *
475 *
476 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
477 *
478 */
479
480VOID
481MemNDisableDCTClientNb (
482 IN OUT MEM_NB_BLOCK *NBPtr
483 )
484{
485 MemNSetBitFieldNb (NBPtr, BFCKETri, 0x03);
486 MemNSetBitFieldNb (NBPtr, BFODTTri, 0x0F);
487 MemNSetBitFieldNb (NBPtr, BFChipSelTri, 0xFF);
488
489 //Wait for 24 MEMCLKs
490 MemNWaitXMemClksNb (NBPtr, 24);
491
492 // To maximize power savings when DisDramInterface=1b,
493 // all of the MemClkDis bits should also be set.
494 //
495 MemNSetBitFieldNb (NBPtr, BFMemClkDis, 0xFF);
496
497 MemNSetBitFieldNb (NBPtr, BFDramPhyStatusReg, 0x80800000);
498
499 MemNSetBitFieldNb (NBPtr, BFDisDramInterface, 1);
500}
501
502/* -----------------------------------------------------------------------------*/
503/**
504 *
505 *
506 * This function disables the DCT and mem clock for UNB
507 *
508 *
509 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
510 *
511 */
512
513VOID
514MemNDisableDCTUnb (
515 IN OUT MEM_NB_BLOCK *NBPtr
516 )
517{
518 MemNSetBitFieldNb (NBPtr, BFExtendedParityEn, 0);
519 MemNSetBitFieldNb (NBPtr, BFParEn, 0);
520 MemNSetBitFieldNb (NBPtr, BFCKETri, 0x0F);
521
522 //Wait for 24 MEMCLKs
523 MemNWaitXMemClksNb (NBPtr, 24);
524
525 // To maximize power savings when DisDramInterface=1b,
526 // all of the MemClkDis bits should also be set.
527 //
528 MemNSetBitFieldNb (NBPtr, BFMemClkDis, 0xFF);
529
530 MemNSetBitFieldNb (NBPtr, BFDisDramInterface, 1);
531
532 if (NBPtr->Dct == 0) {
533 MemNSetBitFieldNb (NBPtr, BFPhyPSMasterChannel, 0x100);
534 }
535
536 // If channel is disabled after dram init, set DisDllShutdownSR
537 if (MemNGetBitFieldNb (NBPtr, BFDramEnabled) == 1) {
538 MemNSetBitFieldNb (NBPtr, BFDisDllShutdownSR, 1);
539 }
540}
541
542/* -----------------------------------------------------------------------------*/
543/**
544 *
545 *
546 * This function initializes the DRAM devices on all DCTs at the same time
547 *
548 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
549 *
550 */
551
552VOID
553MemNStartupDCTNb (
554 IN OUT MEM_NB_BLOCK *NBPtr
555 )
556{
557 // 1. Ensure F2x[1, 0]9C_x08[DisAutoComp] = 1.
558 // 2. BIOS waits 5 us for the disabling of the compensation engine to complete.
559 // DisAutoComp is still being set since InitPhyComp
560
561 if (NBPtr->MCTPtr->NodeMemSize != 0) {
562 // Init MemClk frequency
563 MemNBrdcstSetNb (NBPtr, BFMemClkFreqVal, 1);
564
565
566 AGESA_TESTPOINT (TpProcMemBeforeDramInit, &(NBPtr->MemPtr->StdHeader));
567 NBPtr->MemNBeforeDramInitNb (NBPtr);
568 IDS_HDT_CONSOLE (MEM_FLOW, "\nMemClkFreq: %d MHz\n", NBPtr->DCTPtr->Timings.Speed);
569 AGESA_TESTPOINT (TpProcMemDramInit, &(NBPtr->MemPtr->StdHeader));
570 NBPtr->FeatPtr->DramInit (NBPtr->TechPtr);
571 }
572
573 // 7. Program F2x[1, 0]9C_x08[DisAutoComp] = 0.
574 // 8. BIOS must wait 750 us for the phy compensation engine
575 // to reinitialize.
576 // DisAutoComp will be cleared after DramEnabled turns to 1
577
578}
579
580/* -----------------------------------------------------------------------------*/
581/**
582 *
583 *
584 * This function initializes the DRAM devices on all DCTs at the same time
585 *
586 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
587 *
588 */
589
590VOID
591MemNStartupDCTUnb (
592 IN OUT MEM_NB_BLOCK *NBPtr
593 )
594{
595 UINT8 Dct;
596 UINT16 FinalPllLockTime;
597
598 if (NBPtr->MCTPtr->NodeMemSize != 0) {
599 // Update NB frequency for startup DDR speed
600 NBPtr->ChangeNbFrequency (NBPtr);
601
602 if (!NBPtr->IsSupported[ForcePhyToM0]) {
603 MemNBrdcstSetNb (NBPtr, BFDramPhyStatusReg, 0x80000000);
604
605 MemNBrdcstSetNb (NBPtr, BFPllRegWaitTime, 0x118);
606 }
607
608 // Phy Voltage Level Programming
609 MemNPhyVoltageLevelNb (NBPtr);
610
611 // Run frequency change sequence
612 MemNBrdcstSetNb (NBPtr, BFPllLockTime, NBPtr->FreqChangeParam->PllLockTimeDefault);
613 MemNBrdcstSetNb (NBPtr, BFMemClkFreq, NBPtr->GetMemClkFreqId (NBPtr, NBPtr->DCTPtr->Timings.Speed));
614 NBPtr->FamilySpecificHook[SetSkewMemClk] (NBPtr, NULL);
615 NBPtr->ProgramNbPsDependentRegs (NBPtr);
616 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
617 MemNSwitchDCTNb (NBPtr, Dct);
618 if ((NBPtr->DCTPtr->Timings.DctMemSize != 0)) {
619 MemNSetBitFieldNb (NBPtr, BFMemClkFreqVal, 1);
620 MemNPollBitFieldNb (NBPtr, BFFreqChgInProg, 0, PCI_ACCESS_TIMEOUT, FALSE);
621 }
622 }
623 FinalPllLockTime = 0xF;
624 NBPtr->FamilySpecificHook[AfterMemClkFreqVal] (NBPtr, &FinalPllLockTime);
625 if (!NBPtr->IsSupported[CsrPhyPllPdEn]) {
626 // IF (D18F2x[1,0]9C_x0D0F_E00A[CsrPhySrPllPdMode]==0) THEN program
627 // D18F2x[1,0]9C_x0D0F_E006[PllLockTime] = 0Fh
628 MemNBrdcstSetNb (NBPtr, BFPllLockTime, FinalPllLockTime);
629 }
630
631 NBPtr->FamilySpecificHook[BeforePhyFenceTraining] (NBPtr, NBPtr);
632
633 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
634 MemNSwitchDCTNb (NBPtr, Dct);
635 if (NBPtr->DCTPtr->Timings.DctMemSize != 0) {
636 IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct);
637
638 // Phy fence programming
639 AGESA_TESTPOINT (TpProcMemPhyFenceTraining, &(NBPtr->MemPtr->StdHeader));
640 NBPtr->PhyFenceTraining (NBPtr);
641
642 // Phy compensation initialization
643 AGESA_TESTPOINT (TPProcMemPhyCompensation, &(NBPtr->MemPtr->StdHeader));
644 NBPtr->MemNInitPhyComp (NBPtr);
645 MemProcessConditionalOverrides (NBPtr->RefPtr->PlatformMemoryConfiguration, NBPtr, PSO_ACTION_SLEWRATE, ALL_DIMMS);
646 }
647 }
648
649 AGESA_TESTPOINT (TpProcMemBeforeDramInit, &(NBPtr->MemPtr->StdHeader));
650 NBPtr->MemNBeforeDramInitNb (NBPtr);
651
652 AGESA_TESTPOINT (TpProcMemDramInit, &(NBPtr->MemPtr->StdHeader));
653 IDS_HDT_CONSOLE (MEM_FLOW, "\nMemClkFreq: %d MHz\n", NBPtr->DCTPtr->Timings.Speed);
654 NBPtr->FeatPtr->DramInit (NBPtr->TechPtr);
655 }
656}
657
658/* -----------------------------------------------------------------------------*/
659/**
660 *
661 * MemNChangeFrequencyHy:
662 *
663 * This function change MemClk frequency to the value that is specified by DCTPtr->Timings.Speed
664 *
665 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
666 *
667 */
668
669VOID
670MemNChangeFrequencyNb (
671 IN OUT MEM_NB_BLOCK *NBPtr
672 )
673{
674 MEM_TECH_BLOCK *TechPtr;
675 UINT8 Dct;
676 UINT8 ChipSel;
677
678 TechPtr = NBPtr->TechPtr;
679 if (NBPtr->IsSupported[CheckDisDllShutdownSR] && !(NBPtr->IsSupported[SetDllShutDown])) {
680 // #107421
681 MemNBrdcstSetNb (NBPtr, BFDisDllShutdownSR, 1);
682 }
683
684 //Program F2x[1,0]90[EnterSelfRefresh]=1.
685 //Wait until the hardware resets F2x[1,0]90[EnterSelfRefresh]=0.
686 MemNBrdcstSetNb (NBPtr, BFEnterSelfRef, 1);
687 MemNPollBitFieldNb (NBPtr, BFEnterSelfRef, 0, PCI_ACCESS_TIMEOUT, TRUE);
688
689 //Program F2x9C_x08[DisAutoComp]=1
690 MemNSwitchDCTNb (NBPtr, 0);
691 MemNSetBitFieldNb (NBPtr, BFDisAutoComp, 1);
692
693 //Program F2x[1, 0]94[MemClkFreqVal] = 0.
694 MemNBrdcstSetNb (NBPtr, BFMemClkFreqVal, 0);
695
696 //Program F2x[1, 0]94[MemClkFreq] to specify the target MEMCLK frequency.
697 MemNBrdcstSetNb (NBPtr, BFMemClkFreq, NBPtr->GetMemClkFreqId (NBPtr, NBPtr->DCTPtr->Timings.Speed));
698
699 IDS_OPTION_HOOK (IDS_BEFORE_MEM_FREQ_CHG, NBPtr, &(NBPtr->MemPtr->StdHeader));
700 //Program F2x[1, 0]94[MemClkFreqVal] = 1.
701 MemNBrdcstSetNb (NBPtr, BFMemClkFreqVal, 1);
702
703 //Wait until F2x[1, 0]94[FreqChgInProg]=0.
704 MemNPollBitFieldNb (NBPtr, BFFreqChgInProg, 0, PCI_ACCESS_TIMEOUT, TRUE);
705
706 if (NBPtr->IsSupported[CheckPhyFenceTraining]) {
707 //Perform Phy Fence retraining after frequency changed
708 AGESA_TESTPOINT (TpProcMemPhyFenceTraining, &(NBPtr->MemPtr->StdHeader));
709 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
710 MemNSwitchDCTNb (NBPtr, Dct);
711 if (NBPtr->DCTPtr->Timings.DctMemSize != 0) {
712 IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct);
713 AGESA_TESTPOINT (TpProcMemPhyFenceTraining, &(NBPtr->MemPtr->StdHeader));
714 MemNPhyFenceTrainingNb (NBPtr);
715 }
716 }
717 }
718
719 //Program F2x9C_x08[DisAutoComp]=0
720 MemNSwitchDCTNb (NBPtr, 0);
721 MemNSetBitFieldNb (NBPtr, BFDisAutoComp, 0);
722
723 //Program F2x[1,0]90[ExitSelfRef]=1 for both DCTs.
724 //Wait until the hardware resets F2x[1, 0]90[ExitSelfRef]=0.
725 MemNBrdcstSetNb (NBPtr, BFExitSelfRef, 1);
726 MemNPollBitFieldNb (NBPtr, BFExitSelfRef, 0, PCI_ACCESS_TIMEOUT, TRUE);
727
728 if (NBPtr->MCTPtr->Status[SbRegistered]) {
729 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
730 MemNSwitchDCTNb (NBPtr, Dct);
731 if (NBPtr->DCTPtr->Timings.DctMemSize != 0) {
732 TechPtr->FreqChgCtrlWrd (TechPtr);
733 }
734 }
735 }
736
737 //wait for 500 MCLKs after ExitSelfRef, 500*2.5ns=1250ns
738 MemNWaitXMemClksNb (NBPtr, 500);
739
740 if (NBPtr->IsSupported[CheckDisDllShutdownSR] && !(NBPtr->IsSupported[SetDllShutDown])) {
741 // #107421
742 MemNBrdcstSetNb (NBPtr, BFDisDllShutdownSR, 0);
743 }
744
745 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
746 IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct);
747 MemNSwitchDCTNb (NBPtr, Dct);
748 if (NBPtr->DCTPtr->Timings.DctMemSize != 0) {
749
750 //9.Configure the DCT to send initialization MR commands:
751 // BIOS must reprogram Twr, Tcwl, and Tcl based on the new MEMCLK frequency.
752 // Program F2x[1, 0]7C similar to step #2 in Pass 1 above for the new Dimm values.
753 TechPtr->AutoCycTiming (TechPtr);
754 if (!MemNPlatformSpecNb (NBPtr)) {
755 IDS_ERROR_TRAP;
756 }
757
758 for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel++) {
759 if (NBPtr->IsSupported[CheckGetMCTSysAddr]) {
760 if ((NBPtr->DCTPtr->Timings.CsPresent & ((UINT16)1 << ChipSel)) != 0) {
761 // if chip select present
762 TechPtr->SendAllMRCmds (TechPtr, ChipSel);
763 // NOTE: wait 512 clocks for DLL-relock
764 MemUWait10ns (50000, NBPtr->MemPtr); // wait 500us
765 }
766 }
767 if (NBPtr->IsSupported[CheckSendAllMRCmds]) {
768 if ((NBPtr->DCTPtr->Timings.CsPresent & ((UINT16)1 << ChipSel)) != 0) {
769
770 // if chip select present
771 TechPtr->SendAllMRCmds (TechPtr, ChipSel);
772 }
773 }
774 }
775 if ((NBPtr->DCTPtr->Timings.Speed == DDR1600_FREQUENCY) && (NBPtr->IsSupported[CheckDllSpeedUp])) {
776 MemNSetBitFieldNb (NBPtr, BFPhy0x0D080F11, (MemNGetBitFieldNb (NBPtr, BFPhy0x0D080F11) | 0x2000));
777 MemNSetBitFieldNb (NBPtr, BFPhy0x0D080F10, (MemNGetBitFieldNb (NBPtr, BFPhy0x0D080F10) | 0x2000));
778 MemNSetBitFieldNb (NBPtr, BFPhy0x0D088F30, (MemNGetBitFieldNb (NBPtr, BFPhy0x0D088F30) | 0x2000));
779 MemNSetBitFieldNb (NBPtr, BFPhy0x0D08C030, (MemNGetBitFieldNb (NBPtr, BFPhy0x0D08C030) | 0x2000));
780 if (Dct == 0) {
781 MemNSetBitFieldNb (NBPtr, BFPhy0x0D082F30, (MemNGetBitFieldNb (NBPtr, BFPhy0x0D082F30) | 0x2000));
782 }
783 // NOTE: wait 512 clocks for DLL-relock
784 MemUWait10ns (50000, NBPtr->MemPtr); // wait 500us
785 }
786 }
787 }
788 // Re-enable phy compensation since it had been disabled during InitPhyComp
789 MemNSwitchDCTNb (NBPtr, 0);
790 MemNSetBitFieldNb (NBPtr, BFDisAutoComp, 0);
791
792 MemFInitTableDrive (NBPtr, MTAfterFreqChg);
793}
794
795
796/* -----------------------------------------------------------------------------*/
797/**
798 *
799 * This function ramp up frequency the next level if it have not reached
800 * its TargetSpeed yet.
801 *
802 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
803 *
804 * @return TRUE - No fatal error occurs.
805 * @return FALSE - Fatal error occurs.
806 */
807
808BOOLEAN
809MemNRampUpFrequencyNb (
810 IN OUT MEM_NB_BLOCK *NBPtr
811 )
812{
813 CONST UINT16 FreqList[] = {
814 DDR400_FREQUENCY,
815 DDR533_FREQUENCY,
816 DDR667_FREQUENCY,
817 DDR800_FREQUENCY,
818 DDR1066_FREQUENCY,
819 DDR1333_FREQUENCY,
820 DDR1600_FREQUENCY,
821 DDR1866_FREQUENCY,
822 DDR2133_FREQUENCY
823 };
824 UINT8 Dct;
825 UINT8 i;
826 UINT16 NewSpeed;
827 DIE_STRUCT *MCTPtr;
828
829 MCTPtr = NBPtr->MCTPtr;
830
831 // Do not change frequency when it is already at TargetSpeed
832 if (NBPtr->DCTPtr->Timings.Speed == NBPtr->DCTPtr->Timings.TargetSpeed) {
833 return TRUE;
834 }
835
836 // Find the next supported frequency level
837 NewSpeed = NBPtr->DCTPtr->Timings.TargetSpeed;
838 for (i = 0; i < (GET_SIZE_OF (FreqList) - 1); i++) {
839 if (NBPtr->DCTPtr->Timings.Speed == FreqList[i]) {
840 NewSpeed = FreqList[i + 1];
841 break;
842 }
843 }
844 ASSERT (i < (GET_SIZE_OF (FreqList) - 1));
845 ASSERT (NewSpeed <= NBPtr->DCTPtr->Timings.TargetSpeed);
846
847 // BIOS must program both DCTs to the same frequency.
848 IDS_HDT_CONSOLE (MEM_FLOW, "\nMemClkFreq changed: %d MHz", NBPtr->DCTPtr->Timings.Speed);
849 for (Dct = 0; Dct < MCTPtr->DctCount; Dct++) {
850 NBPtr->SwitchDCT (NBPtr, Dct);
851 NBPtr->DCTPtr->Timings.Speed = NewSpeed;
852 }
853 IDS_HDT_CONSOLE (MEM_FLOW, " -> %d MHz", NewSpeed);
854
855 NBPtr->ChangeFrequency (NBPtr);
856
857 return (BOOLEAN) (MCTPtr->ErrCode < AGESA_FATAL);
858}
859
860/* -----------------------------------------------------------------------------*/
861/**
862 *
863 * This function ramp up frequency to target frequency
864 *
865 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
866 *
867 * @return TRUE - No fatal error occurs.
868 * @return FALSE - Fatal error occurs.
869 */
870
871BOOLEAN
872MemNRampUpFrequencyUnb (
873 IN OUT MEM_NB_BLOCK *NBPtr
874 )
875{
876 UINT8 Dct;
877 DIE_STRUCT *MCTPtr;
878
879 MCTPtr = NBPtr->MCTPtr;
880
881 // Do not change frequency when it is already at TargetSpeed
882 if (NBPtr->DCTPtr->Timings.Speed == NBPtr->DCTPtr->Timings.TargetSpeed) {
883 return TRUE;
884 }
885
886 // BIOS must program both DCTs to the same frequency.
887 IDS_HDT_CONSOLE (MEM_FLOW, "\nMemClkFreq changed: %d MHz", NBPtr->DCTPtr->Timings.Speed);
888 for (Dct = 0; Dct < MCTPtr->DctCount; Dct++) {
889 NBPtr->SwitchDCT (NBPtr, Dct);
890 NBPtr->DCTPtr->Timings.Speed = NBPtr->DCTPtr->Timings.TargetSpeed;
891 }
892 IDS_HDT_CONSOLE (MEM_FLOW, " -> %d MHz", NBPtr->DCTPtr->Timings.TargetSpeed);
893
894 NBPtr->ChangeFrequency (NBPtr);
895
896 return (BOOLEAN) (MCTPtr->ErrCode < AGESA_FATAL);
897}
898
899/* -----------------------------------------------------------------------------*/
900/**
901 *
902 *
903 * This function uses calculated values from DCT.Timings structure to
904 * program its registers.
905 *
906 *
907 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
908 *
909 */
910
911VOID
912MemNProgramCycTimingsNb (
913 IN OUT MEM_NB_BLOCK *NBPtr
914 )
915{
916 CONST CTENTRY TmgAdjTab[] = {
917 // BitField, Min, Max, Bias, Ratio_x2
918 {BFTcl, 4, 12, 4, 2},
919 {BFTrcd, 5, 12, 5, 2},
920 {BFTrp, 5, 12, 5, 2},
921 {BFTrtp, 4, 7, 4, 2},
922 {BFTras, 15, 30, 15, 2},
923 {BFTrc, 11, 42, 11, 2},
924 {BFTwrDDR3, 5, 12, 4, 2},
925 {BFTrrd, 4, 7, 4, 2},
926 {BFTwtr, 4, 7, 4, 2},
927 {BFFourActWindow, 16, 32, 14, 1}
928 };
929
930 DCT_STRUCT *DCTPtr;
931 UINT8 *MiniMaxTmg;
932 UINT8 *MiniMaxTrfc;
933 UINT8 Value8;
934 UINT8 j;
935 BIT_FIELD_NAME BitField;
936
937 DCTPtr = NBPtr->DCTPtr;
938
939 //======================================================================
940 // Program turnaround timings to their max during DRAM init and training
941 //======================================================================
942 //
943 MemNSetBitFieldNb (NBPtr, BFNonSPD, 0x28FF);
944
945 MemNSetBitFieldNb (NBPtr, BFNonSPDHi, 0x2A);
946
947 //======================================================================
948 // Program DRAM Timing values
949 //======================================================================
950 //
951 MiniMaxTmg = &DCTPtr->Timings.CasL;
952 for (j = 0; j < GET_SIZE_OF (TmgAdjTab); j++) {
953 BitField = TmgAdjTab[j].BitField;
954
955 if (MiniMaxTmg[j] < TmgAdjTab[j].Min) {
956 MiniMaxTmg[j] = TmgAdjTab[j].Min;
957 } else if (MiniMaxTmg[j] > TmgAdjTab[j].Max) {
958 MiniMaxTmg[j] = TmgAdjTab[j].Max;
959 }
960
961 Value8 = (UINT8) MiniMaxTmg[j];
962
963 if (BitField == BFTwrDDR3) {
964 Value8 = (Value8 == 10) ? 9 : (Value8 >= 11) ? 10 : Value8;
965 } else if (BitField == BFTrtp) {
966 Value8 = (DCTPtr->Timings.Speed <= DDR1066_FREQUENCY) ? 4 : (DCTPtr->Timings.Speed == DDR1333_FREQUENCY) ? 5 : 6;
967 }
968
969 Value8 = Value8 - TmgAdjTab[j].Bias;
970 Value8 = (Value8 * TmgAdjTab[j].Ratio_x2) >> 1;
971
972 ASSERT ((BitField == BFTcl ) ? (Value8 <= 8) :
973 (BitField == BFTrcd) ? (Value8 <= 7) :
974 (BitField == BFTrp ) ? (Value8 <= 7) :
975 (BitField == BFTrtp) ? (Value8 <= 3) :
976 (BitField == BFTras) ? (Value8 <= 15) :
977 (BitField == BFTrc ) ? (Value8 <= 31) :
978 (BitField == BFTrrd) ? (Value8 <= 3) :
979 (BitField == BFTwtr) ? (Value8 <= 3) :
980 (BitField == BFTwrDDR3) ? ((Value8 >= 1) && (Value8 <= 6)) :
981 (BitField == BFFourActWindow) ? ((Value8 >= 1) && (Value8 <= 9)) : FALSE);
982 MemNSetBitFieldNb (NBPtr, BitField, Value8);
983 }
984
985 MiniMaxTrfc = &DCTPtr->Timings.Trfc0;
986 for (j = 0; j < 4; j++) {
987 ASSERT (MiniMaxTrfc[j] <= 4);
988 MemNSetBitFieldNb (NBPtr, BFTrfc0 + j, MiniMaxTrfc[j]);
989 }
990
991 MemNSetBitFieldNb (NBPtr, BFTcwl, ((DCTPtr->Timings.Speed >= DDR800_FREQUENCY) ?
992 (NBPtr->GetMemClkFreqId (NBPtr, DCTPtr->Timings.Speed) - 3) : 0));
993
994 MemNSetBitFieldNb (NBPtr, BFTref, 2); // 7.8 us
995
996 //======================================================================
997 // DRAM MRS Register, set ODT
998 //======================================================================
999 //
1000 // DrvImpCtrl: drive impedance control.01b(34 ohm driver; Ron34 = Rzq/7)
1001 MemNSetBitFieldNb (NBPtr, BFDrvImpCtrl, 1);
1002
1003 // burst length control
1004 if (NBPtr->MCTPtr->Status[Sb128bitmode]) {
1005 MemNSetBitFieldNb (NBPtr, BFBurstCtrl, 2);
1006 }
1007
1008 // ASR=1, auto self refresh; SRT=0
1009 MemNSetBitFieldNb (NBPtr, BFASR, 1);
1010}
1011
1012/* -----------------------------------------------------------------------------*/
1013/**
1014 *
1015 *
1016 * This function uses calculated values from DCT.Timings structure to
1017 * program its registers.
1018 *
1019 *
1020 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1021 *
1022 */
1023
1024VOID
1025MemNProgramCycTimingsClientNb (
1026 IN OUT MEM_NB_BLOCK *NBPtr
1027 )
1028{
1029 CONST CTENTRY TmgAdjTab[] = {
1030 // BitField, Min, Max, Bias, Ratio_x2
1031 {BFTcl, 5, 14, 4, 2},
1032 {BFTrcd, 5, 14, 5, 2},
1033 {BFTrp, 5, 14, 5, 2},
1034 {BFTrtp, 4, 8, 4, 2},
1035 {BFTras, 15, 36, 15, 2},
1036 {BFTrc, 20, 49, 11, 2},
1037 {BFTwrDDR3, 5, 16, 4, 2},
1038 {BFTrrd, 4, 8, 4, 2},
1039 {BFTwtr, 4, 8, 4, 2},
1040 {BFFourActWindow, 16, 40, 14, 1}
1041 };
1042
1043 DCT_STRUCT *DCTPtr;
1044 UINT8 *MiniMaxTmg;
1045 UINT8 *MiniMaxTrfc;
1046 UINT8 Value8;
1047 UINT8 j;
1048 UINT8 Tcwl;
1049 UINT8 Trcd;
1050 INT32 TCK_ps;
1051 BIT_FIELD_NAME BitField;
1052
1053 DCTPtr = NBPtr->DCTPtr;
1054
1055 //======================================================================
1056 // Program DRAM Timing values
1057 //======================================================================
1058 //
1059 MiniMaxTmg = &DCTPtr->Timings.CasL;
1060 for (j = 0; j < GET_SIZE_OF (TmgAdjTab); j++) {
1061 BitField = TmgAdjTab[j].BitField;
1062
1063 if (MiniMaxTmg[j] < TmgAdjTab[j].Min) {
1064 MiniMaxTmg[j] = TmgAdjTab[j].Min;
1065 } else if (MiniMaxTmg[j] > TmgAdjTab[j].Max) {
1066 MiniMaxTmg[j] = TmgAdjTab[j].Max;
1067 }
1068
1069 Value8 = (UINT8) MiniMaxTmg[j];
1070
1071 if (BitField == BFTwrDDR3) {
1072 if (NBPtr->IsSupported[AdjustTwr]) {
1073 Value8 ++;
1074 }
1075 Value8 = (Value8 >= 10) ? (((Value8 + 1) / 2) + 4) : Value8;
1076 }
1077
1078 if ((BitField == BFTrc) && NBPtr->IsSupported[AdjustTrc]) {
1079 Value8 -= 5;
1080 }
1081
1082 Value8 = Value8 - TmgAdjTab[j].Bias;
1083 Value8 = (Value8 * TmgAdjTab[j].Ratio_x2) >> 1;
1084
1085 ASSERT ((BitField == BFTcl ) ? ((Value8 >= 1) && (Value8 <= 10)) :
1086 (BitField == BFTrcd) ? (Value8 <= 9) :
1087 (BitField == BFTrp ) ? (Value8 <= 9) :
1088 (BitField == BFTrtp) ? (Value8 <= 4) :
1089 (BitField == BFTras) ? (Value8 <= 21) :
1090 (BitField == BFTrc ) ? (NBPtr->IsSupported[AdjustTrc] ? ((Value8 >= 4) && (Value8 <= 38)) : ((Value8 >= 9) && (Value8 <= 38))) :
1091 (BitField == BFTrrd) ? (Value8 <= 4) :
1092 (BitField == BFTwtr) ? (Value8 <= 4) :
1093 (BitField == BFTwrDDR3) ? (Value8 <= 7) :
1094 (BitField == BFFourActWindow) ? ((Value8 >= 1) && (Value8 <= 13)) : FALSE);
1095 MemNSetBitFieldNb (NBPtr, BitField, Value8);
1096 }
1097
1098 MiniMaxTrfc = &DCTPtr->Timings.Trfc0;
1099 for (j = 0; j < 4; j++) {
1100 ASSERT (MiniMaxTrfc[j] <= 5);
1101 MemNSetBitFieldNb (NBPtr, BFTrfc0 + j, MiniMaxTrfc[j]);
1102 }
1103
1104 Tcwl = (UINT8) (DCTPtr->Timings.Speed / 133) + 2;
1105 MemNSetBitFieldNb (NBPtr, BFTcwl, ((Tcwl > 5) ? (Tcwl - 5) : 0));
1106
1107 MemNSetBitFieldNb (NBPtr, BFTref, 2); // Tref = 7.8 us
1108
1109 // Skid buffer can only be programmed once before Dram init
1110 if (NBPtr->DCTPtr->Timings.Speed == DDR800_FREQUENCY) {
1111 TCK_ps = 1000500 / DCTPtr->Timings.TargetSpeed;
1112 Trcd = (UINT8) ((((1000 / 40) * (UINT32)DCTPtr->Timings.DIMMTrcd) + TCK_ps - 1) / TCK_ps);
1113 MemNSetBitFieldNb (NBPtr, BFDbeSkidBufDis, (Trcd > 10) ? 0 : 1);
1114 }
1115
1116 MemNSetBitFieldNb (NBPtr, BFRdOdtTrnOnDly, (DCTPtr->Timings.CasL > Tcwl) ? (DCTPtr->Timings.CasL - Tcwl) : 0);
1117
1118}
1119
1120/* -----------------------------------------------------------------------------*/
1121/**
1122 *
1123 *
1124 * This function uses calculated values from DCT.Timings structure to
1125 * program its registers for UNB
1126 *
1127 *
1128 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1129 *
1130 */
1131
1132VOID
1133MemNProgramCycTimingsUnb (
1134 IN OUT MEM_NB_BLOCK *NBPtr
1135 )
1136{
1137 CONST CTENTRY TmgAdjTab[] = {
1138 // BitField, Min, Max, Bias, Ratio_x2
1139 {BFTcl, 5, 14, 0, 2},
1140 {BFTrcd, 2, 19, 0, 2},
1141 {BFTrp, 2, 19, 0, 2},
1142 {BFTrtp, 4, 10, 0, 2},
1143 {BFTras, 8, 40, 0, 2},
1144 {BFTrc, 10, 56, 0, 2},
1145 {BFTwrDDR3, 5, 16, 0, 2},
1146 {BFTrrd, 4, 9, 0, 2},
1147 {BFTwtr, 4, 9, 0, 2},
1148 {BFFourActWindow, 6, 42, 0, 2}
1149 };
1150
1151 DCT_STRUCT *DCTPtr;
1152 UINT8 *MiniMaxTmg;
1153 UINT8 *MiniMaxTrfc;
1154 UINT8 Value8;
1155 UINT8 j;
1156 UINT8 Tcwl;
1157 UINT8 RdOdtTrnOnDly;
1158 BIT_FIELD_NAME BitField;
1159
1160 DCTPtr = NBPtr->DCTPtr;
1161
1162 //======================================================================
1163 // Program DRAM Timing values
1164 //======================================================================
1165 //
1166 MiniMaxTmg = &DCTPtr->Timings.CasL;
1167 for (j = 0; j < GET_SIZE_OF (TmgAdjTab); j++) {
1168 BitField = TmgAdjTab[j].BitField;
1169
1170 if (BitField == BFTrp) {
1171 if (NBPtr->IsSupported[AdjustTrp]) {
1172 MiniMaxTmg[j] ++;
1173 if (MiniMaxTmg[j] < 5) {
1174 MiniMaxTmg[j] = 5;
1175 }
1176 }
1177 }
1178
1179 if (MiniMaxTmg[j] < TmgAdjTab[j].Min) {
1180 MiniMaxTmg[j] = TmgAdjTab[j].Min;
1181 } else if (MiniMaxTmg[j] > TmgAdjTab[j].Max) {
1182 MiniMaxTmg[j] = TmgAdjTab[j].Max;
1183 }
1184
1185 Value8 = (UINT8) MiniMaxTmg[j];
1186
1187 if (BitField == BFTwrDDR3) {
1188 if ((Value8 > 8) && ((Value8 & 1) != 0)) {
1189 ASSERT (FALSE);
1190 }
1191 }
1192
1193 MemNSetBitFieldNb (NBPtr, BitField, Value8);
1194 }
1195
1196 MiniMaxTrfc = &DCTPtr->Timings.Trfc0;
1197 for (j = 0; j < 4; j++) {
1198 if ((NBPtr->DCTPtr->Timings.DctDimmValid & (1 << j)) != 0) {
1199 ASSERT (MiniMaxTrfc[j] <= 4);
1200 MemNSetBitFieldNb (NBPtr, BFTrfc0 + j, MiniMaxTrfc[j]);
1201 }
1202 }
1203
1204 Tcwl = (UINT8) (DCTPtr->Timings.Speed / 133) + 2;
1205 Tcwl = (Tcwl > 5) ? Tcwl : 5;
1206 MemNSetBitFieldNb (NBPtr, BFTcwl, Tcwl);
1207
1208 MemNSetBitFieldNb (NBPtr, BFTref, 2); // 7.8 us
1209
1210 RdOdtTrnOnDly = (DCTPtr->Timings.CasL > Tcwl) ? (DCTPtr->Timings.CasL - Tcwl) : 0;
1211 MemNSetBitFieldNb (NBPtr, BFRdOdtTrnOnDly, RdOdtTrnOnDly);
1212 NBPtr->FamilySpecificHook[ProgOdtControl] (NBPtr, NULL);
1213
1214 //
1215 // Program Tmod
1216 //
1217 MemNSetBitFieldNb (NBPtr, BFTmod, (DCTPtr->Timings.Speed < DDR1866_FREQUENCY) ? 0x0C :
1218 (DCTPtr->Timings.Speed > DDR1866_FREQUENCY) ? 0x10 : 0x0E);
1219 //
1220 // Program Tzqcs and Tzqoper
1221 //
1222 // Tzqcs max(64nCK, 80ns)
1223 MemNSetBitFieldNb (NBPtr, BFTzqcs, MIN (6, (MAX (64, MemUnsToMemClk (NBPtr->DCTPtr->Timings.Speed, 80)) + 15) / 16));
1224 // Tzqoper max(256nCK, 320ns)
1225 MemNSetBitFieldNb (NBPtr, BFTzqoper, MIN (0xC, (MAX (256, MemUnsToMemClk (NBPtr->DCTPtr->Timings.Speed, 320)) + 31) / 32));
1226
1227 // Program power management timing
1228 MemNDramPowerMngTimingNb (NBPtr);
1229}
1230
1231/* -----------------------------------------------------------------------------*/
1232/**
1233 *
1234 *
1235 * This function gets platform specific settings for the current channel
1236 *
1237 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1238 *
1239 * @return TRUE - All platform types defined have initialized successfully
1240 * @return FALSE - At least one of the platform types gave not been initialized successfully
1241 */
1242
1243BOOLEAN
1244MemNGetPlatformCfgNb (
1245 IN OUT MEM_NB_BLOCK *NBPtr
1246 )
1247{
1248 UINT8 p;
1249
1250 for (p = 0; p < MAX_PLATFORM_TYPES; p++) {
1251 ASSERT (NBPtr->MemPtr->GetPlatformCfg[p] != NULL);
1252 if (NBPtr->MemPtr->GetPlatformCfg[p] (NBPtr->MemPtr, NBPtr->MCTPtr->SocketId, NBPtr->ChannelPtr) == AGESA_SUCCESS) {
1253 break;
1254 }
1255 }
1256 return (p < MAX_PLATFORM_TYPES);
1257}
1258
1259/* -----------------------------------------------------------------------------*/
1260/**
1261 *
1262 *
1263 * This function retrieves the Max latency parameters
1264 *
1265 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1266 *
1267 * @param[in] *MinDlyPtr - Pointer to variable to store the Minimum Delay value
1268 * @param[in] *MaxDlyPtr - Pointer to variable to store the Maximum Delay value
1269 * @param[in] *DlyBiasPtr - Pointer to variable to store Delay Bias value
1270 * @param[in] MaxRcvEnDly - Maximum receiver enable delay value
1271 */
1272
1273VOID
1274MemNGetMaxLatParamsNb (
1275 IN OUT MEM_NB_BLOCK *NBPtr,
1276 IN UINT16 MaxRcvEnDly,
1277 IN OUT UINT16 *MinDlyPtr,
1278 IN OUT UINT16 *MaxDlyPtr,
1279 IN OUT UINT16 *DlyBiasPtr
1280 )
1281{
1282 *MinDlyPtr = (MemNTotalSyncComponentsNb (NBPtr) + (MaxRcvEnDly >> 5)) * 2;
1283 MemNQuarterMemClk2NClkNb (NBPtr, MinDlyPtr);
1284
1285 *MaxDlyPtr = 0x3FF;
1286
1287 *DlyBiasPtr = 4;
1288 MemNQuarterMemClk2NClkNb (NBPtr, DlyBiasPtr); // 1 MEMCLK Margin
1289
1290 *DlyBiasPtr += 1; // add 1 NCLK
1291}
1292
1293/* -----------------------------------------------------------------------------*/
1294/**
1295 *
1296 *
1297 * This function sets the maximum round-trip latency in the system from the processor to the DRAM
1298 * devices and back.
1299 *
1300 *
1301 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1302 * @param[in] MaxRcvEnDly - Maximum receiver enable delay value
1303 *
1304 */
1305
1306VOID
1307MemNSetMaxLatencyNb (
1308 IN OUT MEM_NB_BLOCK *NBPtr,
1309 IN UINT16 MaxRcvEnDly
1310 )
1311{
1312 UINT16 SubTotal;
1313
1314 AGESA_TESTPOINT (TpProcMemRcvrCalcLatency, &(NBPtr->MemPtr->StdHeader));
1315
1316 SubTotal = 0xC8; // init value for MaxRdLat used in training
1317
1318
1319 if (MaxRcvEnDly != 0xFFFF) {
1320 // Get all sync components BKDG steps 1-5
1321 SubTotal = MemNTotalSyncComponentsNb (NBPtr);
1322
1323 // Add the maximum (worst case) delay value of DqsRcvEnGrossDelay
1324 // that exists across all DIMMs and byte lanes.
1325 //
1326 SubTotal += MaxRcvEnDly >> 5;
1327
1328
1329 // Add 14.5 to the sub-total. 14.5 represents part of the processor
1330 // specific constant delay value in the DRAM clock domain.
1331 //
1332 SubTotal <<= 1; // scale 1/2 MemClk to 1/4 MemClk
1333 SubTotal += 29; // add 14.5 1/2 MemClk
1334
1335 // Convert the sub-total (in 1/2 MEMCLKs) to northbridge clocks (NCLKs)
1336 // as follows (assuming DDR400 and assuming that no P-state or link speed
1337 // changes have occurred).
1338 //
1339 MemNQuarterMemClk2NClkNb (NBPtr, &SubTotal);
1340
1341 // Add 2 NCLKs to the sub-total. 2 represents part of the processor
1342 // specific constant value in the northbridge clock domain.
1343 //
1344 SubTotal += 2;
1345 }
1346
1347 NBPtr->DCTPtr->Timings.MaxRdLat = SubTotal;
1348 // Program the F2x[1, 0]78[MaxRdLatency] register with the total delay value
1349 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tMaxRdLat: %03x\n", SubTotal);
1350 MemNSetBitFieldNb (NBPtr, BFMaxLatency, SubTotal);
1351}
1352
1353/* -----------------------------------------------------------------------------*/
1354/**
1355 *
1356 *
1357 * This function sends the ZQCL command
1358 *
1359 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1360 *
1361 */
1362
1363VOID
1364MemNSendZQCmdNb (
1365 IN OUT MEM_NB_BLOCK *NBPtr
1366 )
1367{
1368 // 1.Program MrsAddress[10]=1
1369 MemNSetBitFieldNb (NBPtr, BFMrsAddress, (UINT32)1 << 10);
1370
1371 // 2.Set SendZQCmd=1
1372 MemNSetBitFieldNb (NBPtr, BFSendZQCmd, 1);
1373
1374 // 3.Wait for SendZQCmd=0
1375 MemNPollBitFieldNb (NBPtr, BFSendZQCmd, 0, PCI_ACCESS_TIMEOUT, FALSE);
1376
1377 // 4.Wait 512 MEMCLKs
1378 MemNWaitXMemClksNb (NBPtr, 512);
1379}
1380
1381
1382/*----------------------------------------------------------------------------
1383 * LOCAL FUNCTIONS
1384 *
1385 *----------------------------------------------------------------------------
1386 */
1387
1388/* -----------------------------------------------------------------------------*/
1389/**
1390 *
1391 *
1392 * This function is used to create the DRAM map
1393 *
1394 *
1395 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1396 */
1397
1398VOID
1399STATIC
1400MemNAfterStitchMemNb (
1401 IN OUT MEM_NB_BLOCK *NBPtr
1402 )
1403{
1404 if (NBPtr->MCTPtr->GangedMode) {
1405 NBPtr->MCTPtr->NodeMemSize = NBPtr->DCTPtr->Timings.DctMemSize;
1406 NBPtr->MCTPtr->NodeSysLimit = NBPtr->MCTPtr->NodeMemSize - 1;
1407 NBPtr->MCTPtr->DctData[1].Timings.CsPresent = NBPtr->DCTPtr->Timings.CsPresent;
1408 NBPtr->MCTPtr->DctData[1].Timings.CsEnabled = NBPtr->DCTPtr->Timings.CsEnabled;
1409 NBPtr->MCTPtr->DctData[1].Timings.DctMemSize = NBPtr->DCTPtr->Timings.DctMemSize;
1410 } else {
1411 // In unganged mode, add DCT0 and DCT1 to NodeMemSize
1412 NBPtr->MCTPtr->NodeMemSize += NBPtr->DCTPtr->Timings.DctMemSize;
1413 NBPtr->MCTPtr->NodeSysLimit = NBPtr->MCTPtr->NodeMemSize - 1;
1414 }
1415}
1416
1417
1418/* -----------------------------------------------------------------------------*/
1419/**
1420 *
1421 *
1422 * This function Return the binary value of tfaw associated with
1423 * the index k
1424 *
1425 * @param[in] k value
1426 *
1427 * @return F[k], in Binary MHz.
1428 */
1429
1430UINT8
1431MemNGet1KTFawTkNb (
1432 IN UINT8 k
1433 )
1434{
1435 CONST UINT8 Tab1KTfawTK[] = {0, 8, 10, 13, 14, 19};
1436 ASSERT (k <= 5);
1437 return Tab1KTfawTK[k];
1438}
1439
1440/* -----------------------------------------------------------------------------*/
1441/**
1442 *
1443 *
1444 * This function Return the binary value of the 2KTFaw associated with
1445 * the index k
1446 *
1447 * @param[in] k value
1448 *
1449 * @return 2KTFaw converted based on k.
1450 */
1451
1452UINT8
1453MemNGet2KTFawTkNb (
1454 IN UINT8 k
1455 )
1456{
1457 CONST UINT8 Tab2KTfawTK[] = {0, 10, 14, 17, 18, 24};
1458 ASSERT (k <= 5);
1459 return Tab2KTfawTK[k];
1460}
1461
1462/* -----------------------------------------------------------------------------*/
1463/**
1464 *
1465 *
1466 * This function converts the sub-total (in 1/4 MEMCLKs) to northbridge clocks (NCLKs)
1467 * (assuming DDR400 and assuming that no P-state or link speed
1468 * changes have occurred).
1469 *
1470 *
1471 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1472 * @param[in,out] *SubTotalPtr - pointer to Sub-Total
1473 */
1474
1475VOID
1476STATIC
1477MemNQuarterMemClk2NClkNb (
1478 IN OUT MEM_NB_BLOCK *NBPtr,
1479 IN OUT UINT16 *SubTotalPtr
1480 )
1481{
1482 UINT32 NBFreq;
1483 UINT32 MemFreq;
1484
1485 // Multiply SubTotal by NB COF
1486 NBFreq = (MemNGetBitFieldNb (NBPtr, BFNbFid) + 4) * 200;
1487 // Divide SubTotal by 4 times current MemClk frequency
1488 MemFreq = NBPtr->DCTPtr->Timings.Speed * 4;
1489 *SubTotalPtr = (UINT16) (((NBFreq * (*SubTotalPtr)) + MemFreq - 1) / MemFreq); // round up
1490}
1491
1492/* -----------------------------------------------------------------------------*/
1493/**
1494 *
1495 *
1496 * This function gets the total of sync components for Max Read Latency calculation
1497 *
1498 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1499 *
1500 * @return Total in 1/2 MEMCLKs
1501 */
1502
1503UINT16
1504MemNTotalSyncComponentsNb (
1505 IN OUT MEM_NB_BLOCK *NBPtr
1506 )
1507{
1508 UINT16 SubTotal;
1509
1510 // Multiply the CAS Latency by two to get a number of 1/2 MEMCLKs UINTs.
1511 SubTotal = (UINT16) MemNGetBitFieldNb (NBPtr, BFTcl) + 1;
1512 if ((MemNGetBitFieldNb (NBPtr, BFDdr3Mode)) != 0) {
1513 SubTotal += 3;
1514 }
1515 SubTotal *= 2;
1516
1517 // If registered DIMMs are being used then add 1 MEMCLK to the sub-total.
1518 if ((MemNGetBitFieldNb (NBPtr, BFUnBuffDimm)) == 0) {
1519 SubTotal += 2;
1520 }
1521
1522 // If (F2x[1, 0]9C_x04[AddrCmdSetup] and F2x[1, 0]9C_x04[CsOdtSetup] and F2x[1, 0]9C_x04[Cke-Setup] = 0) then K = K + 1
1523 // If (F2x[1, 0]9C_x04[AddrCmdSetup] or F2x[1, 0]9C_x04[CsOdtSetup] or F2x[1, 0]9C_x04[CkeSetup] = 1) then K = K + 2
1524 if ((MemNGetBitFieldNb (NBPtr, BFAddrTmgControl) & 0x0202020) == 0) {
1525 SubTotal += 1;
1526 } else {
1527 SubTotal += 2;
1528 }
1529
1530 // If the F2x[1, 0]78[RdPtrInit] field is 4, 5, 6 or 7 MEMCLKs,
1531 // then add 4, 3, 2, or 1 MEMCLKs, respectively to the sub-total.
1532 //
1533 SubTotal = SubTotal + (8 - (UINT16) MemNGetBitFieldNb (NBPtr, BFRdPtrInit));
1534
1535 return SubTotal;
1536}
1537
1538/* -----------------------------------------------------------------------------*/
1539/**
1540 *
1541 *
1542 * This function swaps bits for OnDimmMirror support
1543 *
1544 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1545 *
1546 */
1547
1548VOID
1549MemNSwapBitsNb (
1550 IN OUT MEM_NB_BLOCK *NBPtr
1551 )
1552{
1553 UINT8 ChipSel;
1554 UINT32 MRSReg;
1555
1556 ChipSel = (UINT8) MemNGetBitFieldNb (NBPtr, BFMrsChipSel);
1557 if ((ChipSel & 1) != 0) {
1558 MRSReg = MemNGetBitFieldNb (NBPtr, BFDramInitRegReg);
1559 if ((NBPtr->DCTPtr->Timings.DimmMirrorPresent & (1 << (ChipSel >> 1))) != 0) {
1560 MRSReg = (MRSReg & 0xFFFCFE07) | ((MRSReg&0x100A8) << 1) | ((MRSReg&0x20150) >> 1);
1561 MemNSetBitFieldNb (NBPtr, BFDramInitRegReg, MRSReg);
1562 }
1563 }
1564}
1565
1566/* -----------------------------------------------------------------------------*/
1567/**
1568 *
1569 *
1570 * This function swaps bits for OnDimmMirror support for Unb
1571 *
1572 * Dimm Mirroring Requires that, during MRS command cycles, the following
1573 * bits are swapped by software
1574 *
1575 * A3 -> A4 A7 -> A8
1576 * A4 -> A3 BA0 -> BA1
1577 * A5 -> A6 BA1 -> BA0
1578 * A6 -> A5
1579 *
1580 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1581 *
1582 */
1583
1584VOID
1585MemNSwapBitsUnb (
1586 IN OUT MEM_NB_BLOCK *NBPtr
1587 )
1588{
1589 UINT8 ChipSel;
1590 UINT32 MRSBank;
1591 UINT32 MRSAddr;
1592
1593 ChipSel = (UINT8) MemNGetBitFieldNb (NBPtr, BFMrsChipSel);
1594 if ((ChipSel & 1) != 0) {
1595 if ((NBPtr->DCTPtr->Timings.DimmMirrorPresent & (1 << (ChipSel >> 1))) != 0) {
1596 MRSBank = MemNGetBitFieldNb (NBPtr, BFMrsBank);
1597 MRSAddr = MemNGetBitFieldNb (NBPtr, BFMrsAddress);
1598
1599 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tCS%d MR%d %05x swapped to ->",
1600 (ChipSel & 0x7),
1601 (MRSBank & 0x7),
1602 (MRSAddr & 0x3FFFF));
1603 //
1604 // Swap Mrs Bank bits 0 with 1
1605 MRSBank = (MRSBank & 0x0100) | ((MRSBank & 0x01) << 1) | ((MRSBank & 0x02) >> 1);
1606 //
1607 // Swap Mrs Address bits 3 with 4, 5 with 6, and 7 with 8
1608 MRSAddr = (MRSAddr & 0x03FE07) | ((MRSAddr&0x000A8) << 1) | ((MRSAddr&0x00150) >> 1);
1609 MemNSetBitFieldNb (NBPtr, BFMrsBank, MRSBank);
1610 MemNSetBitFieldNb (NBPtr, BFMrsAddress, MRSAddr);
1611 }
1612 }
1613}
1614
1615/* -----------------------------------------------------------------------------*/
1616/**
1617 *
1618 * Programs Address/command timings, driver strengths, and tri-state fields.
1619 *
1620 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1621 *
1622 */
1623VOID
1624MemNProgramPlatformSpecNb (
1625 IN OUT MEM_NB_BLOCK *NBPtr
1626 )
1627{
1628 CONST UINT8 PinType[3] = {PSO_CKE_TRI, PSO_ODT_TRI, PSO_CS_TRI};
1629 CONST UINT8 TabSize[3] = { 2, 4, 8};
1630 CONST BIT_FIELD_NAME BitField[3] = { BFCKETri, BFODTTri, BFChipSelTri};
1631 UINT8 *TabPtr;
1632 UINT8 i;
1633 UINT8 k;
1634 UINT8 Value;
1635 //===================================================================
1636 // Tristate unused CKE, ODT and chip select to save power
1637 //===================================================================
1638 //
1639 TabPtr = NULL;
1640 for (k = 0; k < sizeof (PinType); k++) {
1641 if (NBPtr->IsSupported[CheckFindPSOverideWithSocket]) {
1642 TabPtr = FindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PinType[k], NBPtr->MCTPtr->SocketId, MemNGetSocketRelativeChannelNb (NBPtr, NBPtr->Dct, 0), 0,
1643 &(NBPtr->MCTPtr->LogicalCpuid), &(NBPtr->MemPtr->StdHeader));
1644 }
1645 if (NBPtr->IsSupported[CheckFindPSDct]) {
1646 TabPtr = FindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PinType[k], NBPtr->MCTPtr->SocketId, NBPtr->Dct, 0,
1647 &(NBPtr->MCTPtr->LogicalCpuid), &(NBPtr->MemPtr->StdHeader));
1648 }
1649 if (TabPtr == NULL) {
1650 switch (k) {
1651 case 0:
1652 TabPtr = NBPtr->ChannelPtr->CKETriMap;
1653 break;
1654 case 1:
1655 TabPtr = NBPtr->ChannelPtr->ODTTriMap;
1656 break;
1657 case 2:
1658 TabPtr = NBPtr->ChannelPtr->ChipSelTriMap;
1659 break;
1660 default:
1661 IDS_ERROR_TRAP;
1662 }
1663 }
1664 ASSERT (TabPtr != NULL);
1665
1666 Value = 0;
1667 for (i = 0; i < TabSize[k]; i++) {
1668 if ((NBPtr->DCTPtr->Timings.CsPresent & TabPtr[i]) == 0) {
1669 Value |= (UINT8) (1 << i);
1670 }
1671 }
1672
1673 if (k == PSO_CS_TRI) {
1674 NBPtr->FamilySpecificHook[BeforeSetCsTri] (NBPtr, &Value);
1675 }
1676
1677 ASSERT (k < GET_SIZE_OF (BitField));
1678 MemNSetBitFieldNb (NBPtr, BitField[k], Value);
1679 }
1680 NBPtr->MemNBeforePlatformSpecNb (NBPtr);
1681
1682 //===================================================================
1683 // Program Address/Command timings and driver strength
1684 //===================================================================
1685 //
1686 MemProcessConditionalOverrides (NBPtr->RefPtr->PlatformMemoryConfiguration, NBPtr, PSO_ACTION_ADDRTMG, ALL_DIMMS);
1687 MemProcessConditionalOverrides (NBPtr->RefPtr->PlatformMemoryConfiguration, NBPtr, PSO_ACTION_ODCCONTROL, ALL_DIMMS);
1688
1689 MemNSetBitFieldNb (NBPtr, BFSlowAccessMode, (NBPtr->ChannelPtr->SlowMode) ? 1 : 0);
1690 MemNSetBitFieldNb (NBPtr, BFODCControl, NBPtr->ChannelPtr->DctOdcCtl);
1691 MemNSetBitFieldNb (NBPtr, BFAddrTmgControl, NBPtr->ChannelPtr->DctAddrTmg);
1692 NBPtr->FamilySpecificHook[SetDqsODT] (NBPtr, NBPtr);
1693
1694 if (NBPtr->IsSupported[CheckODTControls]) {
1695 MemNSetBitFieldNb (NBPtr, BFPhyRODTCSLow, NBPtr->ChannelPtr->PhyRODTCSLow);
1696 MemNSetBitFieldNb (NBPtr, BFPhyRODTCSHigh, NBPtr->ChannelPtr->PhyRODTCSHigh);
1697 MemNSetBitFieldNb (NBPtr, BFPhyWODTCSLow, NBPtr->ChannelPtr->PhyWODTCSLow);
1698 MemNSetBitFieldNb (NBPtr, BFPhyWODTCSHigh, NBPtr->ChannelPtr->PhyWODTCSHigh);
1699 }
1700}
1701/* -----------------------------------------------------------------------------*/
1702/**
1703 *
1704 *
1705 * This function gets the Trdrd value
1706 *
1707 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1708 *
1709 * @return Trdrd value
1710 */
1711
1712UINT8
1713MemNGetTrdrdNb (
1714 IN OUT MEM_NB_BLOCK *NBPtr
1715 )
1716{
1717 DCT_STRUCT *DCTPtr;
1718 INT8 Cgdd;
1719
1720 DCTPtr = NBPtr->DCTPtr;
1721
1722 // BIOS calculates Trdrd (in MEMCLKs) = CGDD / 2 + 3 clocks and programs F2x[1, 0]8C[Trdrd] with the
1723 // converted field value. BIOS rounds fractional values down.
1724 // The Critical Gross Delay Difference (CGDD) for Trdrd on any given byte lane is the largest F2x[1,
1725 // 0]9C_x[3:0][2B:10][DqsRcvEnGrossDelay] delay of any DIMM minus the F2x[1,
1726 // 0]9C_x[3:0][2B:10][DqsRcvEnGrossDelay] delay of any other DIMM.
1727
1728 Cgdd = MemNGetOptimalCGDDNb (NBPtr, AccessRcvEnDly, AccessRcvEnDly);
1729 DCTPtr->Timings.Trdrd = (Cgdd / 2) + 3;
1730
1731 // Transfer clk to reg definition, 2T is 00b, etc.
1732 DCTPtr->Timings.Trdrd -= 2;
1733 if (DCTPtr->Timings.Trdrd > 8) {
1734 DCTPtr->Timings.Trdrd = 8;
1735 }
1736
1737 return DCTPtr->Timings.Trdrd;
1738}
1739
1740
1741/* -----------------------------------------------------------------------------*/
1742/**
1743 *
1744 *
1745 * This function gets the Twrwr value
1746 *
1747 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1748 *
1749 * @return Twrwr value
1750 */
1751
1752UINT8
1753MemNGetTwrwrNb (
1754 IN OUT MEM_NB_BLOCK *NBPtr
1755 )
1756{
1757 DCT_STRUCT *DCTPtr;
1758 INT8 Cgdd;
1759
1760 DCTPtr = NBPtr->DCTPtr;
1761
1762 // Twrwr (in MEMCLKs) = CGDD / 2 + 3 clocks and programs F2x[1, 0]8C[Twrwr] with the
1763 // converted field value. BIOS rounds fractional values down.
1764 // On any given byte lane, the largest F2x[1, 0]9C_x[3:0][A, 7, 6, 0][2:1]:F2x[1, 0]9C_x[3:0][A, 7, 6,
1765 // 0]3[WrDatGrossDlyByte] delay of any DIMM minus the F2x[1, 0]9C_x[3:0][A, 7, 6, 0][2:1]:F2x[1,
1766 // 0]9C_x[3:0][A, 7, 6, 0]3[WrDatGrossDlyByte] delay of any other DIMM is equal to the Critical Gross
1767 // Delay Difference (CGDD) for Twrwr.
1768
1769 Cgdd = MemNGetOptimalCGDDNb (NBPtr, AccessWrDatDly, AccessWrDatDly);
1770 DCTPtr->Timings.Twrwr = (Cgdd / 2) + 3;
1771 NBPtr->TechPtr->AdjustTwrwr (NBPtr->TechPtr);
1772
1773 return DCTPtr->Timings.Twrwr;
1774}
1775
1776/* -----------------------------------------------------------------------------*/
1777/**
1778 *
1779 *
1780 * This function gets the Twrrd value. BIOS calculates Twrrd (in MEMCLKs) = CGDD / 2 - LD + 3 clocks and programs
1781 * F2x[1, 0]8C[Twrrd] with the converted field value. BIOS rounds fractional
1782 * values down.
1783 *
1784 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1785 *
1786 * @return Value to be programmed to Twrrd field
1787 * pDCT->Timings.Twrrd updated
1788 */
1789
1790UINT8
1791MemNGetTwrrdNb (
1792 IN OUT MEM_NB_BLOCK *NBPtr
1793 )
1794{
1795 INT8 Cgdd;
1796 INT8 Ld;
1797 INT8 Twrrd;
1798 DCT_STRUCT *DCTPtr;
1799
1800 DCTPtr = NBPtr->DCTPtr;
1801
1802 //
1803 // For DDR3, BIOS calculates the latency difference (Ld) as equal to read CAS latency minus write CAS
1804 // latency, in MEMCLKs (see F2x[1, 0]88[Tcl] and F2x[1, 0]84[Tcwl]) which can be a negative or positive
1805 // value.
1806 // For DDR2, LD is always one clock (For DDR2, Tcwl is always Tcl minus 1).
1807 //
1808 Ld = NBPtr->TechPtr->GetLD (NBPtr->TechPtr);
1809
1810 // On any given byte lane, the largest WrDatGrossDlyByte delay of any DIMM
1811 // minus the DqsRcvEnGrossDelay delay of any other DIMM is
1812 // equal to the Critical Gross Delay Difference (CGDD) for Twrrd.
1813 Cgdd = MemNGetOptimalCGDDNb (NBPtr, AccessWrDatDly, AccessRcvEnDly);
1814 Twrrd = (Cgdd / 2) - Ld + 3;
1815 DCTPtr->Timings.Twrrd = (UINT8) ((Twrrd >= 0) ? Twrrd : 0);
1816 NBPtr->TechPtr->AdjustTwrrd (NBPtr->TechPtr);
1817
1818 return DCTPtr->Timings.Twrrd;
1819}
1820
1821/* -----------------------------------------------------------------------------*/
1822/**
1823 *
1824 *
1825 * This function gets the TrwtTO value
1826 *
1827 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1828 *
1829 * @return pDCT->Timings.TrwtTO updated
1830 */
1831
1832UINT8
1833MemNGetTrwtTONb (
1834 IN OUT MEM_NB_BLOCK *NBPtr
1835 )
1836{
1837 INT8 Cgdd;
1838 INT8 Ld;
1839 INT8 TrwtTO;
1840 DCT_STRUCT *DCTPtr;
1841
1842 DCTPtr = NBPtr->DCTPtr;
1843 //
1844 // For DDR3, BIOS calculates the latency difference (Ld) as equal to read CAS latency minus write CAS
1845 // latency, in MEMCLKs (see F2x[1, 0]88[Tcl] and F2x[1, 0]84[Tcwl]) which can be a negative or positive
1846 // value.
1847 // For DDR2, LD is always one clock (For DDR2, Tcwl is always Tcl minus 1).
1848 //
1849 Ld = NBPtr->TechPtr->GetLD (NBPtr->TechPtr);
1850
1851 // On any byte lane, the largest DqsRcvEnGrossDelay delay of any DIMM minus
1852 // the WrDatGrossDlyByte delay of any other DIMM is equal to the Critical Gross
1853 // Delay Difference (CGDD) for TrwtTO.
1854 Cgdd = MemNGetOptimalCGDDNb (NBPtr, AccessRcvEnDly, AccessWrDatDly);
1855 TrwtTO = (Cgdd / 2) + Ld + 3;
1856 TrwtTO -= 2;
1857 DCTPtr->Timings.TrwtTO = (UINT8) ((TrwtTO > 1) ? TrwtTO : 1);
1858
1859 return DCTPtr->Timings.TrwtTO;
1860}
1861
1862/* -----------------------------------------------------------------------------*/
1863/**
1864 *
1865 *
1866 * This function gets the TrwtWB value
1867 *
1868 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1869 *
1870 * @return TrwtWB value
1871 */
1872UINT8
1873MemNGetTrwtWBNb (
1874 IN OUT MEM_NB_BLOCK *NBPtr
1875 )
1876{
1877 DCT_STRUCT *DCTPtr;
1878
1879 DCTPtr = NBPtr->DCTPtr;
1880
1881 // TrwtWB ensures read-to-write data-bus turnaround.
1882 // This value should be one more than the programmed TrwtTO.
1883 return DCTPtr->Timings.TrwtWB = DCTPtr->Timings.TrwtTO;
1884}
1885
1886/* -----------------------------------------------------------------------------*/
1887/**
1888 *
1889 *
1890 * This function converts MemClk frequency in MHz to MemClkFreq value
1891 *
1892 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1893 * @param[in] Speed - MemClk frequency in MHz
1894 *
1895 * @return MemClkFreq value
1896 */
1897UINT8
1898MemNGetMemClkFreqIdNb (
1899 IN OUT MEM_NB_BLOCK *NBPtr,
1900 IN UINT16 Speed
1901 )
1902{
1903 return (UINT8) ((Speed < DDR800_FREQUENCY) ? ((Speed / 66) - 3) : (Speed / 133));
1904}
1905
1906/* -----------------------------------------------------------------------------*/
1907/**
1908 *
1909 *
1910 * This function enables swapping interleaved region feature.
1911 *
1912 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1913 * @param[in] Base - Swap interleaved region base [47:27]
1914 * @param[in] Limit - Swap interleaved region limit [47:27]
1915 *
1916 */
1917VOID
1918MemNEnableSwapIntlvRgnNb (
1919 IN OUT MEM_NB_BLOCK *NBPtr,
1920 IN UINT32 Base,
1921 IN UINT32 Limit
1922 )
1923{
1924 UINT32 Size;
1925 UINT32 SizeOfAlign;
1926
1927 // Swapped interleaving region must be below 16G
1928 if (Limit < (1 << (34 - 27))) {
1929 // Adjust Base and Size to meet :
1930 // 1. The size of the swapped region must be less than or equal to the alignment of F2x10C[IntLvRegionBase].
1931 // 2. Entire UMA region is swapped with interleaving region.
1932 Size = Limit - Base;
1933 SizeOfAlign = (UINT32) 1 << LibAmdBitScanForward (Base);
1934 while (SizeOfAlign <= Size) {
1935 // In case of SizeOfAlign <= Size, UmaBase -= 128MB, SizeOfIntlvrgn += 128MB.
1936 Base -= 1;
1937 Size += 1;
1938 SizeOfAlign = (UINT32) 1 << LibAmdBitScanForward (Base);
1939 }
1940 MemNSetBitFieldNb (NBPtr, BFIntLvRgnBaseAddr, Base);
1941 MemNSetBitFieldNb (NBPtr, BFIntLvRgnLmtAddr, (Limit - 1));
1942 MemNSetBitFieldNb (NBPtr, BFIntLvRgnSize, Size);
1943 MemNSetBitFieldNb (NBPtr, BFIntLvRgnSwapEn, 1);
1944 }
1945}
1946
1947/* -----------------------------------------------------------------------------*/
1948/**
1949 *
1950 *
1951 * This function converts MemClk frequency in MHz to MemClkFreq value
1952 *
1953 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1954 * @param[in] Speed - MemClk frequency in MHz
1955 *
1956 * @return MemClkFreq value
1957 */
1958UINT8
1959MemNGetMemClkFreqIdClientNb (
1960 IN OUT MEM_NB_BLOCK *NBPtr,
1961 IN UINT16 Speed
1962 )
1963{
1964 return (UINT8) ((Speed > DDR400_FREQUENCY) ? ((Speed / 33) - 6) : ((Speed == DDR400_FREQUENCY) ? 2 : (Speed / 55)));
1965}
1966
1967/* -----------------------------------------------------------------------------*/
1968/**
1969 *
1970 *
1971 * This function converts MemClk frequency in MHz to MemClkFreq value
1972 *
1973 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1974 * @param[in] Speed - MemClk frequency in MHz
1975 *
1976 * @return MemClkFreq value
1977 */
1978UINT8
1979MemNGetMemClkFreqIdUnb (
1980 IN OUT MEM_NB_BLOCK *NBPtr,
1981 IN UINT16 Speed
1982 )
1983{
1984 return (UINT8) ((Speed > DDR400_FREQUENCY) ? ((Speed / 33) - 6) : ((Speed == DDR400_FREQUENCY) ? 2 : (Speed / 55)));
1985}
1986
1987/* -----------------------------------------------------------------------------*/
1988/**
1989 *
1990 *
1991 * This function converts MemClkFreq Id value to MemClk frequency in MHz
1992 *
1993 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
1994 * @param[in] FreqId - FreqId from Register
1995 *
1996 * @return MemClk frequency in MHz
1997 */
1998UINT16
1999MemNGetMemClkFreqUnb (
2000 IN OUT MEM_NB_BLOCK *NBPtr,
2001 IN UINT8 FreqId
2002 )
2003{
2004 UINT16 MemClkFreq;
2005 if (FreqId > 2) {
2006 MemClkFreq = (FreqId == 14) ? 667 : (300 + ((FreqId - 3) * 33) + (FreqId - 3) / 3);
2007 } else if (FreqId == 2) {
2008 MemClkFreq = 200;
2009 } else {
2010 MemClkFreq = 50 + (50 * FreqId);
2011 }
2012 return MemClkFreq;
2013}
2014
2015/* -----------------------------------------------------------------------------*/
2016/**
2017 *
2018 * This function change MemClk frequency to the value that is specified by DCTPtr->Timings.Speed
2019 * for client NB.
2020 *
2021 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
2022 *
2023 */
2024
2025VOID
2026MemNChangeFrequencyClientNb (
2027 IN OUT MEM_NB_BLOCK *NBPtr
2028 )
2029{
2030 MEM_TECH_BLOCK *TechPtr;
2031 UINT8 Dct;
2032 UINT8 ChipSel;
2033 UINT16 FinalPllLockTime;
2034 BOOLEAN FrequencyChangeSuccess;
2035 UINT64 OrgMMIOCfgBase;
2036 UINT64 NewMMIOCfgBase;
2037
2038 TechPtr = NBPtr->TechPtr;
2039
2040 // Disable MMIO to prevent speculative DRAM reads during self refresh
2041 LibAmdMsrRead (MSR_MMIO_Cfg_Base, &OrgMMIOCfgBase, &(NBPtr->MemPtr->StdHeader));
2042 NewMMIOCfgBase = OrgMMIOCfgBase & (~(BIT0));
2043 LibAmdMsrWrite (MSR_MMIO_Cfg_Base, &NewMMIOCfgBase, &(NBPtr->MemPtr->StdHeader));
2044
2045 MemNBrdcstSetNb (NBPtr, BFDisDllShutdownSR, 1);
2046
2047 //Program F2x[1,0]90[EnterSelfRefresh]=1.
2048 //Wait until the hardware resets F2x[1,0]90[EnterSelfRefresh]=0.
2049 MemNBrdcstSetNb (NBPtr, BFEnterSelfRef, 1);
2050 MemNPollBitFieldNb (NBPtr, BFEnterSelfRef, 0, PCI_ACCESS_TIMEOUT, TRUE);
2051
2052 if (NBPtr->ChangeNbFrequency (NBPtr)) {
2053 // Reprogram Twr, Tcwl, and Tcl based on the new MEMCLK frequency.
2054 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
2055 MemNSwitchDCTNb (NBPtr, Dct);
2056 if (NBPtr->DCTPtr->Timings.DctMemSize != 0) {
2057 TechPtr->AutoCycTiming (TechPtr);
2058 if (!MemNPlatformSpecUnb (NBPtr)) {
2059 IDS_ERROR_TRAP;
2060 }
2061 }
2062 }
2063
2064 // 1. Program PllLockTime to Family-specific value
2065 MemNBrdcstSetNb (NBPtr, BFPllLockTime, NBPtr->FreqChangeParam->PllLockTimeDefault);
2066
2067 // 2. Program D18F2x[1,0]94[MemClkFreqVal] = 0.
2068 MemNBrdcstSetNb (NBPtr, BFMemClkFreqVal, 0);
2069
2070 // 3. Program D18F2x[1,0]94[MemClkFreq] to the desired DRAM frequency.
2071 MemNBrdcstSetNb (NBPtr, BFMemClkFreq, NBPtr->GetMemClkFreqId (NBPtr, NBPtr->DCTPtr->Timings.Speed));
2072
2073 // 4. Program D18F2x[1,0]F4_x30[DbeGskFifoNumerator] and D18F2x[1,0]F4_x31[DbeGskFifoDenominator].
2074 // 5. Program D18F2x[1,0]F4_x32[DataTxFifoSchedDlyNegSlot1, DataTxFifoSchedDlySlot1,
2075 // DataTxFifoSchedDlyNegSlot0, DataTxFifoSchedDlySlot0]. See 2.10.3.2.2.1 [DCT Transmit Fifo Schedule
2076 // Delay Programming].
2077 // 6. D18F2x[1,0]78[RdPtrInit] = IF (D18F2x[1,0]94[MemClkFreq] >= 667 MHz) THEN 7 ELSE 8 ENDIF (Llano)
2078 // THEN 2 ELSE 3 ENDIF (Ontario)
2079 NBPtr->ProgramNbPsDependentRegs (NBPtr);
2080
2081 NBPtr->FamilySpecificHook[BeforeMemClkFreqVal] (NBPtr, NBPtr);
2082 IDS_OPTION_HOOK (IDS_BEFORE_MEM_FREQ_CHG, NBPtr, &(NBPtr->MemPtr->StdHeader));
2083 // 7. Program D18F2x[1,0]94[MemClkFreqVal] = 1.
2084 MemNBrdcstSetNb (NBPtr, BFMemClkFreqVal, 1);
2085 MemNPollBitFieldNb (NBPtr, BFFreqChgInProg, 0, PCI_ACCESS_TIMEOUT, TRUE);
2086 FinalPllLockTime = 0xF;
2087 NBPtr->FamilySpecificHook[AfterMemClkFreqVal] (NBPtr, &FinalPllLockTime);
2088
2089 // 8. IF (D18F2x[1,0]9C_x0D0F_E00A[CsrPhySrPllPdMode]==0) THEN program
2090 // D18F2x[1,0]9C_x0D0F_E006[PllLockTime] = 0Fh.
2091 if (!NBPtr->IsSupported[CsrPhyPllPdEn]) {
2092 MemNBrdcstSetNb (NBPtr, BFPllLockTime, FinalPllLockTime);
2093 }
2094
2095 FrequencyChangeSuccess = TRUE;
2096 } else {
2097 // If NB frequency cannot be updated, use the current speed as the target speed
2098 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
2099 MemNSwitchDCTNb (NBPtr, Dct);
2100 NBPtr->DCTPtr->Timings.Speed = NBPtr->TechPtr->PrevSpeed;
2101 NBPtr->DCTPtr->Timings.TargetSpeed = NBPtr->TechPtr->PrevSpeed;
2102 }
2103 FrequencyChangeSuccess = FALSE;
2104 }
2105
2106 //Program F2x[1,0]90[ExitSelfRef]=1 for both DCTs.
2107 //Wait until the hardware resets F2x[1, 0]90[ExitSelfRef]=0.
2108 MemNBrdcstSetNb (NBPtr, BFExitSelfRef, 1);
2109 MemNPollBitFieldNb (NBPtr, BFExitSelfRef, 0, PCI_ACCESS_TIMEOUT, TRUE);
2110 MemNBrdcstSetNb (NBPtr, BFDisDllShutdownSR, 0);
2111
2112 if (FrequencyChangeSuccess) {
2113 NBPtr->FamilySpecificHook[AfterMemClkFreqChg] (NBPtr, NULL);
2114
2115 // Perform Phy Fence training and Phy comp init after frequency change
2116 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
2117 MemNSwitchDCTNb (NBPtr, Dct);
2118 if (NBPtr->DCTPtr->Timings.DctMemSize != 0) {
2119 IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct);
2120
2121 // Phy fence programming
2122 AGESA_TESTPOINT (TpProcMemPhyFenceTraining, &(NBPtr->MemPtr->StdHeader));
2123 NBPtr->PhyFenceTraining (NBPtr);
2124
2125 // Phy compensation initialization
2126 AGESA_TESTPOINT (TPProcMemPhyCompensation, &(NBPtr->MemPtr->StdHeader));
2127 NBPtr->MemNInitPhyComp (NBPtr);
2128 MemProcessConditionalOverrides (NBPtr->RefPtr->PlatformMemoryConfiguration, NBPtr, PSO_ACTION_SLEWRATE, ALL_DIMMS);
2129 }
2130 }
2131
2132 //======================================================================
2133 // Calculate and program DRAM Timings at new frequency
2134 //======================================================================
2135 //
2136 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
2137 IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct);
2138 MemNSwitchDCTNb (NBPtr, Dct);
2139 if (NBPtr->DCTPtr->Timings.DctMemSize != 0) {
2140 for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel++) {
2141 if ((NBPtr->DCTPtr->Timings.CsPresent & ((UINT16)1 << ChipSel)) != 0) {
2142 // if chip select present
2143 if (!(TechPtr->TechnologySpecificHook[LrdimmSendAllMRCmds] (TechPtr, &ChipSel))) {
2144 TechPtr->SendAllMRCmds (TechPtr, ChipSel);
2145 }
2146 }
2147 }
2148 // Wait 512 clocks for DLL-relock
2149 MemNWaitXMemClksNb (NBPtr, 512);
2150 }
2151 }
2152 }
2153
2154 // Restore MMIO setting
2155 LibAmdMsrWrite (MSR_MMIO_Cfg_Base, &OrgMMIOCfgBase, &(NBPtr->MemPtr->StdHeader));
2156
2157 MemFInitTableDrive (NBPtr, MTAfterFreqChg);
2158}
2159
2160/* -----------------------------------------------------------------------------*/
2161/**
2162 *
2163 * This function change MemClk frequency to the value that is specified by DCTPtr->Timings.Speed
2164 * for UNB.
2165 *
2166 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
2167 *
2168 */
2169
2170VOID
2171MemNChangeFrequencyUnb (
2172 IN OUT MEM_NB_BLOCK *NBPtr
2173 )
2174{
2175 MEM_TECH_BLOCK *TechPtr;
2176 UINT8 Dct;
2177 UINT8 ChipSel;
2178 UINT16 FinalPllLockTime;
2179 BOOLEAN FrequencyChangeSuccess;
2180 UINT64 OrgMMIOCfgBase;
2181 UINT64 NewMMIOCfgBase;
2182
2183 TechPtr = NBPtr->TechPtr;
2184
2185 // Disable MMIO to prevent speculative DRAM reads during self refresh
2186 LibAmdMsrRead (MSR_MMIO_Cfg_Base, &OrgMMIOCfgBase, &(NBPtr->MemPtr->StdHeader));
2187 NewMMIOCfgBase = OrgMMIOCfgBase & (~(BIT0));
2188 LibAmdMsrWrite (MSR_MMIO_Cfg_Base, &NewMMIOCfgBase, &(NBPtr->MemPtr->StdHeader));
2189
2190 MemNBrdcstSetNb (NBPtr, BFDisDllShutdownSR, 1);
2191
2192 //Program F2x[1,0]90[EnterSelfRefresh]=1.
2193 //Wait until the hardware resets F2x[1,0]90[EnterSelfRefresh]=0.
2194 MemNBrdcstSetNb (NBPtr, BFEnterSelfRef, 1);
2195 MemNPollBitFieldNb (NBPtr, BFEnterSelfRef, 0, PCI_ACCESS_TIMEOUT, TRUE);
2196
2197 if (NBPtr->ChangeNbFrequency (NBPtr)) {
2198 // Reprogram Twr, Tcwl, and Tcl based on the new MEMCLK frequency.
2199 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
2200 MemNSwitchDCTNb (NBPtr, Dct);
2201 if (NBPtr->DCTPtr->Timings.DctMemSize != 0) {
2202 TechPtr->AutoCycTiming (TechPtr);
2203 if (!MemNPlatformSpecUnb (NBPtr)) {
2204 IDS_ERROR_TRAP;
2205 }
2206 }
2207 }
2208
2209 // 1. Program PllLockTime to Family-specific value
2210 MemNBrdcstSetNb (NBPtr, BFPllLockTime, NBPtr->FreqChangeParam->PllLockTimeDefault);
2211
2212 // 2. Program D18F2x[1,0]94[MemClkFreqVal] = 0.
2213 MemNBrdcstSetNb (NBPtr, BFMemClkFreqVal, 0);
2214
2215 // 3. Program D18F2x[1,0]94[MemClkFreq] to the desired DRAM frequency.
2216 MemNBrdcstSetNb (NBPtr, BFMemClkFreq, NBPtr->GetMemClkFreqId (NBPtr, NBPtr->DCTPtr->Timings.Speed));
2217
2218 // 4. Program D18F2x[1,0]F4_x30[DbeGskFifoNumerator] and D18F2x[1,0]F4_x31[DbeGskFifoDenominator].
2219 // 5. Program D18F2x[1,0]F4_x32[DataTxFifoSchedDlyNegSlot1, DataTxFifoSchedDlySlot1,
2220 // DataTxFifoSchedDlyNegSlot0, DataTxFifoSchedDlySlot0]. See 2.10.3.2.2.1 [DCT Transmit Fifo Schedule
2221 // Delay Programming].
2222 // 6. D18F2x[1,0]78[RdPtrInit] = IF (D18F2x[1,0]94[MemClkFreq] >= 667 MHz) THEN 7 ELSE 8 ENDIF (Llano)
2223 // THEN 2 ELSE 3 ENDIF (Ontario)
2224 NBPtr->ProgramNbPsDependentRegs (NBPtr);
2225
2226 IDS_OPTION_HOOK (IDS_BEFORE_MEM_FREQ_CHG, NBPtr, &(NBPtr->MemPtr->StdHeader));
2227 // 7. Program D18F2x[1,0]94[MemClkFreqVal] = 1.
2228 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
2229 MemNSwitchDCTNb (NBPtr, Dct);
2230 if ((NBPtr->DCTPtr->Timings.DctMemSize != 0)) {
2231 MemNSetBitFieldNb (NBPtr, BFMemClkFreqVal, 1);
2232 MemNPollBitFieldNb (NBPtr, BFFreqChgInProg, 0, PCI_ACCESS_TIMEOUT, FALSE);
2233 }
2234 }
2235 FinalPllLockTime = 0xF;
2236 NBPtr->FamilySpecificHook[AfterMemClkFreqVal] (NBPtr, &FinalPllLockTime);
2237
2238 // 8. IF (D18F2x[1,0]9C_x0D0F_E00A[CsrPhySrPllPdMode]==0) THEN program
2239 // D18F2x[1,0]9C_x0D0F_E006[PllLockTime] = 0Fh.
2240 if (!NBPtr->IsSupported[CsrPhyPllPdEn]) {
2241 MemNBrdcstSetNb (NBPtr, BFPllLockTime, FinalPllLockTime);
2242 }
2243
2244 FrequencyChangeSuccess = TRUE;
2245 } else {
2246 // If NB frequency cannot be updated, use the current speed as the target speed
2247 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
2248 MemNSwitchDCTNb (NBPtr, Dct);
2249 NBPtr->DCTPtr->Timings.Speed = NBPtr->TechPtr->PrevSpeed;
2250 NBPtr->DCTPtr->Timings.TargetSpeed = NBPtr->TechPtr->PrevSpeed;
2251 }
2252 FrequencyChangeSuccess = FALSE;
2253 }
2254
2255 if (FrequencyChangeSuccess) {
2256 // Perform Phy Fence training and Phy comp init after frequency change
2257 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
2258 MemNSwitchDCTNb (NBPtr, Dct);
2259 if (NBPtr->DCTPtr->Timings.DctMemSize != 0) {
2260 IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct);
2261
2262 // Phy fence programming
2263 AGESA_TESTPOINT (TpProcMemPhyFenceTraining, &(NBPtr->MemPtr->StdHeader));
2264 NBPtr->PhyFenceTraining (NBPtr);
2265
2266 // Phy compensation initialization
2267 AGESA_TESTPOINT (TPProcMemPhyCompensation, &(NBPtr->MemPtr->StdHeader));
2268 NBPtr->MemNInitPhyComp (NBPtr);
2269 MemProcessConditionalOverrides (NBPtr->RefPtr->PlatformMemoryConfiguration, NBPtr, PSO_ACTION_SLEWRATE, ALL_DIMMS);
2270 }
2271 }
2272 }
2273
2274 //Program F2x[1,0]90[ExitSelfRef]=1 for both DCTs.
2275 //Wait until the hardware resets F2x[1, 0]90[ExitSelfRef]=0.
2276 MemNBrdcstSetNb (NBPtr, BFExitSelfRef, 1);
2277 MemNPollBitFieldNb (NBPtr, BFExitSelfRef, 0, PCI_ACCESS_TIMEOUT, TRUE);
2278 if (NBPtr->IsSupported[SetDllShutDown]) {
2279 MemNBrdcstSetNb (NBPtr, BFDisDllShutdownSR, 0);
2280 }
2281
2282 if (FrequencyChangeSuccess) {
2283 NBPtr->FamilySpecificHook[AfterMemClkFreqChg] (NBPtr, NULL);
2284
2285 //======================================================================
2286 // Calculate and program DRAM Timings at new frequency
2287 //======================================================================
2288 //
2289 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
2290 IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct);
2291 MemNSwitchDCTNb (NBPtr, Dct);
2292 if (NBPtr->DCTPtr->Timings.DctMemSize != 0) {
2293 for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel++) {
2294 if ((NBPtr->DCTPtr->Timings.CsPresent & ((UINT16)1 << ChipSel)) != 0) {
2295 // if chip select present
2296 if (!(TechPtr->TechnologySpecificHook[LrdimmSendAllMRCmds] (TechPtr, &ChipSel))) {
2297 TechPtr->SendAllMRCmds (TechPtr, ChipSel);
2298 }
2299 }
2300 }
2301 // Wait 512 clocks for DLL-relock
2302 MemNWaitXMemClksNb (NBPtr, 512);
2303 }
2304 }
2305 }
2306
2307 // Restore MMIO setting
2308 LibAmdMsrWrite (MSR_MMIO_Cfg_Base, &OrgMMIOCfgBase, &(NBPtr->MemPtr->StdHeader));
2309
2310 MemFInitTableDrive (NBPtr, MTAfterFreqChg);
2311}
2312
zbao7d94cf92012-07-02 14:19:14 +08002313/* -----------------------------------------------------------------------------*/
2314CONST UINT8 PllDivTab[] = {0, 0, 0, 2, 3, 3, 2, 3};
2315CONST UINT8 PllMultTab[] = {0, 0, 0, 16, 32, 40, 32, 56};
2316
2317/**
2318 *
2319 * This function calculates and programs NB P-state dependent registers
2320 *
2321 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
2322 *
2323 */
2324
2325VOID
2326MemNProgramNbPstateDependentRegistersClientNb (
2327 IN OUT MEM_NB_BLOCK *NBPtr
2328 )
2329{
2330 UINT8 i;
2331 UINT8 Dct;
2332 UINT8 NclkFid;
2333 UINT16 MemClkDid;
2334 UINT8 PllMult;
2335 UINT8 NclkDiv;
2336 UINT8 RdPtrInitMin;
2337 UINT8 RdPtrInit;
2338 UINT32 NclkPeriod;
2339 UINT32 MemClkPeriod;
2340 INT32 PartialSum2x;
2341 INT32 PartialSumSlotI2x;
2342 INT32 RdPtrInitRmdr2x;
2343 INT32 TDataProp;
2344 UINT8 NbPstate;
2345 UINT8 SlowMode;
2346 UINT32 CalcNclkDiv;
2347 UINT32 DbeGskFifoNumeratorVal;
2348 UINT32 DbeGskFifoDenominatorVal;
2349
2350 CalcNclkDiv = 0;
2351 NclkFid = (UINT8) (MemNGetBitFieldNb (NBPtr, BFMainPllOpFreqId) + 0x10); // NclkFid is in 100MHz
2352
2353 MemClkDid = PllDivTab[NBPtr->DCTPtr->Timings.Speed / 133];
2354 NBPtr->FamilySpecificHook[OverridePllDiv] (NBPtr, &MemClkDid);
2355 PllMult = PllMultTab[NBPtr->DCTPtr->Timings.Speed / 133];
2356 NBPtr->FamilySpecificHook[OverridePllMult] (NBPtr, &PllMult);
2357
2358 if (NBPtr->NbFreqChgState == 2) {
2359 MemNSetBitFieldNb (NBPtr, BFNbPsCsrAccSel, 1);
2360 MemNSetBitFieldNb (NBPtr, BFNbPsDbgEn, 1);
2361 NclkDiv = (UINT8) MemNGetBitFieldNb (NBPtr, BFNbPs1NclkDiv);
2362 // Divisors less than 8 are undefined. Maybe the CPU does not support NB P-states.
2363 if (NclkDiv < 8) {
2364 // Set a dummy divisor to prevent divide by zero exception below.
2365 NclkDiv = 8;
2366 }
2367 NbPstate = 1;
2368 } else {
2369 NclkDiv = (UINT8) MemNGetBitFieldNb (NBPtr, BFNbPs0NclkDiv);
2370 NbPstate = 0;
2371 }
2372
2373 if (NclkDiv >= 0x60) {
2374 CalcNclkDiv = (NclkDiv - 0x40) * 10000;
2375 } else if (NclkDiv >= 0x40) {
2376 CalcNclkDiv = ((NclkDiv - 0x40) * 5000) + 160000;
2377 } else {
2378 CalcNclkDiv = 2500 * NclkDiv;
2379 }
2380
2381 NclkPeriod = CalcNclkDiv / NclkFid; // (1,000,000 * 0.25 * NclkDiv) / (NclkFid * 100MHz) = ps
2382 MemClkPeriod = 1000000 / NBPtr->DCTPtr->Timings.Speed;
2383 NBPtr->NBClkFreq = ((UINT32) NclkFid * 1000000) / CalcNclkDiv;
2384
2385 IDS_HDT_CONSOLE (MEM_FLOW, "\n\tNB P%d Freq: %dMHz\n", NbPstate, NBPtr->NBClkFreq);
2386 IDS_HDT_CONSOLE (MEM_FLOW, "\tMemClk Freq: %dMHz\n", NBPtr->DCTPtr->Timings.Speed);
2387 // D18F2x[1,0]78[RdPtrInit] = IF (D18F2x[1,0]94[MemClkFreq] >= 667 MHz) THEN 7 ELSE 8 ENDIF (Llano)
2388 // THEN 2 ELSE 3 ENDIF (Ontario)
2389 RdPtrInit = RdPtrInitMin = (NBPtr->DCTPtr->Timings.Speed >= DDR1333_FREQUENCY) ? NBPtr->FreqChangeParam->RdPtrInit667orHigher : NBPtr->FreqChangeParam->RdPtrInitLower667;
2390 NBPtr->FamilySpecificHook[AdjustRdPtrInit] (NBPtr, &RdPtrInit);
2391 MemNBrdcstSetNb (NBPtr, BFRdPtrInit, RdPtrInit);
2392 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tRdPtr: %d\n", RdPtrInit);
2393
2394 // Program D18F2x[1,0]F4_x30[DbeGskFifoNumerator] and D18F2x[1,0]F4_x31[DbeGskFifoDenominator].
2395 DbeGskFifoNumeratorVal = NclkFid * MemClkDid * 16;
2396 DbeGskFifoDenominatorVal = NclkDiv * PllMult;
2397 if (NclkDiv >= 0x40) {
2398 DbeGskFifoNumeratorVal = NclkFid * MemClkDid * 4;
2399 DbeGskFifoDenominatorVal = CalcNclkDiv * PllMult / 10000;
2400 }
2401 MemNBrdcstSetNb (NBPtr, BFDbeGskFifoNumerator, DbeGskFifoNumeratorVal);
2402 MemNBrdcstSetNb (NBPtr, BFDbeGskFifoDenominator, DbeGskFifoDenominatorVal);
2403 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDbeGskFifoNumerator: %d\n", DbeGskFifoNumeratorVal);
2404 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDbeGskFifoDenominator: %d\n", DbeGskFifoDenominatorVal);
2405
2406 // Program D18F2x[1,0]F4_x32[DataTxFifoSchedDlyNegSlot1, DataTxFifoSchedDlySlot1,
2407 // DataTxFifoSchedDlyNegSlot0, DataTxFifoSchedDlySlot0].
2408 // PartialSum = ((7 * NclkPeriod) + (1.5 * MemClkPeriod) + 520ps)*MemClkFrequency - tCWL -
2409 // CmdSetup - PtrSeparation - 1. (Llano)
2410 // PartialSum = ((5 * NclkPeriod) + MemClkPeriod) + 520ps)*MemClkFrequency - tCWL -
2411 // CmdSetup - PtrSeparation - 1. (Ontario)
2412 for (Dct = 0; Dct < NBPtr->DctCount; Dct++) {
2413 MemNSwitchDCTNb (NBPtr, Dct);
2414 if (NBPtr->DCTPtr->Timings.DctMemSize != 0) {
2415 PartialSum2x = NBPtr->FreqChangeParam->NclkPeriodMul2x * NclkPeriod;
2416 PartialSum2x += NBPtr->FreqChangeParam->MemClkPeriodMul2x * MemClkPeriod;
2417 PartialSum2x += 520 * 2;
2418
2419 // PtrSeparation = ((16 + RdPtrInitMin - D18F2x[1,0]78[RdPtrInit]) MOD 16)/2 + RdPtrInitRmdr
2420 // If (D18F2x[1,0]94[MemClkFreq] >= 800 MHz)
2421 // then RdPtrInitRmdr = (((4.5 * MemClkPeriod) - 990ps) MOD MemClkPeriod)/MemClkPeriod
2422 // else RdPtrInitRmdr = (((4.5 * MemClkPeriod) - 1466ps) MOD MemClkPeriod)/MemClkPeriod
2423 TDataProp = (NBPtr->DCTPtr->Timings.Speed >= DDR1600_FREQUENCY) ?
2424 NBPtr->FreqChangeParam->TDataProp800orHigher : NBPtr->FreqChangeParam->TDataPropLower800;
2425 RdPtrInitRmdr2x = ((NBPtr->FreqChangeParam->SyncTimeMul4x * MemClkPeriod) / 2) - 2 * (TDataProp + 520);
2426 RdPtrInitRmdr2x %= MemClkPeriod;
2427 PartialSum2x -= ((16 + RdPtrInitMin - RdPtrInit) % 16) * MemClkPeriod + RdPtrInitRmdr2x;
2428
2429 // Convert PartialSum2x to PCLK
2430 PartialSum2x = (PartialSum2x + MemClkPeriod - 1) / MemClkPeriod; // round-up here
2431 PartialSum2x -= 2 * (MemNGetBitFieldNb (NBPtr, BFTcwl) + 5);
2432 if ((MemNGetBitFieldNb (NBPtr, BFAddrTmgControl) & 0x0202020) == 0) {
2433 PartialSum2x -= 1;
2434 } else {
2435 PartialSum2x -= 2;
2436 }
2437 PartialSum2x -= 2;
2438
2439 // If PartialSumSlotN is positive:
2440 // DataTxFifoSchedDlySlotN=CEIL(PartialSumSlotN).
2441 // DataTxFifoSchedDlyNegSlotN=0.
2442 // Else if PartialSumSlotN is negative:
2443 // DataTxFifoSchedDlySlotN=ABS(CEIL(PartialSumSlotN*MemClkPeriod/NclkPeriod)).
2444 // DataTxFifoSchedDlyNegSlotN=1.
2445 for (i = 0; i < 2; i++) {
2446 PartialSumSlotI2x = PartialSum2x;
2447 SlowMode = (UINT8) MemNGetBitFieldNb (NBPtr, BFSlowAccessMode);
2448 if ((i == 0) && (SlowMode == 0)) {
2449 PartialSumSlotI2x += 2;
2450 }
2451 if (NBPtr->IsSupported[SchedDlySlot1Extra] && (i == 1) && (SlowMode != 0)) {
2452 PartialSumSlotI2x -= 2;
2453 }
2454 if (PartialSumSlotI2x > 0) {
2455 MemNSetBitFieldNb (NBPtr, BFDataTxFifoSchedDlyNegSlot0 + i, 0);
2456 MemNSetBitFieldNb (NBPtr, BFDataTxFifoSchedDlySlot0 + i, (PartialSumSlotI2x + 1) / 2);
2457 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDataTxFifoSchedDlySlot%d: %d\n", i, (PartialSumSlotI2x + 1) / 2);
2458 } else {
2459 MemNSetBitFieldNb (NBPtr, BFDataTxFifoSchedDlyNegSlot0 + i, 1);
2460 PartialSumSlotI2x = ((-PartialSumSlotI2x) * MemClkPeriod) / (2 * NclkPeriod);
2461 MemNSetBitFieldNb (NBPtr, BFDataTxFifoSchedDlySlot0 + i, PartialSumSlotI2x);
2462 IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDataTxFifoSchedDlySlot%d: -%d\n", i, PartialSumSlotI2x);
2463 }
2464 }
2465
2466 // Set ProcOdtAdv
2467 if ((NBPtr->DCTPtr->Timings.Speed <= DDR1333_FREQUENCY) &&
2468 ((!(NBPtr->IsSupported[EnProcOdtAdvForUDIMM])) || (NBPtr->ChannelPtr->SODimmPresent != 0))) {
2469 MemNSetBitFieldNb (NBPtr, BFProcOdtAdv, 0);
2470 } else {
2471 MemNSetBitFieldNb (NBPtr, BFProcOdtAdv, 0x4000);
2472 }
2473 }
2474 }
2475
2476 MemFInitTableDrive (NBPtr, MTAfterNbPstateChange);
2477 if (NBPtr->NbFreqChgState == 2) {
2478 MemNSetBitFieldNb (NBPtr, BFNbPsDbgEn, 0);
2479 MemNSetBitFieldNb (NBPtr, BFNbPsCsrAccSel, 0);
2480 }
2481}
2482
2483/* -----------------------------------------------------------------------------*/
2484/**
2485 *
2486 *
2487 * This function gets the total of sync components for Max Read Latency calculation
2488 *
2489 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
2490 *
2491 * @return Total in ps
2492 */
2493
2494UINT32
2495MemNTotalSyncComponentsClientNb (
2496 IN OUT MEM_NB_BLOCK *NBPtr
2497 )
2498{
2499 UINT32 P;
2500 UINT32 T;
2501 UINT8 RdPtrInitMin;
2502 UINT8 RdPtrInit;
2503 UINT32 AddrTmgCtl;
2504 UINT8 DbeGskMemClkAlignMode;
2505 UINT32 MemClkPeriod;
2506
2507 // P = P + ((16 + RdPtrInitMin - D18F2x[1,0]78[RdPtrInit]) MOD 16)
2508 RdPtrInitMin = (NBPtr->DCTPtr->Timings.Speed >= DDR1333_FREQUENCY) ? NBPtr->FreqChangeParam->RdPtrInit667orHigher : NBPtr->FreqChangeParam->RdPtrInitLower667;
2509 RdPtrInit = (UINT8) MemNGetBitFieldNb (NBPtr, BFRdPtrInit);
2510 P = (16 + RdPtrInitMin - RdPtrInit) % 16;
2511
2512 // IF (AddrCmdSetup != CkeSetup) THEN P = P + 1
2513 AddrTmgCtl = MemNGetBitFieldNb (NBPtr, BFAddrTmgControl);
2514 if (((AddrTmgCtl >> 16) & 0x20) != (AddrTmgCtl & 0x20)) {
2515 P += 1;
2516 }
2517
2518 // IF (DbeGskMemClkAlignMode==01b || (DbeGskMemClkAlignMode==00b && !(AddrCmdSetup==CsOdtSetup==CkeSetup)))
2519 // THEN P = P + 1
2520 DbeGskMemClkAlignMode = (UINT8) MemNGetBitFieldNb (NBPtr, BFDbeGskMemClkAlignMode);
2521 if ((DbeGskMemClkAlignMode == 1) || ((DbeGskMemClkAlignMode == 0) &&
2522 !((((AddrTmgCtl >> 16) & 0x20) == (AddrTmgCtl & 0x20)) && (((AddrTmgCtl >> 8) & 0x20) == (AddrTmgCtl & 0x20))))) {
2523 P += 1;
2524 }
2525
2526 // IF (SlowAccessMode==1) THEN P = P + 2
2527 if (MemNGetBitFieldNb (NBPtr, BFSlowAccessMode) == 1) {
2528 P += 2;
2529 }
2530
2531 // P = P + 2
2532 P += 2;
2533 T = 0;
2534
2535 // If (AddrCmdSetup==0 && CsOdtSetup==0 && CkeSetup==0)
2536 // then P = P + 1
2537 // else P = P + 2
2538 if ((AddrTmgCtl & 0x0202020) == 0) {
2539 P += 1;
2540 } else {
2541 P += 2;
2542 }
2543
2544 // P = P + (2 * (D18F2x[1,0]88[Tcl] clocks - 1))
2545 P += 2 * (NBPtr->DCTPtr->Timings.CasL - 1);
2546
2547 // If (DisCutThroughMode==0)
2548 // then P = P + 3
2549 // else P = P + 7
2550 if (MemNGetBitFieldNb (NBPtr, BFDisCutThroughMode) == 0) {
2551 P += 3;
2552 } else {
2553 P += 7;
2554 }
2555
2556 MemClkPeriod = 1000000 / NBPtr->DCTPtr->Timings.Speed;
2557 return (((P * MemClkPeriod + 1) / 2) + T);
2558}
2559
2560/* -----------------------------------------------------------------------------*/
2561/**
2562 *
2563 *
2564 * This function sets up phy power saving for client NB
2565 *
2566 *
2567 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
2568 *
2569 */
2570VOID
2571MemNPhyPowerSavingClientNb (
2572 IN OUT MEM_NB_BLOCK *NBPtr
2573 )
2574{
2575 // 4. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]13[DllDisEarlyU] = 1b.
2576 // 5. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]13[DllDisEarlyL] = 1b.
2577 // 6. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]13[7:4] = 1010b.
2578 MemNSetBitFieldNb (NBPtr, BFPhy0x0D0F0F13Bit0to7, 0xA3);
2579 // 7. Program D18F2x[1,0]9C_x0D0F_812F[7, 5, 0] = {1b, 1b, 1b} to disable unused PAR and A[17:16] pins.
2580 MemNSetBitFieldNb (NBPtr, BFAddrCmdTri, MemNGetBitFieldNb (NBPtr, BFAddrCmdTri) | 0xA1);
2581 // 8. Program D18F2x[1,0]9C_x0D0F_C000[LowPowerDrvStrengthEn] = 1.
2582 if (!NBPtr->FamilySpecificHook[DisLowPwrDrvStr] (NBPtr, NULL)) {
2583 MemNSetBitFieldNb (NBPtr, BFLowPowerDrvStrengthEn, 0x100);
2584 }
2585 // 9. Program D18F2x[1,0]9C_x0D0F_0[F,7:0]10[EnRxPadStandby]= IF (D18F2x[1,0]94[MemClkFreq] <=
2586 // 800 MHz) THEN 1 ELSE 0 ENDIF.
2587 MemNSetBitFieldNb (NBPtr, BFEnRxPadStandby, (NBPtr->DCTPtr->Timings.Speed <= DDR1600_FREQUENCY) ? 0x1000 : 0);
2588 // 10. Program D18F2x[1,0]9C_x0000_000D as follows:
2589 // TxMaxDurDllNoLock/RxMaxDurDllNoLock = 7h.
2590 MemNSetBitFieldNb (NBPtr, BFRxMaxDurDllNoLock, 7);
2591 MemNSetBitFieldNb (NBPtr, BFTxMaxDurDllNoLock, 7);
2592 // TxCPUpdPeriod/RxCPUpdPeriod = 011b.
2593 MemNSetBitFieldNb (NBPtr, BFTxCPUpdPeriod, 3);
2594 MemNSetBitFieldNb (NBPtr, BFRxCPUpdPeriod, 3);
2595 // TxDLLWakeupTime/RxDLLWakeupTime = 11b.
2596 MemNSetBitFieldNb (NBPtr, BFTxDLLWakeupTime, 3);
2597 MemNSetBitFieldNb (NBPtr, BFRxDLLWakeupTime, 3);
2598
2599 IDS_OPTION_HOOK (IDS_PHY_DLL_STANDBY_CTRL, NBPtr, &NBPtr->MemPtr->StdHeader);
2600}
2601
2602/* -----------------------------------------------------------------------------*/
2603/**
2604 *
2605 *
2606 * This function sets up phy power saving for UNB
2607 *
2608 *
2609 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
2610 *
2611 */
2612VOID
2613MemNPhyPowerSavingUnb (
2614 IN OUT MEM_NB_BLOCK *NBPtr
2615 )
2616{
2617 UINT16 MixedX4AndX8Dimms;
2618
2619 // 4. Program D18F2x9C_x0D0F_0[F,8:0]13_dct[1:0][DllDisEarlyU] = 1b.
2620 // 5. Program D18F2x9C_x0D0F_0[F,8:0]13_dct[1:0][DllDisEarlyL] = 1b.
2621 MemNSetBitFieldNb (NBPtr, BFPhy0x0D0F0F13, MemNGetBitFieldNb (NBPtr, BFPhy0x0D0F0F13) | 3);
2622 // 6. D18F2x9C_x0D0F_0[F,8:0]13_dct[1:0][RxDqsUDllPowerDown] = (D18F2x90_dct[1:0][X4Dimm]!=0).
2623 MemNSetBitFieldNb (NBPtr, BFPhy0x0D0F0F13, MemNGetBitFieldNb (NBPtr, BFX4Dimm) == 0 ? (MemNGetBitFieldNb (NBPtr, BFPhy0x0D0F0F13) | 0x80) : (MemNGetBitFieldNb (NBPtr, BFPhy0x0D0F0F13) & 0xFF7F));
2624 // 7. D18F2x9C_x0D0F_812F_dct[1:0][PARTri] = ~D18F2x90_dct[1:0][ParEn].
2625 MemNSetBitFieldNb (NBPtr, BFAddrCmdTri, MemNGetBitFieldNb (NBPtr, BFParEn) == 0 ? (MemNGetBitFieldNb (NBPtr, BFAddrCmdTri) | 1) : (MemNGetBitFieldNb (NBPtr, BFAddrCmdTri) & 0xFFFE));
2626 // 8. D18F2x9C_x0D0F_812F_dct[1:0][Add17Tri, Add16Tri] = {1b, 1b}
2627 MemNSetBitFieldNb (NBPtr, BFAddrCmdTri, MemNGetBitFieldNb (NBPtr, BFAddrCmdTri) | 0xA0);
2628 // 9. IF (D18F2x94_dct[1:0][MemClkFreq] <= 800 MHz && ~(mixed channel of x4 and x8 DIMMs)) THEN
2629 // Program D18F2x9C_x0D0F_0[F,8:0]10_dct[1:0][EnRxPadStandby] = 1.
2630 // ELSE
2631 // Program D18F2x9C_x0D0F_0[F,8:0]10_dct[1:0][EnRxPadStandby] = 0.
2632 // ENDIF.
2633 MixedX4AndX8Dimms = NBPtr->DCTPtr->Timings.Dimmx4Present != 0 && NBPtr->DCTPtr->Timings.Dimmx8Present != 0;
2634 MemNSetBitFieldNb (NBPtr, BFEnRxPadStandby, (NBPtr->DCTPtr->Timings.Speed <= DDR1600_FREQUENCY) && !MixedX4AndX8Dimms ? 0x1000 : 0);
2635 // 10. IF (~(mixed channel of x4 and x8 DIMMs)) THEN
2636 if (MixedX4AndX8Dimms == FALSE) {
2637 // Program D18F2x9C_x0000_000D_dct[1:0] as follows:
2638 // TxMaxDurDllNoLock = RxMaxDurDllNoLock = 7h.
2639 MemNSetBitFieldNb (NBPtr, BFTxMaxDurDllNoLock, 7);
2640 MemNSetBitFieldNb (NBPtr, BFRxMaxDurDllNoLock, 7);
2641 // TxCPUpdPeriod = RxCPUpdPeriod = 011b.
2642 MemNSetBitFieldNb (NBPtr, BFTxCPUpdPeriod, 3);
2643 MemNSetBitFieldNb (NBPtr, BFRxCPUpdPeriod, 3);
2644 // TxDLLWakeupTime = RxDLLWakeupTime = 11b.
2645 MemNSetBitFieldNb (NBPtr, BFTxDLLWakeupTime, 3);
2646 MemNSetBitFieldNb (NBPtr, BFRxDLLWakeupTime, 3);
2647 } else {
2648 // ELSE
2649 // Program D18F2x9C_x0000_000D_dct[1:0][TxMaxDurDllNoLock, RxMaxDurDllNoLock, TxCPUpdPeriod,
2650 // RxCPUpdPeriod, TxDLLWakeupTime, RxDLLWakeupTime] = {0, 0, 0, 0, 0, 0}.
2651 MemNSetBitFieldNb (NBPtr, BFTxMaxDurDllNoLock, 0);
2652 MemNSetBitFieldNb (NBPtr, BFRxMaxDurDllNoLock, 0);
2653 MemNSetBitFieldNb (NBPtr, BFTxCPUpdPeriod, 0);
2654 MemNSetBitFieldNb (NBPtr, BFRxCPUpdPeriod, 0);
2655 MemNSetBitFieldNb (NBPtr, BFTxDLLWakeupTime, 0);
2656 MemNSetBitFieldNb (NBPtr, BFRxDLLWakeupTime, 0);
2657 }
2658 // 11. Program D18F2x9C_x0D0F_0[F,8:0]30_dct[1:0][PwrDn] to disable unused ECC byte lane.
2659 if (NBPtr->IsSupported[CheckEccDLLPwrDnConfig]) {
2660 if (!NBPtr->MCTPtr->Status[SbEccDimms]) {
2661 MemNSetBitFieldNb (NBPtr, BFEccDLLPwrDnConf, 0x0010);
2662 }
2663 }
2664
2665 // 12. Program D18F2x9C_x0D0F_0[F,8:0]04_dct[1:0][TriDM] = IF (LRDIMM & (D18F2x90_dct[1:0][X4Dimm] == 0)) THEN 1 ELSE 0.
2666 if (NBPtr->MCTPtr->Status[SbLrdimms]) {
2667 MemNSetBitFieldNb (NBPtr, BFDataByteDMConf, (MemNGetBitFieldNb (NBPtr, BFX4Dimm) == 0) ? 0x2000 : 0);
2668 }
2669
2670 IDS_OPTION_HOOK (IDS_PHY_DLL_STANDBY_CTRL, NBPtr, &NBPtr->MemPtr->StdHeader);
2671}
2672
2673/* -----------------------------------------------------------------------------*/
2674/**
2675 *
2676 *
2677 * This function overrides the ASR and SRT value in MRS command
2678 *
2679 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
2680 *
2681 */
2682VOID
2683MemNSetASRSRTNb (
2684 IN OUT MEM_NB_BLOCK *NBPtr
2685 )
2686{
2687 UINT32 MrsAddress;
2688 UINT8 Dimm;
2689 UINT8 *SpdBufferPtr;
2690 BOOLEAN ASREn;
2691 BOOLEAN SRTEn;
2692
2693 // Look for MR2
2694 if (NBPtr->GetBitField (NBPtr, BFMrsBank) == 2) {
2695 MrsAddress = NBPtr->GetBitField (NBPtr, BFMrsAddress);
2696 // Clear A6(ASR) and A7(SRT)
2697 MrsAddress &= (UINT32) ~0xC0;
2698 if ((NBPtr->ChannelPtr->RegDimmPresent) || (NBPtr->ChannelPtr->LrDimmPresent)) {
2699 // For registered dimm and LR dimm, MRS command is sent to all chipselects.
2700 // So different ASR/SRT setting can be sent to each chip select.
2701 Dimm = (UINT8) (NBPtr->GetBitField (NBPtr, BFMrsChipSel) >> 1);
2702 // Make sure we access SPD of the second logical dimm of QR dimm correctly
2703 if ((Dimm >= 2) && ((NBPtr->ChannelPtr->DimmQrPresent & (UINT8) (1 << Dimm)) != 0)) {
2704 Dimm -= 2;
2705 }
2706 if (NBPtr->TechPtr->GetDimmSpdBuffer (NBPtr->TechPtr, &SpdBufferPtr, Dimm)) {
2707 // Bit 2 is ASR
2708 if (SpdBufferPtr[THERMAL_OPT] & 0x4) {
2709 // when ASR is 1, set SRT to 0
2710 MrsAddress |= 0x40;
2711 } else {
2712 // Set SRT based on bit on of thermal byte
2713 MrsAddress |= ((SpdBufferPtr[THERMAL_OPT] & 1) << 7);
2714 }
2715 }
2716 } else {
2717 // Udimm and unbuffered dimm, MSR command will be broadcasted during Dram Init.
2718 // ASR/SRT value needs to be leveled across the DCT. Only if all dimms on the DCT
2719 // support ASR or SRT can ASR or SRT be enabled.
2720 ASREn = TRUE;
2721 SRTEn = TRUE;
2722 for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm ++) {
2723 if (NBPtr->TechPtr->GetDimmSpdBuffer (NBPtr->TechPtr, &SpdBufferPtr, Dimm)) {
2724 // Bit 2 is ASR
2725 if ((SpdBufferPtr[THERMAL_OPT] & 0x4) == 0) {
2726 // when any dimm in the DCT does not support ASR, disable ASR for the DCT
2727 ASREn = FALSE;
2728 // When any dimm does not have SRT with a value of 1, set SRT to 0 for the DCT
2729 if ((SpdBufferPtr[THERMAL_OPT] & 1) == 0) {
2730 SRTEn = FALSE;
2731 }
2732 }
2733 }
2734 }
2735 if (ASREn) {
2736 MrsAddress |= 0x40;
2737 } else {
2738 MrsAddress |= (UINT8) SRTEn << 7;
2739 }
2740 }
2741
2742 NBPtr->SetBitField (NBPtr, BFMrsAddress, MrsAddress);
2743 }
2744}
2745
2746/* -----------------------------------------------------------------------------*/
2747/**
2748 *
2749 * This function changes NB frequency as below:
2750 * NBP0-DDR800 -> NBP0-DDR1066 -> ... -> NBP0-DDRTarget -> NBP1-DDRTarget -> NBP0-DDRTarget
2751 *
2752 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
2753 *
2754 */
2755
2756BOOLEAN
2757MemNChangeNbFrequencyNb (
2758 IN OUT MEM_NB_BLOCK *NBPtr
2759 )
2760{
2761 BOOLEAN Status;
2762
2763 Status = FALSE;
2764
2765 // State machine to change NB frequency and NB Pstate
2766 switch (NBPtr->NbFreqChgState) {
2767 case 0:
2768 // Starting up by not changing NB P state, but only updating NB frequency based on current MemClk frequency
2769 Status = NBPtr->ChangeNbFrequencyWrap (NBPtr, 0);
2770 ASSERT (Status);
2771
2772 if (NBPtr->DCTPtr->Timings.Speed == NBPtr->DCTPtr->Timings.TargetSpeed) {
2773 // When MemClk has been ramped up to its max, transition to next state, which changes NBPstate to P1
2774 NBPtr->NbFreqChgState = 1;
2775 IDS_OPTION_HOOK (IDS_NB_PSTATE_DIDVID, NBPtr, &(NBPtr->MemPtr->StdHeader));
2776 }
2777 break;
2778
2779 case 1:
2780 // Clear ForceCasToSlot0 after MaxRdLatency training is completed for NB-P0
2781 MemNBrdcstSetNb (NBPtr, BFForceCasToSlot0, 0);
2782
2783 // Next state would be to change NBPstate back to P0
2784 NBPtr->NbFreqChgState = 2;
2785
2786 // Update NB freq dependent registers
2787 NBPtr->ProgramNbPsDependentRegs (NBPtr);
2788
2789 // Change NB P-State to NBP1 for MaxRdLat training
2790 if (NBPtr->ChangeNbFrequencyWrap (NBPtr, 1)) {
2791 // Enable cut through mode for NB P1
2792 MemNBrdcstSetNb (NBPtr, BFDisCutThroughMode, 0);
2793
2794 // Return TRUE to repeat MaxRdLat training
2795 Status = TRUE;
2796
2797 } else {
2798 // If transition to NB-P1 fails, transition to exit state machine
2799 NBPtr->NbFreqChgState = 3;
2800 }
2801 break;
2802
2803 case 2:
2804 // Clear ForceCasToSlot0 after MaxRdLatency training is completed for NB-P1
2805 MemNBrdcstSetNb (NBPtr, BFForceCasToSlot0, 0);
2806
2807 // Change NB P-State back to NBP0
2808 Status = NBPtr->ChangeNbFrequencyWrap (NBPtr, 0);
2809 ASSERT (Status);
2810
2811 // Return FALSE to get out of MaxRdLat training loop
2812 Status = FALSE;
2813
2814 // Exit state machine
2815 NBPtr->NbFreqChgState = 3;
2816 break;
2817
2818 default:
2819 break;
2820 }
2821
2822 return Status;
2823}
2824
2825/*-----------------------------------------------------------------------------
2826 *
2827 *
2828 * This function programs registers before phy fence training for CNB
2829 *
2830 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
2831 * @param[in,out] OptParam - Optional parameter
2832 *
2833 * @return TRUE
2834 * ----------------------------------------------------------------------------
2835 */
2836BOOLEAN
2837MemNBeforePhyFenceTrainingClientNb (
2838 IN OUT MEM_NB_BLOCK *NBPtr,
2839 IN OUT VOID *OptParam
2840 )
2841{
2842 IDS_HDT_CONSOLE (MEM_FLOW, "\tMemClkAlign=0\n");
2843 MemNBrdcstSetNb (NBPtr, BFDbeGskMemClkAlignMode, 0);
2844
2845 IDS_HDT_CONSOLE (MEM_FLOW, "\tEnDramInit = 1 for both DCTs\n");
2846 MemNBrdcstSetNb (NBPtr, BFEnDramInit, 1);
2847
2848 return TRUE;
2849}
2850
2851/* -----------------------------------------------------------------------------*/
2852/**
2853 *
2854 * This function changes NB frequency foras below:
2855 * NBP0-DDR800 -> NBP0-DDR1066 -> ... -> NBP0-DDRTarget -> NBP1-DDRTarget -> NBP2-DDRTarget -> NBP3-DDRTarget -> NBP0-DDRTarget
2856 *
2857 *
2858 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
2859 *
2860 */
2861
2862BOOLEAN
2863MemNChangeNbFrequencyUnb (
2864 IN OUT MEM_NB_BLOCK *NBPtr
2865 )
2866{
2867 BOOLEAN Status;
2868
2869 Status = FALSE;
2870
2871 // State machine to change NB frequency and NB Pstate
2872 switch (NBPtr->NbFreqChgState) {
2873 case 0:
2874 // Do not change NB Pstate, just to save initial NB Pstate value
2875 Status = NBPtr->ChangeNbFrequencyWrap (NBPtr, 0);
2876 if (NBPtr->DCTPtr->Timings.Speed == NBPtr->DCTPtr->Timings.TargetSpeed) {
2877 // When MemClk has been ramped up to its max, transition to next state, which changes NBPstate to P1
2878 NBPtr->NbFreqChgState = 1;
2879 IDS_OPTION_HOOK (IDS_NB_PSTATE_DIDVID, NBPtr, &(NBPtr->MemPtr->StdHeader));
2880 }
2881 break;
2882
2883 case 1:
2884 case 2:
2885 case 3:
2886 // Change NB P-State to NBP1 for MaxRdLat training
2887 if (NBPtr->ChangeNbFrequencyWrap (NBPtr, NBPtr->NbFreqChgState)) {
2888 // Next state is to try all NBPstates
2889 NBPtr->NbFreqChgState++;
2890
2891 // Return TRUE to repeat MaxRdLat training
2892 Status = TRUE;
2893 } else {
2894 // If transition to any NBPs fails, transition to exit state machine
2895 NBPtr->NbFreqChgState = 4;
2896 }
2897 break;
2898
2899 case 4:
2900 // Change NB P-State back to NBP0
2901 Status = NBPtr->ChangeNbFrequencyWrap (NBPtr, 0);
2902 ASSERT (Status);
2903
2904 // Return FALSE to get out of MaxRdLat training loop
2905 Status = FALSE;
2906
2907 // Exit state machine
2908 NBPtr->NbFreqChgState = 5;
2909 break;
2910
2911 default:
2912 break;
2913 }
2914
2915 return Status;
2916}
2917
2918
2919/* -----------------------------------------------------------------------------*/
2920/**
2921 *
2922 *
2923 * This function gets "Dram Term" value from data structure
2924 *
2925 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
2926 * @param[in] ChipSel - Targeted chipsel
2927 *
2928 * @return Dram Term value
2929 */
2930UINT8
2931MemNGetDramTermNb (
2932 IN OUT MEM_NB_BLOCK *NBPtr,
2933 IN UINT8 ChipSel
2934 )
2935{
2936 UINT8 DramTerm;
2937
2938 if ((NBPtr->ChannelPtr->DimmQrPresent & ((UINT16) (1 << (ChipSel >> 1)))) != 0) {
2939 DramTerm = NBPtr->PsPtr->QR_DramTerm;
2940 } else {
2941 DramTerm = NBPtr->PsPtr->DramTerm;
2942 }
2943
2944 return DramTerm;
2945}
2946
2947/* -----------------------------------------------------------------------------*/
2948/**
2949 *
2950 *
2951 * This function gets "Dram Term" value from data structure for Unb
2952 *
2953 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
2954 * @param[in] ChipSel - Targeted chipsel
2955 *
2956 * @return Dram Term value
2957 */
2958UINT8
2959MemNGetDramTermTblDrvNb (
2960 IN OUT MEM_NB_BLOCK *NBPtr,
2961 IN UINT8 ChipSel
2962 )
2963{
2964 UINT8 RttNom;
2965 RttNom = NBPtr->PsPtr->RttNom[ChipSel];
2966 IDS_OPTION_HOOK (IDS_MEM_DRAM_TERM, &RttNom, &NBPtr->MemPtr->StdHeader);
2967 return RttNom;
2968}
2969
2970/* -----------------------------------------------------------------------------*/
2971/**
2972 *
2973 *
2974 * This function gets "Dynamic Dram Term" value from data structure
2975 *
2976 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
2977 * @param[in] ChipSel - Targeted chipsel
2978 *
2979 * @return Dynamic Dram Term value
2980 */
2981UINT8
2982MemNGetDynDramTermNb (
2983 IN OUT MEM_NB_BLOCK *NBPtr,
2984 IN UINT8 ChipSel
2985 )
2986{
2987 return (NBPtr->PsPtr->DynamicDramTerm);
2988}
2989
2990/* -----------------------------------------------------------------------------*/
2991/**
2992 *
2993 *
2994 * This function gets "Dynamic Dram Term" value from data structure
2995 *
2996 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
2997 * @param[in] ChipSel - Targeted chipsel
2998 *
2999 * @return Dynamic Dram Term value
3000 */
3001UINT8
3002MemNGetDynDramTermTblDrvNb (
3003 IN OUT MEM_NB_BLOCK *NBPtr,
3004 IN UINT8 ChipSel
3005 )
3006{
3007 UINT8 RttWr;
3008 RttWr = NBPtr->PsPtr->RttWr[ChipSel];
3009 IDS_OPTION_HOOK (IDS_MEM_DYN_DRAM_TERM, &RttWr, &NBPtr->MemPtr->StdHeader);
3010 return RttWr;
3011}
3012
3013/* -----------------------------------------------------------------------------*/
3014/**
3015 *
3016 *
3017 * This function returns MR0[CL] value
3018 *
3019 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
3020 *
3021 * @return MR0[CL] value
3022 */
3023UINT32
3024MemNGetMR0CLNb (
3025 IN OUT MEM_NB_BLOCK *NBPtr
3026 )
3027{
3028 UINT8 Tcl;
3029 UINT32 Value32;
3030
3031 Tcl = (UINT8) MemNGetBitFieldNb (NBPtr, BFTcl);
3032 Value32 = (UINT32) ((Tcl < 8) ? (Tcl << 4) : (((Tcl - 8) << 4) | 4));
3033
3034 return Value32;
3035}
3036
3037/* -----------------------------------------------------------------------------*/
3038/**
3039 *
3040 *
3041 * This function returns MR0[WR] value
3042 *
3043 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
3044 *
3045 * @return MR0[WR] value
3046 */
3047UINT32
3048MemNGetMR0WRNb (
3049 IN OUT MEM_NB_BLOCK *NBPtr
3050 )
3051{
3052 UINT32 Value32;
3053
3054 Value32 = MemNGetBitFieldNb (NBPtr, BFTwrDDR3) << 9;
3055
3056 return Value32;
3057}
3058
3059/* -----------------------------------------------------------------------------*/
3060/**
3061 *
3062 *
3063 * This function returns MR0[WR] value
3064 *
3065 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
3066 *
3067 * @return MR0[WR] value
3068 */
3069UINT32
3070MemNGetMR0WRTblDrvNb (
3071 IN OUT MEM_NB_BLOCK *NBPtr
3072 )
3073{
3074 return (UINT32) (NBPtr->PsPtr->MR0WR << 9);
3075}
3076
3077/* -----------------------------------------------------------------------------*/
3078/**
3079 *
3080 *
3081 * This function returns MR2[CWL] value
3082 *
3083 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
3084 *
3085 * @return MR0[CWL] value
3086 */
3087UINT32
3088MemNGetMR2CWLNb (
3089 IN OUT MEM_NB_BLOCK *NBPtr
3090 )
3091{
3092 UINT32 Value32;
3093
3094 Value32 = MemNGetBitFieldNb (NBPtr, BFTcwl) << 3;
3095
3096 return Value32;
3097}
3098
3099/* -----------------------------------------------------------------------------*/
3100/**
3101 *
3102 * This function returns MR2[CWL] value for UNB
3103 *
3104 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
3105 *
3106 * @return MR0[CWL] value
3107 */
3108UINT32
3109MemNGetMR2CWLUnb (
3110 IN OUT MEM_NB_BLOCK *NBPtr
3111 )
3112{
3113 UINT32 Value32;
3114
3115 Value32 = (MemNGetBitFieldNb (NBPtr, BFTcwl) - 5) << 3;
3116
3117 return Value32;
3118}
3119
3120/* -----------------------------------------------------------------------------*/
3121/**
3122 *
3123 * This function sets Txp and Txpdll
3124 *
3125 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
3126 *
3127 * @return none
3128 */
3129VOID
3130MemNSetTxpNb (
3131 IN OUT MEM_NB_BLOCK *NBPtr
3132 )
3133{
3134 CONST UINT8 Txp[] = {0xFF, 0xFF, 3, 3, 4, 4, 5, 6, 7};
3135 CONST UINT8 Txpdll[] = {0xFF, 0xFF, 0xA, 0xA, 0xD, 0x10, 0x14, 0x17, 0x1A};
3136 UINT8 i;
3137 UINT8 TxpVal;
3138 UINT8 TxpdllVal;
3139 UINT16 Speed;
3140
3141 Speed = NBPtr->DCTPtr->Timings.Speed;
3142 i = (UINT8) ((Speed < DDR800_FREQUENCY) ? ((Speed / 66) - 3) : (Speed / 133));
3143 ASSERT (i < sizeof (Txp));
3144 ASSERT (i < sizeof (Txpdll));
3145
3146 TxpdllVal = Txpdll[i];
3147
3148 if ((NBPtr->MCTPtr->Status[SbLrdimms] || NBPtr->MCTPtr->Status[SbRegistered]) &&
3149 ((NBPtr->DCTPtr->Timings.Speed == DDR667_FREQUENCY) || (NBPtr->DCTPtr->Timings.Speed == DDR800_FREQUENCY)) &&
3150 (NBPtr->RefPtr->DDR3Voltage == VOLT1_25)) {
3151 TxpVal = 4;
3152 } else {
3153 TxpVal = Txp[i];
3154 }
3155
3156 if (TxpVal != 0xFF) {
3157 MemNSetBitFieldNb (NBPtr, BFTxp, TxpVal);
3158 }
3159 if (TxpdllVal != 0xFF) {
3160 NBPtr->FamilySpecificHook[AdjustTxpdll] (NBPtr, &TxpdllVal);
3161 MemNSetBitFieldNb (NBPtr, BFTxpdll, TxpdllVal);
3162 }
3163}
3164
3165/*-----------------------------------------------------------------------------
3166 *
3167 *
3168 * This function adjust value of Txpdll to encoded value.
3169 *
3170 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
3171 * @param[in,out] OptParam - Optional parameter
3172 *
3173 * @return TRUE
3174 * ----------------------------------------------------------------------------
3175 */
3176BOOLEAN
3177MemNAdjustTxpdllClientNb (
3178 IN OUT MEM_NB_BLOCK *NBPtr,
3179 IN OUT VOID *OptParam
3180 )
3181{
3182 *(UINT8 *) OptParam -= 10;
3183 return TRUE;
3184}
3185
3186/* -----------------------------------------------------------------------------*/
3187/**
3188 *
3189 * This function is a wrapper to handle or switch NB Pstate for UNB
3190 *
3191 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
3192 * @param[in] *NBPstate - NB Pstate
3193 *
3194 * @return TRUE - Succeed
3195 * @return FALSE - Fail
3196 */
3197
3198BOOLEAN
3199MemNChangeNbFrequencyWrapUnb (
3200 IN OUT MEM_NB_BLOCK *NBPtr,
3201 IN UINT32 NBPstate
3202 )
3203{
3204 UINT8 TargetNbPs;
3205 UINT32 FreqNumeratorInMHz;
3206 UINT32 FreqDivisor;
3207 UINT32 VoltageInuV;
3208 UINT8 NbPstateMaxVal;
3209 CPU_SPECIFIC_SERVICES *FamilySpecificServices;
3210
3211 if (NBPtr->NbFreqChgState == 0) {
3212 // While in state 0, keep NB Pstate at the highest supported
3213 TargetNbPs = 0;
3214 if (NBPtr->NbPsCtlReg == 0) {
3215 // Save NbPsCtl register on the first run
3216 NBPtr->NbPsCtlReg = MemNGetBitFieldNb (NBPtr, BFNbPstateCtlReg);
3217 } else {
3218 // Do not need to switch NB Pstate again if it is already at highest
3219 return TRUE;
3220 }
3221 } else if (NBPtr->NbFreqChgState < 4) {
3222 // While in other states, go to the next lower NB Pstate
3223 TargetNbPs = (UINT8) MemNGetBitFieldNb (NBPtr, BFCurNbPstate) + 1;
3224 if (TargetNbPs == 1) {
3225 // Set up intermediate NBPstate
3226 NbPstateMaxVal = (UINT8) MemNGetBitFieldNb (NBPtr, BFNbPstateMaxVal);
3227 MemNSetBitFieldNb (NBPtr, BFNbPsSel, NbPstateMaxVal);
3228 GetCpuServicesOfCurrentCore ((CONST CPU_SPECIFIC_SERVICES **)&FamilySpecificServices, &NBPtr->MemPtr->StdHeader);
3229 if (FamilySpecificServices->GetNbPstateInfo (FamilySpecificServices,
3230 NBPtr->MemPtr->PlatFormConfig,
3231 &NBPtr->PciAddr,
3232 (UINT32) NbPstateMaxVal,
3233 &FreqNumeratorInMHz,
3234 &FreqDivisor,
3235 &VoltageInuV,
3236 &(NBPtr->MemPtr->StdHeader))) {
3237 // Get NCLK speed for intermediate NBPstate
3238 NBPtr->NBClkFreq = FreqNumeratorInMHz / FreqDivisor;
3239 NBPtr->ProgramNbPsDependentRegs (NBPtr);
3240 } else {
3241 ASSERT (FALSE);
3242 }
3243 }
3244 } else {
3245 // When done with training, release NB Pstate force by restoring NbPsCtl register
3246 NBPtr->FamilySpecificHook[ReleaseNbPstate] (NBPtr, NBPtr);
3247 IDS_HDT_CONSOLE (MEM_FLOW, "\tRelease NB Pstate force\n");
3248 return TRUE;
3249 }
3250
3251 // Make sure target NB Pstate is enabled, else find next enabled NB Pstate
3252 GetCpuServicesOfCurrentCore ((CONST CPU_SPECIFIC_SERVICES **)&FamilySpecificServices, &NBPtr->MemPtr->StdHeader);
3253 for (; TargetNbPs < 4; TargetNbPs++) {
3254 if (FamilySpecificServices->GetNbPstateInfo (FamilySpecificServices,
3255 NBPtr->MemPtr->PlatFormConfig,
3256 &NBPtr->PciAddr,
3257 (UINT32) TargetNbPs,
3258 &FreqNumeratorInMHz,
3259 &FreqDivisor,
3260 &VoltageInuV,
3261 &(NBPtr->MemPtr->StdHeader))) {
3262 // Record NCLK speed
3263 NBPtr->NBClkFreq = FreqNumeratorInMHz / FreqDivisor;
3264 break;
3265 }
3266 }
3267
3268 if (TargetNbPs < 4) {
3269 IDS_HDT_CONSOLE (MEM_FLOW, "\tNB P%d: %dMHz\n", TargetNbPs, NBPtr->NBClkFreq);
3270
3271 // 1.Program the configuration registers which contain multiple internal copies for each NB P-state. See
3272 // D18F1x10C[NbPsSel].
3273 MemNSetBitFieldNb (NBPtr, BFNbPsSel, TargetNbPs);
3274
3275 // Check to see if NB P-states have been disabled. @todo This should only be needed for
3276 // bring up, but must be included in any releases that occur before NB P-state operation
3277 // has been debugged/fixed.
3278 if ((NBPtr->NbPsCtlReg & 0x00000003) != 0) {
3279 NbPstateMaxVal = (UINT8) MemNGetBitFieldNb (NBPtr, BFNbPstateMaxVal);
3280 // Set up RdPtrInit before transit to target NBPstate
3281 if ((TargetNbPs > 0) && (TargetNbPs != NbPstateMaxVal)) {
3282 NBPtr->ProgramNbPsDependentRegs (NBPtr);
3283 }
3284
3285 // If current NBPstate is already in NBPstateLo, do not do transition to NBPstateLo.
3286 if ((TargetNbPs != 0) || (MemNGetBitFieldNb (NBPtr, BFNbPstateLo) != MemNGetBitFieldNb (NBPtr, BFCurNbPstate))) {
3287 // 2.Program D18F5x170 to transition the NB P-state:
3288 // NbPstateLo = NbPstateMaxVal. (HW requires an intermediate transition to low)
3289 // SwNbPstateLoDis = NbPstateDisOnP0 = NbPstateThreshold = 0.
3290 MemNSetBitFieldNb (NBPtr, BFNbPstateLo, NbPstateMaxVal);
3291 MemNSetBitFieldNb (NBPtr, BFNbPstateCtlReg, MemNGetBitFieldNb (NBPtr, BFNbPstateCtlReg) & 0xFFFF91FF);
3292
3293 // 3.Wait for D18F5x174[CurNbPstate] to equal NbPstateLo.
3294 MemNPollBitFieldNb (NBPtr, BFCurNbPstate, NbPstateMaxVal, PCI_ACCESS_TIMEOUT, FALSE);
3295 }
3296 // 4.Program D18F5x170 to force the NB P-state:
3297 // NbPstateHi = target NB P-state.
3298 // SwNbPstateLoDis = 1 (triggers the transition)
3299 MemNSetBitFieldNb (NBPtr, BFNbPstateHi, TargetNbPs);
3300 MemNSetBitFieldNb (NBPtr, BFSwNbPstateLoDis, 1);
3301
3302 // 5.Wait for D18F5x174[CurNbPstate] to equal the target NB P-state.
3303 MemNPollBitFieldNb (NBPtr, BFCurNbPstate, TargetNbPs, PCI_ACCESS_TIMEOUT, FALSE);
3304 }
3305
3306 // When NB frequency change succeeds, TSC rate may have changed.
3307 // We need to update TSC rate
3308 FamilySpecificServices->GetTscRate (FamilySpecificServices, &NBPtr->MemPtr->TscRate, &NBPtr->MemPtr->StdHeader);
3309 // Switch MemPstate context if the current MemPstate does not sync with MemPstate context
3310 if (MemNGetBitFieldNb (NBPtr, BFCurMemPstate) != MemNGetBitFieldNb (NBPtr, BFMemPsSel)) {
3311 MemNChangeMemPStateContextNb (NBPtr, MemNGetBitFieldNb (NBPtr, BFCurMemPstate));
3312 }
3313 } else {
3314 // Cannot find a supported NB Pstate to switch to
3315 // Release NB Pstate force by restoring NbPsCtl register
3316 NBPtr->FamilySpecificHook[ReleaseNbPstate] (NBPtr, NBPtr);
3317 IDS_HDT_CONSOLE (MEM_FLOW, "\tRelease NB Pstate force\n");
3318 return FALSE;
3319 }
3320 return TRUE;
3321}
3322
3323/* -----------------------------------------------------------------------------*/
3324/**
3325 *
3326 *
3327 * This function sends an MRS command for Unb
3328 *
3329 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
3330 *
3331 */
3332
3333VOID
3334MemNSendMrsCmdUnb (
3335 IN OUT MEM_NB_BLOCK *NBPtr
3336 )
3337{
3338 UINT8 MrsBank;
3339 UINT16 MrsBuffer;
3340 UINT8 MrsChipSel;
3341
3342 MemNSetASRSRTNb (NBPtr);
3343 MemNSwapBitsUnb (NBPtr);
3344
3345 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tCS%d MR%d %05x\n",
3346 (MemNGetBitFieldNb (NBPtr, BFMrsChipSel) & 0x7),
3347 (MemNGetBitFieldNb (NBPtr, BFMrsBank) & 0x7),
3348 (MemNGetBitFieldNb (NBPtr, BFMrsAddress) & 0x3FFFF));
3349
3350 // 1.Set SendMrsCmd=1
3351 MemNSetBitFieldNb (NBPtr, BFSendMrsCmd, 1);
3352
3353 // 2.Wait for SendMrsCmd=0
3354 MemNPollBitFieldNb (NBPtr, BFSendMrsCmd, 0, PCI_ACCESS_TIMEOUT, FALSE);
3355
3356 // Send MRS buffer if memory pstate is supported and enabled
3357 if (NBPtr->MemPstateStage != 0) {
3358 MrsChipSel = (UINT8) (MemNGetBitFieldNb (NBPtr, BFMrsChipSel) & 0x7);
3359 // Only user even rank MRS to set MRS buffer
3360 if ((MrsChipSel & 1) == 0) {
3361 MrsBank = (UINT8) (MemNGetBitFieldNb (NBPtr, BFMrsBank) & 0x7);
3362 MrsBuffer = (UINT16) (MemNGetBitFieldNb (NBPtr, BFMrsAddress) & 0xFFFF);
3363 if (MrsBank == 0) {
3364 MrsBuffer &= 0xFEFF;
3365 MemNSetBitFieldNb (NBPtr, BFMxMr0, MrsBuffer);
3366 } else if (MrsBank == 1) {
3367 MemNSetBitFieldNb (NBPtr, BFMxMr1, MrsBuffer);
3368 } else if (MrsBank == 2) {
3369 MemNSetBitFieldNb (NBPtr, BFMxMr2, MrsBuffer);
3370 }
3371 }
3372 }
3373}
3374
3375/* -----------------------------------------------------------------------------*/
3376/**
3377 *
3378 *
3379 * This function returns MR0[CL] value with table driven support
3380 *
3381 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
3382 *
3383 * @return MR0[CL] value
3384 */
3385UINT32
3386MemNGetMR0CLTblDrvNb (
3387 IN OUT MEM_NB_BLOCK *NBPtr
3388 )
3389{
3390 return (UINT32) ((NBPtr->PsPtr->MR0CL31 << 4) | (NBPtr->PsPtr->MR0CL0 << 2));
3391}
3392
3393/* -----------------------------------------------------------------------------*/
3394/**
3395 *
3396 *
3397 * This function performs MaxRdLat training for slot 1
3398 *
3399 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
3400 * @param[in,out] TestAddrRJ16 - Test address
3401 *
3402 * @return TRUE
3403 * ----------------------------------------------------------------------------
3404 */
3405BOOLEAN
3406MemNSlot1MaxRdLatTrainClientNb (
3407 IN OUT MEM_NB_BLOCK *NBPtr,
3408 IN OUT VOID *TestAddrRJ16
3409 )
3410{
3411 UINT8 DummyBuffer[8];
3412 UINT16 MaxLatDly;
3413 UINT8 i;
3414
3415 // Perform slot1 specific training:
3416 // A.Program D18F2x[1,0]78[SlotSel]=1. Force read CAS to fifo slot1 for training.
3417 // B.Program D18F2x[1,0]78[MaxRdLatency] = TrainedMaxRdLatency. Set to last slot0 value that passed.
3418 // C.Read the DIMM test addresses.
3419 // D.Compare the values read against the pattern written.
3420
3421 IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tTrain Slot 1: \n");
3422 MemNSetBitFieldNb (NBPtr, BFSlotSel, 1);
3423
3424 MaxLatDly = (UINT16) (MemNGetBitFieldNb (NBPtr, BFMaxLatency) + 1); // Add 1 to get back to the last passing value
3425 MemNSetBitFieldNb (NBPtr, BFMaxLatency, MaxLatDly);
3426
3427 for (i = 0; i < 100; i++) {
3428 IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tDly %3x", MaxLatDly);
3429
3430 NBPtr->ReadPattern (NBPtr, DummyBuffer, *(UINT32*)TestAddrRJ16, 6);
3431
3432 if (NBPtr->CompareTestPattern (NBPtr, DummyBuffer, DummyBuffer, 6 * 64) == 0xFFFF) {
3433 IDS_HDT_CONSOLE (MEM_FLOW, " P");
3434 break;
3435 }
3436 IDS_HDT_CONSOLE (MEM_FLOW, "\n");
3437 }
3438
3439 if (i < 100) {
3440 MemNSetBitFieldNb (NBPtr, BFSlot1ExtraClkEn, 0);
3441 } else {
3442 MemNSetBitFieldNb (NBPtr, BFSlot1ExtraClkEn, 1);
3443 }
3444
3445 MemNSetBitFieldNb (NBPtr, BFMaxSkipErrTrain, 0);
3446
3447 return TRUE;
3448}
3449
3450
3451/* -----------------------------------------------------------------------------*/
3452/**
3453 *
3454 *
3455 * This function programs dram power management timing related registers
3456 *
3457 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
3458 *
3459 * @return none
3460 * ----------------------------------------------------------------------------
3461 */
3462VOID
3463MemNDramPowerMngTimingNb (
3464 IN OUT MEM_NB_BLOCK *NBPtr
3465 )
3466{
3467 STATIC CONST UINT8 Tckesr[] = {4, 4, 4, 5, 5, 6, 7, 2, 2};
3468 UINT8 Tck;
3469
3470 // These timings are based on DDR3 spec
3471 // Tcksrx = max(5 nCK, 10 ns)
3472 Tck = (UINT8) MAX (5, (MemUnsToMemClk (NBPtr->DCTPtr->Timings.Speed, 10)));
3473 MemNSetBitFieldNb (NBPtr, BFTcksrx, MIN (0xE, MAX (Tck, 2)));
3474
3475 // Tcksre = max(5 nCK, 10 ns)
3476 MemNSetBitFieldNb (NBPtr, BFTcksre, MIN (0x27, MAX (Tck, 5)));
3477
3478 // Tckesr = tCKE(min) + 1 nCK
3479 // tCKE(min)
3480 // DDR-667 7.5ns = 3nCk max(3nCK, 7.5ns) + 1 = 3nCK + 1nCK = 4nCK
3481 // DDR-800 7.5ns = 3nCk max(3nCK, 7.5ns) + 1 = 3nCK + 1nCK = 4nCK
3482 // DDR-1066 5.625ns = 3nCK max(3nCK, 5.625ns) + 1 = 3nCL + 1nCK = 4nCK
3483 // DDR-1333 5.625ns = 4nCK max(3nCK, 4nCK) + 1 = 4nCK + 1nCK = 5nCK
3484 // DDR-1600 5ns = 4nCK max(3nCK, 4nCK) + 1 = 4nCK + 1nCK = 5nCK
3485 // DDR-1866 5ns = 5nCK max(3nCK, 5nCK) + 1 = 5nCK + 1nCK = 6nCK
3486 // DDR-2133 5ns = 6nCK max(3nCK, 6nCK) + 1 = 6nCK + 1nCK = 7nCK
3487 ASSERT (((NBPtr->DCTPtr->Timings.Speed / 133) >= 2) && ((NBPtr->DCTPtr->Timings.Speed / 133) <= 10));
3488 MemNSetBitFieldNb (NBPtr, BFTckesr, Tckesr[(NBPtr->DCTPtr->Timings.Speed / 133) - 2]);
3489
3490 // Tpd = tCKE(min)
3491 MemNSetBitFieldNb (NBPtr, BFTpd, Tckesr[(NBPtr->DCTPtr->Timings.Speed / 133) - 2] - 1);
3492}
3493
3494/* -----------------------------------------------------------------------------*/
3495/**
3496 *
3497 * The function resets Rcv Fifo
3498 *
3499 * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK
3500 * @param[in] Dummy - Dummy parameter
3501 *
3502 */
3503
3504VOID
3505MemTResetRcvFifoUnb (
3506 IN OUT struct _MEM_TECH_BLOCK *TechPtr,
3507 IN UINT8 Dummy
3508 )
3509{
3510 // Program D18F2x9C_x0000_0050_dct[1:0]=00000000h
3511 MemNSetBitFieldNb (TechPtr->NBPtr, BFRstRcvFifo, 0);
3512}
3513
3514/* -----------------------------------------------------------------------------*/
3515/**
3516 *
3517 *
3518 * This function gets the memory width
3519 *
3520 * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
3521 *
3522 * @return Memory width
3523 */
3524
3525UINT32
3526MemNGetMemoryWidthUnb (
3527 IN OUT MEM_NB_BLOCK *NBPtr
3528 )
3529{
3530 DIE_STRUCT *MCTPtr;
3531 MEM_SHARED_DATA *SharedPtr;
3532
3533 MCTPtr = NBPtr->MCTPtr;
3534 SharedPtr = NBPtr->SharedPtr;
3535
3536 return 64 + ((SharedPtr->AllECC && MCTPtr->Status[SbEccDimms]) ? 8 : 0);
3537}