blob: 84f69ff94b4f8244e2ad38631cb2b79f138cc7f2 [file] [log] [blame]
zbao7d94cf92012-07-02 14:19:14 +08001/* $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: 63425 $ @e \$Date: 2011-12-22 11:24:10 -0600 (Thu, 22 Dec 2011) $
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 * MODULES USED
48 *
49 *----------------------------------------------------------------------------
50 */
51
52
53
54#include "AGESA.h"
55#include "amdlib.h"
56#include "mm.h"
57#include "mn.h"
58#include "Ids.h"
59#include "OptionMemory.h"
60#include "heapManager.h"
61#include "mfidendimm.h"
62#include "GeneralServices.h"
63#include "Filecode.h"
64CODE_GROUP (G2_PEI)
65RDATA_GROUP (G2_PEI)
66
67#define FILECODE PROC_MEM_FEAT_IDENDIMM_MFIDENDIMM_FILECODE
68extern MEM_NB_SUPPORT memNBInstalled[];
69
70/*----------------------------------------------------------------------------
71 * DEFINITIONS AND MACROS
72 *
73 *----------------------------------------------------------------------------
74 */
75#define MAX_DCTS_PER_DIE 2 ///< Max DCTs per die
76#define MAX_CHLS_PER_DCT 1 ///< Max Channels per DCT
77
78/*----------------------------------------------------------------------------
79 * TYPEDEFS AND STRUCTURES
80 *
81 *----------------------------------------------------------------------------
82 */
83
84/*----------------------------------------------------------------------------
85 * PROTOTYPES OF LOCAL FUNCTIONS
86 *
87 *----------------------------------------------------------------------------
88 */
89AGESA_STATUS
90STATIC
91MemFTransSysAddrToCS (
92 IN OUT AMD_IDENTIFY_DIMM *AmdDimmIdentify,
93 IN MEM_MAIN_DATA_BLOCK *mmPtr
94 );
95
96UINT32
97STATIC
98MemFGetPCI (
99 IN MEM_NB_BLOCK *NBPtr,
100 IN UINT8 NodeID,
101 IN UINT8 DctNum,
102 IN BIT_FIELD_NAME BitFieldName
103 );
104
105UINT8
106STATIC
107MemFUnaryXOR (
108 IN UINT32 address
109 );
110
111/*----------------------------------------------------------------------------
112 * EXPORTED FUNCTIONS
113 *
114 *----------------------------------------------------------------------------
115 */
116/*-----------------------------------------------------------------------------*/
117/**
118*
119* This function identifies the dimm on which the given memory address locates.
120*
121* @param[in, out] *AmdDimmIdentify - Pointer to the parameter structure AMD_IDENTIFY_DIMM
122*
123* @retval AGESA_SUCCESS - Successfully translate physical system address
124* to dimm identification.
125* AGESA_BOUNDS_CHK - Targeted address is out of bound.
126*
127*/
128
129AGESA_STATUS
130AmdIdentifyDimm (
131 IN OUT AMD_IDENTIFY_DIMM *AmdDimmIdentify
132 )
133{
134 UINT8 i;
135 AGESA_STATUS RetVal;
136 MEM_MAIN_DATA_BLOCK mmData; // Main Data block
137 MEM_NB_BLOCK *NBPtr;
138 MEM_DATA_STRUCT MemData;
139 LOCATE_HEAP_PTR LocHeap;
140 ALLOCATE_HEAP_PARAMS AllocHeapParams;
141 UINT8 Node;
142 UINT8 Dct;
143 UINT8 Die;
144 UINT8 DieCount;
145
146 LibAmdMemCopy (&(MemData.StdHeader), &(AmdDimmIdentify->StdHeader), sizeof (AMD_CONFIG_PARAMS), &(AmdDimmIdentify->StdHeader));
147 mmData.MemPtr = &MemData;
148 RetVal = MemSocketScan (&mmData);
149 if (RetVal == AGESA_FATAL) {
150 return RetVal;
151 }
152 DieCount = mmData.DieCount;
153
154 // Search for AMD_MEM_AUTO_HANDLE on the heap first.
155 // Only apply for space on the heap if cannot find AMD_MEM_AUTO_HANDLE on the heap.
156 LocHeap.BufferHandle = AMD_MEM_AUTO_HANDLE;
157 if (HeapLocateBuffer (&LocHeap, &AmdDimmIdentify->StdHeader) == AGESA_SUCCESS) {
158 // NB block has already been constructed by main block.
159 // No need to construct it here.
160 NBPtr = (MEM_NB_BLOCK *)LocHeap.BufferPtr;
161 mmData.NBPtr = NBPtr;
162 } else {
163 AllocHeapParams.RequestedBufferSize = (DieCount * (sizeof (MEM_NB_BLOCK)));
164 AllocHeapParams.BufferHandle = AMD_MEM_AUTO_HANDLE;
165 AllocHeapParams.Persist = HEAP_SYSTEM_MEM;
166 if (HeapAllocateBuffer (&AllocHeapParams, &AmdDimmIdentify->StdHeader) != AGESA_SUCCESS) {
167 PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_ALLOCATE_FOR_IDENTIFY_DIMM_MEM_NB_BLOCK, 0, 0, 0, 0, &AmdDimmIdentify->StdHeader);
168 ASSERT(FALSE); // Could not allocate heap space for NB block for Identify DIMM
169 return AGESA_FATAL;
170 }
171 NBPtr = (MEM_NB_BLOCK *)AllocHeapParams.BufferPtr;
172 mmData.NBPtr = NBPtr;
173 // Construct each die.
174 for (Die = 0; Die < DieCount; Die ++) {
175 i = 0;
176 while (memNBInstalled[i].MemIdentifyDimmConstruct != 0) {
177 if (memNBInstalled[i].MemIdentifyDimmConstruct (&NBPtr[Die], &MemData, Die)) {
178 break;
179 }
180 i++;
181 };
182 if (memNBInstalled[i].MemIdentifyDimmConstruct == 0) {
183 PutEventLog (AGESA_FATAL, MEM_ERROR_NO_CONSTRUCTOR_FOR_IDENTIFY_DIMM, Die, 0, 0, 0, &AmdDimmIdentify->StdHeader);
184 ASSERT(FALSE); // No Identify DIMM constructor found
185 return AGESA_FATAL;
186 }
187 }
188 }
189
190 if ((RetVal = MemFTransSysAddrToCS (AmdDimmIdentify, &mmData)) == AGESA_SUCCESS) {
191 // Translate Node, DCT and Chip select number to Socket, Channel and Dimm number.
192 Node = AmdDimmIdentify->SocketId;
193 Dct = AmdDimmIdentify->MemChannelId;
194 AmdDimmIdentify->SocketId = MemData.DiesPerSystem[Node].SocketId;
195 AmdDimmIdentify->MemChannelId = NBPtr[Node].GetSocketRelativeChannel (&NBPtr[Node], Dct, 0);
196 AmdDimmIdentify->DimmId /= 2;
197 }
198
199 return RetVal;
200}
201
202
203/*----------------------------------------------------------------------------
204 * LOCAL FUNCTIONS
205 *
206 *----------------------------------------------------------------------------
207 */
208
209/*-----------------------------------------------------------------------------*/
210/**
211*
212* This function translates the given physical system address to
213* a node, channel select, chip select, bank, row, and column address.
214*
215* @param[in, out] *AmdDimmIdentify - Pointer to the parameter structure AMD_IDENTIFY_DIMM
216* @param[in, out] *mmPtr - Pointer to the MEM_MAIN_DATA_BLOCK
217*
218* @retval AGESA_SUCCESS - The chip select address is found
219* @retval AGESA_BOUNDS_CHK - Targeted address is out of bound.
220*
221*/
222AGESA_STATUS
223STATIC
224MemFTransSysAddrToCS (
225 IN OUT AMD_IDENTIFY_DIMM *AmdDimmIdentify,
226 IN MEM_MAIN_DATA_BLOCK *mmPtr
227 )
228{
229 BOOLEAN CSFound;
230 BOOLEAN DctSelHiRngEn;
231 BOOLEAN DctSelIntLvEn;
232 BOOLEAN DctGangEn;
233 BOOLEAN HiRangeSelected;
234 BOOLEAN DramHoleValid;
235 BOOLEAN CSEn;
236 BOOLEAN SwapDone;
237 BOOLEAN IntLvRgnSwapEn;
238 UINT8 DctSelHi;
239 UINT8 DramEn;
240 UINT8 range;
241 UINT8 IntlvEn;
242 UINT8 IntlvSel;
243 UINT8 ILog;
244 UINT8 DctSelIntLvAddr;
245 UINT8 DctNum;
246 UINT8 cs;
247 UINT8 BadDramCs;
248 UINT8 spare;
249 UINT8 IntLvRgnBaseAddr;
250 UINT8 IntLvRgnLmtAddr;
251 UINT8 IntLvRgnSize;
252 UINT32 temp;
253 UINT32 DramHoleOffset;
254 UINT32 DramHoleBase;
255 UINT64 DramBase;
256 UINT64 DramLimit;
257 UINT64 DramLimitSysAddr;
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 UINT8 Die;
267
268 UINT64 SysAddr;
269 UINT8 *NodeID;
270 UINT8 *ChannelSelect;
271 UINT8 *ChipSelect;
272
273 SysAddr = AmdDimmIdentify->MemoryAddress;
274 NodeID = &(AmdDimmIdentify->SocketId);
275 ChannelSelect = &(AmdDimmIdentify->MemChannelId);
276 ChipSelect = &(AmdDimmIdentify->DimmId);
277 CSFound = FALSE;
278 ILog = 0;
279 NBPtr = mmPtr->NBPtr;
280
281 NBPtr->FamilySpecificHook[FixupSysAddr] (NBPtr, &SysAddr);
282
283 // Loop to determine the dram range
284 for (Die = 0; Die < mmPtr->DieCount; Die ++) {
285 range = NBPtr[Die].Node;
286
287 // DRAM Base
288 temp = MemFGetPCI (NBPtr, 0, 0, BFDramBaseReg0 + range);
289 DramEn = (UINT8) (temp & 0x3);
290 IntlvEn = (UINT8) ((temp >> 8) & 0x7);
291
292 DramBase = ((UINT64) (MemFGetPCI (NBPtr, 0, 0, BFDramBaseHiReg0 + range) & 0xFF) << 40) |
293 (((UINT64) temp & 0xFFFF0000) << 8);
294
295 // DRAM Limit
296 temp = MemFGetPCI (NBPtr, 0, 0, BFDramLimitReg0 + range);
297 *NodeID = (UINT8) (temp & 0x7);
298 IntlvSel = (UINT8) ((temp >> 8) & 0x7);
299 DramLimit = ((UINT64) (MemFGetPCI (NBPtr, 0, 0, BFDramLimitHiReg0 + range) & 0xFF) << 40) |
300 (((UINT64) temp << 8) | 0xFFFFFF);
301 DramLimitSysAddr = (((UINT64) MemFGetPCI (NBPtr, *NodeID, 0, BFDramLimitAddr)) << 27) | 0x7FFFFFF;
302 ASSERT (DramLimit <= DramLimitSysAddr);
303
304 if ((DramEn != 0) && (DramBase <= SysAddr) && (SysAddr <= DramLimitSysAddr) &&
305 ((IntlvEn == 0) || (IntlvSel == ((SysAddr >> 12) & IntlvEn)))) {
306 // Determine the number of bit positions consumed by Node Interleaving
307 switch (IntlvEn) {
308
309 case 0x0:
310 ILog = 0;
311 break;
312
313 case 0x1:
314 ILog = 1;
315 break;
316
317 case 0x3:
318 ILog = 2;
319 break;
320
321 case 0x7:
322 ILog = 3;
323 break;
324
325 default:
326 IDS_ERROR_TRAP;
327 }
328
329 DramHoleOffset = MemFGetPCI (NBPtr, *NodeID, 0, BFDramHoleOffset) << 23;
330 DramHoleValid = (BOOLEAN) MemFGetPCI (NBPtr, *NodeID, 0, BFDramHoleValid);
331 DramHoleBase = MemFGetPCI (NBPtr, *NodeID, 0, BFDramHoleBase) << 24;
332 // Address belongs to this node based on DramBase/Limit,
333 // but is in the memory hole so it doesn't map to DRAM
334 if (DramHoleValid && (DramHoleBase <= SysAddr) && (SysAddr < 0x100000000)) {
335 return AGESA_BOUNDS_CHK;
336 }
337
338 // F2x10C Swapped Interleaved Region
339 IntLvRgnSwapEn = (BOOLEAN) MemFGetPCI (NBPtr, *NodeID, 0, BFIntLvRgnSwapEn);
340 if (IntLvRgnSwapEn) {
341 IntLvRgnBaseAddr = (UINT8) MemFGetPCI (NBPtr, *NodeID, 0, BFIntLvRgnBaseAddr);
342 IntLvRgnLmtAddr = (UINT8) MemFGetPCI (NBPtr, *NodeID, 0, BFIntLvRgnLmtAddr);
343 IntLvRgnSize = (UINT8) MemFGetPCI (NBPtr, *NodeID, 0, BFIntLvRgnSize);
344 ASSERT (IntLvRgnSize == (IntLvRgnLmtAddr - IntLvRgnBaseAddr + 1));
345 if (((SysAddr >> 34) == 0) &&
346 ((((SysAddr >> 27) >= IntLvRgnBaseAddr) && ((SysAddr >> 27) <= IntLvRgnLmtAddr))
347 || ((SysAddr >> 27) < IntLvRgnSize))) {
348 SysAddr ^= (UINT64) IntLvRgnBaseAddr << 27;
349 }
350 }
351
352 // Extract variables from F2x110 DRAM Controller Select Low Register
353 DctSelHiRngEn = (BOOLEAN) MemFGetPCI (NBPtr, *NodeID, 0, BFDctSelHiRngEn);
354 DctSelHi = (UINT8) MemFGetPCI (NBPtr, *NodeID, 0, BFDctSelHi);
355 DctSelIntLvEn = (BOOLEAN) MemFGetPCI (NBPtr, *NodeID, 0, BFDctSelIntLvEn);
356 DctGangEn = (BOOLEAN) MemFGetPCI (NBPtr, *NodeID, 0, BFDctGangEn);
357 DctSelIntLvAddr = (UINT8) MemFGetPCI (NBPtr, *NodeID, 0, BFDctSelIntLvAddr);
358 DctSelBaseAddr = (UINT64) MemFGetPCI (NBPtr, *NodeID, 0, BFDctSelBaseAddr) << 27;
359 DctSelBaseOffset = (UINT64) MemFGetPCI (NBPtr, *NodeID, 0, BFDctSelBaseOffset) << 26;
360
361
362 // Determine if high DCT address range is being selected
363 if (DctSelHiRngEn && !DctGangEn && (SysAddr >= DctSelBaseAddr)) {
364 HiRangeSelected = TRUE;
365 } else {
366 HiRangeSelected = FALSE;
367 }
368
369 // Determine Channel
370 if (DctGangEn) {
371 *ChannelSelect = (UINT8) ((SysAddr >> 3) & 0x1);
372 } else if (HiRangeSelected) {
373 *ChannelSelect = DctSelHi;
374 } else if (DctSelIntLvEn && (DctSelIntLvAddr == 0)) {
375 *ChannelSelect = (UINT8) ((SysAddr >> 6) & 0x1);
376 } else if (DctSelIntLvEn && (((DctSelIntLvAddr >> 1) & 0x1) != 0)) {
377 temp = MemFUnaryXOR ((UINT32) ((SysAddr >> 16) & 0x1F));
378 if ((DctSelIntLvAddr & 0x1) != 0) {
379 *ChannelSelect = (UINT8) (((SysAddr >> 9) & 0x1) ^ temp);
380 } else {
381 *ChannelSelect = (UINT8) (((SysAddr >> 6) & 0x1) ^ temp);
382 }
383 } else if (DctSelIntLvEn) {
384 *ChannelSelect = (UINT8) ((SysAddr >> (12 + ILog)) & 0x1);
385 } else if (DctSelHiRngEn) {
386 *ChannelSelect = ~DctSelHi & 0x1;
387 } else {
388 *ChannelSelect = 0;
389 }
390 ASSERT (*ChannelSelect < NBPtr[*NodeID].DctCount);
391
392 // Determine base address offset
393 if (HiRangeSelected) {
394 if ((DctSelBaseAddr < DramHoleBase) && DramHoleValid && (SysAddr >= (UINT64) 0x100000000)) {
395 ChannelOffset = (UINT64) DramHoleOffset;
396 } else {
397 ChannelOffset = DctSelBaseOffset;
398 }
399 } else {
400 if (DramHoleValid && (SysAddr >= (UINT64) 0x100000000)) {
401 ChannelOffset = (UINT64) DramHoleOffset;
402 } else {
403 ChannelOffset = DramBase;
404 }
405 }
406
407 // Remove hoisting offset and normalize to DRAM bus addresses
408 ChannelAddr = SysAddr - ChannelOffset;
409
410 // Remove node interleaving
411 if (IntlvEn != 0) {
412 ChannelAddr = ((ChannelAddr >> (12 + ILog)) << 12) | (ChannelAddr & 0xFFF);
413 }
414
415 // Remove channel interleave
416 if (DctSelIntLvEn && !HiRangeSelected && !DctGangEn) {
417 if ((DctSelIntLvAddr & 1) != 1) {
418 // A[6] Select or Hash 6
419 ChannelAddr = ((ChannelAddr >> 7) << 6) | (ChannelAddr & 0x3F);
420 } else if (DctSelIntLvAddr == 1) {
421 // A[12]
422 ChannelAddr = ((ChannelAddr >> 13) << 12) | (ChannelAddr & 0xFFF);
423 } else {
424 // Hash 9
425 ChannelAddr = ((ChannelAddr >> 10) << 9) | (ChannelAddr & 0x1FF);
426 }
427 }
428
429 // Determine the Chip Select
430 for (cs = 0; cs < MAX_CS_PER_CHANNEL; ++ cs) {
431 DctNum = DctGangEn ? 0 : *ChannelSelect;
432
433 // Obtain the CS Base
434 temp = MemFGetPCI (NBPtr, *NodeID, DctNum, BFCSBaseAddr0Reg + cs);
435 CSEn = (BOOLEAN) (temp & 0x1);
436 CSBase = ((UINT64) temp & NBPtr->CsRegMsk) << 8;
437
438 // Obtain the CS Mask
439 CSMask = ((UINT64) MemFGetPCI (NBPtr, *NodeID, DctNum, BFCSMask0Reg + (cs >> 1)) & NBPtr->CsRegMsk) << 8;
440
441 // Adjust the Channel Addr for easy comparison
442 InputAddr = ((ChannelAddr >> 8) & NBPtr->CsRegMsk) << 8;
443
444 if (CSEn && ((InputAddr & ~CSMask) == (CSBase & ~CSMask))) {
445 CSFound = TRUE;
446
447 *ChipSelect = cs;
448
449 temp = MemFGetPCI (NBPtr, *NodeID, 0, BFOnLineSpareControl);
450 SwapDone = (BOOLEAN) ((temp >> (1 + 2 * (*ChannelSelect))) & 0x1);
451 BadDramCs = (UINT8) ((temp >> (4 + 4 * (*ChannelSelect))) & 0x7);
452 if (SwapDone && (cs == BadDramCs)) {
453 // Find the spare rank for the channel
454 for (spare = 0; spare < MAX_CS_PER_CHANNEL; ++spare) {
455 if ((MemFGetPCI (NBPtr, *NodeID, DctNum, BFCSBaseAddr0Reg + spare) & 0x2) != 0) {
456 *ChipSelect = spare;
457 break;
458 }
459 }
460 }
461 ASSERT (*ChipSelect < MAX_CS_PER_CHANNEL);
462
463 break;
464 }
465 }
466 }
467 if (CSFound) {
468 break;
469 }
470 }
471
472 // last ditch sanity check
473 ASSERT (!CSFound || ((*NodeID < mmPtr->DieCount) && (*ChannelSelect < NBPtr[*NodeID].DctCount) && (*ChipSelect < MAX_CS_PER_CHANNEL)));
474 if (CSFound) {
475 return AGESA_SUCCESS;
476 } else {
477 return AGESA_BOUNDS_CHK;
478 }
479
480}
481
482
483/*-----------------------------------------------------------------------------*/
484/**
485*
486* This function is the interface to call the PCI register access function
487* defined in NB block.
488*
489* @param[in] *NBPtr - Pointer to the parameter structure MEM_NB_BLOCK
490* @param[in] NodeID - Node ID number of the target Northbridge
491* @param[in] DctNum - DCT number if applicable, otherwise, put 0
492* @param[in] BitFieldName - targeted bitfield
493*
494* @retval UINT32 - 32 bits PCI register value
495*
496*/
497UINT32
498STATIC
499MemFGetPCI (
500 IN MEM_NB_BLOCK *NBPtr,
501 IN UINT8 NodeID,
502 IN UINT8 DctNum,
503 IN BIT_FIELD_NAME BitFieldName
504 )
505{
506 MEM_NB_BLOCK *LocalNBPtr;
507 UINT8 Die;
508
509 // Find NBBlock that associates with node NodeID
Edward O'Callaghan8ff0ead2014-12-08 01:00:43 +1100510 for (Die = 0; Die < MAX_NODES_SUPPORTED; Die ++)
511 if (NBPtr[Die].Node == NodeID)
512 break;
513
zbao7d94cf92012-07-02 14:19:14 +0800514 ASSERT (Die < MAX_NODES_SUPPORTED);
515
516 // Get the northbridge pointer for the targeted node.
517 LocalNBPtr = &NBPtr[Die];
518 LocalNBPtr->FamilySpecificHook[DCTSelectSwitch] (LocalNBPtr, &DctNum);
519 LocalNBPtr->Dct = DctNum;
520 // The caller of this function will take care of the ganged/unganged situation.
521 // So Ganged is set to be false here, and do PCI read on the DCT specified by DctNum.
522 return LocalNBPtr->GetBitField (LocalNBPtr, BitFieldName);
523}
524
525/*-----------------------------------------------------------------------------*/
526/**
527*
528* This function returns an even parity bit (making the total # of 1's even)
529* {0, 1} = number of set bits in argument is {even, odd}.
530*
531* @param[in] address - the address on which the parity bit will be calculated
532*
533* @retval UINT8 - parity bit
534*
535*/
536
537UINT8
538STATIC
539MemFUnaryXOR (
540 IN UINT32 address
541 )
542{
543 UINT8 parity;
544 UINT8 index;
545 parity = 0;
546 for (index = 0; index < 32; ++ index) {
547 parity = (UINT8) (parity ^ (address & 0x1));
548 address = address >> 1;
549 }
550 return parity;
551}