/* $NoKeywords:$ */
/**
 * @file
 *
 * mrttwl3.c
 *
 * Technology Phy assisted write levelization for recovery DDR3
 *
 * @xrefitem bom "File Content Label" "Release Content"
 * @e project: AGESA
 * @e sub-project: (Proc/Recovery/Mem)
 * @e \$Revision: 50454 $ @e \$Date: 2011-04-10 21:20:37 -0600 (Sun, 10 Apr 2011) $
 *
 **/
/*****************************************************************************
*
* Copyright (C) 2012 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 "mm.h"
#include "mn.h"
#include "mru.h"
#include "mt.h"
#include "mrt3.h"
#include "Filecode.h"
CODE_GROUP (G2_PEI)
RDATA_GROUP (G2_PEI)

#define FILECODE PROC_RECOVERY_MEM_TECH_DDR3_MRTTWL3_FILECODE
/*----------------------------------------------------------------------------
 *                          DEFINITIONS AND MACROS
 *
 *----------------------------------------------------------------------------
 */
#define MAX_BYTELANES 8             /* Max Bytelanes per channel */

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

/*----------------------------------------------------------------------------
 *                        PROTOTYPES OF LOCAL FUNCTIONS
 *
 *----------------------------------------------------------------------------
 */

VOID
STATIC
MemRecTPrepareDIMMs3 (
  IN OUT   MEM_TECH_BLOCK *TechPtr,
  IN       BOOLEAN Wl
  );

VOID
STATIC
MemRecTProcConfig3 (
  IN OUT   MEM_TECH_BLOCK *TechPtr
  );

VOID
STATIC
MemRecTBeginWLTrain3 (
  IN OUT   MEM_TECH_BLOCK *TechPtr
  );

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

/* -----------------------------------------------------------------------------*/
/**
 *
 *      This function executed hardware based write levelization for a specific die
 *
 *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
 *
 */

VOID
MemRecTTrainDQSWriteHw3 (
  IN OUT   MEM_TECH_BLOCK *TechPtr
  )
{
  MEM_NB_BLOCK  *NBPtr;

  NBPtr = TechPtr->NBPtr;

  IDS_HDT_CONSOLE (MEM_STATUS, "\nStart write leveling\n");
  //  Disable auto refresh by configuring F2x[1, 0]8C[DisAutoRefresh] = 1.
  NBPtr->SetBitField (NBPtr, BFDisAutoRefresh, 1);
  //  Disable ZQ calibration short command by configuring F2x[1, 0]94[ZqcsInterval] = 00b.
  NBPtr->SetBitField (NBPtr, BFZqcsInterval, 0);

  // 1. Specify the target Dimm that is to be trained by programming
  //     F2x[1, 0]9C_x08[TrDimmSel].
  IDS_HDT_CONSOLE (MEM_STATUS, "\t\tCS %d\n", NBPtr->DimmToBeUsed << 1);
  NBPtr->SetBitField (NBPtr, BFTrDimmSel, NBPtr->DimmToBeUsed);

  // 2. Prepare the DIMMs for write levelization using DDR3-defined
  // MR commands.
  MemRecTPrepareDIMMs3 (TechPtr, TRUE);

  // 3. After the DIMMs are configured, BIOS waits 40 MEMCLKs to
  //     satisfy DDR3-defined internal DRAM timing.
  MemRecUWait10ns (10, NBPtr->MemPtr);

  // 4. Configure the processor's DDR phy for write levelization training:
  MemRecTProcConfig3 (TechPtr);

  // 5. Begin write levelization training
  MemRecTBeginWLTrain3 (TechPtr);

  // 6. Configure DRAM Phy Control Register so that the phy stops driving write levelization ODT.
  // Program WrLvOdtEn=0
  NBPtr->SetBitField (NBPtr, BFWrLvOdtEn, 0);

  // Wait 10 MEMCLKs to allow for ODT signal settling.
  MemRecUWait10ns (3, NBPtr->MemPtr);

  // 7. Program the target Dimm back to normal operation
  MemRecTPrepareDIMMs3 (TechPtr, FALSE);

  //  13.Program F2x[1, 0]8C[DisAutoRefresh] = 0.
  NBPtr->SetBitField (NBPtr, BFDisAutoRefresh, 0);
  //  14.Program F2x[1, 0]94[ZqcsInterval] to the proper interval for the current memory configuration.
  NBPtr->SetBitField (NBPtr, BFZqcsInterval, 2);

  IDS_HDT_CONSOLE (MEM_FLOW, "End write leveling\n\n");
}

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

