/** @file
  PEI Library Functions. Initialize GPIOs


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

#include <PiPei.h>
#include <PeiPlatformHookLib.h>
#include <SaPolicyCommon.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/IoLib.h>
#include <Library/HobLib.h>
#include <Library/PcdLib.h>
#include <Library/TimerLib.h>
#include <Library/PchCycleDecodingLib.h>
#include <Library/PeiPlatformLib.h>
#include <Library/PciSegmentLib.h>
#include <Library/PeiServicesLib.h>
#include <Library/PmcLib.h>
#include <Library/PeiSaPolicyLib.h>
#include <PchAccess.h>
#include <Library/CpuPlatformLib.h>
#include <Library/GpioNativeLib.h>
#include <Library/GpioLib.h>
#include <GpioPinsCnlLp.h>
#include <GpioPinsCnlH.h>
#include <Library/PchInfoLib.h>
#include <Library/CnviLib.h>
#include <SioRegs.h>
#include <PlatformBoardConfig.h>
#include <Library/PchPcrLib.h>
#include <Library/GpioCheckConflictLib.h>

#define SIO_RUNTIME_REG_BASE_ADDRESS                          0x0680

#define RECOVERY_MODE_GPIO_PIN                    0                    // Platform specific @todo use PCD

#define MANUFACTURE_MODE_GPIO_PIN                 0                    // Platform specific @todo use PCD

/**
  Configures GPIO

  @param[in]  GpioTable       Point to Platform Gpio table
  @param[in]  GpioTableCount  Number of Gpio table entries

**/
VOID
ConfigureGpio (
  IN GPIO_INIT_CONFIG                 *GpioDefinition,
  IN UINT16                           GpioTableCount
  )
{
  DEBUG ((DEBUG_INFO, "ConfigureGpio() Start\n"));

  CreateGpioCheckConflictHob (GpioDefinition, GpioTableCount);

  GpioConfigurePads (GpioTableCount, GpioDefinition);

  DEBUG ((DEBUG_INFO, "ConfigureGpio() End\n"));
}

/**
  Configure GPIO group GPE tier.

  @retval     none.
**/
VOID
GpioGroupTierInitHook(
  VOID
  )
{
  DEBUG ((DEBUG_INFO, "GpioGroupTierInitHook Start\n"));

  if (PcdGet32 (PcdGpioGroupToGpeDw0)) {
    GpioSetGroupToGpeDwX (PcdGet32 (PcdGpioGroupToGpeDw0),
                          PcdGet32 (PcdGpioGroupToGpeDw1),
                          PcdGet32 (PcdGpioGroupToGpeDw2));
  }
  DEBUG ((DEBUG_INFO, "GpioGroupTierInitHook End\n"));
}

/**
  Configure single GPIO pad for touchpanel interrupt
**/
VOID
TouchpanelGpioInit (
  VOID
  )
{
  GPIO_INIT_CONFIG*     TouchpanelPad;
  GPIO_PAD_OWN          PadOwnVal;

  PadOwnVal = 0;
  TouchpanelPad = (VOID *) (UINTN) PcdGet32 (PcdBoardGpioTableTouchPanel);
  if (TouchpanelPad != NULL) {
    GpioGetPadOwnership (TouchpanelPad->GpioPad, &PadOwnVal);
    if (PadOwnVal == GpioPadOwnHost) {
      GpioConfigurePads (1, TouchpanelPad);
    }
  }
}

/**
  Configure GPIO Before Memory is not ready.

**/
VOID
GpioInitPreMem (
  VOID
  )
{
  if (PcdGet32 (PcdBoardGpioTablePreMem) != 0 && PcdGet16 (PcdBoardGpioTablePreMemSize) != 0) {
    ConfigureGpio ((VOID *) (UINTN) PcdGet32 (PcdBoardGpioTablePreMem), (UINTN) PcdGet16 (PcdBoardGpioTablePreMemSize));
  }
}

