blob: 69bc8ddb6f3b487837a2428f172f46d1a2d18d6e [file] [log] [blame]
Frank Vibrans2b4c8312011-02-14 18:30:54 +00001/* $NoKeywords:$ */
2/**
3 * @file
4 *
5 * mfidendimm.c
6 *
7 * Translate physical system address to dimm identification.
8 *
9 * @xrefitem bom "File Content Label" "Release Content"
10 * @e project: AGESA
11 * @e sub-project: (Mem/Feat)
efdesign9884cbce22011-08-04 12:09:17 -060012 * @e \$Revision: 46495 $ @e \$Date: 2011-02-03 14:10:56 -0700 (Thu, 03 Feb 2011) $
Frank Vibrans2b4c8312011-02-14 18:30:54 +000013 *
14 **/
15/*
16 *****************************************************************************
17 *
18 * Copyright (c) 2011, Advanced Micro Devices, Inc.
19 * All rights reserved.
Edward O'Callaghan1542a6f2014-07-06 19:24:06 +100020 *
Frank Vibrans2b4c8312011-02-14 18:30:54 +000021 * Redistribution and use in source and binary forms, with or without
22 * modification, are permitted provided that the following conditions are met:
23 * * Redistributions of source code must retain the above copyright
24 * notice, this list of conditions and the following disclaimer.
25 * * Redistributions in binary form must reproduce the above copyright
26 * notice, this list of conditions and the following disclaimer in the
27 * documentation and/or other materials provided with the distribution.
Edward O'Callaghan1542a6f2014-07-06 19:24:06 +100028 * * Neither the name of Advanced Micro Devices, Inc. nor the names of
29 * its contributors may be used to endorse or promote products derived
Frank Vibrans2b4c8312011-02-14 18:30:54 +000030 * from this software without specific prior written permission.
Edward O'Callaghan1542a6f2014-07-06 19:24:06 +100031 *
Frank Vibrans2b4c8312011-02-14 18:30:54 +000032 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
34 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
35 * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY
36 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
37 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
38 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
39 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
40 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
41 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Edward O'Callaghan1542a6f2014-07-06 19:24:06 +100042 *
Frank Vibrans2b4c8312011-02-14 18:30:54 +000043 * ***************************************************************************
44 *
45 */
46
47/*
48 *----------------------------------------------------------------------------
49 * MODULES USED
50 *
51 *----------------------------------------------------------------------------
52 */
53
54
55
56#include "AGESA.h"
57#include "amdlib.h"
58#include "mm.h"
59#include "mn.h"
60#include "Ids.h"
61#include "OptionMemory.h"
62#include "heapManager.h"
63#include "mfidendimm.h"
64#include "GeneralServices.h"
65#include "Filecode.h"
66CODE_GROUP (G2_PEI)
67RDATA_GROUP (G2_PEI)
68
69#define FILECODE PROC_MEM_FEAT_IDENDIMM_MFIDENDIMM_FILECODE
70extern MEM_NB_SUPPORT memNBInstalled[];
71
72/*----------------------------------------------------------------------------
73 * DEFINITIONS AND MACROS
74 *
75 *----------------------------------------------------------------------------
76 */
77#define MAX_DCTS_PER_DIE 2 ///< Max DCTs per die
78#define MAX_CHLS_PER_DCT 1 ///< Max Channels per DCT
79
80/*----------------------------------------------------------------------------
81 * TYPEDEFS AND STRUCTURES
82 *
83 *----------------------------------------------------------------------------
84 */
85
86/*----------------------------------------------------------------------------
87 * PROTOTYPES OF LOCAL FUNCTIONS
88 *
89 *----------------------------------------------------------------------------
90 */
91AGESA_STATUS
92STATIC
93MemFTransSysAddrToCS (
94 IN OUT AMD_IDENTIFY_DIMM *AmdDimmIdentify,
95 IN MEM_MAIN_DATA_BLOCK *mmPtr
96 );
97
98UINT32
99STATIC
100MemFGetPCI (
101 IN MEM_NB_BLOCK *NBPtr,
102 IN UINT8 NodeID,
103 IN UINT8 DctNum,
104 IN BIT_FIELD_NAME BitFieldName
105 );
106
107UINT8
108STATIC
109MemFUnaryXOR (
110 IN UINT32 address
111 );
112
113/*----------------------------------------------------------------------------
114 * EXPORTED FUNCTIONS
115 *
116 *----------------------------------------------------------------------------
117 */
118/*-----------------------------------------------------------------------------*/
119/**
120*
121* This function identifies the dimm on which the given memory address locates.
122*
123* @param[in, out] *AmdDimmIdentify - Pointer to the parameter structure AMD_IDENTIFY_DIMM
124*
125* @retval AGESA_SUCCESS - Successfully translate physical system address
126* to dimm identification.
127* AGESA_BOUNDS_CHK - Targeted address is out of bound.
128*
129*/
130
131AGESA_STATUS
132AmdIdentifyDimm (
133 IN OUT AMD_IDENTIFY_DIMM *AmdDimmIdentify
134 )
135{
136 UINT8 i;
137 AGESA_STATUS RetVal;
138 MEM_MAIN_DATA_BLOCK mmData; // Main Data block
139 MEM_NB_BLOCK *NBPtr;
140 MEM_DATA_STRUCT MemData;
141 LOCATE_HEAP_PTR LocHeap;
142 ALLOCATE_HEAP_PARAMS AllocHeapParams;
143 UINT8 Node;
144 UINT8 Dct;
145 UINT8 Die;
146 UINT8 DieCount;
147
148 LibAmdMemCopy (&(MemData.StdHeader), &(AmdDimmIdentify->StdHeader), sizeof (AMD_CONFIG_PARAMS), &(AmdDimmIdentify->StdHeader));
149 mmData.MemPtr = &MemData;
150 RetVal = MemSocketScan (&mmData);
151 if (RetVal == AGESA_FATAL) {
152 return RetVal;
153 }
154 DieCount = mmData.DieCount;
155
156 // Search for AMD_MEM_AUTO_HANDLE on the heap first.
157 // Only apply for space on the heap if cannot find AMD_MEM_AUTO_HANDLE on the heap.
158 LocHeap.BufferHandle = AMD_MEM_AUTO_HANDLE;
159 if (HeapLocateBuffer (&LocHeap, &AmdDimmIdentify->StdHeader) == AGESA_SUCCESS) {
160 // NB block has already been constructed by main block.
161 // No need to construct it here.
162 NBPtr = (MEM_NB_BLOCK *)LocHeap.BufferPtr;
efdesign9884cbce22011-08-04 12:09:17 -0600163 mmData.NBPtr = NBPtr;
Frank Vibrans2b4c8312011-02-14 18:30:54 +0000164 } else {
165 AllocHeapParams.RequestedBufferSize = (DieCount * (sizeof (MEM_NB_BLOCK)));
166 AllocHeapParams.BufferHandle = AMD_MEM_AUTO_HANDLE;
167 AllocHeapParams.Persist = HEAP_SYSTEM_MEM;
168 if (HeapAllocateBuffer (&AllocHeapParams, &AmdDimmIdentify->StdHeader) != AGESA_SUCCESS) {
169 PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_ALLOCATE_FOR_IDENTIFY_DIMM_MEM_NB_BLOCK, 0, 0, 0, 0, &AmdDimmIdentify->StdHeader);
170 ASSERT(FALSE); // Could not allocate heap space for NB block for Identify DIMM
171 return AGESA_FATAL;
172 }
173 NBPtr = (MEM_NB_BLOCK *)AllocHeapParams.BufferPtr;
174 mmData.NBPtr = NBPtr;
175 // Construct each die.
176 for (Die = 0; Die < DieCount; Die ++) {
177 i = 0;
178 while (memNBInstalled[i].MemIdentifyDimmConstruct != 0) {
179 if (memNBInstalled[i].MemIdentifyDimmConstruct (&NBPtr[Die], &MemData, Die)) {
180 break;
181 }
182 i++;
183 };
184 if (memNBInstalled[i].MemIdentifyDimmConstruct == 0) {
185 PutEventLog (AGESA_FATAL, MEM_ERROR_NO_CONSTRUCTOR_FOR_IDENTIFY_DIMM, Die, 0, 0, 0, &AmdDimmIdentify->StdHeader);
186 ASSERT(FALSE); // No Identify DIMM constructor found
187 return AGESA_FATAL;
188 }
189 }
190 }
191
192 if ((RetVal = MemFTransSysAddrToCS (AmdDimmIdentify, &mmData)) == AGESA_SUCCESS) {
193 // Translate Node, DCT and Chip select number to Socket, Channel and Dimm number.
194 Node = AmdDimmIdentify->SocketId;
195 Dct = AmdDimmIdentify->MemChannelId;
196 AmdDimmIdentify->SocketId = MemData.DiesPerSystem[Node].SocketId;
197 AmdDimmIdentify->MemChannelId = NBPtr[Node].GetSocketRelativeChannel (&NBPtr[Node], Dct, 0);
198 AmdDimmIdentify->DimmId /= 2;
199 }
200
201 return RetVal;
202}
203
204
205/*----------------------------------------------------------------------------
206 * LOCAL FUNCTIONS
207 *
208 *----------------------------------------------------------------------------
209 */
210
211/*-----------------------------------------------------------------------------*/
212/**
213*
214* This function translates the given physical system address to
215* a node, channel select, chip select, bank, row, and column address.
216*
217* @param[in, out] *AmdDimmIdentify - Pointer to the parameter structure AMD_IDENTIFY_DIMM
218* @param[in, out] *mmPtr - Pointer to the MEM_MAIN_DATA_BLOCK
219*
220* @retval AGESA_SUCCESS - The chip select address is found
221* @retval AGESA_BOUNDS_CHK - Targeted address is out of bound.
222*
223*/
224AGESA_STATUS
225STATIC
226MemFTransSysAddrToCS (
227 IN OUT AMD_IDENTIFY_DIMM *AmdDimmIdentify,
228 IN MEM_MAIN_DATA_BLOCK *mmPtr
229 )
230{
231 BOOLEAN CSFound;
232 BOOLEAN DctSelHiRngEn;
233 BOOLEAN DctSelIntLvEn;
234 BOOLEAN DctGangEn;
235 BOOLEAN HiRangeSelected;
236 BOOLEAN DramHoleValid;
237 BOOLEAN CSEn;
238 BOOLEAN SwapDone;
239 BOOLEAN IntLvRgnSwapEn;
240 UINT8 DctSelHi;
241 UINT8 DramEn;
242 UINT8 range;
243 UINT8 IntlvEn;
244 UINT8 IntlvSel;
245 UINT8 ILog;
246 UINT8 DctSelIntLvAddr;
247 UINT8 DctNum;
248 UINT8 cs;
249 UINT8 BadDramCs;
250 UINT8 spare;
251 UINT8 IntLvRgnBaseAddr;
252 UINT8 IntLvRgnLmtAddr;
253 UINT8 IntLvRgnSize;
254 UINT32 temp;
255 UINT32 DramHoleOffset;
256 UINT64 DramBase;
257 UINT64 DramLimit;
258 UINT64 DctSelBaseAddr;
259 UINT64 DctSelBaseOffset;
260 UINT64 ChannelAddr;
261 UINT64 CSBase;
262 UINT64 CSMask;
263 UINT64 InputAddr;
264 UINT64 ChannelOffset;
265 MEM_NB_BLOCK *NBPtr;
266
267 UINT64 SysAddr;
268 UINT8 *NodeID;
269 UINT8 *ChannelSelect;
270 UINT8 *ChipSelect;
271
272 SysAddr = AmdDimmIdentify->MemoryAddress;
273 NodeID = &(AmdDimmIdentify->SocketId);
274 ChannelSelect = &(AmdDimmIdentify->MemChannelId);
275 ChipSelect = &(AmdDimmIdentify->DimmId);
276 CSFound = FALSE;
277 ILog = 0;
278 NBPtr = mmPtr->NBPtr;
279
280 // Loop to determine the dram range
281 for (range = 0; range < mmPtr->DieCount; range ++) {
282 // DRAM Base
283 temp = MemFGetPCI (NBPtr, 0, 0, BFDramBaseReg0 + range);
284 DramEn = (UINT8) (temp & 0x3);
285 IntlvEn = (UINT8) ((temp >> 8) & 0x7);
286
287 DramBase = ((UINT64) (MemFGetPCI (NBPtr, 0, 0, BFDramBaseHiReg0 + range) & 0xFF) << 40) |
288 (((UINT64) temp & 0xFFFF0000) << 8);
289
290 // DRAM Limit
291 temp = MemFGetPCI (NBPtr, 0, 0, BFDramLimitReg0 + range);
292 *NodeID = (UINT8) (temp & 0x7);
293 IntlvSel = (UINT8) ((temp >> 8) & 0x7);
294 DramLimit = ((UINT64) (MemFGetPCI (NBPtr, 0, 0, BFDramLimitHiReg0 + range) & 0xFF) << 40) |
295 (((UINT64) temp << 8) | 0xFFFFFF);
296
297
298 if ((DramEn != 0) && (DramBase <= SysAddr) && (SysAddr <= DramLimit) &&
299 ((IntlvEn == 0) || (IntlvSel == ((SysAddr >> 12) & IntlvEn)))) {
300 // Determine the number of bit positions consumed by Node Interleaving
301 switch (IntlvEn) {
302
303 case 0x0:
304 ILog = 0;
305 break;
306
307 case 0x1:
308 ILog = 1;
309 break;
310
311 case 0x3:
312 ILog = 2;
313 break;
314
315 case 0x7:
316 ILog = 3;
317 break;
318
319 default:
320 IDS_ERROR_TRAP;
321 }
322
323 // F2x10C Swapped Interleaved Region
324 IntLvRgnSwapEn = (BOOLEAN) MemFGetPCI (NBPtr, *NodeID, 0, BFIntLvRgnSwapEn);
325 if (IntLvRgnSwapEn) {
326 IntLvRgnBaseAddr = (UINT8) MemFGetPCI (NBPtr, *NodeID, 0, BFIntLvRgnBaseAddr);
327 IntLvRgnLmtAddr = (UINT8) MemFGetPCI (NBPtr, *NodeID, 0, BFIntLvRgnLmtAddr);
328 IntLvRgnSize = (UINT8) MemFGetPCI (NBPtr, *NodeID, 0, BFIntLvRgnSize);
329 ASSERT (IntLvRgnSize == (IntLvRgnLmtAddr - IntLvRgnBaseAddr + 1));
330 if (((SysAddr >> 34) == 0) &&
331 ((((SysAddr >> 27) >= IntLvRgnBaseAddr) && ((SysAddr >> 27) <= IntLvRgnLmtAddr))
332 || ((SysAddr >> 27) < IntLvRgnSize))) {
333 SysAddr ^= (UINT64) IntLvRgnBaseAddr << 27;
334 }
335 }
336
337 // Extract variables from F2x110 DRAM Controller Select Low Register
338 DctSelHiRngEn = (BOOLEAN) MemFGetPCI (NBPtr, *NodeID, 0, BFDctSelHiRngEn);
339 DctSelHi = (UINT8) MemFGetPCI (NBPtr, *NodeID, 0, BFDctSelHi);
340 DctSelIntLvEn = (BOOLEAN) MemFGetPCI (NBPtr, *NodeID, 0, BFDctSelIntLvEn);
341 DctGangEn = (BOOLEAN) MemFGetPCI (NBPtr, *NodeID, 0, BFDctGangEn);
342 DctSelIntLvAddr = (UINT8) MemFGetPCI (NBPtr, *NodeID, 0, BFDctSelIntLvAddr);
343 DctSelBaseAddr = (UINT64) MemFGetPCI (NBPtr, *NodeID, 0, BFDctSelBaseAddr) << 27;
344 DctSelBaseOffset = (UINT64) MemFGetPCI (NBPtr, *NodeID, 0, BFDctSelBaseOffset) << 26;
345
346
347 // Determine if high DCT address range is being selected
348 if (DctSelHiRngEn && !DctGangEn && (SysAddr >= DctSelBaseAddr)) {
349 HiRangeSelected = TRUE;
350 } else {
351 HiRangeSelected = FALSE;
352 }
353
354 // Determine Channel
355 if (DctGangEn) {
356 *ChannelSelect = (UINT8) ((SysAddr >> 3) & 0x1);
357 } else if (HiRangeSelected) {
358 *ChannelSelect = DctSelHi;
359 } else if (DctSelIntLvEn && (DctSelIntLvAddr == 0)) {
360 *ChannelSelect = (UINT8) ((SysAddr >> 6) & 0x1);
361 } else if (DctSelIntLvEn && (((DctSelIntLvAddr >> 1) & 0x1) != 0)) {
362 temp = MemFUnaryXOR ((UINT32) ((SysAddr >> 16) & 0x1F));
363 if ((DctSelIntLvAddr & 0x1) != 0) {
364 *ChannelSelect = (UINT8) (((SysAddr >> 9) & 0x1) ^ temp);
365 } else {
366 *ChannelSelect = (UINT8) (((SysAddr >> 6) & 0x1) ^ temp);
367 }
368 } else if (DctSelIntLvEn) {
369 *ChannelSelect = (UINT8) ((SysAddr >> (12 + ILog)) & 0x1);
370 } else if (DctSelHiRngEn) {
371 *ChannelSelect = ~DctSelHi & 0x1;
372 } else {
373 *ChannelSelect = 0;
374 }
375 ASSERT (*ChannelSelect < NBPtr[*NodeID].DctCount);
376
377 DramHoleOffset = MemFGetPCI (NBPtr, *NodeID, 0, BFDramHoleOffset) << 23;
378 DramHoleValid = (BOOLEAN) MemFGetPCI (NBPtr, *NodeID, 0, BFDramHoleValid);
379
380 // Determine base address offset
381 if (HiRangeSelected) {
382 if (((DctSelBaseAddr >> 32) == 0) && DramHoleValid && (SysAddr >= (UINT64) 0x100000000)) {
383 ChannelOffset = (UINT64) DramHoleOffset;
384 } else {
385 ChannelOffset = DctSelBaseOffset;
386 }
387 } else {
388 if (DramHoleValid && (SysAddr >= (UINT64) 0x100000000)) {
389 ChannelOffset = (UINT64) DramHoleOffset;
390 } else {
391 ChannelOffset = DramBase;
392 }
393 }
394
395 // Remove hoisting offset and normalize to DRAM bus addresses
396 ChannelAddr = SysAddr - ChannelOffset;
397
398 // Remove node interleaving
399 if (IntlvEn != 0) {
400 ChannelAddr = ((ChannelAddr >> (12 + ILog)) << 12) | (ChannelAddr & 0xFFF);
401 }
402
403 // Remove channel interleave
404 if (DctSelIntLvEn && !HiRangeSelected && !DctGangEn) {
405 if ((DctSelIntLvAddr & 1) != 1) {
406 // A[6] Select or Hash 6
407 ChannelAddr = ((ChannelAddr >> 7) << 6) | (ChannelAddr & 0x3F);
408 } else if (DctSelIntLvAddr == 1) {
409 // A[12]
410 ChannelAddr = ((ChannelAddr >> 13) << 12) | (ChannelAddr & 0xFFF);
411 } else {
412 // Hash 9
413 ChannelAddr = ((ChannelAddr >> 10) << 9) | (ChannelAddr & 0x1FF);
414 }
415 }
416
417 // Determine the Chip Select
418 for (cs = 0; cs < MAX_CS_PER_CHANNEL; ++ cs) {
419 DctNum = DctGangEn ? 0 : *ChannelSelect;
420
421 // Obtain the CS Base
422 temp = MemFGetPCI (NBPtr, *NodeID, DctNum, BFCSBaseAddr0Reg + cs);
423 CSEn = (BOOLEAN) (temp & 0x1);
424 CSBase = ((UINT64) temp & CS_REG_MASK) << 8;
425
426 // Obtain the CS Mask
427 CSMask = ((UINT64) MemFGetPCI (NBPtr, *NodeID, DctNum, BFCSMask0Reg + (cs >> 1)) & CS_REG_MASK) << 8;
428
429 // Adjust the Channel Addr for easy comparison
430 InputAddr = ((ChannelAddr >> 8) & CS_REG_MASK) << 8;
431
432 if (CSEn && ((InputAddr & ~CSMask) == (CSBase & ~CSMask))) {
433 CSFound = TRUE;
434
435 *ChipSelect = cs;
436
437 temp = MemFGetPCI (NBPtr, *NodeID, 0, BFOnLineSpareControl);
438 SwapDone = (BOOLEAN) ((temp >> (1 + 2 * (*ChannelSelect))) & 0x1);
439 BadDramCs = (UINT8) ((temp >> (4 + 4 * (*ChannelSelect))) & 0x7);
440 if (SwapDone && (cs == BadDramCs)) {
441 // Find the spare rank for the channel
442 for (spare = 0; spare < MAX_CS_PER_CHANNEL; ++spare) {
443 if ((MemFGetPCI (NBPtr, *NodeID, DctNum, BFCSBaseAddr0Reg + spare) & 0x2) != 0) {
444 *ChipSelect = spare;
445 break;
446 }
447 }
448 }
449 ASSERT (*ChipSelect < MAX_CS_PER_CHANNEL);
450
451 break;
452 }
453 }
454 }
455 if (CSFound) {
456 break;
457 }
458 }
459
460 // last ditch sanity check
461 ASSERT (!CSFound || ((*NodeID < mmPtr->DieCount) && (*ChannelSelect < NBPtr[*NodeID].DctCount) && (*ChipSelect < MAX_CS_PER_CHANNEL)));
462 if (CSFound) {
463 return AGESA_SUCCESS;
464 } else {
465 return AGESA_BOUNDS_CHK;
466 }
467
468}
469
470
471/*-----------------------------------------------------------------------------*/
472/**
473*
474* This function is the interface to call the PCI register access function
475* defined in NB block.
476*
477* @param[in] *NBPtr - Pointer to the parameter structure MEM_NB_BLOCK
478* @param[in] NodeID - Node ID number of the target Northbridge
479* @param[in] DctNum - DCT number if applicable, otherwise, put 0
480* @param[in] BitFieldName - targeted bitfield
481*
482* @retval UINT32 - 32 bits PCI register value
483*
484*/
485UINT32
486STATIC
487MemFGetPCI (
488 IN MEM_NB_BLOCK *NBPtr,
489 IN UINT8 NodeID,
490 IN UINT8 DctNum,
491 IN BIT_FIELD_NAME BitFieldName
492 )
493{
494 MEM_NB_BLOCK *LocalNBPtr;
495 // Get the northbridge pointer for the targeted node.
496 LocalNBPtr = &NBPtr[NodeID];
497 LocalNBPtr->Dct = DctNum;
498 // The caller of this function will take care of the ganged/unganged situation.
499 // So Ganged is set to be false here, and do PCI read on the DCT specified by DctNum.
500 return LocalNBPtr->GetBitField (LocalNBPtr, BitFieldName);
501}
502
503/*-----------------------------------------------------------------------------*/
504/**
505*
506* This function returns an even parity bit (making the total # of 1's even)
507* {0, 1} = number of set bits in argument is {even, odd}.
508*
509* @param[in] address - the address on which the parity bit will be calculated
510*
511* @retval UINT8 - parity bit
512*
513*/
514
515UINT8
516STATIC
517MemFUnaryXOR (
518 IN UINT32 address
519 )
520{
521 UINT8 parity;
522 UINT8 index;
523 parity = 0;
524 for (index = 0; index < 32; ++ index) {
525 parity = (UINT8) (parity ^ (address & 0x1));
526 address = address >> 1;
527 }
528 return parity;
529}