/**
 * @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 "amdDebugOutLib.h"

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



/*----------------------------------------------------------------------------------------*/
/**
 * Misc Initialization in late init
 *
 *
 *
 * @param[in] pConfig   Northbridge configuration structure pointer.
 *
 */

VOID
PcieLibLateInit (
  IN      AMD_NB_CONFIG     *pConfig
  )
{
  CORE  CoreId;
  PORT  PortId;
  PCIE_CONFIG *pPcieConfig;
  PCI_ADDR ClkPciAddress;

  pPcieConfig = GET_PCIE_CONFIG_PTR (pConfig);
  ClkPciAddress = pConfig->NbPciAddress;
  ClkPciAddress.Address.Function = 1;
  //Restore general setting in scratch
  LibNbEnableClkConfig (pConfig);
  LibNbPciRead (ClkPciAddress.AddressValue | NB_CLK_REG78, AccessWidth32, &pPcieConfig->PcieConfiguration, pConfig);
  LibNbDisableClkConfig (pConfig);
  // Restore Core setting from scratch
  for (CoreId = 0; CoreId <= MAX_CORE_ID; CoreId++) {
//    if (PcieLibIsCoreAccessible (CoreId, pConfig) && pPcieConfig->CoreSetting[CoreId].CoreDisabled != ON ) {
    UINT32  CoreAddress;
    CoreAddress = PcieLibGetCoreAddress (CoreId, pConfig);
    LibNbPciIndexRead (
      pConfig->NbPciAddress.AddressValue | NB_BIF_INDEX,
      NB_BIFNB_REG01 | CoreAddress,
      AccessWidth32,
      (UINT32*)&pPcieConfig->CoreSetting[CoreId],
      pConfig
      );
//  } else {
//    pPcieConfig->CoreSetting[CoreId].CoreDisabled = ON;
//  }
//    CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "   Recover Core Setting CoreId %d Setting %x Enter\n", CoreId, (UINT32)(pPcieConfig->CoreSetting[CoreId])));
  }
  // Restore port Setting from scratch
  for (PortId = MIN_PORT_ID; PortId <= MAX_PORT_ID; PortId++) {
    if (PcieLibIsValidPortId (PortId, pConfig)) {
      PCI_ADDR Port;
      Port = PcieLibGetPortPciAddress (PortId, pConfig);
      //Reload port configuration from scratch register
      LibNbPciIndexRead (
        Port.AddressValue | NB_BIF_INDEX,
        NB_BIFNBP_REG01,
        AccessWidth32,
        (UINT32*)&pPcieConfig->PortConfiguration[PortId],
        pConfig
        );
      LibNbPciRead (Port.AddressValue | NB_PCIP_REG108, AccessWidth32, (UINT32*)&pPcieConfig->ExtPortConfiguration[PortId], pConfig);
//      CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "   Recover Port setting PortId %d Setting %x Enter\n", PortId, (UINT32)(pPcieConfig->PortConfiguration[PortId])));
    } else {
      *((UINT32*)&pPcieConfig->ExtPortConfiguration[PortId]) = 0;
      *((UINT32*)&pPcieConfig->PortConfiguration[PortId]) = 0;
    }
  }
}

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

