REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3171
Adds the following files: * IpBlock/PcieRp/IncludePrivate * IpBlock/PcieRp/Library * IpBlock/PcieRp/LibraryPrivate Cc: Sai Chaganty <rangasai.v.chaga...@intel.com> Cc: Nate DeSimone <nathaniel.l.desim...@intel.com> Signed-off-by: Heng Luo <heng....@intel.com> --- Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/IncludePrivate/Library/DxePchPcieRpPolicyLib.h | 55 ++++++++++++++++++++++++ Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/IncludePrivate/Library/PciExpressHelpersLib.h | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/IncludePrivate/Library/PcieRpLib.h | 109 ++++++++++++++++++++++++++++++++++++++++++++++++ Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/IncludePrivate/Register/PcieSipRegs.h | 45 ++++++++++++++++++++ Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/Library/BasePcieHelperLib/BasePcieHelperLib.c | 315 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/Library/BasePcieHelperLib/BasePcieHelperLib.inf | 37 +++++++++++++++++ Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/Library/PeiDxeSmmPchPcieRpLib/PchPcieRpLib.c | 69 ++++++++++++++++++++++++++++++ Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/Library/PeiDxeSmmPchPcieRpLib/PchPcieRpLibInternal.h | 20 +++++++++ Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/Library/PeiDxeSmmPchPcieRpLib/PchPcieRpLibVer2.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/Library/PeiDxeSmmPchPcieRpLib/PeiDxeSmmPchPcieRpLibVer2.inf | 39 +++++++++++++++++ Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/DxePchPcieRpPolicyLib/DxePchPcieRpPolicyLib.c | 179 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/DxePchPcieRpPolicyLib/DxePchPcieRpPolicyLib.inf | 30 ++++++++++++++ Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/PciExpressHelpersLibrary/PciExpressHelpersLibrary.c | 1997 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/PciExpressHelpersLibrary/PciExpressHelpersLibrary.h | 40 ++++++++++++++++++ Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/PciExpressHelpersLibrary/PeiDxeSmmPciExpressHelpersLib.inf | 49 ++++++++++++++++++++++ Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/PcieClientRpLib/PcieClientRpLib.c | 247 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/PcieClientRpLib/PcieClientRpLib.inf | 43 +++++++++++++++++++ 17 files changed, 3575 insertions(+) diff --git a/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/IncludePrivate/Library/DxePchPcieRpPolicyLib.h b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/IncludePrivate/Library/DxePchPcieRpPolicyLib.h new file mode 100644 index 0000000000..1dea61388e --- /dev/null +++ b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/IncludePrivate/Library/DxePchPcieRpPolicyLib.h @@ -0,0 +1,55 @@ +/** @file + DXE PcieRp policy library. + + Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#ifndef _DXE_PCH_PCIERP_POLICY_LIB_H_ +#define _DXE_PCH_PCIERP_POLICY_LIB_H_ + +#include <Protocol/PchPolicy.h> + +/** + Load DXE Config block default for Pch PCIE RP + + @param[in] ConfigBlockPointer Pointer to config block +**/ +VOID +LoadPchPcieRpDxeConfigDefault ( + IN VOID *ConfigBlockPointer + ); + +/** + Print PCIE_RP_DXE_CONFIG. + + @param[in] PchPolicy Pointer to a PCH_POLICY_PROTOCOL +**/ +VOID +PchPcieRpDxePrintConfig ( + IN PCH_POLICY_PROTOCOL *PchPolicy + ); + +/** + Get PchPcieRp config block table size. + + @retval Size of config block +**/ +UINT16 +PchPcieRpDxeGetConfigBlockTotalSize ( + VOID + ); + +/** + Add PchPcieRp ConfigBlock. + + @param[in] ConfigBlockTableAddress The pointer to config block table + + @retval EFI_SUCCESS The policy default is initialized. + @retval EFI_OUT_OF_RESOURCES Insufficient resources to create buffer +**/ +EFI_STATUS +PchPcieRpDxeAddConfigBlock ( + IN VOID *ConfigBlockTableAddress + ); + +#endif diff --git a/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/IncludePrivate/Library/PciExpressHelpersLib.h b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/IncludePrivate/Library/PciExpressHelpersLib.h new file mode 100644 index 0000000000..d7ca34dc38 --- /dev/null +++ b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/IncludePrivate/Library/PciExpressHelpersLib.h @@ -0,0 +1,173 @@ +/** @file + Header file for PCI Express helpers library + + Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#ifndef _PCI_EXPRESS_HELPERS_LIB_H_ +#define _PCI_EXPRESS_HELPERS_LIB_H_ + +#include <PchPolicyCommon.h> +#include <Library/PcieRpLib.h> + +typedef enum { + TpoScale2us, + TpoScale10us, + TpoScale100us, + TpoScaleMax +} T_PO_SCALE; +// +// This structure keeps segment:bus:device:function coordinates of a PCIe device +// in a single variable. PcieCap is offset to PCI Express capabilities. +// +typedef struct { + UINT32 Seg : 8; + UINT32 Bus : 8; + UINT32 Dev : 5; + UINT32 Func : 3; + UINT32 PcieCap : 8; +} SBDF; + +/* + Converts Tpower_on from value:scale notation to microseconds + + @param[in] TpoScale T power on scale + @param[in] TpoValue T power on value + + @retval number of microseconds +*/ +UINT32 +TpoToUs ( + UINT32 TpoScale, + UINT32 TpoValue + ); + +/** +PCIe controller Sku. +**/ + +typedef enum { + EnumPchPcie = 0, + EnumiTbtPcie = 1, + EnumCpuPcie = 2, + EnumPciSkuMax = 3 +} PCI_SKU; + +/* + Initializes the following features in rootport and devices behind it: + Maximum Payload Size (generic) + Rootport packet split (proprietary) + EonOfInterrupt forwarding (proprietary) + Common Clock Configuration (generic) + + Generic: any code written according to PCIE Express base specification can do that. + Proprietary: code uses registers and features that are specific to Intel silicon + and probably only this Reference Code knows how to handle that. + + If OEM implemented generic feature enabling in his platform code or trusts Operating System + to do it, then those features can be deleted from here. + + CCC requires link retrain, which takes a while. CCC must happen before L0s/L1 programming. + If there was guarantee no code would access PCI while links retrain, it would be possible to skip this waiting + + @param[in] RpSegment address of rootport on PCIe + @param[in] RpBus address of rootport on PCIe + @param[in] RpDevice address of rootport on PCIe + @param[in] RpFunction address of rootport on PCIe + @param[in] BusMin minimum Bus number that can be assigned below this rootport + @param[in] BusMax maximum Bus number that can be assigned below this rootport +*/ +VOID +RootportDownstreamConfiguration ( + UINT8 RpSegment, + UINT8 RpBus, + UINT8 RpDevice, + UINT8 RpFunction, + UINT8 BusMin, + UINT8 BusMax, + PCI_SKU PciSku + ); + +/* + Configures the following power-management related features in rootport and devices behind it: + LTR limit (generic) + LTR override (proprietary) + Clock Power Management (generic) + L1 substates (generic except for the override table) + L1.LOW substate (proprietary) + L0s and L1 (generic) + + Generic: any code written according to PCIE Express base specification can do that. + Proprietary: code uses registers and features that are specific to Intel silicon + and probably only this Reference Code knows how to handle that. + + If OEM implemented generic feature enabling in his platform code or trusts Operating System + to do it, then those features can be deleted from here. + + @param[in] RpSegment address of rootport on PCIe + @param[in] RpBus address of rootport on PCIe + @param[in] RpDevice address of rootport on PCIe + @param[in] RpFunction address of rootport on PCIe + @param[in] BusMin minimal Bus number that can be assigned below this rootport + @param[in] BusMax maximum Bus number that can be assigned below this rootport + @param[in] PcieRpCommonConfig a pointer to Pcie Root Port Common Config + @param[in] AspmOverrideTableSize size of override array + @param[in] AspmOverrideTable array of device that need exceptions in configuration +*/ +VOID +RootportDownstreamPmConfiguration ( + UINT8 RpSegment, + UINT8 RpBus, + UINT8 RpDevice, + UINT8 RpFunction, + UINT8 BusMin, + UINT8 BusMax, + PCIE_ROOT_PORT_COMMON_CONFIG *PcieRpCommonConfig, + UINT32 AspmOverrideTableSize, + PCH_PCIE_DEVICE_OVERRIDE *AspmOverrideTable + ); + +typedef struct { + UINT32 MaxSnoopLatencyValue : 10; + UINT32 MaxSnoopLatencyScale : 3; + UINT32 MaxNoSnoopLatencyValue : 10; + UINT32 MaxNoSnoopLatencyScale : 3; + UINT32 Reserved : 6; +} LTR_LIMIT; + +/** + Checks if given PCI device is capable of Latency Tolerance Reporting + + @param[in] Sbdf device's segment:bus:device:function coordinates + + @retval TRUE if yes +**/ +BOOLEAN +IsLtrCapable ( + SBDF Sbdf + ); + +/** + Returns combination of two LTR override values + The resulting LTR override separately chooses stricter limits for snoop and nosnoop + + @param[in] LtrA LTR override values to be combined + @param[in] LtrB LTR override values to be combined + + @retval LTR override value +**/ +LTR_OVERRIDE +CombineLtr ( + LTR_OVERRIDE LtrA, + LTR_OVERRIDE LtrB + ); + +/** + Extended Virtual Channel Configuration +**/ +typedef struct { + UINT16 CapOffset; + UINT8 ExtVcCount; +} MULTI_VC_SUPPORT; + +#endif // _PCI_EXPRESS_HELPERS_LIB_H_ diff --git a/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/IncludePrivate/Library/PcieRpLib.h b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/IncludePrivate/Library/PcieRpLib.h new file mode 100644 index 0000000000..ed75e438c7 --- /dev/null +++ b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/IncludePrivate/Library/PcieRpLib.h @@ -0,0 +1,109 @@ +/** @file + Header file for PCH PCI Express helpers library + + Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#ifndef _PCIE_RP_LIB_ +#define _PCIE_RP_LIB_ + +#include <PchPolicyCommon.h> + +typedef struct { + UINT32 MaxSnoopLatencyValue : 10; + UINT32 MaxSnoopLatencyScale : 3; + UINT32 MaxSnoopLatencyRequirement : 1; + UINT32 MaxNoSnoopLatencyValue : 10; + UINT32 MaxNoSnoopLatencyScale : 3; + UINT32 MaxNoSnoopLatencyRequirement : 1; + UINT32 ForceOverride : 1; +} LTR_OVERRIDE; + +/** + Get PCIe port number for enabled Root Port. + + @param[in] RpBase Root Port pci segment base address + + @retval Root Port number (1 based) +**/ +UINT32 +PciePortNum ( + IN UINT64 RpBase + ); + +/** + Get PCIe root port index + @param[in] RpBase Root Port pci segment base address + @return Root Port index (0 based) +**/ +UINT32 +PciePortIndex ( + IN UINT64 RpBase + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + Configures LTR override in Root Port's proprietary registers. + + @param[in] Segment,Bus,Device,Function address of currently visited PCIe device + @param[in] LtrConfig 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 + ); + +/** + 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 + ); + +/** + 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 + ); +#endif diff --git a/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/IncludePrivate/Register/PcieSipRegs.h b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/IncludePrivate/Register/PcieSipRegs.h new file mode 100644 index 0000000000..1fc470b8ba --- /dev/null +++ b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/IncludePrivate/Register/PcieSipRegs.h @@ -0,0 +1,45 @@ +/** @file + Register names for PCIe SIP specific registers + + Conventions: + + - Register definition format: + Prefix_[GenerationName]_[ComponentName]_SubsystemName_RegisterSpace_RegisterName + - Prefix: + Definitions beginning with "R_" are registers + Definitions beginning with "B_" are bits within registers + Definitions beginning with "V_" are meaningful values within the bits + Definitions beginning with "S_" are register size + Definitions beginning with "N_" are the bit position + - [GenerationName]: + Three letter acronym of the generation is used (e.g. SKL,KBL,CNL etc.). + Register name without GenerationName applies to all generations. + - [ComponentName]: + This field indicates the component name that the register belongs to (e.g. PCH, SA etc.) + Register name without ComponentName applies to all components. + Register that is specific to -LP denoted by "_PCH_LP_" in component name. + - SubsystemName: + This field indicates the subsystem name of the component that the register belongs to + (e.g. PCIE, USB, SATA, GPIO, PMC etc.). + - RegisterSpace: + MEM - MMIO space register of subsystem. + IO - IO space register of subsystem. + PCR - Private configuration register of subsystem. + CFG - PCI configuration space register of subsystem. + - RegisterName: + Full register name. + + Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef _PCIE_SIP_RP_REGS_H_ +#define _PCIE_SIP_RP_REGS_H_ + +#include <PcieRegs.h> + +#define R_PCIE_CFG_CCFG 0xD0 +#define B_PCIE_CFG_CCFG_UNRS (BIT6 | BIT5 | BIT4) +#define N_PCIE_CFG_CCFG_UNRS 4 + +#endif diff --git a/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/Library/BasePcieHelperLib/BasePcieHelperLib.c b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/Library/BasePcieHelperLib/BasePcieHelperLib.c new file mode 100644 index 0000000000..1b3e629431 --- /dev/null +++ b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/Library/BasePcieHelperLib/BasePcieHelperLib.c @@ -0,0 +1,315 @@ +/** @file + This file contains routines that support PCI Express initialization + + Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#include <Library/PcieHelperLib.h> +#include <Library/BaseMemoryLib.h> +#include <PcieRegs.h> + +#define ASPM_L1_NO_LIMIT 0xFF +#define LINK_RETRAIN_WAIT_TIME 1000 // microseconds + +/** + Finds the Offset to a given Capabilities ID + Each capability has an ID and a pointer to next Capability, so they form a linked list. + This function walks the list of Capabilities present in device's pci cfg. If requested capability + can be found, its offset is returned. + If the capability can't be found or if device doesn't exist, function returns 0 + CAPID list: + 0x01 = PCI Power Management Interface + 0x04 = Slot Identification + 0x05 = MSI Capability + 0x10 = PCI Express Capability + + @param[in] DeviceBase device's base address + @param[in] CapId CAPID to search for + + @retval 0 CAPID not found (this includes situation where device doesn't exit) + @retval Other CAPID found, Offset of desired CAPID +**/ +UINT8 +PcieBaseFindCapId ( + IN UINT64 DeviceBase, + IN UINT8 CapId + ) +{ + UINT8 CapHeaderOffset; + UINT8 CapHeaderId; + UINT16 Data16; + // + // We do not explicitly check if device exists to save time and avoid unnecessary PCI access + // If the device doesn't exist, check for CapHeaderId != 0xFF will fail and function will return offset 0 + // + if ((PciSegmentRead8 (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 ((PciSegmentRead8 (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 = PciSegmentRead8 (DeviceBase + CapHeaderOffset) & ((UINT8) ~(BIT0 | BIT1)); + while (CapHeaderOffset != 0 && CapHeaderId != 0xFF) { + Data16 = PciSegmentRead16 (DeviceBase + CapHeaderOffset); + CapHeaderId = (UINT8)(Data16 & 0xFF); + 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 = (UINT8)(Data16 >> 8); + } + return 0; + } +} + +/** + 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] Segment Pci Segment Number + @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 Segment, + IN UINT8 Bus, + IN UINT8 Device, + IN UINT8 Function, + IN UINT8 CapId + ) +{ + UINT64 DeviceBase; + + DeviceBase = PCI_SEGMENT_LIB_ADDRESS (Segment, Bus, Device, Function, 0); + return PcieBaseFindCapId (DeviceBase, CapId); +} + +/** + 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] DeviceBase device base address + @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 +PcieBaseFindExtendedCapId ( + IN UINT64 DeviceBase, + IN UINT16 CapId + ) +{ + UINT16 CapHeaderOffset; + UINT16 CapHeaderId; + /// + /// 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_PCIE_CFG_EXCAP_OFFSET; + while (CapHeaderOffset != 0 && CapHeaderId != MAX_UINT16) { + CapHeaderId = PciSegmentRead16 (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 = (PciSegmentRead16 (DeviceBase + CapHeaderOffset + 2) >> 4) & ((UINT16) ~(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] Segment Pci Segment Number + @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 Segment, + IN UINT8 Bus, + IN UINT8 Device, + IN UINT8 Function, + IN UINT16 CapId + ) +{ + UINT64 DeviceBase; + + DeviceBase = PCI_SEGMENT_LIB_ADDRESS (Segment, Bus, Device, Function, 0); + return PcieBaseFindExtendedCapId (DeviceBase, CapId); +} + +/** + Checks if PCI device at given address exists + + @param[in] Base device's base address + + @retval TRUE if exists +**/ +BOOLEAN +IsDevicePresent ( + UINT64 Base + ) +{ + if (PciSegmentRead16 (Base) == 0xFFFF) { + return FALSE; + } + return TRUE; +} + +/** + Checks if device is a multifunction device + Besides comparing Multifunction bit (BIT7) it checks if contents of HEADER_TYPE register + make sense (header != 0xFF) to prevent false positives when called on devices which do not exist + + @param[in] Base device's base address + + @retval TRUE if multifunction; FALSE otherwise +**/ +BOOLEAN +IsMultifunctionDevice ( + UINT64 Base + ) +{ + UINT8 HeaderType; + HeaderType = PciSegmentRead8(Base + PCI_HEADER_TYPE_OFFSET); + if ((HeaderType == 0xFF) || ((HeaderType & HEADER_TYPE_MULTI_FUNCTION) == 0)) { + return FALSE; + } + return TRUE; +} + +/** + Checks device's Slot Clock Configuration + + @param[in] Base device's base address + @param[in] PcieCapOffset devices Pci express capability list register offset + + @retval TRUE when device uses slot clock, FALSE otherwise +**/ +BOOLEAN +GetScc ( + UINT64 Base, + UINT8 PcieCapOffset + ) +{ + return !!(PciSegmentRead16 (Base + PcieCapOffset + R_PCIE_LSTS_OFFSET) & B_PCIE_LSTS_SCC); +} + +/** + Sets Common Clock Configuration bit for given device. + + @param[in] PcieCapOffset devices Pci express capability list register offset + @param[in] Base device's base address +**/ +VOID +EnableCcc ( + UINT64 Base, + UINT8 PcieCapOffset + ) +{ + PciSegmentOr8 (Base + PcieCapOffset + R_PCIE_LCTL_OFFSET, B_PCIE_LCTL_CCC); +} + +/** + Retrains link behind given device. + It only makes sense to call it for downstream ports. If called for upstream port nothing will happen. + If WaitUntilDone is TRUE function will wait until link retrain had finished, otherwise it will return immediately. + Link must finish retrain before software can access the device on the other side. If it's not going to access it + then considerable time can be saved by not waiting here. + + @param[in] Base device's base address + @param[in] PcieCapOffset devices Pci express capability list register offset + @param[in] WaitUntilDone when TRUE, function waits until link has retrained +**/ +VOID +RetrainLink ( + UINT64 Base, + UINT8 PcieCapOffset, + BOOLEAN WaitUntilDone + ) +{ + UINT16 LinkTraining; + UINT32 TimeoutUs; + + TimeoutUs = LINK_RETRAIN_WAIT_TIME; + // + // Before triggering link retrain make sure it's not already retraining. Otherwise + // settings recently entered in LCTL register might go unnoticed + // + do { + LinkTraining = (PciSegmentRead16 (Base + PcieCapOffset + R_PCIE_LSTS_OFFSET) & B_PCIE_LSTS_LT); + TimeoutUs--; + } while (LinkTraining && (TimeoutUs != 0)); + + PciSegmentOr8 (Base + PcieCapOffset + R_PCIE_LCTL_OFFSET, B_PCIE_LCTL_RL); + + TimeoutUs = LINK_RETRAIN_WAIT_TIME; + if (WaitUntilDone) { + do { + LinkTraining = (PciSegmentRead16 (Base + PcieCapOffset + R_PCIE_LSTS_OFFSET) & B_PCIE_LSTS_LT); + TimeoutUs--; + } while (LinkTraining && (TimeoutUs != 0)); + } +} diff --git a/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/Library/BasePcieHelperLib/BasePcieHelperLib.inf b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/Library/BasePcieHelperLib/BasePcieHelperLib.inf new file mode 100644 index 0000000000..458a540859 --- /dev/null +++ b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/Library/BasePcieHelperLib/BasePcieHelperLib.inf @@ -0,0 +1,37 @@ +## @file +# Component description file for the BasePcieHelperLib +# +# Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + + +[Defines] +INF_VERSION = 0x00010017 +BASE_NAME = BasePcieHelperLib +FILE_GUID = 568F5812-A9A8-4A4A-AD4D-471DD5F0BC11 +VERSION_STRING = 1.0 +MODULE_TYPE = BASE +LIBRARY_CLASS = BasePcieHelperLib +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + + + +[LibraryClasses] +DebugLib +PciSegmentLib + + +[Packages] +MdePkg/MdePkg.dec +TigerlakeSiliconPkg/SiPkg.dec + + +[Sources] +BasePcieHelperLib.c + diff --git a/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/Library/PeiDxeSmmPchPcieRpLib/PchPcieRpLib.c b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/Library/PeiDxeSmmPchPcieRpLib/PchPcieRpLib.c new file mode 100644 index 0000000000..153c073093 --- /dev/null +++ b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/Library/PeiDxeSmmPchPcieRpLib/PchPcieRpLib.c @@ -0,0 +1,69 @@ +/** @file + PCIE root port library. + All function in this library is available for PEI, DXE, and SMM, + But do not support UEFI RUNTIME environment call. + + Copyright (c) 2021, 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/BaseLib.h> +#include <Library/PciSegmentLib.h> +#include <Library/PchInfoLib.h> +#include <Library/PchPcrLib.h> +#include <Library/PchPcieRpLib.h> +#include <PcieRegs.h> +#include <Register/PchRegs.h> +#include <PchPcieRpInfo.h> +#include <Register/PchPcieRpRegs.h> +#include <Register/PchPcrRegs.h> +#include <Library/PchPciBdfLib.h> + +#include "PchPcieRpLibInternal.h" + +/** + Gets pci segment base address of PCIe root port. + + @param RpIndex Root Port Index (0 based) + @return PCIe port base address. +**/ +UINT64 +PchPcieBase ( + IN UINT32 RpIndex + ) +{ + return PchPcieRpPciCfgBase (RpIndex); +} + +/** + Determines whether L0s is supported on current stepping. + + @return TRUE if L0s is supported, FALSE otherwise +**/ +BOOLEAN +PchIsPcieL0sSupported ( + VOID + ) +{ + return TRUE; +} + +/** + Some early PCH steppings require Native ASPM to be disabled due to hardware issues: + - RxL0s exit causes recovery + - Disabling PCIe L0s capability disables L1 + Use this function to determine affected steppings. + + @return TRUE if Native ASPM is supported, FALSE otherwise +**/ +BOOLEAN +PchIsPcieNativeAspmSupported ( + VOID + ) +{ + return PchIsPcieL0sSupported (); +} diff --git a/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/Library/PeiDxeSmmPchPcieRpLib/PchPcieRpLibInternal.h b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/Library/PeiDxeSmmPchPcieRpLib/PchPcieRpLibInternal.h new file mode 100644 index 0000000000..1766cff618 --- /dev/null +++ b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/Library/PeiDxeSmmPchPcieRpLib/PchPcieRpLibInternal.h @@ -0,0 +1,20 @@ +/** @file + PCIE root port library. + All function in this library is available for PEI, DXE, and SMM, + But do not support UEFI RUNTIME environment call. + + Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef _PCH_PCIE_RP_LIB_INTERNAL_H_ +#define _PCH_PCIE_RP_LIB_INTERNAL_H_ + +typedef struct { + UINT8 DevNum; + UINT8 Pid; + UINT8 RpNumBase; +} PCH_PCIE_CONTROLLER_INFO; + +#endif + diff --git a/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/Library/PeiDxeSmmPchPcieRpLib/PchPcieRpLibVer2.c b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/Library/PeiDxeSmmPchPcieRpLib/PchPcieRpLibVer2.c new file mode 100644 index 0000000000..df90a87df7 --- /dev/null +++ b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/Library/PeiDxeSmmPchPcieRpLib/PchPcieRpLibVer2.c @@ -0,0 +1,128 @@ +/** @file + PCIE root port library. + All function in this library is available for PEI, DXE, and SMM, + But do not support UEFI RUNTIME environment call. + + Copyright (c) 2021, 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/BaseLib.h> +#include <Library/PciSegmentLib.h> +#include <Library/PchInfoLib.h> +#include <Library/PchPcrLib.h> +#include <Library/PchPcieRpLib.h> +#include <PcieRegs.h> +#include <Register/PchRegs.h> +#include <PchBdfAssignment.h> +#include <PchPcieRpInfo.h> +#include <Register/PchPcieRpRegs.h> +#include <Register/PchPcrRegs.h> + +#include "PchPcieRpLibInternal.h" + +GLOBAL_REMOVE_IF_UNREFERENCED PCH_PCIE_CONTROLLER_INFO mPchPcieControllerInfo[] = { + { PCI_DEVICE_NUMBER_PCH_PCIE_ROOT_PORT_1, PID_SPA, 0 }, + { PCI_DEVICE_NUMBER_PCH_PCIE_ROOT_PORT_5, PID_SPB, 4 }, + { PCI_DEVICE_NUMBER_PCH_PCIE_ROOT_PORT_9, PID_SPC, 8 }, + { PCI_DEVICE_NUMBER_PCH_PCIE_ROOT_PORT_13, PID_SPD, 12 }, + { PCI_DEVICE_NUMBER_PCH_PCIE_ROOT_PORT_17, PID_SPE, 16 }, // PCH-H only + { PCI_DEVICE_NUMBER_PCH_PCIE_ROOT_PORT_21, PID_SPF, 20 } // PCH-H only +}; + +/** + Get Pch Pcie Root Port Device and Function Number by Root Port physical Number + + @param[in] RpNumber Root port physical number. (0-based) + @param[out] RpDev Return corresponding root port device number. + @param[out] RpFun Return corresponding root port function number. + + @retval EFI_SUCCESS Root port device and function is retrieved + @retval EFI_INVALID_PARAMETER RpNumber is invalid +**/ +EFI_STATUS +EFIAPI +GetPchPcieRpDevFun ( + IN UINTN RpNumber, + OUT UINTN *RpDev, + OUT UINTN *RpFun + ) +{ + UINTN Index; + UINTN FuncIndex; + UINT32 PciePcd; + + if (RpNumber >= GetPchMaxPciePortNum ()) { + DEBUG ((DEBUG_ERROR, "GetPchPcieRpDevFun invalid RpNumber %x", RpNumber)); + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + Index = RpNumber / PCH_PCIE_CONTROLLER_PORTS; + FuncIndex = RpNumber - mPchPcieControllerInfo[Index].RpNumBase; + *RpDev = mPchPcieControllerInfo[Index].DevNum; + PciePcd = PchPcrRead32 (mPchPcieControllerInfo[Index].Pid, R_SPX_PCR_PCD); + *RpFun = (PciePcd >> (FuncIndex * S_SPX_PCR_PCD_RP_FIELD)) & B_SPX_PCR_PCD_RP1FN; + + return EFI_SUCCESS; +} + +/** + Get Root Port physical Number by Pch Pcie Root Port Device and Function Number + + @param[in] RpDev Root port device number. + @param[in] RpFun Root port function number. + @param[out] RpNumber Return corresponding physical Root Port index (0-based) + + @retval EFI_SUCCESS Physical root port is retrieved +**/ +EFI_STATUS +EFIAPI +GetPchPcieRpNumber ( + IN UINTN RpDev, + IN UINTN RpFun, + OUT UINTN *RpNumber + ) +{ + UINT64 RpBase; + + RpBase = PCI_SEGMENT_LIB_ADDRESS (DEFAULT_PCI_SEGMENT_NUMBER_PCH, DEFAULT_PCI_BUS_NUMBER_PCH, RpDev, RpFun, 0); + *RpNumber = (PciSegmentRead32 (RpBase + R_PCH_PCIE_CFG_LCAP) >> N_PCH_PCIE_CFG_LCAP_PN) -1; + return EFI_SUCCESS; +} + +/** + This function returns PID according to PCIe controller index + + @param[in] ControllerIndex PCIe controller index + + @retval PCH_SBI_PID Returns PID for SBI Access +**/ +PCH_SBI_PID +PchGetPcieControllerSbiPid ( + IN UINT32 ControllerIndex + ) +{ + ASSERT (ControllerIndex < ARRAY_SIZE (mPchPcieControllerInfo)); + return mPchPcieControllerInfo[ControllerIndex].Pid; +} + +/** + This function returns PID according to Root Port Number + + @param[in] RpIndex Root Port Index (0-based) + + @retval PCH_SBI_PID Returns PID for SBI Access +**/ +PCH_SBI_PID +GetRpSbiPid ( + IN UINTN RpIndex + ) +{ + return PchGetPcieControllerSbiPid ((UINT32) (RpIndex / PCH_PCIE_CONTROLLER_PORTS)); +} + diff --git a/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/Library/PeiDxeSmmPchPcieRpLib/PeiDxeSmmPchPcieRpLibVer2.inf b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/Library/PeiDxeSmmPchPcieRpLib/PeiDxeSmmPchPcieRpLibVer2.inf new file mode 100644 index 0000000000..4fc217581b --- /dev/null +++ b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/Library/PeiDxeSmmPchPcieRpLib/PeiDxeSmmPchPcieRpLibVer2.inf @@ -0,0 +1,39 @@ +## @file +# PCIE root port Library. +# +# All function in this library is available for PEI, DXE, and SMM, +# But do not support UEFI RUNTIME environment call. +# +# Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + + +[Defines] +INF_VERSION = 0x00010017 +BASE_NAME = PeiDxeSmmPchPcieRpLib +FILE_GUID = B522981C-E0C5-4E04-A82A-C61D4F0B2C75 +VERSION_STRING = 1.0 +MODULE_TYPE = BASE +LIBRARY_CLASS = PchPcieRpLib + + +[LibraryClasses] +BaseLib +IoLib +DebugLib +PciSegmentLib +PchInfoLib +PchPcrLib + + +[Packages] +MdePkg/MdePkg.dec +TigerlakeSiliconPkg/SiPkg.dec + + +[Sources] +PchPcieRpLib.c +PchPcieRpLibVer2.c + diff --git a/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/DxePchPcieRpPolicyLib/DxePchPcieRpPolicyLib.c b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/DxePchPcieRpPolicyLib/DxePchPcieRpPolicyLib.c new file mode 100644 index 0000000000..577e436e32 --- /dev/null +++ b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/DxePchPcieRpPolicyLib/DxePchPcieRpPolicyLib.c @@ -0,0 +1,179 @@ +/** @file + This file provides services for PcieRp policy function + + Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#include <Uefi.h> +#include <Library/DebugLib.h> +#include <Library/SiConfigBlockLib.h> +#include <Library/ConfigBlockLib.h> +#include <Protocol/PchPolicy.h> +#include <PchPcieRpConfig.h> + +#define PCI_CLASS_NETWORK 0x02 +#define PCI_CLASS_NETWORK_ETHERNET 0x00 +#define PCI_CLASS_NETWORK_OTHER 0x80 + +GLOBAL_REMOVE_IF_UNREFERENCED PCH_PCIE_DEVICE_OVERRIDE mPcieDeviceTable[] = { + // + // Intel PRO/Wireless + // + { 0x8086, 0x422b, 0xff, 0xff, 0xff, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + { 0x8086, 0x422c, 0xff, 0xff, 0xff, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + { 0x8086, 0x4238, 0xff, 0xff, 0xff, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + { 0x8086, 0x4239, 0xff, 0xff, 0xff, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + // + // Intel WiMAX/WiFi Link + // + { 0x8086, 0x0082, 0xff, 0xff, 0xff, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + { 0x8086, 0x0085, 0xff, 0xff, 0xff, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + { 0x8086, 0x0083, 0xff, 0xff, 0xff, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + { 0x8086, 0x0084, 0xff, 0xff, 0xff, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + { 0x8086, 0x0086, 0xff, 0xff, 0xff, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + { 0x8086, 0x0087, 0xff, 0xff, 0xff, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + { 0x8086, 0x0088, 0xff, 0xff, 0xff, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + { 0x8086, 0x0089, 0xff, 0xff, 0xff, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + { 0x8086, 0x008F, 0xff, 0xff, 0xff, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + { 0x8086, 0x0090, 0xff, 0xff, 0xff, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + // + // Intel Crane Peak WLAN NIC + // + { 0x8086, 0x08AE, 0xff, PCI_CLASS_NETWORK, PCI_CLASS_NETWORK_OTHER, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + { 0x8086, 0x08AF, 0xff, PCI_CLASS_NETWORK, PCI_CLASS_NETWORK_OTHER, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + // + // Intel Crane Peak w/BT WLAN NIC + // + { 0x8086, 0x0896, 0xff, PCI_CLASS_NETWORK, PCI_CLASS_NETWORK_OTHER, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + { 0x8086, 0x0897, 0xff, PCI_CLASS_NETWORK, PCI_CLASS_NETWORK_OTHER, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + // + // Intel Kelsey Peak WiFi, WiMax + // + { 0x8086, 0x0885, 0xff, PCI_CLASS_NETWORK, PCI_CLASS_NETWORK_OTHER, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + { 0x8086, 0x0886, 0xff, PCI_CLASS_NETWORK, PCI_CLASS_NETWORK_OTHER, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + // + // Intel Centrino Wireless-N 105 + // + { 0x8086, 0x0894, 0xff, PCI_CLASS_NETWORK, PCI_CLASS_NETWORK_OTHER, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + { 0x8086, 0x0895, 0xff, PCI_CLASS_NETWORK, PCI_CLASS_NETWORK_OTHER, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + // + // Intel Centrino Wireless-N 135 + // + { 0x8086, 0x0892, 0xff, PCI_CLASS_NETWORK, PCI_CLASS_NETWORK_OTHER, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + { 0x8086, 0x0893, 0xff, PCI_CLASS_NETWORK, PCI_CLASS_NETWORK_OTHER, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + // + // Intel Centrino Wireless-N 2200 + // + { 0x8086, 0x0890, 0xff, PCI_CLASS_NETWORK, PCI_CLASS_NETWORK_OTHER, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + { 0x8086, 0x0891, 0xff, PCI_CLASS_NETWORK, PCI_CLASS_NETWORK_OTHER, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + // + // Intel Centrino Wireless-N 2230 + // + { 0x8086, 0x0887, 0xff, PCI_CLASS_NETWORK, PCI_CLASS_NETWORK_OTHER, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + { 0x8086, 0x0888, 0xff, PCI_CLASS_NETWORK, PCI_CLASS_NETWORK_OTHER, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + // + // Intel Centrino Wireless-N 6235 + // + { 0x8086, 0x088E, 0xff, PCI_CLASS_NETWORK, PCI_CLASS_NETWORK_OTHER, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + { 0x8086, 0x088F, 0xff, PCI_CLASS_NETWORK, PCI_CLASS_NETWORK_OTHER, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + // + // Intel CampPeak 2 Wifi + // + { 0x8086, 0x08B5, 0xff, PCI_CLASS_NETWORK, PCI_CLASS_NETWORK_OTHER, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + { 0x8086, 0x08B6, 0xff, PCI_CLASS_NETWORK, PCI_CLASS_NETWORK_OTHER, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + // + // Intel WilkinsPeak 1 Wifi + // + { 0x8086, 0x08B3, 0xff, PCI_CLASS_NETWORK, PCI_CLASS_NETWORK_OTHER, PchPcieAspmL1, PchPcieL1L2AndL1SubstatesOverride, 0x0158, 0x0000000F, 0, 0, 0, 0, 0 }, + { 0x8086, 0x08B4, 0xff, PCI_CLASS_NETWORK, PCI_CLASS_NETWORK_OTHER, PchPcieAspmL1, PchPcieL1L2AndL1SubstatesOverride, 0x0158, 0x0000000F, 0, 0, 0, 0, 0 }, + // + // Intel Wilkins Peak 2 Wifi + // + { 0x8086, 0x08B1, 0xff, PCI_CLASS_NETWORK, PCI_CLASS_NETWORK_OTHER, PchPcieAspmL1, PchPcieL1L2AndL1SubstatesOverride, 0x0158, 0x0000000F, 0, 0, 0, 0, 0 }, + { 0x8086, 0x08B2, 0xff, PCI_CLASS_NETWORK, PCI_CLASS_NETWORK_OTHER, PchPcieAspmL1, PchPcieL1L2AndL1SubstatesOverride, 0x0158, 0x0000000F, 0, 0, 0, 0, 0 }, + // + // Intel Wilkins Peak PF Wifi + // + { 0x8086, 0x08B0, 0xff, PCI_CLASS_NETWORK, PCI_CLASS_NETWORK_OTHER, PchPcieAspmL1, PchPcieL1L2Override, 0, 0, 0, 0, 0, 0, 0 }, + // + // Teton Glacier Endpoint + // + { 0x8086, 0x0975, 0xff, 0, 0, 0, PchPcieL1SubstatesOverride, 0, 0xff, 0x3C, 0, 5, 0, 0, 0, 0 }, + + // + // End of Table + // + { 0 } +}; + +/** + Print PCIE_RP_DXE_CONFIG. + + @param[in] PchPolicy Pointer to a PCH_POLICY_PROTOCOL +**/ +VOID +PchPcieRpDxePrintConfig ( + IN PCH_POLICY_PROTOCOL *PchPolicy + ) +{ + EFI_STATUS Status; + PCIE_RP_DXE_CONFIG *PchPcieRpDxeConfig; + + Status = GetConfigBlock ((VOID *) PchPolicy, &gPchPcieRpDxeConfigGuid, (VOID *) &PchPcieRpDxeConfig); + ASSERT_EFI_ERROR (Status); + + DEBUG ((DEBUG_INFO, "------------------ PCH PCIE DXE CONFIG ------------------\n")); + DEBUG ((DEBUG_INFO, " PcieDeviceOverrideTablePtr : 0x%x\n",PchPcieRpDxeConfig->PcieDeviceOverrideTablePtr)); +} + +/** + Load DXE Config block default for Pch PCIE RP + + @param[in] ConfigBlockPointer Pointer to config block +**/ +VOID +LoadPchPcieRpDxeConfigDefault ( + IN VOID *ConfigBlockPointer + ) +{ + PCIE_RP_DXE_CONFIG *PchPcieRpDxeConfig; + + PchPcieRpDxeConfig = ConfigBlockPointer; + PchPcieRpDxeConfig->PcieDeviceOverrideTablePtr = mPcieDeviceTable; +} + +STATIC COMPONENT_BLOCK_ENTRY mPchPcieRpBlocks = { + &gPchPcieRpDxeConfigGuid, + sizeof (PCIE_RP_DXE_CONFIG), + PCIE_RP_DXE_CONFIG_REVISION, + LoadPchPcieRpDxeConfigDefault +}; + +/** + Get PchPcieRp config block table size. + + @retval Size of config block +**/ +UINT16 +PchPcieRpDxeGetConfigBlockTotalSize ( + VOID + ) +{ + return mPchPcieRpBlocks.Size; +} + +/** + Add PchPcieRp ConfigBlock. + + @param[in] ConfigBlockTableAddress The pointer to config block table + + @retval EFI_SUCCESS The policy default is initialized. + @retval EFI_OUT_OF_RESOURCES Insufficient resources to create buffer +**/ +EFI_STATUS +PchPcieRpDxeAddConfigBlock ( + IN VOID *ConfigBlockTableAddress + ) +{ + return AddComponentConfigBlocks (ConfigBlockTableAddress, &mPchPcieRpBlocks, 1); +} diff --git a/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/DxePchPcieRpPolicyLib/DxePchPcieRpPolicyLib.inf b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/DxePchPcieRpPolicyLib/DxePchPcieRpPolicyLib.inf new file mode 100644 index 0000000000..6158e77a35 --- /dev/null +++ b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/DxePchPcieRpPolicyLib/DxePchPcieRpPolicyLib.inf @@ -0,0 +1,30 @@ +## @file +# Component description file for the PchPcieRp policy library +# +# Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] +INF_VERSION = 0x00010017 +BASE_NAME = DxePchPcieRpPolicyLib +FILE_GUID = 328B0DE5-189A-4DB0-BFE1-557F4F89010B +VERSION_STRING = 1.0 +MODULE_TYPE = BASE +LIBRARY_CLASS = DxePchPcieRpPolicyLib + +[LibraryClasses] +DebugLib +ConfigBlockLib +SiConfigBlockLib + +[Packages] +MdePkg/MdePkg.dec +TigerlakeSiliconPkg/SiPkg.dec + +[Sources] +DxePchPcieRpPolicyLib.c + +[Guids] +gPchPcieRpDxeConfigGuid ## CONSUMES diff --git a/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/PciExpressHelpersLibrary/PciExpressHelpersLibrary.c b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/PciExpressHelpersLibrary/PciExpressHelpersLibrary.c new file mode 100644 index 0000000000..401a9fbe7b --- /dev/null +++ b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/PciExpressHelpersLibrary/PciExpressHelpersLibrary.c @@ -0,0 +1,1997 @@ +/** @file + This file contains routines that support PCI Express initialization + + Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#include "PciExpressHelpersLibrary.h" +#include <Library/BaseMemoryLib.h> +#include <Library/PcieHelperLib.h> +#include <Library/PchPciBdfLib.h> +#include <Library/PcieRpLib.h> +#include <PchPcieRpInfo.h> +#include <CpuPcieInfo.h> +#include <CpuPcieHob.h> +#include <Library/HobLib.h> +#include <Register/PchPcieRpRegs.h> + +#define ASPM_L1_NO_LIMIT 0xFF +#define ASPM_L0s_NO_LIMIT 0x7 +#define LINK_RETRAIN_WAIT_TIME 1000 // microseconds + +typedef union { + struct { + UINT32 RequesterCapable : 1; + UINT32 ResponderCapable : 1; + UINT32 RootCapable : 1; + UINT32 Reserved : 5; + UINT32 LocalClockGranularity : 8; + UINT32 Reserved2 : 16; + } Bits; + UINT32 Uint32; +} PTM_CAPS; + +typedef union { + struct { + UINT32 Enable : 1; + UINT32 RootSelect : 1; + UINT32 Reserved : 6; + UINT32 EffectiveGranularity : 8; + UINT32 Reserved2 : 16; + } Bits; + UINT32 Uint32; +} PTM_CTRL; + +typedef struct { + UINT32 Size; + const PCH_PCIE_DEVICE_OVERRIDE* Table; +} OVERRIDE_TABLE; + +typedef enum { + DevTypePci, + DevTypePcieEndpoint, + DevTypePcieUpstream, + DevTypePcieDownstream, + DevTypeMax +} PCI_DEV_TYPE; + +// +// This structure keeps in one place all data relevant to enabling L0s and L1. +// L0s latencies are encoded in the same way as in hardware registers. The only operation +// that will be performed on them is comparison +// L1 latencies are decoded to microseconds, because they will be used in subtractions and additions +// +typedef struct { + UINT32 L0sSupported : 1; + UINT32 L1Supported : 1; + UINT32 L0sAcceptableLatency : 3; // encoded as in hardware register + UINT32 L1AcceptableLatencyUs : 8; // decoded to microseconds + UINT32 LinkL0sExitLatency : 3; // encoded as in hardware register + UINT32 LinkL1ExitLatencyUs : 8; // decoded to microseconds +} ASPM_CAPS; + +typedef struct { + UINT32 AspmL11 : 1; + UINT32 AspmL12 : 1; + UINT32 PmL11 : 1; + UINT32 PmL12 : 1; + UINT32 Cmrt : 8; // Common Mode Restore Time + UINT32 TpoScale : 2; // T power_on scale + UINT32 TpoValue : 6; // T power_on value +} L1SS_CAPS; + +#define MAX_SBDF_TABLE_SIZE 50 //arbitrary table size; big enough to accomodate even full length TBT chain. + +typedef struct { + UINT32 Count; + SBDF Entry [MAX_SBDF_TABLE_SIZE]; +} SBDF_TABLE; + +/** + Converts device's segment:bus:device:function coordinates to flat address + + @param[in] Sbdf device's segment:bus:device:function coordinates + + @retval address of device's PCI cfg space +**/ +STATIC +UINT64 +SbdfToBase ( + SBDF Sbdf + ) +{ + return PCI_SEGMENT_LIB_ADDRESS (Sbdf.Seg, Sbdf.Bus, Sbdf.Dev, Sbdf.Func, 0); +} + +/* + Converts Tpower_on from value:scale notation to microseconds + + @param[in] TpoScale T power on scale + @param[in] TpoValue T power on value + + @retval number of microseconds +*/ +UINT32 +TpoToUs ( + UINT32 TpoScale, + UINT32 TpoValue + ) +{ + static const UINT8 TpoScaleMultiplier[] = {2, 10, 100}; + + ASSERT (TpoScale < TpoScaleMax); + if (TpoScale >= TpoScaleMax) { + return 0; + } + return (TpoScaleMultiplier[TpoScale] * TpoValue); +} + +/** + Get max payload size supported by device. + + @param[in] Sbdf device's segment:bus:device:function coordinates + + @retval Max payload size, encoded in the same way as in register (0=128b, 1=256b, etc) +**/ +STATIC +UINT8 +GetMps ( + SBDF Sbdf + ) +{ + return (PciSegmentRead16 (SbdfToBase (Sbdf) + Sbdf.PcieCap + R_PCIE_DCAP_OFFSET) & B_PCIE_DCAP_MPS); +} + +/** + Sets Maximum Payload Size to be used by device + + @param[in] Sbdf device's segment:bus:device:function coordinates + @param[in] Mps Max payload size, encoded in the same way as in register (0=128b, 1=256b, etc) +**/ +STATIC +VOID +SetMps ( + SBDF Sbdf, + UINT8 Mps + ) +{ + PciSegmentAndThenOr16 (SbdfToBase (Sbdf) + Sbdf.PcieCap + R_PCIE_DCTL_OFFSET, (UINT16) ~B_PCIE_DCTL_MPS, Mps << N_PCIE_DCTL_MPS); +} + +/** + Enables LTR feature in given device + + @param[in] Sbdf device's segment:bus:device:function coordinates +**/ +STATIC +VOID +EnableLtr ( + SBDF Sbdf + ) +{ + if (Sbdf.PcieCap == 0) { + return; + } + PciSegmentOr32 (SbdfToBase (Sbdf) + Sbdf.PcieCap + R_PCIE_DCTL2_OFFSET, B_PCIE_DCTL2_LTREN); +} + +/** + Returns information about type of device. + + @param[out] Sbdf device's segment:bus:device:function coordinates + + @retval one of: not a PCIe device (legacy PCI), PCIe endpoint, PCIe upstream port or PCIe downstream port (including rootport) +**/ +STATIC +PCI_DEV_TYPE +GetDeviceType ( + SBDF Sbdf + ) +{ + UINT8 DeviceType; + + if (Sbdf.PcieCap == 0) { + return DevTypePci; + } + DeviceType = (UINT8) ((PciSegmentRead16 (SbdfToBase (Sbdf) + Sbdf.PcieCap + R_PCIE_XCAP_OFFSET) & B_PCIE_XCAP_DT) >> N_PCIE_XCAP_DT); + if (DeviceType == PCIE_DEVICE_PORT_TYPE_UPSTREAM_PORT) { + return DevTypePcieUpstream; + } else if (DeviceType == PCIE_DEVICE_PORT_TYPE_DOWNSTREAM_PORT || DeviceType == PCIE_DEVICE_PORT_TYPE_ROOT_PORT) { + return DevTypePcieDownstream; + } else { + return DevTypePcieEndpoint; + } +} + +/** + Initializes Dev:Func numbers for use in FindNextPcieChild or FindNextLegalSbdf functions. + + @param[out] Sbdf device's segment:bus:device:function coordinates +**/ +STATIC +VOID +InitChildFinder ( + OUT SBDF *Sbdf + ) +{ + // + // Initialize Dev/Func to maximum values, so that when FindNextLegalSbdf () + // is called on those input parameters, it will return 1st legal address (Dev 0 Func 0). + // + Sbdf->Dev = PCI_MAX_DEVICE; + Sbdf->Func = PCI_MAX_FUNC; +} + +/** + Checks the device is a bridge and has non-zero secondary bus number assigned. + If so, it returns TRUE and initializes ChildSbdf with such values that + allow searching for devices on the secondary bus. + ChildSbdf will be mangled even if this function returns FALSE. + + Legal bus assignment is assumed. This function doesn't check subordinate bus numbers of + the the device it was called on or any bridges between it and root complex + + @param[in] Sbdf device's segment:bus:device:function coordinates + @param[out] ChildSbdf SBDF initialized in such way that calling FindNextPcieChild( ) on it will find all children devices + + @retval TRUE if device is a bridge and has a bus behind it; FALSE otherwise +**/ +STATIC +BOOLEAN +HasChildBus ( + SBDF Sbdf, + SBDF *ChildSbdf + ) +{ + UINT32 Data32; + UINT64 Base; + UINT8 SecondaryBus; + + ChildSbdf->Seg = Sbdf.Seg; + InitChildFinder (ChildSbdf); + + Base = SbdfToBase (Sbdf); + + if (PciSegmentRead8 (Base + R_PCI_BCC_OFFSET) != PCI_CLASS_BRIDGE) { + DEBUG ((DEBUG_INFO, "HasChildBus%02:%02:%02: no\n", Sbdf.Bus, Sbdf.Dev, Sbdf.Func)); + return FALSE; + } + Data32 = PciSegmentRead32 (Base + PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET); + SecondaryBus = (UINT8)((Data32 & B_PCI_BRIDGE_BNUM_SCBN) >> 8); + ChildSbdf->Bus = SecondaryBus; + if (SecondaryBus == 0) { + DEBUG ((DEBUG_INFO, "HasChildBus%02x:%02x:%02x: no\n", Sbdf.Bus, Sbdf.Dev, Sbdf.Func)); + return FALSE; + } else { + DEBUG ((DEBUG_INFO, "HasChildBus%02x:%02x:%02x: yes, %x\n", Sbdf.Bus, Sbdf.Dev, Sbdf.Func, SecondaryBus)); + return TRUE; + } +} + +/** + Sets LTR limit in a device. + + @param[in] Base device's base address + @param[in] Ltr LTR limit +**/ +STATIC +VOID +SetLtrLimit ( + UINT64 Base, + LTR_LIMIT Ltr + ) +{ + UINT16 LtrCapOffset; + UINT16 Data16; + + LtrCapOffset = PcieBaseFindExtendedCapId (Base, R_PCIE_LTRECH_CID); + if (LtrCapOffset == 0) { + return; + } + Data16 = (UINT16)((Ltr.MaxSnoopLatencyValue << N_PCIE_LTRECH_MSLR_VALUE) | (Ltr.MaxSnoopLatencyScale << N_PCIE_LTRECH_MSLR_SCALE)); + PciSegmentWrite16(Base + LtrCapOffset + R_PCIE_LTRECH_MSLR_OFFSET, Data16); + + Data16 = (UINT16)((Ltr.MaxNoSnoopLatencyValue << N_PCIE_LTRECH_MNSLR_VALUE) | (Ltr.MaxNoSnoopLatencyScale << N_PCIE_LTRECH_MNSLR_SCALE)); + PciSegmentWrite16(Base + LtrCapOffset + R_PCIE_LTRECH_MNSLR_OFFSET, Data16); +} + +/** + Checks if device at given address exists and is a PCI Express device. + PCI express devices are distinguished from PCI by having Capability ID 0x10 + If the device is PCI express then its SDBF structure gets updated with pointer to + the PCIe Capability. This is an optimization feature. It greatly decreases the number + of bus accesses, since most features configured by this library depend on registers + whose location is relative to PCIe capability. + + @param[in,out] Sbdf on entry, segment:bus:device:function coordinates + on exit, PcieCap offset is updated + + @retval TRUE when PCIe device exists; FALSE if it's not PCIe or there's no device at all +**/ +STATIC +BOOLEAN +IsPcieDevice ( + SBDF *Sbdf + ) +{ + UINT8 PcieCapOffset; + UINT64 Base; + + Base = SbdfToBase(*Sbdf); + + if (PciSegmentRead16 (Base) == 0xFFFF) { + return FALSE; + } + + + PcieCapOffset = PcieBaseFindCapId (Base, EFI_PCI_CAPABILITY_ID_PCIEXP); + if (PcieCapOffset == 0) { + DEBUG ((DEBUG_INFO, "IsPcieDevice %02x:%02x:%02x - legacy\n", Sbdf->Bus, Sbdf->Dev, Sbdf->Func)); + return FALSE; + } else { + Sbdf->PcieCap = PcieCapOffset; + DEBUG ((DEBUG_INFO, "IsPcieDevice %02x:%02x:%02x - yes\n", Sbdf->Bus, Sbdf->Dev, Sbdf->Func)); + return TRUE; + } +} + +/** + Returns TRUE and Dev:Func numbers where a PCIe device could legally be located, or FALSE if there + no such coordinates left. + + Segment and Bus fields of SBDF structure are input only and determine which bus will be scanned. + This function should be called in a while() loop. It replaces the less efficient method of + using nested FOR loops that iterate over all device and function numbers. It is optimized for + the amount of bus access. If function0 doesn't exist or doesn't have Multifunction bit set, + then higher function numbers are skipped. If parent of this bus is a downstream port, then + Device numbers 1-31 get skipped too (there can be only Dev0 behind downstream ports) + If device/function number == 0x1F/0x7, this function returns first possible address, that is 0:0 + Any other device number means Dev:Func contain address of last found child device + and this function should search for next one + + @param[in] ParentDevType type of bridge who's partent of this bus + @param[in,out] Sbdf On entry: location returned previously from this function + Dev:Func value of 1F:07 means search should start from the beginning + On exit: if legal Dev:Func combination was found, that Dev:Func is returned + otherwise, Dev:Func are initialized to 1F:07 for convenience + + @retval TRUE when next legal Dev:Func address was found; FALSE otherwise +**/ +STATIC +BOOLEAN +FindNextLegalSbdf ( + IN PCI_DEV_TYPE ParentDevType, + IN OUT SBDF *Sbdf + ) +{ + UINT8 MaxDev; + UINT64 Func0Base; + + if (ParentDevType == DevTypePcieEndpoint) { + return FALSE; + } + if (ParentDevType == DevTypePcieUpstream) { + MaxDev = PCI_MAX_DEVICE; + } else { + MaxDev = 0; + } + Func0Base = PCI_SEGMENT_LIB_ADDRESS (Sbdf->Seg, Sbdf->Bus, Sbdf->Dev, 0, 0); + if ((Sbdf->Dev == PCI_MAX_DEVICE) && Sbdf->Func == PCI_MAX_FUNC) { + Sbdf->Dev = 0; + Sbdf->Func = 0; + return TRUE; + } else if ((Sbdf->Func == PCI_MAX_FUNC) || (Sbdf->Func == 0 && !IsMultifunctionDevice (Func0Base))) { + // + // if it's the last function of a device, then return Func0 of new device or FALSE in case there are no more devices + // + if (Sbdf->Dev == MaxDev) { + InitChildFinder (Sbdf); + return FALSE; + } + (Sbdf->Dev)++; + Sbdf->Func = 0; + return TRUE; + } else { + (Sbdf->Func)++; + return TRUE; + } +} + +/** + Finds next PCIe (not legacy PCI) device behind given device + If device/function number == 0x1F/0x7, this function searches for children from scratch + Any other device number means Dev:Func contain address of last found child device + and this function should search for next one + + @param[in] ParentDevType type of bridge who's partent of this bus + @param[in,out] Sbdf On entry: location returned previously from this function + Dev:Func value of 0x1F:0x07 means search should start from the beginning + On exit: if PCIe device was found, its SBDF coordinates are returned + otherwise, Dev:Func are initialized to 0x1F:0x07 for convenience + @retval TRUE when next PCIe device was found; FALSE otherwise +**/ +BOOLEAN +FindNextPcieChild ( + IN PCI_DEV_TYPE ParentDevType, + IN OUT SBDF *Sbdf + ) +{ + while ( FindNextLegalSbdf (ParentDevType, Sbdf)) { + if (IsPcieDevice (Sbdf)) { + return TRUE; + } + } + return FALSE; +} + +/** + Checks if given device supports Clock Power Management + + @param[in] Sbdf segment:bus:device:function coordinates of a device + + @retval TRUE when device supports it, FALSE otherwise +**/ +STATIC +BOOLEAN +IsCpmSupported ( + SBDF Sbdf + ) +{ + return !!(PciSegmentRead32 (SbdfToBase (Sbdf) + Sbdf.PcieCap + R_PCIE_LCAP_OFFSET) & B_PCIE_LCAP_CPM); +} + +/** + Sets Enable Clock Power Management bit for given device + + @param[in] Sbdf segment:bus:device:function coordinates of a device +**/ +STATIC +VOID +EnableCpm ( + SBDF Sbdf + ) +{ + PciSegmentOr16 (SbdfToBase (Sbdf) + Sbdf.PcieCap + R_PCIE_LCTL_OFFSET, B_PCIE_LCTL_ECPM); +} + +/** + Checks if given device is an IoAPIC + + @param[in] Base device's base address + + @retval TRUE if it's an IoAPIC +**/ +BOOLEAN +IsIoApicDevice ( + UINT64 Base + ) +{ + UINT8 BaseClassCode; + UINT8 SubClassCode; + UINT8 ProgInterface; + + BaseClassCode = PciSegmentRead8 (Base + PCI_CLASSCODE_OFFSET + 2); + SubClassCode = PciSegmentRead8 (Base + PCI_CLASSCODE_OFFSET + 1); + ProgInterface = PciSegmentRead8 (Base + PCI_CLASSCODE_OFFSET); + if ((BaseClassCode == PCI_CLASS_SYSTEM_PERIPHERAL) && + (SubClassCode == PCI_SUBCLASS_PIC) && + ((ProgInterface == PCI_IF_APIC_CONTROLLER) || + (ProgInterface == PCI_IF_APIC_CONTROLLER2))) { + return TRUE; + } + return FALSE; +} + +/** + There are some devices which support L1 substates, but due to silicon bugs the corresponding register + cannot be found by scanning PCIe capabilities. This function checks list of such devices and if one + is found, returns its L1ss capability register offset + + @param[in] Base base address of device + @param[in] Override table of devices that need override + + @retval offset to L1ss capability register +**/ +UINT16 +GetOverrideL1ssCapsOffset ( + UINT64 Base, + OVERRIDE_TABLE *Override + ) +{ + UINT16 DeviceId; + UINT16 VendorId; + UINT8 Revision; + UINT32 Index; + + VendorId = PciSegmentRead16 (Base + PCI_VENDOR_ID_OFFSET); + DeviceId = PciSegmentRead16 (Base + PCI_DEVICE_ID_OFFSET); + Revision = PciSegmentRead8 (Base + PCI_REVISION_ID_OFFSET); + + for (Index = 0; Index < Override->Size; Index++) { + if (((Override->Table[Index].OverrideConfig & PchPcieL1SubstatesOverride) == PchPcieL1SubstatesOverride) && + (Override->Table[Index].VendorId == VendorId) && + (Override->Table[Index].DeviceId == DeviceId) && + (Override->Table[Index].RevId == Revision || Override->Table[Index].RevId == 0xFFFF)) { + return Override->Table[Index].L1SubstatesCapOffset; + } + } + return 0; +} + +/** + There are some devices whose implementation of L1 substates is partially broken. This function checks + list of such devices and if one is found, overrides their L1ss-related capabilities + + @param[in] Base base address of device + @param[in] Override table of devices that need override + @param[in,out] L1ss on entry, capabilities read from register; on exit, capabilities modified according ot override table +**/ +STATIC +VOID +OverrideL1ssCaps ( + UINT64 Base, + OVERRIDE_TABLE *Override, + L1SS_CAPS *L1ss + ) +{ + UINT16 DeviceId; + UINT16 VendorId; + UINT8 Revision; + UINT32 Index; + + VendorId = PciSegmentRead16 (Base + PCI_VENDOR_ID_OFFSET); + DeviceId = PciSegmentRead16 (Base + PCI_DEVICE_ID_OFFSET); + Revision = PciSegmentRead8 (Base + PCI_REVISION_ID_OFFSET); + + for (Index = 0; Index < Override->Size; Index++) { + if (((Override->Table[Index].OverrideConfig & PchPcieL1SubstatesOverride) == PchPcieL1SubstatesOverride) && + (Override->Table[Index].VendorId == VendorId) && + (Override->Table[Index].DeviceId == DeviceId) && + (Override->Table[Index].RevId == Revision || Override->Table[Index].RevId == 0xFF)) { + + L1ss->PmL12 &= !!(Override->Table[Index].L1SubstatesCapMask & B_PCIE_EX_L1SCAP_PPL12S); + L1ss->PmL11 &= !!(Override->Table[Index].L1SubstatesCapMask & B_PCIE_EX_L1SCAP_PPL11S); + L1ss->AspmL12 &= !!(Override->Table[Index].L1SubstatesCapMask & B_PCIE_EX_L1SCAP_AL12S); + L1ss->AspmL11 &= !!(Override->Table[Index].L1SubstatesCapMask & B_PCIE_EX_L1SCAP_AL1SS); + if (Override->Table[Index].L1sTpowerOnValue != 0) { + L1ss->Cmrt = Override->Table[Index].L1sCommonModeRestoreTime; + L1ss->TpoScale = Override->Table[Index].L1sTpowerOnScale; + L1ss->TpoValue = Override->Table[Index].L1sTpowerOnValue; + } + return; + } + } +} + +/** + Returns L1 sub states capabilities of a device + + @param[in] Base base address of a device + + @retval L1SS_CAPS structure filled with device's capabilities +**/ +STATIC +L1SS_CAPS +GetL1ssCaps ( + UINT64 Base, + OVERRIDE_TABLE *Override + ) +{ + L1SS_CAPS Capabilities = {0}; + UINT16 PcieCapOffset; + UINT32 CapsRegister; + + PcieCapOffset = GetOverrideL1ssCapsOffset (Base, Override); + if (PcieCapOffset == 0) { + PcieCapOffset = PcieBaseFindExtendedCapId (Base, V_PCIE_EX_L1S_CID); + } + if (PcieCapOffset == 0) { + return Capabilities; + } + CapsRegister = PciSegmentRead32 (Base + PcieCapOffset + R_PCIE_EX_L1SCAP_OFFSET); + if (CapsRegister & B_PCIE_EX_L1SCAP_L1PSS) { + // + // Skip L1.1 checking since some device only indecate L1.2 support. + // [1604452805] + // + Capabilities.PmL11 = !!(CapsRegister & B_PCIE_EX_L1SCAP_PPL11S); + Capabilities.PmL12 = !!(CapsRegister & B_PCIE_EX_L1SCAP_PPL12S); + Capabilities.AspmL12 = !!(CapsRegister & B_PCIE_EX_L1SCAP_AL12S); + Capabilities.AspmL11 = !!(CapsRegister & B_PCIE_EX_L1SCAP_AL1SS); + Capabilities.Cmrt = (CapsRegister & B_PCIE_EX_L1SCAP_CMRT) >> N_PCIE_EX_L1SCAP_CMRT; + Capabilities.TpoValue = (CapsRegister & B_PCIE_EX_L1SCAP_PTV) >> N_PCIE_EX_L1SCAP_PTV; + Capabilities.TpoScale = (CapsRegister & B_PCIE_EX_L1SCAP_PTPOS) >> N_PCIE_EX_L1SCAP_PTPOS; + } + OverrideL1ssCaps (Base, Override, &Capabilities); + return Capabilities; +} + +/** + Returns combination of two sets of L1 substate capabilities + Given feature is supported by the link only if both sides support it + Time parameters for link (Cmrt and Tpo) depend on the bigger value between two sides + + @param[in] L1ssA L1 substate capabilities of first device + @param[in] L1ssB L1 substate capabilities of second device + + @retval Link's L1 substate capabilities +**/ +STATIC +L1SS_CAPS +CombineL1ss ( + L1SS_CAPS L1ssA, + L1SS_CAPS L1ssB + ) +{ + L1SS_CAPS Combined; + + Combined.PmL12 = L1ssA.PmL12 && L1ssB.PmL12; + Combined.PmL11 = L1ssA.PmL11 && L1ssB.PmL11; + Combined.AspmL12 = L1ssA.AspmL12 && L1ssB.AspmL12; + Combined.AspmL11 = L1ssA.AspmL11 && L1ssB.AspmL11; + Combined.Cmrt = MAX (L1ssA.Cmrt, L1ssB.Cmrt); + if (TpoToUs (L1ssA.TpoScale, L1ssA.TpoValue) > TpoToUs (L1ssB.TpoScale, L1ssB.TpoValue)) { + Combined.TpoScale = L1ssA.TpoScale; + Combined.TpoValue = L1ssA.TpoValue; + } else { + Combined.TpoScale = L1ssB.TpoScale; + Combined.TpoValue = L1ssB.TpoValue; + } + return Combined; +} + +/** + Configures L1 substate feature in a device + + @param[in] Sbdf segment:bus:device:function coordinates of a device + @param[in] L1ss configuration to be programmed + @param[in] Override table of devices that require special handling +**/ +STATIC +VOID +SetL1ss ( + SBDF Sbdf, + L1SS_CAPS L1ss, + OVERRIDE_TABLE *Override, + BOOLEAN IsCpuPcie + + ) +{ + UINT16 PcieCapOffset; + UINT32 Ctrl1Register; + UINT32 Ctrl2Register; + UINT64 Base; + + Base = SbdfToBase(Sbdf); + Ctrl1Register = 0; + Ctrl2Register = 0; + + PcieCapOffset = GetOverrideL1ssCapsOffset (Base, Override); + if (PcieCapOffset == 0) { + PcieCapOffset = PcieBaseFindExtendedCapId (Base, V_PCIE_EX_L1S_CID); + } + if (PcieCapOffset == 0) { + return; + } + + Ctrl1Register |= (L1ss.PmL12 ? B_PCIE_EX_L1SCAP_PPL12S : 0); + Ctrl1Register |= (L1ss.PmL11 ? B_PCIE_EX_L1SCAP_PPL11S : 0); + Ctrl1Register |= (L1ss.AspmL12 ? B_PCIE_EX_L1SCAP_AL12S : 0); + Ctrl1Register |= (L1ss.AspmL11 ? B_PCIE_EX_L1SCAP_AL1SS : 0); + if ((GetDeviceType (Sbdf) == DevTypePcieDownstream)) { + Ctrl1Register |= (L1ss.Cmrt << N_PCIE_EX_L1SCAP_CMRT); + } + /// + /// BWG 1.3 Section 5.5.7.6 LTR Threshold Latency + /// Set L1.2 LTR threshold using formula (TpoToUs (L1ss.TpoScale, L1ss.TpoValue) + L1ss.Cmrt + 10) + /// + Ctrl1Register |= ((TpoToUs (L1ss.TpoScale, L1ss.TpoValue) + L1ss.Cmrt + 10) << N_PCIE_EX_L1SCTL1_L12LTRTLV); + Ctrl1Register |= (2 << N_PCIE_EX_L1SCTL1_L12LTRTLSV); + + Ctrl2Register |= (L1ss.TpoScale); + Ctrl2Register |= (L1ss.TpoValue << N_PCIE_EX_L1SCTL2_POWT); + // + // Set CLKREQ Acceleration Interrupt Enable + // + Ctrl1Register |= B_PCIE_EX_L1SCTL1_L1SSEIE; + PciSegmentWrite32 (Base + PcieCapOffset + R_PCIE_EX_L1SCTL1_OFFSET, 0); + PciSegmentWrite32 (Base + PcieCapOffset + R_PCIE_EX_L1SCTL2_OFFSET, Ctrl2Register); + PciSegmentWrite32 (Base + PcieCapOffset + R_PCIE_EX_L1SCTL1_OFFSET, Ctrl1Register); +} + +/** + Converts L1 latency from enumerated register value to microseconds + + @param[in] L1Latency latency value retrieved from register; see PCIE specification for encoding + + @retval L1 latency converted to microseconds +**/ +UINT32 +L1LatencyToUs ( + UINT32 L1Latency + ) +{ + if (L1Latency < 7) { + return 1 * (BIT0 << L1Latency); + } else { + return ASPM_L1_NO_LIMIT; + } +} + +/** + Modifies L1 latency by provided value + + @param[in] Aspm Structure that contains ASPM capabilities of a link, including L1 acceptable latency + @param[in] Value Value, in microseconds, to be added to L1 acceptable latency. Can be negative. + + @retval Aspm structure with modified L1 acceptable latency +**/ +STATIC +ASPM_CAPS +PatchL1AcceptableLatency ( + ASPM_CAPS Aspm, + INT8 Value + ) +{ + if (Aspm.L1AcceptableLatencyUs != ASPM_L1_NO_LIMIT) { + if (Value > 0) { + Aspm.L1AcceptableLatencyUs += Value; + } else { + if (Aspm.L1AcceptableLatencyUs > (UINT32)(-1*Value)) { + Aspm.L1AcceptableLatencyUs = Aspm.L1AcceptableLatencyUs + Value; + } else { + Aspm.L1AcceptableLatencyUs = 0; + } + } + } + return Aspm; +} + +/** + Reads ASPM capabilities of a device + + @param[in] Sbdf segment:bus:device:function coordinates of a device + + @retval structure containing device's ASPM capabilities +**/ +STATIC +ASPM_CAPS +GetAspmCaps ( + SBDF Sbdf + ) +{ + + UINT32 LinkCapRegister; + UINT32 DevCapRegister; + UINT64 Base; + ASPM_CAPS Aspm = {0}; + + Base = SbdfToBase (Sbdf); + + LinkCapRegister = PciSegmentRead32 (Base + Sbdf.PcieCap + R_PCIE_LCAP_OFFSET); + DevCapRegister = PciSegmentRead32 (Base + Sbdf.PcieCap + R_PCIE_DCAP_OFFSET); + + /// + /// Check endpoint for pre-1.1 devices based on the Role based Error Reporting Capability bit. Don't report L0s support for old devices + /// + if (DevCapRegister & B_PCIE_DCAP_RBER) { + Aspm.L0sSupported = !!(LinkCapRegister & B_PCIE_LCAP_APMS_L0S); + } + Aspm.L1Supported = !!(LinkCapRegister & B_PCIE_LCAP_APMS_L1); + + Aspm.LinkL0sExitLatency = (LinkCapRegister & B_PCIE_LCAP_EL0) >> N_PCIE_LCAP_EL0; + Aspm.LinkL1ExitLatencyUs = L1LatencyToUs( (LinkCapRegister & B_PCIE_LCAP_EL1) >> N_PCIE_LCAP_EL1); + + if (GetDeviceType (Sbdf) == DevTypePcieEndpoint) { + Aspm.L0sAcceptableLatency = (DevCapRegister & B_PCIE_DCAP_E0AL) >> N_PCIE_DCAP_E0AL; + Aspm.L1AcceptableLatencyUs = L1LatencyToUs( (DevCapRegister & B_PCIE_DCAP_E1AL) >> N_PCIE_DCAP_E1AL); + DEBUG ((DEBUG_INFO, "GetAspmCaps %02x:%02x:%02x L0s%c %d:%d L1%c %d:%d\n", Sbdf.Bus, Sbdf.Dev, Sbdf.Func, + Aspm.L0sSupported?'+':'-', Aspm.LinkL0sExitLatency, Aspm.L0sAcceptableLatency, + Aspm.L1Supported?'+':'-', Aspm.LinkL1ExitLatencyUs, Aspm.L1AcceptableLatencyUs)); + } else { + Aspm.L0sAcceptableLatency = ASPM_L0s_NO_LIMIT; + Aspm.L1AcceptableLatencyUs = ASPM_L1_NO_LIMIT; + DEBUG ((DEBUG_INFO, "GetAspmCaps %02x:%02x:%02x L0s%c %d:x L1%c %d:x\n", Sbdf.Bus, Sbdf.Dev, Sbdf.Func, + Aspm.L0sSupported?'+':'-', Aspm.LinkL0sExitLatency, + Aspm.L1Supported?'+':'-', Aspm.LinkL1ExitLatencyUs)); + } + return Aspm; +} + +/** + Get ASPM L0s and L1 override of given device. + + @param[in] Sbdf Segment,Bus,Device,Function address of currently visited PCIe device + @param[in,out] MyAspm Current device's ASPM capabilities structure + @param[in] Override Pch Pcie devices OverrideTable +**/ +STATIC +VOID +GetOverrideAspm ( + SBDF Sbdf, + ASPM_CAPS *MyAspm, + OVERRIDE_TABLE *Override + ) +{ + UINT16 DeviceId; + UINT16 VendorId; + UINT8 Revision; + UINT32 Index; + UINT64 Base; + + Base = SbdfToBase (Sbdf); + + VendorId = PciSegmentRead16 (Base + PCI_VENDOR_ID_OFFSET); + DeviceId = PciSegmentRead16 (Base + PCI_DEVICE_ID_OFFSET); + Revision = PciSegmentRead8 (Base + PCI_REVISION_ID_OFFSET); + + for (Index = 0; Index < Override->Size; Index++) { + if (((Override->Table[Index].OverrideConfig & PchPcieL1L2Override) == PchPcieL1L2Override) && + (Override->Table[Index].VendorId == VendorId) && + (Override->Table[Index].DeviceId == DeviceId) && + (Override->Table[Index].RevId == Revision || Override->Table[Index].RevId == 0xFF)) { + DEBUG ((DEBUG_INFO, "GetOverrideAspm %02x:%02x:%02x, original L0sSupported = 0x%x, L1Supported = 0x%x\n", + Sbdf.Bus, Sbdf.Dev, Sbdf.Func, MyAspm->L0sSupported, MyAspm->L1Supported)); + if (MyAspm->L0sSupported) { + // + // If L0s is supported in capability, apply platform override. + // + MyAspm->L0sSupported = Override->Table[Index].EndPointAspm & BIT0; + } + if (MyAspm->L1Supported) { + // + // If L1 is supported in capability, apply platform override. + // + MyAspm->L1Supported = (Override->Table[Index].EndPointAspm & BIT1) >> 1; + } + DEBUG ((DEBUG_INFO, "GetOverrideAspm %02x:%02x:%02x, override L0sSupported = 0x%x, L1Supported = 0x%x\n", + Sbdf.Bus, Sbdf.Dev, Sbdf.Func, MyAspm->L0sSupported, MyAspm->L1Supported)); + } + } +} + +/** + Combines ASPM capabilities of two devices on both ends of a link to determine link's ASPM capabilities + + @param[in] AspmA, AspmB ASPM capabilities of two devices + + @retval ASPM_CAPS structure containing combined ASPM capabilities +**/ +STATIC +ASPM_CAPS +CombineAspm ( + ASPM_CAPS AspmA, + ASPM_CAPS AspmB, + BOOLEAN DownstreamPort + ) +{ + ASPM_CAPS Combined; + + if (DownstreamPort) { + // + // When combining ASPM in downstream ports, combination must reflect state of link just below + // and consider all acceptable latencies of all endpoints anywhere down below that port + // + Combined.L0sSupported = AspmA.L0sSupported & AspmB.L0sSupported; + Combined.L1Supported = AspmA.L1Supported & AspmB.L1Supported; + Combined.LinkL0sExitLatency = MAX (AspmA.LinkL0sExitLatency, AspmB.LinkL0sExitLatency); + Combined.LinkL1ExitLatencyUs = MAX (AspmA.LinkL1ExitLatencyUs, AspmB.LinkL1ExitLatencyUs); + Combined.L0sAcceptableLatency = MIN (AspmA.L0sAcceptableLatency, AspmB.L0sAcceptableLatency); + Combined.L1AcceptableLatencyUs = MIN (AspmA.L1AcceptableLatencyUs, AspmB.L1AcceptableLatencyUs); + } else { + // + // When combining ASPM in switch upstream ports, + // Supported and ExitLatency must only reflect capabilities of upstream port itself + // But acceptable latencies must consider all endpoints anywhere below + // + Combined.L0sSupported = AspmA.L0sSupported; + Combined.L1Supported = AspmA.L1Supported; + Combined.LinkL0sExitLatency = AspmA.LinkL0sExitLatency; + Combined.LinkL1ExitLatencyUs = AspmA.LinkL1ExitLatencyUs; + Combined.L0sAcceptableLatency = MIN (AspmA.L0sAcceptableLatency, AspmB.L0sAcceptableLatency); + Combined.L1AcceptableLatencyUs = MIN (AspmA.L1AcceptableLatencyUs, AspmB.L1AcceptableLatencyUs); + } + DEBUG ((DEBUG_INFO, "CombineAspm %x:%x -> %x\n", AspmA.L1AcceptableLatencyUs, AspmB.L1AcceptableLatencyUs, Combined.L1AcceptableLatencyUs)); + return Combined; +} + +/** + Checks if L1 can be enabled on given link, according to ASPM parameters of that link + + @param[in] Aspm set of parameters describing this link and endpoint devices below it + + @retval TRUE if L1 can be enabled +**/ +STATIC +BOOLEAN +IsL1Allowed ( + ASPM_CAPS Aspm + ) +{ + return (Aspm.L1Supported && (Aspm.L1AcceptableLatencyUs >= Aspm.LinkL1ExitLatencyUs)); +} + +/** + Checks if L0s can be enabled on given link, according to ASPM parameters of that link + + @param[in] Aspm set of parameters describing this link and endpoint devices below it + + @retval TRUE if L0s can be enabled +**/ +STATIC +BOOLEAN +IsL0sAllowed ( + ASPM_CAPS Aspm + ) +{ + return (Aspm.L0sSupported && (Aspm.L0sAcceptableLatency >= Aspm.LinkL0sExitLatency)); +} + +/** + Enables L0s and L1 for given port, if possible. + L0s/L1 can be enabled if it's supported on both sides of a link and if link's latency doesn't exceed + acceptable latency of any endpoint below this link + + @param[in] Base device's base address + @param[in] Aspm set of parameters describing this link and endpoint devices below it +**/ +STATIC +VOID +SetAspm ( + SBDF Sbdf, + ASPM_CAPS Aspm + ) +{ + UINT16 DataOr; + + DataOr = 0; + if (IsL0sAllowed (Aspm)) { + DataOr |= V_PCIE_LCTL_ASPM_L0S; + } + if (IsL1Allowed (Aspm)) { + DataOr |= V_PCIE_LCTL_ASPM_L1; + } + DEBUG ((DEBUG_INFO, "SetAspm on %02x:%02x:%02x to %d\n", Sbdf.Bus,Sbdf.Dev,Sbdf.Func, DataOr)); + PciSegmentAndThenOr16 (SbdfToBase (Sbdf) + Sbdf.PcieCap + R_PCIE_LCTL_OFFSET, (UINT16)~B_PCIE_LCTL_ASPM, DataOr); +} + +/** + Adds device entry to a list of devices. + + @param[in,out] Table array of devices + @param[in] Sbdf segment:bus:device:function coordinates of device to be added to table +**/ +STATIC +VOID +AddToDeviceTable ( + SBDF_TABLE *Table, + SBDF Sbdf + ) +{ + if (Table->Count < MAX_SBDF_TABLE_SIZE) { + Table->Entry[Table->Count++] = Sbdf; + } else { + ASSERT (FALSE); + } +} + +/** + Remove device entry from a list and clear its bus assignment + + @param[in,out] Table array of devices +**/ +STATIC +VOID +ClearBusFromTable ( + SBDF_TABLE *Table + ) +{ + while (Table->Count > 0) { + PciSegmentWrite32 (SbdfToBase (Table->Entry[Table->Count - 1]) + PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET, 0); + Table->Count--; + } +} + +/** + Attempts to assign secondary and subordinate bus numbers to uninitialized bridges in PCIe tree + If the device is a bridge and already has bus numbers assigned, they won't be changed + Otherwise new bus number will be assigned below this bridge. + This function can be called from SMM, where BIOS must not modify bus numbers to prevent + conflict with OS enumerator. To prevent this, this function returns list of bridges whose + bus numbers were changed. All devices from that list must have buses cleared afterwards. + + @param[in] Sbdf segment:bus:device:function coordinates of device to be added to table + @param[in] MinBus minimum Bus number that can be assigned below this port + @param[in] MaxBus maximum Bus number that can be assigned below this port + @param[in] BridgeCleanupList list of bridges where bus numbers were modified + + @retval maximum bus number assigned anywhere below this device +**/ +STATIC +UINT8 +RecursiveBusAssignment ( + SBDF Sbdf, + UINT8 MinBus, + UINT8 MaxBus, + SBDF_TABLE *BridgeCleanupList + ) +{ + UINT64 Base; + SBDF ChildSbdf; + PCI_DEV_TYPE DevType; + UINT32 Data32; + UINT8 BelowBus; + UINT8 SecondaryBus; + UINT8 SubordinateBus; + + ChildSbdf.Seg = Sbdf.Seg; + InitChildFinder (&ChildSbdf); + Base = SbdfToBase (Sbdf); + + // + // On way down: + // assign secondary bus, then increase it by one before stepping down; temporarily assign max subordinate bus + // On way up: + // fix subordinate bus assignment to equal max bus number assigned anywhere below; return that number + // + DevType = GetDeviceType (Sbdf); + if ((Sbdf.Bus >= MaxBus) || (DevType == DevTypePcieEndpoint) || (DevType == DevTypePci)) { + return (UINT8) Sbdf.Bus; + } else { + Data32 = PciSegmentRead32 (Base + PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET); + SecondaryBus = (UINT8)((Data32 & B_PCI_BRIDGE_BNUM_SCBN) >> 8); + SubordinateBus = (UINT8)((Data32 & B_PCI_BRIDGE_BNUM_SBBN) >> 16); + if (SecondaryBus != 0) { + ChildSbdf.Bus = SecondaryBus; + MinBus = SecondaryBus + 1; + DEBUG ((DEBUG_INFO, "RecursiveBusAssignmentP %x:%x:%x -> %x,%x,%x \n", Sbdf.Bus, Sbdf.Dev, Sbdf.Func, Sbdf.Bus, MinBus, SubordinateBus)); + while (FindNextPcieChild (DevType, &ChildSbdf)) { + BelowBus = RecursiveBusAssignment (ChildSbdf, MinBus, SubordinateBus, BridgeCleanupList); + MinBus = BelowBus + 1; + } + return SubordinateBus; + } else { + Data32 = Sbdf.Bus + (MinBus << 8) + (MaxBus << 16); + PciSegmentWrite32(Base + PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET, Data32); + AddToDeviceTable (BridgeCleanupList, Sbdf); + DEBUG ((DEBUG_INFO, "RecursiveBusAssignmentE %x:%x:%x -> %x,%x,%x \n", Sbdf.Bus, Sbdf.Dev, Sbdf.Func, Sbdf.Bus, MinBus, MaxBus)); + BelowBus = MinBus; + ChildSbdf.Bus = MinBus; + MinBus++; + while (FindNextPcieChild (DevType, &ChildSbdf)) { + BelowBus = RecursiveBusAssignment (ChildSbdf, MinBus, MaxBus, BridgeCleanupList); + MinBus = BelowBus + 1; + } + Data32 &= ~B_PCI_BRIDGE_BNUM_SBBN; + Data32 |= (BelowBus << 16); + PciSegmentWrite32 (Base + PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET, Data32); + DEBUG ((DEBUG_INFO, "RecursiveBusAssignmentL %x:%x:%x -> %x,%x,%x \n", Sbdf.Bus, Sbdf.Dev, Sbdf.Func, Sbdf.Bus, (Data32&0xFF00)>>8, BelowBus)); + return BelowBus; + } + } +} + +/** + Enables L0s and/or L1 for PCIE links in the hierarchy below + L0s/L1 can be enabled when both sides of a link support it and link latency is smaller than acceptable latency + ASPM of a given link is independend from any other link (except 1ms L1 adjustment, read below), so it's possible to + have a hierarchy when RP link has no ASPM but links below do. + + @param[in] Segment,Bus,Device,Function address of currently visited PCIe device + @param[in] Depth How many links there are between this port and root complex + @param[in] Override Pch Pcie devices OverrideTable + + @retval structure that describes acceptable latencies of all endpoints below plus ASPM parameters of last link +**/ +STATIC +ASPM_CAPS +RecursiveAspmConfiguration ( + SBDF Sbdf, + UINT8 Depth, + OVERRIDE_TABLE *Override + ) +{ + SBDF ChildSbdf; + ASPM_CAPS MyAspm; + ASPM_CAPS ChildAspm; + PCI_DEV_TYPE DevType; + + DEBUG ((DEBUG_INFO, "RecursiveAspmConfiguration %x:%x:%x\n", Sbdf.Bus, Sbdf.Dev, Sbdf.Func)); + + // + // On way down: + // pass number of links traversed; increase it per upstream port visited (not endpoint) + // On way up: + // EndPoint: read Acceptable Latencies; subtract Depth From L1AcceptableLat to account for "1us per switch additional delay" + // Downstreamport: AND L0s/L1 caps; calculate LinkLatency; enable L0s/L1 if supported and if acceptable latency is bigger than link latency; + // if L1 not enabled, add back 1us to Acceptable Latency to cancel earlier Depth subtraction + // UpstreamPort: calculate minimum of below Acceptable Latencies; return that, with upper link's Latency and L0s/L1 support + // + DevType = GetDeviceType(Sbdf); + if (DevType == DevTypePcieUpstream) { + Depth++; + } + MyAspm = GetAspmCaps (Sbdf); + // + // Get ASPM L0s and L1 override + // + if (Override != NULL) { + GetOverrideAspm (Sbdf, &MyAspm, Override); + } + if (DevType == DevTypePcieEndpoint) { + // + // Every switch between endpoint and CPU introduces 1us additional latency on L1 exit. This is reflected by + // subtracting 1us per switch from endpoint's acceptable L1 latency. + // In case L1 doesn't get enabled in one of switches, that 1us will be added back. + // This calculation is not precise. It ignores that some switches' added delay may be shadowed by + // other links' exit latency. But it guarantees that acceptable latency won't be exceeded and is simple + // enough to perform in a single iteration without backtracking. + // + return PatchL1AcceptableLatency (MyAspm, (-1 * Depth)); + } + if (HasChildBus (Sbdf, &ChildSbdf)) { + while (FindNextPcieChild (DevType, &ChildSbdf)) { + ChildAspm = RecursiveAspmConfiguration (ChildSbdf, Depth, Override); + MyAspm = CombineAspm (MyAspm, ChildAspm, (DevType == DevTypePcieDownstream)); + } + if (DevType == DevTypePcieDownstream) { + SetAspm (Sbdf, MyAspm); + // + // ASPM config must be consistent across all functions of a device. That's why there's while loop. + // + while (FindNextPcieChild (DevType, &ChildSbdf)) { + SetAspm (ChildSbdf, MyAspm); + } + if (!IsL1Allowed (MyAspm)) { + MyAspm = PatchL1AcceptableLatency (MyAspm, 1); + } + } + } + return MyAspm; +} + +/** + Enables L1 substates for PCIE links in the hierarchy below + L1.1 / L1.2 can be enabled if both sides of a link support it. + + @param[in] Segment,Bus,Device,Function address of currently visited PCIe device + + @retval structure that describes L1ss capabilities of the device +**/ +STATIC +L1SS_CAPS +RecursiveL1ssConfiguration ( + SBDF Sbdf, + OVERRIDE_TABLE *Override + ) +{ + UINT64 Base; + SBDF ChildSbdf; + L1SS_CAPS CombinedCaps; + L1SS_CAPS ChildCaps; + PCI_DEV_TYPE DevType; + BOOLEAN IsCpuPcie; + UINT32 DevNum; + + IsCpuPcie = FALSE; + + DEBUG ((DEBUG_INFO, "RecursiveL1ssConfiguration %x:%x:%x\n", Sbdf.Bus, Sbdf.Dev, Sbdf.Func)); + + Base = SbdfToBase (Sbdf); + DevNum = Sbdf.Dev; + if (DevNum == SA_PEG0_DEV_NUM || DevNum == SA_PEG3_DEV_NUM) { + IsCpuPcie = TRUE; + } + // + // On way down: + // do nothing + // On way up: + // In downstream ports, combine L1ss capabilities of that port and device behind it, then enable L1.1 and/or L1.2 if possible + // Return L1ss capabilities + // + if (HasChildBus (Sbdf, &ChildSbdf)) { + DevType = GetDeviceType (Sbdf); + while (FindNextPcieChild (DevType, &ChildSbdf)) { + ChildCaps = RecursiveL1ssConfiguration (ChildSbdf, Override); + if (DevType == DevTypePcieDownstream && ChildSbdf.Func == 0) { + CombinedCaps = CombineL1ss (GetL1ssCaps (Base, Override), ChildCaps); + SetL1ss (Sbdf, CombinedCaps, Override, IsCpuPcie); + SetL1ss (ChildSbdf, CombinedCaps, Override, IsCpuPcie); + } + } + } + return GetL1ssCaps (Base, Override); +} + +/** + Checks if there is an IoAPIC device in the PCIe hierarchy. + If one is found, this function doesn't check for more and returns + + @param[in] BusLimit maximum Bus number that can be assigned below this port + @param[in] Segment,Bus,Device,Function address of currently visited PCIe device + + @retval TRUE if IoAPIC device was found +**/ +STATIC +BOOLEAN +RecursiveIoApicCheck ( + SBDF Sbdf + ) +{ + SBDF ChildSbdf; + UINT8 IoApicPresent; + PCI_DEV_TYPE DevType; + + DEBUG ((DEBUG_INFO, "RecursiveIoApicCheck %x:%x:%x\n", Sbdf.Bus, Sbdf.Dev, Sbdf.Func)); + + IoApicPresent = FALSE; + + if (IsIoApicDevice (SbdfToBase (Sbdf))) { + DEBUG ((DEBUG_INFO, "IoApicFound @%x:%x:%x:%x\n", Sbdf.Bus, Sbdf.Dev, Sbdf.Func)); + return TRUE; + } + if (HasChildBus (Sbdf, &ChildSbdf)) { + DevType = GetDeviceType (Sbdf); + while (FindNextPcieChild (DevType, &ChildSbdf)) { + IoApicPresent = RecursiveIoApicCheck (ChildSbdf); + if (IoApicPresent) { + break; + } + } + } + DEBUG ((DEBUG_INFO, "IoApic status %d @%x:%x:%x:%x\n", IoApicPresent, Sbdf.Seg, Sbdf.Bus, Sbdf.Dev, Sbdf.Func)); + return IoApicPresent; +} + +/** + Calculates Maximum Payload Size supported by PCIe hierarchy. + Starting from a device, it finds the minimum MPS supported by devices below it. + There are many valid strategies for setting MPS. This implementation chooses + one that is safest, but doesn't guarantee maximum performance: + Find minimum MPS under given rootport, then program that minimum value everywhere below that rootport + + @param[in] BusLimit maximum Bus number that can be assigned below this port + @param[in] Segment,Bus,Device,Function address of currently visited PCIe device + + @retval MPS supported by PCIe hierarchy, calculated as MIN(MPS of all devices below) +**/ +STATIC +UINT8 +RecursiveMpsCheck ( + SBDF Sbdf + ) +{ + SBDF ChildSbdf; + UINT8 MyMps; + UINT8 SubtreeMps; + PCI_DEV_TYPE DevType; + + DEBUG ((DEBUG_INFO, "RecursiveMpsCheck %x:%x:%x\n", Sbdf.Bus, Sbdf.Dev, Sbdf.Func)); + + MyMps = GetMps (Sbdf); + if (MyMps == 0) { + return MyMps; + } + if (HasChildBus (Sbdf, &ChildSbdf)) { + DevType = GetDeviceType (Sbdf); + while (FindNextPcieChild (DevType, &ChildSbdf)) { + SubtreeMps = RecursiveMpsCheck (ChildSbdf); + MyMps = MIN(MyMps, SubtreeMps); + } + } + return MyMps; +} + +/** + Sets Maximum Payload Size in PCIe hierarchy. + Starting from a device, it programs the same MPS value to it and all devices below it. + There are many valid strategies for setting MPS. This implementation chooses + one that is safest, but doesn't guarantee maximum performance: + Find minimum MPS under given rootport, then program that minimum value everywhere below that rootport + + @param[in] BusLimit maximum Bus number that can be assigned below this port + @param[in] Segment,Bus,Device,Function address of currently visited PCIe device + @param[in] Mps Maximum Payload Size to be programmed +**/ +STATIC +VOID +RecursiveMpsConfiguration ( + SBDF Sbdf, + UINT8 Mps + ) +{ + SBDF ChildSbdf; + PCI_DEV_TYPE DevType; + + DEBUG ((DEBUG_INFO, "RecursiveMpsConfiguration %x:%x:%x\n", Sbdf.Bus, Sbdf.Dev, Sbdf.Func)); + + if (HasChildBus (Sbdf, &ChildSbdf)) { + DevType = GetDeviceType (Sbdf); + while (FindNextPcieChild (DevType, &ChildSbdf)) { + RecursiveMpsConfiguration (ChildSbdf, Mps); + } + } + SetMps (Sbdf, Mps); +} + +/** + Sets Enable Clock Power Management bit for devices that support it. + A device supports CPM only if all function of this device report CPM support. + Downstream ports never report CPM capability, so it's only relevant for upstream ports. + When this function executes on upstream component, it will check CPM & set ECPM of downstream component + When this function executes on downstream component, all devices below it are guaranteed to + return CPM=0 so it will do nothing + + @param[in] Segment,Bus,Device,Function address of currently visited PCIe device + + @retval TRUE = this device supports CPM, FALSE = it doesn't +**/ +STATIC +BOOLEAN +RecursiveCpmConfiguration ( + SBDF Sbdf + ) +{ + SBDF ChildSbdf; + BOOLEAN ChildCpm; + PCI_DEV_TYPE DevType; + + DEBUG ((DEBUG_INFO, "RecursiveCpmConfiguration %x:%x:%x\n", Sbdf.Bus, Sbdf.Dev, Sbdf.Func)); + + ChildCpm = FALSE; + + if (HasChildBus (Sbdf, &ChildSbdf)) { + ChildCpm = TRUE; + DevType = GetDeviceType (Sbdf); + while (FindNextPcieChild (DevType, &ChildSbdf)) { + ChildCpm &= RecursiveCpmConfiguration (ChildSbdf); + } + if (ChildCpm) { + while (FindNextPcieChild (DevType, &ChildSbdf)) { + EnableCpm (ChildSbdf); + } + } + } + return IsCpmSupported (Sbdf); +} + +/** + Sets Common Clock Configuration bit for devices that share common clock across link + Devices on both sides of a PCIE link share common clock if both upstream component + and function 0 of downstream component report Slot Clock Configuration bit = 1. + When this function executes on upstream component, it checks SCC of both sides of the link + If they both support it, sets CCC for both sides (this means all functions of downstream component) + When this function executes on downstream component, it only returns SCC capability + + @param[in] Segment,Bus,Device,Function address of currently visited PCIe device + @param[in] WaitForRetrain decides if this function should busy-wait for link retrain + + @retval TRUE = this device supports SCC, FALSE = it doesn't +**/ +STATIC +BOOLEAN +RecursiveCccConfiguration ( + SBDF Sbdf, + BOOLEAN WaitForRetrain + ) +{ + UINT64 Base; + SBDF ChildSbdf; + BOOLEAN MyScc; + BOOLEAN ChildScc; + BOOLEAN LinkScc; + PCI_DEV_TYPE DevType; + + DEBUG ((DEBUG_INFO, "RecursiveCccConfiguration %x:%x:%x\n", Sbdf.Bus, Sbdf.Dev, Sbdf.Func)); + + ChildScc = 0; + Base = SbdfToBase(Sbdf); + MyScc = GetScc (SbdfToBase(Sbdf), (UINT8)Sbdf.PcieCap); + if (HasChildBus (Sbdf, &ChildSbdf)) { + DevType = GetDeviceType (Sbdf); + while (FindNextPcieChild (DevType, &ChildSbdf)) { + ChildScc |= RecursiveCccConfiguration (ChildSbdf, WaitForRetrain); + } + if (DevType == DevTypePcieDownstream) { + LinkScc = MyScc & ChildScc; + if (LinkScc) { + EnableCcc (SbdfToBase(Sbdf), (UINT8)Sbdf.PcieCap); + while (FindNextPcieChild (DevType, &ChildSbdf)) { + EnableCcc (SbdfToBase(ChildSbdf), (UINT8)ChildSbdf.PcieCap); + } + RetrainLink(Base, (UINT8)Sbdf.PcieCap, WaitForRetrain); + } + } + } + return MyScc; +} + +/** + Configures Latency Tolerance Reporting in given device and in PCIe tree below it. + This function configures Maximum LTR and enables LTR mechanism. It visits devices using depth-first search + and skips branches behind devices which do not support LTR. + Maximum LTR: + This function will set LTR's upper bound for every visited device. Max LTR value is provided as a parameter + Enable LTR: + LTR should be enabled top-to-bottom in every visited device that supports LTR. This function does not + iterate down behind devices with no LTR support. In effect, LTR will be enabled in given device if that device + and all devices above it on the way to RootComplex do support LTR. + + This function expects that bridges have bus numbers already configured + + @param[in] Segment,Bus,Device,Function address of currently visited PCIe device + @param[in] LtrLimit Ltr to be programmed to every endpoint + + @retval MaxLTR programmed in this device +**/ +STATIC +VOID +RecursiveLtrConfiguration ( + SBDF Sbdf, + LTR_LIMIT LtrLimit + ) +{ + UINT64 Base; + SBDF ChildSbdf; + PCI_DEV_TYPE DevType; + + DEBUG ((DEBUG_INFO, "RecursiveLtrConfiguration %x:%x:%x\n", Sbdf.Bus, Sbdf.Dev, Sbdf.Func)); + + Base = SbdfToBase(Sbdf); + + if (!IsLtrCapable (Sbdf)) { + DEBUG ((DEBUG_INFO, "Not LtrCapable %02x:%02x:%02x\n", Sbdf.Bus, Sbdf.Dev, Sbdf.Func)); + return; + } + EnableLtr (Sbdf); + if (HasChildBus (Sbdf, &ChildSbdf)) { + DevType = GetDeviceType (Sbdf); + while (FindNextPcieChild (DevType, &ChildSbdf)) { + RecursiveLtrConfiguration (ChildSbdf, LtrLimit); + } + } + SetLtrLimit (Base, LtrLimit); +} + +/** + Checks to see device is PTM Capable + + @param[in] Sbdf device's segment:bus:device:function coordinates + + @retval TRUE = PTM Capability found, FALSE = Not PTM capable +**/ +STATIC +BOOLEAN +IsPtmCapable ( + SBDF Sbdf + ) +{ + UINT16 CapHeaderOffset; + + CapHeaderOffset = PcieFindExtendedCapId ((UINT8) Sbdf.Seg, (UINT8) Sbdf.Bus, (UINT8) Sbdf.Dev, (UINT8) Sbdf.Func, V_PCIE_EX_PTM_CID); + + return (CapHeaderOffset != 0); +} + +/** + Get PTM Capability register from PCIe Extended Capability Space. + + @param[in] Sbdf device's segment:bus:device:function coordinates + @param[in] PtmCapHeaderOffset PTM Capability Header Offset + + @retval LocalPtm Returns PTM Capability. + If Device is not PTM capable then PTM Capability is zeroed out. +**/ +STATIC +PTM_CAPS +GetPtmCapability ( + SBDF Sbdf, + UINT16 PtmCapHeaderOffset + ) +{ + PTM_CAPS PtmCaps; + + PtmCaps.Uint32 = 0; + + if (PtmCapHeaderOffset != 0) { + PtmCaps.Uint32 = PciSegmentRead32 (SbdfToBase (Sbdf) + PtmCapHeaderOffset + R_PCIE_EX_PTMCAP_OFFSET); + } + + return PtmCaps; +} + +/** + Get PTM Control register from PCIe Extended Capability Space. + + @param[in] Sbdf device's segment:bus:device:function coordinates + @param[in] PtmCapHeaderOffset PTM Capability Header Offset + + @retval LocalPtm Returns PTM Control. + If Device is not PTM capable then PTM Control is zeroed out. +**/ +STATIC +PTM_CTRL +GetPtmControl ( + SBDF Sbdf, + UINT16 PtmCapHeaderOffset + ) +{ + PTM_CTRL PtmCtrl; + + PtmCtrl.Uint32 = 0; + + if (PtmCapHeaderOffset != 0) { + PtmCtrl.Uint32 = PciSegmentRead32 (SbdfToBase (Sbdf) + PtmCapHeaderOffset + R_PCIE_EX_PTMCTL_OFFSET); + } + + return PtmCtrl; +} + +/** + Set PTM Control register in the PCIe Extended Capability Space. + + @param[in] Sbdf device's segment:bus:device:function coordinates + @param[in] PtmCapHeaderOffset PTM Capability Header Offset + @param[in] PtmCtrl PTM Control Register +**/ +STATIC +VOID +SetPtmControl ( + SBDF Sbdf, + UINT16 PtmCapHeaderOffset, + PTM_CTRL PtmCtrl + ) +{ + if (PtmCapHeaderOffset != 0) { + PciSegmentWrite32 (SbdfToBase (Sbdf) + PtmCapHeaderOffset + R_PCIE_EX_PTMCTL_OFFSET, PtmCtrl.Uint32); + } +} + +/** + Enable PTM on device's control register. Set the Effective Clock Granularity. + + @param[in] Sbdf device's segment:bus:device:function coordinates + @param[in out] EffectiveGranularity Effective Clock Granularity of the PTM hierarchy + @param[in out] PtmHierarchy Indicates if the current device is within a PTM hierarchy +**/ +STATIC +VOID +SetPtm ( + IN SBDF Sbdf, + IN OUT UINT8 *EffectiveGranularity, + IN OUT BOOLEAN *PtmHierarchy + ) +{ + PTM_CTRL CurrentPtmCtrl; + PTM_CAPS CurrentPtmCaps; + PTM_CTRL OrigPtmCtrl; + UINT16 PtmCapHeaderOffset; + + PtmCapHeaderOffset = PcieFindExtendedCapId ((UINT8) Sbdf.Seg, (UINT8) Sbdf.Bus, (UINT8) Sbdf.Dev, (UINT8) Sbdf.Func, V_PCIE_EX_PTM_CID); + CurrentPtmCtrl = GetPtmControl (Sbdf, PtmCapHeaderOffset); + CurrentPtmCaps = GetPtmCapability (Sbdf, PtmCapHeaderOffset); + + OrigPtmCtrl.Uint32 = CurrentPtmCtrl.Uint32; + + if ( (*PtmHierarchy == FALSE) && CurrentPtmCaps.Bits.RootCapable) { + // Select device as PTM Root if PTM Root is not selected. + CurrentPtmCtrl.Bits.RootSelect = TRUE; + *EffectiveGranularity = (UINT8) CurrentPtmCaps.Bits.LocalClockGranularity; + *PtmHierarchy = TRUE; + } + + if (*PtmHierarchy == TRUE) { + // Enable PTM if device is part of a ptm hierarchy + CurrentPtmCtrl.Bits.Enable = TRUE; + + if (CurrentPtmCaps.Bits.RequesterCapable) { + // Set Effective Granularity if PTM device is Requester roles. + CurrentPtmCtrl.Bits.EffectiveGranularity = *EffectiveGranularity; + } + } + + if (OrigPtmCtrl.Uint32 != CurrentPtmCtrl.Uint32) { + SetPtmControl (Sbdf, PtmCapHeaderOffset, CurrentPtmCtrl); + } + + // Update EffectiveGranularity. + // Set to zero if 1 or more switches between the root and endpoint report local granularity = 0. + // Otherwise set to the max local granularity of the hierarchy. + if ( ((CurrentPtmCaps.Bits.LocalClockGranularity == 0) && CurrentPtmCaps.Bits.RequesterCapable) || + (*EffectiveGranularity == 0) ) { + *EffectiveGranularity = 0; + } else { + *EffectiveGranularity = MAX (*EffectiveGranularity, (UINT8) CurrentPtmCaps.Bits.LocalClockGranularity); + } +} + +/** + Configures PTM hierarchies by searching for Endpoints and Upstream Switches + that are PTM capable. Each PTM device role is identified and configured accordingly. + PTM root capable devices are selected as PTM root if the device is not already in a + PTM hierarchy. + PTM capable Root Ports must be configured before calling this function. + @note: This function has not been tested with switches. + + @param[in] Sbdf Address of curretly visited Pcie device + @param[in] EffectiveGranularity The largest Clock Granularity from Upstream Devices + @param[in] PtmHierarchy TRUE = Device in a PTM Hierarchy, FALSE = Not in PTM Hierarchy +**/ +STATIC +VOID +RecursivePtmConfiguration ( + SBDF Sbdf, + UINT8 EffectiveGranularity, + BOOLEAN PtmHierarchy + ) +{ + SBDF ChildSbdf; + PCI_DEV_TYPE DevType; + + DEBUG ((DEBUG_INFO, "RecursivePtmConfiguration %x:%x:%x\n", Sbdf.Bus, Sbdf.Dev, Sbdf.Func)); + + DevType = GetDeviceType (Sbdf); + + if (IsPtmCapable (Sbdf)) { + // + // Enable PTM for PTM Capable devices (Endpoints, Upstream Switch, and Root Ports). + // @Note: Switches have not been tested. + // + SetPtm (Sbdf, &EffectiveGranularity, &PtmHierarchy); + } else if (!IsPtmCapable (Sbdf) && (DevType == DevTypePcieUpstream) ) { + // + // Non-PTM UpStream Switch Ports breaks the PTM Hierarchy. + // No other downstream PTM devices should be PTM enabled until a PTM Root capable device is selected. + // + PtmHierarchy = FALSE; + EffectiveGranularity = 0; + } + + if (HasChildBus (Sbdf, &ChildSbdf)) { + while (FindNextPcieChild (DevType, &ChildSbdf)) { + RecursivePtmConfiguration (ChildSbdf, EffectiveGranularity, PtmHierarchy); + } + } +} + +/** + Initializes the following features in rootport and devices behind it: + Maximum Payload Size (generic) + Rootport packet split (proprietary) + EonOfInterrupt forwarding (proprietary) + Common Clock Configuration (generic) + + Generic: any code written according to PCIE Express base specification can do that. + Proprietary: code uses registers and features that are specific to Intel silicon + and probably only this Reference Code knows how to handle that. + + If OEM implemented generic feature enabling in his platform code or trusts Operating System + to do it, then those features can be deleted from here. + + CCC requires link retrain, which takes a while. CCC must happen before L0s/L1 programming. + If there was guarantee no code would access PCI while links retrain, it would be possible to skip this waiting + + @param[in] RpSegment address of rootport on PCIe + @param[in] RpBus address of rootport on PCIe + @param[in] RpDevice address of rootport on PCIe + @param[in] RpFunction address of rootport on PCIe + @param[in] BusMin minimum Bus number that can be assigned below this rootport + @param[in] BusMax maximum Bus number that can be assigned below this rootport +**/ +VOID +RootportDownstreamConfiguration ( + UINT8 RpSegment, + UINT8 RpBus, + UINT8 RpDevice, + UINT8 RpFunction, + UINT8 BusMin, + UINT8 BusMax, + PCI_SKU PciSku + ) +{ + UINT8 Mps; + BOOLEAN IoApicPresent; + UINT64 RpBase; + SBDF RpSbdf; + SBDF_TABLE BridgeCleanupList; + + IoApicPresent = FALSE; + RpBase = PCI_SEGMENT_LIB_ADDRESS (RpSegment, RpBus, RpDevice, RpFunction, 0); + if (!(IsDevicePresent (RpBase))) { + return; + } + RpSbdf.Seg = RpSegment; + RpSbdf.Bus = RpBus; + RpSbdf.Dev = RpDevice; + RpSbdf.Func = RpFunction; + RpSbdf.PcieCap = PcieBaseFindCapId (RpBase, EFI_PCI_CAPABILITY_ID_PCIEXP); + + DEBUG ((DEBUG_INFO, "RootportDownstreamConfiguration %x:%x\n", RpDevice, RpFunction)); + BridgeCleanupList.Count = 0; + RecursiveBusAssignment (RpSbdf, BusMin, BusMax, &BridgeCleanupList); + + Mps = RecursiveMpsCheck (RpSbdf); + RecursiveMpsConfiguration (RpSbdf, Mps); + if (PciSku == EnumPchPcie) { + RpBase = SbdfToBase (RpSbdf); + ConfigureRpPacketSplit(RpBase, Mps); + IoApicPresent = RecursiveIoApicCheck(RpSbdf); + } + if ((PciSku == EnumPchPcie) || (PciSku == EnumCpuPcie)) { + ConfigureEoiForwarding (RpBase, IoApicPresent); + } + RecursiveCccConfiguration (RpSbdf, TRUE); + + if (IsPtmCapable (RpSbdf)) { + RecursivePtmConfiguration (RpSbdf, 0, FALSE); + } + + ClearBusFromTable (&BridgeCleanupList); +} + +/** + Checks if given PCI device is capable of Latency Tolerance Reporting + + @param[in] Sbdf device's segment:bus:device:function coordinates + + @retval TRUE if yes +**/ +BOOLEAN +IsLtrCapable ( + SBDF Sbdf + ) +{ + if (Sbdf.PcieCap == 0) { + return FALSE; + } + return !!(PciSegmentRead32 (SbdfToBase (Sbdf) + Sbdf.PcieCap + R_PCIE_DCAP2_OFFSET) & B_PCIE_DCAP2_LTRMS); +} + +/** + Returns combination of two LTR override values + The resulting LTR override separately chooses stricter limits for snoop and nosnoop + + @param[in] LtrA LTR override values to be combined + @param[in] LtrB LTR override values to be combined + + @retval LTR override value +**/ +LTR_OVERRIDE +CombineLtr ( + LTR_OVERRIDE LtrA, + LTR_OVERRIDE LtrB + ) +{ + UINT64 DecodedLatencyA; + UINT64 DecodedLatencyB; + LTR_OVERRIDE Result; + static UINT32 ScaleEncoding [8] = {1, 32, 1024, 32768, 1048576, 33554432, 0, 0}; + + ZeroMem (&Result, sizeof (LTR_OVERRIDE)); + DecodedLatencyA = ScaleEncoding[LtrA.MaxSnoopLatencyScale] * LtrA.MaxSnoopLatencyValue; + DecodedLatencyB = ScaleEncoding[LtrB.MaxSnoopLatencyScale] * LtrB.MaxSnoopLatencyValue; + if ((!LtrB.MaxSnoopLatencyRequirement) || ((DecodedLatencyA < DecodedLatencyB) && LtrA.MaxSnoopLatencyRequirement)) { + Result.MaxSnoopLatencyValue = LtrA.MaxSnoopLatencyValue; + Result.MaxSnoopLatencyScale = LtrA.MaxSnoopLatencyScale; + Result.MaxSnoopLatencyRequirement = LtrA.MaxSnoopLatencyRequirement; + } else { + Result.MaxSnoopLatencyValue = LtrB.MaxSnoopLatencyValue; + Result.MaxSnoopLatencyScale = LtrB.MaxSnoopLatencyScale; + Result.MaxSnoopLatencyRequirement = LtrB.MaxSnoopLatencyRequirement; + } + DecodedLatencyA = ScaleEncoding[LtrA.MaxNoSnoopLatencyScale] * LtrA.MaxNoSnoopLatencyValue; + DecodedLatencyB = ScaleEncoding[LtrB.MaxNoSnoopLatencyScale] * LtrB.MaxNoSnoopLatencyValue; + if ((!LtrB.MaxNoSnoopLatencyRequirement) || ((DecodedLatencyA < DecodedLatencyB) && LtrA.MaxNoSnoopLatencyRequirement)) { + Result.MaxNoSnoopLatencyValue = LtrA.MaxNoSnoopLatencyValue; + Result.MaxNoSnoopLatencyScale = LtrA.MaxNoSnoopLatencyScale; + Result.MaxNoSnoopLatencyRequirement = LtrA.MaxNoSnoopLatencyRequirement; + } else { + Result.MaxNoSnoopLatencyValue = LtrB.MaxNoSnoopLatencyValue; + Result.MaxNoSnoopLatencyScale = LtrB.MaxNoSnoopLatencyScale; + Result.MaxNoSnoopLatencyRequirement = LtrB.MaxNoSnoopLatencyRequirement; + } + if (LtrA.ForceOverride || LtrB.ForceOverride) { + Result.ForceOverride = TRUE; + } + DEBUG ((DEBUG_INFO, "CombineLtr: A(V%d S%d E%d : V%d S%d E%d, F%d)\n", + LtrA.MaxSnoopLatencyValue, LtrA.MaxSnoopLatencyScale, LtrA.MaxSnoopLatencyRequirement, + LtrA.MaxNoSnoopLatencyValue, LtrA.MaxNoSnoopLatencyScale, LtrA.MaxNoSnoopLatencyRequirement, + LtrA.ForceOverride + )); + DEBUG ((DEBUG_INFO, " : B(V%d S%d E%d : V%d S%d E%d, F%d)\n", + LtrB.MaxSnoopLatencyValue, LtrB.MaxSnoopLatencyScale, LtrB.MaxSnoopLatencyRequirement, + LtrB.MaxNoSnoopLatencyValue, LtrB.MaxNoSnoopLatencyScale, LtrB.MaxNoSnoopLatencyRequirement, + LtrB.ForceOverride + )); + DEBUG ((DEBUG_INFO, " : R(V%d S%d E%d : V%d S%d E%d, F%d)\n", + Result.MaxSnoopLatencyValue, Result.MaxSnoopLatencyScale, Result.MaxSnoopLatencyRequirement, + Result.MaxNoSnoopLatencyValue, Result.MaxNoSnoopLatencyScale, Result.MaxNoSnoopLatencyRequirement, + Result.ForceOverride + )); + return Result; +} + +/** + Returns LTR override value for given device + The value is extracted from Device Override table. If the device is not found, + the returned value will have Requirement bits clear + + @param[in] Base device's base address + @param[in] Override device override table + + @retval LTR override value +**/ +LTR_OVERRIDE +GetOverrideLtr ( + UINT64 Base, + OVERRIDE_TABLE *Override + ) +{ + UINT16 DevId; + UINT16 VenId; + UINT16 RevId; + UINT32 Index; + LTR_OVERRIDE ReturnValue = {0}; + + VenId = PciSegmentRead16 (Base + PCI_VENDOR_ID_OFFSET); + DevId = PciSegmentRead16 (Base + PCI_DEVICE_ID_OFFSET); + RevId = PciSegmentRead16 (Base + PCI_REVISION_ID_OFFSET); + + for (Index = 0; Index < Override->Size; Index++) { + if (((Override->Table[Index].OverrideConfig & PchPcieLtrOverride) == PchPcieLtrOverride) && + (Override->Table[Index].VendorId == VenId) && + ((Override->Table[Index].DeviceId == DevId) || (Override->Table[Index].DeviceId == 0xFFFF)) && + ((Override->Table[Index].RevId == RevId) || (Override->Table[Index].RevId == 0xFF))) { + if (Override->Table[Index].SnoopLatency & 0x8000) { + ReturnValue.MaxSnoopLatencyRequirement = 1; + ReturnValue.MaxSnoopLatencyValue = Override->Table[Index].SnoopLatency & 0x3FF; + ReturnValue.MaxSnoopLatencyScale = (Override->Table[Index].SnoopLatency & 0x1C00) >> 10; + } + if (Override->Table[Index].NonSnoopLatency & 0x8000) { + ReturnValue.MaxNoSnoopLatencyRequirement = 1; + ReturnValue.MaxNoSnoopLatencyValue = Override->Table[Index].NonSnoopLatency & 0x3FF; + ReturnValue.MaxNoSnoopLatencyScale = (Override->Table[Index].NonSnoopLatency & 0x1C00) >> 10; + } + ReturnValue.ForceOverride = Override->Table[Index].ForceLtrOverride; + break; + } + } + return ReturnValue; +} + +/** + In accordance with PCIe spec, devices with no LTR support are considered to have no LTR requirements + which means infinite latency tolerance. This was found to cause problems with HID and Audio devices without LTR + support placed behind PCIe switches with LTR support, as Switch's upstream link would be allowed to enter L1.2 + and cause large latency downstream. To work around such issues and to fix some devices with broken + LTR reporting, Device Override table was introduced. + This function scans PCIe tree for devices mentioned in override table and calculates the strictest + LTR requirement between them. That value will be programmed into rootport's LTR override register + + This function expects that bridges have bus numbers already configured + + @param[in] BusLimit maximum Bus number that can be assigned below this port + @param[in] Segment,Bus,Device,Function address of currently visited PCIe device + @param[in] AspmOverride Device specific ASPM policy override items + + @retval MaxLTR programmed in this device +**/ +LTR_OVERRIDE +RecursiveLtrOverrideCheck ( + SBDF Sbdf, + OVERRIDE_TABLE *AspmOverride + ) +{ + UINT64 Base; + SBDF ChildSbdf; + LTR_OVERRIDE MyLtrOverride; + LTR_OVERRIDE ChildLtr; + PCI_DEV_TYPE DevType; + + DEBUG ((DEBUG_INFO, "RecursiveLtrOverrideCheck %x:%x:%x\n", Sbdf.Bus, Sbdf.Dev, Sbdf.Func)); + + Base = SbdfToBase(Sbdf); + + MyLtrOverride = GetOverrideLtr (Base, AspmOverride); + if (HasChildBus (Sbdf, &ChildSbdf)) { + DevType = GetDeviceType (Sbdf); + while (FindNextPcieChild (DevType, &ChildSbdf)) { + ChildLtr = RecursiveLtrOverrideCheck (ChildSbdf, AspmOverride); + MyLtrOverride = CombineLtr (MyLtrOverride, ChildLtr); + } + } + return MyLtrOverride; +} + +/** + Configures the following power-management related features in rootport and devices behind it: + LTR limit (generic) + LTR override (proprietary) + Clock Power Management (generic) + L1 substates (generic except for the override table) + L1.LOW substate (proprietary) + L0s and L1 (generic) + + Generic: any code written according to PCIE Express base specification can do that. + Proprietary: code uses registers and features that are specific to Intel silicon + and probably only this Reference Code knows how to handle that. + + If OEM implemented generic feature enabling in his platform code or trusts Operating System + to do it, then those features can be deleted from here. + + @param[in] RpSegment address of rootport on PCIe + @param[in] RpBus address of rootport on PCIe + @param[in] RpDevice address of rootport on PCIe + @param[in] RpFunction address of rootport on PCIe + @param[in] BusLimit maximum Bus number that can be assigned below this rootport + @param[in] PcieRpLtrConfig address of LTR Policy struct + @param[in] PcieRpCommonConfig address of Common PCIe Policy struct + @param[in] AspmOverrideTableSize size of override array + @param[in] AspmOverrideTable array of device that need exceptions in configuration +**/ +VOID +RootportDownstreamPmConfiguration ( + UINT8 RpSegment, + UINT8 RpBus, + UINT8 RpDevice, + UINT8 RpFunction, + UINT8 BusMin, + UINT8 BusMax, + PCIE_ROOT_PORT_COMMON_CONFIG *PcieRpCommonConfig, + UINT32 AspmOverrideTableSize, + PCH_PCIE_DEVICE_OVERRIDE *AspmOverrideTable + ) +{ + LTR_LIMIT PolicyLtr; + LTR_OVERRIDE TreeLtr; + OVERRIDE_TABLE PmOverrideTable; + UINT64 RpBase; + SBDF RpSbdf; + SBDF_TABLE BridgeCleanupList; + + RpBase = PCI_SEGMENT_LIB_ADDRESS (RpSegment, RpBus, RpDevice, RpFunction, 0); + if (!(IsDevicePresent (RpBase))) { + return; + } + PmOverrideTable.Size = AspmOverrideTableSize; + PmOverrideTable.Table = AspmOverrideTable; + + DEBUG ((DEBUG_INFO, "RootportDownstreamPmConfiguration %x:%x\n", RpDevice, RpFunction)); + PolicyLtr.MaxNoSnoopLatencyScale = (PcieRpCommonConfig->PcieRpLtrConfig.LtrMaxNoSnoopLatency & 0x1c00) >> 10; + PolicyLtr.MaxNoSnoopLatencyValue = PcieRpCommonConfig->PcieRpLtrConfig.LtrMaxNoSnoopLatency & 0x3FF; + PolicyLtr.MaxSnoopLatencyScale = (PcieRpCommonConfig->PcieRpLtrConfig.LtrMaxSnoopLatency & 0x1c00) >> 10; + PolicyLtr.MaxSnoopLatencyValue = PcieRpCommonConfig->PcieRpLtrConfig.LtrMaxSnoopLatency & 0x3FF; + + RpSbdf.Seg = RpSegment; + RpSbdf.Bus = RpBus; + RpSbdf.Dev = RpDevice; + RpSbdf.Func = RpFunction; + RpSbdf.PcieCap = PcieBaseFindCapId (RpBase, EFI_PCI_CAPABILITY_ID_PCIEXP); + // + // This code could execute either before or after enumeration. If before, then buses would not yet be assigned to bridges, + // making devices deeper in the hierarchy inaccessible. + // RecursiveBusAssignment will scan whole PCie tree and assign bus numbers to uninitialized bridges, if there are any + // List of such bridges will be kept in CleanupList, so that after PM programming is done, bus numbers can brought to original state + // + BridgeCleanupList.Count = 0; + RecursiveBusAssignment(RpSbdf, BusMin, BusMax, &BridgeCleanupList); + // + // The 'Recursive...' functions below expect bus numbers to be already assigned + // + RecursiveLtrConfiguration (RpSbdf, PolicyLtr); + TreeLtr = RecursiveLtrOverrideCheck (RpSbdf, &PmOverrideTable); + ConfigureRpLtrOverride (RpBase, RpSbdf.Dev, &TreeLtr, &(PcieRpCommonConfig->PcieRpLtrConfig)); + DEBUG ((DEBUG_INFO, "ConfigureRpLtrOverride %x:%x\n", RpSbdf.Dev, RpSbdf.Func)); + if (PcieRpCommonConfig->EnableCpm) { + RecursiveCpmConfiguration (RpSbdf); + } + // + // L1 substates can be modified only when L1 is disabled, so this function must execute + // before Aspm configuration which enables L1 + // + RecursiveL1ssConfiguration (RpSbdf, &PmOverrideTable); + L1ssProprietaryConfiguration (RpBase, IsLtrCapable (RpSbdf)); + RecursiveAspmConfiguration (RpSbdf, 0, &PmOverrideTable); + + ClearBusFromTable (&BridgeCleanupList); +} diff --git a/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/PciExpressHelpersLibrary/PciExpressHelpersLibrary.h b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/PciExpressHelpersLibrary/PciExpressHelpersLibrary.h new file mode 100644 index 0000000000..19c1051771 --- /dev/null +++ b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/PciExpressHelpersLibrary/PciExpressHelpersLibrary.h @@ -0,0 +1,40 @@ +/** @file + Header file for Pci Express helps library implementation. + + Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#ifndef _PCI_EXPRESS_HELPERS_LIBRARY_H_ +#define _PCI_EXPRESS_HELPERS_LIBRARY_H_ + +#include <Uefi/UefiBaseType.h> +#include <Library/BaseLib.h> +#include <Library/IoLib.h> +#include <Library/DebugLib.h> +#include <IndustryStandard/Pci.h> +#include <PchPolicyCommon.h> +#include <Library/PchPcieRpLib.h> +#include <Library/PchPcrLib.h> +#include <Library/PchInfoLib.h> +#include <Library/PciSegmentLib.h> +#include <Library/GpioNativeLib.h> +#include <Library/TimerLib.h> +#include <Library/PciExpressHelpersLib.h> +#include <Library/PcieRpLib.h> +#include <PcieRegs.h> +#include <Register/CpuPcieRegs.h> +#include <Register/PchPcieRpRegs.h> + +#define LTR_VALUE_MASK (BIT0 + BIT1 + BIT2 + BIT3 + BIT4 + BIT5 + BIT6 + BIT7 + BIT8 + BIT9) +#define LTR_SCALE_MASK (BIT10 + BIT11 + BIT12) + + #define CONFIG_WRITE_LOOP_COUNT 100000 + +// +// LTR related macros +// +#define LTR_LATENCY_VALUE(x) ((x) & LTR_VALUE_MASK) +#define LTR_SCALE_VALUE(x) (((x) & LTR_SCALE_MASK) >> 10) +#define LTR_LATENCY_NS(x) (LTR_LATENCY_VALUE(x) * (1 << (5 * LTR_SCALE_VALUE(x)))) + +#endif diff --git a/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/PciExpressHelpersLibrary/PeiDxeSmmPciExpressHelpersLib.inf b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/PciExpressHelpersLibrary/PeiDxeSmmPciExpressHelpersLib.inf new file mode 100644 index 0000000000..8f673d14c2 --- /dev/null +++ b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/PciExpressHelpersLibrary/PeiDxeSmmPciExpressHelpersLib.inf @@ -0,0 +1,49 @@ +## @file +# Component description file for the PeiDxeSmmPciExpressHelpersLib +# +# Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + + +[Defines] + INF_VERSION = 0x00010017 + BASE_NAME = PeiDxeSmmPciExpressHelpersLib + FILE_GUID = 07E3F76D-6D26-419d-9053-58696A15B519 + VERSION_STRING = 1.0 + MODULE_TYPE = BASE +LIBRARY_CLASS = PciExpressHelpersLib +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + + + + +[LibraryClasses] + IoLib + DebugLib + PchPcieRpLib + PchPcrLib + PchInfoLib + GpioLib + TimerLib + BasePcieHelperLib + PchPciBdfLib + HobLib + PcieRpLib + +[Packages] + MdePkg/MdePkg.dec + TigerlakeSiliconPkg/SiPkg.dec + + +[Sources] + PciExpressHelpersLibrary.c + PciExpressHelpersLibrary.h + +[Guids] + gCpuPcieHobGuid diff --git a/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/PcieClientRpLib/PcieClientRpLib.c b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/PcieClientRpLib/PcieClientRpLib.c new file mode 100644 index 0000000000..15d295a573 --- /dev/null +++ b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/PcieClientRpLib/PcieClientRpLib.c @@ -0,0 +1,247 @@ +/** @file + This file contains routines that support PCI Express initialization + + Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#include <Uefi/UefiBaseType.h> +#include <IndustryStandard/Pci.h> +#include <Library/BaseMemoryLib.h> +#include <Library/PcieHelperLib.h> +#include <Library/PchSbiAccessLib.h> +#include <Library/PchPciBdfLib.h> +#include <Library/PcieRpLib.h> +#include <Library/PchInfoLib.h> +#include <Library/PchPcieRpLib.h> +#include <Library/PsfLib.h> +#include <Library/HobLib.h> +#include <Register/PchPcieRpRegs.h> +#include <Register/PcieSipRegs.h> +#include <PcieRegs.h> +#include <PchPcieRpInfo.h> +#include <CpuPcieInfo.h> +#include <CpuPcieHob.h> + +/** + 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; + UINT32 LtrCfgLock; + + IsCpuPcie = FALSE; + OvrEn = 0; + OvrVal = 0; + LtrCfgLock = 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); + } +} diff --git a/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/PcieClientRpLib/PcieClientRpLib.inf b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/PcieClientRpLib/PcieClientRpLib.inf new file mode 100644 index 0000000000..86dceb14ee --- /dev/null +++ b/Silicon/Intel/TigerlakeSiliconPkg/IpBlock/PcieRp/LibraryPrivate/PcieClientRpLib/PcieClientRpLib.inf @@ -0,0 +1,43 @@ +## @file +# Component description file for the PcieClientRpLib +# +# Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + + +[Defines] + INF_VERSION = 0x00010017 + BASE_NAME = PcieClientRpLib + FILE_GUID = 77EB467D-674C-4C20-A13E-381600E182C4 + VERSION_STRING = 1.0 + MODULE_TYPE = BASE + LIBRARY_CLASS = PcieRpLib +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + + + +[LibraryClasses] + IoLib + DebugLib + PchPcieRpLib + PchPcrLib + PchInfoLib + GpioLib + TimerLib + PsfLib + BasePcieHelperLib + PchPciBdfLib + +[Packages] + MdePkg/MdePkg.dec + TigerlakeSiliconPkg/SiPkg.dec + + +[Sources] + PcieClientRpLib.c -- 2.24.0.windows.2 -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#71207): https://edk2.groups.io/g/devel/message/71207 Mute This Topic: https://groups.io/mt/80375664/21656 Group Owner: devel+ow...@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [arch...@mail-archive.com] -=-=-=-=-=-=-=-=-=-=-=-