/**
 * @file
 *
 * mrndct.c
 *
 * Northbridge common DCT support for Recovery
 *
 * @xrefitem bom "File Content Label" "Release Content"
 * @e project: AGESA
 * @e sub-project: (Proc/Recovery/Mem/NB)
 * @e \$Revision: 6781 $ @e \$Date: 2008-07-17 12:11:08 -0500 (Thu, 17 Jul 2008) $
 *
 **/
/*****************************************************************************
*
* Copyright (c) 2011, Advanced Micro Devices, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of Advanced Micro Devices, Inc. nor the names of
*       its contributors may be used to endorse or promote products derived
*       from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ***************************************************************************
*
*/


/*
 *----------------------------------------------------------------------------
 *                                MODULES USED
 *
 *----------------------------------------------------------------------------
 */



#include "AGESA.h"
#include "OptionMemory.h"
#include "PlatformMemoryConfiguration.h"
#include "Ids.h"
#include "cpuFamRegisters.h"
#include "mm.h"
#include "mn.h"
#include "mt.h"
#include "mru.h"
#include "Filecode.h"
#define FILECODE PROC_RECOVERY_MEM_NB_MRNDCT_FILECODE
/*----------------------------------------------------------------------------
 *                          DEFINITIONS AND MACROS
 *
 *----------------------------------------------------------------------------
 */
#define RECDEF_DRAM_CONTROL_REG   0x320C2A06
#define RECDEF_DRAM_MRSREG        0x000400A4
#define RECDEF_DRAM_TIMING_LO     0x000A0092
#define RECDEF_DRAM_TIMING_HI     0xB6D218FF
#define RECDEF_CSMASK_REG         0x00083FE0
#define RECDEF_DRAM_CONFIG_LO_REG 0x00000000
#define RECDEF_DRAM_CONFIG_HI_REG 0x1F48010B
#define RECDEF_DRAM_BASE_REG      0x00000003


/*----------------------------------------------------------------------------
 *                           TYPEDEFS AND STRUCTURES
 *
 *----------------------------------------------------------------------------
 */

/*----------------------------------------------------------------------------
 *                        PROTOTYPES OF LOCAL FUNCTIONS
 *
 *----------------------------------------------------------------------------
 */
VOID
STATIC
MemRecTCtlOnDimmMirrorNb (
  IN OUT   MEM_NB_BLOCK *NBPtr,
  IN       BOOLEAN SetFlag
  );

VOID
STATIC
MemRecNSwapBitsNb (
  IN OUT   MEM_NB_BLOCK *NBPtr
  );

/*----------------------------------------------------------------------------
 *                            EXPORTED FUNCTIONS
 *
 *----------------------------------------------------------------------------
 */

/* -----------------------------------------------------------------------------*/
/**
 *
 *      This function programs the memory controller with configuration parameters
 *
 *
 *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
 *
 *     @return          TRUE - An Error value lower than AGESA_ERROR may have occurred
 *     @return          FALSE - An Error value greater than or equal to AGESA_ERROR may have occurred
 */

