Selaa lähdekoodia

AmpereAltraPkg: Update Ampere specific platform PCIe core

This patch updates the following:
- Check End Point configuration space is accessible or
  not before getting its capability.
- Add new function to check PCIe card is present or not.
- Set CRS (Configuration Request Retry Status) to follow
  PCIe specifications.

Signed-off-by: Nhi Pham <nhi@os.amperecomputing.com>
Reviewed-by: Leif Lindholm <quic_llindhol@quicinc.com>
Vu Nguyen 1 vuosi sitten
vanhempi
commit
380b4b40c6

+ 121 - 29
Silicon/Ampere/AmpereAltraPkg/Library/Ac01PcieLib/PcieCore.c

@@ -1,6 +1,6 @@
 /** @file
 
-  Copyright (c) 2020 - 2021, Ampere Computing LLC. All rights reserved.<BR>
+  Copyright (c) 2020 - 2023, Ampere Computing LLC. All rights reserved.<BR>
 
   SPDX-License-Identifier: BSD-2-Clause-Patent
 
@@ -10,8 +10,10 @@
 
 #include <Guid/PlatformInfoHob.h>
 #include <Guid/RootComplexInfoHob.h>
+#include <IndustryStandard/Pci.h>
 #include <Library/ArmGenericTimerCounterLib.h>
 #include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
 #include <Library/BoardPcieLib.h>
 #include <Library/DebugLib.h>
 #include <Library/HobLib.h>
@@ -22,6 +24,25 @@
 
 #include "PcieCore.h"
 
+VOID
+EnableDbiAccess (
+  AC01_ROOT_COMPLEX *RootComplex,
+  UINT32            PcieIndex,
+  BOOLEAN           EnableDbi
+  );
+
+BOOLEAN
+EndpointCfgReady (
+  IN AC01_ROOT_COMPLEX  *RootComplex,
+  IN UINT8              PcieIndex,
+  IN UINT32             Timeout
+  );
+
+BOOLEAN
+PcieLinkUpCheck (
+  IN AC01_PCIE_CONTROLLER *Pcie
+  );
+
 /**
   Return the next extended capability base address
 
@@ -41,14 +62,38 @@ GetCapabilityBase (
 {
   BOOLEAN                IsExtCapability = FALSE;
   PHYSICAL_ADDRESS       CfgBase;
+  PHYSICAL_ADDRESS       Ret = 0;
+  PHYSICAL_ADDRESS       RootComplexCfgBase;
   UINT32                 CapabilityId;
   UINT32                 NextCapabilityPtr;
   UINT32                 Val;
+  UINT32                 RestoreVal;
 
-  if (IsRootComplex) {
-    CfgBase = RootComplex->MmcfgBase + (RootComplex->Pcie[PcieIndex].DevNum << DEV_SHIFT);
-  } else {
+  RootComplexCfgBase = RootComplex->MmcfgBase + (RootComplex->Pcie[PcieIndex].DevNum << DEV_SHIFT);
+  if (!IsRootComplex) {
+    // Allow programming to config space
+    EnableDbiAccess (RootComplex, PcieIndex, TRUE);
+
+    Val        = MmioRead32 (RootComplexCfgBase + SEC_LAT_TIMER_SUB_BUS_SEC_BUS_PRI_BUS_REG);
+    RestoreVal = Val;
+    Val        = SUB_BUS_SET (Val, DEFAULT_SUB_BUS);
+    Val        = SEC_BUS_SET (Val, RootComplex->Pcie[PcieIndex].DevNum);
+    Val        = PRIM_BUS_SET (Val, 0x0);
+    MmioWrite32 (RootComplexCfgBase + SEC_LAT_TIMER_SUB_BUS_SEC_BUS_PRI_BUS_REG, Val);
     CfgBase = RootComplex->MmcfgBase + (RootComplex->Pcie[PcieIndex].DevNum << BUS_SHIFT);
+
+    if (!EndpointCfgReady (RootComplex, PcieIndex, EP_LINKUP_TIMEOUT)) {
+      goto _CheckCapEnd;
+    }
+  } else {
+    CfgBase = RootComplexCfgBase;
+  }
+
+  // Check if device provide capability
+  Val = MmioRead32 (CfgBase + PCI_COMMAND_OFFSET);
+  Val = GET_HIGH_16_BITS (Val); /* Status */
+  if (!(Val & EFI_PCI_STATUS_CAPABILITY)) {
+    goto _CheckCapEnd;
   }
 
   Val = MmioRead32 (CfgBase + TYPE1_CAP_PTR_REG);
