/** @file
|
This file contains routines that support PCI Express initialization
|
|
Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
**/
|
#include "PchPciExpressHelpersLibrary.h"
|
|
const UINT8 mPcieL1sTposMultiplier[] = {2, 10, 100};
|
|
/*
|
Returns Tpower_on capability of device
|
|
@param[in] DeviceBase device base address
|
@param[in] L1ssCapOffset offset to L1substates capability in device's extended config space
|
*/
|
T_POWER_ON GetTpoCapability (
|
UINTN DeviceBase,
|
UINT32 L1ssCapOffset
|
)
|
{
|
T_POWER_ON Tpo;
|
UINT32 L1ssCapabilities;
|
|
L1ssCapabilities = MmioRead32 (DeviceBase + L1ssCapOffset + R_PCIE_EX_L1SCAP_OFFSET);
|
Tpo.Scale = (L1ssCapabilities & B_PCIE_EX_L1SCAP_PTPOS) >> N_PCIE_EX_L1SCAP_PTPOS;
|
Tpo.Value = (L1ssCapabilities & B_PCIE_EX_L1SCAP_PTV) >> N_PCIE_EX_L1SCAP_PTV;
|
return Tpo;
|
}
|
|
/*
|
Returns Tpower_on currently programed in a device
|
|
@param[in] DeviceBase device base address
|
@param[in] L1ssCapOffset offset to L1substates capability in device's extended config space
|
*/
|
T_POWER_ON GetTpo (
|
UINTN DeviceBase,
|
UINT32 L1ssCapOffset
|
)
|
{
|
T_POWER_ON Tpo;
|
UINT32 L1ssControl2;
|
|
L1ssControl2 = MmioRead32 (DeviceBase + L1ssCapOffset + R_PCIE_EX_L1SCTL2_OFFSET);
|
Tpo.Scale = L1ssControl2 & 0x3;
|
Tpo.Value = (L1ssControl2 & 0xF8) >> N_PCIE_EX_L1SCTL2_POWT;
|
return Tpo;
|
}
|
|
/*
|
Sets Tpower_on in a device
|
According to spec, Tpower_on can only be updated while L1.2 is disabled
|
|
@param[in] DeviceBase device base address
|
@param[in] L1ssCapOffset offset to L1substates capability in device's extended config space
|
@param[in] Tpo value to be programmed into Tpower_on
|
*/
|
VOID SetTpo (
|
UINTN DeviceBase,
|
UINT32 L1ssCapOffset,
|
T_POWER_ON Tpo
|
)
|
{
|
UINT32 L1_2;
|
|
L1_2 = MmioRead32 (DeviceBase + L1ssCapOffset + R_PCIE_EX_L1SCTL1_OFFSET) & (BIT2 | BIT0);
|
MmioAnd32 (DeviceBase + L1ssCapOffset + R_PCIE_EX_L1SCTL1_OFFSET, (UINT32)(~ (BIT2 | BIT0)));
|
MmioAndThenOr32 (DeviceBase + L1ssCapOffset + R_PCIE_EX_L1SCTL2_OFFSET, (UINT32)(~0xFB), (Tpo.Value << 3) + Tpo.Scale);
|
MmioOr32 (DeviceBase + L1ssCapOffset + R_PCIE_EX_L1SCTL1_OFFSET, L1_2);
|
}
|
|
/*
|
Converts Tpower_on from value:scale notation to microseconds
|
*/
|
UINT32 TpoToUs (
|
T_POWER_ON Tpo
|
)
|
{
|
static const UINT8 TpoScaleMultiplier[] = {2, 10, 100};
|
|
ASSERT (Tpo.Scale < TpoScaleMax);
|
if (Tpo.Scale >= TpoScaleMax) {
|
return 0;
|
}
|
return (TpoScaleMultiplier[Tpo.Scale] * Tpo.Value);
|
}
|
|
/**
|
Find the Offset to a given Capabilities ID
|
CAPID list:
|
0x01 = PCI Power Management Interface
|
0x04 = Slot Identification
|
0x05 = MSI Capability
|
0x10 = PCI Express Capability
|
|
@param[in] Bus Pci Bus Number
|
@param[in] Device Pci Device Number
|
@param[in] Function Pci Function Number
|
@param[in] CapId CAPID to search for
|
|
@retval 0 CAPID not found
|
@retval Other CAPID found, Offset of desired CAPID
|
**/
|
UINT8
|
PcieFindCapId (
|
IN UINT8 Bus,
|
IN UINT8 Device,
|
IN UINT8 Function,
|
IN UINT8 CapId
|
)
|
{
|
UINT8 CapHeaderOffset;
|
UINT8 CapHeaderId;
|
UINTN DeviceBase;
|
|
DeviceBase = MmPciBase (Bus, Device, Function);
|
|
DEBUG ((DEBUG_INFO,"PcieFindCapId () BDF %0x: %0x :%0x, CapId = %0x \n", Bus, Device, Function, CapId));
|
if ((MmioRead8 (DeviceBase + PCI_PRIMARY_STATUS_OFFSET) & EFI_PCI_STATUS_CAPABILITY) == 0x00) {
|
///
|
/// Function has no capability pointer
|
///
|
return 0;
|
} else {
|
///
|
/// Check the header layout to determine the Offset of Capabilities Pointer Register
|
///
|
if ((MmioRead8 (DeviceBase + PCI_HEADER_TYPE_OFFSET) & HEADER_LAYOUT_CODE) == (HEADER_TYPE_CARDBUS_BRIDGE)) {
|
///
|
/// If CardBus bridge, start at Offset 0x14
|
///
|
CapHeaderOffset = EFI_PCI_CARDBUS_BRIDGE_CAPABILITY_PTR;
|
} else {
|
///
|
/// Otherwise, start at Offset 0x34
|
///
|
CapHeaderOffset = PCI_CAPBILITY_POINTER_OFFSET;
|
}
|
///
|
/// Get Capability Header, A pointer value of 00h is used to indicate the last capability in the list.
|
///
|
CapHeaderId = 0;
|
CapHeaderOffset = MmioRead8 (DeviceBase + CapHeaderOffset) & ((UINT8) ~(BIT0 | BIT1));
|
while (CapHeaderOffset != 0 && CapHeaderId != 0xFF) {
|
CapHeaderId = MmioRead8 (DeviceBase + CapHeaderOffset);
|
if (CapHeaderId == CapId) {
|
if (CapHeaderOffset > PCI_MAXLAT_OFFSET) {
|
///
|
/// Return valid capability offset
|
///
|
return CapHeaderOffset;
|
} else {
|
ASSERT ((FALSE));
|
return 0;
|
}
|
}
|
///
|
/// Each capability must be DWORD aligned.
|
/// The bottom two bits of all pointers (including the initial pointer at 34h) are reserved
|
/// and must be implemented as 00b although software must mask them to allow for future uses of these bits.
|
///
|
CapHeaderOffset = MmioRead8 (DeviceBase + CapHeaderOffset + 1) & ((UINT8) ~(BIT0 | BIT1));
|
}
|
return 0;
|
}
|
}
|
|
/**
|
Search and return the offset of desired Pci Express Capability ID
|
CAPID list:
|
0x0001 = Advanced Error Reporting Capability
|
0x0002 = Virtual Channel Capability
|
0x0003 = Device Serial Number Capability
|
0x0004 = Power Budgeting Capability
|
|
@param[in] Bus Pci Bus Number
|
@param[in] Device Pci Device Number
|
@param[in] Function Pci Function Number
|
@param[in] CapId Extended CAPID to search for
|
|
@retval 0 CAPID not found, this includes situation where device doesn't exist
|
@retval Other CAPID found, Offset of desired CAPID
|
**/
|
UINT16
|
PcieFindExtendedCapId (
|
IN UINT8 Bus,
|
IN UINT8 Device,
|
IN UINT8 Function,
|
IN UINT16 CapId
|
)
|
{
|
UINT16 CapHeaderOffset;
|
UINT16 CapHeaderId;
|
UINTN DeviceBase;
|
|
DeviceBase = MmPciBase (Bus, Device, Function);
|
|
///
|
/// Start to search at Offset 0x100
|
/// Get Capability Header, A pointer value of 00h is used to indicate the last capability in the list.
|
///
|
CapHeaderId = 0;
|
CapHeaderOffset = R_PCH_PCIE_EXCAP_OFFSET;
|
while (CapHeaderOffset != 0 && CapHeaderId != MAX_UINT16) {
|
CapHeaderId = MmioRead16 (DeviceBase + CapHeaderOffset);
|
if (CapHeaderId == CapId) {
|
return CapHeaderOffset;
|
}
|
///
|
/// Each capability must be DWORD aligned.
|
/// The bottom two bits of all pointers are reserved and must be implemented as 00b
|
/// although software must mask them to allow for future uses of these bits.
|
///
|
CapHeaderOffset = (MmioRead16 (DeviceBase + CapHeaderOffset + 2) >> 4) & ((UINT16) ~(BIT0 | BIT1));
|
}
|
|
return 0;
|
}
|
|
/**
|
This returns ClkReq Number from Port Number
|
|
@param[in] PortIndex PCIe Port Number (Zero Base)
|
|
@retval ClkReq Number
|
**/
|
UINT8
|
GetPortClkReqNumber (
|
IN UINT8 PortIndex
|
)
|
{
|
UINT8 ClkReqNum;
|
PchPcrRead8 (PID_FIA, R_PCH_PCR_FIA_DRCRM1 + (PortIndex / 2), &ClkReqNum);
|
if (PortIndex % 2 == 0) {
|
ClkReqNum &= 0x0F;
|
} else {
|
ClkReqNum = ClkReqNum >> 4;
|
}
|
return ClkReqNum;
|
}
|
|
/**
|
Set Common clock to Root port and Endpoint PCI device
|
|
@param[in] Bus1 Root port Pci Bus Number
|
@param[in] Device1 Root port Pci Device Number
|
@param[in] Function1 Root port Pci Function Number
|
@param[in] Bus2 Endpoint Pci Bus Number
|
@param[in] Device2 Endpoint Pci Device Number
|
|
@exception EFI_UNSUPPORTED Unsupported operation.
|
@retval EFI_SUCCESS VC mapping correctly initialized
|
**/
|
EFI_STATUS
|
PcieSetCommonClock (
|
IN UINT8 Bus1,
|
IN UINT8 Device1,
|
IN UINT8 Function1,
|
IN UINT8 Bus2,
|
IN UINT8 Device2
|
)
|
{
|
UINT8 CapOffset1;
|
UINT8 CapOffset2;
|
BOOLEAN CommonClockSupport;
|
UINTN DeviceBase1;
|
UINTN DeviceBase2;
|
UINT16 RegData16;
|
UINT8 FunctionIndex;
|
UINT8 Function2;
|
|
DeviceBase1 = MmPciBase (Bus1, Device1, Function1);
|
|
///
|
/// Get the pointer to the Port PCI Express Capability Structure.
|
///
|
CommonClockSupport = FALSE;
|
CapOffset1 = PcieFindCapId (Bus1, Device1, Function1, EFI_PCI_CAPABILITY_ID_PCIEXP);
|
if (CapOffset1 == 0) {
|
return EFI_UNSUPPORTED;
|
}
|
///
|
/// Check the Port Slot Clock Configuration Bit.
|
///
|
if ((MmioRead16 (DeviceBase1 + CapOffset1 + R_PCIE_LSTS_OFFSET) & B_PCIE_LSTS_SCC) == 0) {
|
return EFI_UNSUPPORTED;
|
}
|
|
DeviceBase2 = MmPciBase (Bus2, Device2, 0);
|
///
|
/// Check if EndPoint device is Multi-Function Device
|
///
|
if (MmioRead8 (DeviceBase2 + PCI_HEADER_TYPE_OFFSET) & HEADER_TYPE_MULTI_FUNCTION) {
|
///
|
/// If multi-function Device, check function 0-7
|
///
|
Function2 = PCI_MAX_FUNC;
|
} else {
|
///
|
/// Otherwise, check function 0 only
|
///
|
Function2 = 0;
|
}
|
|
for (FunctionIndex = 0; FunctionIndex <= Function2; FunctionIndex++) {
|
DeviceBase2 = MmPciBase (Bus2, Device2, FunctionIndex);
|
///
|
/// Check the Endpoint Slot Clock Configuration Bit.
|
///
|
CapOffset2 = PcieFindCapId (Bus2, Device2, FunctionIndex, EFI_PCI_CAPABILITY_ID_PCIEXP);
|
if ((CapOffset2 != 0) && ((MmioRead16 (DeviceBase2 + CapOffset2 + R_PCIE_LSTS_OFFSET) & B_PCIE_LSTS_SCC) != 0)) {
|
///
|
/// Common clock is supported, set common clock bit on root port
|
/// and the endpoint
|
///
|
if (CommonClockSupport == FALSE) {
|
MmioOr8 (DeviceBase1 + CapOffset1 + R_PCIE_LCTL_OFFSET, B_PCIE_LCTL_CCC);
|
CommonClockSupport = TRUE;
|
}
|
MmioOr8 (DeviceBase2 + CapOffset2 + R_PCIE_LCTL_OFFSET, B_PCIE_LCTL_CCC);
|
}
|
}
|
///
|
/// If common clock not supported on root port and endpoint, return EFI_UNSUPPORTED
|
///
|
if (CommonClockSupport == FALSE) {
|
return EFI_UNSUPPORTED;
|
}
|
///
|
/// Retrain the Link per PCI Express Specification.
|
///
|
MmioOr8 (DeviceBase1 + CapOffset1 + R_PCIE_LCTL_OFFSET, B_PCIE_LCTL_RL);
|
|
///
|
/// Wait until Re-Training has completed.
|
///
|
do {
|
RegData16 = MmioRead16 (DeviceBase1 + CapOffset1 + R_PCIE_LSTS_OFFSET) & B_PCIE_LSTS_LT;
|
} while (RegData16 != 0);
|
return EFI_SUCCESS;
|
}
|
|
|
/**
|
This function enables the CLKREQ# PM on all the end point functions
|
|
@param[in] Bus Pci Bus Number
|
@param[in] Device Pci Device Number
|
@param[in] RootDevice Rootport Device Number
|
@param[in] RootFunction Rootport Function Number
|
|
@retval None
|
**/
|
VOID
|
PcieSetClkreq (
|
IN UINT8 EndPointBus,
|
IN UINT8 EndPointDevice,
|
IN UINT8 RootDevice,
|
IN UINT8 RootFunction
|
)
|
{
|
UINT8 CapOffset;
|
UINTN DeviceBase;
|
UINT8 FunctionIndex;
|
UINT8 Function;
|
BOOLEAN ClkreqPerPortSupported;
|
|
DeviceBase = MmPciBase (EndPointBus, EndPointDevice, 0);
|
ClkreqPerPortSupported = TRUE;
|
|
///
|
/// Check if EndPoint device is Multi-Function Device
|
///
|
if (MmioRead8 (DeviceBase + PCI_HEADER_TYPE_OFFSET) & HEADER_TYPE_MULTI_FUNCTION) {
|
///
|
/// If multi-function Device, check function 0-7
|
///
|
Function = PCI_MAX_FUNC;
|
} else {
|
///
|
/// Otherwise, check function 0 only
|
///
|
Function = 0;
|
}
|
///
|
/// Parse thro all the functions of the endpoint and find the PCIe Cap ID (offset 10h) and if
|
/// exists then enable the CLKREQ# bit (BIT8) on that function
|
///
|
for (FunctionIndex = 0; FunctionIndex <= Function; FunctionIndex++) {
|
///
|
/// Find the PCIe Cap Id (10h)
|
///
|
CapOffset = PcieFindCapId (EndPointBus, EndPointDevice, FunctionIndex, EFI_PCI_CAPABILITY_ID_PCIEXP);
|
if (CapOffset == 0) {
|
continue;
|
}
|
|
DeviceBase = MmPciBase (EndPointBus, EndPointDevice, FunctionIndex);
|
///
|
/// Check if CLKREQ# is supported by the endpoints
|
///
|
if ((MmioRead32 (DeviceBase + CapOffset + R_PCIE_LCAP_OFFSET) & B_PCIE_LCAP_CPM) == 0) {
|
///
|
/// CLKREQ# is not supported so dont do anything
|
///
|
ClkreqPerPortSupported = FALSE;
|
break;
|
}
|
}
|
|
if (ClkreqPerPortSupported == FALSE) {
|
return;
|
}
|
///
|
/// Now enable the CLKREQ#
|
///
|
for (FunctionIndex = 0; FunctionIndex <= Function; FunctionIndex++) {
|
///
|
/// Find the PCIe Cap Id (10h)
|
///
|
CapOffset = PcieFindCapId (EndPointBus, EndPointDevice, FunctionIndex, EFI_PCI_CAPABILITY_ID_PCIEXP);
|
if (CapOffset == 0) {
|
continue;
|
}
|
|
DeviceBase = MmPciBase (EndPointBus, EndPointDevice, FunctionIndex);
|
MmioOr16 (DeviceBase + CapOffset + R_PCIE_LCTL_OFFSET, B_PCIE_LCTL_ECPM);
|
}
|
}
|
/**
|
This function get or set the Max Payload Size on all the end point functions
|
|
@param[in] EndPointBus The Bus Number of the Endpoint
|
@param[in] EndPointDevice The Device Number of the Endpoint
|
@param[in, out] MaxPayload The Max Payolad Size of the root port
|
@param[in] Operation True: Set the Max Payload Size on all the end point functions
|
False: Get the Max Payload Size on all the end point functions
|
|
@retval EFI_SUCCESS Successfully completed.
|
**/
|
EFI_STATUS
|
PcieMaxPayloadSize (
|
IN UINT8 EndPointBus,
|
IN UINT8 EndPointDevice,
|
IN OUT UINT16 *MaxPayload,
|
IN BOOLEAN Operation
|
)
|
{
|
UINTN DeviceBase;
|
UINT8 PcieCapOffset;
|
UINT16 EndPointMaxPayload;
|
UINT8 FunctionIndex;
|
UINT8 EndPointFunction;
|
|
///
|
/// Obtain the Max Payload Size for all the end point functions
|
///
|
DeviceBase = MmPciBase (EndPointBus, EndPointDevice, 0);
|
///
|
/// Check if EndPoint device is Multi-Function Device
|
///
|
if (MmioRead8 (DeviceBase + PCI_HEADER_TYPE_OFFSET) & HEADER_TYPE_MULTI_FUNCTION) {
|
///
|
/// If multi-function Device, check function 0-7
|
///
|
EndPointFunction = PCI_MAX_FUNC;
|
} else {
|
///
|
/// Otherwise, check function 0 only
|
///
|
EndPointFunction = 0;
|
}
|
|
for (FunctionIndex = 0; FunctionIndex <= EndPointFunction; FunctionIndex++) {
|
DeviceBase = MmPciBase (EndPointBus, EndPointDevice, FunctionIndex);
|
if (MmioRead16 (DeviceBase + PCI_VENDOR_ID_OFFSET) != MAX_UINT16) {
|
///
|
/// Get the pointer to the Endpoint PCI Express Capability Structure.
|
///
|
PcieCapOffset = PcieFindCapId (EndPointBus, EndPointDevice, FunctionIndex, EFI_PCI_CAPABILITY_ID_PCIEXP);
|
if (PcieCapOffset == 0) {
|
continue;
|
}
|
|
if (Operation == TRUE) {
|
///
|
/// Set the Max Payload Size of the end point function
|
///
|
MmioAndThenOr16 (
|
DeviceBase + PcieCapOffset + R_PCIE_DCTL_OFFSET,
|
(UINT16) ~(B_PCIE_DCTL_MPS),
|
*MaxPayload << N_PCIE_DCTL_MPS
|
);
|
} else {
|
///
|
/// Get the end point function Max Payload Size support
|
///
|
EndPointMaxPayload = MmioRead16 (DeviceBase + PcieCapOffset + R_PCIE_DCAP_OFFSET) & B_PCIE_DCAP_MPS;
|
///
|
/// Obtain the minimum Max Payload Size between the PCIE root Port and the end point functions
|
///
|
if (*MaxPayload > EndPointMaxPayload) {
|
*MaxPayload = EndPointMaxPayload;
|
}
|
}
|
}
|
}
|
|
return EFI_SUCCESS;
|
}
|
|
/**
|
This function disable the forwarding of EOI messages unless it discovers
|
an IOAPIC behind this root port.
|
|
@param[in] RootBus The Bus Number of the root port
|
@param[in] RootDevice The Device Number of the root port
|
@param[in] RootFunction The Function Number of the root port
|
@param[in] EndPointBus The Bus Number of the Endpoint
|
@param[in] EndPointDevice The Device Number of the Endpoint
|
|
@exception EFI_UNSUPPORTED Unsupported operation.
|
@retval EFI_SUCCESS Successfully completed.
|
**/
|
EFI_STATUS
|
PcieSetEoiFwdDisable (
|
IN UINT8 RootBus,
|
IN UINT8 RootDevice,
|
IN UINT8 RootFunction,
|
IN UINT8 EndPointBus,
|
IN UINT8 EndPointDevice
|
)
|
{
|
BOOLEAN IoApicBehind;
|
UINTN RootDeviceBase;
|
UINTN DeviceBase;
|
UINT8 ProgInterface;
|
UINT8 SubClassCode;
|
UINT8 BaseClassCode;
|
UINT8 FunctionIndex;
|
UINT8 EndPointFunction;
|
UINTN RootPortIndex;
|
EFI_STATUS Status;
|
|
Status = EFI_SUCCESS;
|
IoApicBehind = FALSE;
|
RootDeviceBase = MmPciBase (RootBus, RootDevice, RootFunction);
|
|
///
|
/// Check if an IOAPIC behind the root port
|
///
|
DeviceBase = MmPciBase (EndPointBus, EndPointDevice, 0);
|
///
|
/// Check if EndPoint device is Multi-Function Device
|
///
|
if (MmioRead8 (DeviceBase + PCI_HEADER_TYPE_OFFSET) & HEADER_TYPE_MULTI_FUNCTION) {
|
///
|
/// If multi-function Device, check function 0-7
|
///
|
EndPointFunction = PCI_MAX_FUNC;
|
} else {
|
///
|
/// Otherwise, check function 0 only
|
///
|
EndPointFunction = 0;
|
}
|
|
for (FunctionIndex = 0; FunctionIndex <= EndPointFunction; FunctionIndex++) {
|
DeviceBase = MmPciBase (EndPointBus, EndPointDevice, FunctionIndex);
|
BaseClassCode = MmioRead8 (DeviceBase + PCI_CLASSCODE_OFFSET + 2);
|
SubClassCode = MmioRead8 (DeviceBase + PCI_CLASSCODE_OFFSET + 1);
|
ProgInterface = MmioRead8 (DeviceBase + PCI_CLASSCODE_OFFSET);
|
if ((BaseClassCode == PCI_CLASS_SYSTEM_PERIPHERAL) &&
|
(SubClassCode == PCI_SUBCLASS_PIC) &&
|
((ProgInterface == PCI_IF_APIC_CONTROLLER) ||
|
(ProgInterface == PCI_IF_APIC_CONTROLLER2))) {
|
IoApicBehind = TRUE;
|
}
|
}
|
///
|
/// PCH BIOS Spec Section 8.15 Additional PCI Express* Programming Steps
|
/// Step 27
|
/// If there is no IOAPIC behind the root port, set EOI Forwarding Disable bit (PCIE RP PCI offset D4h[1]) to 1b.
|
///
|
if (IoApicBehind == FALSE) {
|
MmioOr32 (RootDeviceBase + R_PCH_PCIE_MPC2, B_PCH_PCIE_MPC2_EOIFD);
|
} else {
|
///
|
/// 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
|
///
|
///
|
///
|
Status = GetPchPcieRpNumber ((UINTN) RootDevice, (UINTN) RootFunction, &RootPortIndex);
|
ASSERT_EFI_ERROR (Status);
|
|
PsfConfigurEoiForPciePort ((UINT32)RootPortIndex);
|
}
|
|
return Status;
|
}
|
|
typedef enum {
|
CalculateAspm,
|
ManualAspm,
|
SetAspm
|
} OPERATION;
|
|
/**
|
This function compares the actual latency in LatencyValue1
|
with actual latency in LatencyValue2 and stores the minimum
|
back to LatencyValue1, in the required format.
|
If this is the first call, then LatencyValue1 will be replaced by LatencyValue2.
|
|
@param[in, out] LatencyValue1 - Current latency value
|
@param[in] LatencyValue2 - Latency value from the Table
|
|
@retval None
|
**/
|
VOID
|
DetermineLatencyValue (
|
IN OUT UINT16 *LatencyValue1,
|
IN UINT16 LatencyValue2
|
)
|
{
|
ASSERT (LTR_SCALE_VALUE (*LatencyValue1) < 6);
|
ASSERT (LTR_SCALE_VALUE (LatencyValue2) < 6);
|
///
|
/// If there are more than one device behind a bridge that are part of the override table,
|
/// store the lower latency value and corresponding scale bits back to LatencyValue1
|
///
|
if ((LTR_LATENCY_NS (*LatencyValue1) == 0) || (LTR_LATENCY_NS (*LatencyValue1) > LTR_LATENCY_NS (LatencyValue2))) {
|
*LatencyValue1 = LatencyValue2;
|
}
|
}
|
|
/**
|
This function checks exit latency of L1 and L0s and disables the ASPM state if it is longer than
|
the acceptable latency
|
|
@param[in] EndPointBase End Point Base Address
|
@param[in] EndPointPcieCapOffset The pointer to the End Point PCI Express Capability Structure
|
@param[in] RootDeviceBase The Root Port PCI Express Base address
|
@param[in] RootPcieCapOffset The pointer to the Root Port PCI Express Capability Structure
|
@param[in, out] EndPointAspm End Point ASPM setting
|
|
@retval none
|
**/
|
VOID
|
AspmCheckExitLatency (
|
IN UINTN EndPointBase,
|
IN UINT8 EndPointPcieCapOffset,
|
IN UINTN RootDeviceBase,
|
IN UINT32 RootPcieCapOffset,
|
IN OUT UINT16* EndPointAspm
|
)
|
{
|
|
UINT32 PortLxLat;
|
UINT32 EndPointLxLat;
|
UINT32 LxLat;
|
|
///
|
/// Check if L1 should be enabled based on port and endpoint L1 exit latency.
|
///
|
if ((*EndPointAspm) & BIT1) {
|
PortLxLat = MmioRead32 (RootDeviceBase + RootPcieCapOffset + R_PCIE_LCAP_OFFSET) & B_PCIE_LCAP_EL1;
|
EndPointLxLat = MmioRead32 (EndPointBase + EndPointPcieCapOffset + R_PCIE_LCAP_OFFSET) & B_PCIE_LCAP_EL1;
|
|
LxLat = PortLxLat;
|
if (PortLxLat < EndPointLxLat) {
|
LxLat = EndPointLxLat;
|
}
|
///
|
/// check if the value is bigger than endpoint L1 acceptable exit latency, if it is
|
/// larger than accepted value, then we should disable L1
|
///
|
LxLat >>= N_PCIE_LCAP_EL1;
|
if (LxLat > ((MmioRead32 (EndPointBase + EndPointPcieCapOffset + R_PCIE_DCAP_OFFSET) & B_PCIE_DCAP_E1AL) >> N_PCIE_DCAP_E1AL)) {
|
(*EndPointAspm) &= ~BIT1;
|
}
|
}
|
///
|
/// Check if L0s should be enabled based on port and endpoint L0s exit latency.
|
///
|
if ((*EndPointAspm) & BIT0) {
|
PortLxLat = MmioRead32 (RootDeviceBase + RootPcieCapOffset + R_PCIE_LCAP_OFFSET) & B_PCIE_LCAP_EL0;
|
EndPointLxLat = MmioRead32 (EndPointBase + EndPointPcieCapOffset + R_PCIE_LCAP_OFFSET) & B_PCIE_LCAP_EL0;
|
|
LxLat = PortLxLat;
|
if (PortLxLat < EndPointLxLat) {
|
LxLat = EndPointLxLat;
|
}
|
///
|
/// check if the value is bigger than endpoint L0s acceptable exit latency, if it is
|
/// larger than accepted value, then we should disable L0s
|
///
|
LxLat >>= N_PCIE_LCAP_EL0;
|
if (LxLat > ((MmioRead32 (EndPointBase + EndPointPcieCapOffset + R_PCIE_DCAP_OFFSET) & B_PCIE_DCAP_E0AL) >> N_PCIE_DCAP_E0AL)) {
|
(*EndPointAspm) &= ~BIT0;
|
}
|
}
|
return;
|
}
|
/**
|
This function gets override Aspm values if the end point is found in the override look up table
|
|
@param[in] EndPointBase End Point Base Address
|
@param[in] NumOfDevAspmOverride Number of Device specific ASPM policy override items
|
@param[in] DevAspmOverride Pointer to array of Device specific ASPM policy override items
|
@param[in] EndPointVendorId End Point Vendor Id
|
@param[in] EndPointDeviceId End Point Device Id
|
@param[in] EndPointRevId End Point Revision Id
|
@param[in, out] EndPointAspm End Point ASPM setting
|
|
@retval none
|
**/
|
|
VOID
|
GetOverrideAspm (
|
IN UINTN EndPointBase,
|
IN UINT32 NumOfDevAspmOverride,
|
IN const PCH_PCIE_DEVICE_OVERRIDE* DevAspmOverride,
|
IN UINT16 EndPointVendorId,
|
IN UINT16 EndPointDeviceId,
|
IN UINT8 EndPointRevId,
|
IN OUT UINT16 *EndPointAspm
|
)
|
{
|
UINT8 EndPointBaseClassCode;
|
UINT8 EndPointSubClassCode;
|
UINT8 PcieDeviceIndex;
|
///
|
/// Mask APMC with values from lookup table.
|
/// RevID of 0xFF applies to all steppings.
|
///
|
EndPointBaseClassCode = MmioRead8 (EndPointBase + R_PCI_BCC_OFFSET);
|
EndPointSubClassCode = MmioRead8 (EndPointBase + R_PCI_SCC_OFFSET);
|
for (PcieDeviceIndex = 0; PcieDeviceIndex < NumOfDevAspmOverride; PcieDeviceIndex++) {
|
if (((DevAspmOverride[PcieDeviceIndex].OverrideConfig & PchPcieL1L2Override) == PchPcieL1L2Override) &&
|
((DevAspmOverride[PcieDeviceIndex].VendorId == EndPointVendorId) ||
|
(DevAspmOverride[PcieDeviceIndex].VendorId == 0xFFFF)) &&
|
((DevAspmOverride[PcieDeviceIndex].DeviceId == EndPointDeviceId) ||
|
(DevAspmOverride[PcieDeviceIndex].DeviceId == 0xFFFF)) &&
|
((DevAspmOverride[PcieDeviceIndex].RevId == EndPointRevId) ||
|
(DevAspmOverride[PcieDeviceIndex].RevId == 0xFF)) &&
|
((DevAspmOverride[PcieDeviceIndex].BaseClassCode == EndPointBaseClassCode) ||
|
(DevAspmOverride[PcieDeviceIndex].BaseClassCode == 0xFF)) &&
|
((DevAspmOverride[PcieDeviceIndex].SubClassCode == EndPointSubClassCode) ||
|
(DevAspmOverride[PcieDeviceIndex].SubClassCode == 0xFF))) {
|
///
|
/// Override value of 0xFF applies to all.
|
///
|
*EndPointAspm = DevAspmOverride[PcieDeviceIndex].EndPointAspm;
|
break;
|
}
|
}
|
}
|
|
/**
|
This function gets override L1 Substate Capability offset pointer
|
if the end point is found in the override look up table
|
|
@param[in] EndPointBase End Point Base Address
|
@param[in] NumOfDevAspmOverride Number of Device specific ASPM policy override items
|
@param[in] DevAspmOverride Pointer to array of Device specific ASPM policy override items
|
@param[in] EndPointVendorId End Point Vendor Id
|
@param[in] EndPointDeviceId End Point Device Id
|
@param[in] EndPointRevId End Point Revision Id
|
@param[out] EndPointL1SubStateCapOffset Pointer to L1 Substate Capability Structure
|
@param[out] EndPointL1SubStateCapMask L1 Substate Capability Mask
|
@param[out] EndPointL1sCommonModeRestoreTime L1 Substate Port Common Mode Restore Time
|
@param[out] EndPointL1sTpowerOnScale L1 Substate Port Tpower_on Scale
|
@param[out] EndPointL1sTpowerOnValue L1 Substate Port Tpower_on Value
|
|
@retval none
|
**/
|
|
VOID
|
GetOverrideL1sCapOffsetAndValue (
|
IN UINTN EndPointBase,
|
IN UINT32 NumOfDevAspmOverride,
|
IN const PCH_PCIE_DEVICE_OVERRIDE* DevAspmOverride,
|
IN UINT16 EndPointVendorId,
|
IN UINT16 EndPointDeviceId,
|
IN UINT8 EndPointRevId,
|
OUT UINT16 *EndPointL1SubStateCapOffset,
|
OUT UINT8 *EndPointL1SubStateCapMask,
|
OUT UINT8 *EndPointL1sCommonModeRestoreTime,
|
OUT UINT8 *EndPointL1sTpowerOnScale,
|
OUT UINT8 *EndPointL1sTpowerOnValue
|
)
|
{
|
UINT8 PcieDeviceIndex;
|
///
|
/// Get the endpoint supports L1 Substates Capabilities from Override Table
|
///
|
for (PcieDeviceIndex = 0; PcieDeviceIndex < NumOfDevAspmOverride; PcieDeviceIndex++) {
|
if (((DevAspmOverride[PcieDeviceIndex].OverrideConfig & PchPcieL1SubstatesOverride) == PchPcieL1SubstatesOverride) &&
|
(EndPointVendorId == DevAspmOverride[PcieDeviceIndex].VendorId) &&
|
(EndPointDeviceId == DevAspmOverride[PcieDeviceIndex].DeviceId) &&
|
((EndPointRevId == DevAspmOverride[PcieDeviceIndex].RevId) ||
|
(DevAspmOverride[PcieDeviceIndex].RevId == 0xFF))) {
|
if ((EndPointVendorId == V_PCH_INTEL_VENDOR_ID) &&
|
((EndPointDeviceId == 0x08B1) || (EndPointDeviceId == 0x08B2) ||
|
(EndPointDeviceId == 0x08B3) || (EndPointDeviceId == 0x08B4))
|
&& ((MmioRead32 (EndPointBase + DevAspmOverride[PcieDeviceIndex].L1SubstatesCapOffset) & 0xFFFF) != 0xCAFE)) {
|
continue;
|
}
|
*EndPointL1SubStateCapOffset = DevAspmOverride[PcieDeviceIndex].L1SubstatesCapOffset;
|
*EndPointL1SubStateCapMask = DevAspmOverride[PcieDeviceIndex].L1SubstatesCapMask & 0x0F;
|
*EndPointL1sCommonModeRestoreTime = DevAspmOverride[PcieDeviceIndex].L1sCommonModeRestoreTime & 0xFF;
|
*EndPointL1sTpowerOnScale = DevAspmOverride[PcieDeviceIndex].L1sTpowerOnScale & 0x03;
|
*EndPointL1sTpowerOnValue = DevAspmOverride[PcieDeviceIndex].L1sTpowerOnValue & 0x1F;
|
|
break;
|
}
|
}
|
}
|
|
/**
|
This function configures the L1 Substates.
|
It can be used for Rootport and endpoint devices.
|
|
@param[in] DownstreamPort Indicates if the device about to be programmed is a downstream port
|
@param[in] DeviceBase Device PCI configuration base address
|
@param[in] L1SubstateExtCapOffset Pointer to L1 Substate Capability Structure
|
@param[in] PortL1SubstateCapSupport L1 Substate capability setting
|
@param[in] PortCommonModeRestoreTime Common Mode Restore Time
|
@param[in] PortTpowerOnValue Tpower_on Power On Wait Time
|
@param[in] PortTpowerOnScale Tpower-on Scale
|
|
@retval none
|
**/
|
VOID
|
ConfigureL1s (
|
IN BOOLEAN DownstreamPort,
|
IN UINTN DeviceBase,
|
IN UINT16 L1SubstateExtCapOffset,
|
IN UINT32 PortL1SubstateCapSupport,
|
IN UINT32 PortCommonModeRestoreTime,
|
IN UINT32 PortTpowerOnValue,
|
IN UINT32 PortTpowerOnScale
|
)
|
{
|
//
|
// For downstream port only:
|
// Program Common Mode Restore Time, but disable L1.2 first
|
//
|
if (DownstreamPort) {
|
MmioAnd32 (
|
DeviceBase + L1SubstateExtCapOffset + R_PCIE_EX_L1SCTL1_OFFSET,
|
(UINT32) ~(BIT2 | BIT0)
|
);
|
|
///
|
/// b. Read L1 Sub-States Extended Capability Offset + 0x04[15:8], and Set the highest value advertised
|
/// between PCIe rootport and device to L1 Sub-States Extended Capability Offset + 0x08[15:8] on both
|
/// Pcie root port and device.
|
///
|
MmioAndThenOr32 (
|
DeviceBase + L1SubstateExtCapOffset + R_PCIE_EX_L1SCTL1_OFFSET,
|
(UINT32) ~(0xFF00),
|
(UINT32) PortCommonModeRestoreTime << 8
|
);
|
}
|
|
///
|
/// PCH BIOS Spec Section 8.3 ASPM on DMI and the PCI Express* Root Ports
|
/// Step 6
|
/// a. Read L1 Sub-States Extended Capability Offset + 0x04[3:0], and set the smallest common subset supported
|
/// by both PCIe rootport and device to L1 Sub-States Extended Capability Offset + 0x08[3:0]
|
///
|
MmioAndThenOr32 (
|
DeviceBase + L1SubstateExtCapOffset + R_PCIE_EX_L1SCTL1_OFFSET,
|
(UINT32) ~(BIT3 | BIT2 | BIT1 | BIT0),
|
PortL1SubstateCapSupport
|
);
|
///
|
/// c. Read L1 Sub-States Extended Capability Offset + 0x04[23:19] and [17:16], and Set the highest value
|
/// advertised between PCIe root port and device to L1 Sub-States Extended Capability Offset + 0x0C [7:0] on
|
/// both Pcie root port and device.
|
///
|
MmioAndThenOr32 (
|
DeviceBase + L1SubstateExtCapOffset + R_PCIE_EX_L1SCTL2_OFFSET,
|
0xFFFFFF04,
|
(UINT32) ((PortTpowerOnValue << N_PCIE_EX_L1SCTL2_POWT) | PortTpowerOnScale)
|
);
|
///
|
/// d. Set L1 Sub-States Extended Capability Offset + 0x08[31:29] to 010b for both Pcie root port and device
|
/// e. Set L1 Sub-States Extended Capability Offset + 0x08[25:16] to 0010100000b for both Pcie root port and device
|
///
|
|
MmioAndThenOr32 (
|
DeviceBase + L1SubstateExtCapOffset + R_PCIE_EX_L1SCTL1_OFFSET,
|
(UINT32) ~(0xE3FF0000),
|
(UINT32) (BIT30 | BIT23 | BIT21)
|
);
|
}
|
|
/**
|
This function gets the Latency Tolerance Reporting settings from override table
|
if the end point is found in the override look up table
|
|
@param[in] NumOfDevAspmOverride Number of Device specific ASPM policy override items
|
@param[in] DevAspmOverride Pointer to array of Device specific ASPM policy override items
|
@param[in] EndPointVendorId End Point Vendor Id
|
@param[in] EndPointDeviceId End Point Device Id
|
@param[in] EndPointRevId End Point Revision Id
|
@param[in, out] LtrOverrideVal Snoop and Non Snoop Latency Values
|
@param[out] ForceLtrOverride This endpoint needs LTR to be permanently overridden
|
|
@retval none
|
**/
|
VOID
|
GetLtrOverride (
|
IN UINT32 NumOfDevAspmOverride,
|
IN const PCH_PCIE_DEVICE_OVERRIDE* DevAspmOverride,
|
IN UINT16 EndPointVendorId,
|
IN UINT16 EndPointDeviceId,
|
IN UINT8 EndPointRevId,
|
IN OUT UINT32* LtrOverrideVal,
|
OUT UINT8* ForceLtrOverride
|
)
|
{
|
UINT8 PcieDeviceIndex;
|
UINT16 Data16;
|
UINT32 Data32;
|
///
|
/// For each device detected, scan the LTR override table
|
/// If there are endpoints connected directly to the rootport then
|
/// LtrOverrideVal will be replaced by the value from the table for that endpoint
|
/// If there are endpoints that are behind a bridge and that are also part of the table then
|
/// LtrOverrideVal will maintain the minimum of all such values.
|
/// A non zero value of LtrOverrideVal will indicate:
|
/// i):That there is atleast one entry in the LTR override Table
|
/// ii):The final value to be programmed in offset 0x400. This value will be applied for all the devices
|
/// connected to this root port
|
///
|
Data32 = *LtrOverrideVal;
|
for (PcieDeviceIndex = 0; PcieDeviceIndex < NumOfDevAspmOverride; PcieDeviceIndex++) {
|
if (((DevAspmOverride[PcieDeviceIndex].OverrideConfig & PchPcieLtrOverride) == PchPcieLtrOverride) &&
|
(DevAspmOverride[PcieDeviceIndex].VendorId == EndPointVendorId) &&
|
((DevAspmOverride[PcieDeviceIndex].DeviceId == EndPointDeviceId) ||
|
(DevAspmOverride[PcieDeviceIndex].DeviceId == 0xFFFF)) &&
|
((DevAspmOverride[PcieDeviceIndex].RevId == EndPointRevId) ||
|
(DevAspmOverride[PcieDeviceIndex].RevId == 0xFF))) {
|
///
|
/// Get the Non-Snoop latency value from the table, compare and store the minimum
|
///
|
if (DevAspmOverride[PcieDeviceIndex].NonSnoopLatency & BIT15) {
|
Data16 = (UINT16) ((Data32 & 0xFFFF0000) >> 16);
|
DetermineLatencyValue (
|
&Data16,
|
DevAspmOverride[PcieDeviceIndex].NonSnoopLatency);
|
Data32 = (Data32 & 0xFFFF) | ((UINT32) (Data16 << 16));
|
}
|
///
|
/// Get the Snoop latency value from the table, compare and store the minimum
|
///
|
if (DevAspmOverride[PcieDeviceIndex].SnoopLatency & BIT15) {
|
Data16 = (UINT16) (Data32 & 0xFFFF);
|
DetermineLatencyValue (
|
&Data16,
|
DevAspmOverride[PcieDeviceIndex].SnoopLatency);
|
Data32 = (Data32 & 0xFFFF0000) | (UINT32) Data16;
|
}
|
*LtrOverrideVal = Data32;
|
//
|
// Get the ForceLtrOverride value from table
|
//
|
if (DevAspmOverride[PcieDeviceIndex].ForceLtrOverride == TRUE) {
|
*ForceLtrOverride = TRUE;
|
}
|
break;
|
}
|
}
|
return;
|
}
|
|
|
/**
|
This function configures the Latency Tolerance Reporting Settings for endpoint devices
|
|
@param[in] RootPortConfig Rootport PCI Express Configuration
|
@param[in] EndPointBus Endpoint Bus Number
|
@param[in] EndPointDevice Endpoint Device Number
|
@param[in] EndPointFunction Endpoint Function Number
|
@param[in] EndPointBase Endpoint PCI Express Address
|
@param[in] EndPointPcieCapOffset Pointer to Endpoint PCI Express Capability Structure
|
@param[in] DeviceCapabilities2 Endpoint Value of Device Capabilities 2 Register (PciE Cap offset + 0x24)
|
@param[in] PchSeries Pch Series
|
@param[in] LtrOverrideVal Snoop and Non Snoop Latency Values
|
|
@retval none
|
**/
|
VOID
|
ConfigureLtr (
|
IN CONST PCH_PCIE_ROOT_PORT_CONFIG* RootPortConfig,
|
IN UINT8 EndPointBus,
|
IN UINT8 EndPointDevice,
|
IN UINT8 EndPointFunction,
|
IN UINTN EndPointBase,
|
IN UINT8 EndPointPcieCapOffset,
|
IN UINT32 DeviceCapabilities2,
|
IN PCH_SERIES PchSeries,
|
IN UINT32* LtrOverrideVal
|
)
|
{
|
UINT32 Data32;
|
UINT16 Data16;
|
UINT16 LtrExtendedCapOffset;
|
UINT16 DefaultMaxLatency;
|
DefaultMaxLatency = 0;
|
///
|
/// PCH BIOS Spec Section 8.15.1 Power Optimizer Configuration
|
/// Step 3
|
/// If Endpoint device supports LTR, Device Capabilities 2 Register Offset 24h [11] = 1b,
|
///
|
if ((DeviceCapabilities2 & B_PCIE_DCAP2_LTRMS) && (RootPortConfig->LtrEnable == TRUE)) {
|
///
|
/// PCH BIOS Spec Section 8.15.1 Power Optimizer Configuration
|
/// Step 3.1
|
/// Program Endpoint LTR Mechanism Enable, Device Control 2 Register Offset 28h [10] = 1b
|
/// when device supports LTR but is not found in override table (table listing correct
|
/// latency requirements for devices that supports LTR and also for devices that do not
|
/// support LTR).
|
///
|
MmioOr16 (EndPointBase + EndPointPcieCapOffset + R_PCIE_DCTL2_OFFSET, B_PCIE_DCTL2_LTREN);
|
}
|
///
|
/// Get the pointer to the Endpoint PCI Express Extended Capability Structure
|
/// and configure the Max Snoop and Max No-Snoop Latency for the endpoint
|
///
|
LtrExtendedCapOffset = PcieFindExtendedCapId (EndPointBus,
|
EndPointDevice,
|
EndPointFunction,
|
R_PCH_PCIE_LTRECH_CID);
|
if (LtrExtendedCapOffset != 0) {
|
Data32 = *LtrOverrideVal;
|
///
|
/// PCH BIOS Spec Section 8.14.1 Power Optimizer Configuration
|
/// Step 3.2
|
/// If B0:Dxx:Fn + 400h is not programmed with snoop latency override value,
|
/// program endpoint max snoop latency register, Latency Tolerance Reporting (LTR)
|
/// Capability Offset 04h [15:0] = 1003h
|
|
DefaultMaxLatency = RootPortConfig->LtrMaxSnoopLatency;
|
Data16 = (UINT16) (Data32 & 0xFFFF);
|
///
|
/// Set the max snoop latency to either the default max snoop latency or to the snoop latency override value
|
/// that is being programmed for this root port
|
///
|
DetermineLatencyValue (&Data16, DefaultMaxLatency);
|
MmioAndThenOr16 (EndPointBase + LtrExtendedCapOffset + R_PCH_PCIE_LTRECH_MSLR_OFFSET,
|
(UINT16) (~0x1FFF),
|
Data16);
|
///
|
/// PCH BIOS Spec Section 8.14.1 Power Optimizer Configuration
|
/// Step 3.3
|
/// If B0:Dxx:Fn + 400h is not programmed with non-snoop latency override value,
|
/// program endpoint max non-snoop Latency Register, Latency Tolerance Reporting (LTR)
|
/// Capability Offset 06h [15:0] = 1003h
|
|
DefaultMaxLatency = RootPortConfig->LtrMaxNoSnoopLatency;
|
Data16 = (UINT16) ((Data32 & 0xFFFF0000) >> 16);
|
DetermineLatencyValue (&Data16, DefaultMaxLatency);
|
MmioAndThenOr16 (
|
EndPointBase + LtrExtendedCapOffset
|
+ R_PCH_PCIE_LTRECH_MNSLR_OFFSET,
|
(UINT16) (~0x1FFF),
|
Data16);
|
///
|
/// Step 4
|
/// If not all devices support LTR
|
/// Program PWRMBASE + 20h = 00010003h
|
/// (Note this register should be saved and restored during S3 transitions)
|
/// Done in PchPcieSmm.c PchPciePmIoTrapSmiCallback ()
|
///
|
}
|
}
|
|
/**
|
Calculate/Set EndPoint device Power management settings
|
|
@param[in] RootDeviceBase The Root Port PCI Express address
|
@param[in] RootPcieCapOffset The pointer to the Root Port PCI Express Capability Structure
|
@param[in] EndPointBus The Bus Number of the Endpoint
|
@param[in] NumOfDevAspmOverride Number of Device specific ASPM policy override items
|
@param[in] DevAspmOverride Pointer to array of Device specific ASPM policy override items
|
@param[in, out] LinkAspmVal Resulting Link ASPM value programmed
|
@param[in] Operation Operation Types
|
@param[in, out] LtrOverrideVal Resulting LTR override value to be programmed
|
@param[in] RootL1SubstateExtCapOffset The register offset of Root Port L1 Substates
|
@param[in, out] L1SubstatesSupported Input and return the result of L1 Substates support
|
@param[in, out] PortL1SubstateCapSupport Input and return Capability of L1 Substate setting
|
@param[in, out] PortCommonModeRestoreTime Input and return common mode restore time of L1 Substate setting
|
@param[in, out] PortTpowerOnValue Input and return power on value of L1 Substate setting
|
@param[in, out] PortTpowerOnScale Input and return power on scale of L1 Substate setting
|
@param[in] RootPortConfig Pcie Power Optimizer Configuration
|
@param[in, out] AspmOverride Input and return the Aspm Override enable for pre-1.1 devices
|
@param[in, out] ClkreqPerPortSupported Input to check if clkreq per port is supportted
|
@param[in, out] RpAndEndPointsLtrSupported Input to check if LTR per port is supportted
|
@param[out] ForceLtrOverride One of endpoints needs LTR to be permanently overridden
|
|
@retval EFI_SUCCESS Successfully completed
|
@retval EFI_NOT_FOUND Can not find device
|
|
@retval EFI_OUT_OF_RESOURCES The endpoint device is a bridge, but the Subordinate Bus Number of
|
the root port is not greater than its Secondary Bus Number. You may
|
get this error if PCI emulation is not done before this function gets
|
called and the Policy settings of "TempRootPortBusNumMax" and
|
"TempRootPortBusNumMin" do not provide enough resource for temp bus
|
number usage.
|
**/
|
EFI_STATUS
|
PcieEndPointPm (
|
IN UINTN RootDeviceBase,
|
IN UINT32 RootPcieCapOffset,
|
IN UINT8 EndPointBus,
|
IN UINT32 NumOfDevAspmOverride,
|
IN CONST PCH_PCIE_DEVICE_OVERRIDE *DevAspmOverride,
|
IN OUT UINT16 *LinkAspmVal,
|
IN OPERATION Operation,
|
IN OUT UINT32 *LtrOverrideVal,
|
IN UINT16 RootL1SubstateExtCapOffset,
|
IN OUT BOOLEAN *L1SubstatesSupported,
|
IN OUT UINT32 *PortL1SubstateCapSupport,
|
IN OUT UINT32 *PortCommonModeRestoreTime,
|
IN OUT UINT32 *PortTpowerOnValue,
|
IN OUT UINT32 *PortTpowerOnScale,
|
IN CONST PCH_PCIE_ROOT_PORT_CONFIG *RootPortConfig,
|
IN OUT BOOLEAN *AspmOverride,
|
IN BOOLEAN *ClkreqPerPortSupported,
|
IN OUT BOOLEAN *RpAndEndPointsLtrSupported,
|
OUT UINT8 *ForceLtrOverride
|
)
|
{
|
EFI_STATUS Status;
|
UINTN EndPointBase;
|
UINT8 EndPointFunction;
|
UINT8 EndPointPcieCapOffset;
|
UINT16 EndPointAspm;
|
UINT16 EndPointVendorId;
|
UINT16 EndPointDeviceId;
|
UINT8 EndPointRevId;
|
UINT8 DownStreamBusMin;
|
UINT8 ClassCode;
|
UINT8 RootDevSubBusNum;
|
BOOLEAN BusAssign;
|
UINT8 DeviceIndex;
|
UINT8 FunctionIndex;
|
UINT32 DeviceCapabilities2;
|
UINT16 EndPointL1SubStateCapOffset;
|
UINT32 EndPointL1Substates;
|
UINT8 EndPointL1sCommonModeRestoreTime;
|
UINT8 EndPointL1sTpowerOnScale;
|
UINT8 EndPointL1sTpowerOnValue;
|
UINT8 EndPointL1SubStateCapMask;
|
PCH_SERIES PchSeries;
|
BOOLEAN DownstreamPort;
|
UINT16 PcieEndCapOffset;
|
UINT16 PcieEndDeviceType;
|
|
PchSeries = GetPchSeries ();
|
|
DEBUG ((DEBUG_INFO, "PcieEndPointPm () Start EndPointBus %0x\n", EndPointBus));
|
for (DeviceIndex = 0; DeviceIndex <= PCI_MAX_DEVICE; DeviceIndex++) {
|
EndPointBase = MmPciBase (EndPointBus, DeviceIndex, 0);
|
if (MmioRead16 (EndPointBase + PCI_VENDOR_ID_OFFSET) == MAX_UINT16) {
|
continue;
|
}
|
///
|
/// Check if EndPoint device is Multi-Function Device
|
///
|
if (MmioRead8 (EndPointBase + PCI_HEADER_TYPE_OFFSET) & HEADER_TYPE_MULTI_FUNCTION) {
|
///
|
/// If multi-function Device, check function 0-7
|
///
|
EndPointFunction = PCI_MAX_FUNC;
|
} else {
|
///
|
/// Otherwise, check function 0 only
|
///
|
EndPointFunction = 0;
|
}
|
|
for (FunctionIndex = 0; FunctionIndex <= EndPointFunction; FunctionIndex++) {
|
EndPointBase = MmPciBase (EndPointBus, DeviceIndex, FunctionIndex);
|
if (MmioRead16 (EndPointBase + PCI_VENDOR_ID_OFFSET) == MAX_UINT16) {
|
continue;
|
}
|
|
//
|
// Get the pointer to the Endpoint PCI Express Capability Structure.
|
//
|
EndPointPcieCapOffset = PcieFindCapId (EndPointBus, DeviceIndex, FunctionIndex, EFI_PCI_CAPABILITY_ID_PCIEXP);
|
if (EndPointPcieCapOffset == 0) {
|
return EFI_NOT_FOUND;
|
}
|
EndPointVendorId = MmioRead16 (EndPointBase + PCI_VENDOR_ID_OFFSET);
|
EndPointDeviceId = MmioRead16 (EndPointBase + PCI_DEVICE_ID_OFFSET);
|
EndPointRevId = MmioRead8 (EndPointBase + PCI_REVISION_ID_OFFSET);
|
|
EndPointL1SubStateCapOffset = 0;
|
EndPointL1SubStateCapMask = 0;
|
EndPointL1sCommonModeRestoreTime = 0;
|
EndPointL1sTpowerOnScale = 0;
|
EndPointL1sTpowerOnValue = 0;
|
EndPointL1Substates = 0;
|
|
if (RootL1SubstateExtCapOffset != 0) {
|
///
|
/// Get the endpoint supports L1 Substates Capabilities
|
///
|
GetOverrideL1sCapOffsetAndValue (
|
EndPointBase,
|
NumOfDevAspmOverride,
|
DevAspmOverride,
|
EndPointVendorId,
|
EndPointDeviceId,
|
EndPointRevId,
|
&EndPointL1SubStateCapOffset,
|
&EndPointL1SubStateCapMask,
|
&EndPointL1sCommonModeRestoreTime,
|
&EndPointL1sTpowerOnScale,
|
&EndPointL1sTpowerOnValue
|
);
|
|
if (EndPointL1SubStateCapOffset == 0) {
|
EndPointL1SubStateCapOffset = PcieFindExtendedCapId (
|
EndPointBus,
|
DeviceIndex,
|
FunctionIndex,
|
V_PCIE_EX_L1S_CID
|
);
|
}
|
if (EndPointL1SubStateCapOffset != 0) {
|
EndPointL1Substates = MmioRead32 (EndPointBase + EndPointL1SubStateCapOffset + R_PCIE_EX_L1SCAP_OFFSET);
|
}
|
}
|
|
DeviceCapabilities2 = MmioRead32 (EndPointBase + EndPointPcieCapOffset + R_PCIE_DCAP2_OFFSET);
|
if (((DeviceCapabilities2 & B_PCIE_DCAP2_LTRMS) == 0) || (RootPortConfig->LtrEnable != TRUE)) {
|
*RpAndEndPointsLtrSupported = FALSE;
|
}
|
///
|
/// Configure downstream device if present.
|
///
|
if (Operation == CalculateAspm || Operation == ManualAspm) {
|
if ((MmioRead32 (EndPointBase + EndPointPcieCapOffset + R_PCIE_LCAP_OFFSET) & B_PCIE_LCAP_CPM) != B_PCIE_LCAP_CPM) {
|
*ClkreqPerPortSupported = FALSE;
|
}
|
EndPointAspm = (MmioRead16 (EndPointBase + EndPointPcieCapOffset + R_PCIE_LCAP_OFFSET) >> N_PCIE_LCAP_APMS) & 3;
|
DEBUG ((DEBUG_INFO, "Endpoint Device %0x Capability ASPM: %0x\n", DeviceIndex, EndPointAspm));
|
if (Operation == CalculateAspm) {
|
///
|
/// Check endpoint for pre-1.1 devices based on the Role based Error Reporting Capability bit
|
/// and enable Aspm Override
|
///
|
if (!(MmioRead16 (EndPointBase + EndPointPcieCapOffset + R_PCIE_DCAP_OFFSET) & BIT15)) {
|
DEBUG ((DEBUG_INFO, "Override root port ASPM to L1 for pre-1.1 devices\n"));
|
*AspmOverride = TRUE;
|
}
|
GetOverrideAspm (
|
EndPointBase,
|
NumOfDevAspmOverride,
|
DevAspmOverride,
|
EndPointVendorId,
|
EndPointDeviceId,
|
EndPointRevId,
|
&EndPointAspm
|
);
|
AspmCheckExitLatency (
|
EndPointBase,
|
EndPointPcieCapOffset,
|
RootDeviceBase,
|
RootPcieCapOffset,
|
&EndPointAspm
|
);
|
}
|
|
*LinkAspmVal &= EndPointAspm;
|
DEBUG ((DEBUG_INFO, "Calculate Endpoint Device %0x Aspm Value: %0x\n", DeviceIndex, EndPointAspm));
|
///
|
/// Check if the endpoint supports L1 Substates Capabilities
|
///
|
if ((EndPointL1SubStateCapOffset != 0) && (RootL1SubstateExtCapOffset != 0)) {
|
///
|
/// a. Read L1 Sub-States Extended Capability Offset + 0x04[15:8], and program the highest value advertised
|
/// between PCIe rootport and device to L1 Sub-States Extended Capability Offset + 0x08[15:8] on
|
/// Pcie root port.
|
/// b. Read L1 Sub-States Extended Capability Offset + 0x04[23:19] and [17:16], and program the highest value
|
/// advertised between PCIe root port and device.to L1 Sub-States Extended Capability Offset + 0x08 [7:0] on
|
/// both Pcie root port and device.
|
/// c. Program L1 Sub-States Extended Capability Offset + 0x08[31:29] to 010b for both Pcie root port and device
|
/// d. Program L1 Sub-States Extended Capability Offset + 0x08[25:16] to 0010100000b for both Pcie root port and device
|
/// e. Program the L1 PM Sub-States Control 1 Register bits 3:0 to the smallest common subset supported by both sides.
|
///
|
if (((UINT8) EndPointL1Substates & EndPointL1SubStateCapMask) == EndPointL1SubStateCapMask) {
|
*L1SubstatesSupported = TRUE;
|
if (EndPointL1sTpowerOnValue == 0) {
|
EndPointL1sCommonModeRestoreTime = (EndPointL1Substates >> 8) & 0xFF;
|
EndPointL1sTpowerOnScale = (EndPointL1Substates >> 16) & 0x3;
|
EndPointL1sTpowerOnValue = (EndPointL1Substates >> 19) & 0x1F;
|
}
|
|
if (EndPointL1SubStateCapMask == 0) {
|
*PortL1SubstateCapSupport &= (UINT32) (EndPointL1Substates & 0x0F);
|
} else {
|
*PortL1SubstateCapSupport &= (UINT32) EndPointL1SubStateCapMask;
|
}
|
|
if (EndPointL1sCommonModeRestoreTime > *PortCommonModeRestoreTime) {
|
*PortCommonModeRestoreTime = EndPointL1sCommonModeRestoreTime;
|
}
|
|
if (((UINT32) EndPointL1sTpowerOnValue * mPcieL1sTposMultiplier[EndPointL1sTpowerOnScale]) >
|
(*PortTpowerOnValue * mPcieL1sTposMultiplier[*PortTpowerOnScale])) {
|
*PortTpowerOnValue = EndPointL1sTpowerOnValue;
|
*PortTpowerOnScale = EndPointL1sTpowerOnScale;
|
}
|
}
|
}
|
///
|
/// For each device detected, scan the LTR override table
|
/// If there are endpoints connected directly to the rootport then
|
/// LtrOverrideVal will be replaced by the value from the table for that endpoint
|
/// If there are endpoints that are behind a bridge and that are also part of the table then
|
/// LtrOverrideVal will maintain the minimum of all such values.
|
/// A non zero value of LtrOverrideVal will indicate:
|
/// i):That there is atleast one entry in the LTR override Table
|
/// ii):The final value to be programmed in offset 0x400. This value will be applied for all the devices
|
/// connected to this root port
|
///
|
GetLtrOverride (
|
NumOfDevAspmOverride,
|
DevAspmOverride,
|
EndPointVendorId,
|
EndPointDeviceId,
|
EndPointRevId,
|
LtrOverrideVal,
|
ForceLtrOverride
|
);
|
} else if (Operation == SetAspm) {
|
if ((EndPointL1SubStateCapOffset != 0) && (*L1SubstatesSupported)) {
|
///
|
/// a. Read L1 Sub-States Extended Capability Offset + 0x04[15:8], and program the highest value advertised
|
/// between PCIe rootport and device to L1 Sub-States Extended Capability Offset + 0x08[15:8] on
|
/// Pcie root port.
|
/// b. Read L1 Sub-States Extended Capability Offset + 0x04[23:19] and [17:16], and program the highest value
|
/// advertised between PCIe root port and device.to L1 Sub-States Extended Capability Offset + 0x08 [7:0] on
|
/// both Pcie root port and device.
|
/// c. Program L1 Sub-States Extended Capability Offset + 0x08[31:29] to 010b for both Pcie root port and device
|
/// d. Program L1 Sub-States Extended Capability Offset + 0x08[25:16] to 0010100000b for both Pcie root port and device
|
/// e. Program the L1 PM Sub-States Control 1 Register bits 3:0 to the smallest common subset supported by both sides.
|
///
|
if ((((UINT8) EndPointL1Substates & EndPointL1SubStateCapMask) == EndPointL1SubStateCapMask)) {
|
|
//
|
// Check if current device is a downstream port
|
//
|
DownstreamPort = FALSE;
|
PcieEndCapOffset = PcieFindCapId (EndPointBus, DeviceIndex, FunctionIndex, EFI_PCI_CAPABILITY_ID_PCIEXP);
|
if ((MmioRead8 (EndPointBase + R_PCI_BCC_OFFSET) == PCI_CLASS_BRIDGE) && (PcieEndCapOffset != 0)) {
|
PcieEndDeviceType = (MmioRead16 (EndPointBase + PcieEndCapOffset + R_PCIE_XCAP_OFFSET) & B_PCIE_XCAP_DT) >> N_PCIE_XCAP_DT;
|
if (PcieEndDeviceType == 0x06) {
|
DownstreamPort = TRUE;
|
}
|
}
|
ConfigureL1s (
|
DownstreamPort,
|
EndPointBase,
|
EndPointL1SubStateCapOffset,
|
*PortL1SubstateCapSupport,
|
*PortCommonModeRestoreTime,
|
*PortTpowerOnValue,
|
*PortTpowerOnScale
|
);
|
}
|
}
|
///
|
/// Write it to the Link Control register
|
///
|
DEBUG ((DEBUG_INFO, "Program Endpoint Device %0x Aspm Value: %0x\n", DeviceIndex, *LinkAspmVal));
|
MmioAndThenOr16 (EndPointBase + EndPointPcieCapOffset + R_PCIE_LCTL_OFFSET, (UINT16) ~B_PCIE_LCTL_ASPM, *LinkAspmVal);
|
///
|
/// PCH BIOS Spec Section 8.14.1 Power Optimizer Configuration
|
/// Step 3
|
///
|
ConfigureLtr (
|
RootPortConfig,
|
EndPointBus,
|
DeviceIndex,
|
FunctionIndex,
|
EndPointBase,
|
EndPointPcieCapOffset,
|
DeviceCapabilities2,
|
PchSeries,
|
LtrOverrideVal
|
);
|
}
|
///
|
/// Check if this device is a bridge
|
///
|
ClassCode = MmioRead8 (EndPointBase + R_PCI_BCC_OFFSET);
|
|
if (ClassCode == PCI_CLASS_BRIDGE) {
|
///
|
/// Get the downstream Bus number
|
///
|
DownStreamBusMin = MmioRead8 (EndPointBase + PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET);
|
///
|
/// If the Secondary Bus Number of endpoint device is not assigned
|
///
|
if (DownStreamBusMin == 0) {
|
RootDevSubBusNum = MmioRead8 (RootDeviceBase + PCI_BRIDGE_SUBORDINATE_BUS_REGISTER_OFFSET);
|
///
|
/// If the endpoint device is a bridge, the Subordinate Bus Number of the root port will need to be greater
|
/// than the Secondary Bus Number of the root port (the Bus Number of endpoint device).
|
///
|
if (RootDevSubBusNum > EndPointBus) {
|
///
|
/// Assign the Primary, Secondary and Subordinate Bus Number to endpoint device
|
///
|
MmioAndThenOr32 (
|
EndPointBase + PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET,
|
0xFF000000,
|
EndPointBus | (((UINT32) (EndPointBus + 1) << 8)) | ((UINT32) (RootDevSubBusNum << 16))
|
);
|
DownStreamBusMin = EndPointBus + 1;
|
} else {
|
return EFI_OUT_OF_RESOURCES;
|
}
|
|
BusAssign = FALSE;
|
} else {
|
BusAssign = TRUE;
|
}
|
|
if (DownStreamBusMin > EndPointBus) {
|
Status = PcieEndPointPm (
|
RootDeviceBase,
|
RootPcieCapOffset,
|
DownStreamBusMin,
|
NumOfDevAspmOverride,
|
DevAspmOverride,
|
LinkAspmVal,
|
Operation,
|
LtrOverrideVal,
|
RootL1SubstateExtCapOffset,
|
L1SubstatesSupported,
|
PortL1SubstateCapSupport,
|
PortCommonModeRestoreTime,
|
PortTpowerOnValue,
|
PortTpowerOnScale,
|
RootPortConfig,
|
AspmOverride,
|
ClkreqPerPortSupported,
|
RpAndEndPointsLtrSupported,
|
ForceLtrOverride
|
);
|
if (Status == EFI_NOT_FOUND) {
|
DEBUG ((DEBUG_INFO, "Check DownStreamBus:%d and no device found!\n", DownStreamBusMin));
|
}
|
|
if (BusAssign == FALSE) {
|
///
|
/// Clear Bus Numbers.
|
///
|
MmioAnd32 (EndPointBase + PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET, 0xFF000000);
|
}
|
}
|
}
|
}
|
}
|
|
return EFI_SUCCESS;
|
}
|
|
|
/**
|
Initializes the root port and its down stream devices
|
|
@param[in] RootPortBus Pci Bus Number of the root port
|
@param[in] RootPortDevice Pci Device Number of the root port
|
@param[in] RootPortFunc Pci Function Number of the root port
|
@param[in] CurrentPortBus Pci Bus Number of the current port. Can be a root port or Switch Upstream/Downstream port
|
@param[in] CurrentPortDevice Pci Device Number of the current port. Can be a root port or Switch Upstream/Downstream port
|
@param[in] CurrentPortFunc Pci Function Number of the current port. Can be a root port or Switch Upstream/Downstream port
|
@param[in] TempBusNumberMin Minimal temp bus number that can be assigned to the root port (as secondary
|
bus number) and its down stream switches
|
@param[in] TempBusNumberMax Maximal temp bus number that can be assigned to the root port (as subordinate
|
bus number) and its down stream switches
|
@param[in, out] MaxPayload The Max Payolad Size of the root port
|
@param[in] SetConfig FALSE to Get information or TRUE to set configuration
|
@param[in] EnableCpm Enables Clock Power Management; even if disabled, CLKREQ# can still be used by L1 PM substates mechanism
|
|
@retval EFI_SUCCESS Successfully completed
|
@retval EFI_NOT_FOUND Can not find device.
|
**/
|
EFI_STATUS
|
PchPcieInitDownstreamDevices (
|
IN UINT8 RootPortBus,
|
IN UINT8 RootPortDevice,
|
IN UINT8 RootPortFunc,
|
IN UINT8 CurrentPortBus,
|
IN UINT8 CurrentPortDevice,
|
IN UINT8 CurrentPortFunc,
|
IN UINT8 TempBusNumberMin,
|
IN UINT8 TempBusNumberMax,
|
IN OUT UINT16 *MaxPayload,
|
IN BOOLEAN SetConfig,
|
IN BOOLEAN EnableCpm
|
)
|
{
|
UINT32 Index;
|
UINTN CurrentDeviceBase;
|
UINTN EndPointBase;
|
UINT8 EndPointBus;
|
UINT8 PcieCapOffset;
|
UINT8 PcieDeviceType;
|
UINT8 PcieEndCapOffset;
|
UINT8 PcieEndDeviceType;
|
UINT16 SlotStatus;
|
DEBUG ((DEBUG_INFO,"PchPcieInitDownstreamDevices RootBDF %0x : %0x :%0x\n",RootPortBus, RootPortDevice, RootPortFunc));
|
DEBUG ((DEBUG_INFO,"PchPcieInitDownstreamDevices CurrentBDF %0x : %0x :%0x\n",CurrentPortBus, CurrentPortDevice, CurrentPortFunc));
|
DEBUG ((DEBUG_INFO,"PchPcieInitDownstreamDevices TempBusMin %0x , TempBusMax %0x\n",TempBusNumberMin, TempBusNumberMax));
|
DEBUG ((DEBUG_INFO,"PchPcieInitDownstreamDevices MaxPayLoad %0x\n", *MaxPayload));
|
CurrentDeviceBase = MmPciBase (CurrentPortBus, CurrentPortDevice, CurrentPortFunc);
|
///
|
/// Get the downstream Bus number
|
///
|
EndPointBus = MmioRead8 (CurrentDeviceBase + PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET);
|
///
|
/// If the Secondary Bus Number of the root port is not assigned,
|
/// then temporarily Hardcode the Root Port Bridge Number to TempBusNumberMin,
|
/// otherwise use the assigned Bus Number
|
///
|
if (EndPointBus == 0) {
|
MmioAndThenOr32 (
|
CurrentDeviceBase + PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET,
|
0xFF000000,
|
CurrentPortBus | ((UINT32) (TempBusNumberMin << 8)) | ((UINT32) (TempBusNumberMax << 16))
|
);
|
} else {
|
TempBusNumberMin = EndPointBus;
|
TempBusNumberMax = MmioRead8 (CurrentDeviceBase + PCI_BRIDGE_SUBORDINATE_BUS_REGISTER_OFFSET);
|
}
|
PcieCapOffset = PcieFindCapId (
|
CurrentPortBus,
|
CurrentPortDevice,
|
CurrentPortFunc,
|
EFI_PCI_CAPABILITY_ID_PCIEXP
|
);
|
PcieDeviceType = (UINT8) ((MmioRead16 (CurrentDeviceBase + PcieCapOffset + R_PCIE_XCAP_OFFSET) & B_PCIE_XCAP_DT) >> N_PCIE_XCAP_DT);
|
|
///
|
/// Skip this section if Upstream device
|
///
|
if ((PcieDeviceType != 0x05) ||
|
((MmioRead8 (CurrentDeviceBase + PCI_HEADER_TYPE_OFFSET) & HEADER_LAYOUT_CODE) == HEADER_TYPE_DEVICE)) {
|
///
|
/// This Endpoint check should immediately pass.
|
/// Completion Retry Status Replay Enable is set in PchRootPorts.c PchInitRootPorts ()
|
/// to bypass the timing requirements of the PCI Express Base
|
/// Specification, Revision 1.0A, Section 6.6 ("...software must allow 1.0s
|
/// after a reset of a device, before it may determine that a device which
|
/// fails to return a Successful Completion status for a valid Configuration
|
/// Request is a broken device").
|
///
|
EndPointBase = MmPciBase (TempBusNumberMin, 0, 0);
|
///
|
/// A config write is required in order for the device to re-capture the Bus number,
|
/// according to PCI Express Base Specification, 2.2.6.2 ("Note that the Bus Number
|
/// and Device Number may be changed at run time, and so it is necessary to re-capture
|
/// this information with each and every Configuration Write Request")
|
///
|
MmioWrite8 (EndPointBase + 0x0, 0);
|
|
if (MmioRead16 (EndPointBase + PCI_VENDOR_ID_OFFSET) == MAX_UINT16) {
|
///
|
/// Clear Bus Numbers.
|
///
|
if (EndPointBus == 0) {
|
MmioAnd32 (CurrentDeviceBase + PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET, 0xFF000000);
|
}
|
return EFI_NOT_FOUND;
|
}
|
}
|
for (Index = 0; Index <= PCI_MAX_DEVICE; Index++) {
|
EndPointBase = MmPciBase (TempBusNumberMin, Index, 0);
|
if (MmioRead16 (EndPointBase + PCI_VENDOR_ID_OFFSET) == MAX_UINT16) {
|
continue;
|
}
|
|
if (!SetConfig) {
|
///
|
/// Get the Max Payload Size on all the end point functions
|
///
|
PcieMaxPayloadSize (TempBusNumberMin, (UINT8) Index, MaxPayload, FALSE);
|
}
|
|
PcieEndCapOffset = PcieFindCapId (
|
TempBusNumberMin,
|
(UINT8) Index,
|
0,
|
EFI_PCI_CAPABILITY_ID_PCIEXP
|
);
|
|
///
|
/// Check if this device is a bridge
|
/// Pcie to Pci/PciX bridges are skipped
|
///
|
if ((MmioRead8 (EndPointBase + R_PCI_BCC_OFFSET) == PCI_CLASS_BRIDGE) && (PcieEndCapOffset != 0)) {
|
DEBUG ((DEBUG_INFO,"PchPcieInitDownstreamDevices Bridge Detected\n"));
|
PcieEndDeviceType = (UINT8) ((MmioRead16 (EndPointBase + PcieEndCapOffset + R_PCIE_XCAP_OFFSET) & B_PCIE_XCAP_DT) >> N_PCIE_XCAP_DT);
|
SlotStatus = MmioRead16 (EndPointBase + PcieEndCapOffset + R_PCIE_SLSTS_OFFSET);
|
|
///
|
/// Initialize the downstream device in following conditions:
|
/// PcieDeviceType is 5 (endpoint is an upstream device).
|
/// PcieDeviceType is 6 (endpoint is a downstream device). And the slot has a device.
|
///
|
if ((PcieEndDeviceType == 0x05) || ((PcieEndDeviceType == 0x06) && ((SlotStatus & B_PCIE_SLSTS_PDS) != 0))) {
|
///
|
/// Initialize downstream devices
|
///
|
if (TempBusNumberMax > TempBusNumberMin) {
|
PchPcieInitDownstreamDevices (
|
RootPortBus,
|
RootPortDevice,
|
RootPortFunc,
|
TempBusNumberMin,
|
(UINT8) Index,
|
0,
|
TempBusNumberMin + 1,
|
TempBusNumberMax,
|
MaxPayload,
|
SetConfig,
|
EnableCpm
|
);
|
} else {
|
ASSERT (FALSE);
|
}
|
}
|
}
|
|
if (SetConfig) {
|
///
|
/// Disable the forwarding of EOI messages unless it discovers an IOAPIC behind this root port
|
///
|
if (CurrentPortBus == DEFAULT_PCI_BUS_NUMBER_PCH) {
|
PcieSetEoiFwdDisable (CurrentPortBus, CurrentPortDevice, CurrentPortFunc, TempBusNumberMin, (UINT8) Index);
|
}
|
|
///
|
/// Set Common Clock for inserted cards
|
///
|
///
|
/// PCH BIOS Spec Section 8.3 ASPM on DMI and the PCI Express* Root Ports
|
/// Before determining whether ASPM can be enabled or not,
|
/// the System BIOS must perform the following steps:
|
///
|
/// For PCH H
|
/// 1. Update the Link Capabilities of the DMI link to indicate L0s/L1 is
|
/// supported by programming the LCAP Register,
|
/// PCR[DMI] + 21A4h[11:10] = 11b. (for PCH with DMI ONLY)
|
/// (Done in PchDmiPei.c)
|
///
|
/// 2. Enable L0s on DMI for Desktop platforms by setting the APMC field,
|
/// PCR[DMI] + 21A8h[1:0] to 01b.
|
/// Enable L0s/L1 on DMI by setting PCR[DMI] + 21A8h[1:0] to 11b. (for PCH with DMI ONLY)
|
/// (Done in PchDmiPei.c)
|
///
|
/// 3. For each root port, read the Slot Clock Configuration bit, Dxx:Fn:52h[12],
|
/// of the root port and the endpoint device connected to the port (i.e., D0:F0 on the
|
/// secondary bus behind the root port). If both components have this bit set, then the
|
/// System BIOS should set the Common Clock Configuration (CCC) bit, Dxx:Fn:50h[6],
|
/// for both components at both sides of the link to indicate that components at both ends
|
/// of the link use a common clock source.
|
///
|
/// 4. If the CCC bit was changed by the System BIOS in step 3, System BIOS should initiate
|
/// a link training by setting the Retrain Link (RL) bit, Dxx:Fn:50h[5], and then poll the Link
|
/// Training (LT) bit, Dxx:Fn:52h[11], until it is clear.
|
/// Note that System BIOS should save and restore CCC bit on S3.
|
///
|
PcieSetCommonClock (CurrentPortBus, CurrentPortDevice, CurrentPortFunc, TempBusNumberMin, (UINT8) Index);
|
|
///
|
/// Enable the PCIe CLKREQ#
|
///
|
if (EnableCpm) {
|
PcieSetClkreq (TempBusNumberMin, (UINT8) Index, RootPortDevice, RootPortFunc);
|
}
|
|
///
|
/// Set the Max Payload Size on all the end point functions
|
///
|
PcieMaxPayloadSize (TempBusNumberMin, (UINT8) Index, MaxPayload, TRUE);
|
}
|
}
|
|
///
|
/// Clear Bus Numbers if it's not assigned yet
|
///
|
if (EndPointBus == 0) {
|
MmioAnd32 (CurrentDeviceBase + PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET, 0xFF000000);
|
}
|
return EFI_SUCCESS;
|
}
|
|
/**
|
Initializes the root port and its down stream devices
|
|
@param[in] RootPortBus Pci Bus Number of the root port
|
@param[in] RootPortDevice Pci Device Number of the root port
|
@param[in] RootPortFunc Pci Function Number of the root port
|
@param[in] TempBusNumberMin Minimal temp bus number that can be assigned to the root port (as secondary
|
bus number) and its down stream switches
|
@param[in] TempBusNumberMax Maximal temp bus number that can be assigned to the root port (as subordinate
|
bus number) and its down stream switches
|
@param[in] EnableCpm Enables Clock Power Management; even if disabled, CLKREQ# can still be used by L1 PM substates mechanism
|
|
@retval EFI_SUCCESS Successfully completed
|
@retval EFI_NOT_FOUND Can not find device.
|
**/
|
EFI_STATUS
|
PchPcieInitRootPortDownstreamDevices (
|
IN UINT8 RootPortBus,
|
IN UINT8 RootPortDevice,
|
IN UINT8 RootPortFunc,
|
IN UINT8 TempBusNumberMin,
|
IN UINT8 TempBusNumberMax,
|
IN BOOLEAN EnableCpm
|
)
|
{
|
UINT16 SlotStatus;
|
UINTN RpBase;
|
UINT16 MaxPayload;
|
UINT8 PcieCapOffset;
|
EFI_STATUS Status;
|
UINT8 CurrentPortBus;
|
UINT8 CurrentPortDevice;
|
UINT8 CurrentPortFunc;
|
UINT32 Timeout;
|
|
|
RpBase = MmPciBase (RootPortBus, RootPortDevice, RootPortFunc);
|
///
|
/// Check for a Presence Detect Change.
|
///
|
SlotStatus = MmioRead16 (RpBase + R_PCH_PCIE_SLSTS);
|
|
///
|
/// Check whether the slot has a device connected
|
///
|
if ((SlotStatus & B_PCIE_SLSTS_PDS) == 0) {
|
return EFI_NOT_FOUND;
|
}
|
///
|
/// Make sure the link is active before trying to talk to device behind it
|
/// Wait up to 100ms, according to PCIE spec chapter 6.7.3.3
|
///
|
Timeout = 100 * 1000;
|
while ((MmioRead16 (RpBase + R_PCH_PCIE_LSTS) & B_PCIE_LSTS_LA) == 0 ) {
|
MicroSecondDelay (10);
|
Timeout-=10;
|
if (Timeout == 0) {
|
return EFI_NOT_FOUND;
|
}
|
}
|
///
|
/// Get the pointer to the Endpoint PCI Express Capability Structure.
|
///
|
PcieCapOffset = PcieFindCapId (
|
RootPortBus,
|
RootPortDevice,
|
RootPortFunc,
|
EFI_PCI_CAPABILITY_ID_PCIEXP
|
);
|
if (PcieCapOffset == 0) {
|
DEBUG ((DEBUG_INFO,"PcieCapOffset Not Found"));
|
ASSERT (FALSE);
|
return EFI_UNSUPPORTED;
|
}
|
|
//
|
// Get the root port Max Payload Size support
|
//
|
MaxPayload = MmioRead16 (RpBase + PcieCapOffset + R_PCIE_DCAP_OFFSET) & B_PCIE_DCAP_MPS;
|
|
///
|
/// Initialize downstream devices
|
///
|
CurrentPortBus = RootPortBus;
|
CurrentPortDevice = RootPortDevice;
|
CurrentPortFunc = RootPortFunc;
|
|
//
|
// Get configuration from all downstreams
|
//
|
// In current implementation, only the MaxPayloadSize need to be calculated.
|
// Therefore, for performance, skip the calculation if the MaxPayloadSize is already minimum.
|
//
|
if (MaxPayload != 0) {
|
Status = PchPcieInitDownstreamDevices (
|
RootPortBus,
|
RootPortDevice,
|
RootPortFunc,
|
CurrentPortBus,
|
CurrentPortDevice,
|
CurrentPortFunc,
|
TempBusNumberMin,
|
TempBusNumberMax,
|
&MaxPayload,
|
FALSE,
|
EnableCpm
|
);
|
}
|
//
|
// Set configuration to all downstreams
|
//
|
Status = PchPcieInitDownstreamDevices (
|
RootPortBus,
|
RootPortDevice,
|
RootPortFunc,
|
CurrentPortBus,
|
CurrentPortDevice,
|
CurrentPortFunc,
|
TempBusNumberMin,
|
TempBusNumberMax,
|
&MaxPayload,
|
TRUE,
|
EnableCpm
|
);
|
return Status;
|
}
|
|
/**
|
Get current PCIe link speed.
|
|
@param[in] RpBase Root Port base address
|
@return Link speed
|
**/
|
UINT32
|
GetLinkSpeed (
|
UINTN RpBase
|
)
|
{
|
return MmioRead16 (RpBase + R_PCH_PCIE_LSTS) & B_PCIE_LSTS_CLS;
|
}
|
|
/**
|
Get max PCIe link speed supported by the root port.
|
|
@param[in] RpBase Root Port base address
|
@return Max link speed
|
**/
|
UINT32
|
GetMaxLinkSpeed (
|
UINTN RpBase
|
)
|
{
|
return MmioRead32 (RpBase + R_PCH_PCIE_LCAP) & B_PCIE_LCAP_MLS;
|
}
|
|
/**
|
Get Pch Maximum Pcie Controller Number
|
|
@retval Pch Maximum Pcie Controller Number
|
**/
|
UINT8
|
EFIAPI
|
GetPchMaxPcieControllerNum (
|
VOID
|
)
|
{
|
if (GetPchSeries () == PchLp) {
|
return PCH_LP_PCIE_MAX_CONTROLLERS;
|
} else {
|
if (GetPchGeneration () == KblPch) {
|
return KBL_PCH_H_PCIE_MAX_CONTROLLERS;
|
} else {
|
return SKL_PCH_H_PCIE_MAX_CONTROLLERS;
|
}
|
}
|
}
|
|
/**
|
PCIe controller configuration strings.
|
**/
|
GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8* mPcieControllerConfigName[] = {
|
"4x1",
|
"1x2-2x1",
|
"2x2",
|
"1x4"
|
};
|
|
/**
|
Returns the PCIe controller configuration (4x1, 1x2-2x1, 2x2, 1x4)
|
|
@param[in] ControllerIndex Number of PCIe controller (0 based)
|
|
@retval PCIe controller configuration
|
**/
|
PCIE_CONTROLLER_CONFIG
|
GetPcieControllerConfig (
|
IN UINT32 ControllerIndex
|
)
|
{
|
EFI_STATUS Status;
|
UINT32 Data32;
|
PCIE_CONTROLLER_CONFIG Config;
|
UINT32 FirstRp;
|
|
FirstRp = ControllerIndex * PCH_PCIE_CONTROLLER_PORTS;
|
|
Status = PchSbiRpPciRead32 (FirstRp, R_PCH_PCIE_STRPFUSECFG, &Data32);
|
ASSERT_EFI_ERROR (Status);
|
|
Config = ((Data32 & B_PCH_PCIE_STRPFUSECFG_RPC) >> N_PCH_PCIE_STRPFUSECFG_RPC);
|
DEBUG ((DEBUG_INFO, "PCIe SP%c is %a\n", (UINTN) ('A' + ControllerIndex), mPcieControllerConfigName[Config]));
|
return Config;
|
}
|