/* $NoKeywords:$ */
/**
 * @file
 *
 * Procedure to map user define topology to processor configuration
 *
 *
 *
 * @xrefitem bom "File Content Label" "Release Content"
 * @e project:     AGESA
 * @e sub-project: GNB
 * @e \$Revision: 39898 $   @e \$Date: 2010-10-15 17:08:45 -0400 (Fri, 15 Oct 2010) $
 *
 */
/*
 *****************************************************************************
 *
 * Copyright (c) 2011, 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  "AGESA.h"
#include  "Ids.h"
#include  "amdlib.h"
#include  "Gnb.h"
#include  "GnbPcie.h"
#include  "GnbPcieFamServices.h"
#include  "GeneralServices.h"
#include  "PcieInputParser.h"
#include  "PcieMapTopology.h"
#include  GNB_MODULE_DEFINITIONS (GnbPcieConfig)
#include  "Filecode.h"
#define FILECODE PROC_GNB_MODULES_GNBPCIECONFIG_PCIEMAPTOPOLOGY_FILECODE
/*----------------------------------------------------------------------------------------
 *                   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
 *----------------------------------------------------------------------------------------
 */

AGESA_STATUS
PcieEnginesToWrapper (
  IN      PCIE_ENGINE_TYPE            EngineType,
  IN      PCIe_COMPLEX_DESCRIPTOR     *ComplexDescriptor,
  IN      PCIe_WRAPPER_CONFIG         *Wrapper
  );

AGESA_STATUS
STATIC
PcieMapPortsPciAddresses (
  IN      PCIe_SILICON_CONFIG         *Silicon,
  IN      PCIe_PLATFORM_CONFIG        *Pcie
  );

AGESA_STATUS
PcieMapTopologyOnWrapper (
  IN      PCIe_COMPLEX_DESCRIPTOR     *ComplexDescriptor,
  IN OUT  PCIe_WRAPPER_CONFIG         *Wrapper,
  IN      PCIe_PLATFORM_CONFIG        *Pcie
 );

VOID
PcieMapInitializeEngineData (
  IN      PCIe_COMPLEX_DESCRIPTOR     *ComplexDescriptor,
  IN OUT  PCIe_WRAPPER_CONFIG         *Wrapper,
  IN      PCIe_PLATFORM_CONFIG        *Pcie
 );

BOOLEAN
PcieCheckPortPciDeviceMapping (
  IN      PCIe_PORT_DESCRIPTOR  *PortDescriptor,
  IN      PCIe_ENGINE_CONFIG    *Engine
  );

VOID
PcieComplexConfigConfigDump (
  IN      PCIe_COMPLEX_DESCRIPTOR     *ComplexDescriptor,
  IN      PCIe_PLATFORM_CONFIG        *Pcie
  );

BOOLEAN
PcieIsDescriptorLinkWidthValid (
  IN      PCIe_ENGINE_DESCRIPTOR      *EngineDescriptor
  );

BOOLEAN
PcieCheckLanesMatch (
  IN      PCIe_ENGINE_DESCRIPTOR      *EngineDescriptor,
  IN      PCIe_ENGINE_CONFIG          *Engine
  );

BOOLEAN
PcieCheckDescriptorMapsToWrapper (
  IN      PCIe_ENGINE_DESCRIPTOR      *EngineDescriptor,
  IN      PCIe_WRAPPER_CONFIG         *Wrapper
  );

VOID
PcieAllocateEngine (
  IN      UINT8                       DescriptorIndex,
  IN      PCIe_ENGINE_CONFIG          *Engine
  );
/*----------------------------------------------------------------------------------------*/
/**
 * Configure engine list to support lane allocation according to configuration ID.
 *
 *
 *
 * @param[in]  ComplexDescriptor   Pointer to used define complex descriptor
 * @param[in]  Complex             Pointer to complex descriptor
 * @param[in]  Pcie                Pointer to global PCIe configuration
 * @retval     AGESA_SUCCESS       Topology successfully mapped
 * @retval     AGESA_ERROR         Topology can not be mapped
 */

