summaryrefslogtreecommitdiff
path: root/ReferenceCode/Chipset/SystemAgent/SaInit/Pei/PcieTraining.c
diff options
context:
space:
mode:
Diffstat (limited to 'ReferenceCode/Chipset/SystemAgent/SaInit/Pei/PcieTraining.c')
-rw-r--r--ReferenceCode/Chipset/SystemAgent/SaInit/Pei/PcieTraining.c628
1 files changed, 628 insertions, 0 deletions
diff --git a/ReferenceCode/Chipset/SystemAgent/SaInit/Pei/PcieTraining.c b/ReferenceCode/Chipset/SystemAgent/SaInit/Pei/PcieTraining.c
new file mode 100644
index 0000000..26f16be
--- /dev/null
+++ b/ReferenceCode/Chipset/SystemAgent/SaInit/Pei/PcieTraining.c
@@ -0,0 +1,628 @@
+/** @file
+ This driver trains the PEG interface.
+
+@copyright
+ Copyright (c) 2012 - 2013 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"
+
+#ifdef PEG_FLAG
+
+UINT16
+GetErrorTarget (
+ IN SA_PLATFORM_POLICY_PPI *SaPlatformPolicyPpi
+ )
+{
+ UINT16 ErrorTarget;
+
+ if ((SaPlatformPolicyPpi->Revision >= SA_PLATFORM_POLICY_PPI_REVISION_3) &&
+ (SaPlatformPolicyPpi->PcieConfig->PegGen3PresetSearchErrorTarget >= 1)) {
+ ErrorTarget = SaPlatformPolicyPpi->PcieConfig->PegGen3PresetSearchErrorTarget;
+ } else {
+ ErrorTarget = 4;
+ }
+
+ return ErrorTarget;
+}
+
+VOID
+GetPortInfo (
+ OUT PORT_INFO *PortInfoList,
+ OUT UINT8 *PortInfoListLength,
+ OUT BOOLEAN *SkipBundle0
+ )
+{
+ UINT32 HwStrap;
+ UINT8 PegBus;
+ UINT8 PegDev;
+ UINT8 PcieController;
+ UINT8 Index;
+ UINT8 Lane;
+ UINT8 LaneIndex;
+ UINT8 FurcationSetup[SA_PEG_MAX_FUN];
+ UINT8 PcieControllerList[SA_PEG_MAX_FUN];
+ UINT8 NumberToCheck;
+ UINT8 StartLane;
+ UINT8 Width;
+ UINT32 Lcap;
+ UINT32 CapOffset;
+ CPU_FAMILY CpuFamilyId;
+ CPU_STEPPING CpuSteppingId;
+
+ PegBus = SA_MC_BUS;
+ PegDev = 1;
+ HwStrap = (McD1PciCfg32(R_SA_PEG_FUSESCMN_OFFSET) >> 16) & 0x3;
+ CpuFamilyId = GetCpuFamily();
+ CpuSteppingId = GetCpuStepping();
+
+ switch(HwStrap) {
+ case SA_PEG_x8_x4_x4:
+ FurcationSetup[0] = 8;
+ FurcationSetup[1] = 4;
+ FurcationSetup[2] = 4;
+ NumberToCheck = 3;
+ break;
+ case SA_PEG_x8_x8_x0:
+ FurcationSetup[0] = 8;
+ FurcationSetup[1] = 8;
+ NumberToCheck = 2;
+ break;
+ default:
+ case SA_PEG_x16_x0_x0:
+ FurcationSetup[0] = 16;
+ NumberToCheck = 1;
+ break;
+ }
+
+ ///
+ /// Figure out which PcieControllers are enabled
+ ///
+ (*PortInfoListLength) = 0;
+ for (PcieController = 0; PcieController < NumberToCheck; PcieController++) {
+ ///
+ /// Sanity check to make sure width > 0
+ ///
+ if (FurcationSetup[PcieController] == 0) {
+ continue;
+ }
+
+ ///
+ /// Check to make sure the Root Port Exists
+ ///
+ if (MmPci16 (0, PegBus, PegDev, PcieController, PCI_VID) == 0xFFFF) {
+ continue;
+ }
+
+ ///
+ /// Add the PcieController to the list of enabled controllers
+ ///
+ PcieControllerList[(*PortInfoListLength)] = PcieController;
+ (*PortInfoListLength)++;
+ }
+
+ ///
+ /// If needed, skip Bundle 0's preset search and use Bundle 1's preset instead.
+ ///
+ (*SkipBundle0) = FALSE;
+ if (((CpuFamilyId == EnumCpuHsw) && (CpuSteppingId < EnumHswC0)) ||
+ ((CpuFamilyId == EnumCpuCrw) && (CpuSteppingId < EnumCrwC0))) {
+ (*SkipBundle0) = TRUE;
+ }
+ if ((*SkipBundle0)) {
+ DEBUG ((EFI_D_INFO, "Skipping each controller's Lane 0-1 preset searches; using their Lane 2 preset instead.\n"));
+ }
+
+ StartLane = 0;
+ for (Index = 0; Index < (*PortInfoListLength); Index++) {
+ PcieController = PcieControllerList[Index];
+
+ ///
+ /// Get information for the current port
+ ///
+ (PortInfoList[Index]).EnableMargin = TRUE;
+ (PortInfoList[Index]).FoundUsablePreset = FALSE;
+ (PortInfoList[Index]).PegPort.Bus = PegBus;
+ (PortInfoList[Index]).PegPort.Device = PegDev;
+ (PortInfoList[Index]).PegPort.Function = PcieController;
+ (PortInfoList[Index]).PegPort.Index = PcieController;
+ (PortInfoList[Index]).PegPort.EndpointMaxLinkSpeed = 0;
+ ReportPcieLinkStatus (PegBus, PegDev, PcieController);
+ Width = GetNegotiatedWidth(&((PortInfoList[Index]).PegPort));
+ (PortInfoList[Index]).LaneListLength = Width;
+ for (Lane = 0, LaneIndex = 0; Lane < Width; Lane++) {
+ if ((*SkipBundle0)) {
+ if ((Lane == 0) || (Lane == 1)) {
+ (PortInfoList[Index]).LaneListLength--;
+ continue;
+ }
+ }
+ if (LaneIndex < SA_PEG_MAX_LANE) {
+ (PortInfoList[Index]).LaneList[LaneIndex] = (Lane + StartLane);
+ }
+ LaneIndex++;
+ }
+
+ ///
+ /// Check that both root port and endpoint support Gen3
+ ///
+ Lcap = MmPci32 (0, PegBus, PegDev, PcieController, R_SA_PEG_LCAP_OFFSET);
+ if ((Lcap & 0x0F) != 3) {
+ DEBUG ((EFI_D_INFO, " PEG%x%x (%x:%x:%x) - Root Port is not Gen3-capable. Max Link Speed = %d.\n",
+ PegDev, PcieController, PegBus, PegDev, PcieController, Lcap & 0x0F));
+ (PortInfoList[Index]).LinkIsGen3Capable = FALSE;
+ } else {
+
+ DEBUG ((EFI_D_INFO, " PEG%x%x (%x:%x:%x) - Root Port is Gen3-capable.\n",
+ PegDev, PcieController, PegBus, PegDev, PcieController));
+
+ ///
+ /// Set PEG PortBus = 1 to Read Endpoint.
+ ///
+ MmPci32AndThenOr (0, PegBus, PegDev, PcieController, PCI_PBUS, 0xFF0000FF, 0x00010100);
+
+ ///
+ /// A config write is required in order for the device to re-capture the Bus number,
+ /// according to PCI Express Base Specification, 2.2.6.2
+ /// Write to a read-only register VendorID to not cause any side effects.
+ ///
+ MmPci16 (0, 1, 0, 0, PCI_VID) = 0;
+
+ ///
+ /// Save end point vendor id and device id
+ ///
+ (PortInfoList[Index]).EndPointVendorIdDeviceId = MmPci32 (0, 1, 0, 0, 0);
+
+ ///
+ /// Negotiation Done?
+ ///
+ if ((MmPci16 (0, PegBus, PegDev, PcieController, R_SA_PEG_VC0RSTS_OFFSET) & BIT1) != 0) {
+ (PortInfoList[Index]).LinkIsGen3Capable = FALSE;
+ DEBUG ((EFI_D_INFO, " PEG%x%x (%x:%x:%x) - VC0 negotiation is pending! Skipping endpoint.\n",
+ PegDev, PcieController, PegBus, PegDev, PcieController, Lcap & 0x0F));
+ ReportPcieLinkStatus (PegBus, PegDev, PcieController);
+ } else {
+ ///
+ /// Get the pointer to the Port PCI Express Capability Structure.
+ ///
+ CapOffset = PcieFindCapId (1, 0, 0, EFI_PCI_CAPABILITY_ID_PCIEXP);
+ if (CapOffset == 0) {
+ (PortInfoList[Index]).LinkIsGen3Capable = FALSE;
+ DEBUG ((EFI_D_INFO, " PEG%x%x (%x:%x:%x) - Endpoint is not Gen3-capable. No PCIe Capability found.\n",
+ PegDev, PcieController, PegBus, PegDev, PcieController, Lcap & 0x0F));
+ } else {
+ Lcap = MmPci32 (0, 1, 0, 0, CapOffset + 0x0C);
+ (PortInfoList[Index]).PegPort.EndpointMaxLinkSpeed = Lcap & 0x0F;
+ if ((Lcap & 0x0F) < 3) {
+ (PortInfoList[Index]).LinkIsGen3Capable = FALSE;
+ DEBUG ((EFI_D_INFO, " PEG%x%x (%x:%x:%x) - Endpoint is not Gen3-capable. Max Link Speed = %d.\n",
+ PegDev, PcieController, PegBus, PegDev, PcieController, Lcap & 0x0F));
+ } else {
+ (PortInfoList[Index]).LinkIsGen3Capable = TRUE;
+ DEBUG ((EFI_D_INFO, " PEG%x%x (%x:%x:%x) - Endpoint is Gen3-capable\n",
+ PegDev, PcieController, PegBus, PegDev, PcieController, Lcap & 0x0F));
+ }
+ }
+ }
+ ///
+ /// Restore bus numbers on the PEG bridge.
+ ///
+ MmPci32And (0, PegBus, PegDev, PcieController, PCI_PBUS, 0xFF0000FF);
+ }
+
+ StartLane += FurcationSetup[PcieController];
+ } ///< End of for each port
+
+ return;
+}
+
+EFI_STATUS
+RunMarginTest (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN SA_PLATFORM_POLICY_PPI *SaPlatformPolicyPpi,
+ IN SA_DATA_HOB *SaDataHob,
+ IN PEI_STALL_PPI *StallPpi,
+ IN UINT32 MonitorPort,
+ IN PORT_INFO *PortInfoList,
+ IN UINT8 PortInfoListLength,
+ IN MARGIN_TEST_TYPE MarginTest,
+ OUT INT32 *Margins
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *LaneList;
+ UINT8 Lane;
+ UINT8 PortListIndex;
+ UINT8 OriginalSpeed;
+ UINT8 OriginalWidth;
+ UINT8 LaneListLength;
+ PEG_PORT *PegPort;
+
+ Status = EFI_SUCCESS;
+
+ ///
+ /// Initialize Margins to -1. Since -1 is an invalid value, we know that lane wasn't tested if its margin == -1
+ ///
+ for (Lane = 0; Lane < SA_PEG_MAX_LANE; Lane++) {
+ Margins[Lane] = -1;
+ }
+
+ for (PortListIndex = 0; PortListIndex < PortInfoListLength; PortListIndex++) {
+ ///
+ /// Test all lanes associated with this the current port
+ ///
+ LaneList = &((PortInfoList[PortListIndex]).LaneList[0]);
+ LaneListLength = (PortInfoList[PortListIndex]).LaneListLength;
+ PegPort = &((PortInfoList[PortListIndex]).PegPort);
+ if ((PortInfoList[PortListIndex]).EnableMargin == FALSE ||
+ (PortInfoList[PortListIndex]).SkipMargin == TRUE) {
+ continue;
+ }
+
+ if ((PortInfoList[PortListIndex]).LinkIsGen3Capable) {
+ OriginalSpeed = 3;
+ } else {
+ OriginalSpeed = GetLinkSpeed (PegPort);
+ }
+ OriginalWidth = GetNegotiatedWidth (PegPort);
+
+ switch (MarginTest) {
+ case LaneLevelRxJitter:
+ Status = LaneLevelJitterTest (
+ PeiServices,
+ SaPlatformPolicyPpi,
+ SaDataHob,
+ StallPpi,
+ MonitorPort,
+ LaneList,
+ LaneListLength,
+ PegPort,
+ OriginalSpeed,
+ OriginalWidth,
+ FALSE,
+ Margins
+ );
+ break;
+ default:
+ DEBUG ((EFI_D_WARN, "Invalid Margin Test Requested.\n"));
+ break;
+ }
+ }
+
+ return Status;
+}
+
+EFI_STATUS
+LaneLevelJitterTest (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN SA_PLATFORM_POLICY_PPI *SaPlatformPolicyPpi,
+ IN SA_DATA_HOB *SaDataHob,
+ IN PEI_STALL_PPI *StallPpi,
+ IN UINT32 MonitorPort,
+ IN UINT8 *LaneList,
+ IN UINT8 LaneListLength,
+ IN PEG_PORT *PegPort,
+ IN UINT8 OriginalLinkSpeed,
+ IN UINT8 OriginalLinkWidth,
+ IN BOOLEAN TxJitterTest,
+ OUT INT32 *Margins
+ )
+{
+ EFI_STATUS Status;
+ UINT8 LaneListIndex;
+ INT8 Jitter;
+ UINT32 Errors;
+ UINT32 PreviousErrors;
+ UINT8 Lane;
+ UINT32 RecoveryCount;
+ BOOLEAN AbortMargin;
+ UINT16 ErrorTarget;
+ UINT8 ConvergenceCounter;
+ INT32 LastMargin;
+ INT32 MarginDifference;
+ INT32 StartJitter;
+ INT8 MarginDirection;
+ UINT8 RepeatCount;
+
+ ErrorTarget = GetErrorTarget (SaPlatformPolicyPpi);
+
+ for (LaneListIndex = 0; LaneListIndex < LaneListLength; LaneListIndex++) {
+ Lane = LaneList[LaneListIndex];
+ Errors = 0;
+ AbortMargin = FALSE;
+ Margins[Lane] = 0;
+ MarginDirection = 1;
+
+ if (TxJitterTest) {
+ ConfigureTxJitterMux (Lane, SaPlatformPolicyPpi->PlatformData->MchBar);
+ EnableTxJitterInjection (Lane, TRUE);
+ }
+
+ ///
+ /// Determine value to start at
+ ///
+ if (LaneListIndex == 0) {
+ StartJitter = 0;
+ } else {
+ StartJitter = (Margins[LaneList[LaneListIndex - 1]] / 100) - JITTER_MARGIN_INITIAL_OFFSET;
+ if (StartJitter < 0) {
+ StartJitter = 0;
+ }
+ }
+
+ ConvergenceCounter = 0;
+ LastMargin = -1;
+ RepeatCount = 0;
+ while (ConvergenceCounter < MARGIN_CONVERGANCE_MIN_MATCHES && RepeatCount < MARGIN_CONVERGANCE_MAX_REPEATS) {
+ RepeatCount++;
+ ///
+ /// Determine whether to go up or down from starting point
+ ///
+ AbortMargin = FALSE;
+ RecoveryCount = SaPcieGetErrorCount (MonitorPort, PegPort->Function);
+ Status = SetJitterTolerance (&Lane, 1, (UINT8) StartJitter);
+ ASSERT_EFI_ERROR (Status);
+
+ Errors = SaPciePointTest (PeiServices, SaPlatformPolicyPpi, StallPpi, MonitorPort, PegPort, RecoveryCount);
+ if (Errors >= ErrorTarget) {
+ if (StartJitter == 0) {
+ Margins[Lane] = 0;
+ AbortMargin = TRUE;
+ } else {
+ MarginDirection = -1;
+ }
+ } else {
+ MarginDirection = 1;
+ }
+ for (Jitter = (INT8) (StartJitter + MarginDirection);
+ Jitter < JITTER_LENGTH &&
+ Jitter >= 0 &&
+ (!AbortMargin);
+ Jitter = (INT8) (Jitter + MarginDirection)) {
+ ///
+ /// Check for a link downgrade
+ ///
+ AbortMargin = LinkIsDowngraded (PegPort, OriginalLinkSpeed, OriginalLinkWidth);
+ if (AbortMargin) {
+ if (MarginDirection < 0) {
+ LastMargin = -1;
+ ConvergenceCounter = 0;
+ StartJitter = 0;
+ MarginDirection = 1;
+ } else {
+ if (Jitter == 0) {
+ Margins[Lane] = 0;
+ } else {
+ Margins[Lane] = (Jitter - 1) * 100;
+ }
+ }
+ break;
+ }
+
+ ///
+ /// Get initial recovery count
+ ///
+ RecoveryCount = SaPcieGetErrorCount (MonitorPort, PegPort->Function);
+ Status = SetJitterTolerance (&Lane, 1, Jitter);
+ ASSERT_EFI_ERROR (Status);
+
+ PreviousErrors = Errors;
+ Errors = SaPciePointTest (PeiServices, SaPlatformPolicyPpi, StallPpi, MonitorPort, PegPort, RecoveryCount);
+ if (MarginDirection < 0) {
+ if (Errors < ErrorTarget) { ///< Downward direction has started passing
+ Margins[Lane] = InterpolateMargin (ErrorTarget, PreviousErrors, Errors, (INT32) Jitter);
+ break;
+ }
+ } else {
+ if (Errors >= ErrorTarget) { ///< Upward direction has started failing
+ Margins[Lane] = InterpolateMargin (ErrorTarget, Errors, PreviousErrors, (INT32) Jitter);
+ break;
+ }
+ }
+ } ///< End of for loop
+
+ ///
+ /// Check if we never reached the error target
+ ///
+ if (MarginDirection < 0) {
+ if ((Errors >= ErrorTarget) && (!AbortMargin)) {
+ Margins[Lane] = 0;
+ }
+ } else {
+ if ((Errors < ErrorTarget) && (!AbortMargin)) {
+ Margins[Lane] = JITTER_LENGTH * 100;
+ }
+ }
+
+ ///
+ /// Compute the next margin point to start at
+ ///
+ StartJitter = (Margins[Lane] / 100) - JITTER_MARGIN_INITIAL_OFFSET;
+ if (StartJitter < 0) {
+ StartJitter = 0;
+ }
+
+ ///
+ /// Check for convergance
+ ///
+ if (LastMargin == -1) {
+ LastMargin = Margins[Lane];
+ } else {
+ MarginDifference = CalculateMarginDifference (LastMargin, Margins[Lane]);
+ if (MarginDifference <= MARGIN_CONVERGANCE_ALLOWED_DELTA) {
+ ConvergenceCounter++;
+ } else {
+ ConvergenceCounter = 0;
+ }
+ LastMargin = Margins[Lane];
+ }
+
+ if (LinkIsDowngraded (PegPort, OriginalLinkSpeed, OriginalLinkWidth)) {
+ Status = SetJitterTolerance (&Lane, 1, 0);
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ ///
+ /// If the link degraded in any way, bring it back to functional state
+ ///
+ Status = EnsureLinkIsHealthy (PeiServices, SaPlatformPolicyPpi, SaDataHob, StallPpi, PegPort, OriginalLinkSpeed, OriginalLinkWidth);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ } ///< End of repeat while loop
+
+ ///
+ /// Remove Jitter in preparation for testing the next lane
+ ///
+ Status = SetJitterTolerance (&Lane, 1, 0);
+ ASSERT_EFI_ERROR (Status);
+ if (TxJitterTest) {
+ EnableTxJitterInjection (Lane, FALSE);
+ }
+ } ///< End of for each lane loop
+
+ return EFI_SUCCESS;
+}
+
+UINT32
+SaPciePointTest (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN SA_PLATFORM_POLICY_PPI *SaPlatformPolicyPpi,
+ IN PEI_STALL_PPI *StallPpi,
+ IN UINT32 MonitorPort,
+ IN PEG_PORT *PegPort,
+ IN UINT32 InitialRecoveryCount
+ )
+{
+ UINT32 Data32;
+
+ StallPpi->Stall (PeiServices, StallPpi, SaPlatformPolicyPpi->PcieConfig->PegGen3PresetSearchDwellTime);
+ Data32 = SaPcieGetErrorCount (MonitorPort, PegPort->Function) - InitialRecoveryCount;
+
+ return Data32;
+}
+
+INT32
+CalculateMarginDifference (
+ IN INT32 Margin1,
+ IN INT32 Margin2
+ )
+{
+ if (Margin1 < Margin2) {
+ return Margin2 - Margin1;
+ } else {
+ return Margin1 - Margin2;
+ }
+}
+
+INT32
+InterpolateMargin (
+ IN UINT32 ErrorTarget,
+ IN UINT32 CurrentErrorCount,
+ IN UINT32 PreviousErrorCount,
+ IN INT32 FailingPoint
+ )
+{
+ UINT32 LnErrorTarget;
+ UINT32 LnCurrentErrorCount;
+ UINT32 LnPreviousErrorCount;
+ INT32 Margin;
+
+ if (ErrorTarget > 40000) {
+ ErrorTarget = 40000;
+ }
+ if (CurrentErrorCount > 40000) {
+ CurrentErrorCount = 40000;
+ }
+ if (PreviousErrorCount > 40000) {
+ PreviousErrorCount = 40000;
+ }
+
+ LnErrorTarget = NaturalLog (ErrorTarget * 100);
+ LnCurrentErrorCount = NaturalLog (CurrentErrorCount * 100);
+ LnPreviousErrorCount = NaturalLog (PreviousErrorCount * 100);
+
+ if (FailingPoint >= 0) {
+ if ((LnCurrentErrorCount - LnPreviousErrorCount) == 0) {
+ Margin = (FailingPoint - 1) * 100;
+ } else {
+ Margin = ((LnErrorTarget - LnPreviousErrorCount) * 100) /
+ (LnCurrentErrorCount - LnPreviousErrorCount) +
+ ((FailingPoint - 1) * 100);
+ }
+ if (Margin < 0) {
+ Margin = 0;
+ }
+ } else {
+ if ((LnCurrentErrorCount - LnPreviousErrorCount) == 0) {
+ Margin = (FailingPoint + 1) * 100;
+ } else {
+ Margin = ((FailingPoint + 1) * 100) -
+ ((LnErrorTarget - LnPreviousErrorCount) * 100) /
+ (LnCurrentErrorCount - LnPreviousErrorCount);
+ }
+ if (Margin > 0) {
+ Margin = 0;
+ }
+ }
+
+ return Margin;
+}
+
+
+UINT32
+NaturalLog (
+ IN UINT32 Input
+ )
+ /*++
+
+ Routine Description:
+
+ This function calculates the Natural Log of the Input parameter using integers
+
+ Arguments:
+
+ Input - 100 times a number to get the Natural log from.
+ - Max Input Number is 40,000 (without 100x)
+
+ Returns:
+
+ Output - 100 times the actual result. Accurate within +/- 2
+
+ --*/
+{
+ UINT32 Output;
+
+ ///
+ ///Special case - treat 0 recoveries as 1 recovery for interpolation purposes
+ ///
+ if (Input == 0) {
+ return 0;
+ }
+
+ Output = 0;
+ while (Input > 271) {
+ Input = (Input * 1000) / 2718;
+ Output += 100;
+ }
+
+ Output += ((-16 * Input * Input + 11578 * Input - 978860) / 10000);
+
+ return Output;
+}
+
+#endif // PEG_FLAG