/**
  Basic GPIO configuration before memory is ready

**/
VOID
GpioInitEarlyPreMem (
  VOID
  )
{
  GPIO_CONFIG                     BbrstConfig;
  UINT32                          WwanBbrstGpio;

  WwanBbrstGpio = PcdGet32 (PcdWwanBbrstGpio);

  if (WwanBbrstGpio) {
    //
    // BIOS needs to put modem in OFF state for the two scenarios below.
    // 1. Modem RESET# is not asserted via PLTRST# in the previous sleep state
    // 2. Modem is disabled via setup option
    //
    GpioGetPadConfig (WwanBbrstGpio, &BbrstConfig);
    if ((PcdGetBool (PcdPcieWwanEnable) == FALSE) ||
        (PcdGetBool (PcdWwanResetWorkaround) == TRUE &&
        BbrstConfig.Direction == GpioDirOut &&
        BbrstConfig.OutputState == GpioOutHigh)) {
      //
      // Assert FULL_CARD_POWER_OFF#, RESET# and PERST# GPIOs
      //
      if (PcdGet32 (PcdBoardGpioTableWwanOffEarlyPreMem) != 0 && PcdGet16 (PcdBoardGpioTableWwanOffEarlyPreMemSize) != 0) {
        ConfigureGpio ((VOID *) (UINTN) PcdGet32 (PcdBoardGpioTableWwanOffEarlyPreMem), (UINTN) PcdGet16 (PcdBoardGpioTableWwanOffEarlyPreMemSize));
      }
      if (PcdGetBool (PcdPcieWwanEnable) == TRUE && PcdGetBool (PcdWwanResetWorkaround) == TRUE) {
        MicroSecondDelay (1 * 1000); // Delay by 1ms
      }
    }

    //
    // Turn ON modem power and de-assert RESET# and PERST# GPIOs
    //
    if (PcdGetBool (PcdPcieWwanEnable) == TRUE) {
      if (PcdGet32 (PcdBoardGpioTableWwanOnEarlyPreMem) != 0 && PcdGet16 (PcdBoardGpioTableWwanOnEarlyPreMemSize) != 0) {
        ConfigureGpio ((VOID *) (UINTN) PcdGet32 (PcdBoardGpioTableWwanOnEarlyPreMem), (UINTN) PcdGet16 (PcdBoardGpioTableWwanOnEarlyPreMemSize));
      }
    }
  }
}

/**
  Configure GPIO

**/
VOID
GpioInit (
  VOID
  )
{
  ConfigureGpio ((VOID *) (UINTN) PcdGet32 (PcdBoardGpioTable), (UINTN) PcdGet16 (PcdBoardGpioTableSize));

  if (PcdGet32 (PcdBoardGpioTable2)) {
    ConfigureGpio ((VOID *) (UINTN) PcdGet32 (PcdBoardGpioTable2), (UINTN) PcdGet16 (PcdBoardGpioTable2Size));
  }

  TouchpanelGpioInit ();

  //
  // Lock pads after initializing platform GPIO.
  // Pads which were requested to be unlocked during configuration
  // will not be locked.
  //
  GpioLockPads ();

  return;
}

/**
  Configure Super IO

**/
VOID
SioInit (
  VOID
  )
{
  //
  // Program and Enable Default Super IO Configuration Port Addresses and range
  //
  PchLpcGenIoRangeSet (PcdGet16 (PcdLpcSioConfigDefaultPort) & (~0xF), 0x10);

  PchLpcGenIoRangeSet (SIO_RUNTIME_REG_BASE_ADDRESS  & (~0x7F), 0x10);

  return;
}

/**
  Configure GPIO and SIO before memory ready

  @retval  EFI_SUCCESS   Operation success.
**/
EFI_STATUS
BoardInitPreMem (
  VOID
  )
{
  //
  // Obtain Platform Info from HOB.
  //
  GpioInitPreMem ();
  GpioGroupTierInitHook ();
  SioInit ();

  return EFI_SUCCESS;
}

/**
  Configure GPIO and SIO

  @retval  EFI_SUCCESS   Operation success.
**/
EFI_STATUS
BoardInit (
  VOID
  )
{

  GpioInit ();

  return EFI_SUCCESS;
}

/**
  Do platform specific programming post-memory.

  @retval  EFI_SUCCESS       The function completed successfully.
**/

EFI_STATUS
PlatformSpecificInit (
  VOID
  )
{
  GPIO_CONFIG                     GpioConfig;

  if (IsCnlPch ()) {

    //
    // Tristate unused pins by audio link mode.
    //
    ZeroMem(&GpioConfig, sizeof(GPIO_CONFIG));
    GpioConfig.PadMode = GpioPadModeGpio;
    GpioConfig.HostSoftPadOwn = GpioHostOwnGpio;
    GpioConfig.Direction = GpioDirNone;
    GpioConfig.OutputState = GpioOutDefault;
    GpioConfig.InterruptConfig = GpioIntDis;
    GpioConfig.PowerConfig = GpioPlatformReset;
    GpioConfig.ElectricalConfig = GpioTermNone;

    GpioSetPadConfig (GPIO_CNL_LP_SSP1_SFRM, &GpioConfig);
    GpioSetPadConfig (GPIO_CNL_LP_SSP1_TXD, &GpioConfig);

  }

  return EFI_SUCCESS;
}

/**
  Early Board Configuration before memory is ready

  @retval  EFI_SUCCESS  Operation success.
**/
EFI_STATUS
BoardInitEarlyPreMem (
  VOID
  )
{
  GpioInitEarlyPreMem ();

  return EFI_SUCCESS;
}

