/** @file
  This file contains PSF routines for RC usage

Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/
#include <Base.h>
#include <Uefi/UefiBaseType.h>
#include <Library/IoLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <PchAccess.h>
#include <Library/PchPcrLib.h>
#include <Library/PchInfoLib.h>
#include <Library/PchPsfPrivateLib.h>
#include <Library/PchPciExpressHelpersLib.h>
#include <Library/S3BootScriptLib.h>

/**
  This procedure will disable CIO2 device at PSF level

  @param[in] None

  @retval None
**/
VOID
PsfDisableCio2Device (
  VOID
  )
{
  PchPcrAndThenOr32 (
    PID_PSF1,
    R_PCH_LP_PCR_PSF1_T0_SHDW_CAM_REG_BASE + R_PCH_PCR_PSFX_T0_SHDW_PCIEN,
    0xFFFFFFFF,
    B_PCH_PCR_PSFX_T0_SHDW_PCIEN_FUNDIS
    );
}

/**
  Hide CIO2 devices PciCfgSpace at PSF level

  @param[in] None

  @retval None
**/
VOID
PsfHideCio2Device (
  VOID
  )
{
  PchPcrAndThenOr32 (
    PID_PSF1,
    R_PCH_LP_PCR_PSF1_T0_SHDW_CAM_REG_BASE + R_PCH_PCR_PSFX_T0_SHDW_CFG_DIS,
    (UINT32) ~0,
    B_PCH_PCR_PSFX_T0_SHDW_CFG_DIS_CFGDIS
    );
}

/**
  Disable HDAudio device at PSF level

  @param[in] None

  @retval None
**/
VOID
PsfDisableHdaDevice (
  VOID
  )
{
  PchPcrAndThenOr32 (
    PID_PSF3,
    R_PCH_PCR_PSF3_T0_SHDW_AUD_REG_BASE + R_PCH_PCR_PSFX_T0_SHDW_PCIEN,
    (UINT32) ~0,
    B_PCH_PCR_PSFX_T0_SHDW_PCIEN_FUNDIS
    );
}

/**
  Disable xDCI device at PSF level

  @param[in] None

  @retval None
**/
VOID
PsfDisableXdciDevice (
  VOID
  )
{
  UINT16 RegOffset;

  if (GetPchSeries () == PchLp) {
    RegOffset = R_PCH_LP_PCR_PSF2_T0_SHDW_OTG_REG_BASE + R_PCH_PCR_PSFX_T0_SHDW_PCIEN;
  } else {
    RegOffset = R_PCH_H_PCR_PSF2_T0_SHDW_OTG_REG_BASE + R_PCH_PCR_PSFX_T0_SHDW_PCIEN;
  }

  PchPcrAndThenOr32 (
    PID_PSF2,
    RegOffset,
    ~0u,
    B_PCH_PCR_PSFX_T0_SHDW_PCIEN_FUNDIS
    );
}

/**
  Disable xHCI device at PSF level

  @param[in] None

  @retval None
**/
VOID
PsfDisableXhciDevice (
  VOID
  )
{
  UINT16 RegOffset;

  if (GetPchSeries () == PchLp) {
    RegOffset = R_PCH_LP_PCR_PSF2_T0_SHDW_XHCI_REG_BASE + R_PCH_PCR_PSFX_T0_SHDW_PCIEN;
  } else {
    RegOffset = R_PCH_H_PCR_PSF2_T0_SHDW_XHCI_REG_BASE + R_PCH_PCR_PSFX_T0_SHDW_PCIEN;
  }

  PchPcrAndThenOr32 (
    PID_PSF2,
    RegOffset,
    ~0u,
    B_PCH_PCR_PSFX_T0_SHDW_PCIEN_FUNDIS
    );
}

/**
  Disable SATA device at PSF level

  @param[in] None

  @retval None
**/
VOID
PsfDisableSataDevice (
  VOID
  )
{
  UINT16 RegOffset;

  if (GetPchSeries () == PchLp) {
    RegOffset = R_PCH_LP_PCR_PSF1_T0_SHDW_SATA_REG_BASE + R_PCH_PCR_PSFX_T0_SHDW_PCIEN;
  } else {
    RegOffset = R_PCH_H_PCR_PSF1_T0_SHDW_SATA_REG_BASE + R_PCH_PCR_PSFX_T0_SHDW_PCIEN;
  }

  PchPcrAndThenOr32 (
    PID_PSF1,
    RegOffset,
    ~0u,
    B_PCH_PCR_PSFX_T0_SHDW_PCIEN_FUNDIS
    );
}

/**
  Disable ISH device at PSF level

  @param[in] None

  @retval None
**/
VOID
PsfDisableIshDevice (
  VOID
  )
{
  PchPcrAndThenOr32 (
    PID_PSF3,
    R_PCH_PCR_PSF3_T0_SHDW_ISH_REG_BASE + R_PCH_PCR_PSFX_T0_SHDW_PCIEN,
    ~0u,
    B_PCH_PCR_PSFX_T0_SHDW_PCIEN_FUNDIS
    );
}

/**
  Disable ISH BAR1 at PSF level

  @param[in] None

  @retval None
**/
VOID
PsfDisableIshBar1 (
  VOID
  )
{
  //
  // Set AGNT_T0_SHDW_PCIEN[19:18] = 11b
  // ISH device BAR is 64bit wide so to disable BAR1
  // at PSF both BAR2 (32bit wide) and BAR3 (32bit wide) need to be disabled
  //
  PchPcrAndThenOr32 (
    PID_PSF3,
    R_PCH_PCR_PSF3_T0_SHDW_ISH_REG_BASE + R_PCH_PCR_PSFX_T0_SHDW_PCIEN,
    (UINT32) ~0,
    (B_PCH_PCR_PSFX_T0_SHDW_PCIEN_BAR2DIS | B_PCH_PCR_PSFX_T0_SHDW_PCIEN_BAR3DIS)
    );
}

