/**
 * @file
 *
 *  PCIe silicon specific functions library.
 *
 *
 *
 * @xrefitem bom "File Content Label" "Release Content"
 * @e project:      CIMx-NB
 * @e sub-project:
 * @e \$Revision:$   @e \$Date:$
 *
 */
/*****************************************************************************
 *
 * 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.
 *
 *
 ***************************************************************************/
/*----------------------------------------------------------------------------------------
 *                             M O D U L E S    U S E D
 *----------------------------------------------------------------------------------------
 */

#include "NbPlatform.h"
#include "HotplugFirmware.h"
/*----------------------------------------------------------------------------------------
 *                   D E F I N I T I O N S    A N D    M A C R O S
 *----------------------------------------------------------------------------------------
 */

#define MCU_CLEAR_BLOCK_LENGTH  16

/*----------------------------------------------------------------------------------------
 *                  T Y P E D E F S     A N D     S T R U C T U  R E S
 *----------------------------------------------------------------------------------------
 */


/*----------------------------------------------------------------------------------------
 *           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
 *----------------------------------------------------------------------------------------
 */


/*----------------------------------------------------------------------------------------
 *                          E X P O R T E D    F U N C T I O N S
 *----------------------------------------------------------------------------------------
 */

INDIRECT_REG_ENTRY
STATIC
PcieMiscInitTable[] = {
  {
    NB_MISC_REG20,
    (UINT32)~BIT1,
    0x0
  }, //enable static device remapping by default
  {
    NB_MISC_REG22,
    0xffffffff,
    BIT27 | (0x8 << 12) | (0x8 << 16) | (0x8 << 20)
  },
  {
    NB_MISC_REG2B,
    0xffffffff,
    (0x8 << 12)
  },
  {
    NB_MISC_REG6C,
    0xffffffff,
    (0x8 << 16)
  },
  {
    NB_MISC_REG6B,
    0xffffffff,
    (UINT32) (0x1f << 27)
  }, //[13][12]Turn Off Offset Cancellation
  {
    NB_MISC_REG37,
    (UINT32)~(BIT11 + BIT12 + BIT13),
    0x0
  }, //[14][13]Disables Rx Clock gating in CDR
  {
    NB_MISC_REG67,
    (UINT32)~(BIT26 + BIT10 + BIT11),
    BIT11
  }, //[13]Disables Rx Clock gating in CDR
     //[16]Sets Electrical  Idle Threshold
  {
    NB_MISC_REG2C,
    (UINT32)~(BIT10),
    0x0
  }, //[13]Disables Rx Clock gating in CDR
  {
    NB_MISC_REG2A,
    (UINT32)~(BIT17 + BIT16),
    BIT17
  }, //[16]Sets Electrical l Idle Threshold
  {
    NB_MISC_REG32,
    (UINT32)~(0x3F << 20),
    (UINT32) (0x2A << 20)
  }  //[17][16]Sets Electrical Idle Threshold
};

/*----------------------------------------------------------------------------------------*/
/**
 * Misc Initialization prior port training.
 *
 *
 *
 * @param[in] pConfig   Northbridge configuration structure pointer.
 *
 */