AGESA_STATUS
PcieMapTopologyOnComplex (
  IN      PCIe_COMPLEX_DESCRIPTOR     *ComplexDescriptor,
  IN      PCIe_COMPLEX_CONFIG         *Complex,
  IN      PCIe_PLATFORM_CONFIG        *Pcie
  )
{
  PCIe_SILICON_CONFIG *Silicon;
  PCIe_WRAPPER_CONFIG *Wrapper;
  AGESA_STATUS        AgesaStatus;
  AGESA_STATUS        Status;

  AgesaStatus = AGESA_SUCCESS;
  IDS_HDT_CONSOLE (GNB_TRACE, "PcieMapTopologyOnComplex Enter\n");
  GNB_DEBUG_CODE (
    PcieComplexConfigConfigDump (ComplexDescriptor, Pcie);
  );
  Silicon = PcieComplexGetSiliconList (Complex);
  while (Silicon != NULL) {
    Wrapper = PcieSiliconGetWrapperList (Silicon);
    while (Wrapper != NULL) {
      Status = PcieMapTopologyOnWrapper (ComplexDescriptor, Wrapper, Pcie);
      AGESA_STATUS_UPDATE (Status, AgesaStatus);
      if (Status == AGESA_ERROR) {
        PcieConfigDisableAllEngines (PciePortEngine | PcieDdiEngine, Wrapper);
        IDS_HDT_CONSOLE (PCIE_MISC, "  ERROR! Fail to map topology on %s Wrapper\n",
          PcieFmDebugGetWrapperNameString (Wrapper)
          );
        ASSERT (FALSE);
      }
      Wrapper = PcieLibGetNextDescriptor (Wrapper);
    }
    Status = PcieMapPortsPciAddresses (Silicon, Pcie);
    AGESA_STATUS_UPDATE (Status, AgesaStatus);
    Silicon = PcieLibGetNextDescriptor (Silicon);
  }
  IDS_HDT_CONSOLE (GNB_TRACE, "PcieMapTopologyOnComplex Exit [%x]\n", AgesaStatus);
  return AgesaStatus;
}

/*----------------------------------------------------------------------------------------*/
/**
 * Configure engine list to support lane allocation according to configuration ID.
 *
 *
 *
 * @param[in]  EngineType          Engine type
 * @param[in]  ComplexDescriptor   Pointer to used define complex descriptor
 * @param[in]  Wrapper             Pointer to wrapper config descriptor
 * @retval     AGESA_SUCCESS       Topology successfully mapped
 * @retval     AGESA_ERROR         Topology can not be mapped
 */