/**
  Disable GbE device at PSF level

  @param[in] None

  @retval None
**/
VOID
PsfDisableGbeDevice (
  VOID
  )
{
  UINT16       RegOffset;
  PCH_SBI_PID  PortId;

  if (GetPchSeries () == PchLp) {
    PortId = PID_PSF1;
    RegOffset = R_PCH_LP_PCR_PSF1_T0_SHDW_GBE_REG_BASE + R_PCH_PCR_PSFX_T0_SHDW_PCIEN;
  } else {
    PortId = PID_PSF3;
    RegOffset = R_PCH_H_PCR_PSF3_T0_SHDW_GBE_REG_BASE + R_PCH_PCR_PSFX_T0_SHDW_PCIEN;
  }

  PchPcrAndThenOr32 (
    PortId,
    RegOffset,
    ~0u,
    B_PCH_PCR_PSFX_T0_SHDW_PCIEN_FUNDIS
    );
}

/**
  Disable SMBUS device at PSF level

  @param[in] None

  @retval None
**/
VOID
PsfDisableSmbusDevice (
  VOID
  )
{
  ///
  /// Hide SMBUS device
  /// Set PCR[PSF_3] + "AGNT_T0_SHDW_CFG_DIS"[SMBUS][0]
  ///
  PchPcrAndThenOr32 (
    PID_PSF3,
    R_PCH_PCR_PSF3_T0_SHDW_SMBUS_REG_BASE + R_PCH_PCR_PSFX_T0_SHDW_CFG_DIS,
    (UINT32) ~0,
    B_PCH_PCR_PSFX_T0_SHDW_CFG_DIS_CFGDIS
    );

  ///
  /// Function disable SMBUS device
  /// Set PCR[PSF_3] + "AGNT_T0_SHDW_PCIEN"[SMBUS][8]
  ///
  PchPcrAndThenOr32 (
    PID_PSF3,
    R_PCH_PCR_PSF3_T0_SHDW_SMBUS_REG_BASE + R_PCH_PCR_PSFX_T0_SHDW_PCIEN,
    (UINT32) ~0,
    B_PCH_PCR_PSFX_T0_SHDW_PCIEN_FUNDIS
    );
}

/**
  Disable Thermal device at PSF level

  @param[in] None

  @retval None
**/
VOID
PsfDisableThermalDevice (
  VOID
  )
{
  ///
  /// Set PCR[PSF2] TRH PCIEN[8] to 1
  ///
  PchPcrAndThenOr32 (
    PID_PSF2, R_PCH_PCR_PSF2_T0_SHDW_TRH_REG_BASE + R_PCH_PCR_PSFX_T0_SHDW_PCIEN,
    (UINT32) ~0,
    B_PCH_PCR_PSFX_T0_SHDW_PCIEN_FUNDIS
    );
}

/**
  Hide Thermal device PciCfgSpace at PSF level

  @param[in] None

  @retval None
**/
VOID
PsfHideThermalDevice (
  VOID
  )
{
  PchPcrAndThenOr32 (
    PID_PSF2, R_PCH_PCR_PSF2_T0_SHDW_TRH_REG_BASE + R_PCH_PCR_PSFX_T0_SHDW_CFG_DIS,
    (UINT32) ~0,
    B_PCH_PCR_PSFX_T0_SHDW_CFG_DIS_CFGDIS
    );
}

GLOBAL_REMOVE_IF_UNREFERENCED UINT16 mPchLpScsDevicePsfRegBase[] =
{
  R_PCH_LP_PCR_PSF2_T0_SHDW_EMMC_REG_BASE,
  R_PCH_LP_PCR_PSF2_T0_SHDW_SDIO_REG_BASE,
  R_PCH_LP_PCR_PSF2_T0_SHDW_SDCARD_REG_BASE,
  R_PCH_LP_PCR_PSF2_T0_SHDW_UFS_REG_BASE
};

/**
  Disable SCS device at PSF level

  @param[in] ScsDevNum   SCS Device

  @retval None
**/
VOID
PsfDisableScsDevice (
  IN PCH_PSF_SCS_DEV_NUM  ScsDevNum
  )
{
  if (ScsDevNum >= PchPsfMaxScsDevNum) {
    ASSERT (FALSE);
  }

  PchPcrAndThenOr32 (
    PID_PSF2,
    mPchLpScsDevicePsfRegBase[ScsDevNum] + R_PCH_PCR_PSFX_T0_SHDW_PCIEN,
    (UINT32) ~0,
    B_PCH_PCR_PSFX_T0_SHDW_PCIEN_FUNDIS
    );
}

/**
  Disable SCS devices BAR1 PSF level

  @param[in] ScsDevNum   SCS Device

  @retval None
**/
VOID
PsfDisableScsBar1 (
  IN PCH_PSF_SCS_DEV_NUM  ScsDevNum
  )
{
  if (ScsDevNum >= PchPsfMaxScsDevNum) {
    ASSERT (FALSE);
  }

  //
  // Set AGNT_T0_SHDW_PCIEN[19:18] = 11b
  // SCS device BAR is 64bit wide so to disable BAR1
  // at PSF both BAR2 (32bit wide) and BAR3 (32bit wide) need to be disabled
  //
  PchPcrAndThenOr32 (
    PID_PSF2,
    mPchLpScsDevicePsfRegBase[ScsDevNum] + R_PCH_PCR_PSFX_T0_SHDW_PCIEN,
    (UINT32) ~0,
    (B_PCH_PCR_PSFX_T0_SHDW_PCIEN_BAR2DIS | B_PCH_PCR_PSFX_T0_SHDW_PCIEN_BAR3DIS)
    );
}

