[edk2-devel] [edk2-staging/UEFI_PCI_ENHANCE-2 PATCH 07/12] PciBusDxe: New PCI Express feature Completion Timeout

Javeed, Ashraf posted 12 patches 4 years, 9 months ago
[edk2-devel] [edk2-staging/UEFI_PCI_ENHANCE-2 PATCH 07/12] PciBusDxe: New PCI Express feature Completion Timeout
Posted by Javeed, Ashraf 4 years, 9 months ago
BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2313

The code changes are made; as per the PCI Express Base Specification 4
Revision 1; to enable the configuration of PCI Express feature Completion
Timeout (CTO), that enables the PCI function to wait on programmed dura-
tion for its transactions before timeout, or disable its detection mecha-
nism.

The code changes are made to configure only those PCI devices which are
requested by platform for override through the new PCI Express Platform
protocol interface for device-specific policies. The changes are made to
also comply with the device-specific capability attributes.

The code follows the below implementation specific rules in case the req-
uested platform policy does not match with the device-specific capability
attributes:-
(1) if device is capable of Range A only and if platform ask for any of
    ranges B, C, D; than this implementation will only program the default
    range value for the duration of 50us to 50ms
(2) if device is capable of Range B, or range B & C, or Ranges B, C & D
    only and if the platform ask for the Range A; than this implementation
    will only program the default range value for the duration of 50us to
    50ms
(3) if the device is capable of Range B only, or the ranges A & B; and if
    the platform ask for Range C, or Range D values, than this implement-
    ation will only program the Range B value for the duration of 65ms to
    210ms
(4) if the device is capable of Ranges B & C, or Ranges A, B, and C; and
    if the platform ask for Range D values; than this implementation will
    only program the Range C for the duration of 1s to 3.5s

Signed-off-by: Ashraf Javeed <ashraf.javeed@intel.com>
Cc: Jian J Wang <jian.j.wang@intel.com>
Cc: Hao A Wu <hao.a.wu@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
---
 MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.h             |   1 +
 MdeModulePkg/Bus/Pci/PciBusDxe/PciExpressFeatures.c | 387 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 MdeModulePkg/Bus/Pci/PciBusDxe/PciExpressFeatures.h |  35 +++++++++++++++++++++++++++++++++++
 MdeModulePkg/Bus/Pci/PciBusDxe/PciFeatureSupport.c  |   8 +++++++-
 MdeModulePkg/Bus/Pci/PciBusDxe/PciPlatformSupport.c | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 561 insertions(+), 1 deletion(-)

diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.h b/MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.h
index e610b52..9b03c12 100644
--- a/MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.h
+++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.h
@@ -294,6 +294,7 @@ struct _PCI_IO_DEVICE {
   UINT8                                     SetupMRRS;
   PCI_FEATURE_POLICY                        SetupRO;
   PCI_FEATURE_POLICY                        SetupNS;
+  PCI_FEATURE_POLICY                        SetupCTO;
 };
 
 #define PCI_IO_DEVICE_FROM_PCI_IO_THIS(a) \
diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciExpressFeatures.c b/MdeModulePkg/Bus/Pci/PciBusDxe/PciExpressFeatures.c
index df85366..f3f4d39 100644
--- a/MdeModulePkg/Bus/Pci/PciBusDxe/PciExpressFeatures.c
+++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciExpressFeatures.c
@@ -521,3 +521,390 @@ ProgramNoSnoop (
   return Status;
 }
 