BOOLEAN
MemRecNAutoConfigNb (
  IN OUT   MEM_NB_BLOCK *NBPtr
  )
{
  UINT8 Dimm;
  UINT8 Dct;
  UINT32 RegValue;
  UINT8 ChipSel;
  UINT32 CSBase;
  DCT_STRUCT *DCTPtr;
  CH_DEF_STRUCT *ChannelPtr;

  Dct = NBPtr->Dct;
  DCTPtr = NBPtr->DCTPtr;
  ChannelPtr = NBPtr->ChannelPtr;

  //Prepare variables for future usage.
  for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) {
    if ((ChannelPtr->ChDimmValid & (UINT8) 1 << Dimm) != 0) {
      DCTPtr->Timings.CsPresent |= (UINT16) 1 << (Dimm * 2);
      if (((ChannelPtr->DimmDrPresent & (UINT8) 1 << Dimm) == 0) && ((ChannelPtr->DimmQrPresent & (UINT8) 1 << Dimm) == 0)) {
        continue;
      } else {
        DCTPtr->Timings.CsPresent |= (UINT16) 1 << (Dimm * 2 + 1);
      }
    }
  }

  Dimm = NBPtr->DimmToBeUsed;

  //Temporarily set all CS Base/Limit registers (corresponding to Dimms exist on a channel) with 256MB size for WL training.
  CSBase = 0;
  for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel++) {
    if (DCTPtr->Timings.CsPresent & (UINT8) 1 << ChipSel) {

      CSBase &= (UINT32) ~0x08; //Clear OnDimmMirror bit.
      if (((ChipSel & 1) != 0) && ((ChannelPtr->DimmMirrorPresent & (UINT8) 1 << (ChipSel >> 1)) != 0)) {
        CSBase |= (UINT32) 0x08; //Set OnDimmMirror bit.
      }
      MemRecNSetBitFieldNb (NBPtr, (BFCSBaseAddr0Reg + ChipSel), (CSBase | 0x01));
      CSBase += 0x100000;
      if ((ChipSel & 1) == 0) {
        MemRecNSetBitFieldNb (NBPtr, (BFCSMask0Reg + (ChipSel >> 1)), RECDEF_CSMASK_REG);
      }
    }
  }
  MemRecNSetBitFieldNb (NBPtr, BFDramBaseReg0, RECDEF_DRAM_BASE_REG);
  MemRecNSetBitFieldNb (NBPtr, BFDramLimitReg0, 0x70000);

  // Disable the other DCT
  NBPtr->MemRecNSwitchDctNb (NBPtr, Dct ^ 0x01);
  MemRecNSetBitFieldNb (NBPtr, BFDisDramInterface, 1);
  NBPtr->MemRecNSwitchDctNb (NBPtr, Dct);
  if (Dct != 0) {
    // If DCT 1, set DctSelBase registers
    MemRecNSetBitFieldNb (NBPtr, BFDctSelBaseAddrReg, 0x00000003);
    MemRecNSetBitFieldNb (NBPtr, BFDctSelBaseOffsetReg, 0x00000000);
  }

  MemRecNSetBitFieldNb (NBPtr, BFDramBankAddrReg, 0x00001111);

  // Set timing registers
  MemRecNSetBitFieldNb (NBPtr, BFDramTimingLoReg, RECDEF_DRAM_TIMING_LO);
  MemRecNSetBitFieldNb (NBPtr, BFDramTimingHiReg, RECDEF_DRAM_TIMING_HI);
  MemRecNSetBitFieldNb (NBPtr, BFDramMRSReg, RECDEF_DRAM_MRSREG);
  MemRecNSetBitFieldNb (NBPtr, BFDramControlReg, RECDEF_DRAM_CONTROL_REG);

  // Set DRAM Config Low Register
  RegValue = RECDEF_DRAM_CONFIG_LO_REG;
  // Set x4Dimm based on DIMM type
  if ((NBPtr->ChannelPtr->Dimmx4Present & ((UINT8) 1 << Dimm)) != 0) {
    RegValue |= ((UINT32) 1 << (Dimm + 12));
  }
  // If not registered, set unbuffered DIMM
  if (!(NBPtr->ChannelPtr->RegDimmPresent & ((UINT8) 1 << Dimm))) {
    RegValue |= ((UINT32) 1 << 16);
  }
  MemRecNSetBitFieldNb (NBPtr, BFDramConfigLoReg, RegValue);

  // Set DRAM Config High Register
  RegValue = RECDEF_DRAM_CONFIG_HI_REG;
  MemRecNSetBitFieldNb (NBPtr, BFDramConfigHiReg, RegValue);

  //======================================================================
  // Build Dram Config Misc Register Value
  //======================================================================
  //
  if ((NBPtr->ChannelPtr->RegDimmPresent != 0) && (NBPtr->ChannelPtr->TechType == DDR3_TECHNOLOGY)) {
    MemRecNSetBitFieldNb (NBPtr, BFSubMemclkRegDly, 1);
  }
  MemRecNSetBitFieldNb (NBPtr, BFOdtSwizzle, 1);

  return TRUE;
}