/**
  Hide TraceHub ACPI devices PciCfgSpace at PSF level

  @param[in] None

  @retval None
**/
VOID
PsfHideTraceHubAcpiDevice (
  VOID
  )
{
  PchPcrAndThenOr32 (
    PID_PSF3,
    R_PCH_PCR_PSF3_T0_SHDW_TRACE_HUB_ACPI_REG_BASE + R_PCH_PCR_PSFX_T0_SHDW_CFG_DIS,
    (UINT32) ~0,
    B_PCH_PCR_PSFX_T0_SHDW_CFG_DIS_CFGDIS
    );
}

/**
  This procedure will hide TraceHub PciCfgSpace at PSF level

  @retval None
**/
VOID
PsfHideTraceHubDevice (
  VOID
  )
{
  PchPcrAndThenOr32 (
    PID_PSF3,
    R_PCH_PCR_PSF3_T0_SHDW_TRACE_HUB_REG_BASE + R_PCH_PCR_PSFX_T0_SHDW_CFG_DIS,
    ~0u,
    B_PCH_PCR_PSFX_T0_SHDW_CFG_DIS_CFGDIS
    );
}

/**
  This procedure will reveal TraceHub PciCfgSpace at PSF level

  @retval None
**/
VOID
PsfRevealTraceHubDevice (
  VOID
  )
{
  PchPcrAndThenOr32 (
    PID_PSF3,
    R_PCH_PCR_PSF3_T0_SHDW_TRACE_HUB_REG_BASE + R_PCH_PCR_PSFX_T0_SHDW_CFG_DIS,
    (UINT32) ~(B_PCH_PCR_PSFX_T0_SHDW_CFG_DIS_CFGDIS),
    0
    );
}

/**
  This procedure will disable and hide TraceHub device at PSF level

  @param[in] None

  @retval None
**/
VOID
PsfDisableTraceHubDevice (
  VOID
  )
{
  PsfHideTraceHubDevice ();

  PchPcrAndThenOr32 (
    PID_PSF3,
    R_PCH_PCR_PSF3_T0_SHDW_TRACE_HUB_REG_BASE + R_PCH_PCR_PSFX_T0_SHDW_PCIEN,
    0xFFFFFFFF,
    B_PCH_PCR_PSFX_T0_SHDW_PCIEN_FUNDIS
    );
}

/**
  Calculates offset of PCR register where given rootport can be enabled/disabled

  @param[in] RpIndex        PCIe Root Port Index (0 based)

  @retval Offset
**/
static UINT16
GetPsfPcieRootPortOffset (
  IN UINT32 RpIndex
  )
{
  UINT16          RootPortReg;

  if (GetPchSeries () == PchLp) {
    RootPortReg = R_PCH_LP_PCR_PSF1_T1_SHDW_PCIE01_REG_BASE;
  } else {
    if (GetPchGeneration () == KblPch) {
      RootPortReg = R_KBL_PCH_H_PCR_PSF1_T1_SHDW_PCIE01_REG_BASE;
    } else {
      RootPortReg = R_SKL_PCH_H_PCR_PSF1_T1_SHDW_PCIE01_REG_BASE;
    }
  }
  return (RootPortReg - ((UINT16)RpIndex * 0x100) + R_PCH_PCR_PSFX_T1_SHDW_PCIEN);
}

/**
  Re-enable PCIe Root Port at PSF level after it was disabled

  @param[in] RpIndex        PCIe Root Port Index (0 based)

  @retval None
**/
VOID
PsfEnablePcieRootPort (
  IN UINT32  RpIndex
  )
{
  ASSERT (RpIndex < GetPchMaxPciePortNum ());

  PchPcrAndThenOr32 (PID_PSF1, GetPsfPcieRootPortOffset (RpIndex), (UINT32)(~B_PCH_PCR_PSFX_T1_SHDW_PCIEN_FUNDIS), 0);
}

/**
  Disable PCIe Root Port at PSF level

  @param[in] RpIndex        PCIe Root Port Index (0 based)

  @retval None
**/
VOID
PsfDisablePcieRootPort (
  IN UINT32  RpIndex
  )
{
  ASSERT (RpIndex < GetPchMaxPciePortNum ());

  PchPcrAndThenOr32 (PID_PSF1, GetPsfPcieRootPortOffset (RpIndex), ~0u, B_PCH_PCR_PSFX_T1_SHDW_PCIEN_FUNDIS);
}