AGESA_STATUS
PcieEnginesToWrapper (
  IN      PCIE_ENGINE_TYPE            EngineType,
  IN      PCIe_COMPLEX_DESCRIPTOR     *ComplexDescriptor,
  IN      PCIe_WRAPPER_CONFIG         *Wrapper
  )
{
  AGESA_STATUS                Status;
  PCIe_ENGINE_CONFIG          *EngineList;
  PCIe_ENGINE_DESCRIPTOR      *EngineDescriptor;
  UINT8                       ConfigurationId;
  UINT8                       Allocations;
  UINTN                       Index;
  UINTN                       NumberOfDescriptors;

  ConfigurationId = 0;
  Allocations = 0;
  IDS_HDT_CONSOLE (GNB_TRACE, "PcieEnginesToWrapper Enter\n");
  NumberOfDescriptors = PcieInputParserGetNumberOfEngines (ComplexDescriptor);
  do {
    Status = PcieFmConfigureEnginesLaneAllocation (Wrapper, EngineType, ConfigurationId++);

    if (Status == AGESA_SUCCESS) {
      Allocations = 0;
      for (Index = 0; Index < NumberOfDescriptors; Index++) {
        EngineDescriptor = PcieInputParserGetEngineDescriptor (ComplexDescriptor, Index);
        if (EngineDescriptor->EngineData.EngineType == EngineType) {
          // Step 1, belongs to wrapper check.
          if (PcieCheckDescriptorMapsToWrapper (EngineDescriptor, Wrapper)) {
            ++Allocations;
            EngineList = PcieWrapperGetEngineList (Wrapper);
            while (EngineList != NULL) {
              if (!PcieLibIsEngineAllocated (EngineList)) {
                // Step 2.user descriptor less or equal to link width of engine
                if (PcieCheckLanesMatch (EngineDescriptor, EngineList)) {
                  // Step 3, Check if link width is correct.x1, x2, x4, x8, x16.
                  if (!PcieIsDescriptorLinkWidthValid (EngineDescriptor)) {
                    PcieConfigDisableEngine (EngineList);
                    return AGESA_ERROR;
                  }
                  if (EngineDescriptor->EngineData.EngineType == PciePortEngine) {
                    // Step 4, Family specifc, port device number match engine device
                    if (PcieCheckPortPciDeviceMapping ((PCIe_PORT_DESCRIPTOR*) EngineDescriptor, EngineList)) {
                      //Step 5, Family specifc, lanes can be muxed.
                      if (PcieFmCheckPortPcieLaneCanBeMuxed ((PCIe_PORT_DESCRIPTOR*) EngineDescriptor, EngineList)) {
                        PcieAllocateEngine ((UINT8) Index, EngineList);
                        --Allocations;
                      }
                    }
                  } else {
                    PcieAllocateEngine ((UINT8) Index, EngineList);
                    --Allocations;
                  }
                }
              }//end if PcieLibIsEngineAllocated
              EngineList = PcieLibGetNextDescriptor (EngineList);
            }
          }//end if PcieCheckDescriptorMapsToWrapper
        }// end if EngineType
      }//end for
    }
  } while (Status == AGESA_SUCCESS && Allocations != 0);
  IDS_HDT_CONSOLE (GNB_TRACE, "PcieEnginesToWrapper Exit [%x]\n", Status);
  return Status;
}

/*----------------------------------------------------------------------------------------*/
/**
 * Check if lane from  user port descriptor (PCIe_PORT_DESCRIPTOR) belongs to wrapper (PCIe_WRAPPER_CONFIG)
 *
 *
 * @param[in]  EngineDescriptor    Pointer to used define engine descriptor
 * @param[in]  Wrapper             Pointer to PCIe_WRAPPER_CONFIG
 * @retval     TRUE                Belongs to wrapper
 * @retval     FALSE               Not belongs to wrapper
 */
BOOLEAN
PcieCheckDescriptorMapsToWrapper (
  IN      PCIe_ENGINE_DESCRIPTOR      *EngineDescriptor,
  IN      PCIe_WRAPPER_CONFIG         *Wrapper
  )
{
  BOOLEAN Result;
  UINT16  DescriptorHiLane;
  UINT16  DescriptorLoLane;
  UINT16  DescriptorNumberOfLanes;

  DescriptorLoLane = MIN (EngineDescriptor->EngineData.StartLane, EngineDescriptor->EngineData.EndLane);
  DescriptorHiLane = MAX (EngineDescriptor->EngineData.StartLane, EngineDescriptor->EngineData.EndLane);
  DescriptorNumberOfLanes = DescriptorHiLane - DescriptorLoLane + 1;
  Result = TRUE;

  if (!(DescriptorLoLane >= Wrapper->StartPhyLane && DescriptorHiLane <= Wrapper->EndPhyLane)) {
    // Lanes of descriptor does not belongs to wrapper
    Result = FALSE;
  }
  return Result;
}

/*----------------------------------------------------------------------------------------*/
/**
 * Set Engine to be allocated.
 *
 *
 * @param[in]  DescriptorIndex    UINT8 index
 * @param[in]  Engine             Pointer to engine config
 */