/* -----------------------------------------------------------------------------*/
/**
 *
 *   This function gets platform specific config/timing values from the interface layer and
 *   programs them into DCT.
 *
 *
 *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
 *
 *     @return          TRUE - An Error value lower than AGESA_ERROR may have occurred
 *     @return          FALSE - An Error value greater than or equal to AGESA_ERROR may have occurred
 */

BOOLEAN
MemRecNPlatformSpecNb (
  IN OUT   MEM_NB_BLOCK *NBPtr
  )
{
  UINT8 MaxDimmPerCH;
  UINT32 AddrTmgValue;
  UINT32 DrvStrValue;
  UINT8 *DimmsPerChPtr;
  CH_DEF_STRUCT *ChannelPtr;

  ChannelPtr = NBPtr->ChannelPtr;
  DimmsPerChPtr = MemRecFindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PSO_MAX_DIMMS, 0, ChannelPtr->ChannelID);
  if (DimmsPerChPtr != NULL) {
    MaxDimmPerCH = *DimmsPerChPtr;
  } else {
    MaxDimmPerCH = 2;
  }

  if (ChannelPtr->RegDimmPresent) {
    if (MaxDimmPerCH == 2) {
      DrvStrValue = 0x20223222;
      AddrTmgValue = 0;
      if ((ChannelPtr->Dimms == 1) && (ChannelPtr->DimmQrPresent == 0)) {
        DrvStrValue = 0x20113222;
      }
    } else if (MaxDimmPerCH == 3) {
      DrvStrValue = 0x20223222;
      AddrTmgValue = 0;
      if ((ChannelPtr->Dimms == 1) && (ChannelPtr->DimmQrPresent == 0)) {
        DrvStrValue = 0x20113222;
      } else if (ChannelPtr->Dimms == 3) {
        AddrTmgValue = 0x00380038;
      }
    } else {
      DrvStrValue = 0x20223222;
      AddrTmgValue = 0;
      if (ChannelPtr->Dimms >= 3) {
        AddrTmgValue = 0x002F0000;
      } else if (ChannelPtr->Dimms == 1) {
        DrvStrValue = 0x20113222;
      }
    }
  } else {
    if (ChannelPtr->Dimms == 2) {
      DrvStrValue = 0x20223323;
      AddrTmgValue = 0x00390039;
    } else {
      DrvStrValue = 0x20113222;
      AddrTmgValue = 0;
      if ((ChannelPtr->Dimms == 1) && (ChannelPtr->Loads >= 16)) {
        AddrTmgValue = 0x003B0000;
      }
    }
  }
  MemRecNSetBitFieldNb (NBPtr, BFODCControl, DrvStrValue);
  MemRecNSetBitFieldNb (NBPtr, BFAddrTmgControl, AddrTmgValue);
  return TRUE;
}

/* -----------------------------------------------------------------------------*/
/**
 *
 *   This function  reads MemClkFreqVal bit to see if the DIMMs are present in this node.
 *  If the DIMMs are present then set the DRAM Enable bit for this node.
 *
 *  Setting dram init starts up the DCT state machine, initializes the
 *  dram devices with MRS commands, and kicks off any
 *  HW memory clear process that the chip is capable of.  The sooner
 *  that dram init is set for all nodes, the faster the memory system
 *  initialization can complete.  Thus, the init loop is unrolled into
 *  two loops so as to start the processes for non BSP nodes sooner.
 *  This procedure will not wait for the process to finish.  Synchronization is
 *  handled elsewhere.
 *
 *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
 *
 */