/**
  Disable PCIe Root Port at PSF level.
  This function will also perform S3 boot script programming

  @param[in] RpIndex        PCIe Root Port Index (0 based)

  @retval None
**/
VOID
PsfDisablePcieRootPortWithS3BootScript (
  IN UINT32  RpIndex
  )
{
  UINT16          RootPortReg;
  UINT32          Data32And;
  UINT32          Data32Or;

  if (GetPchSeries () == PchLp) {
    if (RpIndex >= PCH_LP_PCIE_MAX_ROOT_PORTS) {
      ASSERT (FALSE);
      return;
    }
    RootPortReg = R_PCH_LP_PCR_PSF1_T1_SHDW_PCIE01_REG_BASE;
  } else {
    if (GetPchGeneration () == KblPch) {
      if (RpIndex >= KBL_PCH_H_PCIE_MAX_ROOT_PORTS) {
        ASSERT (FALSE);
        return;
      }
      RootPortReg = R_KBL_PCH_H_PCR_PSF1_T1_SHDW_PCIE01_REG_BASE;
    } else {
      if (RpIndex >= SKL_PCH_H_PCIE_MAX_ROOT_PORTS) {
        ASSERT (FALSE);
        return;
      }
      RootPortReg = R_SKL_PCH_H_PCR_PSF1_T1_SHDW_PCIE01_REG_BASE;
    }
  }

  Data32And = ~0u;
  Data32Or  = B_PCH_PCR_PSFX_T1_SHDW_PCIEN_FUNDIS;

  PchPcrAndThenOr32 (
    PID_PSF1,
    RootPortReg - ((UINT16)RpIndex * 0x100) + R_PCH_PCR_PSFX_T1_SHDW_PCIEN,
    Data32And,
    Data32Or
    );

  PCH_PCR_BOOT_SCRIPT_READ_WRITE (
    S3BootScriptWidthUint32,
    PID_PSF1, (RootPortReg - ((UINT16)RpIndex * 0x100) + R_PCH_PCR_PSFX_T1_SHDW_PCIEN),
    &Data32Or,
    &Data32And
    );
}

/**
  Program PSF grant counts for PCI express depending on controllers configuration

  @param[in] Controller        PCIe controller index
  @param[in] ControllerConfig  Port configuration of controller

  @retval Status
**/
EFI_STATUS
PsfConfigurePcieGrantCounts (
  UINT8       Controller,
  UINT8       ControllerConfig
  )
{
  static CONST UINT8 DgcrPairsLp [PCH_LP_PCIE_MAX_CONTROLLERS][PCH_PCIE_CONTROLLER_PORTS][2] = {
    { {  5,  6 }, {  8,  9 }, { 11, 12 }, { 14, 15 } }, //SPA
    { { 19, 20 }, { 22, 23 }, { 25, 26 }, { 28, 29 } }, //SPB
    { { 33, 34 }, { 36, 37 }, { 39, 40 }, { 42, 43 } }, //SPC
  };

  static CONST UINT8 PgTgtLp [PCH_LP_PCIE_MAX_CONTROLLERS][PCH_PCIE_CONTROLLER_PORTS] = {
    {  4,  5,  6,  7 }, //SPA
    { 12, 13, 14, 15 }, //SPB
    { 20, 21, 22, 23 }  //SPC
  };

  static CONST UINT8 DgcrPairsH [KBL_PCH_H_PCIE_MAX_CONTROLLERS][PCH_PCIE_CONTROLLER_PORTS][2] = {
    { { 11, 12 }, { 14, 15 }, { 17, 18 }, { 20, 21 } }, //SPA
    { { 24, 25 }, { 27, 28 }, { 30, 31 }, { 33, 34 } }, //SPB
    { { 37, 38 }, { 40, 41 }, { 43, 44 }, { 46, 47 } }, //SPC
    { { 50, 51 }, { 53, 54 }, { 56, 57 }, { 59, 60 } }, //SPD
    { { 63, 64 }, { 66, 67 }, { 69, 70 }, { 72, 73 } }, //SPE
    { {182,183 }, {185,186 }, {188,189 }, {191,192 } }  //SPF, only for KBL-H
  };

  static CONST UINT8 PgTgtH [KBL_PCH_H_PCIE_MAX_CONTROLLERS][PCH_PCIE_CONTROLLER_PORTS] = {
    {  0,  1,  2,  3 }, //SPA
    {  4,  5,  6,  7 }, //SPB
    { 12, 13, 14, 15 }, //SPC
    { 20, 21, 22, 23 }, //SPD
    { 28, 29, 30, 31 }, //SPE
    { 32, 33, 34, 35 }  //SPF, only for KBL-H
  };

  static CONST UINT8 DefaultGrantCount = 4;

  PCH_SERIES  PchSeries;
  UINT32      Dgcr0Addr;
  UINT32      PgTgt0Addr;
  UINT32      DgcrNo0;
  UINT32      DgcrNo1;
  UINT32      PgTgtNo;
  UINT32      Channel;
  UINT32      ChannelGrant[PCH_PCIE_CONTROLLER_PORTS];

  DEBUG ((DEBUG_INFO, "PsfConfigurePcieGrantCounts(%d) Start\n", Controller));

  PchSeries = GetPchSeries ();
  if (PchSeries == PchLp) {
    Dgcr0Addr     = R_PCH_LP_PSF1_DEV_GNTCNT_RELOAD_DGCR0;
    PgTgt0Addr    = R_PCH_LP_PSF1_TARGET_GNTCNT_RELOAD_PG1_TGT0;
  } else {
    if (GetPchGeneration () == KblPch) {
      Dgcr0Addr     = R_KBL_PCH_H_PSF1_DEV_GNTCNT_RELOAD_DGCR0;
      PgTgt0Addr    = R_KBL_PCH_H_PSF1_TARGET_GNTCNT_RELOAD_PG1_TGT0;
    } else {
      Dgcr0Addr     = R_SKL_PCH_H_PSF1_DEV_GNTCNT_RELOAD_DGCR0;
      PgTgt0Addr    = R_SKL_PCH_H_PSF1_TARGET_GNTCNT_RELOAD_PG1_TGT0;
    }
  }


  switch (ControllerConfig) {
    case V_PCH_PCIE_STRPFUSECFG_RPC_1_1_1_1: //Pcie4x1
      ChannelGrant[0] = 1;
      ChannelGrant[1] = 1;
      ChannelGrant[2] = 1;
      ChannelGrant[3] = 1;
      break;
    case V_PCH_PCIE_STRPFUSECFG_RPC_2_1_1: //Pcie1x2_2x1
      ChannelGrant[0] = 2;
      ChannelGrant[1] = DefaultGrantCount;
      ChannelGrant[2] = 1;
      ChannelGrant[3] = 1;
      break;
    case V_PCH_PCIE_STRPFUSECFG_RPC_2_2: //Pcie2x2
      ChannelGrant[0] = 2;
      ChannelGrant[1] = DefaultGrantCount;
      ChannelGrant[2] = 2;
      ChannelGrant[3] = DefaultGrantCount;
      break;
    case V_PCH_PCIE_STRPFUSECFG_RPC_4: //Pcie1x4
      ChannelGrant[0] = 4;
      ChannelGrant[1] = DefaultGrantCount;
      ChannelGrant[2] = DefaultGrantCount;
      ChannelGrant[3] = DefaultGrantCount;
      break;
    default:
      ASSERT (FALSE);
      return EFI_INVALID_PARAMETER;
  }

  for (Channel = 0; Channel < PCH_PCIE_CONTROLLER_PORTS; ++Channel) {
    if (PchSeries == PchLp) {
      DgcrNo0 = DgcrPairsLp[Controller][Channel][0];
      DgcrNo1 = DgcrPairsLp[Controller][Channel][1];
      PgTgtNo = PgTgtLp[Controller][Channel];
    } else {
      DgcrNo0 = DgcrPairsH[Controller][Channel][0];
      DgcrNo1 = DgcrPairsH[Controller][Channel][1];
      PgTgtNo = PgTgtH[Controller][Channel];
    }

    DEBUG ((DEBUG_INFO, "DGCR%d = %d\n", DgcrNo0, ChannelGrant[Channel]));
    PchPcrAndThenOr32 (
      PID_PSF1,
      (UINT16) (Dgcr0Addr  + (DgcrNo0 * S_PCH_PSF_DEV_GNTCNT_RELOAD_DGCR)),
      (UINT32) ~B_PCH_PSF_DEV_GNTCNT_RELOAD_DGCR_GNT_CNT_RELOAD,
      ChannelGrant[Channel]
      );
    DEBUG ((DEBUG_INFO, "DGCR%d = %d\n", DgcrNo1, ChannelGrant[Channel]));
    PchPcrAndThenOr32 (
      PID_PSF1,
      (UINT16) (Dgcr0Addr  + (DgcrNo1 * S_PCH_PSF_DEV_GNTCNT_RELOAD_DGCR)),
      (UINT32) ~B_PCH_PSF_DEV_GNTCNT_RELOAD_DGCR_GNT_CNT_RELOAD,
      ChannelGrant[Channel]
      );
    DEBUG ((DEBUG_INFO, "PG1_TGT%d = %d\n", PgTgtNo, ChannelGrant[Channel]));
    PchPcrAndThenOr32 (
      PID_PSF1,
      (UINT16) (PgTgt0Addr + (PgTgtNo * S_PCH_PSF_TARGET_GNTCNT_RELOAD)),
      (UINT32) ~B_PCH_PSF_TARGET_GNTCNT_RELOAD_GNT_CNT_RELOAD,
      ChannelGrant[Channel]
      );
  }

  DEBUG ((DEBUG_INFO, "PsfConfigurePcieGrantCounts() End\n"));

  return EFI_SUCCESS;
}

