7 * Translate physical system address to dimm identification.
9 * @xrefitem bom "File Content Label" "Release Content"
11 * @e sub-project: (Mem/Feat)
12 * @e \$Revision: 51077 $ @e \$Date: 2011-04-18 15:53:51 -0600 (Mon, 18 Apr 2011) $
15 /*****************************************************************************
17 * Copyright (C) 2012 Advanced Micro Devices, Inc.
18 * All rights reserved.
20 * Redistribution and use in source and binary forms, with or without
21 * modification, are permitted provided that the following conditions are met:
22 * * Redistributions of source code must retain the above copyright
23 * notice, this list of conditions and the following disclaimer.
24 * * Redistributions in binary form must reproduce the above copyright
25 * notice, this list of conditions and the following disclaimer in the
26 * documentation and/or other materials provided with the distribution.
27 * * Neither the name of Advanced Micro Devices, Inc. nor the names of
28 * its contributors may be used to endorse or promote products derived
29 * from this software without specific prior written permission.
31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
32 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
33 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
34 * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY
35 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
36 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
38 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
40 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 * ***************************************************************************
47 *----------------------------------------------------------------------------
50 *----------------------------------------------------------------------------
60 #include "OptionMemory.h"
61 #include "heapManager.h"
62 #include "mfidendimm.h"
63 #include "GeneralServices.h"
68 #define FILECODE PROC_MEM_FEAT_IDENDIMM_MFIDENDIMM_FILECODE
69 extern MEM_NB_SUPPORT memNBInstalled[];
71 /*----------------------------------------------------------------------------
72 * DEFINITIONS AND MACROS
74 *----------------------------------------------------------------------------
76 #define MAX_DCTS_PER_DIE 2 ///< Max DCTs per die
77 #define MAX_CHLS_PER_DCT 1 ///< Max Channels per DCT
79 /*----------------------------------------------------------------------------
80 * TYPEDEFS AND STRUCTURES
82 *----------------------------------------------------------------------------
85 /*----------------------------------------------------------------------------
86 * PROTOTYPES OF LOCAL FUNCTIONS
88 *----------------------------------------------------------------------------
92 MemFTransSysAddrToCS (
93 IN OUT AMD_IDENTIFY_DIMM *AmdDimmIdentify,
94 IN MEM_MAIN_DATA_BLOCK *mmPtr
100 IN MEM_NB_BLOCK *NBPtr,
103 IN BIT_FIELD_NAME BitFieldName
112 /*----------------------------------------------------------------------------
115 *----------------------------------------------------------------------------
117 /*-----------------------------------------------------------------------------*/
120 * This function identifies the dimm on which the given memory address locates.
122 * @param[in, out] *AmdDimmIdentify - Pointer to the parameter structure AMD_IDENTIFY_DIMM
124 * @retval AGESA_SUCCESS - Successfully translate physical system address
125 * to dimm identification.
126 * AGESA_BOUNDS_CHK - Targeted address is out of bound.
132 IN OUT AMD_IDENTIFY_DIMM *AmdDimmIdentify
137 MEM_MAIN_DATA_BLOCK mmData; // Main Data block
139 MEM_DATA_STRUCT MemData;
140 LOCATE_HEAP_PTR LocHeap;
141 ALLOCATE_HEAP_PARAMS AllocHeapParams;
147 LibAmdMemCopy (&(MemData.StdHeader), &(AmdDimmIdentify->StdHeader), sizeof (AMD_CONFIG_PARAMS), &(AmdDimmIdentify->StdHeader));
148 mmData.MemPtr = &MemData;
149 RetVal = MemSocketScan (&mmData);
150 if (RetVal == AGESA_FATAL) {
153 DieCount = mmData.DieCount;
155 // Search for AMD_MEM_AUTO_HANDLE on the heap first.
156 // Only apply for space on the heap if cannot find AMD_MEM_AUTO_HANDLE on the heap.
157 LocHeap.BufferHandle = AMD_MEM_AUTO_HANDLE;
158 if (HeapLocateBuffer (&LocHeap, &AmdDimmIdentify->StdHeader) == AGESA_SUCCESS) {
159 // NB block has already been constructed by main block.
160 // No need to construct it here.
161 NBPtr = (MEM_NB_BLOCK *)LocHeap.BufferPtr;
162 mmData.NBPtr = NBPtr;
164 AllocHeapParams.RequestedBufferSize = (DieCount * (sizeof (MEM_NB_BLOCK)));
165 AllocHeapParams.BufferHandle = AMD_MEM_AUTO_HANDLE;
166 AllocHeapParams.Persist = HEAP_SYSTEM_MEM;
167 if (HeapAllocateBuffer (&AllocHeapParams, &AmdDimmIdentify->StdHeader) != AGESA_SUCCESS) {
168 PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_ALLOCATE_FOR_IDENTIFY_DIMM_MEM_NB_BLOCK, 0, 0, 0, 0, &AmdDimmIdentify->StdHeader);
169 ASSERT(FALSE); // Could not allocate heap space for NB block for Identify DIMM
172 NBPtr = (MEM_NB_BLOCK *)AllocHeapParams.BufferPtr;
173 mmData.NBPtr = NBPtr;
174 // Construct each die.
175 for (Die = 0; Die < DieCount; Die ++) {
177 while (memNBInstalled[i].MemIdentifyDimmConstruct != 0) {
178 if (memNBInstalled[i].MemIdentifyDimmConstruct (&NBPtr[Die], &MemData, Die)) {
183 if (memNBInstalled[i].MemIdentifyDimmConstruct == 0) {
184 PutEventLog (AGESA_FATAL, MEM_ERROR_NO_CONSTRUCTOR_FOR_IDENTIFY_DIMM, Die, 0, 0, 0, &AmdDimmIdentify->StdHeader);
185 ASSERT(FALSE); // No Identify DIMM constructor found
191 if ((RetVal = MemFTransSysAddrToCS (AmdDimmIdentify, &mmData)) == AGESA_SUCCESS) {
192 // Translate Node, DCT and Chip select number to Socket, Channel and Dimm number.
193 Node = AmdDimmIdentify->SocketId;
194 Dct = AmdDimmIdentify->MemChannelId;
195 AmdDimmIdentify->SocketId = MemData.DiesPerSystem[Node].SocketId;
196 AmdDimmIdentify->MemChannelId = NBPtr[Node].GetSocketRelativeChannel (&NBPtr[Node], Dct, 0);
197 AmdDimmIdentify->DimmId /= 2;
204 /*----------------------------------------------------------------------------
207 *----------------------------------------------------------------------------
210 /*-----------------------------------------------------------------------------*/
213 * This function translates the given physical system address to
214 * a node, channel select, chip select, bank, row, and column address.
216 * @param[in, out] *AmdDimmIdentify - Pointer to the parameter structure AMD_IDENTIFY_DIMM
217 * @param[in, out] *mmPtr - Pointer to the MEM_MAIN_DATA_BLOCK
219 * @retval AGESA_SUCCESS - The chip select address is found
220 * @retval AGESA_BOUNDS_CHK - Targeted address is out of bound.
225 MemFTransSysAddrToCS (
226 IN OUT AMD_IDENTIFY_DIMM *AmdDimmIdentify,
227 IN MEM_MAIN_DATA_BLOCK *mmPtr
231 BOOLEAN DctSelHiRngEn;
232 BOOLEAN DctSelIntLvEn;
234 BOOLEAN HiRangeSelected;
235 BOOLEAN DramHoleValid;
238 BOOLEAN IntLvRgnSwapEn;
245 UINT8 DctSelIntLvAddr;
250 UINT8 IntLvRgnBaseAddr;
251 UINT8 IntLvRgnLmtAddr;
254 UINT32 DramHoleOffset;
258 UINT64 DramLimitSysAddr;
259 UINT64 DctSelBaseAddr;
260 UINT64 DctSelBaseOffset;
265 UINT64 ChannelOffset;
271 UINT8 *ChannelSelect;
274 SysAddr = AmdDimmIdentify->MemoryAddress;
275 NodeID = &(AmdDimmIdentify->SocketId);
276 ChannelSelect = &(AmdDimmIdentify->MemChannelId);
277 ChipSelect = &(AmdDimmIdentify->DimmId);
280 NBPtr = mmPtr->NBPtr;
282 NBPtr->FamilySpecificHook[FixupSysAddr] (NBPtr, &SysAddr);
284 // Loop to determine the dram range
285 for (Die = 0; Die < mmPtr->DieCount; Die ++) {
286 range = NBPtr[Die].Node;
289 temp = MemFGetPCI (NBPtr, 0, 0, BFDramBaseReg0 + range);
290 DramEn = (UINT8) (temp & 0x3);
291 IntlvEn = (UINT8) ((temp >> 8) & 0x7);
293 DramBase = ((UINT64) (MemFGetPCI (NBPtr, 0, 0, BFDramBaseHiReg0 + range) & 0xFF) << 40) |
294 (((UINT64) temp & 0xFFFF0000) << 8);
297 temp = MemFGetPCI (NBPtr, 0, 0, BFDramLimitReg0 + range);
298 *NodeID = (UINT8) (temp & 0x7);
299 IntlvSel = (UINT8) ((temp >> 8) & 0x7);
300 DramLimit = ((UINT64) (MemFGetPCI (NBPtr, 0, 0, BFDramLimitHiReg0 + range) & 0xFF) << 40) |
301 (((UINT64) temp << 8) | 0xFFFFFF);
302 DramLimitSysAddr = (((UINT64) MemFGetPCI (NBPtr, *NodeID, 0, BFDramLimitAddr)) << 27) | 0x7FFFFFF;
303 ASSERT (DramLimit <= DramLimitSysAddr);
305 if ((DramEn != 0) && (DramBase <= SysAddr) && (SysAddr <= DramLimitSysAddr) &&
306 ((IntlvEn == 0) || (IntlvSel == ((SysAddr >> 12) & IntlvEn)))) {
307 // Determine the number of bit positions consumed by Node Interleaving
330 DramHoleOffset = MemFGetPCI (NBPtr, *NodeID, 0, BFDramHoleOffset) << 23;
331 DramHoleValid = (BOOLEAN) MemFGetPCI (NBPtr, *NodeID, 0, BFDramHoleValid);
332 DramHoleBase = MemFGetPCI (NBPtr, *NodeID, 0, BFDramHoleBase) << 24;
333 // Address belongs to this node based on DramBase/Limit,
334 // but is in the memory hole so it doesn't map to DRAM
335 if (DramHoleValid && (DramHoleBase <= SysAddr) && (SysAddr < 0x100000000)) {
336 return AGESA_BOUNDS_CHK;
339 // F2x10C Swapped Interleaved Region
340 IntLvRgnSwapEn = (BOOLEAN) MemFGetPCI (NBPtr, *NodeID, 0, BFIntLvRgnSwapEn);
341 if (IntLvRgnSwapEn) {
342 IntLvRgnBaseAddr = (UINT8) MemFGetPCI (NBPtr, *NodeID, 0, BFIntLvRgnBaseAddr);
343 IntLvRgnLmtAddr = (UINT8) MemFGetPCI (NBPtr, *NodeID, 0, BFIntLvRgnLmtAddr);
344 IntLvRgnSize = (UINT8) MemFGetPCI (NBPtr, *NodeID, 0, BFIntLvRgnSize);
345 ASSERT (IntLvRgnSize == (IntLvRgnLmtAddr - IntLvRgnBaseAddr + 1));
346 if (((SysAddr >> 34) == 0) &&
347 ((((SysAddr >> 27) >= IntLvRgnBaseAddr) && ((SysAddr >> 27) <= IntLvRgnLmtAddr))
348 || ((SysAddr >> 27) < IntLvRgnSize))) {
349 SysAddr ^= (UINT64) IntLvRgnBaseAddr << 27;
353 // Extract variables from F2x110 DRAM Controller Select Low Register
354 DctSelHiRngEn = (BOOLEAN) MemFGetPCI (NBPtr, *NodeID, 0, BFDctSelHiRngEn);
355 DctSelHi = (UINT8) MemFGetPCI (NBPtr, *NodeID, 0, BFDctSelHi);
356 DctSelIntLvEn = (BOOLEAN) MemFGetPCI (NBPtr, *NodeID, 0, BFDctSelIntLvEn);
357 DctGangEn = (BOOLEAN) MemFGetPCI (NBPtr, *NodeID, 0, BFDctGangEn);
358 DctSelIntLvAddr = (UINT8) MemFGetPCI (NBPtr, *NodeID, 0, BFDctSelIntLvAddr);
359 DctSelBaseAddr = (UINT64) MemFGetPCI (NBPtr, *NodeID, 0, BFDctSelBaseAddr) << 27;
360 DctSelBaseOffset = (UINT64) MemFGetPCI (NBPtr, *NodeID, 0, BFDctSelBaseOffset) << 26;
363 // Determine if high DCT address range is being selected
364 if (DctSelHiRngEn && !DctGangEn && (SysAddr >= DctSelBaseAddr)) {
365 HiRangeSelected = TRUE;
367 HiRangeSelected = FALSE;
372 *ChannelSelect = (UINT8) ((SysAddr >> 3) & 0x1);
373 } else if (HiRangeSelected) {
374 *ChannelSelect = DctSelHi;
375 } else if (DctSelIntLvEn && (DctSelIntLvAddr == 0)) {
376 *ChannelSelect = (UINT8) ((SysAddr >> 6) & 0x1);
377 } else if (DctSelIntLvEn && (((DctSelIntLvAddr >> 1) & 0x1) != 0)) {
378 temp = MemFUnaryXOR ((UINT32) ((SysAddr >> 16) & 0x1F));
379 if ((DctSelIntLvAddr & 0x1) != 0) {
380 *ChannelSelect = (UINT8) (((SysAddr >> 9) & 0x1) ^ temp);
382 *ChannelSelect = (UINT8) (((SysAddr >> 6) & 0x1) ^ temp);
384 } else if (DctSelIntLvEn) {
385 *ChannelSelect = (UINT8) ((SysAddr >> (12 + ILog)) & 0x1);
386 } else if (DctSelHiRngEn) {
387 *ChannelSelect = ~DctSelHi & 0x1;
391 ASSERT (*ChannelSelect < NBPtr[*NodeID].DctCount);
393 // Determine base address offset
394 if (HiRangeSelected) {
395 if ((DctSelBaseAddr < DramHoleBase) && DramHoleValid && (SysAddr >= (UINT64) 0x100000000)) {
396 ChannelOffset = (UINT64) DramHoleOffset;
398 ChannelOffset = DctSelBaseOffset;
401 if (DramHoleValid && (SysAddr >= (UINT64) 0x100000000)) {
402 ChannelOffset = (UINT64) DramHoleOffset;
404 ChannelOffset = DramBase;
408 // Remove hoisting offset and normalize to DRAM bus addresses
409 ChannelAddr = SysAddr - ChannelOffset;
411 // Remove node interleaving
413 ChannelAddr = ((ChannelAddr >> (12 + ILog)) << 12) | (ChannelAddr & 0xFFF);
416 // Remove channel interleave
417 if (DctSelIntLvEn && !HiRangeSelected && !DctGangEn) {
418 if ((DctSelIntLvAddr & 1) != 1) {
419 // A[6] Select or Hash 6
420 ChannelAddr = ((ChannelAddr >> 7) << 6) | (ChannelAddr & 0x3F);
421 } else if (DctSelIntLvAddr == 1) {
423 ChannelAddr = ((ChannelAddr >> 13) << 12) | (ChannelAddr & 0xFFF);
426 ChannelAddr = ((ChannelAddr >> 10) << 9) | (ChannelAddr & 0x1FF);
430 // Determine the Chip Select
431 for (cs = 0; cs < MAX_CS_PER_CHANNEL; ++ cs) {
432 DctNum = DctGangEn ? 0 : *ChannelSelect;
434 // Obtain the CS Base
435 temp = MemFGetPCI (NBPtr, *NodeID, DctNum, BFCSBaseAddr0Reg + cs);
436 CSEn = (BOOLEAN) (temp & 0x1);
437 CSBase = ((UINT64) temp & NBPtr->CsRegMsk) << 8;
439 // Obtain the CS Mask
440 CSMask = ((UINT64) MemFGetPCI (NBPtr, *NodeID, DctNum, BFCSMask0Reg + (cs >> 1)) & NBPtr->CsRegMsk) << 8;
442 // Adjust the Channel Addr for easy comparison
443 InputAddr = ((ChannelAddr >> 8) & NBPtr->CsRegMsk) << 8;
445 if (CSEn && ((InputAddr & ~CSMask) == (CSBase & ~CSMask))) {
450 temp = MemFGetPCI (NBPtr, *NodeID, 0, BFOnLineSpareControl);
451 SwapDone = (BOOLEAN) ((temp >> (1 + 2 * (*ChannelSelect))) & 0x1);
452 BadDramCs = (UINT8) ((temp >> (4 + 4 * (*ChannelSelect))) & 0x7);
453 if (SwapDone && (cs == BadDramCs)) {
454 // Find the spare rank for the channel
455 for (spare = 0; spare < MAX_CS_PER_CHANNEL; ++spare) {
456 if ((MemFGetPCI (NBPtr, *NodeID, DctNum, BFCSBaseAddr0Reg + spare) & 0x2) != 0) {
462 ASSERT (*ChipSelect < MAX_CS_PER_CHANNEL);
473 // last ditch sanity check
474 ASSERT (!CSFound || ((*NodeID < mmPtr->DieCount) && (*ChannelSelect < NBPtr[*NodeID].DctCount) && (*ChipSelect < MAX_CS_PER_CHANNEL)));
476 return AGESA_SUCCESS;
478 return AGESA_BOUNDS_CHK;
484 /*-----------------------------------------------------------------------------*/
487 * This function is the interface to call the PCI register access function
488 * defined in NB block.
490 * @param[in] *NBPtr - Pointer to the parameter structure MEM_NB_BLOCK
491 * @param[in] NodeID - Node ID number of the target Northbridge
492 * @param[in] DctNum - DCT number if applicable, otherwise, put 0
493 * @param[in] BitFieldName - targeted bitfield
495 * @retval UINT32 - 32 bits PCI register value
501 IN MEM_NB_BLOCK *NBPtr,
504 IN BIT_FIELD_NAME BitFieldName
507 MEM_NB_BLOCK *LocalNBPtr;
510 // Find NBBlock that associates with node NodeID
511 for (Die = 0; (Die < MAX_NODES_SUPPORTED) && (NBPtr[Die].Node != NodeID); Die ++);
512 ASSERT (Die < MAX_NODES_SUPPORTED);
514 // Get the northbridge pointer for the targeted node.
515 LocalNBPtr = &NBPtr[Die];
516 LocalNBPtr->FamilySpecificHook[DCTSelectSwitch] (LocalNBPtr, &DctNum);
517 LocalNBPtr->Dct = DctNum;
518 // The caller of this function will take care of the ganged/unganged situation.
519 // So Ganged is set to be false here, and do PCI read on the DCT specified by DctNum.
520 return LocalNBPtr->GetBitField (LocalNBPtr, BitFieldName);
523 /*-----------------------------------------------------------------------------*/
526 * This function returns an even parity bit (making the total # of 1's even)
527 * {0, 1} = number of set bits in argument is {even, odd}.
529 * @param[in] address - the address on which the parity bit will be calculated
531 * @retval UINT8 - parity bit
544 for (index = 0; index < 32; ++ index) {
545 parity = (UINT8) (parity ^ (address & 0x1));
546 address = address >> 1;