| /** |
| * @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; |
| } |