/**
  Disable ISM NP Completion Tracking for GbE PSF port

  @param[in] None

  @retval None
**/
VOID
PsfDisableIsmNpCompletionTrackForGbe (
  VOID
  )
{
  UINT16       RegOffset;
  PCH_SBI_PID  PortId;

  if (GetPchSeries () == PchLp) {
    PortId = PID_PSF1;
    RegOffset = R_PCH_LP_PCR_PSF1_PSF_PORT_CONFIG_PG1_PORT7;
  } else {
    PortId = PID_PSF3;
    RegOffset = R_PCH_H_PCR_PSF3_PSF_PORT_CONFIG_PG1_PORT1;
  }
  ///
  /// Clear PCR PSF_x_PSF_PORT_CONFIG_PGx_PORTx[5] = 0b
  ///
  PchPcrAndThenOr8 (
    PortId,
    RegOffset,
    (UINT8) ~(BIT5),
    0
    );
}

/**
  Program PSF EOI Multicast configuration

  @param[in] None

  @retval None
**/
VOID
PsfSetEoiMulticastConfiguration (
  VOID
  )
{
  UINT32                McastTarget;

  ///
  /// Program PSF Multicast registers accordingly to SKL PCH BWG 5.14.4 PSF EOI Multicast Configuration
  ///
  if (GetPchSeries () == PchLp) {
    McastTarget = V_PCH_LP_PCR_PSFX_PSF_MC_AGENT_MCAST_TGT_P2SB;
    ///
    /// Set PCR[PSF1] + 4060h = 38A00h
    ///
    PchPcrWrite32 (
      PID_PSF1, R_PCH_LP_PCR_PSF1_PSF_MC_AGENT_MCAST0_TGT0_EOI,
      McastTarget
      );
    ///
    /// Set PCR[PSF1] + 4050h[7:0] = 11b
    ///
    PchPcrWrite32 (
      PID_PSF1, R_PCH_LP_PCR_PSF1_PSF_MC_CONTROL_MCAST0_EOI,
      0x3
      );
  } else {
    McastTarget = V_PCH_H_PCR_PSFX_PSF_MC_AGENT_MCAST_TGT_P2SB;
    if (GetPchGeneration () == KblPch) {
      ///
      /// Set PCR[PSF1] + 405Ch = 38B00h
      ///
      PchPcrWrite32 (
        PID_PSF1, R_KBL_PCH_H_PCR_PSF1_PSF_MC_AGENT_MCAST0_TGT0_EOI,
        McastTarget
        );
      ///
      /// Set PCR[PSF1] + 404Ch[7:0] = 11b
      ///
      PchPcrWrite32 (
        PID_PSF1, R_KBL_PCH_H_PCR_PSF1_PSF_MC_CONTROL_MCAST0_EOI,
        0x3
        );
    } else {
      ///
      /// Set PCR[PSF1] + 4058h = 38B00h
      ///
      PchPcrWrite32 (
        PID_PSF1, R_SKL_PCH_H_PCR_PSF1_PSF_MC_AGENT_MCAST0_TGT0_EOI,
        McastTarget
        );
      ///
      /// Set PCR[PSF1] + 4048h[7:0] = 11b
      ///
      PchPcrWrite32 (
        PID_PSF1, R_SKL_PCH_H_PCR_PSF1_PSF_MC_CONTROL_MCAST0_EOI,
        0x3
        );
    }
  }
  ///
  /// Set PCR[PSF1] + 4054h to P2SB target
  ///
  PchPcrWrite32 (
    PID_PSF3, R_PCH_PCR_PSF3_PSF_MC_AGENT_MCAST0_TGT0_EOI,
    McastTarget
    );
  ///
  /// Set PCR[PSF3] + 404Ch[7:0] = 11b
  ///
  PchPcrWrite32 (
    PID_PSF3, R_PCH_PCR_PSF3_PSF_MC_CONTROL_MCAST0_EOI,
    0x3
    );
}

