/* $NoKeywords:$ */
/**
 * @file
 *
 * Fuse table initialization
 *
 *
 *
 * @xrefitem bom "File Content Label" "Release Content"
 * @e project:     AGESA
 * @e sub-project: GNB
 * @e \$Revision: 48452 $   @e \$Date: 2011-03-09 12:50:44 +0800 (Wed, 09 Mar 2011) $
 *
 */
/*
*****************************************************************************
*
* 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.
* ***************************************************************************
*
*/

/*----------------------------------------------------------------------------------------
 *                             M O D U L E S    U S E D
 *----------------------------------------------------------------------------------------
 */

#include  "AGESA.h"
#include  "amdlib.h"
#include  "Ids.h"
#include  "heapManager.h"
#include  "Gnb.h"
#include  "GnbFuseTable.h"
#include  "GnbCommonLib.h"
#include  "GnbNbInitLibV1.h"
#include  "OptionGnb.h"
#include  "GnbRegistersLN.h"
#include  "NbSmuLib.h"
#include  "NbConfigData.h"
#include  "NbFuseTable.h"
#include  "NbFamilyServices.h"
#include  "GfxLib.h"
#include  "Filecode.h"

#define FILECODE PROC_GNB_NB_FEATURE_NBFUSETABLE_FILECODE
/*----------------------------------------------------------------------------------------
 *                   D E F I N I T I O N S    A N D    M A C R O S
 *----------------------------------------------------------------------------------------
 */

/*----------------------------------------------------------------------------------------
 *                  T Y P E D E F S     A N D     S T R U C T U  R E S
 *----------------------------------------------------------------------------------------
 */
extern GNB_BUILD_OPTIONS  GnbBuildOptions;

/*----------------------------------------------------------------------------------------
 *           P R O T O T Y P E S     O F     L O C A L     F U  N C T I O N S
 *----------------------------------------------------------------------------------------
 */

VOID
NbFuseLoadDefaultFuseTable (
  OUT     PP_FUSE_ARRAY       *PpFuseArray,
  IN      AMD_CONFIG_PARAMS   *StdHeader
  );

VOID
NbFuseLoadFuseTableFromFcr (
  OUT     PP_FUSE_ARRAY       *PpFuseArray,
  IN      AMD_CONFIG_PARAMS   *StdHeader
  );

VOID
NbFuseDebugDump (
  IN       PP_FUSE_ARRAY       *PpFuseArray,
  IN       AMD_CONFIG_PARAMS   *StdHeader
  );

VOID
NbFuseAdjustFuseTableToCurrentMainPllVco (
  IN  OUT   PP_FUSE_ARRAY      *PpFuseArray,
  IN       AMD_CONFIG_PARAMS   *StdHeader
  );

PP_FUSE_ARRAY  DefaultPpFuseArray = {
  0,                              ///< PP table revision
  {1,    0,  0,  0,  0,  0},      ///< Valid DPM states
  {0x40, 0,  0,  0,  0,  0},      ///< Sclk DPM DID
  {0,    0,  0,  0,  0,  0},      ///< Sclk DPM VID
  {0,    0,  0,  0,  0},          ///< Sclk DPM Cac
  {1,    0,  0,  0,  0,  0},      ///< State policy flags
  {2,    0,  0,  0,  0,  0},      ///< State policy label
  {0x40, 0,  0,  0},              ///< VCLK DID
  {0x40, 0,  0,  0},              ///< DCLK DID
  0x40,                           ///< Thermal SCLK
  {0,    0,  0,  0,  0,  0},      ///< Vclk/Dclk selector
  {0,    0,  0,  0},              ///< Valid Lclk DPM states
  {0,    0,  0,  0},              ///< Lclk DPM DID
  {0,    0,  0,  0},              ///< Lclk DPM VID
  {0,    0,  0,  0},              ///< Displclk DID
  3,                              ///< Pcie Gen 2 VID
  0x10,                           ///< Main PLL id for 3200 VCO
  0,                              ///< WRCK SMU clock Divisor
  {0x24, 0x24, 0x24, 0x24}        ///< Sclk VID
};