+/**
+  To determine the CTO Range A values
+
+  @param  CtoValue    input CTO range value from 0 to 14
+  @retval TRUE        the given CTO value belongs to Range A
+          FALSE       the given value does not belong to Range A
+**/
+BOOLEAN
+IsCtoRangeA (
+  IN  UINT8   CtoValue
+  )
+{
+  switch (CtoValue) {
+    case  PCIE_COMPLETION_TIMEOUT_50US_100US:
+    case  PCIE_COMPLETION_TIMEOUT_1MS_10MS:
+      return TRUE;
+  }
+  return FALSE;
+}
+
+/**
+  To determine the CTO Range B values
+
+  @param  CtoValue    input CTO range value from 0 to 14
+  @retval TRUE        the given CTO value belongs to Range B
+          FALSE       the given value does not belong to Range B
+**/
+BOOLEAN
+IsCtoRangeB (
+  IN  UINT8   CtoValue
+  )
+{
+  switch (CtoValue) {
+    case  PCIE_COMPLETION_TIMEOUT_16MS_55MS:
+    case  PCIE_COMPLETION_TIMEOUT_65MS_210MS:
+      return TRUE;
+  }
+  return FALSE;
+}
+
+/**
+  To determine the CTO Range C values
+
+  @param  CtoValue    input CTO range value from 0 to 14
+  @retval TRUE        the given CTO value belongs to Range C
+          FALSE       the given value does not belong to Range C
+**/
+BOOLEAN
+IsCtoRangeC (
+  IN  UINT8   CtoValue
+  )
+{
+  switch (CtoValue) {
+    case  PCIE_COMPLETION_TIMEOUT_260MS_900MS:
+    case  PCIE_COMPLETION_TIMEOUT_1S_3_5S:
+      return TRUE;
+  }
+  return FALSE;
+}
+
+/**
+  To determine the CTO Range D values
+
+  @param  CtoValue    input CTO range value from 0 to 14
+  @retval TRUE        the given CTO value belongs to Range D
+          FALSE       the given value does not belong to Range D
+**/
+BOOLEAN
+IsCtoRangeD (
+  IN  UINT8   CtoValue
+  )
+{
+  switch (CtoValue) {
+    case  PCIE_COMPLETION_TIMEOUT_4S_13S:
+    case  PCIE_COMPLETION_TIMEOUT_17S_64S:
+      return TRUE;
+  }
+  return FALSE;
+}
+
+/**
+  The main routine which setup the PCI feature Completion Timeout as per the
+  device-specific platform policy, as well as in complaince with the PCI Base
+  specification Revision 4.
+
+  @param PciDevice                      A pointer to the PCI_IO_DEVICE.
+
+  @retval EFI_SUCCESS                   processing of PCI feature CTO is successful.
+**/
+EFI_STATUS
+SetupCompletionTimeout (
+  IN PCI_IO_DEVICE          *PciDevice,
+  IN VOID                   *PciExFeatureConfiguration
+  )
+{
+  PCI_REG_PCIE_DEVICE_CAPABILITY2 DeviceCap2;
+  UINT8                           CtoRangeValue;
+
+  if (!PciDevice->SetupCTO.Override) {
+    //
+    // No override of CTO is required for this device
+    //
+    return  EFI_SUCCESS;
+  }
+
+  //
+  // determine the CTO range values as per its device capability register
+  //
+  DeviceCap2.Uint32 = PciDevice->PciExpressCapabilityStructure.DeviceCapability2.Uint32;
+  if (!DeviceCap2.Bits.CompletionTimeoutRanges
+      && !DeviceCap2.Bits.CompletionTimeoutDisable
+  ) {
+    //
+    // device does not support the CTO mechanism, hence no override is applicable
+    //
+    return EFI_SUCCESS;
+  }
+
+  //
+  // override the device CTO values if applicable
+  //
+  if (PciDevice->SetupCTO.Act) {
+    //
+    // program the CTO range values
+    //
+    if (DeviceCap2.Bits.CompletionTimeoutRanges) {
+      CtoRangeValue = PCIE_COMPLETION_TIMEOUT_50US_50MS;
+      //
+      // in case if the supported CTO range and the requirement from platform
+      // policy does not match, than the CTO range setting would be based on
+      // this driver's implementation specific, and its rules are as follows:-
+      //
+      // if device is capable of Range A only and if platform ask for any of
+      // ranges B, C, D; than this implementation will only program the default
+      // range value for the duration of 50us to 50ms.
+      //
+      // if device is capable of Range B, or range B & C, or Ranges B, C & D only
+      // and if the platform ask for the Range A; than this implementation will
+      // only program the default range value for the duration of 50us to 50ms.
+      //
+      // if the device is capable of Range B only, or the ranges A & B; and the
+      // platform ask for Range C, or Range D values, than this implementation
+      // will only program the Range B value for the duration of 65ms to 210ms.
+      //
+      // if the device is capable of Ranges B & C, or Ranges A, B, and C; and
+      // if the platform ask for Range D values; than this implementation will
+      // only program the Range C for the duration of 1s to 3.5s.
+      //
+
+      switch (DeviceCap2.Bits.CompletionTimeoutRanges) {
+        case  PCIE_COMPLETION_TIMEOUT_RANGE_A_SUPPORTED:
+          if (IsCtoRangeA (PciDevice->SetupCTO.Support)) {
+            CtoRangeValue = PciDevice->SetupCTO.Support;
+          }
+          //
+          // if device is capable of Range A only and if platform ask for any of
+          // ranges B, C, D; than this implementation will only program the default
+          // range value for the duration of 50us to 50ms.
+          //
+          if (IsCtoRangeB (PciDevice->SetupCTO.Support)
+              || IsCtoRangeC (PciDevice->SetupCTO.Support)
+              || IsCtoRangeD (PciDevice->SetupCTO.Support)
+          ) {
+            CtoRangeValue = PCIE_COMPLETION_TIMEOUT_50US_50MS;
+          }
+          break;
+
+        case  PCIE_COMPLETION_TIMEOUT_RANGE_B_SUPPORTED:
+          //
+          // if device is capable of Range B, or range B & C, or Ranges B, C & D only
+          // and if the platform ask for the Range A; than this implementation will
+          // only program the default range value for the duration of 50us to 50ms.
+          //
+          if (IsCtoRangeA (PciDevice->SetupCTO.Support)) {
+            CtoRangeValue = PCIE_COMPLETION_TIMEOUT_50US_50MS;
+          }
+
+          if (IsCtoRangeB (PciDevice->SetupCTO.Support)) {
+            CtoRangeValue = PciDevice->SetupCTO.Support;
+          }
+          //
+          // if the device is capable of Range B only, or the ranges A & B; and the
+          // platform ask for Range C, or Range D values, than this implementation
+          // will only program the Range B value for the duration of 65ms to 210ms.
+          //
+          if (IsCtoRangeC (PciDevice->SetupCTO.Support)
+              || IsCtoRangeD (PciDevice->SetupCTO.Support)
+          ) {
+            CtoRangeValue = PCIE_COMPLETION_TIMEOUT_65MS_210MS;
+          }
+          break;
+
+        case  PCIE_COMPLETION_TIMEOUT_RANGE_B_C_SUPPORTED:
+          if (IsCtoRangeA (PciDevice->SetupCTO.Support)) {
+            CtoRangeValue = PCIE_COMPLETION_TIMEOUT_50US_50MS;
+          }
+
+          if (IsCtoRangeB (PciDevice->SetupCTO.Support)
+              || IsCtoRangeC (PciDevice->SetupCTO.Support)
+              ) {
+            CtoRangeValue = PciDevice->SetupCTO.Support;
+          }
+          //
+          // if the device is capable of Ranges B & C, or Ranges A, B, and C; and
+          // if the platform ask for Range D values; than this implementation will
+          // only program the Range C for the duration of 1s to 3.5s.
+          //
+          if (IsCtoRangeD (PciDevice->SetupCTO.Support)) {
+            CtoRangeValue = PCIE_COMPLETION_TIMEOUT_1S_3_5S;
+          }
+          break;
+
+        case  PCIE_COMPLETION_TIMEOUT_RANGE_B_C_D_SUPPORTED:
+          if (IsCtoRangeA (PciDevice->SetupCTO.Support)) {
+            CtoRangeValue = PCIE_COMPLETION_TIMEOUT_50US_50MS;
+          }
+          if (IsCtoRangeB (PciDevice->SetupCTO.Support)
+              || IsCtoRangeC (PciDevice->SetupCTO.Support)
+              || IsCtoRangeD (PciDevice->SetupCTO.Support)
+          ) {
+            CtoRangeValue = PciDevice->SetupCTO.Support;
+          }
+          break;
+
+        case  PCIE_COMPLETION_TIMEOUT_RANGE_A_B_SUPPORTED:
+          if (IsCtoRangeA (PciDevice->SetupCTO.Support)
+              || IsCtoRangeB (PciDevice->SetupCTO.Support)
+              ) {
+            CtoRangeValue = PciDevice->SetupCTO.Support;
+          }
+          if (IsCtoRangeC (PciDevice->SetupCTO.Support)
+              || IsCtoRangeD (PciDevice->SetupCTO.Support)
+          ) {
+            CtoRangeValue = PCIE_COMPLETION_TIMEOUT_65MS_210MS;
+          }
+          break;
+
+        case  PCIE_COMPLETION_TIMEOUT_RANGE_A_B_C_SUPPORTED:
+          if (IsCtoRangeA (PciDevice->SetupCTO.Support)
+              || IsCtoRangeB (PciDevice->SetupCTO.Support)
+              || IsCtoRangeC (PciDevice->SetupCTO.Support)
+          ) {
+            CtoRangeValue = PciDevice->SetupCTO.Support;
+          }
+          if (IsCtoRangeD (PciDevice->SetupCTO.Support)) {
+            CtoRangeValue = PCIE_COMPLETION_TIMEOUT_1S_3_5S;
+          }
+          break;
+
+        case  PCIE_COMPLETION_TIMEOUT_RANGE_A_B_C_D_SUPPORTED:
+          if (IsCtoRangeA (PciDevice->SetupCTO.Support)
+              || IsCtoRangeB (PciDevice->SetupCTO.Support)
+              || IsCtoRangeC (PciDevice->SetupCTO.Support)
+              || IsCtoRangeD (PciDevice->SetupCTO.Support)
+          ) {
+            CtoRangeValue = PciDevice->SetupCTO.Support;
+          }
+          break;
+
+        default:
+          DEBUG ((
+            DEBUG_ERROR,
+            "Invalid CTO range: %d\n",
+            DeviceCap2.Bits.CompletionTimeoutRanges
+            ));
+          return EFI_INVALID_PARAMETER;
+      }
+
+      if (PciDevice->SetupCTO.Support != CtoRangeValue) {
+        PciDevice->SetupCTO.Support = CtoRangeValue;
+      }
+    }
+    DEBUG (( DEBUG_INFO, "CTO enable: %d, CTO range: 0x%x,",
+        PciDevice->SetupCTO.Act,
+        PciDevice->SetupCTO.Support
+    ));
+  }
+  return EFI_SUCCESS;
+}
+
+/**
+  Overrides the PCI Device Control2 register Completion Timeout range; if
+  the hardware value is different than the intended value.
+
+  @param  PciDevice             A pointer to the PCI_IO_DEVICE instance.
+
+  @retval EFI_SUCCESS           The data was read from or written to the PCI device.
+  @retval EFI_UNSUPPORTED       The address range specified by Offset, Width, and Count is not
+                                valid for the PCI configuration header of the PCI controller.
+  @retval EFI_INVALID_PARAMETER Buffer is NULL or Width is invalid.
+
+**/
+EFI_STATUS
+ProgramCompletionTimeout (
+  IN PCI_IO_DEVICE          *PciDevice,
+  IN VOID                   *PciExFeatureConfiguration
+  )
+{
+  PCI_REG_PCIE_DEVICE_CONTROL2    DeviceCtl2;
+  PCI_REG_PCIE_DEVICE_CAPABILITY2 DeviceCap2;
+  UINT32                          Offset;
+  EFI_STATUS                      Status;
+  EFI_TPL                         OldTpl;
+
+  if (!PciDevice->SetupCTO.Override) {
+    //
+    // No override of CTO is required for this device
+    //
+    DEBUG (( DEBUG_INFO, "CTO skipped,"));
+    return  EFI_SUCCESS;
+  }
+
+  //
+  // to program the CTO range values, determine in its device capability register
+  //
+  DeviceCap2.Uint32 = PciDevice->PciExpressCapabilityStructure.DeviceCapability2.Uint32;
+  if (DeviceCap2.Bits.CompletionTimeoutRanges
+      || DeviceCap2.Bits.CompletionTimeoutDisable) {
+    //
+    // device supports the CTO mechanism
+    //
+    DeviceCtl2.Uint16 = 0;
+    Offset = PciDevice->PciExpressCapabilityOffset +
+              OFFSET_OF (PCI_CAPABILITY_PCIEXP, DeviceControl2);
+    Status = PciDevice->PciIo.Pci.Read (
+                                  &PciDevice->PciIo,
+                                  EfiPciIoWidthUint16,
+                                  Offset,
+                                  1,
+                                  &DeviceCtl2.Uint16
+                                  );
+    ASSERT (Status == EFI_SUCCESS);
+  } else {
+    //
+    // device does not support the CTO mechanism, hence no override performed
+    //
+    DEBUG (( DEBUG_INFO, "CTO n/a,"));
+    return EFI_SUCCESS;
+  }
+
+  //
+  // override the device CTO values if applicable
+  //
+  if (PciDevice->SetupCTO.Act) {
+    //
+    // program the CTO range values
+    //
+    if (PciDevice->SetupCTO.Support != DeviceCtl2.Bits.CompletionTimeoutValue) {
+      DeviceCtl2.Bits.CompletionTimeoutValue = PciDevice->SetupCTO.Support;
+    }
+  } else {
+    //
+    // disable the CTO mechanism in device
+    //
+    DeviceCtl2.Bits.CompletionTimeoutValue = 0;
+    DeviceCtl2.Bits.CompletionTimeoutDisable = 1;
+  }
+  DEBUG (( DEBUG_INFO, "CTO disable: %d, CTO range: 0x%x,",
+      DeviceCtl2.Bits.CompletionTimeoutDisable,
+      DeviceCtl2.Bits.CompletionTimeoutValue
+  ));
+
+  //
+  // Raise TPL to high level to disable timer interrupt while the write operation completes
+  //
+  OldTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
+
+  Status = PciDevice->PciIo.Pci.Write (
+                                &PciDevice->PciIo,
+                                EfiPciIoWidthUint16,
+                                Offset,
+                                1,
+                                &DeviceCtl2.Uint16
+                                );
+  //
+  // Restore TPL to its original level
+  //
+  gBS->RestoreTPL (OldTpl);
+
+  if (!EFI_ERROR(Status)) {
+    PciDevice->PciExpressCapabilityStructure.DeviceControl2.Uint16 = DeviceCtl2.Uint16;
+  } else {
+    ReportPciWriteError (PciDevice->BusNumber, PciDevice->DeviceNumber, PciDevice->FunctionNumber, Offset);
+  }
+  return Status;
+}
+
diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciExpressFeatures.h b/MdeModulePkg/Bus/Pci/PciBusDxe/PciExpressFeatures.h
index ee636ce..2ee7d4d 100644
--- a/MdeModulePkg/Bus/Pci/PciBusDxe/PciExpressFeatures.h
+++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciExpressFeatures.h
@@ -133,4 +133,39 @@ ProgramNoSnoop (
   IN VOID                   *PciExFeatureConfiguration
   );
 