/**
  This function enables EOI message forwarding in PSF for PCIe ports
  for cases where IOAPIC is present behind this root port.

  @param[in] RpIndex        Root port index (0 based)

  @retval Status
**/
EFI_STATUS
PsfConfigurEoiForPciePort (
  IN  UINT32  RpIndex
  )
{

  UINT32          Data32;
  UINT8           NumMcVal;
  UINT32          RpDestId;
  ///
  /// PCH-LP:
  /// RP Destination ID  RP  Destination ID
  ///  1     0x18100      7     0x18302
  ///  2     0x18101      8     0x18303
  ///  3     0x18102      9     0x18500
  ///  4     0x18103      10    0x18501
  ///  5     0x18300      11    0x18502
  ///  6     0x18301      12    0x18503
  ///
  static UINT32   PchSklLpRpDestId[PCH_LP_PCIE_MAX_ROOT_PORTS] =
  {
    0x18100, 0x18101, 0x18102, 0x18103, 0x18300,
    0x18301, 0x18302, 0x18303, 0x18500, 0x18501,
    0x18502, 0x18503
  };
  ///
  /// PCH-H:
  /// RP Destination ID  RP  Destination ID
  ///  1     0x18000      11    0x18302
  ///  2     0x18001      12    0x18303
  ///  3     0x18002      13    0x18500
  ///  4     0x18003      14    0x18501
  ///  5     0x18100      15    0x18502
  ///  6     0x18101      16    0x18503
  ///  7     0x18102      17    0x18700
  ///  8     0x18103      18    0x18701
  ///  9     0x18300      19    0x18702
  ///  10    0x18301      20    0x18703
  ///
  /// KBL-H: same as SKL-H plus the following 4 ports
  ///  21    0x18800      23    0x18802
  ///  22    0x18801      24    0x18803
  ///
  static UINT32   PchHRpDestId[KBL_PCH_H_PCIE_MAX_ROOT_PORTS] =
  {
    0x18000, 0x18001, 0x18002, 0x18003,
    0x18100, 0x18101, 0x18102, 0x18103,
    0x18300, 0x18301, 0x18302, 0x18303,
    0x18500, 0x18501, 0x18502, 0x18503,
    0x18700, 0x18701, 0x18702, 0x18703,
    0x18800, 0x18801, 0x18802, 0x18803
  };
  PCH_SERIES      PchSeries;
  PCH_GENERATION  PchGen;
  UINT16          PsfReg;

  ASSERT (RpIndex < GetPchMaxPciePortNum ());

  PchSeries = GetPchSeries ();
  PchGen = GetPchGeneration ();

  ///
  /// If there is an IOAPIC discovered behind root port program PSF Multicast registers
  /// accordingly to SKL PCH BWG 5.14.4 PSF EOI Multicast Configuration
  ///
  ///
  ///
  ///
  /// Read PSF_1_PSF_MC_CONTROL_MCAST0_RS0_EOI bit [7:1], increase it by 1, write back
  ///
  if (PchSeries == PchLp) {
    PsfReg = R_PCH_LP_PCR_PSF1_PSF_MC_CONTROL_MCAST0_EOI;
  } else {
    if (PchGen == KblPch) {
      PsfReg = R_KBL_PCH_H_PCR_PSF1_PSF_MC_CONTROL_MCAST0_EOI;
    } else {
      PsfReg = R_SKL_PCH_H_PCR_PSF1_PSF_MC_CONTROL_MCAST0_EOI;
    }
  }
  PchPcrRead32 (PID_PSF1, PsfReg, &Data32);
  NumMcVal = (UINT8) (Data32 >> 1);
  Data32 += 0x2;
  PchPcrWrite32 (PID_PSF1, PsfReg, Data32);

  ///
  /// Program PSF_1_PSF_MC_AGENT_MCAST0_RS0_TGT<x>_EOI, the next empty register,
  /// where x is the same as the value read in step 1 before the increment.
  /// Program this register according to the RP# and the table below
  ///
  if (PchSeries == PchLp) {
    PsfReg = R_PCH_LP_PCR_PSF1_PSF_MC_AGENT_MCAST0_TGT0_EOI;
    RpDestId = PchSklLpRpDestId[RpIndex];
  } else {
    if (PchGen == KblPch) {
      PsfReg = R_KBL_PCH_H_PCR_PSF1_PSF_MC_AGENT_MCAST0_TGT0_EOI;
    } else {
      PsfReg = R_SKL_PCH_H_PCR_PSF1_PSF_MC_AGENT_MCAST0_TGT0_EOI;
    }
    RpDestId = PchHRpDestId[RpIndex];
  }

  PchPcrAndThenOr32 (
    PID_PSF1, PsfReg + (NumMcVal * 4),
    (UINT32) ~0,
    RpDestId
    );

  return EFI_SUCCESS;
}