VOID
PcieAllocateEngine (
  IN      UINT8                       DescriptorIndex,
  IN      PCIe_ENGINE_CONFIG          *Engine
  )
{
  Engine->Flags |= DESCRIPTOR_ALLOCATED;
  Engine->Scratch = DescriptorIndex;
}

/*----------------------------------------------------------------------------------------*/
/**
 * Configure engine list to support lane allocation according to configuration ID.
 *
 * PCIE port
 *
 *
 * 1 Check if lane from  user port descriptor (PCIe_PORT_DESCRIPTOR) belongs to wrapper (PCIe_WRAPPER_CONFIG)
 * 2 Check if link width from user descriptor less or equal to link width of engine (PCIe_ENGINE_CONFIG)
 * 3 Check if link width is correct. Correct link width for PCIe port x1, x2, x4, x8, x16, correct link width for DDI x4, x8
 * 4 Check if user port device number (PCIe_PORT_DESCRIPTOR) match engine port device number (PCIe_ENGINE_CONFIG)
 * 5 Check if lane can be muxed
 *
 *
 * DDI Link
 *
 * 1 Check if lane from  user port descriptor (PCIe_DDI_DESCRIPTOR) belongs to wrapper (PCIe_WRAPPER_CONFIG)
 * 2 Check lane from  (PCIe_DDI_DESCRIPTOR) match exactly phy lane (PCIe_ENGINE_CONFIG)
 *
 *
 *
 * @param[in]     ComplexDescriptor   Pointer to used define complex descriptor
 * @param[in,out] Wrapper             Pointer to wrapper config descriptor
 * @param[in]     Pcie                Pointer to global PCIe configuration
 * @retval        AGESA_SUCCESS       Topology successfully mapped
 * @retval        AGESA_ERROR         Topology can not be mapped
 */
AGESA_STATUS
PcieMapTopologyOnWrapper (
  IN       PCIe_COMPLEX_DESCRIPTOR     *ComplexDescriptor,
  IN OUT   PCIe_WRAPPER_CONFIG         *Wrapper,
  IN       PCIe_PLATFORM_CONFIG        *Pcie
  )
{
  AGESA_STATUS                AgesaStatus;
  AGESA_STATUS                Status;
  PCIe_ENGINE_CONFIG          *EngineList;
  UINT32                      WrapperPhyLaneBitMap;

  AgesaStatus = AGESA_SUCCESS;
  if (PcieLibIsPcieWrapper (Wrapper)) {
    Status = PcieEnginesToWrapper (PciePortEngine, ComplexDescriptor, Wrapper);
    AGESA_STATUS_UPDATE (Status, AgesaStatus);
    if (Status == AGESA_ERROR) {
      // If we can not map topology on wrapper we can not enable any engines.
      PutEventLog (
        AGESA_ERROR,
        GNB_EVENT_INVALID_PCIE_TOPOLOGY_CONFIGURATION,
        Wrapper->WrapId,
        Wrapper->StartPhyLane,
        Wrapper->EndPhyLane,
        0,
        GnbLibGetHeader (Pcie)
        );
      PcieConfigDisableAllEngines (PciePortEngine, Wrapper);
    }
    EngineList = PcieWrapperGetEngineList (Wrapper);
    // Assure SB is allocated
    while (EngineList != NULL) {
      if ((EngineList->EngineData.EngineType == PciePortEngine) && (EngineList->Type.Port.IsSB)) {
        EngineList->Flags |= DESCRIPTOR_ALLOCATED;
      }
      EngineList = PcieLibGetNextDescriptor (EngineList);
    }
  }
  if (PcieLibIsDdiWrapper (Wrapper)) {
    Status = PcieEnginesToWrapper (PcieDdiEngine, ComplexDescriptor, Wrapper);
    AGESA_STATUS_UPDATE (Status, AgesaStatus);
    if (Status == AGESA_ERROR) {
      // If we can not map topology on wrapper we can not enable any engines.
      PutEventLog (
        AGESA_ERROR,
        GNB_EVENT_INVALID_DDI_TOPOLOGY_CONFIGURATION,
        Wrapper->WrapId,
        Wrapper->StartPhyLane,
        Wrapper->EndPhyLane,
        0,
        GnbLibGetHeader (Pcie)
        );
      PcieConfigDisableAllEngines (PcieDdiEngine, Wrapper);
    }
  }
  // Copy engine data
  PcieMapInitializeEngineData (ComplexDescriptor, Wrapper, Pcie);

  EngineList = PcieWrapperGetEngineList (Wrapper);
  // Verify if we oversubscribe lanes and PHY link width
  WrapperPhyLaneBitMap = 0;
  while (EngineList != NULL) {
    UINT32  EnginePhyLaneBitMap;
    if (PcieLibIsEngineAllocated (EngineList)) {
      EnginePhyLaneBitMap = PcieConfigGetEnginePhyLaneBitMap (EngineList);
      if ((WrapperPhyLaneBitMap & EnginePhyLaneBitMap) != 0) {
        IDS_HDT_CONSOLE (PCIE_MISC, "  ERROR! Lanes double subscribe lanes [Engine Lanes %d..%d]\n",
          EngineList->EngineData.StartLane,
          EngineList->EngineData.EndLane
          );
        PutEventLog (
          AGESA_ERROR,
          GNB_EVENT_INVALID_LANES_CONFIGURATION,
          EngineList->EngineData.StartLane,
          EngineList->EngineData.EndLane,
          0,
          0,
          GnbLibGetHeader (Pcie)
          );
        PcieConfigDisableEngine (EngineList);
        Status = AGESA_ERROR;
        AGESA_STATUS_UPDATE (Status, AgesaStatus);
      } else {
        WrapperPhyLaneBitMap |= EnginePhyLaneBitMap;
      }
    }
    EngineList = PcieLibGetNextDescriptor (EngineList);
  }
  return AgesaStatus;
}