VOID
MemRecNStartupDCTNb (
  IN OUT   MEM_NB_BLOCK *NBPtr
  )
{
  // 1. Ensure F2x[1, 0]9C_x08[DisAutoComp] = 1.
  // 2. BIOS waits 5 us for the disabling of the compensation engine to complete.
  // ------- Done in InitPhyComp_Nb -------
  //
  MemRecNSetBitFieldNb (NBPtr, BFDisAutoComp, 1);
  MemRecUWait10ns (500, NBPtr->MemPtr);

  //MemRecNSetBitFieldNb (NBPtr, BFInitDram, 1);    // HW Dram init
  AGESA_TESTPOINT (TpProcMemDramInit, &(NBPtr->MemPtr->StdHeader));
  NBPtr->TechPtr->DramInit (NBPtr->TechPtr);

  // 7. Program F2x[1, 0]9C_x08[DisAutoComp] = 0.
  // 8. BIOS must wait 750 us for the phy compensation engine
  //    to reinitialize.
  //
  MemRecNSetBitFieldNb (NBPtr, BFDisAutoComp, 0);
  MemRecUWait10ns (75000, NBPtr->MemPtr);

  while (MemRecNGetBitFieldNb (NBPtr, BFDramEnabled) == 0);
}

/* -----------------------------------------------------------------------------*/
/**
 *
 *   This function  sets the maximum round-trip latency in the system from the processor to the DRAM
 *   devices and back.

 *
 *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
 *     @param[in]     MaxRcvEnDly - Maximum receiver enable delay value
 *
 */

VOID
MemRecNSetMaxLatencyNb (
  IN OUT   MEM_NB_BLOCK *NBPtr,
  IN       UINT16 MaxRcvEnDly
  )
{
  UINT16 SubTotal;

  AGESA_TESTPOINT (TpProcMemRcvrCalcLatency, &(NBPtr->MemPtr->StdHeader));

  // Multiply the CAS Latency by two to get a number of 1/2 MEMCLKs UINTs.
  SubTotal = 6 * 2;

  // If registered DIMMs are being used then add 1 MEMCLK to the sub-total.
  if (MemRecNGetBitFieldNb (NBPtr, BFUnBuffDimm) == 0) {
    SubTotal += 2;
  }

  // if (AddrCmdSetup || CsOdtSetup || CkeSetup) then K := K + 2;
  SubTotal += 2;

  // If the F2x[1, 0]78[RdPtrInit] field is 4, 5, 6 or 7 MEMCLKs,
  // then add 4, 3, 2, or 1 MEMCLKs, respectively to the sub-total.
  //
  SubTotal += 8 - 5;

  // Add the maximum (worst case) delay value of DqsRcvEnGrossDelay
  // that exists across all DIMMs and byte lanes.
  //
  SubTotal += MaxRcvEnDly >> 5;

  // Add 5.5 to the sub-total. 5.5 represents part of the processor
  // specific constant delay value in the DRAM clock domain.
  //
  SubTotal += 5;             // add 5.5 1/2MemClk

  // Convert the sub-total (in 1/2 MEMCLKs) to northbridge clocks (NCLKs)
  // as follows (assuming DDR400 and assuming that no P-state or link speed
  // changes have occurred).
  //
  //     Simplified formula:
  //     SubTotal *= (Fn2xD4[NBFid]+4)/4
  //
  SubTotal = SubTotal * ((UINT16) MemRecNGetBitFieldNb (NBPtr, BFNbFid) + 4);
  SubTotal /= 4;

  // Add 5 NCLKs to the sub-total. 5 represents part of the processor
  // specific constant value in the northbridge clock domain.
  //
  SubTotal += 5;

  // Program the F2x[1, 0]78[MaxRdLatency] register with the total delay value
  MemRecNSetBitFieldNb (NBPtr, BFMaxLatency, SubTotal);
}