/*----------------------------------------------------------------------------------------*/
/**
 * Fuse Table Init
 *
 *
 *
 * @param[in] StdHeader        Pointer to Standard configuration
 * @retval    AGESA_STATUS
 */

AGESA_STATUS
NbFuseTableFeature (
  IN      AMD_CONFIG_PARAMS   *StdHeader
  )
{
  PP_FUSE_ARRAY          *PpFuseArray;
  D18F3xA0_STRUCT        D18F3xA0;
  BOOLEAN                LoadDefaultFuses;
  IDS_HDT_CONSOLE (GNB_TRACE, "NbFuseTableFeature Enter\n");

  PpFuseArray = (PP_FUSE_ARRAY *) GnbAllocateHeapBuffer (AMD_PP_FUSE_TABLE_HANDLE, sizeof (PP_FUSE_ARRAY), StdHeader);
  ASSERT (PpFuseArray != NULL);
  if (PpFuseArray == NULL) {
    IDS_HDT_CONSOLE (GNB_TRACE, "  ERROR!!! Heap Allocation\n");
    return AGESA_ERROR;
  }
  LibAmdMemFill (PpFuseArray, 0x00, sizeof (PP_FUSE_ARRAY), StdHeader);
  GnbLibPciRead (
       MAKE_SBDFO ( 0, 0, 0x18, 3, D18F3xA0_ADDRESS),
       AccessWidth32,
       &D18F3xA0.Value,
       StdHeader
     );

  LoadDefaultFuses = TRUE;
  if (GnbBuildOptions.GnbLoadRealFuseTable == 1) {
    if (D18F3xA0.Field.CofVidProg == 1) {
      IDS_HDT_CONSOLE (NB_MISC, "  Processor Fused\n");
      NbFuseLoadFuseTableFromFcr (PpFuseArray, StdHeader);
      if (PpFuseArray->PPlayTableRev != 0) {
        LoadDefaultFuses = FALSE;
      } else {
        IDS_HDT_CONSOLE (NB_MISC, "  PowerPlay Table Unfused\n");
      }
    } else {
      IDS_HDT_CONSOLE (NB_MISC, "  Processor Unfuse\n");
    }
  } else {
    IDS_HDT_CONSOLE (NB_MISC, "  Force default fuse table Unfuse\n");
  }

  if (LoadDefaultFuses) {
    IDS_HDT_CONSOLE (NB_MISC, "  Load default fuses\n");
    NbFuseLoadDefaultFuseTable (PpFuseArray, StdHeader);
  }
  NbFmFuseAdjustFuseTablePatch (PpFuseArray, StdHeader);
  NbFuseAdjustFuseTableToCurrentMainPllVco (PpFuseArray, StdHeader);
  IDS_OPTION_CALLOUT (IDS_CALLOUT_GNB_PPFUSE_OVERRIDE, PpFuseArray, StdHeader);
  GNB_DEBUG_CODE (
    NbFuseDebugDump (PpFuseArray, StdHeader)
    );
  IDS_HDT_CONSOLE (GNB_TRACE, "NbFuseTableFeature Exit\n");
  return AGESA_SUCCESS;
}


/*----------------------------------------------------------------------------------------*/
/**
 * Load Fuse Table From FCRs
 *
 *
 * @param[out] PpFuseArray      Pointer to save fuse table
 * @param[in]  StdHeader        Pointer to Standard configuration
 * @retval     AGESA_STATUS
 */