VOID
PcieLibValidatePortStateInit (
  IN      AMD_NB_CONFIG     *pConfig
  )
{
  CORE        CoreId;
  PORT        PortId;
  UINT32      PortAlwaysVisible;
  UINT32      ForcePortDisable;
  PCIE_CONFIG *pPcieConfig;
  PCI_ADDR    ClkPciAddress;

  pPcieConfig = GET_PCIE_CONFIG_PTR (pConfig);
  ClkPciAddress = pConfig->NbPciAddress;
  ClkPciAddress.Address.Function = 1;
  //Restore general setting in scratch
  LibNbEnableClkConfig (pConfig);
  LibNbPciRead (ClkPciAddress.AddressValue | NB_CLK_REG78, AccessWidth32, &pPcieConfig->PcieConfiguration, pConfig);
  LibNbDisableClkConfig (pConfig);
  // Restore Core setting from scratch
  for (CoreId = 0; CoreId <= MAX_CORE_ID; CoreId++) {
//    if (PcieLibIsCoreAccessible (CoreId, pConfig) && pPcieConfig->CoreSetting[CoreId].CoreDisabled != ON ) {
    UINT32  CoreAddress;
    CoreAddress = PcieLibGetCoreAddress (CoreId, pConfig);
    LibNbPciIndexRead (
      pConfig->NbPciAddress.AddressValue | NB_BIF_INDEX,
      NB_BIFNB_REG01 | CoreAddress,
      AccessWidth32,
      (UINT32*)&pPcieConfig->CoreSetting[CoreId],
      pConfig
      );
//  } else {
//    pPcieConfig->CoreSetting[CoreId].CoreDisabled = ON;
//  }
//    CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "   Recover Core Setting CoreId %d Setting %x Enter\n", CoreId, (UINT32)(pPcieConfig->CoreSetting[CoreId])));
  }
  // Restore port Setting from scratch
  for (PortId = MIN_PORT_ID; PortId <= MAX_PORT_ID; PortId++) {
    if (PcieLibIsValidPortId (PortId, pConfig)) {
      PCI_ADDR Port;
      Port = PcieLibGetPortPciAddress (PortId, pConfig);
      PortAlwaysVisible = pPcieConfig->PortConfiguration[PortId].PortAlwaysVisible;
      ForcePortDisable = pPcieConfig->PortConfiguration[PortId].ForcePortDisable;
      //Reload port configuration from scratch register
      LibNbPciIndexRead (
        Port.AddressValue | NB_BIF_INDEX,
        NB_BIFNBP_REG01,
        AccessWidth32,
        (UINT32*)&pPcieConfig->PortConfiguration[PortId],
        pConfig
        );
      pPcieConfig->PortConfiguration[PortId].PortAlwaysVisible = PortAlwaysVisible;
      pPcieConfig->PortConfiguration[PortId].ForcePortDisable = ForcePortDisable;
      LibNbPciRead (Port.AddressValue | NB_PCIP_REG108, AccessWidth32, (UINT32*)&pPcieConfig->ExtPortConfiguration[PortId], pConfig);
//      CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "   Recover Port setting PortId %d Setting %x Enter\n", PortId, (UINT32)(pPcieConfig->PortConfiguration[PortId])));
    } else {
      *((UINT32*)&pPcieConfig->ExtPortConfiguration[PortId]) = 0;
      *((UINT32*)&pPcieConfig->PortConfiguration[PortId]) = 0;
    }
  }
}

/*----------------------------------------------------------------------------------------*/
/**
 * Enable LCLK clock gating or shutdown LCLK clock banch if possible
 *
 *
 *
 * @param[in] CoreId    PCI Express Core ID
 * @param[in] pConfig   Northbridge configuration structure pointer.
 *
 */