+/**
+  The main routine which process the PCI feature Completion Timeout as per the
+  device-specific platform policy, as well as in complaince with the PCI Base
+  specification Revision 4.
+
+  @param PciDevice                      A pointer to the PCI_IO_DEVICE.
+  @param PciConfigPhase                 for the PCI feature configuration phases:
+                                        PciExpressFeatureSetupPhase & PciExpressFeatureEntendedSetupPhase
+
+  @retval EFI_SUCCESS                   processing of PCI feature CTO is successful.
+**/
+EFI_STATUS
+SetupCompletionTimeout (
+  IN PCI_IO_DEVICE          *PciDevice,
+  IN VOID                   *PciExFeatureConfiguration
+  );
+
+/**
+  Overrides the PCI Device Control2 register Completion Timeout range; if
+  the hardware value is different than the intended value.
+
+  @param  PciDevice             A pointer to the PCI_IO_DEVICE instance.
+
+  @retval EFI_SUCCESS           The data was read from or written to the PCI device.
+  @retval EFI_UNSUPPORTED       The address range specified by Offset, Width, and Count is not
+                                valid for the PCI configuration header of the PCI controller.
+  @retval EFI_INVALID_PARAMETER Buffer is NULL or Width is invalid.
+
+**/
+EFI_STATUS
+ProgramCompletionTimeout (
+  IN PCI_IO_DEVICE          *PciDevice,
+  IN VOID                   *PciExFeatureConfiguration
+  );
+
 #endif
diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciFeatureSupport.c b/MdeModulePkg/Bus/Pci/PciBusDxe/PciFeatureSupport.c
index d264d13..d4459f3 100644
--- a/MdeModulePkg/Bus/Pci/PciBusDxe/PciFeatureSupport.c
+++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciFeatureSupport.c
@@ -74,7 +74,7 @@ EFI_PCI_EXPRESS_PLATFORM_POLICY             mPciExpressPlatformPolicy = {
     //
     // support for PCI Express feature - Completion Timeout
     //
-    FALSE,
+    TRUE,
     //
     // support for PCI Express feature - Clock Power Management
     //
@@ -119,6 +119,12 @@ PCI_EXPRESS_FEATURE_INITIALIZATION_POINT  mPciExpressFeatureInitializationList[]
   },
   {
     PciExpressFeatureProgramPhase,        PciExpressNoSnoop,    ProgramNoSnoop
+  },
+  {
+    PciExpressFeatureSetupPhase,          PciExpressCto,        SetupCompletionTimeout
+  },
+  {
+    PciExpressFeatureProgramPhase,        PciExpressCto,        ProgramCompletionTimeout
   }
 };
 
diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciPlatformSupport.c b/MdeModulePkg/Bus/Pci/PciBusDxe/PciPlatformSupport.c
index 954ce16..1afea19 100644
--- a/MdeModulePkg/Bus/Pci/PciBusDxe/PciPlatformSupport.c
+++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciPlatformSupport.c
@@ -228,6 +228,85 @@ SetDevicePolicyPciExpressNs (
   }
 }
 