VOID
NbFuseLoadFuseTableFromFcr (
     OUT   PP_FUSE_ARRAY       *PpFuseArray,
  IN       AMD_CONFIG_PARAMS   *StdHeader
  )
{
  FUSE_TABLE             *FuseTable;
  UINTN                  RegisterIndex;
  FuseTable = NbFmGetFuseTranslationTable ();
  for (RegisterIndex = 0; RegisterIndex < FuseTable->FuseTableLength; RegisterIndex++ ) {
    UINTN   FieldIndex;
    UINTN   FuseRegisterTableLength;
    UINT32  FuseValue;
    FuseRegisterTableLength = FuseTable->FuseTable[RegisterIndex].FuseRegisterTableLength;
    FuseValue = NbSmuReadEfuse (
                  FuseTable->FuseTable[RegisterIndex].Register,
                  StdHeader
                  );
    for (FieldIndex = 0; FieldIndex < FuseRegisterTableLength;  FieldIndex++) {
      FUSE_REGISTER_ENTRY   RegisterEntry;
      UINT8                 *FuseArrayPtr;
      UINT32                FuseArrauValue;
      RegisterEntry = FuseTable->FuseTable[RegisterIndex].FuseRegisterTable[FieldIndex];
      FuseArrayPtr = (UINT8*) PpFuseArray + RegisterEntry.FuseOffset;
      FuseArrauValue = (FuseValue >> RegisterEntry.FieldOffset) & ((1 << RegisterEntry.FieldWidth) - 1);
      if (RegisterEntry.FieldWidth > 16) {
        *((UINT32 *) FuseArrayPtr) = FuseArrauValue;
      } else if (RegisterEntry.FieldWidth > 8) {
        *((UINT16 *) FuseArrayPtr) = (UINT16) FuseArrauValue;
      } else {
        *((UINT8 *) FuseArrayPtr) = (UINT8) FuseArrauValue;
      }
    }
  }
}

/*----------------------------------------------------------------------------------------*/
/**
 * Load Default Fuse Table
 *
 *
 * @param[out] PpFuseArray      Pointer to save fuse table
 * @param[in]  StdHeader        Pointer to Standard configuration
 * @retval     AGESA_STATUS
 */

VOID
NbFuseLoadDefaultFuseTable (
     OUT   PP_FUSE_ARRAY       *PpFuseArray,
  IN       AMD_CONFIG_PARAMS   *StdHeader
  )
{
  D18F3x15C_STRUCT  D18F3x15C;
  UINT8             MaxVidIndex;
  LibAmdMemCopy (PpFuseArray, &DefaultPpFuseArray, sizeof (PP_FUSE_ARRAY), StdHeader);
  GnbLibPciRead (
    MAKE_SBDFO ( 0, 0, 0x18, 3, D18F3x15C_ADDRESS),
    AccessWidth32,
    &D18F3x15C.Value,
    StdHeader
    );
  if (D18F3x15C.Value == 0) {
    D18F3x15C.Value = 0x24242424;
    GnbLibPciWrite (
      MAKE_SBDFO ( 0, 0, 0x18, 3, D18F3x15C_ADDRESS),
      AccessWidth32,
      &D18F3x15C.Value,
      StdHeader
      );
  }
  MaxVidIndex = GnbLocateHighestVidIndex (StdHeader);
  PpFuseArray->SclkDpmVid[0] = MaxVidIndex;
  PpFuseArray->PcieGen2Vid = MaxVidIndex;

}

/*----------------------------------------------------------------------------------------*/
/**
 * Adjust DIDs to current main PLL VCO
 *
 *  Main PLL VCO can be changed for debug perpouses
 *
 * @param[in,out] PpFuseArray      Pointer to save fuse table
 * @param[in]     StdHeader        Pointer to Standard configuration
 */