VOID
PcieLibManageLclkClock (
  IN      CORE            CoreId,
  IN      AMD_NB_CONFIG   *pConfig
  )
{
  UINT32      Value;
  UINT32      Mask;
  PCI_ADDR    ClkPciAddress;
  UINT32      CoreAddress;
  PCIE_CONFIG *pPcieConfig;

  CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieLibManageLclkClock [CoreId %d] Enter \n", CoreId));
  pPcieConfig = GET_PCIE_CONFIG_PTR (pConfig);
  CoreAddress = PcieLibGetCoreAddress (CoreId, pConfig);
  ClkPciAddress = pConfig->NbPciAddress;
  ClkPciAddress.Address.Function = 1;
  LibNbEnableClkConfig (pConfig);

  if (pPcieConfig->CoreSetting[CoreId].LclkClockGating == ON) {
    ClkPciAddress.Address.Register = NB_CLK_REGE8;
    Value = 0;
    Mask = 0;
    switch (CoreAddress) {
    case  GPP1_CORE:
      ClkPciAddress.Address.Register = NB_CLK_REG94;
      Mask = BIT16;
      break;
    case  GPP2_CORE:
      Value = BIT28;
      break;
    case  GPP3a_CORE:
      Value = BIT31;
      break;
    case  GPP3b_CORE:
      Value = BIT25;
      break;
    case  SB_CORE:
      ClkPciAddress.Address.Register = NB_CLK_REG94;
      Mask = BIT24;
      break;
    default:
      CIMX_ASSERT (FALSE);
    }
    LibNbPciRMW (ClkPciAddress.AddressValue, AccessS3SaveWidth32, ~Mask, Value, pConfig);
  }
  if (pPcieConfig->CoreSetting[CoreId].LclkClockOff == ON) {
    UINT8   ActiveCoreMap;
    ActiveCoreMap = PcieLibGetActiveCoreMap (pConfig);
    if ((ActiveCoreMap & (1 << CoreId)) == 0) {
      //Core not active we can shutdown LCLK permanantly
      CORE_INFO *pCoreInfo;
      CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "    Shutdown LCKL clock\n"));
      pCoreInfo = PcieLibGetCoreInfo (CoreId, pConfig);
      ClkPciAddress.Address.Register = NB_CLK_REGE0;
      pPcieConfig->CoreSetting[CoreId].CoreDisableStatus = ON;
      // We have to setup Index for BIFNB to point out to SB core. After this point core registers no longer accesasable
      LibNbPciIndexRMW (pConfig->NbPciAddress.AddressValue | NB_BIF_INDEX, 0x00 | SB_CORE, AccessS3SaveWidth32, 0xffffffff, 0x00, pConfig);
      LibNbPciRMW (ClkPciAddress.AddressValue, AccessS3SaveWidth32, 0xffffffff, 1 << pCoreInfo->LclkOffOffset, pConfig);

      Value = 0;
      if (CoreAddress == GPP1_CORE) {
        if ((ActiveCoreMap & 0xb) == 0 && !LibNbIsIommuEnabled (pConfig)) {
        // Can shutdown master core
          Value = 1 << pCoreInfo->LclkPermOffOffset;
        }
      } else {
        Value = 1 << pCoreInfo->LclkPermOffOffset;
      }
      if (Value != 0) {
        NbIommuDisconnectPcieCore (CoreId, pConfig);
        LibNbPciIndexRMW (pConfig->NbPciAddress.AddressValue | NB_MISC_INDEX, NB_MISC_REG27, AccessS3SaveWidth32, 0xffffffff, Value, pConfig);

      }
    }
  }
  LibNbDisableClkConfig (pConfig);
}

/*----------------------------------------------------------------------------------------*/
/**
 * Power Off Pll for unused lanes.
 *
 *
 *
 * @param[in] CoreId    PCI Express Core ID
 * @param[in] pConfig   Northbridge configuration structure pointer.
 */
