blob: 6fdf2b120c35be1b63f427729c4a2d340bbc7611 [file] [log] [blame]
/**
* @file
*
* PCIe Early initialization.
*
*
*
* @xrefitem bom "File Content Label" "Release Content"
* @e project: CIMx-NB
* @e sub-project:
* @e \$Revision:$ @e \$Date:$
*
*/
/*****************************************************************************
*
* Copyright (C) 2012 Advanced Micro Devices, Inc.
* All rights reserved.
*
* 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 Advanced Micro Devices, Inc. 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 ADVANCED MICRO DEVICES, INC. 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.
*
*
***************************************************************************/
/*----------------------------------------------------------------------------------------
* M O D U L E S U S E D
*----------------------------------------------------------------------------------------
*/
#include "NbPlatform.h"
#include "amdDebugOutLib.h"
#include "amdSbLib.h"
/*----------------------------------------------------------------------------------------
* D E F I N I T I O N S A N D M A C R O S
*----------------------------------------------------------------------------------------
*/
/*----------------------------------------------------------------------------------------
* T Y P E D E F S A N D S T R U C T U R E S
*----------------------------------------------------------------------------------------
*/
/*----------------------------------------------------------------------------------------
* P R O T O T Y P E S O F L O C A L F U N C T I O N S
*----------------------------------------------------------------------------------------
*/
/*----------------------------------------------------------------------------------------
* E X P O R T E D F U N C T I O N S
*----------------------------------------------------------------------------------------
*/
/*----------------------------------------------------------------------------------------*/
/**
* PCIE Init for all NB.
* Basic programming / EP training. After this call EP are fully operational.
*
*
*
* @param[in] ConfigPtr Northbridges configuration block pointer.
*
*/
/*----------------------------------------------------------------------------------------*/
AGESA_STATUS
AmdPcieEarlyInit (
IN OUT AMD_NB_CONFIG_BLOCK *ConfigPtr
)
{
AGESA_STATUS Status;
Status = LibNbApiCall (PcieEarlyInit, ConfigPtr);
return Status;
}
/*----------------------------------------------------------------------------------------*/
/**
* Northbridge PCIE Init.
* Basic programming / EP training. After this call EP are fully operational on particular NB.
*
*
*
* @param[in] NbConfigPtr Northbridge configuration structure pointer.
*
*/
/*----------------------------------------------------------------------------------------*/
AGESA_STATUS
PcieEarlyInit (
IN OUT AMD_NB_CONFIG *NbConfigPtr
)
{
AGESA_STATUS Status;
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (NbConfigPtr), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieEarlyInit Enter\n"));
Status = PcieLibInitValidateInput (NbConfigPtr);
if (Status == AGESA_FATAL) {
REPORT_EVENT (AGESA_FATAL, GENERAL_ERROR_BAD_CONFIGURATION, 0 , 0, 0, 0, NbConfigPtr);
CIMX_ASSERT (FALSE);
return Status;
}
Status = PciePreTrainingInit (NbConfigPtr);
Status = PcieInitPorts (NbConfigPtr);
Status = PcieAfterTrainingInit (NbConfigPtr);
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (NbConfigPtr), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieEarlyInit Exit [0x%x]\n", Status));
return Status;
}
/*----------------------------------------------------------------------------------------*/
/**
* Misc initialization prior port link training started
*
*
*
* @param[in] pConfig Northbridge configuration structure pointer.
*
*/
/*----------------------------------------------------------------------------------------*/
AGESA_STATUS
PciePreTrainingInit (
IN OUT AMD_NB_CONFIG *pConfig
)
{
PCIE_CONFIG *pPcieConfig;
CORE CoreId ;
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PciePreTrainingInit Enter\n"));
pPcieConfig = GET_PCIE_CONFIG_PTR (pConfig);
//Unhide all ports
PcieLibUnHidePorts (pConfig);
if (pPcieConfig->PcieMmioBaseAddress != 0 && pPcieConfig->PcieMmioSize != 0) {
PcieLibSetPcieMmioBase (pPcieConfig->PcieMmioBaseAddress, pPcieConfig->PcieMmioSize, pConfig);
}
for (CoreId = 0; CoreId <= MAX_CORE_ID; CoreId++) {
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), " CoreId %d CoreSetting = 0x%x\n", CoreId, *((UINT32*)&pConfig->pPcieConfig->CoreSetting[CoreId])));
//if (pPcieConfig->CoreSetting[CoreId].CoreDisabled == OFF) {
//Configure cores
if (pPcieConfig->CoreSetting[CoreId].SkipConfiguration == OFF) {
PcieLibSetCoreConfiguration (CoreId, pConfig);
}
//Init core registers
PcieLibCommonCoreInit (CoreId, pConfig);
//}
}
PcieLibPreTrainingInit (pConfig);
for (CoreId = 0; CoreId <= MAX_CORE_ID; CoreId++) {
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), " CoreId %d CoreSetting = 0x%x\n", CoreId, *((UINT32*)&pConfig->pPcieConfig->CoreSetting[CoreId])));
//Init CPL buffer allocation
//if (pPcieConfig->CoreSetting[CoreId].CoreDisabled == OFF && pPcieConfig->CoreSetting[CoreId].CplBufferAllocation == ON) {
if (pPcieConfig->CoreSetting[CoreId].CplBufferAllocation == ON) {
PcieLibCplBufferAllocation (CoreId, pConfig);
}
}
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PciePreTrainingInit Exit\n"));
return AGESA_SUCCESS;
}
/*----------------------------------------------------------------------------------------*/
/**
* Misc initialization after port training complete
*
*
*
* @param[in] pConfig Northbridge configuration structure pointer.
*
*/
/*----------------------------------------------------------------------------------------*/
AGESA_STATUS
PcieAfterTrainingInit (
IN AMD_NB_CONFIG *pConfig
)
{
PCIE_CONFIG *pPcieConfig;
CORE CoreId;
pPcieConfig = GET_PCIE_CONFIG_PTR (pConfig);
for (CoreId = 0; CoreId <= MAX_CORE_ID; CoreId++) {
// if (pPcieConfig->CoreSetting[CoreId].CoreDisabled == OFF) {
//Configure cores
PcieLibCoreAfterTrainingInit (CoreId, pConfig);
// }
}
//Hide all Ports
PcieLibHidePorts (pConfig);
return AGESA_SUCCESS;
}
/*----------------------------------------------------------------------------------------*/
/**
* Train PCIE Ports
*
*
*
* @param[in] pConfig Northbridge configuration structure pointer.
*
*/
/*----------------------------------------------------------------------------------------*/
AGESA_STATUS
PcieInitPorts (
IN OUT AMD_NB_CONFIG *pConfig
)
{
AGESA_STATUS Status;
PCIE_CONFIG *pPcieConfig;
Status = AGESA_SUCCESS;
pPcieConfig = GET_PCIE_CONFIG_PTR (pConfig);
if (pPcieConfig->DeviceInitMaskS1 != 0) {
Status = PcieInitSelectedPorts (pPcieConfig->DeviceInitMaskS1, pConfig);
}
if (pPcieConfig->DeviceInitMaskS2 != 0) {
Status = PcieInitSelectedPorts (pPcieConfig->DeviceInitMaskS2, pConfig);
}
return Status;
}
/*----------------------------------------------------------------------------------------*/
/**
* Train PCIE Ports selected for this stage
*
*
* @param[in] SelectedPortMask Bitmap of port ID selected for training.
* @param[in] pConfig Northbridge configuration structure pointer.
*
*/
/*----------------------------------------------------------------------------------------*/
AGESA_STATUS
PcieInitSelectedPorts (
IN UINT16 SelectedPortMask,
IN OUT AMD_NB_CONFIG *pConfig
)
{
AGESA_STATUS Status;
PCIE_CONFIG *pPcieConfig;
PORT PortId;
BOOLEAN RequestResetDelay;
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieInitSelectedPorts (Ports = 0x%x) Enter\n", SelectedPortMask));
pPcieConfig = GET_PCIE_CONFIG_PTR (pConfig);
Status = AGESA_SUCCESS;
RequestResetDelay = FALSE;
for (PortId = MIN_PORT_ID; PortId <= MAX_PORT_ID; PortId++) {
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), " PortId %d PortConfiguration = 0x%x ExtPortConfiguration = 0x%x\n", PortId, *((UINT32*)&pPcieConfig->PortConfiguration[PortId]), *((UINT32*)&pPcieConfig->ExtPortConfiguration[PortId])));
if ((SelectedPortMask & (1 << PortId)) != 0) {
if (pPcieConfig->PortConfiguration[PortId].PortPresent == ON && PcieLibIsValidPortId (PortId, pConfig)) {
PCIE_LINK_MODE LinkMode;
//Deassert slot reset. Bring EP out of reset
Status = LibNbCallBack (PHCB_AmdPortResetDeassert, 1 << PortId, pConfig);
if (Status == AGESA_SUCCESS) {
//STALL (GET_BLOCK_CONFIG_PTR (pConfig), pPcieConfig->ResetToTrainingDelay * 1000, 0);
RequestResetDelay = TRUE;
}
//Init common registers
PcieLibCommonPortInit (PortId, pConfig);
//Check if we already have device failure to go to Gen2 before
if (PcieLibCheckGen2Disabled (PortId, pConfig)) {
pPcieConfig->PortConfiguration[PortId].PortLinkMode = PcieLinkModeGen1;
pPcieConfig->ExtPortConfiguration[PortId].PortDeemphasis = PcieTxDeemphasis6dB; // this is to workaround Gen2
}
//@todo Add handling for scratch register for PCIE Gen
switch (pPcieConfig->PortConfiguration[PortId].PortLinkMode) {
case PcieLinkModeGen2:
case PcieLinkModeGen2AdvertizeOnly:
case PcieLinkModeGen1:
LinkMode = pPcieConfig->PortConfiguration[PortId].PortLinkMode;
break;
default:
LinkMode = PcieLinkModeGen1;
}
PcieLibSetLinkMode (PortId, LinkMode, pConfig);
//Enable Compliance Mode
if (pPcieConfig->PortConfiguration[PortId].PortCompliance == ON) {
PcieLibSetLinkCompliance (PortId, pConfig);
}
} else {
//Port disabled
SelectedPortMask &= (~(1 << PortId));
}
}
}
if (RequestResetDelay) {
STALL (GET_BLOCK_CONFIG_PTR (pConfig), pPcieConfig->ResetToTrainingDelay * 1000, 0);
}
for (PortId = MIN_PORT_ID; PortId <= MAX_PORT_ID; PortId++) {
if ((SelectedPortMask & (1 << PortId)) != 0) {
//Release Port Training
PcieLibPortTrainingControl (PortId, PcieLinkTrainingRelease, pConfig);
}
}
STALL (GET_BLOCK_CONFIG_PTR (pConfig), pPcieConfig->TrainingToLinkTestDelay * 1000, 0);
Status = PcieCheckSelectedPorts (SelectedPortMask, pConfig);
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieInitSelectedPorts Exit\n"));
return Status;
}
/*----------------------------------------------------------------------------------------*/
/**
* Check link state on selected ports.
*
*
*
* @param[in] SelectedPortMask Bitmap of port ID selected for training.
* @param[in] pConfig Northbridge configuration structure pointer.
*/
/*----------------------------------------------------------------------------------------*/
AGESA_STATUS
PcieCheckSelectedPorts (
IN UINT16 SelectedPortMask,
IN AMD_NB_CONFIG *pConfig
)
{
AGESA_STATUS Status;
PCIE_CONFIG *pPcieConfig;
PORT PortId;
UINT16 PortMask;
UINT16 CurrentPortMask;
PCIE_LINK_STATUS PortsLinkStatus[MAX_PORT_ID + 1];
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieCheckSelectedPorts (Ports = 0x%x) Enter\n", SelectedPortMask));
Status = AGESA_SUCCESS;
pPcieConfig = GET_PCIE_CONFIG_PTR (pConfig);
PortMask = SelectedPortMask;
// Clear up link state storage
LibAmdMemFill (PortsLinkStatus, 0, sizeof (PortsLinkStatus), (AMD_CONFIG_PARAMS *)&(pPcieConfig->sHeader));
// Initial check for link status on all ports
if (PortMask != 0) {
PcieGetPortsLinkStatus (PortMask, &PortsLinkStatus[0], pPcieConfig->ReceiverDetectionPooling, pConfig);
}
// Check if training on any ports in progress
PortMask = PcieFindPortsWithLinkStatus (&PortsLinkStatus[0], PcieLinkStatusTrainingInProgress);
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), " #1 PortMask = 0x%x\n", PortMask));
if (PortMask != 0) {
// Try to recover ports in case of broken lane
if (PcieBrokenLaneWorkaround (PortMask, pConfig) != AGESA_UNSUPPORTED) {
// Update port status array
PortMask |= PcieFindPortsWithLinkStatus (&PortsLinkStatus[0], PcieLinkStatusConnected);
PcieGetPortsLinkStatus (PortMask, &PortsLinkStatus[0], pPcieConfig->ReceiverDetectionPooling, pConfig);
}
}
// Check if training on any ports still in progress
CurrentPortMask = PcieFindPortsWithLinkStatus (&PortsLinkStatus[0], PcieLinkStatusTrainingInProgress);
if (PortMask != CurrentPortMask) {
REPORT_EVENT (AGESA_WARNING, PCIE_ERROR_BROKEN_LINE, (PortMask^CurrentPortMask)&PortMask, 0, 0, 0, pConfig);
}
PortMask = CurrentPortMask;
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), " #2 PortMask = 0x%x\n", PortMask));
if (PortMask != 0) {
// Try to recover port training by downgrading link speed to Gen1
if (PcieGen2Workaround (PortMask, pConfig) != AGESA_UNSUPPORTED) {
PortMask |= PcieFindPortsWithLinkStatus (&PortsLinkStatus[0], PcieLinkStatusConnected);
PcieGetPortsLinkStatus (PortMask, &PortsLinkStatus[0], pPcieConfig->ReceiverDetectionPooling, pConfig);
}
}
// Check if training on any ports still in progress
CurrentPortMask = PcieFindPortsWithLinkStatus (&PortsLinkStatus[0], PcieLinkStatusTrainingInProgress);
if (PortMask != CurrentPortMask) {
REPORT_EVENT (AGESA_WARNING, PCIE_ERROR_GEN2_FAIL, (PortMask^CurrentPortMask)&PortMask, 0, 0, 0, pConfig);
}
PortMask = CurrentPortMask;
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), " #3 PortMask = 0x%x\n", PortMask));
if (PortMask != 0) {
REPORT_EVENT (AGESA_WARNING, PCIE_ERROR_TRAINING_FAIL, PortMask, 0, 0, 0, pConfig);
PcieMiscWorkaround (&PortsLinkStatus[0], pConfig);
}
//Get bitmap of successfully trained ports
PortMask = PcieFindPortsWithLinkStatus (&PortsLinkStatus[0], PcieLinkStatusConnected);
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), " #4 PortMask = 0x%x\n", PortMask));
if (PortMask != 0) {
// Check if VCO negotiation is completed
PcieCheckVco (PortMask, &PortsLinkStatus[0], pConfig);
}
CurrentPortMask = PcieFindPortsWithLinkStatus (&PortsLinkStatus[0], PcieLinkStatusConnected);
if (PortMask != CurrentPortMask) {
REPORT_EVENT (AGESA_WARNING, PCIE_ERROR_VCO_NEGOTIATON, (PortMask^CurrentPortMask)&PortMask, 0, 0, 0, pConfig);
}
PortMask = CurrentPortMask;
//Update status port status info
for (PortId = MIN_PORT_ID; PortId <= MAX_PORT_ID; PortId++) {
if ((SelectedPortMask & (1 << PortId)) != 0) {
PCI_ADDR Port;
Port = PcieLibGetPortPciAddress (PortId, pConfig);
if (PortsLinkStatus[PortId] == PcieLinkStatusInCompliance) {
pPcieConfig->PortConfiguration[PortId].PortCompliance = ON;
} else {
if (PortsLinkStatus[PortId] == PcieLinkStatusConnected) {
if (LibNbCallBack (PHCB_AmdPortTrainingCompleted, Port.AddressValue, pConfig) == AGESA_ERROR) {
PortsLinkStatus[PortId] = 0;
}
if (PortsLinkStatus[PortId] == PcieLinkStatusConnected &&
pPcieConfig->PcieConfiguration.DisableGfxWorkaround == OFF &&
PcieLibGetPortLinkInfo (PortId, pConfig).MaxLinkWidth >= PcieLinkWidth_x8 &&
PcieGfxWorkarounds (PortId, pConfig) != AGESA_SUCCESS) {
//CIMX_ASSERT (FALSE);
PortsLinkStatus[PortId] = 0;
}
if (PortsLinkStatus[PortId] == PcieLinkStatusConnected) {
pPcieConfig->PortConfiguration[PortId].PortDetected = ON;
PcieLibSetLinkWidth (PortId, pPcieConfig->ExtPortConfiguration[PortId].PortLinkWidth, pConfig);
}
}
if (pPcieConfig->PortConfiguration[PortId].PortDetected == OFF &&
pPcieConfig->PortConfiguration[PortId].PortHotplug == OFF) {
//Port training on Hold if Link in not connected and not in compliance
PcieLibPortTrainingControl (PortId, PcieLinkTrainingHold, pConfig);
}
}
if (pPcieConfig->PortConfiguration[PortId].PortDetected == OFF ||
pPcieConfig->PortConfiguration[PortId].PortHotplug == ON) {
// For all port without devices and hotplug ports
LibNbPciIndexRMW (Port.AddressValue | NB_BIF_INDEX, NB_BIFNBP_REG70, AccessWidth32, (UINT32)~BIT19, BIT19, pConfig);
}
LibNbPciIndexWrite (Port.AddressValue | NB_BIF_INDEX, NB_BIFNBP_REG01, AccessWidth32, (UINT32*)&pPcieConfig->PortConfiguration[PortId], pConfig);
LibNbPciWrite (Port.AddressValue | NB_PCIP_REG108, AccessWidth32, (UINT32*)&pPcieConfig->ExtPortConfiguration[PortId], pConfig);
}
}
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieCheckSelectedPorts Exit\n"));
return Status;
}
/*----------------------------------------------------------------------------------------*/
/**
* Workaround for broken TX line.
*
*
*
* @param[in] SelectedPortMask Bitmap of port ID selected for training.
* @param[in] pConfig Northbridge configuration structure pointer.
*/
/*----------------------------------------------------------------------------------------*/
AGESA_STATUS
PcieBrokenLaneWorkaround (
IN UINT16 SelectedPortMask,
IN AMD_NB_CONFIG *pConfig
)
{
AGESA_STATUS Status;
PORT PortId;
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieBrokenLaneWorkaround Enter\n"));
Status = AGESA_UNSUPPORTED;
for (PortId = MIN_PORT_ID; PortId <= MAX_PORT_ID; PortId++) {
if ((SelectedPortMask & (1 << PortId)) != 0) {
LINK_INFO LinkInfo = PcieLibGetPortLinkInfo (PortId, pConfig);
if (LinkInfo.MaxLinkWidth > PcieLinkWidth_x1 && LinkInfo.LinkWidth < LinkInfo.MaxLinkWidth) {
PcieLibPowerOffPortLanes (PortId, LinkInfo.LinkWidth, pConfig);
if (PcieLibResetSlot (PortId, pConfig) == AGESA_SUCCESS) {
Status = AGESA_SUCCESS;
}
}
}
}
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieBrokenLaneWorkaround Exit\n"));
return Status;
}
/*----------------------------------------------------------------------------------------*/
/**
* Workaround for device violating Gen2 spec.
* Downgrade link speed to Gen1.
*
*
*
* @param[in] SelectedPortMask Bitmap of port ID selected for training.
* @param[in] pConfig Northbridge configuration structure pointer.
*/
/*----------------------------------------------------------------------------------------*/
AGESA_STATUS
PcieGen2Workaround (
IN UINT16 SelectedPortMask,
IN AMD_NB_CONFIG *pConfig
)
{
AGESA_STATUS Status;
PCIE_CONFIG *pPcieConfig;
PORT PortId;
BOOLEAN RequestPciReset;
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieGen2Workaround Enter\n"));
pPcieConfig = GET_PCIE_CONFIG_PTR (pConfig);
RequestPciReset = FALSE;
Status = AGESA_UNSUPPORTED;
for (PortId = MIN_PORT_ID; PortId <= MAX_PORT_ID; PortId++) {
if ((SelectedPortMask & (1 << PortId)) != 0) {
if (pPcieConfig->PortConfiguration[PortId].PortLinkMode == PcieLinkModeGen2 ||
pPcieConfig->PortConfiguration[PortId].PortLinkMode == PcieLinkModeGen2AdvertizeOnly ||
pPcieConfig->ExtPortConfiguration[PortId].PortDeemphasis == PcieTxDeemphasis3p5dB) {
//Degrade link speed to Gen1
pPcieConfig->ExtPortConfiguration[PortId].PortDeemphasis = PcieTxDeemphasis6dB;
PcieLibSetLinkMode (PortId, PcieLinkModeGen1, pConfig);
PcieLibSetGen2Disabled (PortId, pConfig);
if (PcieLibResetSlot (PortId, pConfig) != AGESA_SUCCESS) {
//Slot reset logic not supported request PCI reset.
RequestPciReset = TRUE;
}
//Report back to caller that potential downgrade case is detected.
Status = AGESA_SUCCESS;
}
}
if (RequestPciReset) {
PcieLibRequestPciReset (pConfig);
}
}
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieGen2Workaround Enter\n"));
return Status;
}
/*----------------------------------------------------------------------------------------*/
/**
* Try to recover system by issuing system wide PCI reset.
*
*
*
* @param[in] PortsLinkStatus Array of link status for every Port
* @param[in] pConfig Northbridge configuration structure pointer.
*/
/*----------------------------------------------------------------------------------------*/
AGESA_STATUS
PcieMiscWorkaround (
IN PCIE_LINK_STATUS *PortsLinkStatus,
IN AMD_NB_CONFIG *pConfig
)
{
AGESA_STATUS Status;
PORT PortId;
UINT16 PortMask;
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieMiscWorkaround Enter\n"));
Status = AGESA_UNSUPPORTED;
PortMask = PcieFindPortsWithLinkStatus (PortsLinkStatus, PcieLinkStatusTrainingInProgress);
for (PortId = MIN_PORT_ID; PortId <= MAX_PORT_ID; PortId++) {
if ((PortMask & (1 << PortId)) != 0) {
if (PcieLibRequestPciReset (pConfig)!= AGESA_SUCCESS) {
break;
}
PortMask = PcieFindPortsWithLinkStatus (PortsLinkStatus, PcieLinkStatusTrainingInProgress);
if (PortMask == 0) {
break;
}
}
}
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieMiscWorkaround Exit\n"));
return Status;
}
/*----------------------------------------------------------------------------------------*/
/**
* Check VCO negotiation complete.
* Routine will retry retrain device infinitely if VCO negotiation is failing.
*
*
* @param[in] SelectedPortMask Bitmap of port ID selected for training.
* @param[in] PortsLinkStatus Array of link status for every Port
* @param[in] pConfig Northbridge configuration structure pointer.
*
*/
/*----------------------------------------------------------------------------------------*/
AGESA_STATUS
PcieCheckVco (
IN UINT16 SelectedPortMask,
IN PCIE_LINK_STATUS *PortsLinkStatus,
IN AMD_NB_CONFIG *pConfig
)
{
AGESA_STATUS Status;
UINT16 VcoNegotiationInProgressPortMask;
PORT PortId;
UINT16 VcoStatus;
UINT32 LinkRetrainCount;
UINT32 VcoPoll;
UINT32 Value;
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]VcoNegotiationInProgress Enter\n"));
Status = AGESA_SUCCESS;
VcoNegotiationInProgressPortMask = SelectedPortMask;
for (PortId = MIN_PORT_ID; PortId <= MAX_PORT_ID; PortId++) {
if (VcoNegotiationInProgressPortMask & (1 << PortId)) {
// For each port where VCO needs to be checked
PCI_ADDR Port;
Port = PcieLibGetPortPciAddress (PortId, pConfig);
PortsLinkStatus[PortId] = PcieLinkStatusVcoNegotiationInProgress;
for (LinkRetrainCount = 0; LinkRetrainCount < 10; LinkRetrainCount++) {
// Poll for 200 ms for VC0 negotioation completion
for (VcoPoll = 0; VcoPoll < 200; VcoPoll++) {
LibNbPciRead (Port.AddressValue | NB_PCIP_REG12A, AccessWidth16, &VcoStatus, pConfig);
if ((VcoStatus & BIT1) != 0) {
STALL (GET_BLOCK_CONFIG_PTR (pConfig), 1000, 0);
} else {
PortsLinkStatus[PortId] = PcieLinkStatusConnected;
break;
}
} //For each VcoPoll
if (PortsLinkStatus[PortId] == PcieLinkStatusVcoNegotiationInProgress) {
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_MISC), " Vco Not Completed. Retrain link on PortId %d\n", PortId));
LibNbPciIndexRead (Port.AddressValue | NB_BIF_INDEX, NB_BIFNBP_REGA2, AccessWidth32, &Value, pConfig);
Value = (Value & 0xfffffe80) | ((Value & 0x70) >> 4) | BIT8;
LibNbPciIndexWrite (Port.AddressValue | NB_BIF_INDEX, NB_BIFNBP_REGA2, AccessWidth32, &Value, pConfig);
} else {
break; //Vco negotiations complete
}
} //For each LinkRetrainCount
if (PortsLinkStatus[PortId] == PcieLinkStatusVcoNegotiationInProgress) {
PortsLinkStatus[PortId] = PcieLinkStatusNotConnected;
}
} // Vco negotiations required
} //For each port
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]VcoNegotiationInProgress Exit\n"));
return Status;
}
/*----------------------------------------------------------------------------------------*/
/**
* Get bit map of ports with particular link status
*
*
*
* @param[in] PortLinkStatus Pointer to array of link status for every Port
* @param[in] LinkStatus LinkStatus to search for.
*
*/
/*----------------------------------------------------------------------------------------*/
UINT16
PcieFindPortsWithLinkStatus (
IN PCIE_LINK_STATUS *PortLinkStatus,
IN PCIE_LINK_STATUS LinkStatus
)
{
UINT16 PortMask;
PORT PortId;
PortMask = 0;
for (PortId = MIN_PORT_ID; PortId <= MAX_PORT_ID; PortId++) {
if (PortLinkStatus[PortId] == LinkStatus) PortMask |= (1 << PortId);
}
return PortMask;
}
/*----------------------------------------------------------------------------------------*/
/**
* Gather link state for selected ports.
*
*
* @param[in] SelectedPortMask Bitmap of port ID selected for training.
* @param[in] PortLinkStatus Pointer to array of link status for every Port
* @param[in] Pooling Time in MS to pool for link status change.
* @param[in] pConfig Northbridge configuration structure pointer.
*
*/
/*----------------------------------------------------------------------------------------*/
PCIE_LINK_STATUS
PcieGetPortsLinkStatus (
IN UINT16 SelectedPortMask,
IN OUT PCIE_LINK_STATUS *PortLinkStatus,
IN UINT32 Pooling,
IN AMD_NB_CONFIG *pConfig
)
{
PCIE_LINK_STATUS Status;
PORT PortId;
CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_TRACE), "[NBPCIE]PcieGetPortsLinkStatus Enter\n"));
Status = PcieLinkStatusNotConnected;
Pooling *= 10;
while (Pooling-- != 0 && Status != PcieLinkStatusConnected) {
Status = PcieLinkStatusConnected; //Set up initial overall state as connected
for (PortId = MIN_PORT_ID; PortId <= MAX_PORT_ID; PortId++) {
//Work only on selected ports
if ((SelectedPortMask & (1 << PortId)) != 0) {
PCI_ADDR Port;
UINT32 LinkState;
Port = PcieLibGetPortPciAddress (PortId, pConfig); //Get PCI address of this port
//Get link state
LibNbPciIndexRead (Port.AddressValue | NB_BIF_INDEX, NB_BIFNBP_REGA5, AccessWidth32, &LinkState, pConfig);
LinkState &= 0x3F;
//CIMX_TRACE ((TRACE_DATA (GET_BLOCK_CONFIG_PTR (pConfig), CIMX_NBPCIE_MISC), " PortId %d LinkState = 0x%x \n", PortId, LinkState));
printk(BIOS_INFO, "[NBPCIE] PortId %02d LinkState = 0x%x \n", PortId, LinkState);
//Check if link in L0 state
if (LinkState == 0x10) {
PortLinkStatus[PortId] = PcieLinkStatusConnected;
} else {
Status = PcieLinkStatusNotConnected;
//Check if link in compliance mode
if (LinkState == 0x7) {
PortLinkStatus[PortId] = PcieLinkStatusInCompliance;
} else {
//Check if we passed receiver detection. It will indicate that device present.
if (LinkState > 0x4) {
PortLinkStatus[PortId] = PcieLinkStatusTrainingInProgress;
}
}
}
}
}
STALL (GET_BLOCK_CONFIG_PTR (pConfig), 100, 0);
}
return Status;
}