blob: a5e99c90df4dc5cf43479f29348756ad199ffbc5 [file] [log] [blame]
/** @file
QNC PCI Express initialization entry
Copyright (c) 2013 Intel Corporation.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Intel Corporation nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**/
#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 EndPointVendorId;
UINT16 EndPointDeviceId;
UINT8 EndPointRevId;
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;
//
// Mask APMC with values from lookup table.
// RevID of 0xFF applies to all steppings.
//
EndPointVendorId = QNCMmPci16 (0, EndpointBus, EndpointDevice, EndpointFunction, 0);
EndPointDeviceId = QNCMmPci16 (0, EndpointBus, EndpointDevice, EndpointFunction, 2);
EndPointRevId = QNCMmPci8 (0, EndpointBus, EndpointDevice, EndpointFunction, 8);
// 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;
UINT64 LpcBase;
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);
LpcBase = PciExpressBar + (((PCI_BUS_NUMBER_QNC << 8) + (31 << 3) + (0 << 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 QNCGpioBase;
UINT32 QNCPmioBase;
UINT32 QNCGpeBase;
UINTN RpEnableMask;
PCIEXP_ROOT_PORT_CONFIGURATION *mRootPortConfig;
QNC_DEVICE_ENABLES mQNCDeviceEnables;
//
// Get BAR registers
//
QNCRootComplexBar = QNC_RCRB_BASE;
QNCGpioBase = LpcPciCfg32 (R_QNC_LPC_GBA_BASE) & B_QNC_LPC_GPA_BASE_MASK;
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;
}