VOID
NbFuseAdjustFuseTableToCurrentMainPllVco (
  IN OUT   PP_FUSE_ARRAY       *PpFuseArray,
  IN       AMD_CONFIG_PARAMS   *StdHeader
  )
{
  UINT32  EffectiveMainPllFreq10KHz;
  UINT32  FusedMainPllFreq10KHz;
  UINT32  TempVco;
  UINTN   Index;
  EffectiveMainPllFreq10KHz = GfxLibGetMainPllFreq (StdHeader) * 100;
  FusedMainPllFreq10KHz = (PpFuseArray->MainPllId + 0x10) * 100 * 100;
  if (FusedMainPllFreq10KHz != EffectiveMainPllFreq10KHz) {
    IDS_HDT_CONSOLE (NB_MISC, "  WARNING! Adjusting fuse table for reprogrammed VCO \n");
    IDS_HDT_CONSOLE (NB_MISC, "  Actual main Freq %d \n", EffectiveMainPllFreq10KHz);
    IDS_HDT_CONSOLE (NB_MISC, "  Fused  main Freq %d \n", FusedMainPllFreq10KHz);
    for (Index = 0; Index < 5; Index++) {
      if (PpFuseArray->SclkDpmDid[Index] != 0) {
        TempVco = GfxLibCalculateClk (PpFuseArray->SclkDpmDid[Index], FusedMainPllFreq10KHz);
        PpFuseArray->SclkDpmDid[Index] = GfxLibCalculateDid (TempVco, EffectiveMainPllFreq10KHz);
      }
    }
    for (Index = 0; Index < 4; Index++) {
      if (PpFuseArray->VclkDid[Index] != 0) {
        TempVco = GfxLibCalculateClk (PpFuseArray->VclkDid[Index], FusedMainPllFreq10KHz);
        PpFuseArray->VclkDid[Index] = GfxLibCalculateDid (TempVco, EffectiveMainPllFreq10KHz);
      }
      if (PpFuseArray->DclkDid[Index] != 0) {
        TempVco = GfxLibCalculateClk (PpFuseArray->DclkDid[Index], FusedMainPllFreq10KHz);
        PpFuseArray->DclkDid[Index] = GfxLibCalculateDid (TempVco, EffectiveMainPllFreq10KHz);
      }
      if (PpFuseArray->LclkDpmDid[Index] != 0) {
        TempVco = GfxLibCalculateClk (PpFuseArray->LclkDpmDid[Index], FusedMainPllFreq10KHz);
        PpFuseArray->LclkDpmDid[Index] = GfxLibCalculateDid (TempVco, EffectiveMainPllFreq10KHz);
      }
      if (PpFuseArray->DisplclkDid[Index] != 0) {
        TempVco = GfxLibCalculateClk (PpFuseArray->DisplclkDid[Index], FusedMainPllFreq10KHz);
        PpFuseArray->DisplclkDid[Index] = GfxLibCalculateDid (TempVco, EffectiveMainPllFreq10KHz);
      }
    }
    if (PpFuseArray->SclkThermDid != 0) {
      TempVco = GfxLibCalculateClk (PpFuseArray->SclkThermDid , FusedMainPllFreq10KHz);
      PpFuseArray->SclkThermDid = GfxLibCalculateDid (TempVco, EffectiveMainPllFreq10KHz);
    }
  }
}

/*----------------------------------------------------------------------------------------*/
/**
 * Debug dump fuse table
 *
 *
 * @param[out] PpFuseArray      Pointer to save fuse table
 * @param[in]  StdHeader        Pointer to Standard configuration
 */