/* -----------------------------------------------------------------------------*/
/**
 *
 *      This function prepares the DIMMS for Write Levelization
 *
 *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
 *     @param[in] Wl - Indicates if WL mode should be enabled
 *
 */

VOID
STATIC
MemRecTPrepareDIMMs3 (
  IN OUT   MEM_TECH_BLOCK *TechPtr,
  IN       BOOLEAN Wl
  )
{
  UINT8 ChipSel;
  MEM_NB_BLOCK  *NBPtr;

  NBPtr = TechPtr->NBPtr;

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

  for (ChipSel = 0; ChipSel < MAX_CS_PER_CHANNEL; ChipSel++) {
    // Set Dram ODT based on current mode.
    if ((NBPtr->DCTPtr->Timings.CsPresent & (UINT16) 1 << ChipSel) != 0) {
      if (Wl) {
        NBPtr->SetDramOdtRec (NBPtr, WRITE_LEVELING_MODE, ChipSel, (NBPtr->DimmToBeUsed << 1));
      } else {
        NBPtr->SetDramOdtRec (NBPtr, MISSION_MODE, ChipSel, (NBPtr->DimmToBeUsed << 1));
      }

      NBPtr->SetBitField (NBPtr, BFMrsChipSel, ChipSel);

      // Set MR1 to F2x7C[MrsAddress], F2x7C[MrsBank]=1
      MemRecTEMRS13 (TechPtr);
      // Program Level
      if (Wl) {
        if ((ChipSel >> 1) == NBPtr->DimmToBeUsed) {
          NBPtr->SetBitField (NBPtr, BFLevel, 1);
          if (ChipSel & 1) {
            NBPtr->SetBitField (NBPtr, BFMrsQoff, 1);
          }
        }
      }
      // Send command
      NBPtr->SendMrsCmd (NBPtr);

      // Set MR2 to F2x7C[MrsAddress], F2x7C[MrsBank]=1
      MemRecTEMRS23 (TechPtr);
      // Send command
      NBPtr->SendMrsCmd (NBPtr);
    }
  }
}

/* -----------------------------------------------------------------------------*/
/**
 *
 *      This function configures the DIMMS for Write Levelization
 *
 *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
 *
 */

VOID
STATIC
MemRecTProcConfig3 (
  IN OUT   MEM_TECH_BLOCK *TechPtr
  )
{
  MEM_NB_BLOCK *NBPtr;
  CH_DEF_STRUCT *ChannelPtr;
  UINT8 ByteLane;
  UINT8 *Seed;
  UINT8 DefaultSeed;
  UINT8 CurrentSeed;
  UINT8 Dimm;

  NBPtr = TechPtr->NBPtr;
  ChannelPtr = TechPtr->NBPtr->ChannelPtr;

  Dimm = NBPtr->DimmToBeUsed;

  // Program WrLvOdtEn=1
  NBPtr->SetBitField (NBPtr, BFWrLvOdtEn, 1);

  // Wait 10 MEMCLKs to allow for ODT signal settling.
  MemRecUWait10ns (3, NBPtr->MemPtr);

  IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t Byte: 00 01 02 03 04 05 06 07 ECC\n");
  IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tSeeds: ");
  //  Program an initialization Value to registers F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52 to set
  //  the gross and fine delay for all the byte lane fields. If the target frequency is different than 400MHz,
  //  BIOS must execute two training passes for each Dimm. For pass 1 at a 400MHz MEMCLK frequency,
  //  use an initial total delay Value of 01Fh. This represents a 1UI (UI=.5MEMCLK) delay and is determined
  //  by design.

  //  Get default seed
  if (ChannelPtr->RegDimmPresent != 0) {
    DefaultSeed = 0x41;
  } else if (ChannelPtr->SODimmPresent != 0) {
    DefaultSeed = 0x12;
  } else {
    DefaultSeed = 0x1A;
  }

  //  Get platform override seed
  Seed = (UINT8 *) MemRecFindPSOverrideEntry (NBPtr->RefPtr->PlatformMemoryConfiguration, PSO_WL_SEED, NBPtr->MCTPtr->SocketId, ChannelPtr->ChannelID, Dimm);

  IDS_HDT_CONSOLE (MEM_FLOW, "Seeds: ");
  for (ByteLane = 0; ByteLane < 8; ByteLane++) {
    // This includes ECC as byte 8
    CurrentSeed = ((Seed != NULL) ? Seed[ByteLane] : DefaultSeed);
    NBPtr->SetTrainDly (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), CurrentSeed);
    ChannelPtr->WrDqsDlys[Dimm * MAX_BYTELANES + ByteLane] = CurrentSeed;
    IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", CurrentSeed);
  }
  IDS_HDT_CONSOLE (MEM_FLOW, "\n");

  // Program F2x[1, 0]9C_x08[WrtLvTrMode]=0 for phy assisted training.

  // Program F2x[1, 0]9C_x08[TrNibbleSel]=0

  IDS_HDT_CONSOLE (MEM_FLOW, "\n");
}