/**
  Reload default RP PSF device number and function number.
  The PSF register doesn't got reset after system reset, which will result in mismatch between
  PSF register setting and PCIE PCR PCD register setting. Reset PSF register in early phase
  to avoid cycle decoding confusing.

  @param[in] None

  @retval Status
**/
VOID
PsfReloadDefaultPcieRpDevFunc (
  VOID
  )
{
  UINT16   Psf1RpFuncCfgBase;
  UINTN    PortIndex;
  UINTN    MaxPciePorts;

  PortIndex = 0;
  MaxPciePorts = GetPchMaxPciePortNum ();

  if (GetPchSeries () == PchLp) {
    Psf1RpFuncCfgBase = R_PCH_LP_PCR_PSF1_T1_AGENT_FUNCTION_CONFIG_SPA_D28_F0;
  } else {
    if (GetPchGeneration () == KblPch) {
      Psf1RpFuncCfgBase = R_KBL_PCH_H_PCR_PSF1_T1_AGENT_FUNCTION_CONFIG_SPA_D28_F0;
    } else {
      Psf1RpFuncCfgBase = R_SKL_PCH_H_PCR_PSF1_T1_AGENT_FUNCTION_CONFIG_SPA_D28_F0;
    }
  }
  //
  // Reload D28 registers
  //
  for (; PortIndex < 8; PortIndex++) {
    PchPcrWrite32 (
      PID_PSF1,
      Psf1RpFuncCfgBase + 4 * (UINT16) PortIndex,
      (PCI_DEVICE_NUMBER_PCH_PCIE_DEVICE_1 << N_PCH_PCR_PSFX_TX_AGENT_FUNCTION_CONFIG_DEVICE) +
      ((PortIndex % 8) << N_PCH_PCR_PSFX_TX_AGENT_FUNCTION_CONFIG_FUNCTION)
      );
  }
  //
  // Reload D29 registers
  //
  for (; PortIndex < 16 && PortIndex < MaxPciePorts; PortIndex++) {
    PchPcrWrite32 (
      PID_PSF1,
      Psf1RpFuncCfgBase + 4 * (UINT16) PortIndex,
      (PCI_DEVICE_NUMBER_PCH_PCIE_DEVICE_2 << N_PCH_PCR_PSFX_TX_AGENT_FUNCTION_CONFIG_DEVICE) +
      ((PortIndex % 8) << N_PCH_PCR_PSFX_TX_AGENT_FUNCTION_CONFIG_FUNCTION)
      );
  }
  //
  // Reload D27 registers
  //
  for (; PortIndex < MaxPciePorts; PortIndex++) {
    PchPcrWrite32 (
      PID_PSF1,
      Psf1RpFuncCfgBase + 4 * (UINT16) PortIndex,
      (PCI_DEVICE_NUMBER_PCH_PCIE_DEVICE_3 << N_PCH_PCR_PSFX_TX_AGENT_FUNCTION_CONFIG_DEVICE) +
      ((PortIndex % 8) << N_PCH_PCR_PSFX_TX_AGENT_FUNCTION_CONFIG_FUNCTION)
      );
  }
}


/**
  Assign new function number for PCIe Port Number.
  This function will also perform S3 boot script programming

  @param[in] RpIndex        PCIe Root Port Index (0 based)
  @param[in] NewFunction    New Function number

  @retval None
**/
VOID
PsfSetPcieFunctionWithS3BootScript (
  IN UINT32  RpIndex,
  IN UINT32  NewFunction
  )
{
  UINT16  PsfRpFuncCfgBase;
  UINT16  PsfD28F0FuncCfgBase;
  UINT32  Data32;

  if (GetPchSeries () == PchLp) {
    PsfD28F0FuncCfgBase = R_PCH_LP_PCR_PSF1_T1_AGENT_FUNCTION_CONFIG_SPA_D28_F0;
  } else {
    if (GetPchGeneration () == KblPch) {
      PsfD28F0FuncCfgBase = R_KBL_PCH_H_PCR_PSF1_T1_AGENT_FUNCTION_CONFIG_SPA_D28_F0;
    } else {
      PsfD28F0FuncCfgBase = R_SKL_PCH_H_PCR_PSF1_T1_AGENT_FUNCTION_CONFIG_SPA_D28_F0;
    }
  }

  PsfRpFuncCfgBase = PsfD28F0FuncCfgBase + 4 * (UINT16)RpIndex;

  //
  // Program PSF1 RP function config register.
  //
  PchPcrAndThenOr32 (
    PID_PSF1,
    PsfRpFuncCfgBase,
    (UINT32) ~B_PCH_PCR_PSFX_TX_AGENT_FUNCTION_CONFIG_FUNCTION,
    (UINT32) (NewFunction << N_PCH_PCR_PSFX_TX_AGENT_FUNCTION_CONFIG_FUNCTION)
    );

  PchPcrRead32 (PID_PSF1, PsfRpFuncCfgBase, &Data32);
  PCH_PCR_BOOT_SCRIPT_WRITE (
    S3BootScriptWidthUint32,
    PID_PSF1,
    PsfRpFuncCfgBase,
    1,
    &Data32
    );
}