VOID
PcieLibPowerOffPll (
  IN      CORE            CoreId,
  IN      AMD_NB_CONFIG   *pConfig
  )
{
  PCIE_CONFIG *pPcieConfig;
  UINT32      CoreAddress;
  UINT32      PowerOfPllValue;
  UINT32      PadsMap;
  //UINT32      TxClockOffValue;
  UINT32      PowerOfPllRegister;

  pPcieConfig = GET_PCIE_CONFIG_PTR (pConfig);
  CoreAddress = PcieLibGetCoreAddress (CoreId, pConfig);
  PowerOfPllValue = 0;
  PadsMap = 0;
  //TxClockOffValue = 0;
  PowerOfPllRegister = NB_MISC_REG23;

  LibNbPciIndexRead (pConfig->NbPciAddress.AddressValue | NB_BIF_INDEX, NB_BIFNB_REG65 | CoreAddress, AccessS3SaveWidth32, &PadsMap, pConfig);
  if (CoreAddress == GPP1_CORE || CoreAddress == GPP2_CORE) {
    if ((PadsMap & 0xf0) == 0xf0) {
      //Power Off PLL1
      PowerOfPllValue |= (BIT1 | BIT3);
      if ((PadsMap & 0x0f) == 0x0f && pPcieConfig->CoreConfiguration[CoreId] != GFX_CONFIG_AABB) {
        //Power Off PLL0
        PowerOfPllValue |= (BIT0 | BIT2);
      }
    }
    if (CoreAddress == GPP2_CORE) {
      PowerOfPllValue <<= 8;
      //TxClockOffValue = BIT1;
    } else {
      //TxClockOffValue = BIT0;
    }
    if ((UINT16)PadsMap != 0xffff) {
      //TxClockOffValue = 0;          //Do not disable TX clock in case any line is ON
    }
  }
  if (CoreAddress == GPP3a_CORE ) {
    if ((UINT16)PadsMap == 0x3F3F) {
      PowerOfPllValue = BIT18 | BIT16;
      //TxClockOffValue = BIT2;
    }
  }
  if (CoreAddress == GPP3b_CORE ) {
    PowerOfPllRegister = NB_MISC_REG2E;
    if ((UINT16)PadsMap == 0x0F0F) {
      PowerOfPllValue = BIT8 | BIT6;
      //TxClockOffValue = BIT3;
    }
  }
  //Power Off Pll
  LibNbPciIndexRMW (pConfig->NbPciAddress.AddressValue | NB_MISC_INDEX, PowerOfPllRegister , AccessS3SaveWidth32, 0xffffffff, PowerOfPllValue, pConfig);
  CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "    Power off PLL CoreId %d, Value 0x%x\n", CoreId, PowerOfPllValue));
  //Turn off TXCLK
  //LibNbPciIndexRMW (pConfig->NbPciAddress.AddressValue | NB_MISC_INDEX, NB_MISC_REG07, AccessS3SaveWidth32, 0xffffffff, TxClockOffValue, pConfig);
}


/*----------------------------------------------------------------------------------------*/
/**
 * Enable TX clock gating or shutdown TX clock if possible
 *
 *
 *
 * @param[in] CoreId    PCI Express Core ID
 * @param[in] pConfig   Northbridge configuration structure pointer.  *
 */
VOID
PcieLibManageTxClock (
  IN      CORE            CoreId,
  IN      AMD_NB_CONFIG   *pConfig
  )
{
  PCIE_CONFIG *pPcieConfig;
  UINT32      CoreAddress;
  UINT32      Value;
  pPcieConfig = GET_PCIE_CONFIG_PTR (pConfig);
  CoreAddress = PcieLibGetCoreAddress (CoreId, pConfig);
  if (pPcieConfig->CoreSetting[CoreId].TxClockGating == ON) {
    switch (CoreAddress) {
    case  GPP1_CORE:
      Value = BIT4;
      break;
    case  GPP2_CORE:
      Value = BIT5;
      break;
    case  GPP3a_CORE:
      Value = BIT6;
      break;
    case  GPP3b_CORE:
      Value = BIT24;
      break;
    case  SB_CORE:
      Value = BIT7;
      break;
    default:
      Value = 0;
      CIMX_ASSERT (FALSE);
    }
    LibNbPciIndexRMW (pConfig->NbPciAddress.AddressValue | NB_MISC_INDEX, NB_MISC_REG07, AccessS3SaveWidth32, 0xffffffff, Value, pConfig);
    LibNbPciIndexRMW (pConfig->NbPciAddress.AddressValue | NB_BIF_INDEX, NB_BIFNB_REG40 | CoreAddress, AccessS3SaveWidth32, (UINT32)~BIT6, BIT6, pConfig);
    LibNbPciIndexRMW (pConfig->NbPciAddress.AddressValue | NB_BIF_INDEX, NB_BIFNB_REG11 | CoreAddress, AccessS3SaveWidth32, 0xfffffff0, 0x0C, pConfig);
  }
  if (pPcieConfig->CoreSetting[CoreId].TxClockOff == ON) {
    UINT8   ActiveCoreMap;
    ActiveCoreMap = PcieLibGetActiveCoreMap (pConfig);
    if ((ActiveCoreMap & (1 << CoreId)) == 0) {
      //Core not active we can shutdown TX clk permanantly
      CORE_INFO *pCoreInfo;
      CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "    Shutdown TX clock\n"));
      pPcieConfig->CoreSetting[CoreId].CoreDisableStatus = ON;
      pCoreInfo = PcieLibGetCoreInfo (CoreId, pConfig);
      LibNbPciIndexRMW (pConfig->NbPciAddress.AddressValue | NB_MISC_INDEX, NB_MISC_REG07, AccessS3SaveWidth32, 0xffffffff, 1 << pCoreInfo->TxOffOffset, pConfig);
    }
  }
}