/*----------------------------------------------------------------------------------------*/
/**
 * Initialize engine data
 *
 *
 *
 * @param[in]     ComplexDescriptor   Pointer to user defined complex descriptor
 * @param[in,out] Wrapper             Pointer to wrapper config descriptor
 * @param[in]     Pcie                Pointer to global PCIe configuration
 */
VOID
PcieMapInitializeEngineData (
  IN       PCIe_COMPLEX_DESCRIPTOR     *ComplexDescriptor,
  IN OUT   PCIe_WRAPPER_CONFIG         *Wrapper,
  IN       PCIe_PLATFORM_CONFIG        *Pcie
  )
{
  PCIe_ENGINE_CONFIG        *EngineList;
  PCIe_ENGINE_DESCRIPTOR    *EngineDescriptor;

  EngineList = PcieWrapperGetEngineList (Wrapper);
  while (EngineList != NULL) {
    if (PcieLibIsEngineAllocated (EngineList)) {
      if (EngineList->Scratch != 0xFF) {
        EngineDescriptor = PcieInputParserGetEngineDescriptor (ComplexDescriptor, EngineList->Scratch);
        LibAmdMemCopy (&EngineList->EngineData, &EngineDescriptor->EngineData, sizeof (EngineDescriptor->EngineData), GnbLibGetHeader (Pcie));
        if (PcieLibIsDdiEngine (EngineList)) {
          LibAmdMemCopy (&EngineList->Type.Ddi, &((PCIe_DDI_DESCRIPTOR*) EngineDescriptor)->Ddi, sizeof (PCIe_DDI_DATA), GnbLibGetHeader (Pcie));
          EngineList->Type.Ddi.DisplayPriorityIndex = (UINT8) EngineList->Scratch;
        } else if (PcieLibIsPcieEngine (EngineList)) {
          LibAmdMemCopy (&EngineList->Type.Port, &((PCIe_PORT_DESCRIPTOR*) EngineDescriptor)->Port, sizeof (PCIe_PORT_DATA), GnbLibGetHeader (Pcie));
        }
      }
    }
    EngineList = PcieLibGetNextDescriptor (EngineList);
  }
}