/* -----------------------------------------------------------------------------*/
/**
 *
 *      This function begins WL training for a specific DIMM
 *
 *     @param[in,out]   *TechPtr   - Pointer to the MEM_TECH_BLOCK
 *
 */

VOID
STATIC
MemRecTBeginWLTrain3 (
  IN OUT   MEM_TECH_BLOCK *TechPtr
  )
{
  MEM_NB_BLOCK *NBPtr;
  UINT8 ByteLane;
  UINT8 Seed;
  UINT8 Delay;
  UINT8 Dimm;

  NBPtr = TechPtr->NBPtr;

  Dimm = NBPtr->DimmToBeUsed;
  IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tWrtLvTrEn = 1\n");
  // Program F2x[1, 0]9C_x08[WrtLlTrEn]=1.
  NBPtr->SetBitField (NBPtr, BFWrtLvTrEn, 1);

  //  Wait 200 MEMCLKs. If executing pass 2, wait 32 MEMCLKs.
  MemRecUWait10ns (50, NBPtr->MemPtr);

  //  Program F2x[1, 0]9C_x08[WrtLlTrEn]=0.
  NBPtr->SetBitField (NBPtr, BFWrtLvTrEn, 0);

  //  Read from registers F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52 to get the gross and fine Delay settings
  //   for the target Dimm and save these values.
  IDS_HDT_CONSOLE (MEM_FLOW, " PRE WrDqs\n");
  for (ByteLane = 0; ByteLane < 8; ByteLane++) {
    // This includes ECC as byte 8
    Seed = NBPtr->ChannelPtr->WrDqsDlys[(Dimm * MAX_BYTELANES) + ByteLane];
    Delay = (UINT8)NBPtr->GetTrainDly (NBPtr, AccessPhRecDly, DIMM_BYTE_ACCESS (Dimm, ByteLane));
    IDS_HDT_CONSOLE (MEM_FLOW, "  %02x ", Delay);
    if (((Seed >> 5) == 0) && ((Delay >> 5) == 3)) {

      // If seed has gross delay of 0 and PRE has gross delay of 3,
      // then round the total delay of TxDqs to 0.
      Delay = 0;
    }

    NBPtr->SetTrainDly (NBPtr, AccessWrDqsDly, DIMM_BYTE_ACCESS (Dimm, ByteLane), Delay);
    NBPtr->ChannelPtr->WrDqsDlys[(Dimm * MAX_BYTELANES) + ByteLane] = Delay;
    IDS_HDT_CONSOLE (MEM_FLOW, "  %02x\n", Delay);
  }

  IDS_HDT_CONSOLE_DEBUG_CODE (
    IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\tWrDqs: ");
    for (ByteLane = 0; ByteLane < 8; ByteLane++) {
      IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", NBPtr->ChannelPtr->WrDqsDlys[ByteLane]);
    }
    IDS_HDT_CONSOLE (MEM_FLOW, "\n\n");
  );
}