/*----------------------------------------------------------------------------------------*/
/**
 * Enable Pll Power Down in L1.
 *
 *
 *
 * @param[in] CoreId    PCI Express Core ID
 * @param[in] pConfig   Northbridge configuration structure pointer.  *
 */
VOID
PcieLibEnablePllPowerOffInL1 (
  IN      CORE            CoreId,
  IN      AMD_NB_CONFIG   *pConfig
  )
{
  PCIE_CONFIG *pPcieConfig;
  UINT32      Value;
  UINT32      CoreAddress;
  PORT        PortId;
  pPcieConfig = GET_PCIE_CONFIG_PTR (pConfig);
  if (pPcieConfig->CoreSetting[CoreId].DetectPowerOffPllInL1 == ON && !PciePllOffComatibilityTest (CoreId, pConfig)) {
    return;
  }
  for (PortId = MIN_PORT_ID; PortId <= MAX_PORT_ID; PortId++) {
    if (PcieLibIsValidPortId (PortId, pConfig) && PcieLibGetCoreId (PortId, pConfig) == CoreId) {
      if (pPcieConfig->PortConfiguration[PortId].PortHotplug != OFF) {
        // set up max exit latency requirment for hotplug ports
        PCI_ADDR  Port;
        Port = PcieLibGetPortPciAddress (PortId, pConfig);
        LibNbPciIndexRMW (Port.AddressValue | NB_BIF_INDEX, NB_BIFNBP_REGC1 , AccessS3SaveWidth32, 0xffffffff, 0xf, pConfig);
      }
    }
  }
  CoreAddress = PcieLibGetCoreAddress (CoreId, pConfig);
  Value = BIT8;
  LibNbPciIndexRMW (pConfig->NbPciAddress.AddressValue | NB_BIF_INDEX, NB_BIFNB_REG40 | CoreAddress, AccessS3SaveWidth32, (UINT32)~(BIT9 + BIT4), BIT3 + BIT0 + BIT12, pConfig);
  if (CoreAddress == GPP3b_CORE || CoreAddress == GPP3a_CORE || CoreAddress == SB_CORE) {
    Value |= BIT3;
  }
  LibNbPciIndexRMW (pConfig->NbPciAddress.AddressValue | NB_BIF_INDEX, NB_BIFNB_REG02 | CoreAddress, AccessS3SaveWidth32, 0xffffffff, Value, pConfig);
}

/*----------------------------------------------------------------------------------------*/
/**
 * Misc. core setting.
 *
 *
 *
 * @param[in] CoreId    PCI Express- Core ID
 * @param[in] pConfig   Northbridge configuration structure pointer.
 */
VOID
PcieLibMiscLateCoreSetting (
  IN       CORE  CoreId,
  IN       AMD_NB_CONFIG   *pConfig
  )
{
//Lock
  LibNbPciIndexRMW (
    pConfig->NbPciAddress.AddressValue | NB_BIF_INDEX,
    NB_BIFNB_REG10 | PcieLibGetCoreAddress (CoreId, pConfig),
    AccessS3SaveWidth32,
    0xffffffff,
    BIT0,
    pConfig
    );
}