AGESA F15: AMD family15 AGESA code
[coreboot.git] / src / vendorcode / amd / agesa / f15 / Proc / Mem / Feat / IDENDIMM / mfidendimm.c
1 /* $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)
12  * @e \$Revision: 51077 $ @e \$Date: 2011-04-18 15:53:51 -0600 (Mon, 18 Apr 2011) $
13  *
14  **/
15 /*****************************************************************************
16 *
17 * Copyright (C) 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.
41 *
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 "mm.h"
58 #include "mn.h"
59 #include "Ids.h"
60 #include "OptionMemory.h"
61 #include "heapManager.h"
62 #include "mfidendimm.h"
63 #include "GeneralServices.h"
64 #include "Filecode.h"
65 CODE_GROUP (G2_PEI)
66 RDATA_GROUP (G2_PEI)
67
68 #define FILECODE PROC_MEM_FEAT_IDENDIMM_MFIDENDIMM_FILECODE
69 extern MEM_NB_SUPPORT memNBInstalled[];
70
71 /*----------------------------------------------------------------------------
72  *                          DEFINITIONS AND MACROS
73  *
74  *----------------------------------------------------------------------------
75  */
76 #define MAX_DCTS_PER_DIE        2   ///< Max DCTs per die
77 #define MAX_CHLS_PER_DCT        1   ///< Max Channels per DCT
78
79 /*----------------------------------------------------------------------------
80  *                           TYPEDEFS AND STRUCTURES
81  *
82  *----------------------------------------------------------------------------
83  */
84
85 /*----------------------------------------------------------------------------
86  *                        PROTOTYPES OF LOCAL FUNCTIONS
87  *
88  *----------------------------------------------------------------------------
89  */
90 AGESA_STATUS
91 STATIC
92 MemFTransSysAddrToCS (
93   IN OUT   AMD_IDENTIFY_DIMM *AmdDimmIdentify,
94   IN       MEM_MAIN_DATA_BLOCK *mmPtr
95   );
96
97 UINT32
98 STATIC
99 MemFGetPCI (
100   IN   MEM_NB_BLOCK *NBPtr,
101   IN   UINT8 NodeID,
102   IN   UINT8 DctNum,
103   IN   BIT_FIELD_NAME BitFieldName
104   );
105
106 UINT8
107 STATIC
108 MemFUnaryXOR (
109   IN   UINT32 address
110   );
111
112 /*----------------------------------------------------------------------------
113  *                            EXPORTED FUNCTIONS
114  *
115  *----------------------------------------------------------------------------
116  */
117 /*-----------------------------------------------------------------------------*/
118 /**
119 *
120 *   This function identifies the dimm on which the given memory address locates.
121 *
122 *   @param[in, out]   *AmdDimmIdentify - Pointer to the parameter structure AMD_IDENTIFY_DIMM
123 *
124 *   @retval           AGESA_SUCCESS - Successfully translate physical system address
125 *                                     to dimm identification.
126 *                     AGESA_BOUNDS_CHK - Targeted address is out of bound.
127 *
128 */
129
130 AGESA_STATUS
131 AmdIdentifyDimm (
132   IN OUT   AMD_IDENTIFY_DIMM *AmdDimmIdentify
133   )
134 {
135   UINT8 i;
136   AGESA_STATUS RetVal;
137   MEM_MAIN_DATA_BLOCK mmData;             // Main Data block
138   MEM_NB_BLOCK *NBPtr;
139   MEM_DATA_STRUCT MemData;
140   LOCATE_HEAP_PTR LocHeap;
141   ALLOCATE_HEAP_PARAMS AllocHeapParams;
142   UINT8 Node;
143   UINT8 Dct;
144   UINT8 Die;
145   UINT8 DieCount;
146
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) {
151     return RetVal;
152   }
153   DieCount = mmData.DieCount;
154
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;
163   } else {
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
170       return AGESA_FATAL;
171     }
172     NBPtr = (MEM_NB_BLOCK *)AllocHeapParams.BufferPtr;
173     mmData.NBPtr = NBPtr;
174     // Construct each die.
175     for (Die = 0; Die < DieCount; Die ++) {
176       i = 0;
177       while (memNBInstalled[i].MemIdentifyDimmConstruct != 0) {
178         if (memNBInstalled[i].MemIdentifyDimmConstruct (&NBPtr[Die], &MemData, Die)) {
179           break;
180         }
181         i++;
182       };
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
186         return AGESA_FATAL;
187       }
188     }
189   }
190
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;
198   }
199
200   return RetVal;
201 }
202
203
204 /*----------------------------------------------------------------------------
205  *                              LOCAL FUNCTIONS
206  *
207  *----------------------------------------------------------------------------
208  */
209
210 /*-----------------------------------------------------------------------------*/
211 /**
212 *
213 *   This function translates the given physical system address to
214 *   a node, channel select, chip select, bank, row, and column address.
215 *
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
218 *
219 *   @retval           AGESA_SUCCESS - The chip select address is found
220 *   @retval           AGESA_BOUNDS_CHK - Targeted address is out of bound.
221 *
222 */
223 AGESA_STATUS
224 STATIC
225 MemFTransSysAddrToCS (
226   IN OUT   AMD_IDENTIFY_DIMM *AmdDimmIdentify,
227   IN       MEM_MAIN_DATA_BLOCK *mmPtr
228   )
229 {
230   BOOLEAN CSFound;
231   BOOLEAN DctSelHiRngEn;
232   BOOLEAN DctSelIntLvEn;
233   BOOLEAN DctGangEn;
234   BOOLEAN HiRangeSelected;
235   BOOLEAN DramHoleValid;
236   BOOLEAN CSEn;
237   BOOLEAN SwapDone;
238   BOOLEAN IntLvRgnSwapEn;
239   UINT8 DctSelHi;
240   UINT8 DramEn;
241   UINT8 range;
242   UINT8 IntlvEn;
243   UINT8 IntlvSel;
244   UINT8 ILog;
245   UINT8 DctSelIntLvAddr;
246   UINT8 DctNum;
247   UINT8 cs;
248   UINT8 BadDramCs;
249   UINT8 spare;
250   UINT8 IntLvRgnBaseAddr;
251   UINT8 IntLvRgnLmtAddr;
252   UINT8 IntLvRgnSize;
253   UINT32 temp;
254   UINT32 DramHoleOffset;
255   UINT32 DramHoleBase;
256   UINT64 DramBase;
257   UINT64 DramLimit;
258   UINT64 DramLimitSysAddr;
259   UINT64 DctSelBaseAddr;
260   UINT64 DctSelBaseOffset;
261   UINT64 ChannelAddr;
262   UINT64 CSBase;
263   UINT64 CSMask;
264   UINT64 InputAddr;
265   UINT64 ChannelOffset;
266   MEM_NB_BLOCK *NBPtr;
267   UINT8 Die;
268
269   UINT64 SysAddr;
270   UINT8 *NodeID;
271   UINT8 *ChannelSelect;
272   UINT8 *ChipSelect;
273
274   SysAddr = AmdDimmIdentify->MemoryAddress;
275   NodeID = &(AmdDimmIdentify->SocketId);
276   ChannelSelect = &(AmdDimmIdentify->MemChannelId);
277   ChipSelect = &(AmdDimmIdentify->DimmId);
278   CSFound = FALSE;
279   ILog = 0;
280   NBPtr = mmPtr->NBPtr;
281
282   NBPtr->FamilySpecificHook[FixupSysAddr] (NBPtr, &SysAddr);
283
284   // Loop to determine the dram range
285   for (Die = 0; Die < mmPtr->DieCount; Die ++) {
286     range = NBPtr[Die].Node;
287
288     // DRAM Base
289     temp = MemFGetPCI (NBPtr, 0, 0, BFDramBaseReg0 + range);
290     DramEn = (UINT8) (temp & 0x3);
291     IntlvEn = (UINT8) ((temp >> 8) & 0x7);
292
293     DramBase = ((UINT64) (MemFGetPCI (NBPtr, 0, 0, BFDramBaseHiReg0 + range) & 0xFF) << 40) |
294                  (((UINT64) temp & 0xFFFF0000) << 8);
295
296     // DRAM Limit
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);
304
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
308       switch (IntlvEn) {
309
310       case 0x0:
311         ILog = 0;
312         break;
313
314       case 0x1:
315         ILog = 1;
316         break;
317
318       case 0x3:
319         ILog = 2;
320         break;
321
322       case 0x7:
323         ILog = 3;
324         break;
325
326       default:
327         IDS_ERROR_TRAP;
328       }
329
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;
337       }
338
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;
350         }
351       }
352
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;
361
362
363       // Determine if high DCT address range is being selected
364       if (DctSelHiRngEn && !DctGangEn && (SysAddr >= DctSelBaseAddr)) {
365         HiRangeSelected = TRUE;
366       } else {
367         HiRangeSelected = FALSE;
368       }
369
370       // Determine Channel
371       if (DctGangEn) {
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);
381         } else {
382           *ChannelSelect = (UINT8) (((SysAddr >> 6) & 0x1) ^ temp);
383         }
384       } else if (DctSelIntLvEn) {
385         *ChannelSelect = (UINT8) ((SysAddr >> (12 + ILog)) & 0x1);
386       } else if (DctSelHiRngEn) {
387         *ChannelSelect = ~DctSelHi & 0x1;
388       } else {
389         *ChannelSelect = 0;
390       }
391       ASSERT (*ChannelSelect < NBPtr[*NodeID].DctCount);
392
393       // Determine base address offset
394       if (HiRangeSelected) {
395         if ((DctSelBaseAddr < DramHoleBase) && DramHoleValid && (SysAddr >= (UINT64) 0x100000000)) {
396           ChannelOffset = (UINT64) DramHoleOffset;
397         } else {
398           ChannelOffset = DctSelBaseOffset;
399         }
400       } else {
401         if (DramHoleValid && (SysAddr >= (UINT64) 0x100000000)) {
402           ChannelOffset = (UINT64) DramHoleOffset;
403         } else {
404           ChannelOffset = DramBase;
405         }
406       }
407
408       // Remove hoisting offset and normalize to DRAM bus addresses
409       ChannelAddr = SysAddr - ChannelOffset;
410
411       // Remove node interleaving
412       if (IntlvEn != 0) {
413         ChannelAddr = ((ChannelAddr >> (12 + ILog)) << 12) | (ChannelAddr & 0xFFF);
414       }
415
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) {
422           // A[12]
423           ChannelAddr = ((ChannelAddr >> 13) << 12) | (ChannelAddr & 0xFFF);
424         } else {
425           // Hash 9
426           ChannelAddr = ((ChannelAddr >> 10) << 9) | (ChannelAddr & 0x1FF);
427         }
428       }
429
430       // Determine the Chip Select
431       for (cs = 0; cs < MAX_CS_PER_CHANNEL; ++ cs) {
432         DctNum = DctGangEn ? 0 : *ChannelSelect;
433
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;
438
439         // Obtain the CS Mask
440         CSMask = ((UINT64) MemFGetPCI (NBPtr, *NodeID, DctNum, BFCSMask0Reg + (cs >> 1)) & NBPtr->CsRegMsk) << 8;
441
442         // Adjust the Channel Addr for easy comparison
443         InputAddr = ((ChannelAddr >> 8) & NBPtr->CsRegMsk) << 8;
444
445         if (CSEn && ((InputAddr & ~CSMask) == (CSBase & ~CSMask))) {
446           CSFound = TRUE;
447
448           *ChipSelect = cs;
449
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) {
457                 *ChipSelect = spare;
458                 break;
459               }
460             }
461           }
462           ASSERT (*ChipSelect < MAX_CS_PER_CHANNEL);
463
464           break;
465         }
466       }
467     }
468     if (CSFound) {
469       break;
470     }
471   }
472
473   // last ditch sanity check
474   ASSERT (!CSFound || ((*NodeID < mmPtr->DieCount) && (*ChannelSelect < NBPtr[*NodeID].DctCount) && (*ChipSelect < MAX_CS_PER_CHANNEL)));
475   if (CSFound) {
476     return AGESA_SUCCESS;
477   } else {
478     return AGESA_BOUNDS_CHK;
479   }
480
481 }
482
483
484 /*-----------------------------------------------------------------------------*/
485 /**
486 *
487 *   This function is the interface to call the PCI register access function
488 *   defined in NB block.
489 *
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
494 *
495 *   @retval      UINT32 - 32 bits PCI register value
496 *
497 */
498 UINT32
499 STATIC
500 MemFGetPCI (
501   IN   MEM_NB_BLOCK *NBPtr,
502   IN   UINT8 NodeID,
503   IN   UINT8 DctNum,
504   IN   BIT_FIELD_NAME BitFieldName
505   )
506 {
507   MEM_NB_BLOCK *LocalNBPtr;
508   UINT8 Die;
509
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);
513
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);
521 }
522
523 /*-----------------------------------------------------------------------------*/
524 /**
525 *
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}.
528 *
529 *   @param[in]   address - the address on which the parity bit will be calculated
530 *
531 *   @retval      UINT8 - parity bit
532 *
533 */
534
535 UINT8
536 STATIC
537 MemFUnaryXOR (
538   IN   UINT32 address
539   )
540 {
541   UINT8 parity;
542   UINT8 index;
543   parity = 0;
544   for (index = 0; index < 32; ++ index) {
545     parity = (UINT8) (parity ^ (address & 0x1));
546     address = address >> 1;
547   }
548   return parity;
549 }