/* -----------------------------------------------------------------------------*/
/**
 *
 *   Set Dram ODT for mission mode and write leveling mode.
 *
 *     @param[in,out]   *NBPtr     - Pointer to the MEM_NB_BLOCK
 *     @param[in]       OdtMode    - Mission mode or write leveling mode
 *     @param[in]       ChipSelect - Chip select number
 *     @param[in]       TargetCS   - Chip select number that is being trained
 *
 */

VOID
MemRecNSetDramOdtNb (
  IN OUT   MEM_NB_BLOCK *NBPtr,
  IN       ODT_MODE OdtMode,
  IN       UINT8 ChipSelect,
  IN       UINT8 TargetCS
  )
{
  UINT8 Dimms;
  UINT8 DramTerm;
  UINT8 DramTermDyn;
  BOOLEAN IsDualRank;

  Dimms = NBPtr->ChannelPtr->Dimms;
  IsDualRank = (NBPtr->DCTPtr->Timings.CsPresent & (ChipSelect + 1)) ? TRUE : FALSE;

  if (OdtMode == WRITE_LEVELING_MODE) {
    DramTermDyn = 0;  // Disabled

    if (ChipSelect & 1) {
      DramTerm = 0;   // Disabled
    } else if (Dimms == 1) {
      DramTerm = 1;   // 60 Ohms
    } else {
      DramTerm = 3;   // 40 Ohms
    }

    if (ChipSelect == TargetCS) {
      if (Dimms == 2) {
        DramTerm = 2;   // 120 Ohms
      } else if (IsDualRank) {
        DramTerm = 0;   // Disabled
      }

      // Program WrLvOdt
      MemRecNSetBitFieldNb (NBPtr, BFWrLvOdt, 0x0F);
    } else if (ChipSelect == (TargetCS + 1)) {
      if (Dimms == 1) {
        DramTerm = 1;   // 60 Ohms
      }
    }
  } else {
    // Dram nominal termination
    if (Dimms == 1) {
      DramTerm = 1;   // 60 Ohms
    } else {
      DramTerm = 3;   // 40 Ohms
    }

    // Dram dynamic termination
    if (Dimms < 2) {
      DramTermDyn = 0;    // Disabled
    } else {
      DramTermDyn = 2;    // 120 Ohms
    }
  }
  MemRecNSetBitFieldNb (NBPtr, BFDramTerm, DramTerm);
  MemRecNSetBitFieldNb (NBPtr, BFDramTermDyn, DramTermDyn);
}

/*----------------------------------------------------------------------------
 *                              LOCAL FUNCTIONS
 *
 *----------------------------------------------------------------------------
 */

/* -----------------------------------------------------------------------------*/
/**
 *
 *   This function sends an MRS command
 *
 *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
 *
 */

VOID
MemRecNSendMrsCmdNb (
  IN OUT   MEM_NB_BLOCK *NBPtr
  )
{
  BOOLEAN ClearODM;
  ClearODM = FALSE;
  if (NBPtr->IsSupported[CheckClearOnDimmMirror]) {
    ClearODM = FALSE;
    if ((NBPtr->MCTPtr->LogicalCpuid.Revision & AMD_F10_C0) != 0) {
      if (NBPtr->IsSupported[CheckClearOnDimmMirror]) {
        if (MemRecNGetBitFieldNb (NBPtr, BFEnDramInit) == 0) {
          // For C0, if EnDramInit bit is cleared, ODM needs to be cleared before sending MRS
          MemRecTCtlOnDimmMirrorNb (NBPtr, FALSE);
          ClearODM = TRUE;
        }
      }
    }
  }

  MemRecNSwapBitsNb (NBPtr);

  IDS_HDT_CONSOLE ("\t\t\tCS%d MR%d %04x\n",
              (MemRecNGetBitFieldNb (NBPtr, BFDramInitRegReg) >> 20) & 0xF,
              (MemRecNGetBitFieldNb (NBPtr, BFDramInitRegReg) >> 16) & 0xF,
              (MemRecNGetBitFieldNb (NBPtr, BFDramInitRegReg) & 0xFFFF));

  // 1.Set SendMrsCmd=1
  MemRecNSetBitFieldNb (NBPtr, BFSendMrsCmd, 1);

  // 2.Wait for SendMrsCmd=0
  while (MemRecNGetBitFieldNb (NBPtr, BFSendMrsCmd)) {}

  if (NBPtr->IsSupported[CheckClearOnDimmMirror]) {
    if (ClearODM) {
      // Restore ODM if necessary
      MemRecTCtlOnDimmMirrorNb (NBPtr, TRUE);
    }
  }
}