/*----------------------------------------------------------------------------------------*/
/**
 * Allocate PCI addresses for all PCIe engines on silicon
 *
 *
 *
 * @param[in]  PortDescriptor      Pointer to user defined engine descriptor
 * @param[in]  Engine              Pointer engine configuration
 * @retval     TRUE                Descriptor can be mapped to engine
 * @retval     FALSE               Descriptor can NOT be mapped to engine
 */

BOOLEAN
PcieCheckPortPciDeviceMapping (
  IN      PCIe_PORT_DESCRIPTOR  *PortDescriptor,
  IN      PCIe_ENGINE_CONFIG    *Engine
  )
{
  BOOLEAN Result;

  if ((PortDescriptor->Port.DeviceNumber == Engine->Type.Port.NativeDevNumber &&
    PortDescriptor->Port.FunctionNumber == Engine->Type.Port.NativeFunNumber) ||
    (PortDescriptor->Port.DeviceNumber == 0 && PortDescriptor->Port.FunctionNumber == 0)) {
    Result = TRUE;
  } else {
    Result = PcieFmCheckPortPciDeviceMapping (PortDescriptor, Engine);
  }

  return Result;
}

/*----------------------------------------------------------------------------------------*/
/**
 * Allocate PCI addresses for all PCIe engines on silicon
 *
 *
 *
 * @param[in]  Silicon             Pointer to silicon configurration
 * @param[in]  Pcie                Pointer PCIe configuration
 * @retval     AGESA_ERROR         Fail to allocate PCI device address
 * @retval     AGESA_SUCCESS       Successfully allocate PCI address for all PCIe ports
 */

AGESA_STATUS
STATIC
PcieMapPortsPciAddresses (
  IN      PCIe_SILICON_CONFIG    *Silicon,
  IN      PCIe_PLATFORM_CONFIG   *Pcie
  )
{
  AGESA_STATUS        Status;
  AGESA_STATUS        AgesaStatus;
  PCIe_WRAPPER_CONFIG *WrapperList;
  PCIe_ENGINE_CONFIG  *EngineList;
  AgesaStatus = AGESA_SUCCESS;
  WrapperList = PcieSiliconGetWrapperList (Silicon);
  while (WrapperList != NULL) {
    EngineList = PcieWrapperGetEngineList (WrapperList);
    while (EngineList != NULL) {
      if (PcieLibIsPcieEngine (EngineList) && PcieLibIsEngineAllocated (EngineList)) {
        Status = PcieFmMapPortPciAddress (EngineList, Pcie);
        AGESA_STATUS_UPDATE (Status, AgesaStatus);
        if (Status == AGESA_SUCCESS) {
          EngineList->Type.Port.Address.AddressValue = MAKE_SBDFO (
                                                         0,
                                                         Silicon->Address.Address.Bus,
                                                         EngineList->Type.Port.PortData.DeviceNumber,
                                                         EngineList->Type.Port.PortData.FunctionNumber,
                                                         0
                                                         );
        } else {
          EngineList->Type.Port.PortData.PortPresent = OFF;
          IDS_HDT_CONSOLE (PCIE_MISC, "  ERROR! Fail to allocate PCI address for PCIe port\n"
            );
          //Report error
          PutEventLog (
            AGESA_ERROR,
            GNB_EVENT_INVALID_PCIE_PORT_CONFIGURATION,
            EngineList->Type.Port.PortData.DeviceNumber,
            0,
            0,
            0,
            GnbLibGetHeader (Pcie)
            );
        }
      }
      EngineList = PcieLibGetNextDescriptor (EngineList);
    }
    WrapperList = PcieLibGetNextDescriptor (WrapperList);
  }
  return AgesaStatus;
}