VOID
NbFuseDebugDump (
  IN       PP_FUSE_ARRAY       *PpFuseArray,
  IN       AMD_CONFIG_PARAMS   *StdHeader
  )
{
  UINTN   Index;
  UINT32  EffectiveMainPllFreq10KHz;

  EffectiveMainPllFreq10KHz = GfxLibGetMainPllFreq (StdHeader) * 100;
  IDS_HDT_CONSOLE (NB_MISC, "<------------ GNB FUSE TABLE------------>\n");
  for (Index = 0; Index < 4; Index++) {
    if (PpFuseArray->LclkDpmValid[Index]  != 0) {
      IDS_HDT_CONSOLE (
        NB_MISC,
        "  LCLK DID[%d] - 0x%02x (%dMHz)\n",
        Index,
        PpFuseArray->LclkDpmDid[Index],
        GfxLibCalculateClk (PpFuseArray->LclkDpmDid[Index], EffectiveMainPllFreq10KHz) / 100);
      IDS_HDT_CONSOLE (NB_MISC, "  LCLK VID[%d] - 0x02%x\n", Index, PpFuseArray->LclkDpmVid[Index]);
    }
  }
  for (Index = 0; Index < 4; Index++) {
    IDS_HDT_CONSOLE (
      NB_MISC,
      "  VCLK DID[%d] - 0x%02x (%dMHz)\n",
      Index,
      PpFuseArray->VclkDid[Index],
      (PpFuseArray->VclkDid[Index] != 0) ? (GfxLibCalculateClk (PpFuseArray->VclkDid[Index], EffectiveMainPllFreq10KHz) / 100) : 0
      );
    IDS_HDT_CONSOLE (
      NB_MISC,
      "  DCLK DID[%d] - 0x%02x (%dMHz)\n",
      Index,
      PpFuseArray->DclkDid[Index],
      (PpFuseArray->DclkDid[Index] != 0) ? (GfxLibCalculateClk (PpFuseArray->DclkDid[Index], EffectiveMainPllFreq10KHz) / 100) : 0
    );
  }
  for (Index = 0; Index < 4; Index++) {
    IDS_HDT_CONSOLE (
      NB_MISC,
      "  DISPCLK DID[%d] - 0x%02x (%dMHz)\n",
      Index,
      PpFuseArray->DisplclkDid[Index],
      (PpFuseArray->DisplclkDid[Index] != 0) ? (GfxLibCalculateClk (PpFuseArray->DisplclkDid[Index], EffectiveMainPllFreq10KHz) / 100) : 0
      );
  }
  for (Index = 0; Index < 6; Index++) {
    IDS_HDT_CONSOLE (
      NB_MISC,
      "  SCLK DID[%d] - 0x%02x (%dMHz)\n",
      Index,
      PpFuseArray->SclkDpmDid[Index],
      (PpFuseArray->SclkDpmDid[Index] != 0) ? (GfxLibCalculateClk (PpFuseArray->SclkDpmDid[Index], EffectiveMainPllFreq10KHz) / 100) : 0
    );
    IDS_HDT_CONSOLE (
      NB_MISC,
      "  SCLK TDP[%d] - 0x%x \n",
      Index,
      PpFuseArray->SclkDpmTdpLimit[Index]
    );
    IDS_HDT_CONSOLE (NB_MISC, "  SCLK VID[%d] - 0x%02x\n", Index, PpFuseArray->SclkDpmVid[Index]);
  }
  for (Index = 0; Index < 6; Index++) {
    IDS_HDT_CONSOLE (NB_MISC, "  State #%d\n", Index);
    IDS_HDT_CONSOLE (NB_MISC, "    Policy Label    - 0x%x\n", PpFuseArray->PolicyLabel[Index]);
    IDS_HDT_CONSOLE (NB_MISC, "    Policy Flag     - 0x%x\n", PpFuseArray->PolicyFlags[Index]);
    IDS_HDT_CONSOLE (NB_MISC, "    Valid SCLK      - 0x%x\n", PpFuseArray->SclkDpmValid[Index]);
    IDS_HDT_CONSOLE (NB_MISC, "    Vclk/Dclk Index - 0x%x\n", PpFuseArray->VclkDclkSel[Index]);
  }
  IDS_HDT_CONSOLE (NB_MISC, "  GEN2 VID - 0x%x\n", PpFuseArray->PcieGen2Vid);
  IDS_HDT_CONSOLE (NB_MISC, "  Main PLL Id - 0x%x\n", PpFuseArray->MainPllId);
  IDS_HDT_CONSOLE (NB_MISC, "  GpuBoostCap  - %x\n", PpFuseArray->GpuBoostCap);
  IDS_HDT_CONSOLE (NB_MISC, "  SclkDpmBoostMargin    - %x\n", PpFuseArray->SclkDpmBoostMargin);
  IDS_HDT_CONSOLE (NB_MISC, "  SclkDpmThrottleMargin - %x\n", PpFuseArray->SclkDpmThrottleMargin);
  IDS_HDT_CONSOLE (NB_MISC, "  SclkDpmTdpLimitPG     - %x\n", PpFuseArray->SclkDpmTdpLimitPG);
  IDS_HDT_CONSOLE (
    NB_MISC, "  SclkThermDid          - %x(%dMHz)\n",
    PpFuseArray->SclkThermDid,
    (PpFuseArray->SclkThermDid != 0) ? (GfxLibCalculateClk (PpFuseArray->SclkThermDid, EffectiveMainPllFreq10KHz) / 100) : 0
    );
  IDS_HDT_CONSOLE (NB_MISC, "<------------ GNB FUSE END-------------->\n");
}