VOID
PcieLibPreTrainingInit (
  IN      AMD_NB_CONFIG     *pConfig
  )
{
  UINT32      Value;
  PCIE_CONFIG *pPcieConfig;
  UINT32      ServerHotplugMask;
  BOOLEAN     SmuWa;
  CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieLibPreTrainingInit Enter\n"));
  pPcieConfig = GET_PCIE_CONFIG_PTR (pConfig);
  ServerHotplugMask = 0;
//Init Misc registers
  LibNbIndirectTableInit (
    pConfig->NbPciAddress.AddressValue | NB_MISC_INDEX,
    0,
    (INDIRECT_REG_ENTRY*)FIX_PTR_ADDR (&PcieMiscInitTable[0],NULL),
    (sizeof (PcieMiscInitTable) / sizeof (INDIRECT_REG_ENTRY)),
    pConfig
    );
//Setup peer-to-peer
  if (pPcieConfig->PcieConfiguration.Peer2Peer == ON) {
    if (pPcieConfig->CoreConfiguration[PcieLibGetCoreId (3, pConfig)] == GFX_CONFIG_AABB) {
      Value = 0x08080404;
    } else {
      Value = 0x08080008;
    }
    LibNbPciIndexRMW (pConfig->NbPciAddress.AddressValue | NB_MISC_INDEX, NB_MISC_REG49, AccessWidth32, 0, Value, pConfig);
    if (pPcieConfig->CoreConfiguration[PcieLibGetCoreId (12, pConfig)] == GFX_CONFIG_AABB) {
      Value = 0xFFFF0404;
    } else {
      Value = 0xFFFF0008;
    }
    LibNbPciIndexRMW (pConfig->NbPciAddress.AddressValue | NB_MISC_INDEX, NB_MISC_REG2F, AccessWidth32, 0, Value, pConfig);
    LibNbPciIndexRMW (pConfig->NbPciAddress.AddressValue | NB_MISC_INDEX, NB_MISC_REG48, AccessWidth32, (UINT32)~(BIT8), 0xffff0000, pConfig);
  }

 //Remap device number
#ifndef DEVICE_REMAP_DISABLE
  if (PciePortRemapInit (pConfig) != AGESA_SUCCESS ) {
    REPORT_EVENT (AGESA_ERROR, PCIE_ERROR_DEVICE_REMAP, 0, 0, 0, 0, pConfig);
  }
#endif

#ifndef HOTPLUG_SUPPORT_DISABLED
  ServerHotplugMask = PcieInitHotplug (pConfig);
#endif

  LibNbPciIndexRead (pConfig->NbPciAddress.AddressValue | NB_MISC_INDEX, NB_MISC_REG4A, AccessWidth32, &Value, pConfig);
  SmuWa = ((Value & BIT21) != 0) ? TRUE : FALSE;

  if (SmuWa || ServerHotplugMask != 0) {
    UINT32  BlockIndex;
    UINT32  SmuWaData;
    UINT16  Address;
    UINT32  Data[MCU_CLEAR_BLOCK_LENGTH];

    LibNbMcuControl (AssertReset, pConfig);
    // clear SMU RAM
    LibAmdMemFill (&Data[0], 0, sizeof (Data), (AMD_CONFIG_PARAMS *)&(pConfig->sHeader));
    for (Address = 0; Address < (16 * 1024); Address = Address + 4 * MCU_CLEAR_BLOCK_LENGTH) {
      LibNbLoadMcuFirmwareBlock (Address, MCU_CLEAR_BLOCK_LENGTH, &Data[0], pConfig);
    }
    //Load SMU firmware
    for (BlockIndex = 0; BlockIndex < Fm.NumberOfBlock; BlockIndex++) {
      LibNbLoadMcuFirmwareBlock (Fm.BlockArray[BlockIndex].Address, Fm.BlockArray[BlockIndex].Length, Fm.BlockArray[BlockIndex].Data, pConfig);
    }
    if (SmuWa) {
      SmuWaData = LibHtGetSmuWaData (pConfig);
      LibNbLoadMcuFirmwareBlock (0xFE70, 0x1, &SmuWaData, pConfig);
    }
    SmuWaData = ((SmuWa == TRUE) ? 0x100 : 0x100) | ((ServerHotplugMask != 0) ?  0x1 : 0);
    LibNbLoadMcuFirmwareBlock (0xFE74, 0x1, &SmuWaData, pConfig);

    LibNbMcuControl (DeAssertReset, pConfig);
  }

#ifndef HOTPLUG_SUPPORT_DISABLED
  PcieCheckHotplug (ServerHotplugMask, pConfig);
#endif

}

INDIRECT_REG_ENTRY  PcieCoreInitTable[] = {
  {
    NB_BIFNB_REG10,
    (UINT32)~(BIT10 + BIT11 + BIT12),
    BIT12
  },
  {
    NB_BIFNB_REG20,
    (UINT32)~(BIT8 + BIT9),
    BIT9
  },
  {
    NB_BIFNB_REG02,
    (UINT32)~(BIT0),
    BIT0
  },
  {
    NB_BIFNB_REG40,
    (UINT32)~(BIT14 + BIT15),
    BIT15
  },
  {
    NB_BIFNB_REGC2,
    (UINT32)~(BIT25),
    BIT25
  },
  {
    NB_BIFNB_REGC1,
    (UINT32)~(BIT0),
    (BIT0 + BIT1 + BIT2)
  },
  {
    NB_BIFNB_REG1C,
    0x0,
    (4 << 6) + (4 << 1) + 1
  }
};

INDIRECT_REG_ENTRY  PcieRd790CoreInitTable[] = {
  {
    NB_BIFNB_REGC2,
    (UINT32)~(BIT14),
    (BIT14)
  },
  {
    NB_BIFNB_REGC1,
    (UINT32)~(BIT2),
    0x0
  },
};

/*----------------------------------------------------------------------------------------*/
/**
 * Init Core registers
 *
 *
 *
 * @param[in] CoreId    PCI Express Core ID
 * @param[in] pConfig   Northbridge configuration structure pointer.
 */