/*----------------------------------------------------------------------------------------*/
/**
 * If link width from user descriptor less or equal to link width of engine
 *
 *
 * @param[in]  EngineDescriptor    Pointer to used define engine descriptor
 * @param[in]  Engine              Pointer to engine config
 * @retval     TRUE                Descriptor can be mapped to engine
 * @retval     FALSE               Descriptor can NOT be mapped to engine
 */

BOOLEAN
PcieCheckLanesMatch (
  IN      PCIe_ENGINE_DESCRIPTOR      *EngineDescriptor,
  IN      PCIe_ENGINE_CONFIG          *Engine
  )
{
  BOOLEAN Result;
  UINT16  DescriptorHiLane;
  UINT16  DescriptorLoLane;
  UINT16  DescriptorNumberOfLanes;

  DescriptorLoLane = MIN (EngineDescriptor->EngineData.StartLane, EngineDescriptor->EngineData.EndLane);
  DescriptorHiLane = MAX (EngineDescriptor->EngineData.StartLane, EngineDescriptor->EngineData.EndLane);
  DescriptorNumberOfLanes = DescriptorHiLane - DescriptorLoLane + 1;
  Result = FALSE;

  if (EngineDescriptor->EngineData.EngineType == PciePortEngine) {
    //
    // If link width from user descriptor less or equal to link width of engine (PCIe_ENGINE_CONFIG)
    //
    if (DescriptorNumberOfLanes <= PcieConfigGetNumberOfCoreLane (Engine)) {
      Result = TRUE;
    }
  } else if (EngineDescriptor->EngineData.EngineType == PcieDdiEngine) {
    //
    //For Ddi, check lane from  (PCIe_DDI_DESCRIPTOR) match exactly phy lane (PCIe_ENGINE_CONFIG)
    //
    if ((Engine->EngineData.StartLane == DescriptorLoLane) && (Engine->EngineData.EndLane == DescriptorHiLane)) {
      Result = TRUE;
    }
  }

  return Result;
}

/*----------------------------------------------------------------------------------------*/
/**
 * Correct link width for PCIe port x1, x2, x4, x8, x16, correct link width for DDI x4, x8
 *
 *
 * @param[in]  EngineDescriptor    A pointer of PCIe_ENGINE_DESCRIPTOR
 * @retval     TRUE                Descriptor can be mapped to engine
 * @retval     FALSE               Descriptor can NOT be mapped to engine
 */

BOOLEAN
PcieIsDescriptorLinkWidthValid (
  IN      PCIe_ENGINE_DESCRIPTOR      *EngineDescriptor
  )
{
  BOOLEAN   Result;
  UINT16    DescriptorHiLane;
  UINT16    DescriptorLoLane;
  UINT16    DescriptorNumberOfLanes;

  Result = FALSE;
  DescriptorLoLane = MIN (EngineDescriptor->EngineData.StartLane, EngineDescriptor->EngineData.EndLane);
  DescriptorHiLane = MAX (EngineDescriptor->EngineData.StartLane, EngineDescriptor->EngineData.EndLane);
  DescriptorNumberOfLanes = DescriptorHiLane - DescriptorLoLane + 1;

  if (EngineDescriptor->EngineData.EngineType == PciePortEngine) {
    if (DescriptorNumberOfLanes == 1 || DescriptorNumberOfLanes == 2 || DescriptorNumberOfLanes == 4 ||
        DescriptorNumberOfLanes == 8 || DescriptorNumberOfLanes == 16) {
      Result = TRUE;
    }
  } else if (EngineDescriptor->EngineData.EngineType == PcieDdiEngine) {
    if (DescriptorNumberOfLanes == 4 || DescriptorNumberOfLanes == 8) {
      Result = TRUE;
    }
  }

  GNB_DEBUG_CODE (
    if (!Result) {
      IDS_HDT_CONSOLE (PCIE_MISC, "  Invalid Link width [Engine Lanes %d..%d]\n",
        DescriptorLoLane,
        DescriptorHiLane
      );
    }
  );

  return Result;
}