/* -----------------------------------------------------------------------------*/
/**
 *
 *   This function sends the ZQCL command
 *
 *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
 *
 */

VOID
MemRecNSendZQCmdNb (
  IN OUT   MEM_NB_BLOCK *NBPtr
  )
{
  // 1.Program MrsAddress[10]=1
  MemRecNSetBitFieldNb (NBPtr, BFMrsAddress, (UINT32) 1 << 10);

  // 2.Set SendZQCmd=1
  MemRecNSetBitFieldNb (NBPtr, BFSendZQCmd, 1);

  // 3.Wait for SendZQCmd=0
  while (MemRecNGetBitFieldNb (NBPtr, BFSendZQCmd)) {}

  // 4.Wait 512 MEMCLKs
  MemRecUWait10ns (128, NBPtr->MemPtr);   // 512*2.5ns=1280, wait 1280ns
}


/* -----------------------------------------------------------------------------*/
/**
 *
 *      This function disables/enables F2x[1, 0][5C:40][OnDimmMirror]
 *
 *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
 *     @param[in]   SetFlag   - Enable or disable flag - TRUE - Enable, FALSE - DISABLE
 *
 */

VOID
STATIC
MemRecTCtlOnDimmMirrorNb (
  IN OUT   MEM_NB_BLOCK *NBPtr,
  IN       BOOLEAN SetFlag
  )
{
  UINT8 Chipsel;
  UINT32 CSBaseAddrReg;

  for (Chipsel = 0; Chipsel < MAX_CS_PER_CHANNEL; Chipsel += 2) {
    CSBaseAddrReg = MemRecNGetBitFieldNb (NBPtr, BFCSBaseAddr1Reg + Chipsel);
    if ((CSBaseAddrReg & 1) == 1) {
      if (SetFlag && ((NBPtr->ChannelPtr->DimmMirrorPresent & ((UINT8) 1 << (Chipsel >> 1))) != 0)) {
        CSBaseAddrReg |= ((UINT32) 1 << BFOnDimmMirror);
      } else {
        CSBaseAddrReg &= ~((UINT32) 1 << BFOnDimmMirror);
      }
      MemRecNSetBitFieldNb (NBPtr, BFCSBaseAddr1Reg + Chipsel, CSBaseAddrReg);
    }
  }
}
/* -----------------------------------------------------------------------------*/
/**
 *
 *
 *   This function swaps bits for OnDimmMirror support
 *
 *     @param[in,out]   *NBPtr   - Pointer to the MEM_NB_BLOCK
 *
 */

VOID
STATIC
MemRecNSwapBitsNb (
  IN OUT   MEM_NB_BLOCK *NBPtr
  )
{
  UINT8 ChipSel;
  UINT32 MRSReg;

  ChipSel = (UINT8) MemRecNGetBitFieldNb (NBPtr, BFMrsChipSel);
  if ((ChipSel & 1) != 0) {
    MRSReg = MemRecNGetBitFieldNb (NBPtr, BFDramInitRegReg);
    if ((NBPtr->ChannelPtr->DimmMirrorPresent & (UINT8) 1 << (ChipSel >> 1)) != 0) {
      MRSReg = (MRSReg & 0xFFFCFE07) | ((MRSReg&0x100A8) << 1) | ((MRSReg&0x20150) >> 1);
      MemRecNSetBitFieldNb (NBPtr, BFDramInitRegReg, MRSReg);
    }
  }
}