VOID
PcieLibCommonCoreInit (
  IN       CORE              CoreId,
  IN       AMD_NB_CONFIG     *pConfig
  )
{
  UINT32  CoreAddress;
  NB_INFO NbInfo;

  CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieLibCommonCoreInit (CoreId = %d) Enter\n", CoreId));
  CoreAddress = PcieLibGetCoreAddress (CoreId, pConfig);
  NbInfo = LibNbGetRevisionInfo (pConfig);

  LibNbIndirectTableInit (
    pConfig->NbPciAddress.AddressValue | NB_BIF_INDEX,
    CoreAddress,
    (INDIRECT_REG_ENTRY*)FIX_PTR_ADDR (&PcieCoreInitTable[0],NULL),
    (sizeof (PcieCoreInitTable) / sizeof (INDIRECT_REG_ENTRY)),
    pConfig
    );

  if (CoreAddress == SB_CORE) {
    LibNbPciIndexRMW (pConfig->NbPciAddress.AddressValue | NB_BIF_INDEX, NB_BIFNB_REG10 | CoreAddress, AccessWidth32, (UINT32)~BIT9, BIT9, pConfig);
    LibNbPciIndexRMW (pConfig->NbPciAddress.AddressValue | NB_HTIU_INDEX, NB_HTIU_REG06, AccessWidth32, (UINT32)~BIT26, BIT26 + BIT1, pConfig);
    LibNbPciIndexRMW (pConfig->NbPciAddress.AddressValue | NB_BIF_INDEX, NB_BIFNB_REG1C | CoreAddress, AccessWidth32, (UINT32)~BIT0, 0x0, pConfig);
  }
  if ( NbInfo.Type < NB_SR5690 ) {
    LibNbIndirectTableInit (
      pConfig->NbPciAddress.AddressValue | NB_BIF_INDEX,
      CoreAddress,
      (INDIRECT_REG_ENTRY*)FIX_PTR_ADDR (&PcieRd790CoreInitTable[0], NULL),
      (sizeof (PcieRd790CoreInitTable) / sizeof (INDIRECT_REG_ENTRY)),
      pConfig
      );
  }
  CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieLibCommonCoreInit Exit\n"));
};


/*----------------------------------------------------------------------------------------*/
/**
 * Init Core after training is completed
 *
 *
 *
 * @param[in] CoreId    PCI Express Core ID
 * @param[in] pConfig   Northbridge configuration structure pointer.  *
 */


VOID
PcieLibCoreAfterTrainingInit (
  IN       CORE              CoreId,
  IN       AMD_NB_CONFIG     *pConfig
  )
{
  UINT32      CoreAddress;
  PCIE_CONFIG *pPcieConfig;
  PCI_ADDR    ClkPciAddress;

  pPcieConfig = GET_PCIE_CONFIG_PTR (pConfig);
  CoreAddress = PcieLibGetCoreAddress (CoreId, pConfig);
  ClkPciAddress = pConfig->NbPciAddress;
  ClkPciAddress.Address.Function = 1;
  LibNbPciIndexRMW (pConfig->NbPciAddress.AddressValue | NB_BIF_INDEX, NB_BIFNB_REG20 | CoreAddress, AccessWidth32, (UINT32)~(BIT9), 0, pConfig);
//Save core setting in scratch register
  LibNbPciIndexWrite (
    pConfig->NbPciAddress.AddressValue | NB_BIF_INDEX,
    NB_BIFNB_REG01 | CoreAddress,
    AccessWidth32,
    (UINT32*)&pPcieConfig->CoreSetting[CoreId],
    pConfig
    );
//Save general setting in scratch
  LibNbEnableClkConfig (pConfig);
  LibNbPciWrite (ClkPciAddress.AddressValue | NB_CLK_REG78, AccessWidth32, &pPcieConfig->PcieConfiguration, pConfig);
  LibNbDisableClkConfig (pConfig);

}