/**
  This function enables PCIe Relaxed Order in PSF

  @param[in] None

  @retval None
**/
VOID
PsfEnablePcieRelaxedOrder (
  VOID
  )
{
  ///
  /// PCH BIOS Spec Section 8.2.9
  ///
  PchPcrAndThenOr32 (
    PID_PSF1,
    R_PCH_PCR_PSF_PORT_CONFIG_PG0_PORT0,
    ~0u,
    BIT1
    );
}

/**
  Configure PSF power management.
  Must be called after all PSF configuration is completed.

  @param[in] None

  @retval None
**/
VOID
PsfConfigurePowerManagement (
  VOID
  )
{
  PchPcrAndThenOr32 (PID_PSF1,     R_PCH_PCR_PSF_GLOBAL_CONFIG, ~0u, B_PCH_PCR_PSF_GLOBAL_CONFIG_ENTCG);
  PchPcrAndThenOr32 (PID_PSF2,     R_PCH_PCR_PSF_GLOBAL_CONFIG, ~0u, B_PCH_PCR_PSF_GLOBAL_CONFIG_ENTCG);
  PchPcrAndThenOr32 (PID_PSF3,     R_PCH_PCR_PSF_GLOBAL_CONFIG, ~0u, B_PCH_PCR_PSF_GLOBAL_CONFIG_ENTCG);
  PchPcrAndThenOr32 (PID_PSF4,     R_PCH_PCR_PSF_GLOBAL_CONFIG, ~0u, B_PCH_PCR_PSF_GLOBAL_CONFIG_ENTCG);
  PchPcrAndThenOr32 (PID_CSME_PSF, R_PCH_PCR_PSF_GLOBAL_CONFIG, ~0u, B_PCH_PCR_PSF_GLOBAL_CONFIG_ENTCG);
  PchPcrAndThenOr32 (PID_PSF2,     R_PCH_PCR_PSF_GLOBAL_CONFIG, ~0u, B_PCH_PCR_PSF_GLOBAL_CONFIG_ENLCG);
  PchPcrAndThenOr32 (PID_PSF3,     R_PCH_PCR_PSF_GLOBAL_CONFIG, ~0u, B_PCH_PCR_PSF_GLOBAL_CONFIG_ENLCG);
  PchPcrAndThenOr32 (PID_PSF4,     R_PCH_PCR_PSF_GLOBAL_CONFIG, ~0u, B_PCH_PCR_PSF_GLOBAL_CONFIG_ENLCG);
  PchPcrAndThenOr32 (PID_CSME_PSF, R_PCH_PCR_PSF_GLOBAL_CONFIG, ~0u, B_PCH_PCR_PSF_GLOBAL_CONFIG_ENLCG);
}

/**
  Enable VTd support in PSF.

  @param[in] None

  @retval None
**/
VOID
PchPsfEnableVtd (
  VOID
  )
{
  STATIC PCH_SBI_PID PsfPidTable[] = {PID_PSF1, PID_PSF2, PID_PSF3, PID_PSF4};
  UINTN              PsfPidIndex;

  for (PsfPidIndex = 0; PsfPidIndex < (sizeof (PsfPidTable) / sizeof (PsfPidTable[0])); PsfPidIndex++) {
    PchPcrAndThenOr32 (
      PsfPidTable[PsfPidIndex],
      R_PCH_PCR_PSF_ROOTSPACE_CONFIG_RS0,
      ~0u,
      B_PCH_PCR_PSF_ROOTSPACE_CONFIG_RSX_VTDEN
      );
  }
}

/**
  Disable PSF address-based peer-to-peer decoding.
**/
VOID
PchPsfDisableP2pDecoding (
  VOID
  )
{
  STATIC PCH_SBI_PID PsfPidTable[] = {PID_PSF1, PID_PSF2, PID_PSF3, PID_PSF4};
  UINTN              PsfPidIndex;

  for (PsfPidIndex = 0; PsfPidIndex < (sizeof (PsfPidTable) / sizeof (PsfPidTable[0])); PsfPidIndex++) {
    PchPcrAndThenOr32 (
      PsfPidTable[PsfPidIndex],
      R_PCH_PCR_PSF_ROOTSPACE_CONFIG_RS0,
      (UINT32)~B_PCH_PCR_PSF_ROOTSPACE_CONFIG_RSX_ENADDRP2P,
      0
      );

    PchPcrAndThenOr32 (
      PsfPidTable[PsfPidIndex],
      R_PCH_PCR_PSF_ROOTSPACE_CONFIG_RS1,
      (UINT32)~B_PCH_PCR_PSF_ROOTSPACE_CONFIG_RSX_ENADDRP2P,
      0
      );
  }
}
