/** @file
This file contains routines that support PCI Express initialization
Copyright (c) 2021, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/**
Get PCIe port number for enabled port.
@param[in] RpBase Root Port pci segment base address
@retval Root Port number (1 based)
**/
UINT32
PciePortNum (
IN UINT64 RpBase
)
{
return PciSegmentRead32 (RpBase + R_PCH_PCIE_CFG_LCAP) >> N_PCH_PCIE_CFG_LCAP_PN;
}
/**
Get PCIe root port index
@param[in] RpBase Root Port pci segment base address
@retval Root Port index (0 based)
**/
UINT32
PciePortIndex (
IN UINT64 RpBase
)
{
return PciePortNum (RpBase) - 1;
}
/**
This function checks whether PHY lane power gating is enabled on the port.
@param[in] RpBase Root Port base address
@retval TRUE PHY power gating is enabled
@retval FALSE PHY power gating disabled
**/
BOOLEAN
PcieIsPhyLanePgEnabled (
IN UINT64 RpBase
)
{
UINT32 Data32;
Data32 = PciSegmentRead32 (RpBase + R_PCH_PCIE_CFG_PCIEPMECTL);
return (Data32 & B_PCH_PCIE_CFG_PCIEPMECTL_DLSULPPGE) != 0;
}
/**
Configures Root Port packet split.
@param[in] Segment,Bus,Device,Function address of currently visited PCIe device
@param[in] Mps maximum packet size
**/
VOID
ConfigureRpPacketSplit (
UINT64 RpBase,
UINT8 Mps
)
{
PciSegmentAndThenOr32 (RpBase + R_PCIE_CFG_CCFG, (UINT32) ~(B_PCIE_CFG_CCFG_UNRS), Mps << N_PCIE_CFG_CCFG_UNRS);
}
/**
Configures LTR override in Root Port's proprietary registers.
@param[in] Segment,Bus,Device,Function address of currently visited PCIe device
@param[in] DevNum currently visited device number
@param[in] RpConfig Root Port LTR configuration
@param[in] AspmOverride combination of LTR override values from all devices under this Root Port
**/
VOID
ConfigureRpLtrOverride (
UINT64 RpBase,
UINT32 DevNum,
LTR_OVERRIDE *TreeLtr,
PCIE_LTR_CONFIG *LtrConfig
)
{
UINT32 OvrEn;
UINT32 OvrVal;
BOOLEAN IsCpuPcie;
IsCpuPcie = FALSE;
OvrEn = 0;
OvrVal = 0;
if (DevNum == SA_PEG0_DEV_NUM || DevNum == SA_PEG3_DEV_NUM) {
IsCpuPcie = TRUE;
}
//
// LTR settings from LTROVR register only get acknowledged on rising edge of LTROVR2[1:0]
// If those bits were already set (that can happen on a plug-hotUnplug-hotPlug scenario),
// they need to be toggled
//
if (PciSegmentRead32 (RpBase + R_PCH_PCIE_CFG_LTROVR2) != 0) {
PciSegmentWrite32 (RpBase + R_PCH_PCIE_CFG_LTROVR2, 0);
}
//
// (*)LatencyOverrideMode = 0 -> no override
// 1 -> override with RP policy values
// 2 -> override with endpoint's override values
//
if (LtrConfig->ForceLtrOverride || TreeLtr->ForceOverride) {
OvrEn |= B_PCH_PCIE_CFG_LTROVR2_FORCE_OVERRIDE;
}
if (LtrConfig->LtrConfigLock == TRUE) {
OvrEn |= B_PCH_PCIE_CFG_LTROVR2_LOCK;
}
if (LtrConfig->SnoopLatencyOverrideMode == 1) {
OvrEn |= B_PCH_PCIE_CFG_LTROVR2_LTRSOVREN;
OvrVal |= LtrConfig->SnoopLatencyOverrideValue;
OvrVal |= LtrConfig->SnoopLatencyOverrideMultiplier << 10;
OvrVal |= B_PCH_PCIE_CFG_LTROVR_LTRSROVR;
} else if (LtrConfig->SnoopLatencyOverrideMode == 2) {
if (TreeLtr->MaxSnoopLatencyRequirement) {
OvrEn |= B_PCH_PCIE_CFG_LTROVR2_LTRSOVREN;
OvrVal |= TreeLtr->MaxSnoopLatencyValue;
OvrVal |= TreeLtr->MaxSnoopLatencyScale << 10;
OvrVal |= B_PCH_PCIE_CFG_LTROVR_LTRSROVR;
}
}
if (LtrConfig->NonSnoopLatencyOverrideMode == 1) {
OvrEn |= B_PCH_PCIE_CFG_LTROVR2_LTRNSOVREN;
OvrVal |= LtrConfig->NonSnoopLatencyOverrideValue << 16;
OvrVal |= LtrConfig->NonSnoopLatencyOverrideMultiplier << 26;
OvrVal |= B_PCH_PCIE_CFG_LTROVR_LTRNSROVR;
} else if (LtrConfig->NonSnoopLatencyOverrideMode == 2) {
if (TreeLtr->MaxNoSnoopLatencyRequirement) {
OvrEn |= B_PCH_PCIE_CFG_LTROVR2_LTRNSOVREN;
OvrVal |= TreeLtr->MaxNoSnoopLatencyValue << 16;
OvrVal |= TreeLtr->MaxNoSnoopLatencyScale << 26;
OvrVal |= B_PCH_PCIE_CFG_LTROVR_LTRNSROVR;
}
}
PciSegmentWrite32 (RpBase + R_PCH_PCIE_CFG_LTROVR, OvrVal);
PciSegmentWrite32 (RpBase + R_PCH_PCIE_CFG_LTROVR2, OvrEn);
DEBUG ((DEBUG_INFO, "ConfigureRpLtrOverride IsCpuPcie=%d\n", IsCpuPcie));
DEBUG ((DEBUG_INFO, "ConfigureRpLtrOverride %x Val %x En %x\n", RpBase, OvrVal, OvrEn));
}
/**
This function configures EOI message forwarding for PCIe port.
If there's an IoAPIC behind this port, forwarding will be enabled
Otherwise it will be disabled to minimize bus traffic
@param[in] Segment,Bus,Device,Function address of currently visited PCIe device
@param[in] IoApicPresent TRUE if there's IoAPIC behind this Root Port
**/
VOID
ConfigureEoiForwarding (
UINT64 RpBase,
BOOLEAN IoApicPresent
)
{
UINT32 RpIndex;
RpIndex = PciePortIndex (RpBase);
if (IoApicPresent == FALSE) {
PciSegmentOr32 (RpBase + R_PCH_PCIE_CFG_MPC2, B_PCH_PCIE_CFG_MPC2_EOIFD);
} else {
///
/// If there is an IOAPIC discovered behind Root Port, program PSF Multicast registers
/// in accordance with PCH PCIe BWG PSF EOI Multicast Configuration
///
PciSegmentAnd32 (RpBase + R_PCH_PCIE_CFG_MPC2, (UINT32)~B_PCH_PCIE_CFG_MPC2_EOIFD);
PsfConfigurEoiForPciePort (RpIndex);
}
}
/**
Configures proprietary parts of L1 substates configuration in Root Port
@param[in] RpSbdf segment:bus:device:function coordinates of Root Port
@param[in] LtrCapable TRUE if Root Port is LTR capable
**/
VOID
L1ssProprietaryConfiguration (
UINT64 RpBase,
BOOLEAN LtrCapable
)
{
BOOLEAN ClkreqSupported;
BOOLEAN L1ssEnabled;
UINT16 PcieCapOffset;
UINT32 Data32;
BOOLEAN L1LowSupported;
ClkreqSupported = PcieIsPhyLanePgEnabled (RpBase);
PcieCapOffset = PcieBaseFindExtendedCapId (RpBase, V_PCIE_EX_L1S_CID);
if (PcieCapOffset == 0) {
L1ssEnabled = FALSE;
} else {
Data32 = PciSegmentRead32 (RpBase + PcieCapOffset + R_PCIE_EX_L1SCTL1_OFFSET);
L1ssEnabled = Data32 & (B_PCIE_EX_L1SCAP_AL1SS | B_PCIE_EX_L1SCAP_AL12S | B_PCIE_EX_L1SCAP_PPL11S |B_PCIE_EX_L1SCAP_PPL12S);
}
L1LowSupported = ClkreqSupported && LtrCapable && !L1ssEnabled;
///
/// If L1.SNOOZ and L1.OFF (L1 Sub-States) are not supported and per-port CLKREQ# is supported, and LTR is supported:
/// Enable L1.LOW by setting Dxx:Fn:420[17] = 1b
///
if (L1LowSupported) {
PciSegmentOr32 (RpBase + R_PCH_PCIE_CFG_PCIEPMECTL, (UINT32) B_PCH_PCIE_CFG_PCIEPMECTL_L1LE);
} else {
PciSegmentAnd32 (RpBase + R_PCH_PCIE_CFG_PCIEPMECTL, (UINT32) ~B_PCH_PCIE_CFG_PCIEPMECTL_L1LE);
}
if (L1LowSupported || L1ssEnabled) {
///
/// f. Set Dxx:Fn:420h[0] to 1b prior to L1 enabling if any L1substate is enabled (including L1.LOW)
///
PciSegmentOr32 (RpBase + R_PCH_PCIE_CFG_PCIEPMECTL, B_PCH_PCIE_CFG_PCIEPMECTL_L1FSOE);
}
}