INDIRECT_REG_ENTRY  PciePortInitTable[] = {
  {
    NB_BIFNBP_REG02,
    (UINT32)~(BIT15),
    BIT15
  },
  {
    NB_BIFNBP_REGA1,
    (UINT32)~(BIT24 + BIT26),
    BIT11
  },
  {
    NB_BIFNBP_REGB1,
    0xffffffff,
    BIT28 + BIT23 + BIT19 + BIT20
  },
  {
    NB_BIFNBP_REGA4,
    (UINT32)~(BIT0),
    0x0
  },
  {
    NB_BIFNBP_REGA2,
    (UINT32)~(BIT13),
    BIT13
  },
  {
    NB_BIFNBP_REGA3,
    (UINT32)~(BIT9),
    BIT9
  },
  {
    NB_BIFNBP_REGA0,
    0xffff000f,
    0x6830
  },
  {
    NB_BIFNBP_REGC1,
    0xfffffff0,
    0xC
  },
  {
    NB_BIFNBP_REG70,
    (UINT32)~(BIT16 + BIT17 + BIT18),
    BIT16 + BIT18
  }
};
/*----------------------------------------------------------------------------------------*/
/**
 * Init port registers
 *
 *
 *
 * @param[in] PortId    PCI Express Port ID
 * @param[in] pConfig   Northbridge configuration structure pointer.
 *
 */
VOID
PcieLibCommonPortInit (
  IN       PORT              PortId,
  IN       AMD_NB_CONFIG     *pConfig
  )
{
  PCI_ADDR    Port;
  PCIE_CONFIG *pPcieConfig;
  UINT32      PcieSlotCapability;
  UINT32      CoreAddress;

  CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieLibCommonPortInit PortId %d Enter\n", PortId));
  Port = PcieLibGetPortPciAddress (PortId, pConfig);
  CoreAddress = PcieLibGetCoreAddress (PcieLibGetCoreId (PortId, pConfig), pConfig);
  pPcieConfig = GET_PCIE_CONFIG_PTR (pConfig);

  LibNbIndirectTableInit (
    Port.AddressValue | NB_BIF_INDEX,
    0x0,
    (INDIRECT_REG_ENTRY*)FIX_PTR_ADDR (&PciePortInitTable[0],NULL),
    (sizeof (PciePortInitTable) / sizeof (INDIRECT_REG_ENTRY)),
    pConfig
    );
  if (CoreAddress == GPP3a_CORE || CoreAddress == SB_CORE || CoreAddress == GPP3b_CORE) {
    LibNbPciIndexRMW (Port.AddressValue | NB_BIF_INDEX, NB_BIFNBP_REG70, AccessWidth32, (UINT32)~(BIT16 + BIT17 + BIT18), (BIT17 + BIT18), pConfig);
    if (CoreAddress == GPP3a_CORE) {
      LibNbPciIndexRMW (Port.AddressValue | NB_BIF_INDEX, NB_BIFNBP_REGB1, AccessWidth32, (UINT32)~(BIT22), BIT22, pConfig);
    }
  }
// Set completion timeout
  LibNbPciRMW (Port.AddressValue | NB_PCIP_REG80, AccessS3SaveWidth8, 0xF0, 0x6, pConfig);
  //if (CoreAddress != SB_CORE) {
  //  LibNbPciIndexRMW (Port.AddressValue | NB_BIF_INDEX, NB_BIFNBP_REG10, AccessWidth32, (UINT32)~BIT0, 0x0, pConfig);
  //}
//For hotplug ports
  //if (pPcieConfig->PortConfiguration[PortId].PortHotplug != OFF ||
  //    pPcieConfig->PcieConfiguration.DisableHideUnusedPorts == ON ||
  //    LibNbGetRevisionInfo (pConfig).Revision == NB_REV_A11) {
  LibNbPciIndexRMW (Port.AddressValue | NB_BIF_INDEX, NB_BIFNBP_REG20, AccessWidth32, (UINT32)~BIT19, 0x0, pConfig);
  //}

// Enable Immediate ACK
  if (pPcieConfig->ExtPortConfiguration[PortId].PortL1ImmediateACK == ON) {
    LibNbPciIndexRMW (Port.AddressValue | NB_BIF_INDEX, NB_BIFNBP_REGA0, AccessWidth32, (UINT32)~BIT23, BIT23, pConfig);
  }
//Set up slot capability
  PcieSlotCapability = (pPcieConfig->ExtPortConfiguration[PortId].PortPowerLimit << 7) |
                       ((Port.Address.Device | Port.Address.Bus << 5 ) << 19);
  LibNbPciRMW (Port.AddressValue | NB_PCIP_REG6C, AccessS3SaveWidth32, (UINT32)~((0x3ff << 7) | (0x1fff << 19)), PcieSlotCapability, pConfig);
  LibNbPciRMW (Port.AddressValue | NB_PCIP_REG5A, AccessS3SaveWidth16, (UINT32)~BIT8, BIT8, pConfig);
//Set interrupt pin info
  LibNbPciRMW (Port.AddressValue | NB_PCIP_REG3D, AccessS3SaveWidth8, 0x0, 0x1, pConfig);

  CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieLibCommonPortInit Exit\n"));
};