+/**
+  Routine to set the device-specific policy for the PCI feature CTO value range
+  or disable
+
+  @param  CtoSupport    value corresponding to data type EFI_PCI_EXPRESS_CTO_SUPPORT
+  @param  PciDevice     A pointer to PCI_IO_DEVICE
+**/
+VOID
+SetDevicePolicyPciExpressCto (
+  IN  EFI_PCI_EXPRESS_CTO_SUPPORT    CtoSupport,
+  OUT PCI_IO_DEVICE               *PciDevice
+)
+{
+  //
+  // implementation specific rules for the usage of PCI_FEATURE_POLICY members
+  // exclusively for the PCI Feature CTO
+  //
+  // .Override = 0 to skip this PCI feature CTO for the PCI device
+  // .Override = 1 to program this CTO PCI feature
+  //      .Act = 1 to program the CTO range as per given device policy in .Support
+  //      .Act = 0 to disable the CTO mechanism in the PCI device, CTO set to default range
+  //
+  switch (CtoSupport) {
+    case  EFI_PCI_EXPRESS_CTO_AUTO:
+      PciDevice->SetupCTO.Override = 0;
+      break;
+    case  EFI_PCI_EXPRESS_CTO_DEFAULT:
+      PciDevice->SetupCTO.Override = 1;
+      PciDevice->SetupCTO.Act = 1;
+      PciDevice->SetupCTO.Support = PCIE_COMPLETION_TIMEOUT_50US_50MS;
+      break;
+    case  EFI_PCI_EXPRESS_CTO_RANGE_A1:
+      PciDevice->SetupCTO.Override = 1;
+      PciDevice->SetupCTO.Act = 1;
+      PciDevice->SetupCTO.Support = PCIE_COMPLETION_TIMEOUT_50US_100US;
+      break;
+    case  EFI_PCI_EXPRESS_CTO_RANGE_A2:
+      PciDevice->SetupCTO.Override = 1;
+      PciDevice->SetupCTO.Act = 1;
+      PciDevice->SetupCTO.Support = PCIE_COMPLETION_TIMEOUT_1MS_10MS;
+      break;
+    case  EFI_PCI_EXPRESS_CTO_RANGE_B1:
+      PciDevice->SetupCTO.Override = 1;
+      PciDevice->SetupCTO.Act = 1;
+      PciDevice->SetupCTO.Support = PCIE_COMPLETION_TIMEOUT_16MS_55MS;
+      break;
+    case  EFI_PCI_EXPRESS_CTO_RANGE_B2:
+      PciDevice->SetupCTO.Override = 1;
+      PciDevice->SetupCTO.Act = 1;
+      PciDevice->SetupCTO.Support = PCIE_COMPLETION_TIMEOUT_65MS_210MS;
+      break;
+    case  EFI_PCI_EXPRESS_CTO_RANGE_C1:
+      PciDevice->SetupCTO.Override = 1;
+      PciDevice->SetupCTO.Act = 1;
+      PciDevice->SetupCTO.Support = PCIE_COMPLETION_TIMEOUT_260MS_900MS;
+      break;
+    case  EFI_PCI_EXPRESS_CTO_RANGE_C2:
+      PciDevice->SetupCTO.Override = 1;
+      PciDevice->SetupCTO.Act = 1;
+      PciDevice->SetupCTO.Support = PCIE_COMPLETION_TIMEOUT_1S_3_5S;
+      break;
+    case  EFI_PCI_EXPRESS_CTO_RANGE_D1:
+      PciDevice->SetupCTO.Override = 1;
+      PciDevice->SetupCTO.Act = 1;
+      PciDevice->SetupCTO.Support = PCIE_COMPLETION_TIMEOUT_4S_13S;
+      break;
+    case  EFI_PCI_EXPRESS_CTO_RANGE_D2:
+      PciDevice->SetupCTO.Override = 1;
+      PciDevice->SetupCTO.Act = 1;
+      PciDevice->SetupCTO.Support = PCIE_COMPLETION_TIMEOUT_17S_64S;
+      break;
+    case  EFI_PCI_EXPRESS_CTO_DET_DISABLE:
+      PciDevice->SetupCTO.Override = 1;
+      PciDevice->SetupCTO.Act = 0;
+      PciDevice->SetupCTO.Support = PCIE_COMPLETION_TIMEOUT_50US_50MS;
+      break;
+  }
+}
+
 /**
   Generic routine to setup the PCI features as per its predetermined defaults.
 **/