@@ -58,7 +103,8 @@ GetCapabilityBase (
   while (1) {
     if ((NextCapabilityPtr & WORD_ALIGN_MASK) != 0) {
       // Not alignment, just return
-      return 0;
+      Ret = 0;
+      goto _CheckCapEnd;
     }
 
     Val = MmioRead32 (CfgBase + NextCapabilityPtr);
@@ -69,7 +115,8 @@ GetCapabilityBase (
     }
 
     if (CapabilityId == ExtCapabilityId) {
-      return (CfgBase + NextCapabilityPtr);
+      Ret = (CfgBase + NextCapabilityPtr);
+      goto _CheckCapEnd;
     }
 
     if (NextCapabilityPtr < EXT_CAPABILITY_START_BASE) {
@@ -84,9 +131,20 @@ GetCapabilityBase (
     }
 
     if ((NextCapabilityPtr == 0) && IsExtCapability) {
-      return 0;
+      Ret = 0;
+      goto _CheckCapEnd;
     }
   }
+
+_CheckCapEnd:
+  if (!IsRootComplex) {
+    MmioWrite32 (RootComplexCfgBase + SEC_LAT_TIMER_SUB_BUS_SEC_BUS_PRI_BUS_REG, RestoreVal);
+
+    // Disable programming to config space
+    EnableDbiAccess (RootComplex, PcieIndex, FALSE);
+  }
+
+  return Ret;
 }
 
 /**
@@ -677,6 +735,14 @@ Ac01PcieCoreSetupRC (
     // Hold link training
     StartLinkTraining (RootComplex, PcieIndex, FALSE);
 
+    // Clear BUSCTRL.CfgUrMask to set CRS (Configuration Request Retry Status) to 0xFFFF.FFFF
+    // rather than 0xFFFF.0001 as per PCIe specification requirement. Otherwise, this causes
+    // device drivers respond incorrectly on timeout due to long device operations.
+    TargetAddress = CsrBase + AC01_PCIE_CORE_BUS_CONTROL_REG;
+    Val           = MmioRead32 (TargetAddress);
+    Val          &= ~BUS_CTL_CFG_UR_MASK;
+    MmioWrite32 (TargetAddress, Val);
+
     if (!EnableAxiPipeClock (RootComplex, PcieIndex)) {
       DEBUG ((DEBUG_ERROR, "- Pcie[%d] - PIPE clock is not stable\n", PcieIndex));
       return RETURN_DEVICE_ERROR;
@@ -1067,21 +1133,20 @@ Ac01PFACommand (
   return Ret;
 }
 
-UINT32
+BOOLEAN
 EndpointCfgReady (
-  IN AC01_ROOT_COMPLEX  *RootComplex,
-  IN UINT8              PcieIndex
+  IN AC01_ROOT_COMPLEX *RootComplex,
+  IN UINT8             PcieIndex,
+  IN UINT32            TimeOut
   )
 {
   PHYSICAL_ADDRESS      CfgBase;
-  UINT32                TimeOut;
   UINT32                Val;
 
   CfgBase = RootComplex->MmcfgBase + (RootComplex->Pcie[PcieIndex].DevNum << BUS_SHIFT);
 
   // Loop read CfgBase value until got valid value or
-  // reach to timeout EP_LINKUP_TIMEOUT (or more depend on card)
-  TimeOut = EP_LINKUP_TIMEOUT;
+  // reach to Timeout (or more depend on card)
   do {
     Val = MmioRead32 (CfgBase);
     if (Val != 0xFFFF0001 && Val != 0xFFFFFFFF) {
@@ -1112,6 +1177,7 @@ Ac01PcieCoreGetEndpointInfo (
   )
 {
   PHYSICAL_ADDRESS      CfgBase;
+  PHYSICAL_ADDRESS      EpCfgAddr;
   PHYSICAL_ADDRESS      PcieCapBase;
   PHYSICAL_ADDRESS      SecLatTimerAddr;
   PHYSICAL_ADDRESS      TargetAddress;
@@ -1133,8 +1199,23 @@ Ac01PcieCoreGetEndpointInfo (
   Val = SEC_BUS_SET (Val, RootComplex->Pcie[PcieIndex].DevNum);
   Val = PRIM_BUS_SET (Val, DEFAULT_PRIM_BUS);
   MmioWrite32 (SecLatTimerAddr, Val);
+  EpCfgAddr = RootComplex->MmcfgBase + (RootComplex->Pcie[PcieIndex].DevNum << BUS_SHIFT);
+
+  if (!EndpointCfgReady (RootComplex, PcieIndex, EP_LINKUP_EXTRA_TIMEOUT)) {
+    goto Exit;
+  }
 
-  if (EndpointCfgReady (RootComplex, PcieIndex)) {
+  Val = MmioRead32 (EpCfgAddr);
+  // Check whether EP config space is accessible or not
+  if (Val == 0xFFFFFFFF) {
+    *EpMaxWidth = 0;   // Invalid Width
+    *EpMaxGen   = 0;   // Invalid Speed
+    DEBUG ((DEBUG_ERROR, "PCIE%d.%d Cannot access EP config space!\n", RootComplex->ID, PcieIndex));
+  } else if (Val == 0xFFFF0001) {
+    *EpMaxWidth = 0;   // Invalid Width
+    *EpMaxGen   = 0;   // Invalid Speed
+    DEBUG ((DEBUG_ERROR, "PCIE%d.%d EP config space still not ready to access, need poll more time!!!\n", RootComplex->ID, PcieIndex));
+  } else {
     PcieCapBase = GetCapabilityBase (RootComplex, PcieIndex, FALSE, PCIE_CAPABILITY_ID);
     if (PcieCapBase == 0) {
       DEBUG ((
@@ -1164,6 +1245,7 @@ Ac01PcieCoreGetEndpointInfo (
     }
   }
 
+Exit:
   // Restore value in order to not affect enumeration process
   MmioWrite32 (SecLatTimerAddr, RestoreVal);
 
@@ -1280,6 +1362,30 @@ Ac01PcieCoreQoSLinkCheckRecovery (
   return LINK_CHECK_SUCCESS;
 }
 
+BOOLEAN
+Ac01PcieCoreCheckCardPresent (
+  IN AC01_PCIE_CONTROLLER *PcieController
+  )
+{
+  EFI_PHYSICAL_ADDRESS  TargetAddress;
+  UINT32                ControlValue;
+
+  ControlValue = 0;
+
+  TargetAddress = PcieController->CsrBase;
+
+  ControlValue = MmioRead32 (TargetAddress + AC01_PCIE_CORE_LINK_CTRL_REG);
+
+  if (0 == LTSSMENB_GET (ControlValue)) {
+    //
+    // LTSSMENB is clear to 0x00 by Hardware -> link partner is connected.
+    //
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
 VOID
 Ac01PcieCoreUpdateLink (
   IN  AC01_ROOT_COMPLEX *RootComplex,
@@ -1314,30 +1420,16 @@ Ac01PcieCoreUpdateLink (
         Pcie->LinkUp = TRUE;
         Val = MmioRead32 (CfgBase + PCIE_CAPABILITY_BASE + LINK_CONTROL_LINK_STATUS_REG);
 
-        DEBUG ((
-          DEBUG_INFO,
-          "%a Socket%d RootComplex%d RP%d NEGO_LINK_WIDTH: 0x%x LINK_SPEED: 0x%x\n",
-          __FUNCTION__,
-          RootComplex->Socket,
-          RootComplex->ID,
-          PcieIndex,
-          CAP_NEGO_LINK_WIDTH_GET (Val),
-          CAP_LINK_SPEED_GET (Val)
-          ));
-
         // Doing link checking and recovery if needed
         Ac01PcieCoreQoSLinkCheckRecovery (RootComplex, PcieIndex);
 
-        // Link timeout after 32ms
-        SetLinkTimeout (RootComplex, PcieIndex, 32);
-
         // Un-mask Completion Timeout
         DisableCompletionTimeOut (RootComplex, PcieIndex, FALSE);
 
       } else {
-        *IsNextRoundNeeded = FALSE;
         FailedPciePtr[*FailedPcieCount] = PcieIndex;
         *FailedPcieCount += 1;
+        *IsNextRoundNeeded = !(*IsNextRoundNeeded) ? Ac01PcieCoreCheckCardPresent (Pcie) : TRUE;
       }
     }
   }

+ 7 - 1
Silicon/Ampere/AmpereAltraPkg/Library/Ac01PcieLib/PcieCore.h

@@ -1,6 +1,6 @@
 /** @file
 
-  Copyright (c) 2020 - 2021, Ampere Computing LLC. All rights reserved.<BR>
+  Copyright (c) 2020 - 2023, Ampere Computing LLC. All rights reserved.<BR>
 
   SPDX-License-Identifier: BSD-2-Clause-Patent
 
@@ -39,6 +39,7 @@
 #define PIPE_CLOCK_TIMEOUT               20000       // 20,000 us
 #define LTSSM_TRANSITION_TIMEOUT         100000      // 100 ms in total
 #define EP_LINKUP_TIMEOUT                (10 * 1000) // 10ms
+#define EP_LINKUP_EXTRA_TIMEOUT          (500 * 1000) // 500ms
 #define LINK_WAIT_INTERVAL_US            50
 
 #define PFA_MODE_ENABLE                  0
@@ -80,6 +81,7 @@
 #define AC01_PCIE_CORE_IRQ_ENABLE_REG           0x30
 #define AC01_PCIE_CORE_IRQ_EVENT_STAT_REG       0x38
 #define AC01_PCIE_CORE_BLOCK_EVENT_STAT_REG     0x3C
+#define AC01_PCIE_CORE_BUS_CONTROL_REG          0x40
 #define AC01_PCIE_CORE_RESET_REG                0xC000
 #define AC01_PCIE_CORE_CLOCK_REG                0xC004
 #define AC01_PCIE_CORE_MEM_READY_REG            0xC104
@@ -87,6 +89,7 @@
 
 // AC01_PCIE_CORE_LINK_CTRL_REG
 #define LTSSMENB_SET(dst, src)              (((dst) & ~0x1) | (((UINT32) (src)) & 0x1))
+#define LTSSMENB_GET(dst)                   ((dst) & (BIT0))
 #define   HOLD_LINK_TRAINING                0
 #define   START_LINK_TRAINING               1
 #define DEVICETYPE_SET(dst, src)            (((dst) & ~0xF0) | (((UINT32) (src) << 4) & 0xF0))
@@ -120,6 +123,9 @@
 // AC01_PCIE_CORE_BLOCK_EVENT_STAT_REG
 #define LINKUP_MASK              0x1
 
+// AC01_PCIE_CORE_BUS_CONTROL_REG
+#define BUS_CTL_CFG_UR_MASK      0x8
+
 // AC01_PCIE_CORE_RESET_REG
 #define DWC_PCIE_SET(dst, src)   (((dst) & ~0x1) | (((UINT32) (src)) & 0x1))
 #define   RESET_MASK             0x1