From b7c51c9cf4864df6aabb99a1ae843becd577237c Mon Sep 17 00:00:00 2001 From: raywu Date: Fri, 15 Jun 2018 00:00:50 +0800 Subject: init. 1AQQW051 --- .../SaInit/Pei/PcieTrainingLinkRecovery.c | 721 +++++++++++++++++++++ 1 file changed, 721 insertions(+) create mode 100644 ReferenceCode/Chipset/SystemAgent/SaInit/Pei/PcieTrainingLinkRecovery.c (limited to 'ReferenceCode/Chipset/SystemAgent/SaInit/Pei/PcieTrainingLinkRecovery.c') diff --git a/ReferenceCode/Chipset/SystemAgent/SaInit/Pei/PcieTrainingLinkRecovery.c b/ReferenceCode/Chipset/SystemAgent/SaInit/Pei/PcieTrainingLinkRecovery.c new file mode 100644 index 0000000..1480bc7 --- /dev/null +++ b/ReferenceCode/Chipset/SystemAgent/SaInit/Pei/PcieTrainingLinkRecovery.c @@ -0,0 +1,721 @@ +/*++ @file + This driver recovers the PEG link. + +@copyright + Copyright (c) 2012 - 2014 Intel Corporation. All rights reserved + This software and associated documentation (if any) is furnished + under a license and may only be used or copied in accordance + with the terms of the license. Except as permitted by such + license, no part of this software or documentation may be + reproduced, stored in a retrieval system, or transmitted in any + form or by any means without the express written consent of + Intel Corporation. + + This file contains an 'Intel Peripheral Driver' and uniquely + identified as "Intel Reference Module" and is + licensed for Intel CPUs and chipsets under the terms of your + license agreement with Intel or your vendor. This file may + be modified by the user, subject to additional terms of the + license agreement + +--*/ + +#include "PcieTraining.h" +#include "PciExpressInit.h" +#include EFI_PPI_CONSUMER (PchReset) + +#ifdef PEG_FLAG + +EFI_STATUS +EnsureLinkIsHealthy ( + IN EFI_PEI_SERVICES **PeiServices, + IN SA_PLATFORM_POLICY_PPI *SaPlatformPolicyPpi, + IN SA_DATA_HOB *SaDataHob, + IN PEI_STALL_PPI *StallPpi, + IN PEG_PORT *PegPort, + IN UINT8 OriginalLinkSpeed, + IN UINT8 OriginalLinkWidth + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + /// + /// Check Link Status and Recover the Link if needed + /// + Status = RecoverLinkWidth (PeiServices, StallPpi, PegPort, OriginalLinkWidth); + if (EFI_ERROR (Status)) { + Status = RecoverLinkFailure (PeiServices, SaPlatformPolicyPpi, SaDataHob, StallPpi, PegPort, OriginalLinkSpeed, OriginalLinkWidth); + if (EFI_ERROR (Status)) { + return Status; + } + } + Status = RecoverLinkSpeed (PeiServices, StallPpi, PegPort, OriginalLinkSpeed); + if (EFI_ERROR (Status)) { + Status = RecoverLinkFailure (PeiServices, SaPlatformPolicyPpi, SaDataHob, StallPpi, PegPort, OriginalLinkSpeed, OriginalLinkWidth); + if (EFI_ERROR (Status)) { + return Status; + } + } + Status = WaitForL0 (PeiServices, StallPpi, PegPort, FALSE); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "DOWNGRADE, Link is not in L0\n")); + Status = RetrainLink (PeiServices, StallPpi, PegPort); + if (EFI_ERROR (Status)) { + Status = RecoverLinkFailure (PeiServices, SaPlatformPolicyPpi, SaDataHob, StallPpi, PegPort, OriginalLinkSpeed, OriginalLinkWidth); + if (EFI_ERROR (Status)) { + return Status; + } + } + } + + return Status; +} + +/** + Wait until link is up. + + @param[in] PeiServices - Pointer to the PEI services table + @param[in] StallPpi - Pointer to PEI_STALL_PPI + @param[in] PegPort - Pointer PEG Port + @param[in] UseVcu - If TRUE, use VCU to determine link state. If FALSE, use MMIO CFG to determine link state. + + @retval EFI_SUCCESS - Completed successfully before timeout + @retval EFI_TIMEOUT - Timed out +**/ +EFI_STATUS +WaitForL0 ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_STALL_PPI *StallPpi, + IN PEG_PORT *PegPort, + IN BOOLEAN UseVcu + ) +{ + UINT32 MchBar; + UINT32 i; + EFI_STATUS Status; + UINT32 VcuAddress; + UINT8 VcuReadOp; + UINT8 VcuWriteOp; + CPU_FAMILY CpuFamilyId; + CPU_STEPPING CpuSteppingId; + BOOLEAN CheckEq; + BOOLEAN CompletedEq; + UINT32 EqStatus; + UINT32 LinkStatus; + + Status = EFI_TIMEOUT; + CheckEq = (PegPort->EndpointMaxLinkSpeed >= 0x3) ? TRUE : FALSE; + CompletedEq = FALSE; + i = 0; + MchBar = McD0PciCfg64 (R_SA_MCHBAR) & ~BIT0; + CpuFamilyId = GetCpuFamily(); + CpuSteppingId = GetCpuStepping(); + +#ifndef AMI_OVERRIDE_FOR_ULT_FASTBOOT + if (CpuFamilyId == EnumCpuHswUlt) return EFI_UNSUPPORTED; +#endif // AMI_OVERRIDE_FOR_ULT_FASTBOOT + + if ((CpuFamilyId == EnumCpuHsw) && (CpuSteppingId == EnumHswA0)) { + VcuAddress = R_SA_VCU_REUT_PH1_PIS_ADDRESS_REV1; + VcuReadOp = V_SA_VCU_OPCODE_READ_CSR_REV1; + VcuWriteOp = V_SA_VCU_OPCODE_WRITE_CSR_REV1; + } else { + VcuAddress = R_SA_VCU_REUT_PH1_PIS_ADDRESS_REV2; + VcuReadOp = V_SA_VCU_OPCODE_READ_CSR_REV2; + VcuWriteOp = V_SA_VCU_OPCODE_WRITE_CSR_REV2; + } + + /// + /// If endpoint's LCAP.MLS (Spec section 7.8.6) indicated Gen3 capability, first wait for equalization to complete. + /// Check equalization status LSTS2.EC (Spec section 7.8.20) until Gen3 equalization successfully completed. + /// + if (CheckEq && !UseVcu) { + for (; i < 100; i++) { + EqStatus = MmPci16 (0, PegPort->Bus, PegPort->Device, PegPort->Function, R_SA_PEG_LSTS2_OFFSET); + EqStatus = (EqStatus >> 1) & 0x1; + if (EqStatus == 0x1) { + CompletedEq = TRUE; + break; + } + StallPpi->Stall (PeiServices, StallPpi, STALL_ONE_MILLI_SECOND); + } + } + + /// + /// Check for L0 status. If !UseVcu, check PEGSTS. + /// Continue up to 100 msec of combined delay. + /// Skip if equalization was needed but didn't successfully complete. + /// + if ((CheckEq && CompletedEq) || !CheckEq || UseVcu) { + for (; i < 100; i++) { + if (UseVcu) { + LinkStatus = SendVcuApiSequence (MchBar, VcuAddress, VcuReadOp, 0); + LinkStatus = (LinkStatus >> (PegPort->Function * B_SA_PEG_REUT_PH1_PIS_ST_STEP)) & B_SA_PEG_REUT_PH1_PIS_ST_MASK; + if (LinkStatus == 0x10) { + Status = EFI_SUCCESS; + break; + } + } else { + LinkStatus = MmPci32 (0, PegPort->Bus, PegPort->Device, PegPort->Function, R_SA_PEG_PEGSTS_OFFSET); + LinkStatus = (LinkStatus >> 16) & 0xF; + if (LinkStatus == 0x7) { + Status = EFI_SUCCESS; + break; + } + } + StallPpi->Stall (PeiServices, StallPpi, STALL_ONE_MILLI_SECOND); + } + } + + return Status; +} + +EFI_STATUS +SetPchGpio ( + IN UINT8 GpioNumber, + IN UINT8 Level + ) +/** + This function sets a GPIO to a particular level. + + @param[in] GpioNumber - PCH GPIO number + @param[in] Level - 0 = Low, 1 = High + + @retval EFI_SUCCESS - Did toggle GPIO + @retval EFI_UNSUPPORTED - Didn't toggle GPIO + @retval EFI_INVALID_PARAMETER - Didn't toggle GPIO +**/ +{ + UINT32 Data32; + UINT16 LpcDeviceId; + UINT16 GpioBase; + UINT16 UseSelOffset; + UINT16 IoSelOffset; + UINT16 LvlOffset; + UINT8 GpioBit; + EFI_STATUS Status; + + Level &= 0x1; + Status = EFI_SUCCESS; + GpioBase = 0; + UseSelOffset = 0; + IoSelOffset = 0; + LvlOffset = 0; + GpioBit = 0; + + LpcDeviceId = McDevFunPciCfg16 ( + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_LPC, + PCI_FUNCTION_NUMBER_PCH_LPC, + R_PCH_LPC_DEVICE_ID + ); + if (!IS_PCH_LPT_LPC_DEVICE_ID (LpcDeviceId)) { + Status = EFI_UNSUPPORTED; + return Status; + } + + GpioBase = McDevFunPciCfg16 ( + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_LPC, + PCI_FUNCTION_NUMBER_PCH_LPC, + R_PCH_LPC_GPIO_BASE + ) & B_PCH_LPC_GPIO_BASE_BAR; + if (GpioBase == 0) { + Status = EFI_UNSUPPORTED; + return Status; + } + + if (GpioNumber < 0x20) { + UseSelOffset = R_PCH_GPIO_USE_SEL; + IoSelOffset = R_PCH_GPIO_IO_SEL; + LvlOffset = R_PCH_GPIO_LVL; + GpioBit = GpioNumber; + } else if (GpioNumber < 0x40) { + UseSelOffset = R_PCH_GPIO_USE_SEL2; + IoSelOffset = R_PCH_GPIO_IO_SEL2; + LvlOffset = R_PCH_GPIO_LVL2; + GpioBit = GpioNumber - 0x20; + } else if (GpioNumber < 0x60) { + UseSelOffset = R_PCH_GPIO_USE_SEL3; + IoSelOffset = R_PCH_GPIO_IO_SEL3; + LvlOffset = R_PCH_GPIO_LVL3; + GpioBit = GpioNumber - 0x40; + } else { + Status = EFI_INVALID_PARAMETER; + return Status; + } + + if (!EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "Setting GPIO%d to %x\n", GpioNumber, Level)); + IoOr32 ((UINTN) (GpioBase + UseSelOffset), (UINT32) (1 << GpioBit)); + IoAnd32 ((UINTN) (GpioBase + IoSelOffset), (UINT32) ~(1 << GpioBit)); + Data32 = IoRead32 ((UINTN) (GpioBase + LvlOffset)); + Data32 &= (UINT32) ~(1 << GpioBit); + Data32 |= (UINT32) (Level << GpioBit); + IoWrite32 ((UINTN) (GpioBase + LvlOffset), Data32); + } + + return Status; +} + +EFI_STATUS +TogglePegSlotReset ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_STALL_PPI *StallPpi, + IN SA_PLATFORM_POLICY_PPI *SaPlatformPolicyPpi + ) +/** + This function asserts and deasserts a GPIO that controls PERST#. + The specific GPIO and its active level is provided by a policy. + The GPIO minimum assertion time, T_PERST (100 usec), is defined in the PCIe CEM Specification. + + @param[in] PeiServices - Pointer to the PEI services table + @param[in] StallPpi - Pointer to PEI_STALL_PPI + @param[in] SaPlatformPolicyPpi - Pointer to SA_PLATFORM_POLICY_PPI + + @retval EFI_SUCCESS - Did toggle GPIO + @retval EFI_UNSUPPORTED - Didn't toggle GPIO + @retval EFI_INVALID_PARAMETER - Didn't toggle GPIO +**/ +{ + EFI_STATUS Status; + UINT8 i; + + DEBUG ((EFI_D_INFO, "Toggling PEG slot reset.\n")); + for (i = 0; i < SA_PEG_MAX_FUN; i++) { + MmPci16Or (0, SA_PEG_BUS_NUM, SA_PEG_DEV_NUM, i, R_SA_PEG_LCTL_OFFSET, BIT4); + } + Status = AssertPegSlotReset (SaPlatformPolicyPpi); + if (!EFI_ERROR (Status)) { + StallPpi->Stall (PeiServices, StallPpi, 100 * STALL_ONE_MICRO_SECOND); + for (i = 0; i < SA_PEG_MAX_FUN; i++) { + MmPci16And (0, SA_PEG_BUS_NUM, SA_PEG_DEV_NUM, i, R_SA_PEG_LCTL_OFFSET, (UINT16) ~(BIT4)); + } + Status = DeassertPegSlotReset (SaPlatformPolicyPpi); + } else { + for (i = 0; i < SA_PEG_MAX_FUN; i++) { + MmPci16And (0, SA_PEG_BUS_NUM, SA_PEG_DEV_NUM, i, R_SA_PEG_LCTL_OFFSET, (UINT16) ~(BIT4)); + } + } + + return Status; +} + +EFI_STATUS +AssertPegSlotReset ( + IN SA_PLATFORM_POLICY_PPI *SaPlatformPolicyPpi + ) +/** + This function asserts a GPIO that controls PERST#. + The specific GPIO and its active level is provided by a policy. + The GPIO minimum assertion time, T_PERST (100 usec), is defined in the PCIe CEM Specification. + + @param[in] SaPlatformPolicyPpi - Pointer to SA_PLATFORM_POLICY_PPI + + @retval EFI_SUCCESS - Did assert GPIO + @retval EFI_UNSUPPORTED - Didn't assert GPIO + @retval EFI_INVALID_PARAMETER - Didn't assert GPIO +**/ +{ + EFI_STATUS Status; + UINT8 GpioNumber; + UINT8 AssertLevel; + + Status = EFI_SUCCESS; + + DEBUG ((EFI_D_INFO, "Asserting PEG slot reset.\n")); + + if (!((SaPlatformPolicyPpi->Revision >= SA_PLATFORM_POLICY_PPI_REVISION_3) && + (SaPlatformPolicyPpi->PcieConfig->PegGpioData->GpioSupport == TRUE))) { + Status = EFI_UNSUPPORTED; + } + + if (!EFI_ERROR (Status)) { + GpioNumber = (UINT8) (SaPlatformPolicyPpi->PcieConfig->PegGpioData->SaPegReset->Value); + AssertLevel = (UINT8) (SaPlatformPolicyPpi->PcieConfig->PegGpioData->SaPegReset->Active & 0x1); + Status = SetPchGpio (GpioNumber, AssertLevel); + } + + return Status; +} + +EFI_STATUS +DeassertPegSlotReset ( + IN SA_PLATFORM_POLICY_PPI *SaPlatformPolicyPpi + ) +/** + This function deasserts a GPIO that controls PERST#. + The specific GPIO and its active level is provided by a policy. + The GPIO minimum assertion time, T_PERST (100 usec), is defined in the PCIe CEM Specification. + + @param[in] SaPlatformPolicyPpi - Pointer to SA_PLATFORM_POLICY_PPI + + @retval EFI_SUCCESS - Did deassert GPIO + @retval EFI_UNSUPPORTED - Didn't deassert GPIO + @retval EFI_INVALID_PARAMETER - Didn't deassert GPIO +**/ +{ + EFI_STATUS Status; + UINT8 GpioNumber; + UINT8 DeassertLevel; + + Status = EFI_SUCCESS; + + DEBUG ((EFI_D_INFO, "Deasserting PEG slot reset.\n")); + + if (!((SaPlatformPolicyPpi->Revision >= SA_PLATFORM_POLICY_PPI_REVISION_3) && + (SaPlatformPolicyPpi->PcieConfig->PegGpioData->GpioSupport == TRUE))) { + Status = EFI_UNSUPPORTED; + } + + if (!EFI_ERROR (Status)) { + GpioNumber = (UINT8) (SaPlatformPolicyPpi->PcieConfig->PegGpioData->SaPegReset->Value); + DeassertLevel = (UINT8) ((SaPlatformPolicyPpi->PcieConfig->PegGpioData->SaPegReset->Active & 0x1) ^ 0x1); + SetPchGpio (GpioNumber, DeassertLevel); + } + + return Status; +} + +EFI_STATUS +RecoverLinkFailure ( + IN EFI_PEI_SERVICES **PeiServices, + IN SA_PLATFORM_POLICY_PPI *SaPlatformPolicyPpi, + IN SA_DATA_HOB *SaDataHob, + IN PEI_STALL_PPI *StallPpi, + IN PEG_PORT *PegPort, + IN UINT8 OriginalLinkSpeed, + IN UINT8 OriginalLinkWidth + ) +{ + EFI_STATUS Status; + UINT8 CurrentLinkWidth; + UINT8 CurrentLinkSpeed; + UINT8 PegBus; + UINT8 PegDev; + UINT8 PegFunc; + + /// + /// A platform reset should be done after presets are saved in NVRAM + /// + if (SaDataHob != NULL) { + if ((SaPlatformPolicyPpi->Revision >= SA_PLATFORM_POLICY_PPI_REVISION_2) && + (SaPlatformPolicyPpi->PcieConfig->PegGen3ForcePresetSearch == 0)) { + SaDataHob->PegPlatformResetRequired = TRUE; + } else if (SaPlatformPolicyPpi->Revision < SA_PLATFORM_POLICY_PPI_REVISION_2) { + SaDataHob->PegPlatformResetRequired = TRUE; + } + } + + /// + /// Bypass phase2 and assert slot reset + /// + McD1PciCfg32Or (R_SA_PEG_EQCFG_OFFSET, BIT15); + Status = TogglePegSlotReset (PeiServices, StallPpi, SaPlatformPolicyPpi); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "Error 0x%x.\n", Status)); + } else { + DEBUG ((EFI_D_INFO, "Success.\n")); + } + PegBus = PegPort->Bus; + PegDev = PegPort->Device; + PegFunc = PegPort->Function; + /// + /// Wait for Equalization Done + /// + while (((MmPci32 (0, PegBus, PegDev, PegFunc, R_SA_PEG_LSTS2_OFFSET) >> 1) & 0x1) != 0x1); + /// + /// Wait for flow control credits exchange + /// + WaitForVc0Negotiation (PeiServices, StallPpi, PegBus, PegDev, PegFunc); + + CurrentLinkWidth = GetNegotiatedWidth (PegPort); + if (CurrentLinkWidth < OriginalLinkWidth) { + DEBUG ((EFI_D_ERROR, "Link Width DOWNGRADED!\n")); + Status = EFI_TIMEOUT; + } + CurrentLinkSpeed = GetLinkSpeed (PegPort); + if (CurrentLinkSpeed < OriginalLinkSpeed) { + DEBUG ((EFI_D_ERROR, "Link Speed DOWNGRADED!\n")); + Status = EFI_TIMEOUT; + } + + return Status; +} + +BOOLEAN +LinkIsDowngraded ( + IN PEG_PORT *PegPort, + IN UINT8 OriginalLinkSpeed, + IN UINT8 OriginalLinkWidth + ) +{ + BOOLEAN IsDowngraded; + + IsDowngraded = FALSE; + + if (OriginalLinkSpeed != GetLinkSpeed (PegPort)) { + DEBUG ((EFI_D_INFO, "Link speed downgrade detected\n")); + IsDowngraded = TRUE; + } + if (OriginalLinkWidth != GetNegotiatedWidth (PegPort)) { + DEBUG ((EFI_D_INFO, "Link width downgrade detected\n")); + IsDowngraded = TRUE; + } + return IsDowngraded; +} + +EFI_STATUS +SecondaryBusReset ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_STALL_PPI *StallPpi, + IN PEG_PORT *PegPort + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + DEBUG ((EFI_D_INFO, "SECONDARY BUS RESET!\n")); + MmPci16Or (0, PegPort->Bus, PegPort->Device, PegPort->Function, R_SA_PEG_BCTRL_OFFSET, B_SA_PEG_BCTRL_SRESET_MASK); + MmPci16And (0, PegPort->Bus, PegPort->Device, PegPort->Function, R_SA_PEG_BCTRL_OFFSET,~(B_SA_PEG_BCTRL_SRESET_MASK)); + Status = WaitForL0 (PeiServices, StallPpi, PegPort, FALSE); + DEBUG ((EFI_D_INFO, "Reset Complete\n")); + + return Status; +} + +EFI_STATUS +ResetPhyLayer ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_STALL_PPI *StallPpi, + IN PEG_PORT *PegPort + ) +{ + EFI_STATUS Status; + UINT32 MchBar; + UINT32 Data32; + UINT32 VcuAddress; + UINT8 VcuReadOp; + UINT8 VcuWriteOp; + BOOLEAN UseVcu; + CPU_FAMILY CpuFamilyId; + CPU_STEPPING CpuSteppingId; + + UseVcu = TRUE; + Status = EFI_SUCCESS; + MchBar = McD0PciCfg64 (R_SA_MCHBAR) &~BIT0; + CpuFamilyId = GetCpuFamily(); + CpuSteppingId = GetCpuStepping(); + +#ifndef AMI_OVERRIDE_FOR_ULT_FASTBOOT + if (CpuFamilyId == EnumCpuHswUlt) return EFI_UNSUPPORTED; +#endif // AMI_OVERRIDE_FOR_ULT_FASTBOOT + + if ((CpuFamilyId == EnumCpuHsw) && (CpuSteppingId == EnumHswA0)) { + VcuAddress = R_SA_VCU_REUT_PH_CTR_ADDRESS_REV1; + VcuReadOp = V_SA_VCU_OPCODE_READ_CSR_REV1; + VcuWriteOp = V_SA_VCU_OPCODE_WRITE_CSR_REV1; + } else { + VcuAddress = R_SA_VCU_REUT_PH_CTR_ADDRESS_REV2; + VcuReadOp = V_SA_VCU_OPCODE_READ_CSR_REV2; + VcuWriteOp = V_SA_VCU_OPCODE_WRITE_CSR_REV2; + } + + DEBUG ((EFI_D_INFO, "PHY LAYER RESET!\n")); + if (UseVcu) { + Data32 = SendVcuApiSequence (MchBar, VcuAddress, VcuReadOp, 0); + Data32 &= (UINT32) ~(BIT1); + SendVcuApiSequence (MchBar, VcuAddress, VcuWriteOp, Data32); + } else { + McD1PciCfg32And (R_SA_PEG_REUT_PH_CTR_OFFSET, ~(B_SA_PEG_REUT_PH_CTR_RESETMOD_MASK)); + } + + if (UseVcu) { + Data32 = SendVcuApiSequence (MchBar, VcuAddress, VcuReadOp, 0); + Data32 |= (UINT32) BIT0; + SendVcuApiSequence (MchBar, VcuAddress, VcuWriteOp, Data32); + } else { + McD1PciCfg32Or (R_SA_PEG_REUT_PH_CTR_OFFSET, B_SA_PEG_REUT_PH_CTR_PHYRESET_MASK); + } + + if (UseVcu) { + Data32 = SendVcuApiSequence (MchBar, VcuAddress, VcuReadOp, 0); + Data32 |= (UINT32) BIT13; + SendVcuApiSequence (MchBar, VcuAddress, VcuWriteOp, Data32); + } else { + McD1PciCfg32Or (R_SA_PEG_REUT_PH_CTR_OFFSET, B_SA_PEG_REUT_PH_CTR_AUTOCOMP_MASK); + } + + if (UseVcu) { + Data32 = SendVcuApiSequence (MchBar, VcuAddress, VcuReadOp, 0); + Data32 &= (UINT32) ~(BIT0); + SendVcuApiSequence (MchBar, VcuAddress, VcuWriteOp, Data32); + } else { + McD1PciCfg32And (R_SA_PEG_REUT_PH_CTR_OFFSET, ~(B_SA_PEG_REUT_PH_CTR_PHYRESET_MASK)); + } + + Status = WaitForL0 (PeiServices, StallPpi, PegPort, TRUE); + DEBUG ((EFI_D_INFO, "Reset Complete\n")); + + return Status; +} + +EFI_STATUS +RetrainLink ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_STALL_PPI *StallPpi, + IN PEG_PORT *PegPort + ) +{ + EFI_STATUS Status; + + /// + /// Initiate Link Retrain + /// + MmPci16Or (0, PegPort->Bus, PegPort->Device, PegPort->Function, R_SA_PEG_LCTL_OFFSET, 0x20); + + Status = WaitForL0 (PeiServices, StallPpi, PegPort, FALSE); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "Link retrain FAILED!!!\n")); + } + + return Status; +} + +UINT8 +GetNegotiatedWidth ( + IN PEG_PORT *PegPort + ) +{ + UINT16 Lsts; + + Lsts = MmPci16 (0, PegPort->Bus, PegPort->Device, PegPort->Function, R_SA_PEG_LSTS_OFFSET); + + return (UINT8)((Lsts >> 4) & 0x3F); +} + +EFI_STATUS +RecoverLinkWidth ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_STALL_PPI *StallPpi, + IN PEG_PORT *PegPort, + IN UINT8 OriginalLinkWidth + ) +{ + EFI_STATUS Status; + UINT8 CurrentLinkWidth; + + Status = EFI_SUCCESS; + + CurrentLinkWidth = GetNegotiatedWidth (PegPort); + if (CurrentLinkWidth < OriginalLinkWidth) { + DEBUG ((EFI_D_INFO, "DOWNGRADE from x%d to x%d detected\n", OriginalLinkWidth, CurrentLinkWidth)); + MmPci32AndThenOr ( + 0, + PegPort->Bus, + PegPort->Device, + PegPort->Function, + R_SA_PEG_LTSSMC_OFFSET, + B_SA_PEG_LTSSMC_WIDTH_MASK, + OriginalLinkWidth + ); + MmPci16Or (0, PegPort->Bus, PegPort->Device, PegPort->Function, R_SA_PEG_LCTL_OFFSET, 0x10); + StallPpi->Stall (PeiServices, StallPpi, STALL_ONE_MICRO_SECOND); + MmPci16And (0, PegPort->Bus, PegPort->Device, PegPort->Function, R_SA_PEG_LCTL_OFFSET, 0xFFEF); + Status = WaitForL0 (PeiServices, StallPpi, PegPort, FALSE); + MmPci32Or ( + 0, + PegPort->Bus, + PegPort->Device, + PegPort->Function, + R_SA_PEG_LTSSMC_OFFSET, + 0x1F + ); + if (EFI_ERROR (Status)) { + return Status; + } + + CurrentLinkWidth = GetNegotiatedWidth (PegPort); + if (CurrentLinkWidth == OriginalLinkWidth) { + DEBUG ((EFI_D_INFO, "Width Recovery Successful\n")); + Status = EFI_SUCCESS; + } else { + DEBUG ((EFI_D_INFO, "Width Recovery FAILED!\n")); + Status = EFI_DEVICE_ERROR; + } + } + + return Status; +} + +UINT8 +GetLinkSpeed ( + IN PEG_PORT *PegPort + ) +{ + UINT16 Lsts; + + Lsts = MmPci16 (0, PegPort->Bus, PegPort->Device, PegPort->Function, R_SA_PEG_LSTS_OFFSET); + + return (UINT8)(Lsts & 0xF); +} + +EFI_STATUS +RecoverLinkSpeed ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_STALL_PPI *StallPpi, + IN PEG_PORT *PegPort, + IN UINT8 OriginalLinkSpeed + ) +{ + EFI_STATUS Status; + UINT8 CurrentLinkSpeed; + + Status = EFI_SUCCESS; + + CurrentLinkSpeed = GetLinkSpeed (PegPort); + if (CurrentLinkSpeed < OriginalLinkSpeed) { + DEBUG ((EFI_D_INFO, "DOWNGRADE from Gen %d to Gen %d detected\n", OriginalLinkSpeed, CurrentLinkSpeed)); + + Status = RetrainLink (PeiServices, StallPpi, PegPort); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "Link Speed Recovery FAILED!\n")); + return Status; + } + + CurrentLinkSpeed = GetLinkSpeed (PegPort); + if (CurrentLinkSpeed < OriginalLinkSpeed) { + DEBUG ((EFI_D_INFO, "Link Speed Recovery FAILED!\n")); + Status = EFI_DEVICE_ERROR; + } else { + DEBUG ((EFI_D_INFO, "Link Speed Recovery Successful\n")); + } + } + + return Status; +} + +VOID +PcieTrainingWarmReset ( + IN EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + PCH_RESET_PPI *PchResetPpi; + + Status = (*PeiServices)->LocatePpi ( + PeiServices, + &gPchResetPpiGuid, + 0, + NULL, + &PchResetPpi + ); + ASSERT_EFI_ERROR (Status); + PchResetPpi->Reset (PchResetPpi, WarmReset); + + return; +} + +#endif // PEG_FLAG -- cgit v1.2.3