/** @file
Copyright (c) 2018, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under
the terms and conditions of the BSD License that accompanies this distribution.
The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php.
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
//
// Module specific Includes
//
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "TbtSmiHandler.h"
#include
#include
#include
#include
#define P2P_BRIDGE (((PCI_CLASS_BRIDGE) << 8) | (PCI_CLASS_BRIDGE_P2P))
#define CMD_BM_MEM_IO (CMD_BUS_MASTER | BIT1 | BIT0)
#define DISBL_IO_REG1C 0x01F1
#define DISBL_MEM32_REG20 0x0000FFF0
#define DISBL_PMEM_REG24 0x0001FFF1
#define DOCK_BUSSES 8
#define PCI_CAPABILITY_ID_PCIEXP 0x10
#define PCI_CAPBILITY_POINTER_OFFSET 0x34
#define LTR_MAX_SNOOP_LATENCY_VALUE 0x0846 ///< Intel recommended maximum value for Snoop Latency can we put like this ?
#define LTR_MAX_NON_SNOOP_LATENCY_VALUE 0x0846 ///< Intel recommended maximum value for Non-Snoop Latency can we put like this ?
GLOBAL_REMOVE_IF_UNREFERENCED TBT_NVS_AREA *mTbtNvsAreaPtr;
GLOBAL_REMOVE_IF_UNREFERENCED UINT8 gCurrentDiscreteTbtRootPort;
GLOBAL_REMOVE_IF_UNREFERENCED UINT8 gCurrentDiscreteTbtRootPortType;
GLOBAL_REMOVE_IF_UNREFERENCED UINT16 TbtLtrMaxSnoopLatency;
GLOBAL_REMOVE_IF_UNREFERENCED UINT16 TbtLtrMaxNoSnoopLatency;
GLOBAL_REMOVE_IF_UNREFERENCED UINT8 gDTbtPcieRstSupport;
GLOBAL_REMOVE_IF_UNREFERENCED TBT_INFO_HOB *gTbtInfoHob = NULL;
STATIC UINTN mPciExpressBaseAddress;
STATIC UINT8 TbtSegment = 0;
VOID
GpioWrite (
IN UINT32 GpioNumber,
IN BOOLEAN Value
)
{
GpioSetOutputValue (GpioNumber, (UINT32)Value);
}
/**
Search and return the offset of desired Pci Express Capability ID
CAPID list:
0x0001 = Advanced Error Reporting Capability
0x0002 = Virtual Channel Capability
0x0003 = Device Serial Number Capability
0x0004 = Power Budgeting Capability
@param[in] Bus Pci Bus Number
@param[in] Device Pci Device Number
@param[in] Function Pci Function Number
@param[in] CapId Extended CAPID to search for
@retval 0 CAPID not found
@retval Other CAPID found, Offset of desired CAPID
**/
UINT16
PcieFindExtendedCapId (
IN UINT8 Bus,
IN UINT8 Device,
IN UINT8 Function,
IN UINT16 CapId
)
{
UINT16 CapHeaderOffset;
UINT16 CapHeaderId;
UINT64 DeviceBase;
DeviceBase = PCI_SEGMENT_LIB_ADDRESS (TbtSegment, Bus, Device, Function, 0);
///
/// 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 = 0x100;
while (CapHeaderOffset != 0 && CapHeaderId != 0xFFFF) {
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;
}
/**
Find the Offset to a given Capabilities ID
CAPID list:
0x01 = PCI Power Management Interface
0x04 = Slot Identification
0x05 = MSI Capability
0x10 = PCI Express Capability
@param[in] Bus Pci Bus Number
@param[in] Device Pci Device Number
@param[in] Function Pci Function Number
@param[in] CapId CAPID to search for
@retval 0 CAPID not found
@retval Other CAPID found, Offset of desired CAPID
**/
UINT8
PcieFindCapId (
IN UINT8 Segment,
IN UINT8 Bus,
IN UINT8 Device,
IN UINT8 Function,
IN UINT8 CapId
)
{
UINT8 CapHeaderOffset;
UINT8 CapHeaderId;
UINT64 DeviceBase;
DeviceBase = PCI_SEGMENT_LIB_ADDRESS (Segment, Bus, Device, Function, 0);
if ((PciSegmentRead8 (DeviceBase + PCI_PRIMARY_STATUS_OFFSET) & EFI_PCI_STATUS_CAPABILITY) == 0x00) {
///
/// Function has no capability pointer
///
return 0;
}
///
/// 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 = 0x14;
} else {
///
/// Otherwise, start at Offset 0x34
///
CapHeaderOffset = 0x34;
}
///
/// 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) {
CapHeaderId = PciSegmentRead8 (DeviceBase + CapHeaderOffset);
if (CapHeaderId == CapId) {
return CapHeaderOffset;
}
///
/// 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 = PciSegmentRead8 (DeviceBase + CapHeaderOffset + 1) & ((UINT8) ~(BIT0 | BIT1));
}
return 0;
}
/**
This function configures the L1 Substates.
It can be used for Rootport and endpoint devices.
@param[in] DownstreamPort Indicates if the device about to be programmed is a downstream port
@param[in] DeviceBase Device PCI configuration base address
@param[in] L1SubstateExtCapOffset Pointer to L1 Substate Capability Structure
@param[in] PortL1SubstateCapSupport L1 Substate capability setting
@param[in] PortCommonModeRestoreTime Common Mode Restore Time
@param[in] PortTpowerOnValue Tpower_on Power On Wait Time
@param[in] PortTpowerOnScale Tpower-on Scale
@retval none
**/
VOID
ConfigureL1s (
IN UINTN DeviceBase,
IN UINT16 L1SubstateExtCapOffset,
IN UINT32 PortL1SubstateCapSupport,
IN UINT32 PortCommonModeRestoreTime,
IN UINT32 PortTpowerOnValue,
IN UINT32 PortTpowerOnScale,
IN UINT16 MaxLevel
)
{
PciSegmentAndThenOr32 (
DeviceBase + L1SubstateExtCapOffset + R_PCIE_EX_L1SCTL1_OFFSET,
(UINT32) ~(0xFF00),
(UINT32) PortCommonModeRestoreTime << 8
);
PciSegmentAnd32(DeviceBase + L1SubstateExtCapOffset + R_PCIE_EX_L1SCTL2_OFFSET, 0xFFFFFF04);
PciSegmentOr32(DeviceBase + L1SubstateExtCapOffset + R_PCIE_EX_L1SCTL2_OFFSET,(UINT32) ((PortTpowerOnValue << N_PCIE_EX_L1SCTL2_POWT) | PortTpowerOnScale));
PciSegmentAndThenOr32 (
DeviceBase + L1SubstateExtCapOffset + R_PCIE_EX_L1SCTL1_OFFSET,
(UINT32) ~(0xE3FF0000),
(UINT32) (BIT30 | BIT23 | BIT21)
);
}
VOID
RootportL1sSupport (
IN UINT8 Bus,
IN UINT8 Dev,
IN UINT8 Fun,
IN UINT16 RootL1SubstateExtCapOffset,
IN UINT16 MaxL1Level
)
{
UINTN ComponentABaseAddress;
UINTN ComponentBBaseAddress;
UINT8 SecBus;
UINT32 PortL1SubstateCapSupport;
UINT32 PortCommonModeRestoreTime;
UINT32 PortTpowerOnValue;
UINT32 PortTpowerOnScale;
UINT16 ComponentBL1SubstateExtCapOffset;
UINT32 ComponentBL1Substates;
UINT32 ComponentBCommonModeRestoreTime;
UINT32 ComponentBTpowerOnValue;
UINT32 ComponentBTpowerOnScale;
UINT32 Data32;
PortL1SubstateCapSupport = 0;
PortCommonModeRestoreTime = 0;
PortTpowerOnValue = 0;
PortTpowerOnScale = 0;
Data32 = 0;
ComponentABaseAddress = PCI_SEGMENT_LIB_ADDRESS (TbtSegment, Bus, Dev, Fun, 0);
if (RootL1SubstateExtCapOffset != 0) {
Data32 = PciSegmentRead32 (ComponentABaseAddress + RootL1SubstateExtCapOffset + R_PCIE_EX_L1SCAP_OFFSET);
PortL1SubstateCapSupport = (Data32) & 0x0F;
PortCommonModeRestoreTime = (Data32 >> 8) & 0xFF;
PortTpowerOnScale = (Data32 >> 16) & 0x3;
PortTpowerOnValue = (Data32 >> 19) & 0x1F;
} else {
MaxL1Level = 0; // If L1 Substates from Root Port side is disable, then Disable from Device side also.
}
SecBus = PciSegmentRead8 (ComponentABaseAddress + PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET);
ComponentBBaseAddress = PCI_SEGMENT_LIB_ADDRESS (TbtSegment, SecBus, 0, 0, 0);
if (PciSegmentRead16 (ComponentBBaseAddress + PCI_DEVICE_ID_OFFSET) == 0xFFFF) {
ComponentBL1SubstateExtCapOffset = PcieFindExtendedCapId (
SecBus,
0,
0,
V_PCIE_EX_L1S_CID
);
if (ComponentBL1SubstateExtCapOffset != 0) {
ComponentBL1Substates = PciSegmentRead32 (ComponentBBaseAddress + ComponentBL1SubstateExtCapOffset + R_PCIE_EX_L1SCAP_OFFSET);
ComponentBCommonModeRestoreTime = (ComponentBL1Substates >> 8) & 0xFF;
ComponentBTpowerOnScale = (ComponentBL1Substates >> 16) & 0x3;
ComponentBTpowerOnValue = (ComponentBL1Substates >> 19) & 0x1F;
if (MaxL1Level == 3) {
if (Data32 >= ComponentBL1Substates) {
if (~(Data32 | BIT2)) {
MaxL1Level = 1;
}
}
else {
if (~(ComponentBL1Substates | BIT2)) {
MaxL1Level = 1;
}
}
}
if (MaxL1Level == 3) {
ConfigureL1s (
ComponentABaseAddress,
RootL1SubstateExtCapOffset,
PortL1SubstateCapSupport,
ComponentBCommonModeRestoreTime,
ComponentBTpowerOnValue,
ComponentBTpowerOnScale,
MaxL1Level
);
ConfigureL1s (
ComponentBBaseAddress,
ComponentBL1SubstateExtCapOffset,
ComponentBL1Substates,
PortCommonModeRestoreTime,
PortTpowerOnValue,
PortTpowerOnScale,
MaxL1Level
);
}
if (MaxL1Level == 1) {
PciSegmentOr32 (
ComponentABaseAddress + RootL1SubstateExtCapOffset + R_PCIE_EX_L1SCTL1_OFFSET,
(UINT32) (BIT3 | BIT1)
);
PciSegmentOr32 (
ComponentBBaseAddress + ComponentBL1SubstateExtCapOffset + R_PCIE_EX_L1SCTL1_OFFSET,
(UINT32) (BIT3 | BIT1)
);
}
else {
if (RootL1SubstateExtCapOffset != 0) {
PciSegmentOr32 (
ComponentABaseAddress + RootL1SubstateExtCapOffset + R_PCIE_EX_L1SCTL1_OFFSET,
(UINT32) (BIT3 | BIT1)
);
PciSegmentOr32 (
ComponentABaseAddress + RootL1SubstateExtCapOffset + R_PCIE_EX_L1SCTL1_OFFSET,
(UINT32) (BIT2 | BIT0)
);
}
if (ComponentBL1SubstateExtCapOffset != 0) {
PciSegmentOr32 (
ComponentBBaseAddress + ComponentBL1SubstateExtCapOffset + R_PCIE_EX_L1SCTL1_OFFSET,
(UINT32) (BIT3 | BIT1)
);
PciSegmentOr32 (
ComponentBBaseAddress + ComponentBL1SubstateExtCapOffset + R_PCIE_EX_L1SCTL1_OFFSET,
(UINT32) (BIT2 | BIT0)
);
}
}
}
}
}
VOID
MultiFunctionDeviceAspm (
IN UINT8 Bus,
IN UINT8 Dev
)
{
UINT16 LowerAspm;
UINT16 AspmVal;
UINT8 Fun;
UINT64 DeviceBaseAddress;
UINT8 CapHeaderOffset;
LowerAspm = 3; // L0s and L1 Supported
for (Fun = 0; Fun <= PCI_MAX_FUNC; ++Fun) {
//
// Check for Device availability
//
DeviceBaseAddress = PCI_SEGMENT_LIB_ADDRESS (TbtSegment, Bus, Dev, Fun, 0);
if (PciSegmentRead16 (DeviceBaseAddress + PCI_DEVICE_ID_OFFSET) == 0xFFFF) {
// Device not present
continue;
}
CapHeaderOffset = PcieFindCapId (TbtSegment, Bus, Dev, Fun, 0x10);
AspmVal = (PciSegmentRead16 (DeviceBaseAddress + CapHeaderOffset + 0x00C) >> 10) & 3;
if (LowerAspm > AspmVal) {
LowerAspm = AspmVal;
}
} //Fun
for (Fun = 0; Fun <= PCI_MAX_FUNC; ++Fun) {
//
// Check for Device availability
//
DeviceBaseAddress = PCI_SEGMENT_LIB_ADDRESS (TbtSegment, Bus, Dev, Fun, 0);
if (PciSegmentRead16 (DeviceBaseAddress + PCI_DEVICE_ID_OFFSET) == 0xFFFF) {
//
// Device not present
//
continue;
}
CapHeaderOffset = PcieFindCapId (TbtSegment, Bus, Dev, Fun, 0x10);
PciSegmentAndThenOr16 (DeviceBaseAddress + CapHeaderOffset + 0x10, 0xFFFC, LowerAspm);
} //Fun
}
UINT16
LimitAspmLevel (
IN UINT16 SelectedAspm,
IN UINT16 MaxAspmLevel
)
{
SelectedAspm = SelectedAspm & MaxAspmLevel;
return SelectedAspm;
}
UINT16
FindOptimalAspm (
IN UINT16 ComponentAaspm,
IN UINT16 ComponentBaspm
)
{
UINT16 SelectedAspm;
SelectedAspm = ComponentAaspm & ComponentBaspm;
return SelectedAspm;
}
UINT16
FindComponentBaspm (
IN UINT8 Bus,
IN UINT8 MaxBus
)
{
UINT8 BusNo;
UINT8 DevNo;
UINT8 FunNo;
UINT64 DevBaseAddress;
UINT8 RegVal;
UINT8 SecBusNo;
UINT16 SelectedAspm; // No ASPM Support
UINT8 CapHeaderOffset_B;
BOOLEAN AspmFound;
SelectedAspm = 0;
AspmFound = FALSE;
for (BusNo = MaxBus; (BusNo != 0xFF) && (!AspmFound); --BusNo) {
for (DevNo = 0; (DevNo <= PCI_MAX_DEVICE) && (!AspmFound); ++DevNo) {
for (FunNo = 0; (FunNo <= PCI_MAX_FUNC) && (!AspmFound); ++FunNo) {
//
// Check for Device availability
//
DevBaseAddress = PCI_SEGMENT_LIB_ADDRESS (TbtSegment, BusNo, DevNo, FunNo, 0);
if (PciSegmentRead16 (DevBaseAddress + PCI_DEVICE_ID_OFFSET) == 0xFFFF) {
//
// Device not present
//
continue;
}
RegVal = PciSegmentRead8 (DevBaseAddress + PCI_HEADER_TYPE_OFFSET);
if ((RegVal & (BIT0 + BIT1 + BIT2 + BIT3 + BIT4 + BIT5 + BIT6)) != 0x01) {
//
// Not a PCI-to-PCI bridges device
//
continue;
}
SecBusNo = PciSegmentRead8 (DevBaseAddress + PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET);
if (SecBusNo == Bus) {
//
// This is the Rootbridge for the given 'Bus' device
//
CapHeaderOffset_B = PcieFindCapId (TbtSegment, BusNo, DevNo, FunNo, 0x10);
SelectedAspm = (PciSegmentRead16 (DevBaseAddress + CapHeaderOffset_B + 0x00C) >> 10) & 3;
AspmFound = TRUE;
}
} //FunNo
} //DevNo
} //BusNo
return (SelectedAspm);
}
VOID
NoAspmSupport (
IN UINT8 Bus,
IN UINT8 Dev,
IN UINT8 Fun,
IN UINT8 CapHeaderOffset
)
{
UINT64 DeviceBaseAddress;
DeviceBaseAddress = PCI_SEGMENT_LIB_ADDRESS (TbtSegment, Bus, Dev, Fun, 0);
PciSegmentAndThenOr16 (DeviceBaseAddress + CapHeaderOffset + 0x10, 0xFFFC, 0x00);
}
VOID
EndpointAspmSupport (
IN UINT8 Bus,
IN UINT8 Dev,
IN UINT8 Fun,
IN UINT8 CapHeaderOffset,
IN UINT8 MaxBus,
IN UINT16 MaxAspmLevel
)
{
UINT64 DeviceBaseAddress;
UINT16 ComponentAaspm;
UINT16 ComponentBaspm;
UINT16 SelectedAspm;
DeviceBaseAddress = PCI_SEGMENT_LIB_ADDRESS (TbtSegment, Bus, Dev, Fun, 0);
ComponentAaspm = (PciSegmentRead16 (DeviceBaseAddress + CapHeaderOffset + 0x00C) >> 10) & 3;
ComponentBaspm = FindComponentBaspm (Bus, MaxBus);
SelectedAspm = FindOptimalAspm (ComponentAaspm, ComponentBaspm);
SelectedAspm = LimitAspmLevel (SelectedAspm, MaxAspmLevel);
PciSegmentAndThenOr16 (DeviceBaseAddress + CapHeaderOffset + 0x10, 0xFFFC, SelectedAspm);
}
VOID
UpstreamAspmSupport (
IN UINT8 Bus,
IN UINT8 Dev,
IN UINT8 Fun,
IN UINT8 CapHeaderOffset,
IN UINT8 MaxBus,
IN UINT16 MaxAspmLevel
)
{
UINT64 DeviceBaseAddress;
UINT16 ComponentAaspm;
UINT16 ComponentBaspm;
UINT16 SelectedAspm;
DeviceBaseAddress = PCI_SEGMENT_LIB_ADDRESS (TbtSegment, Bus, Dev, Fun, 0);
ComponentAaspm = (PciSegmentRead16 (DeviceBaseAddress + CapHeaderOffset + 0x00C) >> 10) & 3;
ComponentBaspm = FindComponentBaspm (Bus, MaxBus);
SelectedAspm = FindOptimalAspm (ComponentAaspm, ComponentBaspm);
SelectedAspm = LimitAspmLevel (SelectedAspm, MaxAspmLevel);
PciSegmentAndThenOr16 (DeviceBaseAddress + CapHeaderOffset + 0x10, 0xFFFC, SelectedAspm);
}
VOID
DownstreamAspmSupport (
IN UINT8 Bus,
IN UINT8 Dev,
IN UINT8 Fun,
IN UINT8 CapHeaderOffset,
IN UINT16 MaxAspmLevel
)
{
UINT64 ComponentABaseAddress;
UINT64 ComponentBBaseAddress;
UINT16 ComponentAaspm;
UINT16 ComponentBaspm;
UINT16 SelectedAspm;
UINT8 SecBus;
UINT8 CapHeaderOffset_B;
ComponentABaseAddress = PCI_SEGMENT_LIB_ADDRESS (TbtSegment, Bus, Dev, Fun, 0);
ComponentAaspm = (PciSegmentRead16 (ComponentABaseAddress + CapHeaderOffset + 0x00C) >> 10) & 3;
SecBus = PciSegmentRead8 (ComponentABaseAddress + PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET);
ComponentBBaseAddress = PCI_SEGMENT_LIB_ADDRESS (TbtSegment, SecBus, 0, 0, 0);
ComponentBaspm = 0; // No ASPM Support
if (PciSegmentRead16 (ComponentBBaseAddress + PCI_DEVICE_ID_OFFSET) != 0xFFFF) {
CapHeaderOffset_B = PcieFindCapId (TbtSegment, SecBus, 0, 0, 0x10);
ComponentBaspm = (PciSegmentRead16 (ComponentBBaseAddress + CapHeaderOffset_B + 0x00C) >> 10) & 3;
}
SelectedAspm = FindOptimalAspm (ComponentAaspm, ComponentBaspm);
SelectedAspm = LimitAspmLevel (SelectedAspm, MaxAspmLevel);
PciSegmentAndThenOr16 (ComponentABaseAddress + CapHeaderOffset + 0x10, 0xFFFC, SelectedAspm);
}
VOID
RootportAspmSupport (
IN UINT8 Bus,
IN UINT8 Dev,
IN UINT8 Fun,
IN UINT8 CapHeaderOffset,
IN UINT16 MaxAspmLevel
)
{
UINT64 ComponentABaseAddress;
UINT64 ComponentBBaseAddress;
UINT16 ComponentAaspm;
UINT16 ComponentBaspm;
UINT16 SelectedAspm;
UINT8 SecBus;
UINT8 CapHeaderOffset_B;
ComponentABaseAddress = PCI_SEGMENT_LIB_ADDRESS (TbtSegment, Bus, Dev, Fun, 0);
ComponentAaspm = (PciSegmentRead16 (ComponentABaseAddress + CapHeaderOffset + 0x00C) >> 10) & 3;
SecBus = PciSegmentRead8 (ComponentABaseAddress + PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET);
ComponentBBaseAddress = PCI_SEGMENT_LIB_ADDRESS (TbtSegment, SecBus, 0, 0, 0);
ComponentBaspm = 0; // No ASPM Support
if (PciSegmentRead16 (ComponentBBaseAddress + PCI_DEVICE_ID_OFFSET) != 0xFFFF) {
CapHeaderOffset_B = PcieFindCapId (TbtSegment, SecBus, 0, 0, 0x10);
ComponentBaspm = (PciSegmentRead16 (ComponentBBaseAddress + CapHeaderOffset_B + 0x00C) >> 10) & 3;
}
SelectedAspm = FindOptimalAspm (ComponentAaspm, ComponentBaspm);
SelectedAspm = LimitAspmLevel (SelectedAspm, MaxAspmLevel);
PciSegmentAndThenOr16 (ComponentABaseAddress + CapHeaderOffset + 0x10, 0xFFFC, SelectedAspm);
}
VOID
ThunderboltEnableAspmWithoutLtr (
IN UINT16 MaxAspmLevel,
IN UINTN RpSegment,
IN UINTN RpBus,
IN UINTN RpDevice,
IN UINTN RpFunction
)
{
UINT8 Bus;
UINT8 Dev;
UINT8 Fun;
UINT8 RootBus;
UINT8 RootDev;
UINT8 RootFun;
UINT8 MinBus;
UINT8 MaxBus;
UINT16 DeviceId;
UINT64 DeviceBaseAddress;
UINT8 RegVal;
UINT8 CapHeaderOffset;
UINT16 DevicePortType;
MinBus = 0;
MaxBus = 0;
MinBus = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS (RpSegment, RpBus, RpDevice, RpFunction, PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET));
MaxBus = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS (RpSegment, RpBus, RpDevice, RpFunction, PCI_BRIDGE_SUBORDINATE_BUS_REGISTER_OFFSET));
DeviceId = PciSegmentRead16 (PCI_SEGMENT_LIB_ADDRESS (RpSegment, MinBus, 0x00, 0x00, PCI_DEVICE_ID_OFFSET));
if (!(IsTbtHostRouter (DeviceId))) {
return;
}
TbtSegment = (UINT8)RpSegment;
RootBus = (UINT8)RpBus;
RootDev = (UINT8)RpDevice;
RootFun = (UINT8)RpFunction;
//
// Enumerate all the bridges and devices which are available on TBT host controller
//
for (Bus = MinBus; Bus <= MaxBus; ++Bus) {
for (Dev = 0; Dev <= PCI_MAX_DEVICE; ++Dev) {
//
// Check for Device availability
//
DeviceBaseAddress = PCI_SEGMENT_LIB_ADDRESS (TbtSegment, Bus, Dev, 0, 0);
if (PciSegmentRead16 (DeviceBaseAddress + PCI_DEVICE_ID_OFFSET) == 0xFFFF) {
//
// Device not present
//
continue;
}
RegVal = PciSegmentRead8 (DeviceBaseAddress + PCI_HEADER_TYPE_OFFSET);
if ((RegVal & BIT7) == 0) {
//
// Not a multi-function device
//
continue;
}
MultiFunctionDeviceAspm(Bus, Dev);
} //Dev
} //Bus
for (Bus = MinBus; Bus <= MaxBus; ++Bus) {
for (Dev = 0; Dev <= PCI_MAX_DEVICE; ++Dev) {
for (Fun = 0; Fun <= PCI_MAX_FUNC; ++Fun) {
//
// Check for Device availability
//
DeviceBaseAddress = PCI_SEGMENT_LIB_ADDRESS (TbtSegment, Bus, Dev, Fun, 0);
if (PciSegmentRead16 (DeviceBaseAddress + PCI_DEVICE_ID_OFFSET) == 0xFFFF) {
//
// Device not present
//
continue;
}
CapHeaderOffset = PcieFindCapId (TbtSegment, Bus, Dev, Fun, 0x10);
DevicePortType = (PciSegmentRead16 (DeviceBaseAddress + CapHeaderOffset + 0x002) >> 4) & 0xF;
if(PciSegmentRead8 (DeviceBaseAddress + PCI_CLASSCODE_OFFSET) == PCI_CLASS_SERIAL) {
MaxAspmLevel = (UINT16) 0x1;
}
switch (DevicePortType) {
case 0:
//
// PCI Express Endpoint
//
EndpointAspmSupport (Bus, Dev, Fun, CapHeaderOffset, MaxBus, MaxAspmLevel);
break;
case 1:
//
// Legacy PCI Express Endpoint
//
EndpointAspmSupport (Bus, Dev, Fun, CapHeaderOffset, MaxBus, MaxAspmLevel);
break;
case 4:
//
// Root Port of PCI Express Root Complex
//
RootportAspmSupport (Bus, Dev, Fun, CapHeaderOffset, MaxAspmLevel);
break;
case 5:
//
// Upstream Port of PCI Express Switch
//
UpstreamAspmSupport (Bus, Dev, Fun, CapHeaderOffset, MaxBus, MaxAspmLevel);
break;
case 6:
//
// Downstream Port of PCI Express Switch
//
DownstreamAspmSupport (Bus, Dev, Fun, CapHeaderOffset, MaxAspmLevel);
break;
case 7:
//
// PCI Express to PCI/PCI-X Bridge
//
NoAspmSupport (Bus, Dev, Fun, CapHeaderOffset);
break;
case 8:
//
// PCI/PCI-X to PCI Express Bridge
//
NoAspmSupport (Bus, Dev, Fun, CapHeaderOffset);
break;
case 9:
//
// Root Complex Integrated Endpoint
//
EndpointAspmSupport (Bus, Dev, Fun, CapHeaderOffset, MaxBus, MaxAspmLevel);
break;
case 10:
//
// Root Complex Event Collector
//
EndpointAspmSupport (Bus, Dev, Fun, CapHeaderOffset, MaxBus, MaxAspmLevel);
break;
default:
break;
}
//
// switch(DevicePortType)
//
}
//
// Fun
//
}
//
// Dev
//
}
//
// Bus
//
CapHeaderOffset = PcieFindCapId (TbtSegment, RootBus, RootDev, RootFun, 0x10);
RootportAspmSupport (RootBus, RootDev, RootFun, CapHeaderOffset, MaxAspmLevel);
}
VOID
ThunderboltEnableL1Sub (
IN UINT16 MaxL1Level,
IN UINTN RpSegment,
IN UINTN RpBus,
IN UINTN RpDevice,
IN UINTN RpFunction
)
{
UINT16 CapHeaderOffsetExtd;
RpBus = 0;
CapHeaderOffsetExtd = PcieFindExtendedCapId ((UINT8) RpBus, (UINT8) RpDevice, (UINT8) RpFunction, V_PCIE_EX_L1S_CID);
RootportL1sSupport ((UINT8) RpBus, (UINT8) RpDevice, (UINT8) RpFunction, CapHeaderOffsetExtd, MaxL1Level);
}
VOID
ThunderboltDisableAspmWithoutLtr (
IN UINTN RpSegment,
IN UINTN RpBus,
IN UINTN RpDevice,
IN UINTN RpFunction
)
{
UINT8 Bus;
UINT8 Dev;
UINT8 Fun;
UINT8 RootBus;
UINT8 RootDev;
UINT8 RootFun;
UINT8 MinBus;
UINT8 MaxBus;
UINT16 DeviceId;
UINT64 DeviceBaseAddress;
UINT8 CapHeaderOffset;
MinBus = 0;
MaxBus = 0;
MinBus = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS (RpSegment, RpBus, RpDevice, RpFunction, PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET));
MaxBus = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS (RpSegment, RpBus, RpDevice, RpFunction, PCI_BRIDGE_SUBORDINATE_BUS_REGISTER_OFFSET));
DeviceId = PciSegmentRead16 (PCI_SEGMENT_LIB_ADDRESS (RpSegment, MinBus, 0x00, 0x00, PCI_DEVICE_ID_OFFSET));
if (!(IsTbtHostRouter (DeviceId))) {
return;
}
TbtSegment = (UINT8)RpSegment;
RootBus = (UINT8)RpBus;
RootDev = (UINT8)RpDevice;
RootFun = (UINT8)RpFunction;
//
// Enumerate all the bridges and devices which are available on TBT host controller
//
for (Bus = MinBus; Bus <= MaxBus; ++Bus) {
for (Dev = 0; Dev <= PCI_MAX_DEVICE; ++Dev) {
for (Fun = 0; Fun <= PCI_MAX_FUNC; ++Fun) {
//
// Check for Device availability
//
DeviceBaseAddress = PCI_SEGMENT_LIB_ADDRESS (TbtSegment, Bus, Dev, Fun, 0);
if (PciSegmentRead16 (DeviceBaseAddress + PCI_DEVICE_ID_OFFSET) == 0xFFFF) {
//
// Device not present
//
continue;
}
CapHeaderOffset = PcieFindCapId (TbtSegment, Bus, Dev, Fun, 0x10);
PciSegmentAndThenOr16 (DeviceBaseAddress + CapHeaderOffset + 0x10, 0xFFFC, 0x00);
} //Fun
} //Dev
} //Bus
CapHeaderOffset = PcieFindCapId (TbtSegment, RootBus, RootDev, RootFun, 0x10);
NoAspmSupport(RootBus, RootDev, RootFun, CapHeaderOffset);
}
VOID
TbtProgramClkReq (
IN UINT8 Bus,
IN UINT8 Device,
IN UINT8 Function,
IN UINT8 ClkReqSetup
)
{
UINT64 DeviceBaseAddress;
UINT8 CapHeaderOffset;
UINT16 Data16;
DeviceBaseAddress = PCI_SEGMENT_LIB_ADDRESS (TbtSegment, Bus, Device, Function, 0);
CapHeaderOffset = PcieFindCapId (TbtSegment, Bus, Device, Function, 0x10);
//
// Check if CLKREQ# is supported
//
if ((PciSegmentRead32 (DeviceBaseAddress + CapHeaderOffset + 0x0C) & BIT18) != 0) {
Data16 = PciSegmentRead16 (DeviceBaseAddress + CapHeaderOffset + 0x010);
if (ClkReqSetup) {
Data16 = Data16 | BIT8; // Enable Clock Power Management
} else {
Data16 = Data16 & (UINT16)(~BIT8); // Disable Clock Power Management
}
PciSegmentWrite16 (DeviceBaseAddress + CapHeaderOffset + 0x010, Data16);
}
}
VOID
TbtProgramPtm(
IN UINT8 Bus,
IN UINT8 Device,
IN UINT8 Function,
IN UINT8 PtmSetup,
IN BOOLEAN IsRoot
)
{
UINT64 DeviceBaseAddress;
UINT16 CapHeaderOffset;
UINT16 PtmControlRegister;
UINT16 PtmCapabilityRegister;
DeviceBaseAddress = PCI_SEGMENT_LIB_ADDRESS(TbtSegment, Bus, Device, Function, 0);
CapHeaderOffset = PcieFindExtendedCapId(Bus, Device, Function, 0x001F /*V_PCIE_EX_PTM_CID*/);
if(CapHeaderOffset != 0) {
PtmCapabilityRegister = PciSegmentRead16(DeviceBaseAddress + CapHeaderOffset + 0x04);
//
// Check if PTM Requester/ Responder capability for the EP/Down stream etc
//
if ((PtmCapabilityRegister & (BIT1 | BIT0)) != 0) {
PtmControlRegister = PciSegmentRead16(DeviceBaseAddress + CapHeaderOffset + 0x08);
if (PtmSetup) {
PtmControlRegister = PtmControlRegister | BIT0; // Enable PTM
if(IsRoot) {
PtmControlRegister = PtmControlRegister | BIT1; // Enable PTM
}
PtmControlRegister = PtmControlRegister | (PtmCapabilityRegister & 0xFF00); // Programm Local Clock Granularity
} else {
PtmControlRegister = PtmControlRegister & (UINT16)(~(BIT0 | BIT1)); // Disable Clock Power Management
}
PciSegmentWrite16(DeviceBaseAddress + CapHeaderOffset + 0x08, PtmControlRegister);
}
}
}
VOID
ConfigureTbtPm (
IN UINTN RpSegment,
IN UINTN RpBus,
IN UINTN RpDevice,
IN UINTN RpFunction,
IN UINT8 Configuration // 1- Clk Request , 2- PTM ,
)
{
UINT8 Bus;
UINT8 Dev;
UINT8 Fun;
UINT8 MinBus;
UINT8 MaxBus;
UINT16 DeviceId;
UINT64 DeviceBaseAddress;
MinBus = 0;
MaxBus = 0;
if ((Configuration != 1) && (Configuration != 2)) {
return;
}
MinBus = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS (RpSegment, RpBus, RpDevice, RpFunction, PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET));
MaxBus = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS (RpSegment, RpBus, RpDevice, RpFunction, PCI_BRIDGE_SUBORDINATE_BUS_REGISTER_OFFSET));
DeviceId = PciSegmentRead16 (PCI_SEGMENT_LIB_ADDRESS (RpSegment, MinBus, 0x00, 0x00, PCI_DEVICE_ID_OFFSET));
if (!(IsTbtHostRouter (DeviceId))) {
return;
}
TbtSegment = (UINT8)RpSegment;
//
// Enumerate all the bridges and devices which are available on TBT host controller
//
for (Bus = MaxBus; Bus >= MinBus; --Bus) {
for (Dev = 0; Dev <= PCI_MAX_DEVICE; ++Dev) {
for (Fun = 0; Fun <= PCI_MAX_FUNC; ++Fun) {
//
// Check for Device availability
//
DeviceBaseAddress = PCI_SEGMENT_LIB_ADDRESS (TbtSegment, Bus, Dev, Fun, 0);
if (PciSegmentRead16 (DeviceBaseAddress + PCI_DEVICE_ID_OFFSET) == 0xFFFF) {
if (Fun == 0) {
//
// IF Fun is zero, stop enumerating other functions of the particular bridge
//
break;
}
//
// otherwise, just skip checking for CLKREQ support
//
continue;
}
switch (Configuration) {
case 1:
TbtProgramClkReq (Bus, Dev, Fun, (UINT8) mTbtNvsAreaPtr->TbtSetClkReq);
break;
case 2:
TbtProgramPtm (Bus, Dev, Fun, (UINT8) mTbtNvsAreaPtr->TbtPtm, FALSE);
TbtProgramPtm((UINT8) RpBus, (UINT8) RpDevice, (UINT8) RpFunction, (UINT8) mTbtNvsAreaPtr->TbtPtm, TRUE);
break;
default:
break;
}
} //Fun
} // Dev
} // Bus
}
/**
1) Check LTR support in device capabilities 2 register (bit 11).
2) If supported enable LTR in device control 2 register (bit 10).
**/
VOID
TbtProgramLtr (
IN UINT8 Bus,
IN UINT8 Device,
IN UINT8 Function,
IN UINT8 LtrSetup
)
{
UINT64 DeviceBaseAddress;
UINT8 CapHeaderOffset;
UINT16 Data16;
DeviceBaseAddress = PCI_SEGMENT_LIB_ADDRESS (TbtSegment, Bus, Device, Function, 0);
CapHeaderOffset = PcieFindCapId (TbtSegment, Bus, Device, Function, 0x10);
//
// Check if LTR# is supported
//
if ((PciSegmentRead32 (DeviceBaseAddress + CapHeaderOffset + 0x24) & BIT11) != 0) {
Data16 = PciSegmentRead16 (DeviceBaseAddress + CapHeaderOffset + 0x028);
if (LtrSetup) {
Data16 = Data16 | BIT10; // LTR Mechanism Enable
} else {
Data16 = Data16 & (UINT16)(~BIT10); // LTR Mechanism Disable
}
PciSegmentWrite16 (DeviceBaseAddress + CapHeaderOffset + 0x028, Data16);
}
}
VOID
ConfigureLtr (
IN UINTN RpSegment,
IN UINTN RpBus,
IN UINTN RpDevice,
IN UINTN RpFunction
)
{
UINT8 Bus;
UINT8 Dev;
UINT8 Fun;
UINT8 MinBus;
UINT8 MaxBus;
UINT16 DeviceId;
UINT64 DeviceBaseAddress;
MinBus = 0;
MaxBus = 0;
MinBus = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS (RpSegment, RpBus, RpDevice, RpFunction, PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET));
MaxBus = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS (RpSegment, RpBus, RpDevice, RpFunction, PCI_BRIDGE_SUBORDINATE_BUS_REGISTER_OFFSET));
DeviceId = PciSegmentRead16 (PCI_SEGMENT_LIB_ADDRESS (RpSegment, MinBus, 0x00, 0x00, PCI_DEVICE_ID_OFFSET));
if (!(IsTbtHostRouter (DeviceId))) {
return;
}
TbtSegment = (UINT8)RpSegment;
//
// Enumerate all the bridges and devices which are available on TBT host controller
//
for (Bus = MinBus; Bus <= MaxBus; ++Bus) {
for (Dev = 0; Dev <= PCI_MAX_DEVICE; ++Dev) {
for (Fun = 0; Fun <= PCI_MAX_FUNC; ++Fun) {
//
// Check for Device availability
//
DeviceBaseAddress = PCI_SEGMENT_LIB_ADDRESS (TbtSegment, Bus, Dev, Fun, 0);
if (PciSegmentRead16 (DeviceBaseAddress + PCI_DEVICE_ID_OFFSET) == 0xFFFF) {
if (Fun == 0) {
//
// IF Fun is zero, stop enumerating other functions of the particular bridge
//
break;
}
//
// otherwise, just skip checking for LTR support
//
continue;
}
TbtProgramLtr (Bus, Dev, Fun, (UINT8) mTbtNvsAreaPtr->TbtLtr);
} //Fun
} // Dev
} // Bus
TbtProgramLtr ((UINT8) RpBus, (UINT8) RpDevice, (UINT8) RpFunction, (UINT8) mTbtNvsAreaPtr->TbtLtr);
}
/*
US ports and endpoints which declare support must also have the LTR capability structure (cap ID 18h).
In this structure you need to enter the max snoop latency and max non-snoop latency in accordance with the format specified in the PCIe spec.
The latency value itself is platform specific so you'll need to get it from the platform architect or whatever.
*/
VOID
ThunderboltGetLatencyLtr (
VOID
)
{
PCH_SERIES PchSeries;
PchSeries = GetPchSeries ();
if(gCurrentDiscreteTbtRootPortType == DTBT_TYPE_PEG) {
// PEG selector
TbtLtrMaxSnoopLatency = LTR_MAX_SNOOP_LATENCY_VALUE;
TbtLtrMaxNoSnoopLatency = LTR_MAX_NON_SNOOP_LATENCY_VALUE;
} else if (gCurrentDiscreteTbtRootPortType == DTBT_TYPE_PCH) {
// PCH selector
if (PchSeries == PchLp) {
TbtLtrMaxSnoopLatency = 0x1003;
TbtLtrMaxNoSnoopLatency = 0x1003;
}
if (PchSeries == PchH) {
TbtLtrMaxSnoopLatency = 0x0846;
TbtLtrMaxNoSnoopLatency = 0x0846;
}
}
}
VOID
SetLatencyLtr (
IN UINT8 Bus,
IN UINT8 Dev,
IN UINT8 Fun,
IN UINT16 CapHeaderOffsetExtd,
IN UINT16 LtrMaxSnoopLatency,
IN UINT16 LtrMaxNoSnoopLatency
)
{
UINT64 DeviceBaseAddress;
if(CapHeaderOffsetExtd == 0) {
return;
}
DeviceBaseAddress = PCI_SEGMENT_LIB_ADDRESS (TbtSegment, Bus, Dev, Fun, 0);
PciSegmentWrite16 (DeviceBaseAddress + CapHeaderOffsetExtd + 0x004, LtrMaxSnoopLatency);
PciSegmentWrite16 (DeviceBaseAddress + CapHeaderOffsetExtd + 0x006, LtrMaxNoSnoopLatency);
}
VOID
ThunderboltSetLatencyLtr (
IN UINTN RpSegment,
IN UINTN RpBus,
IN UINTN RpDevice,
IN UINTN RpFunction
)
{
UINT8 Bus;
UINT8 Dev;
UINT8 Fun;
UINT8 MinBus;
UINT8 MaxBus;
UINT16 DeviceId;
UINT64 DeviceBaseAddress;
UINT8 CapHeaderOffsetStd;
UINT16 CapHeaderOffsetExtd;
UINT16 DevicePortType;
MinBus = 0;
MaxBus = 0;
MinBus = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS (RpSegment, RpBus, RpDevice, RpFunction, PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET));
MaxBus = PciSegmentRead8 (PCI_SEGMENT_LIB_ADDRESS (RpSegment, RpBus, RpDevice, RpFunction, PCI_BRIDGE_SUBORDINATE_BUS_REGISTER_OFFSET));
DeviceId = PciSegmentRead16 (PCI_SEGMENT_LIB_ADDRESS (RpSegment, MinBus, 0x00, 0x00, PCI_DEVICE_ID_OFFSET));
if (!(IsTbtHostRouter (DeviceId))) {
return;
}
TbtSegment = (UINT8)RpSegment;
for (Bus = MinBus; Bus <= MaxBus; ++Bus) {
for (Dev = 0; Dev <= PCI_MAX_DEVICE; ++Dev) {
for (Fun = 0; Fun <= PCI_MAX_FUNC; ++Fun) {
//
// Check for Device availability
//
DeviceBaseAddress = PCI_SEGMENT_LIB_ADDRESS (TbtSegment, Bus, Dev, Fun, 0);
if (PciSegmentRead16 (DeviceBaseAddress + PCI_DEVICE_ID_OFFSET) == 0xFFFF) {
//
// Device not present
//
continue;
}
CapHeaderOffsetStd = PcieFindCapId (TbtSegment, Bus, Dev, Fun, 0x10);
DevicePortType = (PciSegmentRead16 (DeviceBaseAddress + CapHeaderOffsetStd + 0x002) >> 4) & 0xF;
CapHeaderOffsetExtd = PcieFindExtendedCapId (Bus, Dev, Fun, 0x0018);
switch (DevicePortType) {
case 0:
//
// PCI Express Endpoint
//
SetLatencyLtr (Bus, Dev, Fun, CapHeaderOffsetExtd, TbtLtrMaxSnoopLatency, TbtLtrMaxNoSnoopLatency);
break;
case 1:
//
// Legacy PCI Express Endpoint
//
SetLatencyLtr (Bus, Dev, Fun, CapHeaderOffsetExtd, TbtLtrMaxSnoopLatency, TbtLtrMaxNoSnoopLatency);
break;
case 4:
//
// Root Port of PCI Express Root Complex
//
// Do-nothing
break;
case 5:
//
// Upstream Port of PCI Express Switch
//
SetLatencyLtr (Bus, Dev, Fun, CapHeaderOffsetExtd, TbtLtrMaxSnoopLatency, TbtLtrMaxNoSnoopLatency);
break;
case 6:
//
// Downstream Port of PCI Express Switch
//
// Do-nothing
break;
case 7:
//
// PCI Express to PCI/PCI-X Bridge
//
// Do-nothing
break;
case 8:
//
// PCI/PCI-X to PCI Express Bridge
//
// Do-nothing
break;
case 9:
//
// Root Complex Integrated Endpoint
//
// Do-nothing
break;
case 10:
//
// Root Complex Event Collector
//
// Do-nothing
break;
default:
break;
}
//
// switch(DevicePortType)
//
}
//
// Fun
//
}
//
// Dev
//
}
//
// Bus
//
}
static
VOID
Stall (
UINTN Usec
)
{
UINTN Index;
UINT32 Data32;
UINT32 PrevData;
UINTN Counter;
Counter = (UINTN) ((Usec * 10) / 3);
//
// Call WaitForTick for Counter + 1 ticks to try to guarantee Counter tick
// periods, thus attempting to ensure Microseconds of stall time.
//
if (Counter != 0) {
PrevData = IoRead32 (PcdGet16 (PcdAcpiBaseAddress) + R_PCH_ACPI_PM1_TMR);
for (Index = 0; Index < Counter;) {
Data32 = IoRead32 (PcdGet16 (PcdAcpiBaseAddress) + R_PCH_ACPI_PM1_TMR);
if (Data32 < PrevData) {
//
// Reset if there is a overlap
//
PrevData = Data32;
continue;
}
Index += (Data32 - PrevData);
PrevData = Data32;
}
}
return ;
}
/**
Called during Sx entry, initates TbtSetPcie2TbtCommand HandShake to set GO2SX_NO_WAKE
for Tbt devices if WakeupSupport is not present.
@param[in] DispatchHandle - The unique handle assigned to this handler by SmiHandlerRegister().
@param[in] DispatchContext - Points to an optional handler context which was specified when the
handler was registered.
@param[in, out] CommBuffer - A pointer to a collection of data in memory that will
be conveyed from a non-SMM environment into an SMM environment.
@param[in, out] CommBufferSize - The size of the CommBuffer.
@retval EFI_SUCCESS - The interrupt was handled successfully.
**/
EFI_STATUS
EFIAPI
SxDTbtEntryCallback (
IN EFI_HANDLE DispatchHandle,
IN CONST VOID *DispatchContext,
IN OUT VOID *CommBuffer OPTIONAL,
IN UINTN *CommBufferSize OPTIONAL
)
{
UINT16 DeviceId;
UINT8 CableConnected;
UINT8 RootportSelected;
UINT8 HoustRouteBus;
volatile UINT32 *PowerState;
UINT32 PowerStatePrev;
BOOLEAN SecSubBusAssigned;
UINT64 DeviceBaseAddress;
UINT8 CapHeaderOffset;
UINTN RpDev;
UINTN RpFunc;
EFI_STATUS Status;
UINT32 Timeout;
UINT32 RegisterValue;
UINT64 Tbt2Pcie;
UINTN Index;
UINT32 TbtCioPlugEventGpioNo;
UINT32 TbtFrcPwrGpioNo;
UINT8 TbtFrcPwrGpioLevel;
UINT32 TbtPcieRstGpioNo;
UINT8 TbtPcieRstGpioLevel;
EFI_SMM_SX_REGISTER_CONTEXT *EntryDispatchContext;
CableConnected = 0;
HoustRouteBus = 3;
SecSubBusAssigned = FALSE;
Timeout = 600;
RootportSelected = 0;
TbtCioPlugEventGpioNo = 0;
TbtFrcPwrGpioNo = 0;
TbtFrcPwrGpioLevel = 0;
TbtPcieRstGpioNo = 0;
TbtPcieRstGpioLevel = 0;
Index = 0;
EntryDispatchContext = (EFI_SMM_SX_REGISTER_CONTEXT*) DispatchContext;
// CableConnected = GetTbtHostRouterStatus ();
//SaveTbtHostRouterStatus (CableConnected & 0xF0);
//
// Get the Power State and Save
//
if (((mTbtNvsAreaPtr->DTbtControllerEn0 == 0) && (Index == 0))) {
RootportSelected = mTbtNvsAreaPtr->RootportSelected0;
TbtCioPlugEventGpioNo = mTbtNvsAreaPtr->TbtCioPlugEventGpioNo0;
TbtFrcPwrGpioNo = mTbtNvsAreaPtr->TbtFrcPwrGpioNo0;
TbtFrcPwrGpioLevel = mTbtNvsAreaPtr->TbtFrcPwrGpioLevel0;
TbtPcieRstGpioNo = mTbtNvsAreaPtr->TbtPcieRstGpioNo0;
TbtPcieRstGpioLevel = mTbtNvsAreaPtr->TbtPcieRstGpioLevel0;
}
Status = GetDTbtRpDevFun (gCurrentDiscreteTbtRootPortType, RootportSelected - 1, &RpDev, &RpFunc);
ASSERT_EFI_ERROR (Status);
CapHeaderOffset = PcieFindCapId (TbtSegment, 0x00, (UINT8)RpDev, (UINT8)RpFunc, 0x01);
DeviceBaseAddress = PCI_SEGMENT_LIB_ADDRESS (TbtSegment, 0x00, (UINT32)RpDev, (UINT32)RpFunc, 0);
PowerState = &*((volatile UINT32 *) (mPciExpressBaseAddress + DeviceBaseAddress + CapHeaderOffset + 4)); //PMCSR
PowerStatePrev = *PowerState;
*PowerState &= 0xFFFFFFFC;
HoustRouteBus = PciSegmentRead8 (DeviceBaseAddress + PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET);
//
// Check the Subordinate bus .If it is Zero ,assign temporary bus to
// find the device presence .
//
if (HoustRouteBus == 0) {
PciSegmentWrite8 (DeviceBaseAddress + PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET, 0xF0);
PciSegmentWrite8 (DeviceBaseAddress + PCI_BRIDGE_SUBORDINATE_BUS_REGISTER_OFFSET, 0xF0);
HoustRouteBus = 0xF0;
SecSubBusAssigned = TRUE;
}
//
// Clear Interrupt capability of TBT CIO Plug Event Pin to make sure no SCI is getting generated,
// This GPIO will be reprogrammed while resuming as part of Platform GPIO Programming.
//
GpioSetPadInterruptConfig (TbtCioPlugEventGpioNo, GpioIntDis);
//
// Read the TBT Host router DeviceID
//
DeviceId = PciSegmentRead16 (PCI_SEGMENT_LIB_ADDRESS (TbtSegment, HoustRouteBus, 0, 0, PCI_DEVICE_ID_OFFSET));
//
// Check For HostRouter Presence
//
if (IsTbtHostRouter (DeviceId)) {
// CableConnected = GetTbtHostRouterStatus ();
if (!((CableConnected & (DTBT_SAVE_STATE_OFFSET << Index)) == (DTBT_SAVE_STATE_OFFSET << Index))) {
CableConnected = CableConnected | (DTBT_SAVE_STATE_OFFSET << Index);
// SaveTbtHostRouterStatus (CableConnected);
}
}
//
// Check value of Tbt2Pcie reg, if Tbt is not present, bios needs to apply force power prior to sending mailbox command
//
GET_TBT2PCIE_REGISTER_ADDRESS(TbtSegment, HoustRouteBus, 0x00, 0x00, Tbt2Pcie)
RegisterValue = PciSegmentRead32 (Tbt2Pcie);
if (0xFFFFFFFF == RegisterValue) {
GpioWrite (TbtFrcPwrGpioNo,TbtFrcPwrGpioLevel);
while (Timeout -- > 0) {
RegisterValue = PciSegmentRead32 (Tbt2Pcie);
if (0xFFFFFFFF != RegisterValue) {
break;
}
Stall(1* (UINTN)1000);
}
//
// Before entering Sx state BIOS should execute GO2SX/NO_WAKE mailbox command for AIC.
// However BIOS shall not execute go2sx mailbox command on S5/reboot cycle.
//
if( (EntryDispatchContext->Type == SxS3) || (EntryDispatchContext->Type == SxS4))
{
if(!mTbtNvsAreaPtr->TbtWakeupSupport) {
//Wake Disabled, GO2SX_NO_WAKE Command
TbtSetPcie2TbtCommand (PCIE2TBT_GO2SX_NO_WAKE, HoustRouteBus, 0, 0, TBT_5S_TIMEOUT);
} else {
//Wake Enabled, GO2SX Command
TbtSetPcie2TbtCommand (PCIE2TBT_GO2SX, HoustRouteBus, 0, 0, TBT_5S_TIMEOUT);
}
}
if (mTbtNvsAreaPtr->TbtFrcPwrEn == 0) {
GpioWrite (TbtFrcPwrGpioNo,!(TbtFrcPwrGpioLevel));
}
} else {
//
// Before entering Sx state BIOS should execute GO2SX/NO_WAKE mailbox command for AIC.
// However BIOS shall not execute go2sx mailbox command on S5/reboot cycle.
//
if( (EntryDispatchContext->Type == SxS3) || (EntryDispatchContext->Type == SxS4))
{
if(!mTbtNvsAreaPtr->TbtWakeupSupport) {
//Wake Disabled, GO2SX_NO_WAKE Command
TbtSetPcie2TbtCommand (PCIE2TBT_GO2SX_NO_WAKE, HoustRouteBus, 0, 0, TBT_5S_TIMEOUT);
} else {
//Wake Enabled, GO2SX Command
TbtSetPcie2TbtCommand (PCIE2TBT_GO2SX, HoustRouteBus, 0, 0, TBT_5S_TIMEOUT);
}
}
}
*PowerState = PowerStatePrev;
//
// Restore the bus number in case we assigned temporarily
//
if (SecSubBusAssigned) {
PciSegmentWrite8 (DeviceBaseAddress + PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET, 0x00);
PciSegmentWrite8 (DeviceBaseAddress + PCI_BRIDGE_SUBORDINATE_BUS_REGISTER_OFFSET, 0x00);
}
if (gDTbtPcieRstSupport) {
GpioWrite (TbtPcieRstGpioNo,TbtPcieRstGpioLevel);
}
return EFI_SUCCESS;
}
VOID
ThunderboltSwSmiCallback (
IN UINT8 Type
)
{
UINT8 ThunderboltSmiFunction;
DEBUG ((DEBUG_INFO, "ThunderboltSwSmiCallback Entry\n"));
ThunderboltSmiFunction = mTbtNvsAreaPtr->ThunderboltSmiFunction;
DEBUG ((DEBUG_INFO, "ThunderboltSwSmiCallback. ThunderboltSmiFunction=%d\n", ThunderboltSmiFunction));
if (Type == DTBT_CONTROLLER) {
gCurrentDiscreteTbtRootPort = mTbtNvsAreaPtr->CurrentDiscreteTbtRootPort;
gCurrentDiscreteTbtRootPortType = mTbtNvsAreaPtr->CurrentDiscreteTbtRootPortType;
}
switch (ThunderboltSmiFunction) {
case 21:
ThunderboltCallback (Type);
break;
case 22:
TbtDisablePCIDevicesAndBridges (Type);
break;
case 23:
ConfigureTbtAspm (Type, (UINT16) 0x02);
break;
case 24:
ConfigureTbtAspm (Type, (UINT16) 0x01);
break;
default:
break;
}
DEBUG ((DEBUG_INFO, "ThunderboltSwSmiCallback Exit.\n"));
}
STATIC
EFI_STATUS
EFIAPI
DiscreteThunderboltSwSmiCallback (
IN EFI_HANDLE DispatchHandle,
IN CONST VOID *DispatchContext,
IN OUT VOID *CommBuffer OPTIONAL,
IN UINTN *CommBufferSize OPTIONAL
)
{
ThunderboltSwSmiCallback(DTBT_CONTROLLER);
return EFI_SUCCESS;
}
EFI_STATUS
TbtRegisterHandlers (
IN BOOLEAN Type
)
{
EFI_STATUS Status;
UINTN SmiInputValue;
EFI_SMM_HANDLER_ENTRY_POINT2 SxHandler;
EFI_SMM_HANDLER_ENTRY_POINT2 SwHandler;
EFI_SMM_SX_DISPATCH2_PROTOCOL *SxDispatchProtocol;
EFI_SMM_SW_DISPATCH2_PROTOCOL *SwDispatch;
EFI_SMM_SX_REGISTER_CONTEXT EntryDispatchContext;
EFI_SMM_SW_REGISTER_CONTEXT SwContext;
EFI_HANDLE SwDispatchHandle;
EFI_HANDLE S3DispatchHandle;
EFI_HANDLE S4DispatchHandle;
EFI_HANDLE S5DispatchHandle;
Status = EFI_UNSUPPORTED;
if(Type == DTBT_CONTROLLER) {
SxHandler = SxDTbtEntryCallback;
SwHandler = DiscreteThunderboltSwSmiCallback;
SmiInputValue = PcdGet8 (PcdSwSmiDTbtEnumerate);
gDTbtPcieRstSupport = gTbtInfoHob->DTbtCommonConfig.PcieRstSupport;
Status = EFI_SUCCESS;
}
if (EFI_ERROR (Status)) {
return Status;
}
SwDispatchHandle = NULL;
S3DispatchHandle = NULL;
S4DispatchHandle = NULL;
S5DispatchHandle = NULL;
Status = gSmst->SmmLocateProtocol (
&gEfiSmmSxDispatch2ProtocolGuid,
NULL,
(VOID **) &SxDispatchProtocol
);
ASSERT_EFI_ERROR (Status);
//
// Register S3 entry phase call back function
//
EntryDispatchContext.Type = SxS3;
EntryDispatchContext.Phase = SxEntry;
Status = SxDispatchProtocol->Register (
SxDispatchProtocol,
SxHandler,
&EntryDispatchContext,
&S3DispatchHandle
);
ASSERT_EFI_ERROR (Status);
//
// Register S4 entry phase call back function
//
EntryDispatchContext.Type = SxS4;
EntryDispatchContext.Phase = SxEntry;
Status = SxDispatchProtocol->Register (
SxDispatchProtocol,
SxHandler,
&EntryDispatchContext,
&S4DispatchHandle
);
ASSERT_EFI_ERROR (Status);
//
// Register S5 entry phase call back function
//
EntryDispatchContext.Type = SxS5;
EntryDispatchContext.Phase = SxEntry;
Status = SxDispatchProtocol->Register (
SxDispatchProtocol,
SxHandler,
&EntryDispatchContext,
&S5DispatchHandle
);
ASSERT_EFI_ERROR (Status);
//
// Locate the SMM SW dispatch protocol
//
Status = gSmst->SmmLocateProtocol (
&gEfiSmmSwDispatch2ProtocolGuid,
NULL,
(VOID **) &SwDispatch
);
ASSERT_EFI_ERROR (Status);
//
// Register SWSMI handler
//
SwContext.SwSmiInputValue = SmiInputValue;
Status = SwDispatch->Register (
SwDispatch,
SwHandler,
&SwContext,
&SwDispatchHandle
);
ASSERT_EFI_ERROR (Status);
return Status;
}
EFI_STATUS
InSmmFunction (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
Status = EFI_SUCCESS;
Status = TbtRegisterHandlers(DTBT_CONTROLLER);
return Status;
}
EFI_STATUS
EFIAPI
TbtSmmEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
TBT_NVS_AREA_PROTOCOL *TbtNvsAreaProtocol;
EFI_STATUS Status;
DEBUG ((DEBUG_INFO, "TbtSmmEntryPoint\n"));
mPciExpressBaseAddress = PcdGet64 (PcdPciExpressBaseAddress);
//
// Locate Tbt shared data area
//
Status = gBS->LocateProtocol (&gTbtNvsAreaProtocolGuid, NULL, (VOID **) &TbtNvsAreaProtocol);
ASSERT_EFI_ERROR (Status);
mTbtNvsAreaPtr = TbtNvsAreaProtocol->Area;
//
// Get TBT INFO HOB
//
gTbtInfoHob = (TBT_INFO_HOB *) GetFirstGuidHob (&gTbtInfoHobGuid);
if (gTbtInfoHob == NULL) {
return EFI_NOT_FOUND;
}
return InSmmFunction (ImageHandle, SystemTable);
}
VOID
EndOfThunderboltCallback (
IN UINTN RpSegment,
IN UINTN RpBus,
IN UINTN RpDevice,
IN UINTN RpFunction
)
{
if(mTbtNvsAreaPtr->TbtL1SubStates != 0) {
ThunderboltEnableL1Sub (mTbtNvsAreaPtr->TbtL1SubStates, RpSegment, RpBus, RpDevice, RpFunction);
}
ConfigureTbtPm(RpSegment, RpBus, RpDevice, RpFunction, 1);
if (!mTbtNvsAreaPtr->TbtAspm) { //Aspm disable case
ThunderboltDisableAspmWithoutLtr (RpSegment, RpBus, RpDevice, RpFunction);
} else { //Aspm enable case
ThunderboltEnableAspmWithoutLtr ((UINT16)mTbtNvsAreaPtr->TbtAspm, RpSegment, RpBus, RpDevice, RpFunction);
}
if (mTbtNvsAreaPtr->TbtLtr) {
ThunderboltGetLatencyLtr ();
ThunderboltSetLatencyLtr (RpSegment, RpBus, RpDevice, RpFunction);
}
ConfigureLtr (RpSegment, RpBus, RpDevice, RpFunction);
ConfigureTbtPm(RpSegment, RpBus, RpDevice, RpFunction, 2);
} // EndOfThunderboltCallback
VOID
ConfigureTbtAspm (
IN UINT8 Type,
IN UINT16 Aspm
)
{
UINTN RpSegment = 0;
UINTN RpBus = 0;
UINTN RpDevice;
UINTN RpFunction;
if(Type == DTBT_CONTROLLER) {
if (gCurrentDiscreteTbtRootPort == 0) {
return;
}
GetDTbtRpDevFun(DTBT_CONTROLLER, gCurrentDiscreteTbtRootPort - 1, &RpDevice, &RpFunction);
ConfigureTbtPm (RpSegment, RpBus, RpDevice, RpFunction, 1);
if (!mTbtNvsAreaPtr->TbtAspm) { //Aspm disable case
ThunderboltDisableAspmWithoutLtr (RpSegment, RpBus, RpDevice, RpFunction);
} else { //Aspm enable case
ThunderboltEnableAspmWithoutLtr ((UINT16) Aspm, RpSegment, RpBus, RpDevice, RpFunction);
}
if (mTbtNvsAreaPtr->TbtLtr) {
ThunderboltGetLatencyLtr ();
ThunderboltSetLatencyLtr (RpSegment, RpBus, RpDevice, RpFunction);
}
ConfigureLtr (RpSegment, RpBus, RpDevice, RpFunction);
} // EndOfThunderboltCallback
}