text: /** @file
  
  Copyright (c) 2017 - 2021, Intel Corporation. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent
  
  **/
  
  #include <PiPei.h>
  #include <Library/DebugLib.h>
  #include <Library/IoLib.h>
  #include <Library/MemoryAllocationLib.h>
  #include <Library/PcdLib.h>
  #include <Library/PchCycleDecodingLib.h>
  #include <Library/PchResetLib.h>
  #include <Library/SiliconInitLib.h>
  #include <Library/TimerLib.h>
  #include <Library/PeiLib.h>
  
  #include <Library/GpioLib.h>
  #include <GpioPinsSklLp.h>
  #include <IndustryStandard/TpmPtp.h>
  #include <PchAccess.h>
  
  #include "PeiAspireVn7Dash572GInitLib.h"
  
  #include <ConfigBlock.h>
  #include <ConfigBlock/MemoryConfig.h>
  
  #ifndef STALL_ONE_MILLI_SECOND
  #define STALL_ONE_MILLI_SECOND  1000
  #endif
  
  //
  // Reference RCOMP resistors on motherboard - for Aspire VN7-572G
  //
  GLOBAL_REMOVE_IF_UNREFERENCED const UINT16 RcompResistorAspireVn7Dash572G[SA_MRC_MAX_RCOMP] = { 121, 80, 100 };
  //
  // RCOMP target values for RdOdt, WrDS, WrDSCmd, WrDSCtl, WrDSClk - for Aspire VN7-572G
  //
  GLOBAL_REMOVE_IF_UNREFERENCED const UINT16 RcompTargetAspireVn7Dash572G[SA_MRC_MAX_RCOMP_TARGETS] = { 100, 40, 40, 23, 40 };
  
  //
  // dGPU power GPIO definitions
  #define DGPU_PRESENT	GPIO_SKL_LP_GPP_A20	/* Active low */
  #define DGPU_HOLD_RST	GPIO_SKL_LP_GPP_B4	/* Active low */
  #define DGPU_PWR_EN	GPIO_SKL_LP_GPP_B21	/* Active low */
  
  EFI_STATUS
  EFIAPI
  AspireVn7Dash572GBoardDetect (
    VOID
    );
  
  /**
    Aspire VN7-572G board configuration init function for PEI pre-memory phase.
  
  **/
  VOID
  AspireVn7Dash572GInitPreMem (
    VOID
    )
  {
    //
    // HSIO PTSS Table
    //
    PcdSet32S (PcdSpecificLpHsioPtssTable1,     (UINTN) PchLpHsioPtss_AspireVn7Dash572G);
    PcdSet16S (PcdSpecificLpHsioPtssTable1Size, (UINTN) PchLpHsioPtss_AspireVn7Dash572G_Size);
    PcdSet32S (PcdSpecificLpHsioPtssTable2,     (UINTN) PchLpHsioPtss_AspireVn7Dash572G);
    PcdSet16S (PcdSpecificLpHsioPtssTable2Size, (UINTN) PchLpHsioPtss_AspireVn7Dash572G_Size);
  
    //
    // DRAM related definition
    //
    PcdSet8S (PcdSaMiscUserBd, 5);     // ULT/ULX/Mobile Halo
    PcdSet8S (PcdMrcCaVrefConfig, 2);  // DDR4: "VREF_CA to CH_A and VREF_DQ_B to CH_B"
    // TODO: Clear Dq/Dqs?
    PcdSetBoolS (PcdMrcDqPinsInterleaved, TRUE);
  
    PcdSet32S (PcdMrcRcompResistor, (UINTN) RcompResistorAspireVn7Dash572G);
    PcdSet32S (PcdMrcRcompTarget,   (UINTN) RcompTargetAspireVn7Dash572G);
    //
    // Example policy for DIMM slots implementation boards:
    // 1. Assign Smbus address of DIMMs and SpdData will be updated later
    //    by reading from DIMM SPD.
    // 2. No need to apply hardcoded SpdData buffers here for such board.
    //   Example:
    //   PcdMrcSpdAddressTable0 = 0xA0
    //   PcdMrcSpdAddressTable1 = 0xA2
    //   PcdMrcSpdAddressTable2 = 0xA4
    //   PcdMrcSpdAddressTable3 = 0xA6
    //   PcdMrcSpdData = 0
    //   PcdMrcSpdDataSize = 0
    //
    PcdSet8S (PcdMrcSpdAddressTable0, 0xA0);
    PcdSet8S (PcdMrcSpdAddressTable1, 0);
    PcdSet8S (PcdMrcSpdAddressTable2, 0xA4);
    PcdSet8S (PcdMrcSpdAddressTable3, 0);
    PcdSet32S (PcdMrcSpdData, 0);
    PcdSet16S (PcdMrcSpdDataSize, 0);
  }
  
  /**
    Configures GPIO before memory is ready.
  
  **/
  EFI_STATUS
  EFIAPI
  GpioInitPreMem (
    VOID
    )
  {
    EFI_STATUS  Status;
  
    DEBUG ((DEBUG_INFO, "GpioInitPreMem() Start\n"));
  
    Status = GpioConfigurePads (mGpioTableAspireVn7Dash572G_earlySize, mGpioTableAspireVn7Dash572G_early);
    if (EFI_ERROR (Status)) {
      DEBUG ((DEBUG_ERROR, "Failed to configure early GPIOs!\n"));
      return EFI_DEVICE_ERROR;
    }
  
    DEBUG ((DEBUG_INFO, "GpioInitPreMem() End\n"));
    return EFI_SUCCESS;
  }
  
  /**
    Initialises the dGPU.
  
  **/
  VOID
  DgpuPowerOn (
    VOID
    )
  {
    UINT32         OutputVal;
  
    DEBUG ((DEBUG_INFO, "DgpuPowerOn() Start\n"));
  
    GpioGetOutputValue (DGPU_PRESENT, &OutputVal);
    if (!OutputVal) {
      DEBUG ((DEBUG_INFO, "dGPU present, enable power...\n"));
      GpioSetOutputValue (DGPU_HOLD_RST, 0);  // Assert dGPU_HOLD_RST#
      MicroSecondDelay (2 * STALL_ONE_MILLI_SECOND);
      GpioSetOutputValue (DGPU_PWR_EN, 0);    // Assert dGPU_PWR_EN#
      MicroSecondDelay (7 * STALL_ONE_MILLI_SECOND);
      GpioSetOutputValue (DGPU_HOLD_RST, 1);  // Deassert dGPU_HOLD_RST#
      MicroSecondDelay (30 * STALL_ONE_MILLI_SECOND);
    } else {
      DEBUG ((DEBUG_INFO, "dGPU not present, disable power...\n"));
      GpioSetOutputValue (DGPU_HOLD_RST, 0);  // Assert dGPU_HOLD_RST#
      GpioSetOutputValue (DGPU_PWR_EN, 1);    // Deassert dGPU_PWR_EN#
    }
  
    DEBUG ((DEBUG_INFO, "DgpuPowerOn() End\n"));
  }
  
  /**
    Configure LPC.
  
  **/
  VOID
  LpcInit (
    VOID
    )
  {
    //
    // Program and Enable EC (sideband) Port Addresses and range
    //
    PchLpcGenIoRangeSet (0x68, 0x08);
  
    //
    // Program and Enable EC (index) Port Addresses and range
    //
    PchLpcGenIoRangeSet (0x1200, 0x10);
  }
  
  /**
    Configure GPIO and SIO before memory ready.
  
    @retval  EFI_SUCCESS   Operation success.
  **/
  EFI_STATUS
  EFIAPI
  AspireVn7Dash572GBoardInitBeforeMemoryInit (
    VOID
    )
  {
    EFI_STATUS    Status;
  
    Status = GpioInitPreMem ();
    if (!EFI_ERROR (Status)) {
      DgpuPowerOn ();
    }
    AspireVn7Dash572GInitPreMem ();
  
    ///
    /// Do basic PCH init
    ///
    SiliconInit ();
  
    //
    // Fix-up LPC configuration
    // Enable I/O decoding for COM1(3F8h-3FFh), COM2(2F8h-2FFh), I/O port 2Eh/2Fh, 4Eh/4Fh, 60h/64h and 62h/66h.
    //
    PchLpcIoDecodeRangesSet (PcdGet16 (PcdLpcIoDecodeRange));
    PchLpcIoEnableDecodingSet (PcdGet16 (PchLpcIoEnableDecoding));
  
    //
    // Install PCH RESET PPI and EFI RESET2 PeiService
    //
    Status = PchInitializeReset ();
    ASSERT_EFI_ERROR (Status);
  
    return EFI_SUCCESS;
  }
  
  /**
    Configure GPIO and SIO before memory ready.
  
    @retval  EFI_SUCCESS   Operation success.
  **/
  EFI_STATUS
  EFIAPI
  AspireVn7Dash572GBoardInitAfterMemoryInit (
    VOID
    )
  {
    EFI_STATUS  Status;
  
    // BUGBUG: Workaround for a misbehaving system firmware not setting goIdle
    // - Based on prior investigation for coreboot, I suspect FSP
    if ((MmioRead32 (0xFED40044) & PTP_CRB_CONTROL_AREA_STATUS_TPM_IDLE) == 0) {
      DEBUG ((DEBUG_WARN, "TPM no-IdleBypass bug: workaround enabled\n"));
      MmioWrite32 (0xFED40040, PTP_CRB_CONTROL_AREA_REQUEST_GO_IDLE);
    }
  
    // Program the same 64K range of EC memory as vendor FW
    Status = PchLpcMemRangeSet (0xFE800000);
    if (EFI_ERROR (Status)) {
      DEBUG ((DEBUG_WARN, "Failed to enable LGMR. Were ACPI tables built for LGMR memory map?\n"));
    }
    return EFI_SUCCESS;
  }
  
  EFI_STATUS
  EFIAPI
  AspireVn7Dash572GBoardDebugInit (
    VOID
    )
  {
    ///
    /// Do Early PCH init
    ///
    EarlySiliconInit ();
    LpcInit ();
  
    // NB: MinPlatform specification defines platform initialisation flow.
    // Therefore, we defer board detection until we can program LPC.
    // - Alternatively, move the preceding calls to BoardDetect()
    AspireVn7Dash572GBoardDetect ();
  
    return EFI_SUCCESS;
  }
  
  EFI_BOOT_MODE
  EFIAPI
  AspireVn7Dash572GBoardBootModeDetect (
    VOID
    )
  {
    UINT16         ABase;
    UINT32         SleepType;
  
    DEBUG ((DEBUG_INFO, "Performing boot mode detection\n"));
  
    // TODO: Perform advanced detection (recovery/capsule)
    // FIXME: This violates PI specification? But BOOT_WITH* would always take precedence
    //        over BOOT_ON_S{4,5}...
    PchAcpiBaseGet (&ABase);
    SleepType = IoRead32 (ABase + R_PCH_ACPI_PM1_CNT) & B_PCH_ACPI_PM1_CNT_SLP_TYP;
  
    switch (SleepType) {
      case V_PCH_ACPI_PM1_CNT_S3:
        return BOOT_ON_S3_RESUME;
      case V_PCH_ACPI_PM1_CNT_S4:
        return BOOT_ON_S4_RESUME;
  //    case V_PCH_ACPI_PM1_CNT_S5:
  //      return BOOT_ON_S5_RESUME;
      default:
        return BOOT_WITH_FULL_CONFIGURATION;
    }
  }
  