mirror of https://github.com/acidanthera/audk.git
939 lines
31 KiB
C
939 lines
31 KiB
C
/** @file
|
|
QNC PCI Express initialization entry
|
|
|
|
Copyright (c) 2013-2015 Intel Corporation.
|
|
|
|
This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which 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.
|
|
|
|
**/
|
|
|
|
#include "CommonHeader.h"
|
|
|
|
#define PCIEXP_ROOT_PORT_URE_ENABLE BIT0 // unsupported request reporting enable
|
|
#define PCIEXP_ROOT_PORT_FEE_ENABLE BIT1 // Fatal Error Reporting Enable
|
|
#define PCIEXP_ROOT_PORT_NFE_ENABLE BIT2 // Non-Fatal Error Reporting Enable
|
|
#define PCIEXP_ROOT_PORT_CEE_ENABLE BIT3 // Correctable Error Reporting Enable
|
|
#define PCIEXP_ROOT_PORT_SFE_ENABLE BIT4 // System Error on Fatal Error Enable
|
|
#define PCIEXP_ROOT_PORT_SNE_ENABLE BIT5 // System Error on Non-Fatal Error Enable
|
|
#define PCIEXP_ROOT_PORT_SCE_ENABLE BIT6 // System Error on Correctable Error Enable
|
|
|
|
EFI_STATUS
|
|
PcieStall (
|
|
IN UINTN Microseconds
|
|
)
|
|
{
|
|
MicroSecondDelay (Microseconds);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
|
|
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 Bus number of the interested device
|
|
@param[in] Device Device number of the interested device
|
|
@param[in] Function Function number of the interested device
|
|
@param[in] CapId Capability ID to be scanned
|
|
|
|
@retval Offset of desired CAPID
|
|
|
|
**/
|
|
UINT32
|
|
PcieFindCapId (
|
|
UINT8 Bus,
|
|
UINT8 Device,
|
|
UINT8 Function,
|
|
UINT8 CapId
|
|
)
|
|
{
|
|
UINT8 CapHeader;
|
|
|
|
//
|
|
// Always start at Offset 0x34
|
|
//
|
|
CapHeader = QNCMmPci8 (0, Bus, Device, Function, R_QNC_PCIE_CAP_PTR);
|
|
|
|
if (CapHeader == 0xFF) {
|
|
return 0;
|
|
}
|
|
|
|
while (CapHeader != 0) {
|
|
if (QNCMmPci8 (0, Bus, Device, Function, CapHeader) == CapId) {
|
|
return CapHeader;
|
|
}
|
|
CapHeader = QNCMmPci8 (0, Bus, Device, Function, CapHeader + 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
|
|
Search and return the offset of desired Pci Express Capability ID
|
|
CAPID list:
|
|
0x0001 = Advanced Error Rreporting Capability
|
|
0x0002 = Virtual Channel Capability
|
|
0x0003 = Device Serial Number Capability
|
|
0x0004 = Power Budgeting Capability
|
|
|
|
@param[in] Bus Bus number of the interested device
|
|
@param[in] Device Device number of the interested device
|
|
@param[in] Function Function number of the interested device
|
|
@param[in] CapId Capability ID to be scanned
|
|
|
|
@retval Offset of desired CAPID
|
|
|
|
**/
|
|
UINT32
|
|
PcieFindExtendedCapId (
|
|
UINT8 Bus,
|
|
UINT8 Device,
|
|
UINT8 Function,
|
|
UINT16 CapId
|
|
)
|
|
{
|
|
UINT16 CapHeaderOffset;
|
|
UINT16 CapHeaderId;
|
|
|
|
// Start to search at Offset 0x100
|
|
// Get Capability Header
|
|
CapHeaderId = 0;
|
|
CapHeaderOffset = PCIE_CAP_EXT_HEARDER_OFFSET;
|
|
|
|
while (CapHeaderOffset != 0 && CapHeaderId != 0xFFFF) {
|
|
CapHeaderId = QNCMmPci16 (0, Bus, Device, Function, CapHeaderOffset);
|
|
if (CapHeaderId == CapId) {
|
|
return CapHeaderOffset;
|
|
}
|
|
CapHeaderOffset = (QNCMmPci16 (0, Bus, Device, Function, CapHeaderOffset + 2) >> 4);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
|
|
Map Vc on both root port and downstream device
|
|
|
|
@param[in] Bus1 Bus number of the root port
|
|
@param[in] Device1 Device number of the root port
|
|
@param[in] Function1 Function number of the root port
|
|
@param[in] Bus2 Bus number of the downstream device
|
|
@param[in] Device2 Device number of the downstream device
|
|
@param[in] Function2 Function number of the downstream device
|
|
|
|
@retval EFI_SUCCESS Map Vc successful
|
|
|
|
**/
|
|
EFI_STATUS
|
|
PcieInitTcxVc0 (
|
|
IN UINT8 Bus1,
|
|
IN UINT8 Device1,
|
|
IN UINT8 Function1,
|
|
IN UINT8 Bus2,
|
|
IN UINT8 Device2,
|
|
IN UINT8 Function2
|
|
)
|
|
{
|
|
UINT32 Offset;
|
|
|
|
//
|
|
// Initialize TCx-VC0 value on the port to only use TC0
|
|
//
|
|
Offset = PcieFindExtendedCapId (Bus1, Device1, Function1, 2);
|
|
if (Offset == 0) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
QNCMmPci8AndThenOr (0, Bus1, Device1, Function1, (Offset + PCIE_SLOT_CAP_OFFSET), ~0xF, 1);
|
|
|
|
// Set TCx-VC0 value on the Endpoint
|
|
|
|
Offset = PcieFindExtendedCapId (Bus2, Device2, Function2, 2);
|
|
if (Offset == 0) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
QNCMmPci8AndThenOr (0, Bus2, Device2, Function2, (Offset + PCIE_SLOT_CAP_OFFSET), ~0xF, 1);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
|
|
Map Traffic Class x to Vc0 on both root port and downstream device
|
|
|
|
@param[in] Bus1 Bus number of the root port
|
|
@param[in] Device1 Device number of the root port
|
|
@param[in] Function1 Function number of the root port
|
|
@param[in] Bus2 Bus number of the downstream device
|
|
@param[in] Device2 Device number of the downstream device
|
|
@param[in] Function2 Function number of the downstream device
|
|
@param[in] TCx Traffic Class to be mapped to vc0
|
|
|
|
@retval EFI_SUCCESS Map Tcx to Vc0 successful
|
|
|
|
**/
|
|
EFI_STATUS
|
|
PcieMapTcxVc0 (
|
|
IN UINT8 Bus1,
|
|
IN UINT8 Device1,
|
|
IN UINT8 Function1,
|
|
IN UINT8 Bus2,
|
|
IN UINT8 Device2,
|
|
IN UINT8 Function2,
|
|
IN UINT8 TCx
|
|
)
|
|
{
|
|
UINT32 Offset;
|
|
|
|
//
|
|
// Set TCx-VC0 value on the port
|
|
//
|
|
|
|
Offset = PcieFindExtendedCapId (Bus1, Device1, Function1, 2);
|
|
if (Offset == 0) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
QNCMmPci8 (0, Bus1, Device1, Function1, (Offset + PCIE_SLOT_CAP_OFFSET)) = (UINT8)(1 << TCx);
|
|
|
|
// Set TCx-VC0 value on the Endpoint
|
|
|
|
Offset = PcieFindExtendedCapId (Bus2, Device2, Function2, 2);
|
|
if (Offset == 0) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
QNCMmPci8 (0, Bus2, Device2, Function2, (Offset + PCIE_SLOT_CAP_OFFSET)) = (UINT8)(1 << TCx);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
|
|
Set common clock for both root port and downstream device.
|
|
|
|
@param[in] Bus1 Bus number of the root port
|
|
@param[in] Device1 Device number of the root port
|
|
@param[in] Function1 Function number of the root port
|
|
@param[in] Bus2 Device number of the downstream device
|
|
@param[in] Device2 Function number of the downstream device
|
|
|
|
@retval EFI_SUCCESS Set common clock successful
|
|
|
|
**/
|
|
EFI_STATUS
|
|
PcieSetCommonClock (
|
|
IN UINT8 Bus1,
|
|
IN UINT8 Device1,
|
|
IN UINT8 Function1,
|
|
IN UINT8 Bus2,
|
|
IN UINT8 Device2
|
|
)
|
|
{
|
|
UINT32 CapOffset1;
|
|
UINT32 CapOffset2;
|
|
UINT8 Function2;
|
|
UINT8 CommonClock;
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// Get the pointer to the Port PCI Express Capability Structure.
|
|
//
|
|
CommonClock = 0;
|
|
CapOffset1 = PcieFindCapId (Bus1, Device1, Function1, PCIE_CAPID);
|
|
if (CapOffset1 == 0) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Step 1
|
|
// Read the Slot Clock Configuration bit of the Link status register of the root port and the endpoint device connected to the port
|
|
// If both components have this bit set to 1, then System BIOS should set the "Common Clock Configuration" bit in the Link Control Registers
|
|
// for both components at both sides of the link to indicate that components at both ends
|
|
// of the link use a common clock source
|
|
//
|
|
|
|
//
|
|
// Check the Port Slot Clock Configuration Bit.
|
|
//
|
|
if ((QNCMmPci16 (0, Bus1, Device1, Function1, (CapOffset1 + PCIE_LINK_STS_OFFSET)) & B_QNC_PCIE_LSTS_SCC) == 0) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
for (Function2 = 0; Function2 < 8; Function2++) {
|
|
//
|
|
// Check the Endpoint Slot Clock Configuration Bit.
|
|
//
|
|
CapOffset2 = PcieFindCapId (Bus2, Device2, Function2, PCIE_CAPID);
|
|
if ((CapOffset2 != 0) &&
|
|
((QNCMmPci16 (0, Bus2, Device2, Function2, (CapOffset2 + PCIE_LINK_STS_OFFSET)) & B_QNC_PCIE_LSTS_SCC) != 0)) {
|
|
|
|
//
|
|
// Common clock is supported, set common clock bit on root port
|
|
// and the endpoint
|
|
//
|
|
if (CommonClock == 0) {
|
|
QNCMmPci8Or (0, Bus1, Device1, Function1, (CapOffset1 + PCIE_LINK_CNT_OFFSET), B_QNC_PCIE_LCTL_CCC);
|
|
CommonClock++;
|
|
}
|
|
QNCMmPci8Or (0, Bus2, Device2, Function2, (CapOffset2 + PCIE_LINK_CNT_OFFSET), B_QNC_PCIE_LCTL_CCC);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Step 2 If the Common Clock Configuration bit was changed by BIOS in step 1,
|
|
// System BIOS should initiate a link training by setting the Retrain Link bit
|
|
// in the Link Control register of the root port (D28:F0/F1 offset
|
|
// 50h [5]) to "1b" and then poll the Link Training bit in the Link Status
|
|
// register of the root port (D28:F0/F1/F2/F3/F4/F5 offset 52h [11]) until it is
|
|
// "0b".
|
|
//
|
|
if (CommonClock == 0) {
|
|
Status = EFI_UNSUPPORTED;
|
|
} else {
|
|
//
|
|
// Retrain the Link per PCI Express Specification.
|
|
//
|
|
QNCMmPci8Or (0, Bus1, Device1, Function1, (CapOffset1 + PCIE_LINK_CNT_OFFSET), B_QNC_PCIE_LCTL_RL);
|
|
|
|
//
|
|
// Wait until Re-Training has completed.
|
|
//
|
|
while ((QNCMmPci16 (0, Bus1, Device1, Function1, (CapOffset1 + PCIE_LINK_STS_OFFSET)) & B_QNC_PCIE_LSTS_LT) != 0);
|
|
Status = EFI_SUCCESS;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
|
|
Enables the CLKREQ# PM on all the end point functions
|
|
|
|
@param[in] Bus Bus number of the downstream device
|
|
@param[in] Device Device number of the downstream device
|
|
|
|
@retval None
|
|
|
|
**/
|
|
VOID
|
|
PcieSetClkreq (
|
|
IN UINT8 Bus,
|
|
IN UINT8 Device
|
|
)
|
|
{
|
|
UINT8 Function;
|
|
UINT32 CapOffset;
|
|
|
|
//
|
|
// Parse thro all the functions of the endpoint and find the PCIe Cap ID (offset 10h) and if
|
|
// exists then enable the CLKREQ# bit (BIT8) on that function
|
|
//
|
|
for (Function = 0; Function < 8; Function++) {
|
|
//
|
|
// Find the PCIe Cap Id (offset 10h)
|
|
//
|
|
CapOffset = PcieFindCapId (Bus, Device, Function, PCIE_CAPID);
|
|
if (CapOffset == 0) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check if CLKREQ# is supported by the endpoints
|
|
//
|
|
if ((QNCMmPci32 (0, Bus, Device, Function, (CapOffset + PCIE_LINK_CAP_OFFSET))
|
|
& B_QNC_PCIE_LCAP_CPM) != B_QNC_PCIE_LCAP_CPM) {
|
|
//
|
|
// CLKREQ# is not supported so dont do anything
|
|
//
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now enable the CLKREQ#
|
|
//
|
|
for (Function = 0; Function < 8; Function++) {
|
|
//
|
|
// Find the PCIe Cap Id (offset 10h)
|
|
//
|
|
CapOffset = PcieFindCapId (Bus, Device, Function, PCIE_CAPID);
|
|
if (CapOffset == 0) {
|
|
continue;
|
|
}
|
|
|
|
QNCMmPci16Or (0, Bus, Device, Function, (CapOffset + PCIE_LINK_CNT_OFFSET), BIT8);
|
|
}
|
|
}
|
|
|
|
/**
|
|
|
|
Configure ASPM automatically for both root port and downstream device.
|
|
|
|
@param[in] RootBus Bus number of the root port
|
|
@param[in] RootDevice Device number of the root port
|
|
@param[in] RootFunction Function number of the root port
|
|
@param[in] EndpointBus Bus number of the downstream device
|
|
@param[in] EndpointDevice Device number of the downstream device
|
|
@param[in] EndpointFunction Function number of the downstream device
|
|
@param[in] LinkAspmVal Currently used ASPM setting
|
|
|
|
@retval EFI_SUCCESS Configure ASPM successful
|
|
|
|
**/
|
|
EFI_STATUS
|
|
PcieSetAspmAuto (
|
|
IN UINT8 RootBus,
|
|
IN UINT8 RootDevice,
|
|
IN UINT8 RootFunction,
|
|
IN UINT8 EndpointBus,
|
|
IN UINT8 EndpointDevice,
|
|
IN UINT8 EndpointFunction,
|
|
OUT UINT16 *LinkAspmVal
|
|
)
|
|
{
|
|
UINT32 RootPcieCapOffset;
|
|
UINT32 EndpointPcieCapOffset;
|
|
UINT16 RootPortAspm;
|
|
UINT16 EndPointAspm;
|
|
UINT16 AspmVal;
|
|
UINT32 PortLxLat;
|
|
UINT32 EndPointLxLat;
|
|
UINT32 LxLat;
|
|
|
|
//
|
|
// Get the pointer to the Port PCI Express Capability Structure.
|
|
//
|
|
RootPcieCapOffset = PcieFindCapId (RootBus, RootDevice, RootFunction, PCIE_CAPID);
|
|
if (RootPcieCapOffset == 0) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Get the pointer to the Endpoint PCI Express Capability Structure.
|
|
//
|
|
EndpointPcieCapOffset = PcieFindCapId (EndpointBus, EndpointDevice, EndpointFunction, PCIE_CAPID);
|
|
if (EndpointPcieCapOffset == 0) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Obtain initial ASPM settings from respective port capability registers.
|
|
//
|
|
RootPortAspm = (QNCMmPci16 (0, RootBus, RootDevice, RootFunction, (RootPcieCapOffset + PCIE_LINK_CAP_OFFSET)) & B_QNC_PCIE_LCAP_APMS_MASK) >> V_QNC_PCIE_LCAP_APMS_OFFSET;
|
|
|
|
//
|
|
// Configure downstream device if present.
|
|
//
|
|
EndPointAspm = (QNCMmPci16 (0, EndpointBus, EndpointDevice, EndpointFunction, (EndpointPcieCapOffset + PCIE_LINK_CAP_OFFSET)) & B_QNC_PCIE_LCAP_APMS_MASK) >> V_QNC_PCIE_LCAP_APMS_OFFSET;
|
|
|
|
//
|
|
// TODO: Mask APMC with values from lookup table.
|
|
// RevID of 0xFF applies to all steppings.
|
|
//
|
|
|
|
// TODO: Mask with latency/acceptable latency comparison results.
|
|
|
|
AspmVal = RootPortAspm;
|
|
if (RootPortAspm > EndPointAspm) {
|
|
AspmVal = EndPointAspm;
|
|
}
|
|
|
|
//
|
|
// Check if L1 should be enabled based on port and endpoint L1 exit latency.
|
|
//
|
|
if(AspmVal & BIT1) {
|
|
PortLxLat = QNCMmPci32 (0, RootBus, RootDevice, RootFunction, (RootPcieCapOffset + PCIE_LINK_CAP_OFFSET)) & B_QNC_PCIE_LCAP_EL1_MASK;
|
|
EndPointLxLat = QNCMmPci32 (0, EndpointBus, EndpointDevice, EndpointFunction, (EndpointPcieCapOffset + PCIE_LINK_CAP_OFFSET)) & B_QNC_PCIE_LCAP_EL1_MASK;
|
|
|
|
LxLat = PortLxLat;
|
|
if(PortLxLat < EndPointLxLat) {
|
|
LxLat = EndPointLxLat;
|
|
}
|
|
|
|
//
|
|
// check if the value is bigger than endpoint L1 acceptable exit latency, if it is
|
|
// larger than accepted value, then we should disable L1
|
|
//
|
|
LxLat >>= 6;
|
|
if(LxLat > (QNCMmPci32 (0, EndpointBus, EndpointDevice, EndpointFunction, (EndpointPcieCapOffset + PCIE_DEV_CAP_OFFSET)) & B_QNC_PCIE_DCAP_E1AL)) {
|
|
AspmVal &= ~BIT1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if L0s should be enabled based on port and endpoint L0s exit latency.
|
|
//
|
|
if(AspmVal & BIT0) {
|
|
PortLxLat = QNCMmPci32 (0, RootBus, RootDevice, RootFunction, (RootPcieCapOffset+ PCIE_LINK_CAP_OFFSET)) & B_QNC_PCIE_LCAP_EL0_MASK;
|
|
EndPointLxLat = QNCMmPci32 (0, EndpointBus, EndpointDevice, EndpointFunction, (EndpointPcieCapOffset + PCIE_LINK_CAP_OFFSET)) & B_QNC_PCIE_LCAP_EL0_MASK;
|
|
|
|
LxLat = PortLxLat;
|
|
if(PortLxLat < EndPointLxLat) {
|
|
LxLat = EndPointLxLat;
|
|
}
|
|
|
|
//
|
|
// check if the value is bigger than endpoint L0s acceptable exit latency, if it is
|
|
// larger than accepted value, then we should disable L0s
|
|
//
|
|
LxLat >>= 6;
|
|
if(LxLat > (QNCMmPci32 (0, EndpointBus, EndpointDevice, EndpointFunction, (EndpointPcieCapOffset + PCIE_DEV_CAP_OFFSET)) & B_QNC_PCIE_DCAP_E0AL)) {
|
|
AspmVal &= ~BIT0;
|
|
}
|
|
}
|
|
|
|
RootPortAspm = AspmVal;
|
|
|
|
*LinkAspmVal = AspmVal;
|
|
//
|
|
// Set Endpoint Aspm
|
|
//
|
|
QNCMmPci16AndThenOr (0, EndpointBus, EndpointDevice, EndpointFunction, (EndpointPcieCapOffset + PCIE_LINK_CNT_OFFSET), 0xFFFC, AspmVal);
|
|
|
|
|
|
//
|
|
// Set Root Port Aspm
|
|
//
|
|
QNCMmPci16AndThenOr (0, RootBus, RootDevice, RootFunction, (RootPcieCapOffset + PCIE_LINK_CNT_OFFSET), 0xFFFC, RootPortAspm);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
|
|
Configure ASPM based on the given setting for the interested device.
|
|
|
|
@param[in] Bus Bus number of the interested device
|
|
@param[in] Device Device number of the interested device
|
|
@param[in] Function Function number of the interested device
|
|
@param[in] AspmSetting Aspm setting
|
|
@param[in] LinkAspmVal Currently used ASPM setting
|
|
|
|
@retval EFI_SUCCESS Configure ASPM successful
|
|
|
|
**/
|
|
EFI_STATUS
|
|
PcieSetAspmManual (
|
|
IN UINT8 Bus,
|
|
IN UINT8 Device,
|
|
IN UINT8 Function,
|
|
IN UINT8 AspmSetting,
|
|
OUT UINT16 *LinkAspmVal
|
|
)
|
|
{
|
|
UINT32 PcieCapOffset;
|
|
UINT16 PortAspm;
|
|
|
|
//
|
|
// Get the pointer to the Port PCI Express Capability Structure.
|
|
//
|
|
PcieCapOffset = PcieFindCapId (Bus, Device, Function, PCIE_CAPID);
|
|
if (PcieCapOffset == 0) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
// Read the Link Capability register's ASPM setting
|
|
PortAspm = (QNCMmPci16 (0, Bus, Device, Function, (PcieCapOffset + PCIE_LINK_CAP_OFFSET)) & B_QNC_PCIE_LCAP_APMS_MASK) >> V_QNC_PCIE_LCAP_APMS_OFFSET;
|
|
// Mask it with the Setup selection
|
|
PortAspm &= AspmSetting;
|
|
|
|
*LinkAspmVal = PortAspm;
|
|
// Write it to the Link Control register
|
|
QNCMmPci16AndThenOr (0, Bus, Device, Function, (PcieCapOffset + PCIE_LINK_CNT_OFFSET), 0xFFFC, PortAspm);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
|
|
Perform Initialization on one PCI Express root port.
|
|
|
|
@param[in] RootPortIndex Index of PCI Express root port
|
|
@param[in] RootPortConfig Pointer to the given pcie root port configuration
|
|
@param[in] PciExpressBar Base address of pcie space
|
|
@param[in] QNCRootComplexBar Base address of root complex
|
|
@param[in] QNCPmioBase Base address of PM IO space
|
|
@param[in] QNCGpeBase Base address of gpe IO space
|
|
|
|
@retval EFI_SUCCESS Initialization successful
|
|
|
|
**/
|
|
EFI_STATUS
|
|
QNCRootPortInit (
|
|
IN UINT32 RootPortIndex,
|
|
IN PCIEXP_ROOT_PORT_CONFIGURATION *RootPortConfig,
|
|
IN UINT64 PciExpressBar,
|
|
IN UINT32 QNCRootComplexBar,
|
|
IN UINT32 QNCPmioBase,
|
|
IN UINT32 QNCGpeBase
|
|
)
|
|
{
|
|
UINT64 RPBase;
|
|
UINT64 EndPointBase;
|
|
UINT16 AspmVal;
|
|
UINT16 SlotStatus;
|
|
UINTN Index;
|
|
UINT32 CapOffset;
|
|
UINT32 DwordReg;
|
|
|
|
RPBase = PciExpressBar + (((PCI_BUS_NUMBER_QNC << 8) + ((PCI_DEVICE_NUMBER_PCIE_ROOTPORT) << 3) + ((PCI_FUNCTION_NUMBER_PCIE_ROOTPORT_0 + RootPortIndex) << 0)) << 12);
|
|
CapOffset = PcieFindCapId (PCI_BUS_NUMBER_QNC, (UINT8)(PCI_DEVICE_NUMBER_PCIE_ROOTPORT), (UINT8)(PCI_FUNCTION_NUMBER_PCIE_ROOTPORT_0 + RootPortIndex), PCIE_CAPID);
|
|
|
|
if (CapOffset == 0) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Initialize "Slot Implmemented Bit" for this root port
|
|
//
|
|
if (RootPortConfig[RootPortIndex].Bits.SlotImplemented) {
|
|
QNCMmio16Or (RPBase, R_QNC_PCIE_XCAP, B_QNC_PCIE_XCAP_SI);
|
|
}
|
|
|
|
//
|
|
// For Root Port Slots Numbering on the CRBs.
|
|
// Root Port 0 = Slot 1
|
|
// Root Port 1 = Slot 2
|
|
// Root Port 2 = Slot 3
|
|
// Root Port 3 = Slot 4
|
|
//
|
|
DwordReg = QNCMmio32 (RPBase, R_QNC_PCIE_SLCAP);
|
|
DwordReg &= B_QNC_PCIE_SLCAP_MASK_RSV_VALUE;
|
|
DwordReg |= (V_QNC_PCIE_SLCAP_SLV << V_QNC_PCIE_SLCAP_SLV_OFFSET);
|
|
DwordReg |= ((RootPortConfig[RootPortIndex].Bits.PhysicalSlotNumber) << V_QNC_PCIE_SLCAP_PSN_OFFSET) ;
|
|
QNCMmio32 (RPBase, R_QNC_PCIE_SLCAP) = DwordReg;
|
|
|
|
//
|
|
// Check for a Presence Detect Change.
|
|
//
|
|
SlotStatus = QNCMmio16 (RPBase, R_QNC_PCIE_SLSTS);
|
|
if ((SlotStatus & (B_QNC_PCIE_SLSTS_PDS + B_QNC_PCIE_SLSTS_PDC)) == 0) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// Temporarily Hardcode the Root Port Bridge Number to 2.
|
|
//
|
|
// This Endpoint check should immediately pass. Howerver, a 900ms delay
|
|
// has been added to match the timing requirements of the PCI Express Base
|
|
// Specification, Revision 1.0A, Section 6.6 ("...software must allow 1.0s
|
|
// after a reset of a device, before it may determine that a device which
|
|
// fails to return a Successful Completion status for a valid Configuration
|
|
// Request is a broken device"). Note that a 100ms delay was already added
|
|
// after the Root Ports were first taken out of reset.
|
|
//
|
|
QNCMmio32AndThenOr (RPBase, R_QNC_PCIE_BNUM, 0xFF0000FF, 0x00020200);
|
|
//
|
|
// Only do this when a downstream device is present
|
|
//
|
|
EndPointBase = PciExpressBar + (((2 << 8) + (0 << 3) + (0 << 0)) << 12);
|
|
if ((SlotStatus & B_QNC_PCIE_SLSTS_PDS) != 0) {
|
|
for (Index = 0; Index < V_PCIE_MAX_TRY_TIMES; Index++){
|
|
if (QNCMmio16 (EndPointBase, 0x0) != 0xFFFF) {
|
|
break;
|
|
}
|
|
PcieStall (15);
|
|
}
|
|
if (Index >= V_PCIE_MAX_TRY_TIMES) {
|
|
//
|
|
// Clear Bus Numbers.
|
|
//
|
|
QNCMmio32And (RPBase, R_QNC_PCIE_BNUM, 0xFF0000FF);
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
//
|
|
// PCI Express* Virtual Channels
|
|
// Clear TC1-7 Traffic classes.
|
|
// Map TC0-VC0
|
|
//
|
|
PcieInitTcxVc0 (PCI_BUS_NUMBER_QNC, (UINT8)(PCI_DEVICE_NUMBER_PCIE_ROOTPORT), (UINT8)(PCI_FUNCTION_NUMBER_PCIE_ROOTPORT_0 + RootPortIndex), 2, 0, 0);
|
|
PcieMapTcxVc0 (PCI_BUS_NUMBER_QNC, (UINT8)(PCI_DEVICE_NUMBER_PCIE_ROOTPORT), (UINT8)(PCI_FUNCTION_NUMBER_PCIE_ROOTPORT_0 + RootPortIndex), 2, 0, 0, 0x0);
|
|
|
|
//
|
|
// Set Common Clock for inserted cards
|
|
//
|
|
if ((SlotStatus & B_QNC_PCIE_SLSTS_PDS) != 0) {
|
|
PcieSetCommonClock (PCI_BUS_NUMBER_QNC, (UINT8)(PCI_DEVICE_NUMBER_PCIE_ROOTPORT), (UINT8)(PCI_FUNCTION_NUMBER_PCIE_ROOTPORT_0 + RootPortIndex), 2, 0);
|
|
}
|
|
|
|
//
|
|
// Flow for Enabling ASPM
|
|
//
|
|
if (RootPortConfig[RootPortIndex].Bits.AspmEnable) {
|
|
if (RootPortConfig[RootPortIndex].Bits.AspmAutoEnable) {
|
|
PcieSetAspmAuto (PCI_BUS_NUMBER_QNC, (UINT8)(PCI_DEVICE_NUMBER_PCIE_ROOTPORT), (UINT8)(PCI_FUNCTION_NUMBER_PCIE_ROOTPORT_0 + RootPortIndex), 2, 0, 0, &AspmVal);
|
|
} else {
|
|
//
|
|
// Set ASPM values according to setup selections, masked by capabilities
|
|
//
|
|
PcieSetAspmManual (
|
|
PCI_BUS_NUMBER_QNC,
|
|
(UINT8) (PCI_DEVICE_NUMBER_PCIE_ROOTPORT),
|
|
(UINT8) (PCI_FUNCTION_NUMBER_PCIE_ROOTPORT_0 + RootPortIndex),
|
|
(UINT8) ((RootPortConfig[RootPortIndex].Bits.AspmL0sEnable & 0x01) | (RootPortConfig[RootPortIndex].Bits.AspmL1Enable << 1)),
|
|
&AspmVal
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Enable the PCIe CLKREQ#
|
|
//
|
|
if ((SlotStatus & B_QNC_PCIE_SLSTS_PDS) != 0) {
|
|
PcieSetClkreq (2, 0);
|
|
}
|
|
|
|
//
|
|
// Clear Bus Numbers
|
|
//
|
|
QNCMmio32And (RPBase, R_QNC_PCIE_BNUM, 0xFF0000FF);
|
|
|
|
//
|
|
// Additional configurations
|
|
//
|
|
|
|
//
|
|
// PCI-E Unsupported Request Reporting Enable
|
|
//
|
|
if (RootPortConfig[RootPortIndex].Bits.PortErrorMask & PCIEXP_ROOT_PORT_URE_ENABLE) {
|
|
QNCMmio16Or (RPBase, R_QNC_PCIE_DCTL, B_QNC_PCIE_DCTL_URE);
|
|
}
|
|
|
|
//
|
|
// Device Fatal Error Reporting Enable
|
|
//
|
|
if (RootPortConfig[RootPortIndex].Bits.PortErrorMask & PCIEXP_ROOT_PORT_FEE_ENABLE) {
|
|
QNCMmio16Or (RPBase, R_QNC_PCIE_DCTL, B_QNC_PCIE_DCTL_FEE);
|
|
}
|
|
|
|
//
|
|
// Device Non Fatal Error Reporting Enable
|
|
//
|
|
if (RootPortConfig[RootPortIndex].Bits.PortErrorMask & PCIEXP_ROOT_PORT_NFE_ENABLE) {
|
|
QNCMmio16Or (RPBase, R_QNC_PCIE_DCTL, B_QNC_PCIE_DCTL_NFE);
|
|
}
|
|
|
|
//
|
|
// Device Correctable Error Reporting Enable
|
|
//
|
|
if (RootPortConfig[RootPortIndex].Bits.PortErrorMask & PCIEXP_ROOT_PORT_CEE_ENABLE) {
|
|
QNCMmio16Or (RPBase, R_QNC_PCIE_DCTL, B_QNC_PCIE_DCTL_CEE);
|
|
}
|
|
//
|
|
// Root PCI-E PME Interrupt Enable
|
|
//
|
|
if (RootPortConfig[RootPortIndex].Bits.PmeInterruptEnable) {
|
|
QNCMmio16Or (RPBase, R_QNC_PCIE_RCTL, B_QNC_PCIE_RCTL_PIE);
|
|
}
|
|
//
|
|
// Root PCI-E System Error on Fatal Error Enable
|
|
//
|
|
if (RootPortConfig[RootPortIndex].Bits.PortErrorMask & PCIEXP_ROOT_PORT_SFE_ENABLE) {
|
|
QNCMmio16Or (RPBase, R_QNC_PCIE_RCTL, B_QNC_PCIE_RCTL_SFE);
|
|
}
|
|
|
|
//
|
|
// Root PCI-E System Error on Non-Fatal Error Enable
|
|
//
|
|
if (RootPortConfig[RootPortIndex].Bits.PortErrorMask & PCIEXP_ROOT_PORT_SNE_ENABLE) {
|
|
QNCMmio16Or (RPBase, R_QNC_PCIE_RCTL, B_QNC_PCIE_RCTL_SNE);
|
|
}
|
|
|
|
//
|
|
// Root PCI-E System Error on Correctable Error Enable
|
|
//
|
|
if (RootPortConfig[RootPortIndex].Bits.PortErrorMask & PCIEXP_ROOT_PORT_SCE_ENABLE) {
|
|
QNCMmio16Or (RPBase, R_QNC_PCIE_RCTL, B_QNC_PCIE_RCTL_SCE);
|
|
}
|
|
|
|
//
|
|
// Root PCI-E Powermanagement SCI Enabled
|
|
//
|
|
if (RootPortConfig[RootPortIndex].Bits.PmSciEnable) {
|
|
//
|
|
// Make sure that PME Interrupt Enable bit of Root Control register
|
|
// of PCI Express Capability struceture is cleared
|
|
//
|
|
QNCMmio32And (RPBase, R_QNC_PCIE_RCTL, (~B_QNC_PCIE_RCTL_PIE));
|
|
QNCMmio32AndThenOr (RPBase, R_QNC_PCIE_MPC, (~B_QNC_PCIE_MPC_PMME), B_QNC_PCIE_MPC_PMCE);
|
|
|
|
//
|
|
// Make sure GPE0 Stutus RW1C Bit is clear.
|
|
//
|
|
DwordReg = IoRead32 (QNCGpeBase + R_QNC_GPE0BLK_GPE0S);
|
|
if ((DwordReg & B_QNC_GPE0BLK_GPE0S_PCIE) != 0) {
|
|
IoWrite32 (QNCGpeBase + R_QNC_GPE0BLK_GPE0S, B_QNC_GPE0BLK_GPE0S_PCIE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// PCIe Hot Plug SCI Enable
|
|
//
|
|
if (RootPortConfig[RootPortIndex].Bits.HotplugSciEnable) {
|
|
//
|
|
// Write clear for :
|
|
// Attention Button Pressed (bit0)
|
|
// Presence Detect Changed (bit3)
|
|
//
|
|
QNCMmio32Or (RPBase, R_QNC_PCIE_SLSTS, (B_QNC_PCIE_SLSTS_PDC | B_QNC_PCIE_SLSTS_ABP));
|
|
|
|
//
|
|
// Sequence 2: Program the following bits in Slot Control register at offset 18h
|
|
// of PCI Express* Capability structure:
|
|
// Attention Button Pressed Enable (bit0) = 1b
|
|
// Presence Detect Changed Enable (bit3) = 1b
|
|
// Hot Plug Interrupt Enable (bit5) = 0b
|
|
//
|
|
QNCMmio32AndThenOr (RPBase, R_QNC_PCIE_SLCTL, (~B_QNC_PCIE_SLCTL_HPE), (B_QNC_PCIE_SLCTL_PDE | B_QNC_PCIE_SLCTL_ABE));
|
|
|
|
//
|
|
// Sequence 3: Program Misc Port Config (MPC) register at PCI config space offset
|
|
// D8h as follows:
|
|
// Hot Plug SCI Enable (HPCE, bit30) = 1b
|
|
// Hot Plug SMI Enable (HPME, bit1) = 0b
|
|
//
|
|
QNCMmio32AndThenOr (RPBase, R_QNC_PCIE_MPC, (~B_QNC_PCIE_MPC_HPME), B_QNC_PCIE_MPC_HPCE);
|
|
}
|
|
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Perform Initialization of the Downstream Root Ports
|
|
**/
|
|
VOID
|
|
QNCDownStreamPortsInit (
|
|
IN PCIEXP_ROOT_PORT_CONFIGURATION *RootPortConfig,
|
|
IN QNC_DEVICE_ENABLES *QNCDeviceEnables,
|
|
IN UINT64 PciExpressBar,
|
|
IN UINT32 QNCRootComplexBar,
|
|
IN UINT32 QNCPmioBase,
|
|
IN UINT32 QNCGpeBase,
|
|
OUT UINTN *RpEnableMask
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT32 Index;
|
|
|
|
//
|
|
// Initialize every root port and downstream device
|
|
//
|
|
for (Index = 0;Index < MAX_PCI_EXPRESS_ROOT_PORTS;Index++) {
|
|
if ((QNCDeviceEnables->Uint32 & (1 << Index)) != 0) {
|
|
Status = QNCRootPortInit (
|
|
Index,
|
|
RootPortConfig,
|
|
PciExpressBar,
|
|
QNCRootComplexBar,
|
|
QNCPmioBase,
|
|
QNCGpeBase
|
|
);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
(*RpEnableMask) |= LShiftU64(1, Index);
|
|
DEBUG ((EFI_D_INFO, " Root Port %x device found, enabled. RpEnableMask: 0x%x\n", Index + 1, *RpEnableMask));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Do early init of pci express rootports on Soc.
|
|
|
|
**/
|
|
|
|
VOID
|
|
EFIAPI
|
|
PciExpressEarlyInit (
|
|
VOID
|
|
)
|
|
{
|
|
//
|
|
// Setup Message Bus Idle Counter (SBIC) values.
|
|
//
|
|
QNCMmPci8(0, PCI_BUS_NUMBER_QNC, PCI_DEVICE_NUMBER_PCIE_ROOTPORT, PCI_FUNCTION_NUMBER_PCIE_ROOTPORT_0, R_QNC_PCIE_IOSFSBCTL) = QNCMmPci8AndThenOr(0, PCI_BUS_NUMBER_QNC, PCI_DEVICE_NUMBER_PCIE_ROOTPORT, PCI_FUNCTION_NUMBER_PCIE_ROOTPORT_0, R_QNC_PCIE_IOSFSBCTL, (~B_QNC_PCIE_IOSFSBCTL_SBIC_MASK), V_PCIE_ROOT_PORT_SBIC_VALUE);
|
|
QNCMmPci8(0, PCI_BUS_NUMBER_QNC, PCI_DEVICE_NUMBER_PCIE_ROOTPORT, PCI_FUNCTION_NUMBER_PCIE_ROOTPORT_1, R_QNC_PCIE_IOSFSBCTL) = QNCMmPci8AndThenOr(0, PCI_BUS_NUMBER_QNC, PCI_DEVICE_NUMBER_PCIE_ROOTPORT, PCI_FUNCTION_NUMBER_PCIE_ROOTPORT_1, R_QNC_PCIE_IOSFSBCTL, (~B_QNC_PCIE_IOSFSBCTL_SBIC_MASK), V_PCIE_ROOT_PORT_SBIC_VALUE);
|
|
|
|
//
|
|
// Program SVID/SID the same as VID/DID for Root ports.
|
|
//
|
|
QNCMmPci32(0, PCI_BUS_NUMBER_QNC, PCI_DEVICE_NUMBER_PCIE_ROOTPORT, PCI_FUNCTION_NUMBER_PCIE_ROOTPORT_0, R_QNC_PCIE_SVID) = QNCMmPci32(0, PCI_BUS_NUMBER_QNC, PCI_DEVICE_NUMBER_PCIE_ROOTPORT, PCI_FUNCTION_NUMBER_PCIE_ROOTPORT_0, PCI_VENDOR_ID_OFFSET);
|
|
QNCMmPci32(0, PCI_BUS_NUMBER_QNC, PCI_DEVICE_NUMBER_PCIE_ROOTPORT, PCI_FUNCTION_NUMBER_PCIE_ROOTPORT_1, R_QNC_PCIE_SVID) = QNCMmPci32(0, PCI_BUS_NUMBER_QNC, PCI_DEVICE_NUMBER_PCIE_ROOTPORT, PCI_FUNCTION_NUMBER_PCIE_ROOTPORT_1, PCI_VENDOR_ID_OFFSET);
|
|
|
|
//
|
|
// Set the IPF bit in MCR2
|
|
//
|
|
QNCMmPci32(0, PCI_BUS_NUMBER_QNC, PCI_DEVICE_NUMBER_PCIE_ROOTPORT, PCI_FUNCTION_NUMBER_PCIE_ROOTPORT_0, R_QNC_PCIE_MPC2) = QNCMmPci32Or(0, PCI_BUS_NUMBER_QNC, PCI_DEVICE_NUMBER_PCIE_ROOTPORT, PCI_FUNCTION_NUMBER_PCIE_ROOTPORT_0, R_QNC_PCIE_MPC2, B_QNC_PCIE_MPC2_IPF);
|
|
QNCMmPci32(0, PCI_BUS_NUMBER_QNC, PCI_DEVICE_NUMBER_PCIE_ROOTPORT, PCI_FUNCTION_NUMBER_PCIE_ROOTPORT_1, R_QNC_PCIE_MPC2) = QNCMmPci32Or(0, PCI_BUS_NUMBER_QNC, PCI_DEVICE_NUMBER_PCIE_ROOTPORT, PCI_FUNCTION_NUMBER_PCIE_ROOTPORT_1, R_QNC_PCIE_MPC2, B_QNC_PCIE_MPC2_IPF);
|
|
|
|
//
|
|
// Set up the Posted and Non Posted Request sizes for PCIe
|
|
//
|
|
QNCMmPci32(0, PCI_BUS_NUMBER_QNC, PCI_DEVICE_NUMBER_PCIE_ROOTPORT, PCI_FUNCTION_NUMBER_PCIE_ROOTPORT_0, R_QNC_PCIE_CCFG) = QNCMmPci32AndThenOr(0, PCI_BUS_NUMBER_QNC, PCI_DEVICE_NUMBER_PCIE_ROOTPORT, PCI_FUNCTION_NUMBER_PCIE_ROOTPORT_0, R_QNC_PCIE_CCFG, ~B_QNC_PCIE_CCFG_UPSD, (B_QNC_PCIE_CCFG_UNRS | B_QNC_PCIE_CCFG_UPRS));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/**
|
|
Complete initialization all the pci express rootports on Soc.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
PciExpressInit (
|
|
)
|
|
{
|
|
UINT64 PciExpressBar;
|
|
UINT32 QNCRootComplexBar;
|
|
UINT32 QNCPmioBase;
|
|
UINT32 QNCGpeBase;
|
|
UINTN RpEnableMask;
|
|
PCIEXP_ROOT_PORT_CONFIGURATION *mRootPortConfig;
|
|
QNC_DEVICE_ENABLES mQNCDeviceEnables;
|
|
|
|
//
|
|
// Get BAR registers
|
|
//
|
|
QNCRootComplexBar = QNC_RCRB_BASE;
|
|
QNCPmioBase = LpcPciCfg32 (R_QNC_LPC_PM1BLK) & B_QNC_LPC_PM1BLK_MASK;
|
|
QNCGpeBase = LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & B_QNC_LPC_GPE0BLK_MASK;
|
|
RpEnableMask = 0; // assume all root ports are disabled
|
|
|
|
PciExpressBar = PcdGet64 (PcdPciExpressBaseAddress);
|
|
|
|
//
|
|
// Get platform information from PCD entries
|
|
//
|
|
mQNCDeviceEnables.Uint32 = PcdGet32 (PcdDeviceEnables);
|
|
mRootPortConfig = (PCIEXP_ROOT_PORT_CONFIGURATION*) PcdGetPtr (PcdPcieRootPortConfiguration);
|
|
|
|
DEBUG ((EFI_D_INFO, " mRootPortConfig: 0x%x, value1: 0x%x, value2: 0x%x, value3: 0x%x, value4: 0x%x\n",
|
|
mRootPortConfig, mRootPortConfig[0].Uint32, mRootPortConfig[1].Uint32,
|
|
mRootPortConfig[2].Uint32, mRootPortConfig[3].Uint32));
|
|
|
|
QNCDownStreamPortsInit (
|
|
mRootPortConfig,
|
|
&mQNCDeviceEnables,
|
|
PciExpressBar,
|
|
QNCRootComplexBar,
|
|
QNCPmioBase,
|
|
QNCGpeBase,
|
|
&RpEnableMask
|
|
);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|