diff options
author | Kerry Sheh <shekairui@gmail.com> | 2012-01-31 20:39:37 +0800 |
---|---|---|
committer | Patrick Georgi <patrick@georgi-clan.de> | 2012-02-02 13:54:36 +0100 |
commit | 9292d89be84d6abf9257ddb872887d4f53b2a00e (patch) | |
tree | 9eaa548f1742745f57fc92a12734649fec8db1cd /src/vendorcode/amd/cimx/rd890/nbPcieInitEarly.c | |
parent | 17670866a0d12839bc2a4c852210ccf11d3cb4b2 (diff) |
RD890 Northbridge: AMD RD890/SR56X0 Northbridge CIMX code
Change-Id: If9908ffeb5b707a660db38dc44f5118347cbcc06
Signed-off-by: Kerry Sheh <kerry.she@amd.com>
Signed-off-by: Kerry Sheh <shekairui@gmail.com>
Reviewed-on: http://review.coreboot.org/557
Tested-by: build bot (Jenkins)
Reviewed-by: Patrick Georgi <patrick@georgi-clan.de>
Diffstat (limited to 'src/vendorcode/amd/cimx/rd890/nbPcieInitEarly.c')
-rw-r--r-- | src/vendorcode/amd/cimx/rd890/nbPcieInitEarly.c | 720 |
1 files changed, 720 insertions, 0 deletions
diff --git a/src/vendorcode/amd/cimx/rd890/nbPcieInitEarly.c b/src/vendorcode/amd/cimx/rd890/nbPcieInitEarly.c new file mode 100644 index 0000000000..6fdf2b120c --- /dev/null +++ b/src/vendorcode/amd/cimx/rd890/nbPcieInitEarly.c @@ -0,0 +1,720 @@ +/** + * @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; +} |