/*----------------------------------------------------------------------------------------*/
/**
 * Helper function to dump input configuration to debug out
 *
 *
 * @param[in]  ComplexDescriptor   Pointer to used define complex descriptor
 * @param[in]  Pcie                Pointer to global PCIe configuration
 */
VOID
PcieComplexConfigConfigDump (
  IN      PCIe_COMPLEX_DESCRIPTOR     *ComplexDescriptor,
  IN      PCIe_PLATFORM_CONFIG        *Pcie
  )
{
  PCIe_ENGINE_DESCRIPTOR      *EngineDescriptor;
  UINTN                       Index;
  UINTN                       NumberOfEngines;
  IDS_HDT_CONSOLE (PCIE_MISC, "<---------- PCIe User Config Start------------->\n");

  NumberOfEngines = PcieInputParserGetNumberOfEngines (ComplexDescriptor);
  IDS_HDT_CONSOLE (PCIE_MISC, "  ComplexDescriptor SocketId - %d\n  NumberOfEngines - %d\n",
      ComplexDescriptor->SocketId,
      NumberOfEngines
    );

  for (Index = 0; Index < NumberOfEngines; Index++) {
    EngineDescriptor = PcieInputParserGetEngineDescriptor (ComplexDescriptor, Index);
    IDS_HDT_CONSOLE (PCIE_MISC, "  Engine Type - %s\n",
      (EngineDescriptor->EngineData.EngineType == PciePortEngine) ? "PCIe Port" : (
      (EngineDescriptor->EngineData.EngineType == PcieDdiEngine) ? "DDI Link" : (
      (EngineDescriptor->EngineData.EngineType == PcieUnusedEngine) ? "Unused" : "Invalid"))
      );
    IDS_HDT_CONSOLE (PCIE_MISC, "    Start Phy Lane - %d\n    End   Phy Lane - %d\n",
      EngineDescriptor->EngineData.StartLane,
      EngineDescriptor->EngineData.EndLane
    );
    if (EngineDescriptor->EngineData.EngineType == PciePortEngine) {
      IDS_HDT_CONSOLE (PCIE_MISC, "    PortPresent - %d\n    ChannelType  - %d\n    DeviceNumber - %d\n    FunctionNumber - %d\n    LinkSpeedCapability - %d\n    LinkAspm - %d\n    LinkHotplug - %d\n    ResetId - %d\n" ,
        ((PCIe_PORT_DESCRIPTOR *) EngineDescriptor)->Port.PortPresent,
        ((PCIe_PORT_DESCRIPTOR *) EngineDescriptor)->Port.ChannelType,
        ((PCIe_PORT_DESCRIPTOR *) EngineDescriptor)->Port.DeviceNumber,
        ((PCIe_PORT_DESCRIPTOR *) EngineDescriptor)->Port.FunctionNumber,
        ((PCIe_PORT_DESCRIPTOR *) EngineDescriptor)->Port.LinkSpeedCapability,
        ((PCIe_PORT_DESCRIPTOR *) EngineDescriptor)->Port.LinkAspm,
        ((PCIe_PORT_DESCRIPTOR *) EngineDescriptor)->Port.LinkHotplug,
        ((PCIe_PORT_DESCRIPTOR *) EngineDescriptor)->Port.ResetId
      );
    }
    if (EngineDescriptor->EngineData.EngineType == PcieDdiEngine) {
      IDS_HDT_CONSOLE (PCIE_MISC, "    ConnectorType - %d\n    AuxIndex  - %d\n    HdpIndex - %d\n" ,
        ((PCIe_DDI_DESCRIPTOR *) EngineDescriptor)->Ddi.ConnectorType,
        ((PCIe_DDI_DESCRIPTOR *) EngineDescriptor)->Ddi.AuxIndex,
        ((PCIe_DDI_DESCRIPTOR *) EngineDescriptor)->Ddi.HdpIndex
      );
    }
  }
  IDS_HDT_CONSOLE (PCIE_MISC, "<---------- PCIe User Config End-------------->\n");
}

