From: Vu Nguyen <vungu...@os.amperecomputing.com> Provides essential functions to initialize the PCIe Root Complex on Ampere Altra processor.
Cc: Thang Nguyen <th...@os.amperecomputing.com> Cc: Chuong Tran <chu...@os.amperecomputing.com> Cc: Phong Vo <ph...@os.amperecomputing.com> Cc: Leif Lindholm <l...@nuviainc.com> Cc: Michael D Kinney <michael.d.kin...@intel.com> Cc: Ard Biesheuvel <ardb+tianoc...@kernel.org> Cc: Nate DeSimone <nathaniel.l.desim...@intel.com> Signed-off-by: Nhi Pham <n...@os.amperecomputing.com> --- Changes since v5: * Fixed an error with the NOOPT build. https://edk2.groups.io/g/devel/message/83855 Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dec | 6 + Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc | 2 + Platform/Ampere/JadePkg/Jade.dsc | 5 + Silicon/Ampere/AmpereAltraPkg/Library/Ac01PcieLib/Ac01PcieLib.inf | 42 + Silicon/Ampere/AmpereAltraPkg/Library/BoardPcieLibNull/BoardPcieLibNull.inf | 25 + Silicon/Ampere/AmpereAltraPkg/Include/Library/Ac01PcieLib.h | 49 + Silicon/Ampere/AmpereAltraPkg/Include/Library/BoardPcieLib.h | 45 + Silicon/Ampere/AmpereAltraPkg/Library/Ac01PcieLib/PcieCore.h | 372 +++++ Silicon/Ampere/AmpereAltraPkg/Library/Ac01PcieLib/PcieCore.c | 1416 ++++++++++++++++++++ Silicon/Ampere/AmpereAltraPkg/Library/BoardPcieLibNull/BoardPcieLibNull.c | 47 + 10 files changed, 2009 insertions(+) diff --git a/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dec b/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dec index e19925c68a0e..7bd4d3ac9462 100644 --- a/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dec +++ b/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dec @@ -43,6 +43,12 @@ [LibraryClasses] ## @libraryclass Defines a set of methods to access flash memory. FlashLib|Silicon/Ampere/AmpereAltraPkg/Include/Library/FlashLib.h + ## @libraryclass Defines a set of platform dependent functions + BoardPcieLib|Silicon/Ampere/AmpereAltraPkg/Include/Library/BoardPcieLib.h + + ## @libraryclass Defines a set of methods to initialize Pcie + Ac01PcieLib|Silicon/Ampere/AmpereAltraPkg/Include/Library/Ac01PcieLib.h + [Guids] ## NVParam MM GUID gNVParamMmGuid = { 0xE4AC5024, 0x29BE, 0x4ADC, { 0x93, 0x36, 0x87, 0xB5, 0xA0, 0x76, 0x23, 0x2D } } diff --git a/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc b/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc index fa9b120b2c2b..5b767ecb024f 100644 --- a/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc +++ b/Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dsc.inc @@ -81,6 +81,8 @@ [LibraryClasses.common] NVParamLib|Silicon/Ampere/AmpereAltraPkg/Library/NVParamLib/NVParamLib.inf MailboxInterfaceLib|Silicon/Ampere/AmpereAltraPkg/Library/MailboxInterfaceLib/MailboxInterfaceLib.inf SystemFirmwareInterfaceLib|Silicon/Ampere/AmpereAltraPkg/Library/SystemFirmwareInterfaceLib/SystemFirmwareInterfaceLib.inf + PciePhyLib|Silicon/Ampere/AmpereAltraBinPkg/Library/PciePhyLib/PciePhyLib.inf + Ac01PcieLib|Silicon/Ampere/AmpereAltraPkg/Library/Ac01PcieLib/Ac01PcieLib.inf AmpereCpuLib|Silicon/Ampere/AmpereAltraPkg/Library/AmpereCpuLib/AmpereCpuLib.inf TimeBaseLib|EmbeddedPkg/Library/TimeBaseLib/TimeBaseLib.inf I2cLib|Silicon/Ampere/AmpereAltraPkg/Library/DwI2cLib/DwI2cLib.inf diff --git a/Platform/Ampere/JadePkg/Jade.dsc b/Platform/Ampere/JadePkg/Jade.dsc index e4b29e36fc8d..23a297d0dbeb 100644 --- a/Platform/Ampere/JadePkg/Jade.dsc +++ b/Platform/Ampere/JadePkg/Jade.dsc @@ -82,6 +82,11 @@ [LibraryClasses] # AcpiLib|EmbeddedPkg/Library/AcpiLib/AcpiLib.inf + # + # Pcie Board + # + BoardPcieLib|Silicon/Ampere/AmpereAltraPkg/Library/BoardPcieLibNull/BoardPcieLibNull.inf + ################################################################################ # # Specific Platform Pcds diff --git a/Silicon/Ampere/AmpereAltraPkg/Library/Ac01PcieLib/Ac01PcieLib.inf b/Silicon/Ampere/AmpereAltraPkg/Library/Ac01PcieLib/Ac01PcieLib.inf new file mode 100644 index 000000000000..8c8661265cd5 --- /dev/null +++ b/Silicon/Ampere/AmpereAltraPkg/Library/Ac01PcieLib/Ac01PcieLib.inf @@ -0,0 +1,42 @@ +## @file +# +# Copyright (c) 2020 - 2021, Ampere Computing LLC. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x0001001B + BASE_NAME = Ac01PcieLib + FILE_GUID = 8ABFA0FC-313E-11E8-B467-0ED5F89F718B + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = Ac01PcieLib + +[Sources] + PcieCore.c + PcieCore.h + +[Packages] + ArmPkg/ArmPkg.dec + MdePkg/MdePkg.dec + Silicon/Ampere/AmpereAltraBinPkg/AmpereAltraBinPkg.dec + Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dec + +[LibraryClasses] + ArmGenericTimerCounterLib + BaseLib + BoardPcieLib + DebugLib + HobLib + IoLib + PciePhyLib + SystemFirmwareInterfaceLib + TimerLib + +[Guids] + gPlatformInfoHobGuid + +[Depex] + TRUE diff --git a/Silicon/Ampere/AmpereAltraPkg/Library/BoardPcieLibNull/BoardPcieLibNull.inf b/Silicon/Ampere/AmpereAltraPkg/Library/BoardPcieLibNull/BoardPcieLibNull.inf new file mode 100644 index 000000000000..435092b864ec --- /dev/null +++ b/Silicon/Ampere/AmpereAltraPkg/Library/BoardPcieLibNull/BoardPcieLibNull.inf @@ -0,0 +1,25 @@ +## @file +# +# Copyright (c) 2021, Ampere Computing LLC. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x0001001B + BASE_NAME = BoardPcieLibNull + FILE_GUID = 7820C925-F525-4101-8E64-87838356B7A6 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = BoardPcieLib + +[Sources.common] + BoardPcieLibNull.c + +[Packages] + MdePkg/MdePkg.dec + Silicon/Ampere/AmpereAltraPkg/AmpereAltraPkg.dec + +[LibraryClasses] + BaseLib diff --git a/Silicon/Ampere/AmpereAltraPkg/Include/Library/Ac01PcieLib.h b/Silicon/Ampere/AmpereAltraPkg/Include/Library/Ac01PcieLib.h new file mode 100644 index 000000000000..692bc2669915 --- /dev/null +++ b/Silicon/Ampere/AmpereAltraPkg/Include/Library/Ac01PcieLib.h @@ -0,0 +1,49 @@ +/** @file + + Copyright (c) 2020 - 2021, Ampere Computing LLC. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef AC01_PCIE_LIB_H_ +#define AC01_PCIE_LIB_H_ + +/** + Setup and initialize the AC01 PCIe Root Complex and underneath PCIe controllers + + @param RootComplex Pointer to Root Complex structure + @param ReInit Re-init status + @param ReInitPcieIndex PCIe controller index + + @retval RETURN_SUCCESS The Root Complex has been initialized successfully. + @retval RETURN_DEVICE_ERROR PHY, Memory or PIPE is not ready. +**/ +RETURN_STATUS +Ac01PcieCoreSetupRC ( + IN AC01_ROOT_COMPLEX *RootComplex, + IN BOOLEAN ReInit, + IN UINT8 ReInitPcieIndex + ); + +/** + Verify the link status and retry to initialize the Root Complex if there's any issue. + + @param RootComplexList Pointer to the Root Complex list +**/ +VOID +Ac01PcieCorePostSetupRC ( + IN AC01_ROOT_COMPLEX *RootComplexList + ); + +/** + Callback function when the Host Bridge enumeration end. + + @param RootComplex Pointer to the Root Complex structure +**/ +VOID +Ac01PcieCoreEndEnumeration ( + IN AC01_ROOT_COMPLEX *RootComplex + ); + +#endif /* AC01_PCIE_LIB_H_ */ diff --git a/Silicon/Ampere/AmpereAltraPkg/Include/Library/BoardPcieLib.h b/Silicon/Ampere/AmpereAltraPkg/Include/Library/BoardPcieLib.h new file mode 100644 index 000000000000..34e7dee702ec --- /dev/null +++ b/Silicon/Ampere/AmpereAltraPkg/Include/Library/BoardPcieLib.h @@ -0,0 +1,45 @@ +/** @file + + Copyright (c) 2020 - 2021, Ampere Computing LLC. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef BOARD_PCIE_LIB_H_ +#define BOARD_PCIE_LIB_H_ + +#include <Guid/RootComplexInfoHob.h> + +/** + Assert PERST of the PCIe controller + + @param[in] RootComplex Root Complex instance. + @param[in] PcieIndex PCIe controller index of input Root Complex. + @param[in] IsPullToHigh Target status for the PERST. + + @retval RETURN_SUCCESS The operation is successful. + @retval Others An error occurred. +**/ +RETURN_STATUS +EFIAPI +BoardPcieAssertPerst ( + IN AC01_ROOT_COMPLEX *RootComplex, + IN UINT8 PcieIndex, + IN BOOLEAN IsPullToHigh + ); + +/** + Override the segment number for a root complex with a board specific number. + + @param[in] RootComplex Root Complex instance with properties. + + @retval Segment number corresponding to the input root complex. + Default segment number is 0x0F. +**/ +UINT16 +BoardPcieGetSegmentNumber ( + IN AC01_ROOT_COMPLEX *RootComplex + ); + +#endif /* BOARD_PCIE_LIB_H_ */ diff --git a/Silicon/Ampere/AmpereAltraPkg/Library/Ac01PcieLib/PcieCore.h b/Silicon/Ampere/AmpereAltraPkg/Library/Ac01PcieLib/PcieCore.h new file mode 100644 index 000000000000..1db8a68b3df4 --- /dev/null +++ b/Silicon/Ampere/AmpereAltraPkg/Library/Ac01PcieLib/PcieCore.h @@ -0,0 +1,372 @@ +/** @file + + Copyright (c) 2020 - 2021, Ampere Computing LLC. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef AC01_PCIE_CORE_H_ +#define AC01_PCIE_CORE_H_ + +#define BUS_SHIFT 20 +#define DEV_SHIFT 15 + +#define GET_LOW_8_BITS(x) ((x) & 0xFF) +#define GET_HIGH_8_BITS(x) (((x) >> 8) & 0xFF) +#define GET_LOW_16_BITS(x) ((x) & 0xFFFF) +#define GET_HIGH_16_BITS(x) (((x) >> 16) & 0xFFFF) +#define GET_CAPABILITY_PTR(x) (GET_LOW_16_BITS (x) >> 8) +#define GET_EXT_CAPABILITY_PTR(x) (GET_HIGH_16_BITS (x) >> 4) + +#define WORD_ALIGN_MASK 0x3 + +#define MAX_REINIT 3 // Number of soft reset retry + +#define SLOT_POWER_LIMIT_75W 75 // Watt + +#define LINK_CHECK_SUCCESS 0 +#define LINK_CHECK_FAILED -1 +#define LINK_CHECK_WRONG_PARAMETER 1 + +#define AMPERE_PCIE_VENDOR_ID 0x1DEF +#define AC01_HOST_BRIDGE_DEVICE_ID_RCA 0xE100 +#define AC01_HOST_BRIDGE_DEVICE_ID_RCB 0xE110 +#define AC01_PCIE_BRIDGE_DEVICE_ID_RCA 0xE101 +#define AC01_PCIE_BRIDGE_DEVICE_ID_RCB 0xE111 + +#define MEMRDY_TIMEOUT 10 // 10 us +#define PIPE_CLOCK_TIMEOUT 20000 // 20,000 us +#define LTSSM_TRANSITION_TIMEOUT 100000 // 100 ms in total +#define EP_LINKUP_TIMEOUT (10 * 1000) // 10ms +#define LINK_WAIT_INTERVAL_US 50 + +#define PFA_MODE_ENABLE 0 +#define PFA_MODE_CLEAR 1 +#define PFA_MODE_READ 2 + +// +// Host Bridge registers +// +#define AC01_HOST_BRIDGE_RCA_DEV_MAP_REG 0x0 +#define AC01_HOST_BRIDGE_RCB_DEV_MAP_REG 0x4 +#define AC01_HOST_BRIDGE_VENDOR_DEVICE_ID_REG 0x10 + +// AC01_HOST_BRIDGE_RCA_DEV_MAP_REG +#define RCA_DEV_MAP_SET(dst, src) (((dst) & ~0x7) | (((UINT32) (src)) & 0x7)) +#define RCA_DEV_MAP_GET(val) ((val) & 0x7) + +// AC01_HOST_BRIDGE_RCB_DEV_MAP_REG +#define RCB_DEV_MAP_LOW_SET(dst, src) (((dst) & ~0x7) | (((UINT32) (src)) & 0x7)) +#define RCB_DEV_MAP_LOW_GET(val) ((val) & 0x7) + +#define RCB_DEV_MAP_HIGH_SET(dst, src) (((dst) & ~0x70) | (((UINT32) (src) << 4) & 0x70)) +#define RCB_DEV_MAP_HIGH_GET(val) (((val) & 0x7) >> 4) + +// AC01_HOST_BRIDGE_VENDOR_DEVICE_ID_REG +#define VENDOR_ID_SET(dst, src) (((dst) & ~0xFFFF) | (((UINT32) (src)) & 0xFFFF)) +#define VENDOR_ID_GET(val) ((val) & 0xFFFF) + +#define DEVICE_ID_SET(dst, src) (((dst) & ~0xFFFF0000) | (((UINT32) (src) << 16) & 0xFFFF0000)) +#define DEVICE_ID_GET(val) (((val) & 0xFFFF0000) >> 16) + +// +// PCIe core registers +// +#define AC01_PCIE_CORE_LINK_CTRL_REG 0x0 +#define AC01_PCIE_CORE_LINK_STAT_REG 0x4 +#define AC01_PCIE_CORE_IRQ_SEL_REG 0xC +#define AC01_PCIE_CORE_HOT_PLUG_STAT_REG 0x28 +#define AC01_PCIE_CORE_IRQ_ENABLE_REG 0x30 +#define AC01_PCIE_CORE_IRQ_EVENT_STAT_REG 0x38 +#define AC01_PCIE_CORE_BLOCK_EVENT_STAT_REG 0x3C +#define AC01_PCIE_CORE_RESET_REG 0xC000 +#define AC01_PCIE_CORE_CLOCK_REG 0xC004 +#define AC01_PCIE_CORE_MEM_READY_REG 0xC104 +#define AC01_PCIE_CORE_RAM_SHUTDOWN_REG 0xC10C + +// AC01_PCIE_CORE_LINK_CTRL_REG +#define LTSSMENB_SET(dst, src) (((dst) & ~0x1) | (((UINT32) (src)) & 0x1)) +#define HOLD_LINK_TRAINING 0 +#define START_LINK_TRAINING 1 +#define DEVICETYPE_SET(dst, src) (((dst) & ~0xF0) | (((UINT32) (src) << 4) & 0xF0)) +#define DEVICETYPE_GET(val) (((val) & 0xF0) >> 4) + +// AC01_PCIE_CORE_LINK_STAT_REG +#define PHY_STATUS_MASK (1 << 2) +#define SMLH_LTSSM_STATE_MASK 0x3F00 +#define SMLH_LTSSM_STATE_GET(val) ((val & SMLH_LTSSM_STATE_MASK) >> 8) +#define LTSSM_STATE_L0 0x11 +#define RDLH_SMLH_LINKUP_STATUS_GET(val) (val & 0x3) +#define PHY_STATUS_MASK_BIT 0x04 +#define SMLH_LINK_UP_MASK_BIT 0x02 +#define RDLH_LINK_UP_MASK_BIT 0x01 + +// AC01_PCIE_CORE_IRQ_SEL_REG +#define AER_SET(dst, src) (((dst) & ~0x1) | (((UINT32) (src)) & 0x1)) +#define PME_SET(dst, src) (((dst) & ~0x2) | (((UINT32) (src) << 1) & 0x2)) +#define LINKAUTOBW_SET(dst, src) (((dst) & ~0x4) | (((UINT32) (src) << 2) & 0x4)) +#define BWMGMT_SET(dst, src) (((dst) & ~0x8) | (((UINT32) (src) << 3) & 0x8)) +#define EQRQST_SET(dst, src) (((dst) & ~0x10) | (((UINT32) (src) << 4) & 0x10)) +#define INTPIN_SET(dst, src) (((dst) & ~0xFF00) | (((UINT32) (src) << 8) & 0xFF00)) +#define IRQ_INT_A 0x01 + +// AC01_PCIE_CORE_HOT_PLUG_STAT_REG +#define PWR_IND_SET(dst, src) (((dst) & ~0x1) | (((UINT32) (src)) & 0x1)) +#define ATTEN_IND_SET(dst, src) (((dst) & ~0x2) | (((UINT32) (src) << 1) & 0x2)) +#define PWR_CTRL_SET(dst, src) (((dst) & ~0x4) | (((UINT32) (src) << 2) & 0x4)) +#define EML_CTRL_SET(dst, src) (((dst) & ~0x8) | (((UINT32) (src) << 3) & 0x8)) + +// AC01_PCIE_CORE_BLOCK_EVENT_STAT_REG +#define LINKUP_MASK 0x1 + +// AC01_PCIE_CORE_RESET_REG +#define DWC_PCIE_SET(dst, src) (((dst) & ~0x1) | (((UINT32) (src)) & 0x1)) +#define RESET_MASK 0x1 +#define ASSERT_RESET 0x1 + +// AC01_PCIE_CORE_CLOCK_REG +#define AXIPIPE_SET(dst, src) (((dst) & ~0x1) | (((UINT32) (src)) & 0x1)) + +// AC01_PCIE_CORE_MEM_READY_REG +#define MEMORY_READY 0x1 + +// AC01_PCIE_CORE_RAM_SHUTDOWN_REG +#define SD_SET(dst, src) (((dst) & ~0x1) | (((UINT32) (src)) & 0x1)) + +// +// AC01 PCIe Type 1 configuration registers +// +#define TYPE1_DEV_ID_VEND_ID_REG 0 +#define TYPE1_CLASS_CODE_REV_ID_REG 0x8 +#define TYPE1_CAP_PTR_REG 0x34 +#define SEC_LAT_TIMER_SUB_BUS_SEC_BUS_PRI_BUS_REG 0x18 +#define BRIDGE_CTRL_INT_PIN_INT_LINE_REG 0x3C +#define PCIE_CAPABILITY_BASE 0x70 +#define EXT_CAPABILITY_START_BASE 0x100 +#define AER_CAPABILITY_BASE 0x100 + +// TYPE1_DEV_ID_VEND_ID_REG +#define VENDOR_ID_SET(dst, src) (((dst) & ~0xFFFF) | (((UINT32) (src)) & 0xFFFF)) +#define DEVICE_ID_SET(dst, src) (((dst) & ~0xFFFF0000) | (((UINT32) (src) << 16) & 0xFFFF0000)) + +// TYPE1_CLASS_CODE_REV_ID_REG +#define BASE_CLASS_CODE_SET(dst, src) (((dst) & ~0xFF000000) | (((UINT32) (src) << 24) & 0xFF000000)) +#define DEFAULT_BASE_CLASS_CODE 6 +#define SUB_CLASS_CODE_SET(dst, src) (((dst) & ~0xFF0000) | (((UINT32) (src) << 16) & 0xFF0000)) +#define DEFAULT_SUB_CLASS_CODE 4 +#define PROGRAM_INTERFACE_SET(dst, src) (((dst) & ~0xFF00) | (((UINT32) (src) << 8) & 0xFF00)) +#define REVISION_ID_SET(dst, src) (((dst) & ~0xFF) | (((UINT32) (src)) & 0xFF)) +#define DEFAULT_REVISION_ID 4 + +// SEC_LAT_TIMER_SUB_BUS_SEC_BUS_PRI_BUS_REG +#define SUB_BUS_SET(dst, src) (((dst) & ~0xFF0000) | (((UINT32) (src) << 16) & 0xFF0000)) +#define DEFAULT_SUB_BUS 0xFF +#define SEC_BUS_SET(dst, src) (((dst) & ~0xFF00) | (((UINT32) (src) << 8) & 0xFF00)) +#define PRIM_BUS_SET(dst, src) (((dst) & ~0xFF) | (((UINT32) (src)) & 0xFF)) +#define DEFAULT_PRIM_BUS 0x00 + +// BRIDGE_CTRL_INT_PIN_INT_LINE_REG +#define INT_PIN_SET(dst, src) (((dst) & ~0xFF00) | (((UINT32) (src) << 8) & 0xFF00)) + +// +// PCI Express Capability +// +#define PCIE_CAPABILITY_ID 0x10 +#define LINK_CAPABILITIES_REG 0xC +#define LINK_CONTROL_LINK_STATUS_REG 0x10 +#define SLOT_CAPABILITIES_REG 0x14 +#define DEVICE_CONTROL2_DEVICE_STATUS2_REG 0x28 +#define LINK_CAPABILITIES2_REG 0x2C +#define LINK_CONTROL2_LINK_STATUS2_REG 0x30 + +// LINK_CAPABILITIES_REG +#define CAP_ACTIVE_STATE_LINK_PM_SUPPORT_SET(dst, src) (((dst) & ~0xC00) | (((UINT32)(src) << 10) & 0xC00)) +#define NO_ASPM_SUPPORTED 0x0 +#define L0S_SUPPORTED 0x1 +#define L1_SUPPORTED 0x2 +#define L0S_L1_SUPPORTED 0x3 +#define CAP_MAX_LINK_WIDTH_GET(val) ((val & 0x3F0) >> 4) +#define CAP_MAX_LINK_WIDTH_SET(dst, src) (((dst) & ~0x3F0) | (((UINT32) (src) << 4) & 0x3F0)) +#define CAP_MAX_LINK_WIDTH_X1 0x1 +#define CAP_MAX_LINK_WIDTH_X2 0x2 +#define CAP_MAX_LINK_WIDTH_X4 0x4 +#define CAP_MAX_LINK_WIDTH_X8 0x8 +#define CAP_MAX_LINK_WIDTH_X16 0x10 +#define CAP_MAX_LINK_SPEED_GET(val) ((val & 0xF)) +#define CAP_MAX_LINK_SPEED_SET(dst, src) (((dst) & ~0xF) | (((UINT32) (src)) & 0xF)) +#define MAX_LINK_SPEED_25 0x1 +#define MAX_LINK_SPEED_50 0x2 +#define MAX_LINK_SPEED_80 0x3 +#define MAX_LINK_SPEED_160 0x4 +#define MAX_LINK_SPEED_320 0x5 + +// LINK_CONTROL_LINK_STATUS_REG +#define CAP_DLL_ACTIVE_GET(val) ((val & 0x20000000) >> 29) +#define CAP_SLOT_CLK_CONFIG_SET(dst, src) (((dst) & ~0x10000000) | (((UINT32) (src) << 28) & 0x10000000)) +#define CAP_NEGO_LINK_WIDTH_GET(val) ((val & 0x3F00000) >> 20) +#define CAP_LINK_SPEED_GET(val) ((val & 0xF0000) >> 16) +#define CAP_LINK_SPEED_SET(dst, src) (((dst) & ~0xF0000) | (((UINT32) (src) << 16) & 0xF0000)) +#define CAP_LINK_SPEED_TO_VECTOR(val) (1 << ((val) - 1)) +#define CAP_EN_CLK_POWER_MAN_GET(val) ((val & 0x100) >> 8) +#define CAP_EN_CLK_POWER_MAN_SET(dst, src) (((dst) & ~0x100) | (((UINT32) (src) << 8) & 0x100)) +#define CAP_COMMON_CLK_SET(dst, src) (((dst) & ~0x40) | (((UINT32) (src) << 6) & 0x40)) +#define CAP_RETRAIN_LINK_SET(dst, src) (((dst) & ~0x20) | (((UINT32) (src) << 5) & 0x20)) +#define CAP_LINK_TRAINING_GET(val) ((val & 0x8000000) >> 27) +#define CAP_LINK_DISABLE_SET(dst, src) (((dst) & ~0x10) | (((UINT32)(src) << 4) & 0x10)) + +// SLOT_CAPABILITIES_REG +#define SLOT_HPC_SET(dst, src) (((dst) & ~0x40) | (((UINT32) (src) << 6) & 0x40)) +#define SLOT_CAP_SLOT_POWER_LIMIT_VALUE_SET(dst, src) \ + (((dst) & ~0x7F80) | (((UINT32)(src) << 7) & 0x7F80)) + +// DEVICE_CONTROL2_DEVICE_STATUS2_REG +#define CAP_CPL_TIMEOUT_VALUE_SET(dst, src) (((dst) & ~0xF) | (((UINT32) (src)) & 0xF)) + +// LINK_CONTROL2_LINK_STATUS2_REG +#define CAP_TARGET_LINK_SPEED_SET(dst, src) (((dst) & ~0xF) | (((UINT32) (src)) & 0xF)) + +// +// Advanced Error Reporting Capability +// +#define AER_CAPABILITY_ID 0x0001 +#define UNCORR_ERR_STATUS_OFF 0x04 +#define UNCORR_ERR_MASK_OFF 0x08 + +// UNCORR_ERR_MASK_OFF +#define CMPLT_TIMEOUT_ERR_MASK_SET(dst, src) (((dst) & ~0x4000) | (((UINT32) (src) << 14) & 0x4000)) +#define SDES_ERR_MASK_SET(dst, src) (((dst) & ~0x20) | (((UINT32)(src) << 5) & 0x20)) + +// +// Vendor specific RAS D.E.S Capability +// +#define RAS_DES_CAPABILITY_ID 0x000B +#define EVENT_COUNTER_CONTROL_REG 0x08 +#define EVENT_COUNTER_DATA_REG 0x0C + +// EVENT_COUNTER_CONTROL_REG +#define ECCR_GROUP_EVENT_SEL_SET(dst, src) (((dst) & ~0xFFF0000) | (((UINT32)(src) << 16) & 0xFFF0000)) +#define ECCR_GROUP_SEL_SET(dst, src) (((dst) & ~0xF000000) | (((UINT32)(src) << 24) & 0xF000000)) +#define ECCR_EVENT_SEL_SET(dst, src) (((dst) & ~0xFF0000) | (((UINT32)(src) << 16) & 0xFF0000)) +#define ECCR_LANE_SEL_SET(dst, src) (((dst) & ~0xF00) | (((UINT32)(src) << 8) & 0xF00)) +#define ECCR_EVENT_COUNTER_ENABLE_SET(dst, src) (((dst) & ~0x1C) | (((UINT32)(src) << 2) & 0x1C)) +#define EVENT_COUNTER_ENABLE_NO_CHANGE 0x00 +#define EVENT_COUNTER_ENABLE_ALL_ON 0x07 +#define ECCR_EVENT_COUNTER_CLEAR_SET(dst, src) (((dst) & ~0x3) | (((UINT32)(src)) & 0x3)) +#define EVENT_COUNTER_CLEAR_NO_CHANGE 0x00 +#define EVENT_COUNTER_CLEAR_ALL_CLEAR 0x03 + +// +// Secondary PCI Express Capability +// +#define SPCIE_CAPABILITY_ID 0x0019 +#define SPCIE_CAP_OFF_0C_REG 0x0C + +// SPCIE_CAP_OFF_0C_REG +#define DSP_TX_PRESET0_SET(dst,src) (((dst) & ~0xF) | (((UINT32) (src)) & 0xF)) +#define DSP_TX_PRESET1_SET(dst,src) (((dst) & ~0xF0000) | (((UINT32) (src) << 16) & 0xF0000)) +#define DEFAULT_GEN3_PRESET 0x05 + +// +// Physical Layer 16.0 GT/s Extended Capability +// +#define PL16G_CAPABILITY_ID 0x0026 +#define PL16G_STATUS_REG 0x0C +#define PL16G_CAP_OFF_20H_REG 0x20 + +// PL16G_STATUS_REG +#define PL16G_STATUS_EQ_CPL_GET(val) (val & 0x1) +#define PL16G_STATUS_EQ_CPL_P1_GET(val) ((val & 0x2) >> 1) +#define PL16G_STATUS_EQ_CPL_P2_GET(val) ((val & 0x4) >> 2) +#define PL16G_STATUS_EQ_CPL_P3_GET(val) ((val & 0x8) >> 3) + +// PL16G_CAP_OFF_20H_REG +#define DSP_16G_TX_PRESET0_SET(dst,src) (((dst) & ~0xF) | (((UINT32) (src)) & 0xF)) +#define DSP_16G_TX_PRESET1_SET(dst,src) (((dst) & ~0xF00) | (((UINT32) (src) << 8) & 0xF00)) +#define DSP_16G_TX_PRESET2_SET(dst,src) (((dst) & ~0xF0000) | (((UINT32) (src) << 16) & 0xF0000)) +#define DSP_16G_TX_PRESET3_SET(dst,src) (((dst) & ~0xF000000) | (((UINT32) (src) << 24) & 0xF000000)) +#define DSP_16G_RXTX_PRESET0_SET(dst,src) (((dst) & ~0xFF) | (((UINT32) (src)) & 0xFF)) +#define DSP_16G_RXTX_PRESET1_SET(dst,src) (((dst) & ~0xFF00) | (((UINT32) (src) << 8) & 0xFF00)) +#define DSP_16G_RXTX_PRESET2_SET(dst,src) (((dst) & ~0xFF0000) | (((UINT32) (src) << 16) & 0xFF0000)) +#define DSP_16G_RXTX_PRESET3_SET(dst,src) (((dst) & ~0xFF000000) | (((UINT32) (src) << 24) & 0xFF000000)) +#define DEFAULT_GEN4_PRESET 0x57 + +// +// Port Logic +// +#define PORT_LINK_CTRL_OFF 0x710 +#define FILTER_MASK_2_OFF 0x720 +#define GEN2_CTRL_OFF 0x80C +#define GEN3_RELATED_OFF 0x890 +#define GEN3_EQ_CONTROL_OFF 0x8A8 +#define MISC_CONTROL_1_OFF 0x8BC +#define AMBA_ERROR_RESPONSE_DEFAULT_OFF 0x8D0 +#define AMBA_LINK_TIMEOUT_OFF 0x8D4 +#define AMBA_ORDERING_CTRL_OFF 0x8D8 +#define DTIM_CTRL0_OFF 0xAB0 +#define AUX_CLK_FREQ_OFF 0xB40 +#define CCIX_CTRL_OFF 0xC20 + +// PORT_LINK_CTRL_OFF +#define LINK_CAPABLE_SET(dst, src) (((dst) & ~0x3F0000) | (((UINT32) (src) << 16) & 0x3F0000)) +#define LINK_CAPABLE_X1 0x1 +#define LINK_CAPABLE_X2 0x3 +#define LINK_CAPABLE_X4 0x7 +#define LINK_CAPABLE_X8 0xF +#define LINK_CAPABLE_X16 0x1F +#define LINK_CAPABLE_X32 0x3F +#define FAST_LINK_MODE_SET(dst, src) (((dst) & ~0x80) | (((UINT32) (src) << 7) & 0x80)) + +// FILTER_MASK_2_OFF +#define CX_FLT_MASK_VENMSG0_DROP_SET(dst, src) (((dst) & ~0x1) | (((UINT32) (src)) & 0x1)) +#define CX_FLT_MASK_VENMSG1_DROP_SET(dst, src) (((dst) & ~0x2) | (((UINT32) (src) << 1) & 0x2)) +#define CX_FLT_MASK_DABORT_4UCPL_SET(dst, src) (((dst) & ~0x4) | (((UINT32) (src) << 2) & 0x4)) + +// GEN2_CTRL_OFF +#define NUM_OF_LANES_SET(dst, src) (((dst) & ~0x1F00) | (((UINT32) (src) << 8) & 0x1F00)) +#define NUM_OF_LANES_X2 0x2 +#define NUM_OF_LANES_X4 0x4 +#define NUM_OF_LANES_X8 0x8 +#define NUM_OF_LANES_X16 0x10 + +// GEN3_RELATED_OFF +#define RATE_SHADOW_SEL_SET(dst, src) (((dst) & ~0x3000000) | (((UINT32) (src) << 24) & 0x3000000)) +#define GEN3_DATA_RATE 0x00 +#define GEN4_DATA_RATE 0x01 +#define EQ_PHASE_2_3_SET(dst, src) (((dst) & ~0x200) | (((UINT32) (src) << 9) & 0x200)) +#define ENABLE_EQ_PHASE_2_3 0x00 +#define DISABLE_EQ_PHASE_2_3 0x01 +#define RXEQ_REGRDLESS_SET(dst, src) (((dst) & ~0x2000) | (((UINT32) (src) << 13) & 0x2000)) +#define ASSERT_RXEQ 0x01 + +// GEN3_EQ_CONTROL_OFF +#define GEN3_EQ_FB_MODE(dst, src) (((dst) & ~0xF) | ((UINT32) (src) & 0xF)) +#define FOM_METHOD 0x01 +#define GEN3_EQ_PRESET_VEC(dst, src) (((dst) & 0xFF0000FF) | (((UINT32) (src) << 8) & 0xFFFF00)) +#define EQ_DEFAULT_PRESET_VECTOR 0x370 +#define GEN3_EQ_INIT_EVAL(dst,src) (((dst) & ~0x1000000) | (((UINT32) (src) << 24) & 0x1000000)) +#define INCLUDE_INIT_FOM 0x01 + +// MISC_CONTROL_1_OFF +#define DBI_RO_WR_EN_SET(dst, src) (((dst) & ~0x1) | (((UINT32) (src)) & 0x1)) +#define ENABLE_WR 0x01 +#define DISABLE_WR 0x00 + +// AMBA_ERROR_RESPONSE_DEFAULT_OFF +#define AMBA_ERROR_RESPONSE_CRS_SET(dst, src) (((dst) & ~0x18) | (((UINT32) (src) << 3) & 0x18)) +#define AMBA_ERROR_RESPONSE_GLOBAL_SET(dst, src) (((dst) & ~0x1) | (((UINT32) (src)) & 0x1)) + +// AMBA_LINK_TIMEOUT_OFF +#define LINK_TIMEOUT_PERIOD_DEFAULT_SET(dst, src) (((dst) & ~0xFF) | (((UINT32) (src)) & 0xFF)) + +// AMBA_ORDERING_CTRL_OFF +#define AX_MSTR_ZEROLREAD_FW_SET(dst, src) (((dst) & ~0x80) | (((UINT32) (src) << 7) & 0x80)) + +// DTIM_CTRL0_OFF +#define DTIM_CTRL0_ROOT_PORT_ID_SET(dst, src) (((dst) & ~0xFFFF) | (((UINT32) (src)) & 0xFFFF)) + +// AUX_CLK_FREQ_OFF +#define AUX_CLK_FREQ_SET(dst, src) (((dst) & ~0x1FF) | (((UINT32) (src)) & 0x1FF)) +#define AUX_CLK_500MHZ 500 + +#endif /* AC01_PCIE_CORE_H_ */ diff --git a/Silicon/Ampere/AmpereAltraPkg/Library/Ac01PcieLib/PcieCore.c b/Silicon/Ampere/AmpereAltraPkg/Library/Ac01PcieLib/PcieCore.c new file mode 100644 index 000000000000..ad648b1b9efd --- /dev/null +++ b/Silicon/Ampere/AmpereAltraPkg/Library/Ac01PcieLib/PcieCore.c @@ -0,0 +1,1416 @@ +/** @file + + Copyright (c) 2020 - 2021, Ampere Computing LLC. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiDxe.h> + +#include <Guid/PlatformInfoHob.h> +#include <Guid/RootComplexInfoHob.h> +#include <Library/ArmGenericTimerCounterLib.h> +#include <Library/BaseLib.h> +#include <Library/BoardPcieLib.h> +#include <Library/DebugLib.h> +#include <Library/HobLib.h> +#include <Library/IoLib.h> +#include <Library/PciePhyLib.h> +#include <Library/SystemFirmwareInterfaceLib.h> +#include <Library/TimerLib.h> + +#include "PcieCore.h" + +/** + Return the next extended capability base address + + @param RootComplex Pointer to AC01_ROOT_COMPLEX structure + @param PcieIndex PCIe controller index + @param IsRootComplex TRUE: Checking RootComplex configuration space + FALSE: Checking EP configuration space + @param ExtCapabilityId +**/ +PHYSICAL_ADDRESS +GetCapabilityBase ( + IN AC01_ROOT_COMPLEX *RootComplex, + IN UINT8 PcieIndex, + IN BOOLEAN IsRootComplex, + IN UINT16 ExtCapabilityId + ) +{ + BOOLEAN IsExtCapability = FALSE; + PHYSICAL_ADDRESS CfgBase; + UINT32 CapabilityId; + UINT32 NextCapabilityPtr; + UINT32 Val; + + if (IsRootComplex) { + CfgBase = RootComplex->MmcfgBase + (RootComplex->Pcie[PcieIndex].DevNum << DEV_SHIFT); + } else { + CfgBase = RootComplex->MmcfgBase + (RootComplex->Pcie[PcieIndex].DevNum << BUS_SHIFT); + } + + Val = MmioRead32 (CfgBase + TYPE1_CAP_PTR_REG); + NextCapabilityPtr = GET_LOW_8_BITS (Val); + + // Loop untill desired capability is found else return 0 + while (1) { + if ((NextCapabilityPtr & WORD_ALIGN_MASK) != 0) { + // Not alignment, just return + return 0; + } + + Val = MmioRead32 (CfgBase + NextCapabilityPtr); + if (NextCapabilityPtr < EXT_CAPABILITY_START_BASE) { + CapabilityId = GET_LOW_8_BITS (Val); + } else { + CapabilityId = GET_LOW_16_BITS (Val); + } + + if (CapabilityId == ExtCapabilityId) { + return (CfgBase + NextCapabilityPtr); + } + + if (NextCapabilityPtr < EXT_CAPABILITY_START_BASE) { + NextCapabilityPtr = GET_CAPABILITY_PTR (Val); + } else { + NextCapabilityPtr = GET_EXT_CAPABILITY_PTR (Val); + } + + if ((NextCapabilityPtr == 0) && !IsExtCapability) { + IsExtCapability = TRUE; + NextCapabilityPtr = EXT_CAPABILITY_START_BASE; + } + + if ((NextCapabilityPtr == 0) && IsExtCapability) { + return 0; + } + } +} + +/** + Configure equalization settings for Gen3 and Gen4 + + @param RootComplex Pointer to AC01_ROOT_COMPLEX structure + @param PcieIndex PCIe controller index +**/ +STATIC +VOID +ConfigureEqualization ( + IN AC01_ROOT_COMPLEX *RootComplex, + IN UINT8 PcieIndex + ) +{ + PHYSICAL_ADDRESS CfgBase; + PHYSICAL_ADDRESS Gen3RelatedAddr; + PHYSICAL_ADDRESS Gen3EqControlAddr; + UINT32 Val; + + CfgBase = RootComplex->MmcfgBase + (RootComplex->Pcie[PcieIndex].DevNum << DEV_SHIFT); + + // + // Gen3 and Gen4 EQ process use the same setting registers which are + // GEN3_RELATED_OFF and GEN3_EQ_CONTROL_OFF. Both are shadow registers + // and controlled by GEN3_RELATED_OFF[25:24]. + // + Gen3RelatedAddr = CfgBase + GEN3_RELATED_OFF; + Gen3EqControlAddr = CfgBase + GEN3_EQ_CONTROL_OFF; + + // + // Equalization setting for Gen3 + // + Val = MmioRead32 (Gen3RelatedAddr); + Val = RATE_SHADOW_SEL_SET (Val, GEN3_DATA_RATE); + MmioWrite32 (Gen3RelatedAddr, Val); + + Val = EQ_PHASE_2_3_SET (Val, ENABLE_EQ_PHASE_2_3); + Val = RXEQ_REGRDLESS_SET (Val, ASSERT_RXEQ); + MmioWrite32 (Gen3RelatedAddr, Val); + + Val = MmioRead32 (Gen3EqControlAddr); + Val = GEN3_EQ_FB_MODE (Val, FOM_METHOD); + Val = GEN3_EQ_PRESET_VEC (Val, EQ_DEFAULT_PRESET_VECTOR); + Val = GEN3_EQ_INIT_EVAL (Val, INCLUDE_INIT_FOM); + MmioWrite32 (Gen3EqControlAddr, Val); + + // + // Equalization setting for Gen4 + // + Val = MmioRead32 (Gen3RelatedAddr); + Val = RATE_SHADOW_SEL_SET (Val, GEN4_DATA_RATE); + MmioWrite32 (Gen3RelatedAddr, Val); + + Val = EQ_PHASE_2_3_SET (Val, ENABLE_EQ_PHASE_2_3); + Val = RXEQ_REGRDLESS_SET (Val, ASSERT_RXEQ); + MmioWrite32 (Gen3RelatedAddr, Val); + + Val = MmioRead32 (Gen3EqControlAddr); + Val = GEN3_EQ_FB_MODE (Val, FOM_METHOD); + Val = GEN3_EQ_PRESET_VEC (Val, EQ_DEFAULT_PRESET_VECTOR); + Val = GEN3_EQ_INIT_EVAL (Val, INCLUDE_INIT_FOM); + MmioWrite32 (Gen3EqControlAddr, Val); +} + +/** + Configure presets for Gen3 equalization + + @param RootComplex Pointer to AC01_ROOT_COMPLEX structure + @param PcieIndex PCIe controller index +**/ +STATIC +VOID +ConfigurePresetGen3 ( + IN AC01_ROOT_COMPLEX *RootComplex, + IN UINT8 PcieIndex + ) +{ + PHYSICAL_ADDRESS LaneEqControlAddr; + PHYSICAL_ADDRESS SpcieCapabilityBase; + UINT32 Idx; + UINT32 LinkWidth; + UINT32 Val; + + // Get the Secondary PCI Express Extended capability base address + SpcieCapabilityBase = GetCapabilityBase (RootComplex, PcieIndex, TRUE, SPCIE_CAPABILITY_ID); + if (SpcieCapabilityBase == 0) { + DEBUG (( + DEBUG_ERROR, + "PCIE%d.%d: Cannot get SPCIE capability address\n", + RootComplex->ID, + PcieIndex + )); + return; + } + + LinkWidth = RootComplex->Pcie[PcieIndex].MaxWidth; + + // Each register holds the Preset for 2 lanes + for (Idx = 0; Idx < (LinkWidth / 2); Idx++) { + LaneEqControlAddr = SpcieCapabilityBase + SPCIE_CAP_OFF_0C_REG + Idx * sizeof (UINT32); + Val = MmioRead32 (LaneEqControlAddr); + Val = DSP_TX_PRESET0_SET (Val, DEFAULT_GEN3_PRESET); + Val = DSP_TX_PRESET1_SET (Val, DEFAULT_GEN3_PRESET); + MmioWrite32 (LaneEqControlAddr, Val); + } +} + +/** + Configure presets for Gen4 equalization + + @param RootComplex Pointer to AC01_ROOT_COMPLEX structure + @param PcieIndex PCIe controller index +**/ +STATIC +VOID +ConfigurePresetGen4 ( + IN AC01_ROOT_COMPLEX *RootComplex, + IN UINT8 PcieIndex + ) +{ + PHYSICAL_ADDRESS LaneEqControlAddr; + PHYSICAL_ADDRESS Pl16gCapabilityBase; + UINT32 Idx; + UINT32 LinkWidth; + UINT32 Val; + UINT8 Preset; + + // Get the Physical Layer 16.0 GT/s Extended capability base address + Pl16gCapabilityBase = GetCapabilityBase (RootComplex, PcieIndex, TRUE, PL16G_CAPABILITY_ID); + if (Pl16gCapabilityBase == 0) { + DEBUG (( + DEBUG_ERROR, + "PCIE%d.%d: Cannot get PL16G capability address\n", + RootComplex->ID, + PcieIndex + )); + return; + } + + if (RootComplex->PresetGen4[PcieIndex] == PRESET_INVALID) { + Preset = DEFAULT_GEN4_PRESET; + } else { + Preset = RootComplex->PresetGen4[PcieIndex]; + } + + LinkWidth = RootComplex->Pcie[PcieIndex].MaxWidth; + + if (LinkWidth == CAP_MAX_LINK_WIDTH_X2) { + LaneEqControlAddr = Pl16gCapabilityBase + PL16G_CAP_OFF_20H_REG; + Val = MmioRead32 (LaneEqControlAddr); + Val = DSP_16G_RXTX_PRESET0_SET (Val, Preset); + Val = DSP_16G_RXTX_PRESET1_SET (Val, Preset); + MmioWrite32 (LaneEqControlAddr, Val); + } else { + // Each register holds the Preset for 4 lanes + for (Idx = 0; Idx < (LinkWidth / 4); Idx++) { + LaneEqControlAddr = Pl16gCapabilityBase + PL16G_CAP_OFF_20H_REG + Idx * sizeof (UINT32); + Val = MmioRead32 (LaneEqControlAddr); + Val = DSP_16G_RXTX_PRESET0_SET (Val, Preset); + Val = DSP_16G_RXTX_PRESET1_SET (Val, Preset); + Val = DSP_16G_RXTX_PRESET2_SET (Val, Preset); + Val = DSP_16G_RXTX_PRESET3_SET (Val, Preset); + MmioWrite32 (LaneEqControlAddr, Val); + } + } +} + +VOID +ProgramHostBridgeInfo ( + AC01_ROOT_COMPLEX *RootComplex + ) +{ + EFI_STATUS Status; + PHYSICAL_ADDRESS TargetAddress; + UINT32 Val; + + // Program Root Complex Bifurcation + if (RootComplex->Active) { + if (RootComplex->Type == RootComplexTypeA) { + TargetAddress = RootComplex->HostBridgeBase + AC01_HOST_BRIDGE_RCA_DEV_MAP_REG; + Status = MailboxMsgRegisterRead (RootComplex->Socket, TargetAddress, &Val); + if (!RETURN_ERROR (Status)) { + Val = RCA_DEV_MAP_SET (Val, RootComplex->DevMapLow); + MailboxMsgRegisterWrite (RootComplex->Socket, TargetAddress, Val); + } + } else { + TargetAddress = RootComplex->HostBridgeBase + AC01_HOST_BRIDGE_RCB_DEV_MAP_REG; + Status = MailboxMsgRegisterRead (RootComplex->Socket, TargetAddress, &Val); + if (!RETURN_ERROR (Status)) { + Val = RCB_DEV_MAP_LOW_SET (Val, RootComplex->DevMapLow); + Val = RCB_DEV_MAP_HIGH_SET (Val, RootComplex->DevMapHigh); + MailboxMsgRegisterWrite (RootComplex->Socket, TargetAddress, Val); + } + } + } + + // Program Vendor ID and Device ID + TargetAddress = RootComplex->HostBridgeBase + AC01_HOST_BRIDGE_VENDOR_DEVICE_ID_REG; + Status = MailboxMsgRegisterRead (RootComplex->Socket, TargetAddress, &Val); + if (!RETURN_ERROR (Status)) { + Val = VENDOR_ID_SET (Val, AMPERE_PCIE_VENDOR_ID); + if (RootComplexTypeA == RootComplex->Type) { + Val = DEVICE_ID_SET (Val, AC01_HOST_BRIDGE_DEVICE_ID_RCA); + } else { + Val = DEVICE_ID_SET (Val, AC01_HOST_BRIDGE_DEVICE_ID_RCB); + } + MailboxMsgRegisterWrite (RootComplex->Socket, TargetAddress, Val); + } +} + +VOID +ProgramRootPortInfo ( + AC01_ROOT_COMPLEX *RootComplex, + UINT32 PcieIndex + ) +{ + PHYSICAL_ADDRESS CfgBase; + PHYSICAL_ADDRESS TargetAddress; + UINT32 Val; + + CfgBase = RootComplex->MmcfgBase + (RootComplex->Pcie[PcieIndex].DevNum << DEV_SHIFT); + + // Program Class Code + TargetAddress = CfgBase + TYPE1_CLASS_CODE_REV_ID_REG; + Val = MmioRead32 (TargetAddress); + Val = REVISION_ID_SET (Val, DEFAULT_REVISION_ID); + Val = SUB_CLASS_CODE_SET (Val, DEFAULT_SUB_CLASS_CODE); + Val = BASE_CLASS_CODE_SET (Val, DEFAULT_BASE_CLASS_CODE); + MmioWrite32 (TargetAddress, Val); + + // Program Vendor ID and Device ID + TargetAddress = CfgBase + TYPE1_DEV_ID_VEND_ID_REG; + Val = MmioRead32 (TargetAddress); + Val = VENDOR_ID_SET (Val, AMPERE_PCIE_VENDOR_ID); + if (RootComplexTypeA == RootComplex->Type) { + Val = DEVICE_ID_SET (Val, AC01_PCIE_BRIDGE_DEVICE_ID_RCA + PcieIndex); + } else { + Val = DEVICE_ID_SET (Val, AC01_PCIE_BRIDGE_DEVICE_ID_RCB + PcieIndex); + } + MmioWrite32 (TargetAddress, Val); +} + +VOID +ProgramLinkCapabilities ( + IN AC01_ROOT_COMPLEX *RootComplex, + IN UINT8 PcieIndex + ) +{ + PHYSICAL_ADDRESS CfgBase; + PHYSICAL_ADDRESS TargetAddress; + UINT32 Val; + UINT8 MaxWidth; + UINT8 MaxGen; + + CfgBase = RootComplex->MmcfgBase + (RootComplex->Pcie[PcieIndex].DevNum << DEV_SHIFT); + + MaxWidth = RootComplex->Pcie[PcieIndex].MaxWidth; + MaxGen = RootComplex->Pcie[PcieIndex].MaxGen; + + TargetAddress = CfgBase + PORT_LINK_CTRL_OFF; + Val = MmioRead32 (TargetAddress); + switch (MaxWidth) { + case LINK_WIDTH_X2: + Val = LINK_CAPABLE_SET (Val, LINK_CAPABLE_X2); + break; + + case LINK_WIDTH_X4: + Val = LINK_CAPABLE_SET (Val, LINK_CAPABLE_X4); + break; + + case LINK_WIDTH_X8: + Val = LINK_CAPABLE_SET (Val, LINK_CAPABLE_X8); + break; + + case LINK_WIDTH_X16: + default: + Val = LINK_CAPABLE_SET (Val, LINK_CAPABLE_X16); + break; + } + MmioWrite32 (TargetAddress, Val); + + TargetAddress = CfgBase + GEN2_CTRL_OFF; + Val = MmioRead32 (TargetAddress); + switch (MaxWidth) { + case LINK_WIDTH_X2: + Val = NUM_OF_LANES_SET (Val, NUM_OF_LANES_X2); + break; + + case LINK_WIDTH_X4: + Val = NUM_OF_LANES_SET (Val, NUM_OF_LANES_X4); + break; + + case LINK_WIDTH_X8: + Val = NUM_OF_LANES_SET (Val, NUM_OF_LANES_X8); + break; + + case LINK_WIDTH_X16: + default: + Val = NUM_OF_LANES_SET (Val, NUM_OF_LANES_X16); + break; + } + MmioWrite32 (TargetAddress, Val); + + TargetAddress = CfgBase + PCIE_CAPABILITY_BASE + LINK_CAPABILITIES_REG; + Val = MmioRead32 (TargetAddress); + switch (MaxWidth) { + case LINK_WIDTH_X2: + Val = CAP_MAX_LINK_WIDTH_SET (Val, CAP_MAX_LINK_WIDTH_X2); + break; + + case LINK_WIDTH_X4: + Val = CAP_MAX_LINK_WIDTH_SET (Val, CAP_MAX_LINK_WIDTH_X4); + break; + + case LINK_WIDTH_X8: + Val = CAP_MAX_LINK_WIDTH_SET (Val, CAP_MAX_LINK_WIDTH_X8); + break; + + case LINK_WIDTH_X16: + default: + Val = CAP_MAX_LINK_WIDTH_SET (Val, CAP_MAX_LINK_WIDTH_X16); + break; + } + + switch (MaxGen) { + case LINK_SPEED_GEN1: + Val = CAP_MAX_LINK_SPEED_SET (Val, MAX_LINK_SPEED_25); + break; + + case LINK_SPEED_GEN2: + Val = CAP_MAX_LINK_SPEED_SET (Val, MAX_LINK_SPEED_50); + break; + + case LINK_SPEED_GEN3: + Val = CAP_MAX_LINK_SPEED_SET (Val, MAX_LINK_SPEED_80); + break; + + default: + Val = CAP_MAX_LINK_SPEED_SET (Val, MAX_LINK_SPEED_160); + break; + } + // Enable ASPM Capability + Val = CAP_ACTIVE_STATE_LINK_PM_SUPPORT_SET (Val, L0S_L1_SUPPORTED); + MmioWrite32 (TargetAddress, Val); + + TargetAddress = CfgBase + PCIE_CAPABILITY_BASE + LINK_CONTROL2_LINK_STATUS2_REG; + Val = MmioRead32 (TargetAddress); + switch (MaxGen) { + case LINK_SPEED_GEN1: + Val = CAP_TARGET_LINK_SPEED_SET (Val, MAX_LINK_SPEED_25); + break; + + case LINK_SPEED_GEN2: + Val = CAP_TARGET_LINK_SPEED_SET (Val, MAX_LINK_SPEED_50); + break; + + case LINK_SPEED_GEN3: + Val = CAP_TARGET_LINK_SPEED_SET (Val, MAX_LINK_SPEED_80); + break; + + default: + Val = CAP_TARGET_LINK_SPEED_SET (Val, MAX_LINK_SPEED_160); + break; + } + MmioWrite32 (TargetAddress, Val); +} + +VOID +DisableCompletionTimeOut ( + IN AC01_ROOT_COMPLEX *RootComplex, + IN UINT8 PcieIndex, + IN BOOLEAN IsMask + ) +{ + PHYSICAL_ADDRESS CfgBase; + PHYSICAL_ADDRESS TargetAddress; + UINT32 Val; + + CfgBase = RootComplex->MmcfgBase + (RootComplex->Pcie[PcieIndex].DevNum << DEV_SHIFT); + + TargetAddress = CfgBase + AER_CAPABILITY_BASE + UNCORR_ERR_MASK_OFF; + Val = MmioRead32 (TargetAddress); + Val = CMPLT_TIMEOUT_ERR_MASK_SET (Val, IsMask ? 1 : 0); + MmioWrite32 (TargetAddress, Val); +} + +BOOLEAN +EnableItsMemory ( + AC01_ROOT_COMPLEX *RootComplex, + UINT32 PcieIndex + ) +{ + PHYSICAL_ADDRESS CsrBase; + PHYSICAL_ADDRESS TargetAddress; + UINT32 TimeOut; + UINT32 Val; + + CsrBase = RootComplex->Pcie[PcieIndex].CsrBase; + + // Clear memory shutdown + TargetAddress = CsrBase + AC01_PCIE_CORE_RAM_SHUTDOWN_REG; + Val = MmioRead32 (TargetAddress); + Val = SD_SET (Val, 0); + MmioWrite32 (TargetAddress, Val); + + // Poll till ITS Memory is ready + TimeOut = MEMRDY_TIMEOUT; + do { + Val = MmioRead32 (CsrBase + AC01_PCIE_CORE_MEM_READY_REG); + if (Val & MEMORY_READY) { + return TRUE; + } + + TimeOut--; + MicroSecondDelay (1); + } while (TimeOut > 0); + + return FALSE; +} + +BOOLEAN +EnableAxiPipeClock ( + AC01_ROOT_COMPLEX *RootComplex, + UINT32 PcieIndex + ) +{ + PHYSICAL_ADDRESS CsrBase; + PHYSICAL_ADDRESS TargetAddress; + UINT32 TimeOut; + UINT32 Val; + + CsrBase = RootComplex->Pcie[PcieIndex].CsrBase; + + // Enable subsystem clock and release reset + TargetAddress = CsrBase + AC01_PCIE_CORE_CLOCK_REG; + Val = MmioRead32 (TargetAddress); + Val = AXIPIPE_SET (Val, 1); + MmioWrite32 (TargetAddress, Val); + + TargetAddress = CsrBase + AC01_PCIE_CORE_RESET_REG; + Val = MmioRead32 (TargetAddress); + Val = DWC_PCIE_SET (Val, 0); + MmioWrite32 (TargetAddress, Val); + + // + // Controller does not provide any indicator for reset released. + // Must wait at least 1us as per EAS. + // + MicroSecondDelay (1); + + // Poll till PIPE clock is stable + TimeOut = PIPE_CLOCK_TIMEOUT; + do { + Val = MmioRead32 (CsrBase + AC01_PCIE_CORE_LINK_STAT_REG); + if (!(Val & PHY_STATUS_MASK)) { + return TRUE; + } + + TimeOut--; + MicroSecondDelay (1); + } while (TimeOut > 0); + + return FALSE; +} + +VOID +SetLinkTimeout ( + AC01_ROOT_COMPLEX *RootComplex, + UINT32 PcieIndex, + UINTN Timeout + ) +{ + PHYSICAL_ADDRESS TargetAddress; + UINT32 Val; + + TargetAddress = RootComplex->MmcfgBase + (RootComplex->Pcie[PcieIndex].DevNum << DEV_SHIFT) + + AMBA_LINK_TIMEOUT_OFF; + + Val = MmioRead32 (TargetAddress); + Val = LINK_TIMEOUT_PERIOD_DEFAULT_SET (Val, Timeout); + MmioWrite32 (TargetAddress, Val); +} + +VOID +StartLinkTraining ( + AC01_ROOT_COMPLEX *RootComplex, + UINT32 PcieIndex, + BOOLEAN StartLink + ) +{ + PHYSICAL_ADDRESS TargetAddress; + UINT32 Val; + + TargetAddress = RootComplex->Pcie[PcieIndex].CsrBase + AC01_PCIE_CORE_LINK_CTRL_REG; + + Val = MmioRead32 (TargetAddress); + Val = LTSSMENB_SET (Val, StartLink ? START_LINK_TRAINING : HOLD_LINK_TRAINING); + MmioWrite32 (TargetAddress, Val); +} + +VOID +EnableDbiAccess ( + AC01_ROOT_COMPLEX *RootComplex, + UINT32 PcieIndex, + BOOLEAN EnableDbi + ) +{ + PHYSICAL_ADDRESS TargetAddress; + UINT32 Val; + + TargetAddress = RootComplex->MmcfgBase + (RootComplex->Pcie[PcieIndex].DevNum << DEV_SHIFT) + + MISC_CONTROL_1_OFF; + + Val = MmioRead32 (TargetAddress); + Val = DBI_RO_WR_EN_SET (Val, EnableDbi ? ENABLE_WR : DISABLE_WR); + MmioWrite32 (TargetAddress, Val); +} + +/** + Setup and initialize the AC01 PCIe Root Complex and underneath PCIe controllers + + @param RootComplex Pointer to Root Complex structure + @param ReInit Re-init status + @param ReInitPcieIndex PCIe controller index + + @retval RETURN_SUCCESS The Root Complex has been initialized successfully. + @retval RETURN_DEVICE_ERROR PHY, Memory or PIPE is not ready. +**/ +RETURN_STATUS +Ac01PcieCoreSetupRC ( + IN AC01_ROOT_COMPLEX *RootComplex, + IN BOOLEAN ReInit, + IN UINT8 ReInitPcieIndex + ) +{ + PHYSICAL_ADDRESS CfgBase; + PHYSICAL_ADDRESS CsrBase; + PHYSICAL_ADDRESS TargetAddress; + RETURN_STATUS Status; + UINT32 Val; + UINT8 PcieIndex; + + DEBUG ((DEBUG_INFO, "Initializing Socket%d RootComplex%d\n", RootComplex->Socket, RootComplex->ID)); + + ProgramHostBridgeInfo (RootComplex); + + if (!ReInit) { + Status = PciePhyInit (RootComplex->SerdesBase); + if (RETURN_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: Failed to initialize the PCIe PHY\n", __FUNCTION__)); + return RETURN_DEVICE_ERROR; + } + } + + // Setup each controller + for (PcieIndex = 0; PcieIndex < RootComplex->MaxPcieController; PcieIndex++) { + + if (ReInit) { + PcieIndex = ReInitPcieIndex; + } + + if (!RootComplex->Pcie[PcieIndex].Active) { + continue; + } + + DEBUG ((DEBUG_INFO, "Initializing Controller %d\n", PcieIndex)); + + CsrBase = RootComplex->Pcie[PcieIndex].CsrBase; + CfgBase = RootComplex->MmcfgBase + (RootComplex->Pcie[PcieIndex].DevNum << DEV_SHIFT); + + // Put Controller into reset if not in reset already + TargetAddress = CsrBase + AC01_PCIE_CORE_RESET_REG; + Val = MmioRead32 (TargetAddress); + if (!(Val & RESET_MASK)) { + Val = DWC_PCIE_SET (Val, ASSERT_RESET); + MmioWrite32 (TargetAddress, Val); + + // Delay 50ms to ensure controller finish its reset + MicroSecondDelay (50000); + } + + if (!EnableItsMemory (RootComplex, PcieIndex)) { + DEBUG ((DEBUG_ERROR, "- Pcie[%d] - ITS Memory is not ready\n", PcieIndex)); + return RETURN_DEVICE_ERROR; + } + + // Hold link training + StartLinkTraining (RootComplex, PcieIndex, FALSE); + + if (!EnableAxiPipeClock (RootComplex, PcieIndex)) { + DEBUG ((DEBUG_ERROR, "- Pcie[%d] - PIPE clock is not stable\n", PcieIndex)); + return RETURN_DEVICE_ERROR; + } + + // Start PERST pulse + BoardPcieAssertPerst (RootComplex, PcieIndex, TRUE); + + // Allow programming to config space + EnableDbiAccess (RootComplex, PcieIndex, TRUE); + + // Program the power limit + TargetAddress = CfgBase + PCIE_CAPABILITY_BASE + SLOT_CAPABILITIES_REG; + Val = MmioRead32 (TargetAddress); + Val = SLOT_CAP_SLOT_POWER_LIMIT_VALUE_SET (Val, SLOT_POWER_LIMIT_75W); + MmioWrite32 (TargetAddress, Val); + + // Program DTI for ATS support + TargetAddress = CfgBase + DTIM_CTRL0_OFF; + Val = MmioRead32 (TargetAddress); + Val = DTIM_CTRL0_ROOT_PORT_ID_SET (Val, 0); + MmioWrite32 (TargetAddress, Val); + + // + // Program number of lanes used + // - Reprogram LINK_CAPABLE of PORT_LINK_CTRL_OFF + // - Reprogram NUM_OF_LANES of GEN2_CTRL_OFF + // - Reprogram CAP_MAX_LINK_WIDTH of LINK_CAPABILITIES_REG + // + ProgramLinkCapabilities (RootComplex, PcieIndex); + + // Set Zero byte request handling + TargetAddress = CfgBase + FILTER_MASK_2_OFF; + Val = MmioRead32 (TargetAddress); + Val = CX_FLT_MASK_VENMSG0_DROP_SET (Val, 0); + Val = CX_FLT_MASK_VENMSG1_DROP_SET (Val, 0); + Val = CX_FLT_MASK_DABORT_4UCPL_SET (Val, 0); + MmioWrite32 (TargetAddress, Val); + + TargetAddress = CfgBase + AMBA_ORDERING_CTRL_OFF; + Val = MmioRead32 (TargetAddress); + Val = AX_MSTR_ZEROLREAD_FW_SET (Val, 0); + MmioWrite32 (TargetAddress, Val); + + // + // Set Completion with CRS handling for CFG Request + // Set Completion with CA/UR handling non-CFG Request + // + TargetAddress = CfgBase + AMBA_ERROR_RESPONSE_DEFAULT_OFF; + Val = MmioRead32 (TargetAddress); + // 0x2: OKAY with FFFF_0001 and FFFF_FFFF + Val = AMBA_ERROR_RESPONSE_CRS_SET (Val, 0x2); + MmioWrite32 (TargetAddress, Val); + + // Set Legacy PCIE interrupt map to INTA + TargetAddress = CfgBase + BRIDGE_CTRL_INT_PIN_INT_LINE_REG; + Val = MmioRead32 (TargetAddress); + Val = INT_PIN_SET (Val, IRQ_INT_A); + MmioWrite32 (TargetAddress, Val); + + TargetAddress = CsrBase + AC01_PCIE_CORE_IRQ_SEL_REG; + Val = MmioRead32 (TargetAddress); + Val = INTPIN_SET (Val, IRQ_INT_A); + MmioWrite32 (TargetAddress, Val); + + if (RootComplex->Pcie[PcieIndex].MaxGen >= LINK_SPEED_GEN2) { + ConfigureEqualization (RootComplex, PcieIndex); + if (RootComplex->Pcie[PcieIndex].MaxGen >= LINK_SPEED_GEN3) { + ConfigurePresetGen3 (RootComplex, PcieIndex); + if (RootComplex->Pcie[PcieIndex].MaxGen >= LINK_SPEED_GEN4) { + ConfigurePresetGen4 (RootComplex, PcieIndex); + } + } + } + + // Link timeout after 1ms + SetLinkTimeout (RootComplex, PcieIndex, 1); + + DisableCompletionTimeOut (RootComplex, PcieIndex, TRUE); + + ProgramRootPortInfo (RootComplex, PcieIndex); + + // Enable common clock for downstream + TargetAddress = CfgBase + PCIE_CAPABILITY_BASE + LINK_CONTROL_LINK_STATUS_REG; + Val = MmioRead32 (TargetAddress); + Val = CAP_SLOT_CLK_CONFIG_SET (Val, 1); + Val = CAP_COMMON_CLK_SET (Val, 1); + MmioWrite32 (TargetAddress, Val); + + // Match aux_clk to system + TargetAddress = CfgBase + AUX_CLK_FREQ_OFF; + Val = MmioRead32 (TargetAddress); + Val = AUX_CLK_FREQ_SET (Val, AUX_CLK_500MHZ); + MmioWrite32 (TargetAddress, Val); + + // Assert PERST low to reset endpoint + BoardPcieAssertPerst (RootComplex, PcieIndex, FALSE); + + // Start link training + StartLinkTraining (RootComplex, PcieIndex, TRUE); + + // Complete the PERST pulse + BoardPcieAssertPerst (RootComplex, PcieIndex, TRUE); + + // Lock programming of config space + EnableDbiAccess (RootComplex, PcieIndex, FALSE); + + if (ReInit) { + return RETURN_SUCCESS; + } + } + + return RETURN_SUCCESS; +} + +BOOLEAN +PcieLinkUpCheck ( + IN AC01_PCIE_CONTROLLER *Pcie + ) +{ + PHYSICAL_ADDRESS CsrBase; + UINT32 BlockEvent; + UINT32 LinkStat; + + CsrBase = Pcie->CsrBase; + + // Check if card present + // smlh_ltssm_state[13:8] = 0 + // phy_status[2] = 0 + // smlh_link_up[1] = 0 + // rdlh_link_up[0] = 0 + LinkStat = MmioRead32 (CsrBase + AC01_PCIE_CORE_LINK_STAT_REG); + LinkStat = LinkStat & (SMLH_LTSSM_STATE_MASK | PHY_STATUS_MASK_BIT | + SMLH_LINK_UP_MASK_BIT | RDLH_LINK_UP_MASK_BIT); + if (LinkStat == 0) { + return FALSE; + } + + BlockEvent = MmioRead32 (CsrBase + AC01_PCIE_CORE_BLOCK_EVENT_STAT_REG); + LinkStat = MmioRead32 (CsrBase + AC01_PCIE_CORE_LINK_STAT_REG); + + if (((BlockEvent & LINKUP_MASK) != 0) + && (SMLH_LTSSM_STATE_GET(LinkStat) == LTSSM_STATE_L0)) { + return TRUE; + } + + return FALSE; +} + +/** + Callback function when the Host Bridge enumeration end. + + @param RootComplex Pointer to the Root Complex structure +**/ +VOID +Ac01PcieCoreEndEnumeration ( + IN AC01_ROOT_COMPLEX *RootComplex + ) +{ + + PHYSICAL_ADDRESS TargetAddress; + UINT32 PcieIndex; + UINT32 Val; + + if (RootComplex == NULL || !RootComplex->Active) { + return; + } + + // Clear uncorrectable error during enumuration phase. Mainly completion timeout. + for (PcieIndex = 0; PcieIndex < RootComplex->MaxPcieController; PcieIndex++) { + if (!RootComplex->Pcie[PcieIndex].Active) { + continue; + } + + if (!PcieLinkUpCheck(&RootComplex->Pcie[PcieIndex])) { + // If link down/disabled after enumeration, disable completed time out + DisableCompletionTimeOut (RootComplex, PcieIndex, TRUE); + } + + // Clear all errors + TargetAddress = RootComplex->MmcfgBase + ((PcieIndex + 1) << DEV_SHIFT) \ + + AER_CAPABILITY_BASE + UNCORR_ERR_STATUS_OFF; + Val = MmioRead32 (TargetAddress); + if (Val != 0) { + // Clear error by writting + MmioWrite32 (TargetAddress, Val); + } + } +} + +/** + Comparing current link status with the max capabilities of the link + + @param RootComplex Pointer to AC01_ROOT_COMPLEX structure + @param PcieIndex PCIe controller index + @param EpMaxWidth EP max link width + @param EpMaxGen EP max link speed + + @retval -1: Link status do not match with link max capabilities + 1: Link capabilites are invalid + 0: Link status are correct +**/ +INT32 +Ac01PcieCoreLinkCheck ( + IN AC01_ROOT_COMPLEX *RootComplex, + IN UINT8 PcieIndex, + IN UINT8 EpMaxWidth, + IN UINT8 EpMaxGen + ) +{ + PHYSICAL_ADDRESS CsrBase, CfgBase; + UINT32 Val, LinkStat; + UINT32 MaxWidth, MaxGen; + + CsrBase = RootComplex->Pcie[PcieIndex].CsrBase; + CfgBase = RootComplex->MmcfgBase + (RootComplex->Pcie[PcieIndex].DevNum << DEV_SHIFT); + + Val = MmioRead32 (CfgBase + PCIE_CAPABILITY_BASE + LINK_CAPABILITIES_REG); + if ((CAP_MAX_LINK_WIDTH_GET (Val) == 0) || + (CAP_MAX_LINK_SPEED_GET (Val) == 0)) { + DEBUG ((DEBUG_INFO, "\tPCIE%d.%d: Wrong RootComplex capabilities\n", RootComplex->ID, PcieIndex)); + return LINK_CHECK_WRONG_PARAMETER; + } + + if ((EpMaxWidth == 0) || (EpMaxGen == 0)) { + DEBUG ((DEBUG_INFO, "\tPCIE%d.%d: Wrong EP capabilities\n", RootComplex->ID, PcieIndex)); + return LINK_CHECK_FAILED; + } + + // Compare RootComplex and EP capabilities + if (CAP_MAX_LINK_WIDTH_GET (Val) > EpMaxWidth) { + MaxWidth = EpMaxWidth; + } else { + MaxWidth = CAP_MAX_LINK_WIDTH_GET (Val); + } + + // Compare RootComplex and EP capabilities + if (CAP_MAX_LINK_SPEED_GET (Val) > EpMaxGen) { + MaxGen = EpMaxGen; + } else { + MaxGen = CAP_MAX_LINK_SPEED_GET (Val); + } + + LinkStat = MmioRead32 (CsrBase + AC01_PCIE_CORE_LINK_STAT_REG); + Val = MmioRead32 (CfgBase + PCIE_CAPABILITY_BASE + LINK_CONTROL_LINK_STATUS_REG); + DEBUG (( + DEBUG_INFO, + "PCIE%d.%d: Link MaxWidth %d MaxGen %d, AC01_PCIE_CORE_LINK_STAT_REG 0x%x", + RootComplex->ID, + PcieIndex, + MaxWidth, + MaxGen, + LinkStat + )); + + // Checking all conditions of the link + // If one of them is not sastified, return link up fail + if ((CAP_NEGO_LINK_WIDTH_GET (Val) != MaxWidth) || + (CAP_LINK_SPEED_GET (Val) != MaxGen) || + (RDLH_SMLH_LINKUP_STATUS_GET (LinkStat) != (SMLH_LINK_UP_MASK_BIT | RDLH_LINK_UP_MASK_BIT))) + { + DEBUG ((DEBUG_INFO, "\tLinkCheck FAILED\n")); + return LINK_CHECK_FAILED; + } + + DEBUG ((DEBUG_INFO, "\tLinkCheck SUCCESS\n")); + return LINK_CHECK_SUCCESS; +} + +INT32 +PFACounterRead ( + IN AC01_ROOT_COMPLEX *RootComplex, + IN UINT8 PcieIndex, + IN UINT64 RasDesCapabilityBase + ) +{ + INT32 Ret = LINK_CHECK_SUCCESS; + UINT32 Val; + UINT8 ErrCode, ErrGrpNum; + + UINT32 ErrCtrlCfg[] = { + 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, 0x008, 0x009, 0x00A, // Per Lane + 0x105, 0x106, 0x107, 0x108, 0x109, 0x10A, + 0x200, 0x201, 0x202, 0x203, 0x204, 0x205, 0x206, 0x207, + 0x300, 0x301, 0x302, 0x303, 0x304, 0x305, + 0x400, 0x401, // Per Lane + 0x500, 0x501, 0x502, 0x503, 0x504, 0x505, 0x506, 0x507, 0x508, 0x509, 0x50A, 0x50B, 0x50C, 0x50D + }; + + for (ErrCode = 0; ErrCode < ARRAY_SIZE (ErrCtrlCfg); ErrCode++) { + ErrGrpNum = GET_HIGH_8_BITS (ErrCtrlCfg[ErrCode]); + // Skipping per lane group + // Checking common lane group because AER error are included in common group only + if ((ErrGrpNum != 0) && (ErrGrpNum != 4)) { + Val = MmioRead32 (RasDesCapabilityBase + EVENT_COUNTER_CONTROL_REG); + if (RootComplex->Type == RootComplexTypeA) { + // RootComplexTypeA - 4 PCIe controller per port, 1 controller in charge of 4 lanes + Val = ECCR_LANE_SEL_SET (Val, PcieIndex * 4); + } else { + // RootComplexTypeB - 8 PCIe controller per port, 1 controller in charge of 2 lanes + Val = ECCR_LANE_SEL_SET (Val, PcieIndex * 2); + } + Val = ECCR_GROUP_EVENT_SEL_SET (Val, ErrCtrlCfg[ErrCode]); + MmioWrite32 (RasDesCapabilityBase + EVENT_COUNTER_CONTROL_REG, Val); + + // After setting Counter Control reg + // This delay just to make sure Counter Data reg is update with new value + MicroSecondDelay (1); + Val = MmioRead32 (RasDesCapabilityBase + EVENT_COUNTER_DATA_REG); + if (Val != 0) { + Ret = LINK_CHECK_FAILED; + DEBUG (( + DEBUG_ERROR, + "\tSocket%d RootComplex%d RP%d \t%s: %d \tGROUP:%d-EVENT:%d\n", + RootComplex->Socket, + RootComplex->ID, + PcieIndex, + Val, + ErrGrpNum, + GET_LOW_8_BITS (ErrCtrlCfg[ErrCode]) + )); + } + } + } + + return Ret; +} + +/** + Handle Predictive Failure Analysis command + + @param RootComplex Pointer to Root Complex structure + @param PcieIndex PCIe controller index + @param PFAMode The PFA mode + + @retval The link status +**/ +INT32 +Ac01PFACommand ( + IN AC01_ROOT_COMPLEX *RootComplex, + IN UINT8 PcieIndex, + IN UINT8 PFAMode + ) +{ + PHYSICAL_ADDRESS RasDesCapabilityBase; + PHYSICAL_ADDRESS TargetAddress; + INT32 Ret = LINK_CHECK_SUCCESS; + UINT32 Val; + + // Allow programming to config space + EnableDbiAccess (RootComplex, PcieIndex, TRUE); + + // Get the RAS D.E.S. capability base address + RasDesCapabilityBase = GetCapabilityBase (RootComplex, PcieIndex, TRUE, RAS_DES_CAPABILITY_ID); + if (RasDesCapabilityBase == 0) { + DEBUG ((DEBUG_INFO, "PCIE%d.%d: Cannot get RAS DES capability address\n", RootComplex->ID, PcieIndex)); + return LINK_CHECK_WRONG_PARAMETER; + } + + TargetAddress = RasDesCapabilityBase + EVENT_COUNTER_CONTROL_REG; + + switch (PFAMode) { + case PFA_MODE_ENABLE: + Val = MmioRead32 (TargetAddress); + Val = ECCR_EVENT_COUNTER_ENABLE_SET (Val, EVENT_COUNTER_ENABLE_ALL_ON); + Val = ECCR_EVENT_COUNTER_CLEAR_SET (Val, EVENT_COUNTER_CLEAR_NO_CHANGE); + MmioWrite32 (TargetAddress, Val); + break; + + case PFA_MODE_CLEAR: + Val = MmioRead32 (TargetAddress); + Val = ECCR_EVENT_COUNTER_ENABLE_SET (Val, EVENT_COUNTER_ENABLE_NO_CHANGE); + Val = ECCR_EVENT_COUNTER_CLEAR_SET (Val, EVENT_COUNTER_CLEAR_ALL_CLEAR); + MmioWrite32 (TargetAddress, Val); + break; + + case PFA_MODE_READ: + Ret = PFACounterRead (RootComplex, PcieIndex, RasDesCapabilityBase); + break; + + default: + DEBUG ((DEBUG_ERROR, "%a: Invalid PFA mode\n")); + } + + // Disable programming to config space + EnableDbiAccess (RootComplex, PcieIndex, FALSE); + + return Ret; +} + +UINT32 +EndpointCfgReady ( + IN AC01_ROOT_COMPLEX *RootComplex, + IN UINT8 PcieIndex + ) +{ + PHYSICAL_ADDRESS CfgBase; + UINT32 TimeOut; + UINT32 Val; + + CfgBase = RootComplex->MmcfgBase + (RootComplex->Pcie[PcieIndex].DevNum << BUS_SHIFT); + + // Loop read CfgBase value until got valid value or + // reach to timeout EP_LINKUP_TIMEOUT (or more depend on card) + TimeOut = EP_LINKUP_TIMEOUT; + do { + Val = MmioRead32 (CfgBase); + if (Val != 0xFFFF0001 && Val != 0xFFFFFFFF) { + return TRUE; + } + + TimeOut -= LINK_WAIT_INTERVAL_US; + MicroSecondDelay (LINK_WAIT_INTERVAL_US); + } while (TimeOut > 0); + + return FALSE; +} + +/** + Get link capabilities link width and speed of endpoint + + @param RootComplex[in] Pointer to AC01_ROOT_COMPLEX structure + @param PcieIndex[in] PCIe controller index + @param EpMaxWidth[out] EP max link width + @param EpMaxGen[out] EP max link speed +**/ +VOID +Ac01PcieCoreGetEndpointInfo ( + IN AC01_ROOT_COMPLEX *RootComplex, + IN UINT8 PcieIndex, + OUT UINT8 *EpMaxWidth, + OUT UINT8 *EpMaxGen + ) +{ + PHYSICAL_ADDRESS CfgBase; + PHYSICAL_ADDRESS PcieCapBase; + PHYSICAL_ADDRESS SecLatTimerAddr; + PHYSICAL_ADDRESS TargetAddress; + UINT32 RestoreVal; + UINT32 Val; + + CfgBase = RootComplex->MmcfgBase + (RootComplex->Pcie[PcieIndex].DevNum << DEV_SHIFT); + SecLatTimerAddr = CfgBase + SEC_LAT_TIMER_SUB_BUS_SEC_BUS_PRI_BUS_REG; + + *EpMaxWidth = 0; + *EpMaxGen = 0; + + // Allow programming to config space + EnableDbiAccess (RootComplex, PcieIndex, TRUE); + + Val = MmioRead32 (SecLatTimerAddr); + RestoreVal = Val; + Val = SUB_BUS_SET (Val, DEFAULT_SUB_BUS); + Val = SEC_BUS_SET (Val, RootComplex->Pcie[PcieIndex].DevNum); + Val = PRIM_BUS_SET (Val, DEFAULT_PRIM_BUS); + MmioWrite32 (SecLatTimerAddr, Val); + + if (EndpointCfgReady (RootComplex, PcieIndex)) { + PcieCapBase = GetCapabilityBase (RootComplex, PcieIndex, FALSE, PCIE_CAPABILITY_ID); + if (PcieCapBase == 0) { + DEBUG (( + DEBUG_ERROR, + "PCIE%d.%d Cannot get PCIe capability base address!\n", + RootComplex->ID, + PcieIndex + )); + } else { + Val = MmioRead32 (PcieCapBase + LINK_CAPABILITIES_REG); + *EpMaxWidth = CAP_MAX_LINK_WIDTH_GET (Val); + *EpMaxGen = CAP_MAX_LINK_SPEED_GET (Val); + DEBUG (( + DEBUG_INFO, + "PCIE%d.%d EP MaxWidth %d EP MaxGen %d \n", RootComplex->ID, + PcieIndex, + *EpMaxWidth, + *EpMaxGen + )); + + // From EP, enabling common clock for upstream + TargetAddress = PcieCapBase + LINK_CONTROL_LINK_STATUS_REG; + Val = MmioRead32 (TargetAddress); + Val = CAP_SLOT_CLK_CONFIG_SET (Val, 1); + Val = CAP_COMMON_CLK_SET (Val, 1); + MmioWrite32 (TargetAddress, Val); + } + } + + // Restore value in order to not affect enumeration process + MmioWrite32 (SecLatTimerAddr, RestoreVal); + + // Disable programming to config space + EnableDbiAccess (RootComplex, PcieIndex, FALSE); +} + +VOID +PollLinkUp ( + IN AC01_ROOT_COMPLEX *RootComplex, + IN UINT8 PcieIndex + ) +{ + UINT32 TimeOut; + + // Poll until link up + // This checking for linkup status and + // give LTSSM state the time to transit from DECTECT state to L0 state + // Total delay are 100ms, smaller number of delay cannot always make sure + // the state transition is completed + TimeOut = LTSSM_TRANSITION_TIMEOUT; + do { + if (PcieLinkUpCheck (&RootComplex->Pcie[PcieIndex])) { + DEBUG (( + DEBUG_INFO, + "\tPCIE%d.%d LinkStat is correct after soft reset, transition time: %d\n", + RootComplex->ID, + PcieIndex, + TimeOut + )); + RootComplex->Pcie[PcieIndex].LinkUp = TRUE; + break; + } + + MicroSecondDelay (100); + TimeOut -= 100; + } while (TimeOut > 0); + + if (TimeOut <= 0) { + DEBUG ((DEBUG_ERROR, "\tPCIE%d.%d LinkStat TIMEOUT after re-init\n", RootComplex->ID, PcieIndex)); + } else { + DEBUG ((DEBUG_INFO, "PCIE%d.%d Link re-initialization passed!\n", RootComplex->ID, PcieIndex)); + } +} + +/** + Check active PCIe controllers of RootComplex, retrain or soft reset if needed + + @param RootComplex[in] Pointer to AC01_ROOT_COMPLEX structure + @param PcieIndex[in] PCIe controller index + + @retval -1: Link recovery had failed + 1: Link width and speed are not correct + 0: Link recovery succeed +**/ +INT32 +Ac01PcieCoreQoSLinkCheckRecovery ( + IN AC01_ROOT_COMPLEX *RootComplex, + IN UINT8 PcieIndex + ) +{ + INT32 LinkStatusCheck, RasdesChecking; + INT32 NumberOfReset = MAX_REINIT; + UINT8 EpMaxWidth, EpMaxGen; + + // PCIe controller is not active or Link is not up + // Nothing to be done + if ((!RootComplex->Pcie[PcieIndex].Active) || (!RootComplex->Pcie[PcieIndex].LinkUp)) { + return LINK_CHECK_WRONG_PARAMETER; + } + + do { + if (RootComplex->Pcie[PcieIndex].LinkUp) { + // Enable all of RASDES register to detect any training error + Ac01PFACommand (RootComplex, PcieIndex, PFA_MODE_ENABLE); + + // Accessing Endpoint and checking current link capabilities + Ac01PcieCoreGetEndpointInfo (RootComplex, PcieIndex, &EpMaxWidth, &EpMaxGen); + LinkStatusCheck = Ac01PcieCoreLinkCheck (RootComplex, PcieIndex, EpMaxWidth, EpMaxGen); + + // Delay to allow the link to perform internal operation and generate + // any error status update. This allows detection of any error observed + // during initial link training. Possible evaluation time can be + // between 100ms to 200ms. + MicroSecondDelay (100000); + + // Check for error + RasdesChecking = Ac01PFACommand (RootComplex, PcieIndex, PFA_MODE_READ); + + // Clear error counter + Ac01PFACommand (RootComplex, PcieIndex, PFA_MODE_CLEAR); + + // If link check functions return passed, then breaking out + // else go to soft reset + if (LinkStatusCheck != LINK_CHECK_FAILED && + RasdesChecking != LINK_CHECK_FAILED && + PcieLinkUpCheck (&RootComplex->Pcie[PcieIndex])) + { + return LINK_CHECK_SUCCESS; + } + + RootComplex->Pcie[PcieIndex].LinkUp = FALSE; + } + + // Trigger controller soft reset + DEBUG ((DEBUG_INFO, "PCIE%d.%d Start link re-initialization..\n", RootComplex->ID, PcieIndex)); + Ac01PcieCoreSetupRC (RootComplex, TRUE, PcieIndex); + + PollLinkUp (RootComplex, PcieIndex); + + NumberOfReset--; + } while (NumberOfReset > 0); + + return LINK_CHECK_SUCCESS; +} + +VOID +Ac01PcieCoreUpdateLink ( + IN AC01_ROOT_COMPLEX *RootComplex, + OUT BOOLEAN *IsNextRoundNeeded, + OUT INT8 *FailedPciePtr, + OUT INT8 *FailedPcieCount + ) +{ + AC01_PCIE_CONTROLLER *Pcie; + PHYSICAL_ADDRESS CfgBase; + UINT8 PcieIndex; + UINT32 Index; + UINT32 Val; + + *IsNextRoundNeeded = FALSE; + *FailedPcieCount = 0; + for (Index = 0; Index < MaxPcieControllerOfRootComplexB; Index++) { + FailedPciePtr[Index] = -1; + } + + if (!RootComplex->Active) { + return; + } + + // Loop for all controllers + for (PcieIndex = 0; PcieIndex < RootComplex->MaxPcieController; PcieIndex++) { + Pcie = &RootComplex->Pcie[PcieIndex]; + CfgBase = RootComplex->MmcfgBase + (RootComplex->Pcie[PcieIndex].DevNum << DEV_SHIFT); + + if (Pcie->Active && !Pcie->LinkUp) { + if (PcieLinkUpCheck (Pcie)) { + Pcie->LinkUp = TRUE; + Val = MmioRead32 (CfgBase + PCIE_CAPABILITY_BASE + LINK_CONTROL_LINK_STATUS_REG); + + DEBUG (( + DEBUG_INFO, + "%a Socket%d RootComplex%d RP%d NEGO_LINK_WIDTH: 0x%x LINK_SPEED: 0x%x\n", + __FUNCTION__, + RootComplex->Socket, + RootComplex->ID, + PcieIndex, + CAP_NEGO_LINK_WIDTH_GET (Val), + CAP_LINK_SPEED_GET (Val) + )); + + // Doing link checking and recovery if needed + Ac01PcieCoreQoSLinkCheckRecovery (RootComplex, PcieIndex); + + // Link timeout after 32ms + SetLinkTimeout (RootComplex, PcieIndex, 32); + + // Un-mask Completion Timeout + DisableCompletionTimeOut (RootComplex, PcieIndex, FALSE); + + } else { + *IsNextRoundNeeded = FALSE; + FailedPciePtr[*FailedPcieCount] = PcieIndex; + *FailedPcieCount += 1; + } + } + } +} + +/** + Verify the link status and retry to initialize the Root Complex if there's any issue. + + @param RootComplexList Pointer to the Root Complex list +**/ +VOID +Ac01PcieCorePostSetupRC ( + IN AC01_ROOT_COMPLEX *RootComplexList + ) +{ + UINT8 RCIndex, Idx; + BOOLEAN IsNextRoundNeeded, NextRoundNeeded; + UINT64 PrevTick, CurrTick, ElapsedCycle; + UINT64 TimerTicks64; + UINT8 ReInit; + INT8 FailedPciePtr[MaxPcieControllerOfRootComplexB]; + INT8 FailedPcieCount; + + ReInit = 0; + +_link_polling: + NextRoundNeeded = FALSE; + // + // It is not guaranteed the timer service is ready prior to PCI Dxe. + // Calculate system ticks for link training. + // + TimerTicks64 = ArmGenericTimerGetTimerFreq (); /* 1 Second */ + PrevTick = ArmGenericTimerGetSystemCount (); + ElapsedCycle = 0; + + do { + CurrTick = ArmGenericTimerGetSystemCount (); + if (CurrTick < PrevTick) { + ElapsedCycle += MAX_UINT64 - PrevTick; + PrevTick = 0; + } + ElapsedCycle += (CurrTick - PrevTick); + PrevTick = CurrTick; + } while (ElapsedCycle < TimerTicks64); + + for (RCIndex = 0; RCIndex < AC01_PCIE_MAX_ROOT_COMPLEX; RCIndex++) { + Ac01PcieCoreUpdateLink (&RootComplexList[RCIndex], &IsNextRoundNeeded, FailedPciePtr, &FailedPcieCount); + if (IsNextRoundNeeded) { + NextRoundNeeded = TRUE; + } + } + + if (NextRoundNeeded && ReInit < MAX_REINIT) { + // + // Timer is up. Give another chance to re-program controller + // + ReInit++; + for (RCIndex = 0; RCIndex < AC01_PCIE_MAX_ROOT_COMPLEX; RCIndex++) { + Ac01PcieCoreUpdateLink (&RootComplexList[RCIndex], &IsNextRoundNeeded, FailedPciePtr, &FailedPcieCount); + if (IsNextRoundNeeded) { + for (Idx = 0; Idx < FailedPcieCount; Idx++) { + if (FailedPciePtr[Idx] == -1) { + continue; + } + + // + // Some controller still observes link-down. Re-init controller + // + Ac01PcieCoreSetupRC (&RootComplexList[RCIndex], TRUE, FailedPciePtr[Idx]); + } + } + } + + goto _link_polling; + } +} diff --git a/Silicon/Ampere/AmpereAltraPkg/Library/BoardPcieLibNull/BoardPcieLibNull.c b/Silicon/Ampere/AmpereAltraPkg/Library/BoardPcieLibNull/BoardPcieLibNull.c new file mode 100644 index 000000000000..0916adb77539 --- /dev/null +++ b/Silicon/Ampere/AmpereAltraPkg/Library/BoardPcieLibNull/BoardPcieLibNull.c @@ -0,0 +1,47 @@ +/** @file + + Copyright (c) 2021, Ampere Computing LLC. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Library/BoardPcieLib.h> + +/** + Assert PERST of input PCIe controller + + @param[in] RootComplex RootComplex instance. + @param[in] PcieIndex PCIe controller index of input Root Complex. + @param[in] Bifurcation Bifurcation mode of input Root Complex. + @param[in] IsPullToHigh Target status for the PERST. + + @retval RETURN_SUCCESS The operation is successful. + @retval Others An error occurred. +**/ +RETURN_STATUS +EFIAPI +BoardPcieAssertPerst ( + IN AC01_ROOT_COMPLEX *RootComplex, + IN UINT8 PcieIndex, + IN BOOLEAN IsPullToHigh + ) +{ + return RETURN_SUCCESS; +} + +/** + Override the segment number for a root complex with a board specific number. + + @param[in] RootComplex Root Complex instance with properties. + + @retval Segment number corresponding to the input root complex. + Default segment number is 0x0F. +**/ +UINT16 +BoardPcieGetSegmentNumber ( + IN AC01_ROOT_COMPLEX *RootComplex + ) +{ + return 0x0F; +} -- 2.17.1 -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#83860): https://edk2.groups.io/g/devel/message/83860 Mute This Topic: https://groups.io/mt/87142966/21656 Group Owner: devel+ow...@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [arch...@mail-archive.com] -=-=-=-=-=-=-=-=-=-=-=-