@@ -253,6 +332,8 @@ SetupDefaultPciExpressDevicePolicy (
 
   PciDevice->SetupNS.Override = 0;
 
+  PciDevice->SetupCTO.Override = 0;
+
 }
 
 /**
@@ -360,6 +441,15 @@ GetPciExpressDevicePolicy (
       PciDevice->SetupNS.Override = 0;
     }
 
+    //
+    // set the device specific policy for Completion Timeout (CTO)
+    //
+    if (mPciExpressPlatformPolicy.Cto) {
+      SetDevicePolicyPciExpressCto (PciExpressDevicePolicy.CTOsupport, PciDevice);
+    } else {
+      PciDevice->SetupCTO.Override = 0;
+    }
+
 
     DEBUG ((
       DEBUG_INFO,
@@ -498,6 +588,34 @@ GetPciExpressMrrs (
   return EFI_PCI_EXPRESS_NOT_APPLICABLE;
 }
 
+EFI_PCI_EXPRESS_CTO_SUPPORT
+GetPciExpressCto (
+  IN UINT8              Cto
+  )
+{
+  switch (Cto) {
+    case PCIE_COMPLETION_TIMEOUT_50US_50MS:
+      return EFI_PCI_EXPRESS_CTO_DEFAULT;
+    case PCIE_COMPLETION_TIMEOUT_50US_100US:
+      return EFI_PCI_EXPRESS_CTO_RANGE_A1;
+    case PCIE_COMPLETION_TIMEOUT_1MS_10MS:
+      return EFI_PCI_EXPRESS_CTO_RANGE_A2;
+    case PCIE_COMPLETION_TIMEOUT_16MS_55MS:
+      return EFI_PCI_EXPRESS_CTO_RANGE_B1;
+    case PCIE_COMPLETION_TIMEOUT_65MS_210MS:
+      return EFI_PCI_EXPRESS_CTO_RANGE_B2;
+    case PCIE_COMPLETION_TIMEOUT_260MS_900MS:
+      return EFI_PCI_EXPRESS_CTO_RANGE_C1;
+    case PCIE_COMPLETION_TIMEOUT_1S_3_5S:
+      return EFI_PCI_EXPRESS_CTO_RANGE_C2;
+    case PCIE_COMPLETION_TIMEOUT_4S_13S:
+      return EFI_PCI_EXPRESS_CTO_RANGE_D1;
+    case PCIE_COMPLETION_TIMEOUT_17S_64S:
+      return EFI_PCI_EXPRESS_CTO_RANGE_D2;
+  }
+  return EFI_PCI_EXPRESS_NOT_APPLICABLE;
+}
+
 
 /**
   Notifies the platform about the current PCI Express state of the device.
@@ -561,6 +679,19 @@ PciExpressPlatformNotifyDeviceState (
     PciExDeviceConfiguration.DeviceCtlNoSnoop = EFI_PCI_EXPRESS_NOT_APPLICABLE;
   }
 
+  //
+  // get the device-specific state for the PCIe CTO feature
+  //
+  if (mPciExpressPlatformPolicy.Cto) {
+    PciExDeviceConfiguration.CTOsupport = PciDevice->PciExpressCapabilityStructure.DeviceControl2.Bits.CompletionTimeoutDisable
+                                          ? EFI_PCI_EXPRESS_CTO_DET_DISABLE
+                                          : GetPciExpressCto (
+                                              (UINT8)PciDevice->PciExpressCapabilityStructure.DeviceControl2.Bits.CompletionTimeoutValue
+                                              );
+  } else {
+    PciExDeviceConfiguration.CTOsupport = EFI_PCI_EXPRESS_NOT_APPLICABLE;
+  }
+
 
   if (mPciExPlatformProtocol != NULL) {
     return mPciExPlatformProtocol->NotifyDeviceState (
-- 
2.21.0.windows.1


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.

View/Reply Online (#54070): https://edk2.groups.io/g/devel/message/54070
Mute This Topic: https://groups.io/mt/71063084/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub  [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-