/** @file
  This file is SampleCode for Intel SA PEI Policy initialization.


  Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent
**/

#include "PeiSaPolicyInit.h"
#include <Library/ConfigBlockLib.h>

/**
  PcieCardResetWorkAround performs PCIe Card reset on root port

  @param[in out] SiPreMemPolicyPpi SI_PREMEM_POLICY_PPI

  @retval EFI_SUCCESS              The policy is installed and initialized.
**/
EFI_STATUS
  PcieCardResetWorkAround (
  IN OUT   SI_PREMEM_POLICY_PPI         *SiPreMemPolicyPpi
  )
{
  EFI_STATUS                      Status;
  SA_MISC_PEI_PREMEM_CONFIG       *MiscPeiPreMemConfig;
  SWITCHABLE_GRAPHICS_CONFIG      *SgGpioData;

  Status = GetConfigBlock((VOID *)SiPreMemPolicyPpi, &gSaMiscPeiPreMemConfigGuid, (VOID *)&MiscPeiPreMemConfig);
  ASSERT_EFI_ERROR(Status);

  Status = GetConfigBlock((VOID *)SiPreMemPolicyPpi, &gSwitchableGraphicsConfigGuid, (VOID *)&SgGpioData);
  ASSERT_EFI_ERROR(Status);

  if (SgGpioData->SaRtd3Pcie0Gpio.GpioSupport != NotSupported) {
    ///
    /// dGPU is present.
    ///      If PCIe Mode or SG Muxless
    ///              Power on MXM
    ///              Configure GPIOs to drive MXM in PCIe mode or SG Muxless
    ///      else
    ///              Do Nothing
    ///
    if ((MiscPeiPreMemConfig->SgMode == SgModeMuxless) ||
        (MiscPeiPreMemConfig->SgMode == SgModeDgpu)) {
      DEBUG((DEBUG_INFO, "Configure GPIOs for driving the dGPU.\n"));
      ///
      ///  Drive DGPU HOLD RST Enable to make sure we hold reset
      ///
      PcieGpioWrite (
        SgGpioData->SaRtd3Pcie0Gpio.HoldRst.GpioNo,
        SgGpioData->SaRtd3Pcie0Gpio.HoldRst.Active,
        GP_ENABLE
        );
      ///
      /// wait 100ms
      ///
      MicroSecondDelay((MiscPeiPreMemConfig->SgDelayAfterHoldReset) * STALL_ONE_MILLI_SECOND);

      ///
      /// Drive DGPU PWR EN to Power On MXM
      ///
      PcieGpioWrite (
        SgGpioData->SaRtd3Pcie0Gpio.PwrEnable.GpioNo,
        SgGpioData->SaRtd3Pcie0Gpio.PwrEnable.Active,
        GP_ENABLE
        );
      ///
      /// wait 300ms
      ///
      MicroSecondDelay((MiscPeiPreMemConfig->SgDelayAfterPwrEn) * STALL_ONE_MILLI_SECOND);

      ///
      /// Drive DGPU HOLD RST Disabled to remove reset
      ///
      PcieGpioWrite (
        SgGpioData->SaRtd3Pcie0Gpio.HoldRst.GpioNo,
        SgGpioData->SaRtd3Pcie0Gpio.HoldRst.Active,
        GP_DISABLE
        );
      ///
      /// wait 100ms
      ///
      MicroSecondDelay((MiscPeiPreMemConfig->SgDelayAfterHoldReset) * STALL_ONE_MILLI_SECOND);
    }
  }
  return EFI_SUCCESS;
}

/**
  PCIe GPIO Write

  @param[in] Gpio        - GPIO Number
  @param[in] Active      - GPIO Active Information; High/Low
  @param[in] Level       - Write GPIO value (0/1)

**/
VOID
PcieGpioWrite (
  IN  UINT32                Gpio,
  IN  BOOLEAN               Active,
  IN  BOOLEAN               Level
  )
{
  EFI_STATUS  Status;

  if (Active == 0) {
    Level = (~Level) & 0x1;
  }
  Status = GpioSetOutputValue(Gpio, (UINT32)Level);
  if (Status != EFI_SUCCESS) {
    return;
  }
}

