/** @file
PCH Pcie SMM Driver Entry
Copyright (c) 2021, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "PchInitSmm.h"
#include
#include
#include
#include
#include
#include
GLOBAL_REMOVE_IF_UNREFERENCED PCH_PCIE_DEVICE_OVERRIDE *mDevAspmOverride;
GLOBAL_REMOVE_IF_UNREFERENCED UINT32 mNumOfDevAspmOverride;
GLOBAL_REMOVE_IF_UNREFERENCED UINT8 mPchBusNumber;
//
// @note:
// These temp bus numbers cannot be used in runtime (hot-plug).
// These can be used only during boot.
//
GLOBAL_REMOVE_IF_UNREFERENCED UINT8 mTempRootPortBusNumMin;
GLOBAL_REMOVE_IF_UNREFERENCED UINT8 mTempRootPortBusNumMax;
GLOBAL_REMOVE_IF_UNREFERENCED PCH_PCIE_ROOT_PORT_CONFIG mPcieRootPortConfig[PCH_MAX_PCIE_ROOT_PORTS];
GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN mPchPciePmTrapExecuted = FALSE;
extern EFI_GUID gPchDeviceTableHobGuid;
/**
Program Common Clock and ASPM of Downstream Devices
@param[in] PortIndex Pcie Root Port Number
@param[in] RpDevice Pcie Root Pci Device Number
@param[in] RpFunction Pcie Root Pci Function Number
@retval EFI_SUCCESS Root port complete successfully
@retval EFI_UNSUPPORTED PMC has invalid vendor ID
**/
EFI_STATUS
PchPcieSmi (
IN UINT8 PortIndex,
IN UINT8 RpDevice,
IN UINT8 RpFunction
)
{
UINT8 SecBus;
UINT8 SubBus;
UINT64 RpBase;
UINT64 EpBase;
UINT8 EpPcieCapPtr;
UINT8 EpMaxSpeed;
BOOLEAN DownstreamDevicePresent;
UINT32 Timeout;
RpBase = PCI_SEGMENT_LIB_ADDRESS (
DEFAULT_PCI_SEGMENT_NUMBER_PCH,
mPchBusNumber,
(UINT32) RpDevice,
(UINT32) RpFunction,
0
);
if (PciSegmentRead16 (RpBase + PCI_VENDOR_ID_OFFSET) == 0xFFFF) {
return EFI_SUCCESS;
}
//
// Check presence detect state. Here the endpoint must be detected using PDS rather than
// the usual LinkActive check, because PDS changes immediately and LA takes a few milliseconds to stabilize
//
DownstreamDevicePresent = !!(PciSegmentRead16 (RpBase + R_PCH_PCIE_CFG_SLSTS) & B_PCIE_SLSTS_PDS);
if (DownstreamDevicePresent) {
///
/// Make sure the link is active before trying to talk to device behind it
/// Wait up to 100ms, according to PCIE spec chapter 6.7.3.3
///
Timeout = 100 * 1000;
while ((PciSegmentRead16 (RpBase + R_PCH_PCIE_CFG_LSTS) & B_PCIE_LSTS_LA) == 0 ) {
MicroSecondDelay (10);
Timeout-=10;
if (Timeout == 0) {
return EFI_NOT_FOUND;
}
}
SecBus = PciSegmentRead8 (RpBase + PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET);
SubBus = PciSegmentRead8 (RpBase + PCI_BRIDGE_SUBORDINATE_BUS_REGISTER_OFFSET);
ASSERT (SecBus != 0 && SubBus != 0);
RootportDownstreamConfiguration (
DEFAULT_PCI_SEGMENT_NUMBER_PCH,
DEFAULT_PCI_BUS_NUMBER_PCH,
RpDevice,
RpFunction,
mTempRootPortBusNumMin,
mTempRootPortBusNumMax,
EnumPchPcie
);
RootportDownstreamPmConfiguration (
DEFAULT_PCI_SEGMENT_NUMBER_PCH,
DEFAULT_PCI_BUS_NUMBER_PCH,
RpDevice,
RpFunction,
mTempRootPortBusNumMin,
mTempRootPortBusNumMax,
&mPcieRootPortConfig[PortIndex].PcieRpCommonConfig,
mNumOfDevAspmOverride,
mDevAspmOverride
);
//
// Perform Equalization
//
EpBase = PCI_SEGMENT_LIB_ADDRESS (DEFAULT_PCI_SEGMENT_NUMBER_PCH, SecBus, 0, 0, 0);
EpPcieCapPtr = PcieFindCapId (DEFAULT_PCI_SEGMENT_NUMBER_PCH, SecBus, 0, 0, EFI_PCI_CAPABILITY_ID_PCIEXP);
EpMaxSpeed = PciSegmentRead8 (EpBase + EpPcieCapPtr + R_PCIE_LCAP_OFFSET) & B_PCIE_LCAP_MLS;
if (EpMaxSpeed >= 3) {
PciSegmentOr32 (RpBase + R_PCH_PCIE_CFG_EX_LCTL3, B_PCIE_EX_LCTL3_PE);
PciSegmentOr32 (RpBase + R_PCH_PCIE_CFG_LCTL, B_PCIE_LCTL_RL);
}
}
return EFI_SUCCESS;
}
/**
PCIE Hotplug SMI call back function for each Root port
@param[in] DispatchHandle Handle of this dispatch function
@param[in] RpContext Rootport context, which contains RootPort Index,
and RootPort PCI BDF.
**/
VOID
EFIAPI
PchPcieSmiRpHandlerFunction (
IN EFI_HANDLE DispatchHandle,
IN PCH_PCIE_SMI_RP_CONTEXT *RpContext
)
{
PchPcieSmi (RpContext->RpIndex, RpContext->DevNum, RpContext->FuncNum);
}
/**
PCIE Link Active State Change Hotplug SMI call back function for all Root ports
@param[in] DispatchHandle Handle of this dispatch function
@param[in] RpContext Rootport context, which contains RootPort Index,
and RootPort PCI BDF.
**/
VOID
EFIAPI
PchPcieLinkActiveStateChange (
IN EFI_HANDLE DispatchHandle,
IN PCH_PCIE_SMI_RP_CONTEXT *RpContext
)
{
return;
}
/**
PCIE Link Equalization Request SMI call back function for all Root ports
@param[in] DispatchHandle Handle of this dispatch function
@param[in] RpContext Rootport context, which contains RootPort Index,
and RootPort PCI BDF.
**/
VOID
EFIAPI
PchPcieLinkEqHandlerFunction (
IN EFI_HANDLE DispatchHandle,
IN PCH_PCIE_SMI_RP_CONTEXT *RpContext
)
{
///
/// From PCI Express specification, the PCIe device can request for Link Equalization. When the
/// Link Equalization is requested by the device, an SMI will be generated by PCIe RP when
/// enabled and the SMI subroutine would invoke the Software Preset/Coefficient Search
/// software to re-equalize the link.
///
return;
}
/**
An IoTrap callback to config PCIE power management settings
**/
VOID
PchPciePmIoTrapSmiCallback (
VOID
)
{
UINT32 PortIndex;
UINT64 RpBase;
UINT8 MaxPciePortNum;
MaxPciePortNum = GetPchMaxPciePortNum ();
for (PortIndex = 0; PortIndex < MaxPciePortNum; PortIndex++) {
RpBase = PchPcieRpPciCfgBase (PortIndex);
if (PciSegmentRead16 (RpBase) != 0xFFFF) {
RootportDownstreamPmConfiguration (
DEFAULT_PCI_SEGMENT_NUMBER_PCH,
DEFAULT_PCI_BUS_NUMBER_PCH,
PchPcieRpDevNumber (PortIndex),
PchPcieRpFuncNumber (PortIndex),
mTempRootPortBusNumMin,
mTempRootPortBusNumMax,
&mPcieRootPortConfig[PortIndex].PcieRpCommonConfig,
mNumOfDevAspmOverride,
mDevAspmOverride
);
}
}
}
/**
An IoTrap callback to config PCIE power management settings
@param[in] DispatchHandle - The handle of this callback, obtained when registering
@param[in] DispatchContext - Pointer to the EFI_SMM_IO_TRAP_DISPATCH_CALLBACK_CONTEXT
**/
VOID
EFIAPI
PchPcieIoTrapSmiCallback (
IN EFI_HANDLE DispatchHandle,
IN EFI_SMM_IO_TRAP_CONTEXT *CallbackContext,
IN OUT VOID *CommBuffer,
IN OUT UINTN *CommBufferSize
)
{
if (CallbackContext->WriteData == PchPciePmTrap) {
if (mPchPciePmTrapExecuted == FALSE) {
PchPciePmIoTrapSmiCallback ();
mPchPciePmTrapExecuted = TRUE;
}
} else {
ASSERT_EFI_ERROR (EFI_INVALID_PARAMETER);
}
}
/**
This function clear the Io trap executed flag before enter S3
@param[in] Handle Handle of the callback
@param[in] Context The dispatch context
@retval EFI_SUCCESS PCH register saved
**/
EFI_STATUS
EFIAPI
PchPcieS3EntryCallBack (
IN EFI_HANDLE Handle,
IN CONST VOID *Context OPTIONAL,
IN OUT VOID *CommBuffer OPTIONAL,
IN OUT UINTN *CommBufferSize OPTIONAL
)
{
mPchPciePmTrapExecuted = FALSE;
return EFI_SUCCESS;
}
/**
Register PCIE Hotplug SMI dispatch function to handle Hotplug enabling
@param[in] ImageHandle The image handle of this module
@param[in] SystemTable The EFI System Table
@retval EFI_SUCCESS The function completes successfully
**/
EFI_STATUS
EFIAPI
InitializePchPcieSmm (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
UINT8 PortIndex;
UINT8 Data8;
UINT32 Data32Or;
UINT32 Data32And;
UINT64 RpBase;
EFI_HANDLE PcieHandle;
PCH_PCIE_SMI_DISPATCH_PROTOCOL *PchPcieSmiDispatchProtocol;
EFI_HANDLE PchIoTrapHandle;
EFI_SMM_IO_TRAP_REGISTER_CONTEXT PchIoTrapContext;
EFI_SMM_SX_REGISTER_CONTEXT SxDispatchContext;
PCH_PCIE_IOTRAP_PROTOCOL *PchPcieIoTrapProtocol;
EFI_HANDLE SxDispatchHandle;
UINT8 MaxPciePortNum;
PCH_POLICY_PROTOCOL *PchPolicy;
PCIE_RP_DXE_CONFIG *PchPcieRpDxeConfig;
UINTN PcieDeviceTableSize;
PCH_PCIE_DEVICE_OVERRIDE *DevAspmOverride;
UINTN Count;
DEBUG ((DEBUG_INFO, "InitializePchPcieSmm () Start\n"));
MaxPciePortNum = GetPchMaxPciePortNum ();
//
// Locate Pch Pcie Smi Dispatch Protocol
//
Status = gSmst->SmmLocateProtocol (&gPchPcieSmiDispatchProtocolGuid, NULL, (VOID**) &PchPcieSmiDispatchProtocol);
ASSERT_EFI_ERROR (Status);
mPchBusNumber = DEFAULT_PCI_BUS_NUMBER_PCH;
mTempRootPortBusNumMin = PcdGet8 (PcdSiliconInitTempPciBusMin);
mTempRootPortBusNumMax = PcdGet8 (PcdSiliconInitTempPciBusMax);
ASSERT (sizeof mPcieRootPortConfig == sizeof mPchConfigHob->PcieRp.RootPort);
CopyMem (
mPcieRootPortConfig,
&(mPchConfigHob->PcieRp.RootPort),
sizeof (mPcieRootPortConfig)
);
DevAspmOverride = NULL;
mDevAspmOverride = NULL;
mNumOfDevAspmOverride = 0;
Status = gBS->LocateProtocol (&gPchPolicyProtocolGuid, NULL, (VOID **) &PchPolicy);
ASSERT_EFI_ERROR (Status);
Status = GetConfigBlock ((VOID*) PchPolicy, &gPchPcieRpDxeConfigGuid, (VOID*) &PchPcieRpDxeConfig);
ASSERT_EFI_ERROR (Status);
DevAspmOverride = PchPcieRpDxeConfig->PcieDeviceOverrideTablePtr;
Count = 0;
if (DevAspmOverride != NULL) {
for (Count = 0; DevAspmOverride[Count].DeviceId != 0; Count++) {}
}
if (Count !=0) {
PcieDeviceTableSize = Count * sizeof (PCH_PCIE_DEVICE_OVERRIDE);
Status = gSmst->SmmAllocatePool (
EfiRuntimeServicesData,
PcieDeviceTableSize,
(VOID **) &mDevAspmOverride
);
ASSERT_EFI_ERROR (Status);
CopyMem (mDevAspmOverride, DevAspmOverride, PcieDeviceTableSize);
mNumOfDevAspmOverride = (UINT32) Count;
}
//
// Throught all PCIE root port function and register the SMI Handler for enabled ports.
//
for (PortIndex = 0; PortIndex < MaxPciePortNum; PortIndex++) {
RpBase = PchPcieRpPciCfgBase (PortIndex);
//
// Skip the root port function which is not enabled
//
if (PciSegmentRead32 (RpBase) == 0xFFFFFFFF) {
continue;
}
//
// Register SMI Handlers for Hot Plug and Link Active State Change
//
Data8 = PciSegmentRead8 (RpBase + R_PCH_PCIE_CFG_SLCAP);
if (Data8 & B_PCIE_SLCAP_HPC) {
PcieHandle = NULL;
Status = PchPcieSmiDispatchProtocol->HotPlugRegister (
PchPcieSmiDispatchProtocol,
PchPcieSmiRpHandlerFunction,
PortIndex,
&PcieHandle
);
ASSERT_EFI_ERROR (Status);
Status = PchPcieSmiDispatchProtocol->LinkActiveRegister (
PchPcieSmiDispatchProtocol,
PchPcieLinkActiveStateChange,
PortIndex,
&PcieHandle
);
ASSERT_EFI_ERROR (Status);
Data32Or = B_PCH_PCIE_CFG_MPC_HPME;
Data32And = (UINT32) ~B_PCH_PCIE_CFG_MPC_HPME;
PciSegmentOr32 (RpBase + R_PCH_PCIE_CFG_MPC, Data32Or);
S3BootScriptSaveMemReadWrite (
S3BootScriptWidthUint32,
PcdGet64 (PcdSiPciExpressBaseAddress) + RpBase + R_PCH_PCIE_CFG_MPC,
&Data32Or, /// Data to be ORed
&Data32And /// Data to be ANDed
);
}
//
// Register SMI Handler for Link Equalization Request from Gen 3 Devices.
//
Data8 = PciSegmentRead8 (RpBase + R_PCH_PCIE_CFG_LCAP);
if ((Data8 & B_PCIE_LCAP_MLS) == V_PCIE_LCAP_MLS_GEN3) {
Status = PchPcieSmiDispatchProtocol->LinkEqRegister (
PchPcieSmiDispatchProtocol,
PchPcieLinkEqHandlerFunction,
PortIndex,
&PcieHandle
);
ASSERT_EFI_ERROR (Status);
}
}
ASSERT_EFI_ERROR (Status);
PchIoTrapContext.Type = WriteTrap;
PchIoTrapContext.Length = 4;
PchIoTrapContext.Address = 0;
Status = mPchIoTrap->Register (
mPchIoTrap,
(EFI_SMM_HANDLER_ENTRY_POINT2) PchPcieIoTrapSmiCallback,
&PchIoTrapContext,
&PchIoTrapHandle
);
ASSERT_EFI_ERROR (Status);
//
// Install the PCH Pcie IoTrap protocol
//
(gBS->AllocatePool) (EfiBootServicesData, sizeof (PCH_PCIE_IOTRAP_PROTOCOL), (VOID **)&PchPcieIoTrapProtocol);
PchPcieIoTrapProtocol->PcieTrapAddress = PchIoTrapContext.Address;
Status = gBS->InstallMultipleProtocolInterfaces (
&ImageHandle,
&gPchPcieIoTrapProtocolGuid,
PchPcieIoTrapProtocol,
NULL
);
//
// Register the callback for S3 entry
//
SxDispatchContext.Type = SxS3;
SxDispatchContext.Phase = SxEntry;
Status = mSxDispatch->Register (
mSxDispatch,
PchPcieS3EntryCallBack,
&SxDispatchContext,
&SxDispatchHandle
);
ASSERT_EFI_ERROR (Status);
DEBUG ((DEBUG_INFO, "InitializePchPcieSmm, IoTrap @ %x () End\n", PchIoTrapContext.Address));
return EFI_SUCCESS;
}