/** @file

  Copyright (c) 2004  - 2019, Intel Corporation. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

Module Name:

**/

#include "CommonHeader.h"

#include "Platform.h"
#include <Library/PciCf8Lib.h>
#include "PlatformBaseAddresses.h"
#include "PchAccess.h"
#include <Guid/PlatformInfo.h>
#include "PchCommonDefinitions.h"
#include <Ppi/MfgMemoryTest.h>
#include <Guid/SetupVariable.h>
#include <Guid/Vlv2Variable.h>
#include <Ppi/fTPMPolicy.h>

//
// Start::Alpine Valley platform
//
enum {
  SMBUS_READ_BYTE,
  SMBUS_WRITE_BYTE,
  SMBUS_READ_BLOCK,
  SMBUS_WRITE_BLOCK
};

#define EC_BASE           0xE0000000

//
// DEVICE 0 (Memroy Controller Hub)
//
#define MC_BUS              0x00
#define MC_DEV              0x00
#define MC_FUN              0x00
#define MC_DEV_FUN          (MC_DEV << 3)
#define MC_BUS_DEV_FUN      ((MC_BUS << 8) + MC_DEV_FUN)

//
// SysCtl SMBus address and block size
//
#define AV_SC_SMBUS_ADDRESS        	0x60
#define AV_SC_BYTE_LEN            	1
#define AV_SC_BLOCK_LEN            	4
#define AV_SC_SMBUS_WRCMD           1
#define AV_SC_SMBUS_RDCMD           0

//
// SysCtl registers offset
//
#define AV_SC_REG_PLATFORM_ID               24  // 0x18
#define AV_SC_REG_BOARD_ID                  28  // 0x1C
#define AV_SC_REG_FAB_ID                    32  // 0x20
#define AV_SC_REG_ECO_ID                    68  // 0x44
#define AV_SC_REG_DDR_DAUGHTER_CARD_ID      144 // 0x90
#define AV_SC_REG_SODIMM_CONFIG             36

//
// ID values
//
#define AV_SC_PLATFORM_ID_TABLET   0
#define AV_SC_PLATFORM_ID_NETBOOK  2
#define AV_SC_PLATFORM_ID_INTERPOSER 3 // Configuration TBD
#define AV_SC_BOARD_ID_AV_SVP      1492

#define BUS_TRIES                 3       // How many times to retry on Bus Errors

#define GTT_SIZE_1MB        1
#define GTT_SIZE_2MB        2

#define PciCfg16Read( PciExpressBase, Bus, Device, Function, Register ) \
  MmioRead16(PciExpressBase + \
    (UINTN)(Bus << 20) + \
    (UINTN)(Device << 15) + \
    (UINTN)(Function << 12) + \
    (UINTN)(Register))
#define PciCfg16Write( PciExpressBase, Bus, Device, Function, Register, Data ) \
    MmioWrite16(PciExpressBase + \
      (UINTN)(Bus << 20) + \
      (UINTN)(Device << 15) + \
      (UINTN)(Function << 12) + \
      (UINTN)(Register), \
      (UINT16)Data)


//
//Memory Test Manufacturing mode
//
UINT32 DataPatternForMemoryTest[] = {
    0x55555555, 0xAAAAAAAA, 0x55555510, 0x555555EF, 0x55555510, 0x555555EF, 0x55555510, 0x555555EF,
    0x55555555, 0xAAAAAAAA, 0x55551055, 0x5555EF55, 0x55551055, 0x5555EF55, 0x55551055, 0x5555EF55,
    0x55555555, 0xAAAAAAAA, 0x55105555, 0x55EF5555, 0x55105555, 0x55EF5555, 0x55105555, 0x55EF5555,
    0x55555555, 0xAAAAAAAA, 0x10555555, 0xEF555555, 0x10555555, 0xEF555555, 0x10555555, 0xEF555555
};
#define DATA_PATTERN_ARRAY_SIZE (sizeof(DataPatternForMemoryTest) / sizeof(UINT32))

//
//Memory Test Manufacturing mode
//
//
// The global indicator, the FvFileLoader callback will modify it to TRUE after loading PEIM into memory
//
BOOLEAN ImageInMemory = FALSE;

EFI_STATUS
EFIAPI
Stall (
  IN CONST EFI_PEI_SERVICES   **PeiServices,
  IN CONST EFI_PEI_STALL_PPI      *This,
  IN UINTN              Microseconds
  );

EFI_STATUS
EFIAPI
MfgMemoryTest (
  IN  CONST EFI_PEI_SERVICES                   **PeiServices,
  IN  PEI_MFG_MEMORY_TEST_PPI           *This,
  IN  UINT32                             BeginAddress,
  IN  UINT32                             MemoryLength
  );

static EFI_PEI_STALL_PPI  mStallPpi = {
  PEI_STALL_RESOLUTION,
  Stall
};

static PEI_MFG_MEMORY_TEST_PPI mPeiMfgMemoryTestPpi = {
  MfgMemoryTest
};

static EFI_PEI_PPI_DESCRIPTOR mInstallStallPpi[] = {
  {
  EFI_PEI_PPI_DESCRIPTOR_PPI,
  &gEfiPeiStallPpiGuid,
  &mStallPpi
  },
  {
    EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
    &gPeiMfgMemoryTestPpiGuid,
    &mPeiMfgMemoryTestPpi
  }
 };

EFI_PEI_NOTIFY_DESCRIPTOR mMemoryDiscoveredNotifyList[1] = {
  {
    (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
    &gEfiPeiMemoryDiscoveredPpiGuid,
    MemoryDiscoveredPpiNotifyCallback
  }
};

EFI_STATUS
EFIAPI
InstallMonoStatusCode (
  IN EFI_FFS_FILE_HEADER       *FfsHeader,
  IN CONST EFI_PEI_SERVICES          **PeiServices
  );


EFI_STATUS
ReadPlatformIds (
  IN CONST EFI_PEI_SERVICES             **PeiServices,
  IN OUT EFI_PLATFORM_INFO_HOB          *PlatformInfoHob
  );

//
// Start::Alpine Valley platform
//
EFI_STATUS
PeiSmbusExec (
  UINT16 SmbusBase,
  UINT8 SlvAddr,
  UINT8 Operation,
  UINT8 Offset,
  UINT8 *Length,
  UINT8 *Buffer
  );


/**

  Detemine Turbot board 
  @return 0: Not Turbot board
          1: Turbot board 

**/
UINT32 
DetermineTurbotBoard (
  void
  )
{
  UINTN PciD31F0RegBase = 0;
  UINT32 GpioValue = 0;
  UINT32 TmpVal = 0;
  UINT32 MmioConf0 = 0;
  UINT32 MmioPadval = 0;
  UINT32 PConf0Offset = 0x200; //GPIO_S5_4 pad_conf0 register offset
  UINT32 PValueOffset = 0x208; //GPIO_S5_4 pad_value register offset
  UINT32 SSUSOffset = 0x2000;
  UINT32 IoBase = 0;

  DEBUG ((EFI_D_ERROR, "DetermineTurbotBoard() Entry\n"));
  PciD31F0RegBase = MmPciAddress (0,
                      0,
                      PCI_DEVICE_NUMBER_PCH_LPC,
                      PCI_FUNCTION_NUMBER_PCH_LPC,
                      0
                    );
  IoBase = MmioRead32 (PciD31F0RegBase + R_PCH_LPC_IO_BASE) & B_PCH_LPC_IO_BASE_BAR;
  
  MmioConf0 = IoBase + SSUSOffset + PConf0Offset;
  MmioPadval = IoBase + SSUSOffset + PValueOffset;
  //0xFED0E200/0xFED0E208 is pad_Conf/pad_val register address of GPIO_S5_4
  DEBUG ((EFI_D_ERROR, "MmioConf0[0x%x], MmioPadval[0x%x]\n", MmioConf0, MmioPadval));
  
  MmioWrite32 (MmioConf0, 0x2003CC00);  

  TmpVal = MmioRead32 (MmioPadval);
  TmpVal &= ~0x6; //Clear bit 1:2
  TmpVal |= 0x2; // Set the pin as GPI
  MmioWrite32 (MmioPadval, TmpVal); 

  GpioValue = MmioRead32 (MmioPadval);

  DEBUG ((EFI_D_ERROR, "Gpio_S5_4 value is 0x%x\n", GpioValue));
  return (GpioValue & 0x1);
}



EFI_STATUS
FtpmPolicyInit (
  IN CONST EFI_PEI_SERVICES             **PeiServices,
  IN SYSTEM_CONFIGURATION         *pSystemConfiguration
  )
{
  EFI_STATUS                      Status;
  EFI_PEI_PPI_DESCRIPTOR          *mFtpmPolicyPpiDesc;
  SEC_FTPM_POLICY_PPI             *mFtpmPolicyPpi;


  DEBUG((EFI_D_INFO, "FtpmPolicyInit Entry \n"));

  if (NULL == PeiServices ||  NULL == pSystemConfiguration) {
    DEBUG((EFI_D_ERROR, "Input error. \n"));
    return EFI_INVALID_PARAMETER;
  }
  
  Status = (*PeiServices)->AllocatePool(
                             PeiServices,
                             sizeof (EFI_PEI_PPI_DESCRIPTOR),
                             (void **)&mFtpmPolicyPpiDesc
                             );
  ASSERT_EFI_ERROR (Status);

  Status = (*PeiServices)->AllocatePool(
                             PeiServices,
                             sizeof (SEC_FTPM_POLICY_PPI),
                             (void **)&mFtpmPolicyPpi
                             );
  ASSERT_EFI_ERROR (Status);

  //
  // Initialize PPI
  //
  (*PeiServices)->SetMem ((VOID *)mFtpmPolicyPpi, sizeof (SEC_FTPM_POLICY_PPI), 0);
  mFtpmPolicyPpiDesc->Flags = EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST;
  mFtpmPolicyPpiDesc->Guid = &gSeCfTPMPolicyPpiGuid;
  mFtpmPolicyPpiDesc->Ppi = mFtpmPolicyPpi;


  DEBUG((EFI_D_INFO, "pSystemConfiguration->fTPM = 0x%x \n", pSystemConfiguration->fTPM)); 
  if(pSystemConfiguration->fTPM == 1) {
    mFtpmPolicyPpi->fTPMEnable = TRUE;
  } else {
    mFtpmPolicyPpi->fTPMEnable = FALSE;
  }

  Status = (*PeiServices)->InstallPpi(
                             PeiServices,
                             mFtpmPolicyPpiDesc
                             );
  ASSERT_EFI_ERROR (Status);

  DEBUG((EFI_D_INFO, "FtpmPolicyInit done \n"));
  
  return EFI_SUCCESS;
}


/**
  This routine attempts to acquire the SMBus

  @retval FAILURE as failed
  @retval SUCCESS as passed

**/
EFI_STATUS
AcquireBus (
    UINT16	SmbusBase
  )
{
  UINT8 StsReg;

  StsReg  = 0;
  StsReg  = (UINT8)IoRead8(SmbusBase + R_PCH_SMBUS_HSTS);
  if (StsReg & B_PCH_SMBUS_IUS) {
    return EFI_DEVICE_ERROR;
  } else if (StsReg & B_PCH_SMBUS_HBSY) {
    //
    // Clear Status Register and exit
    //
    // Wait for HSTS.HBSY to be clear
	  //
    do { StsReg = (UINT8) IoRead8(SmbusBase+R_PCH_SMBUS_HSTS); } while ((StsReg & B_PCH_SMBUS_HBSY) != 0);

	  //
    // Clear all status bits
	  //
    IoWrite8(SmbusBase+R_PCH_SMBUS_HSTS, 0xFE);
    return EFI_SUCCESS;
  } else {
    //
    // Clear out any odd status information (Will Not Clear In Use)
    //
    IoWrite8(SmbusBase+R_PCH_SMBUS_HSTS, StsReg);
    return EFI_SUCCESS;
  }
}
//
// End::Alpine Valley platform
//

/**
  This function checks the memory range in PEI.

  @param  PeiServices     Pointer to PEI Services.
  @param  This            Pei memory test PPI pointer.
  @param  BeginAddress    Beginning of the memory address to be checked.
  @param  MemoryLength    Bytes of memory range to be checked.
  @param  Operation       Type of memory check operation to be performed.
  @param  ErrorAddress    Return the address of the error memory address.

  @retval  EFI_SUCCESS         The operation completed successfully.
  @retval  EFI_DEVICE_ERROR    Memory test failed. It's not safe to use this range of memory.

**/
EFI_STATUS
EFIAPI
MfgMemoryTest (
  IN  CONST EFI_PEI_SERVICES                   **PeiServices,
  IN  PEI_MFG_MEMORY_TEST_PPI           *This,
  IN  UINT32                             BeginAddress,
  IN  UINT32                             MemoryLength
  )
{
  UINT32 i;
  UINT32 memAddr;
  UINT32 readData;
  UINT32 xorData;
  UINT32 TestFlag = 0;
  memAddr = BeginAddress;

  //
  //Output Message for MFG
  //
  DEBUG ((EFI_D_ERROR, "MFGMODE SET\n"));

  //
  //Writting the pattern in defined location.
  //
  while (memAddr < (BeginAddress+MemoryLength)) {
    for (i = 0; i < DATA_PATTERN_ARRAY_SIZE; i++) {
      if (memAddr > (BeginAddress+MemoryLength -4)) {
        memAddr = memAddr + 4;
        break;
      }
      *((volatile UINT32*) memAddr) = DataPatternForMemoryTest[i];
      memAddr = memAddr + 4;
    }
  }

  //
  //Verify the pattern.
  //
  memAddr = BeginAddress;
  while (memAddr < (BeginAddress+MemoryLength)) {
  for (i = 0; i < DATA_PATTERN_ARRAY_SIZE; i++) {
    if (memAddr > (BeginAddress+MemoryLength -4)) {
      memAddr = memAddr + 4;
      break;
    }
    readData = *((volatile UINT32*) memAddr);
    xorData = readData ^ DataPatternForMemoryTest[i];

	  //
    // If xorData is nonzero, this particular memAddr has a failure.
	  //
    if (xorData != 0x00000000) {
      DEBUG ((EFI_D_ERROR, "Expected value....: %x\n", DataPatternForMemoryTest[i]));
      DEBUG ((EFI_D_ERROR, "ReadData value....: %x\n", readData));
      DEBUG ((EFI_D_ERROR, "Pattern failure at....: %x\n", memAddr));
      TestFlag = 1;
    }
    memAddr = memAddr + 4;
    }
  }
  if (TestFlag) {
    return EFI_DEVICE_ERROR;
  }

  //
  //Output Message for MFG
  //
  DEBUG ((EFI_D_ERROR, "MFGMODE MEMORY TEST PASSED\n"));
  return EFI_SUCCESS;
}

BOOLEAN
IsRtcUipAlwaysSet (
  IN CONST EFI_PEI_SERVICES       **PeiServices
  )
{

  EFI_PEI_STALL_PPI *StallPpi;
  UINTN             Count;

  (**PeiServices).LocatePpi (PeiServices, &gEfiPeiStallPpiGuid, 0, NULL, (void **)&StallPpi);

  for (Count = 0; Count < 500; Count++) { // Maximum waiting approximates to 1.5 seconds (= 3 msec * 500)
    IoWrite8 (R_PCH_RTC_INDEX2, R_PCH_RTC_REGISTERA);
    if ((IoRead8 (R_PCH_RTC_TARGET2) & B_PCH_RTC_REGISTERA_UIP) == 0) {
      return FALSE;
    }

    StallPpi->Stall (PeiServices, StallPpi, 3000);
  }

  return TRUE;
}

EFI_STATUS
RtcPowerFailureHandler (
  IN CONST EFI_PEI_SERVICES       **PeiServices
  )
{

  UINT16          DataUint16;
  UINT8           DataUint8;
  BOOLEAN         RtcUipIsAlwaysSet;
  DataUint16        = MmioRead16 (PMC_BASE_ADDRESS + R_PCH_PMC_GEN_PMCON_1);
  RtcUipIsAlwaysSet = IsRtcUipAlwaysSet (PeiServices);
  if ((DataUint16 & B_PCH_PMC_GEN_PMCON_RTC_PWR_STS) || (RtcUipIsAlwaysSet)) {
    //
    // Execute the sequence below. This will ensure that the RTC state machine has been initialized.
    //
    // Step 1.
    // BIOS clears this bit by writing a '0' to it.
    //
    if (DataUint16 & B_PCH_PMC_GEN_PMCON_RTC_PWR_STS) {
      //
      // Set to invalid date in order to reset the time to
      // BIOS build time later in the boot (SBRUN.c file).
      //
      IoWrite8 (R_PCH_RTC_INDEX2, R_PCH_RTC_YEAR);
      IoWrite8 (R_PCH_RTC_TARGET2, 0x0FF);
      IoWrite8 (R_PCH_RTC_INDEX2, R_PCH_RTC_MONTH);
      IoWrite8 (R_PCH_RTC_TARGET2, 0x0FF);
      IoWrite8 (R_PCH_RTC_INDEX2, R_PCH_RTC_DAYOFMONTH);
      IoWrite8 (R_PCH_RTC_TARGET2, 0x0FF);
      IoWrite8 (R_PCH_RTC_INDEX2, R_PCH_RTC_DAYOFWEEK);
      IoWrite8 (R_PCH_RTC_TARGET2, 0x0FF);

      IoWrite8 (R_PCH_RTC_INDEX2, R_PCH_RTC_SECONDSALARM);
      IoWrite8 (R_PCH_RTC_TARGET2, 0x00);
      IoWrite8 (R_PCH_RTC_INDEX2, R_PCH_RTC_MINUTESALARM);
      IoWrite8 (R_PCH_RTC_TARGET2, 0x00);
      IoWrite8 (R_PCH_RTC_INDEX2, R_PCH_RTC_HOURSALARM);
      IoWrite8 (R_PCH_RTC_TARGET2, 0x00);
    }

    //
    // Step 2.
    // Set RTC Register 0Ah[6:4] to '110' or '111'.
    //
    IoWrite8 (R_PCH_RTC_INDEX2, R_PCH_RTC_REGISTERA);
    IoWrite8 (R_PCH_RTC_TARGET2, (V_PCH_RTC_REGISTERA_DV_DIV_RST1 | V_PCH_RTC_REGISTERA_RS_976P5US));

    //
    // Step 3.
    // Set RTC Register 0Bh[7].
    //
    IoWrite8 (R_PCH_RTC_INDEX2, R_PCH_RTC_REGISTERB);
    DataUint8 = (IoRead8 (R_PCH_RTC_TARGET2) | B_PCH_RTC_REGISTERB_SET);
    IoWrite8 (R_PCH_RTC_INDEX2, R_PCH_RTC_REGISTERB);
    IoWrite8 (R_PCH_RTC_TARGET2, DataUint8);

    //
    // Step 4.
    // Set RTC Register 0Ah[6:4] to '010'.
    //
    IoWrite8 (R_PCH_RTC_INDEX2, R_PCH_RTC_REGISTERA);
    IoWrite8 (R_PCH_RTC_TARGET2, (V_PCH_RTC_REGISTERA_DV_NORM_OP | V_PCH_RTC_REGISTERA_RS_976P5US));

    //
    // Step 5.
    // Clear RTC Register 0Bh[7].
    //
    IoWrite8 (R_PCH_RTC_INDEX2, R_PCH_RTC_REGISTERB);
    DataUint8 = (IoRead8 (R_PCH_RTC_TARGET2) & (UINT8)~B_PCH_RTC_REGISTERB_SET);
    IoWrite8 (R_PCH_RTC_INDEX2, R_PCH_RTC_REGISTERB);
    IoWrite8 (R_PCH_RTC_TARGET2, DataUint8);
  }

  return EFI_SUCCESS;
}


VOID
PchBaseInit (
  VOID
  )
{
  //
  // Program ACPI Power Management I/O Space Base Address
  //
  MmioWrite16 (
    MmPciAddress (0,
      DEFAULT_PCI_BUS_NUMBER_PCH,
      PCI_DEVICE_NUMBER_PCH_LPC,
      PCI_FUNCTION_NUMBER_PCH_LPC,
      R_PCH_LPC_ACPI_BASE
    ),
    (UINT16)((ACPI_BASE_ADDRESS & B_PCH_LPC_ACPI_BASE_BAR) | B_PCH_LPC_ACPI_BASE_EN)
  );

  //
  // Program GPIO Base Address
  //
  MmioWrite16 (
    MmPciAddress (0,
      DEFAULT_PCI_BUS_NUMBER_PCH,
      PCI_DEVICE_NUMBER_PCH_LPC,
      PCI_FUNCTION_NUMBER_PCH_LPC,
      R_PCH_LPC_GPIO_BASE
    ),
    (UINT16)((GPIO_BASE_ADDRESS & B_PCH_LPC_GPIO_BASE_BAR) | B_PCH_LPC_GPIO_BASE_EN)
  );

  //
  // Set PMC Base Address
  //
  MmioWrite32 (
    MmPciAddress (0,
      DEFAULT_PCI_BUS_NUMBER_PCH,
      PCI_DEVICE_NUMBER_PCH_LPC,
      PCI_FUNCTION_NUMBER_PCH_LPC,
      R_PCH_LPC_PMC_BASE
    ),
    (UINT32)((PMC_BASE_ADDRESS & B_PCH_LPC_PMC_BASE_BAR) | B_PCH_LPC_PMC_BASE_EN)
  );

  //
  // Set IO Base Address
  //
  MmioWrite32 (
    MmPciAddress (0,
      DEFAULT_PCI_BUS_NUMBER_PCH,
      PCI_DEVICE_NUMBER_PCH_LPC,
      PCI_FUNCTION_NUMBER_PCH_LPC,
      R_PCH_LPC_IO_BASE
    ),
    (UINT32)((IO_BASE_ADDRESS & B_PCH_LPC_IO_BASE_BAR) | B_PCH_LPC_IO_BASE_EN)
  );

  //
  // Set ILB Base Address
  //
  MmioWrite32 (
    MmPciAddress (0,
      DEFAULT_PCI_BUS_NUMBER_PCH,
      PCI_DEVICE_NUMBER_PCH_LPC,
      PCI_FUNCTION_NUMBER_PCH_LPC,
      R_PCH_LPC_ILB_BASE
    ),
    (UINT32)((ILB_BASE_ADDRESS & B_PCH_LPC_ILB_BASE_BAR) | B_PCH_LPC_ILB_BASE_EN)
  );

  //
  // Set PUnit Base Address
  //
  MmioWrite32 (
    MmPciAddress (0,
      DEFAULT_PCI_BUS_NUMBER_PCH,
      PCI_DEVICE_NUMBER_PCH_LPC,
      PCI_FUNCTION_NUMBER_PCH_LPC,
      R_PCH_LPC_PUNIT_BASE
    ),
    (UINT32)((PUNIT_BASE_ADDRESS & B_PCH_LPC_PUNIT_BASE_BAR) | B_PCH_LPC_PUNIT_BASE_EN)
  );

  //
  // Set SPI Base Address
  //
  MmioWrite32 (
    MmPciAddress (0,
      DEFAULT_PCI_BUS_NUMBER_PCH,
      PCI_DEVICE_NUMBER_PCH_LPC,
      PCI_FUNCTION_NUMBER_PCH_LPC,
      R_PCH_LPC_SPI_BASE
    ),
    (UINT32)((SPI_BASE_ADDRESS & B_PCH_LPC_SPI_BASE_BAR) | B_PCH_LPC_SPI_BASE_EN)
  );

  //
  // Set Root Complex Base Address
  //
  MmioWrite32 (
    MmPciAddress (0,
      DEFAULT_PCI_BUS_NUMBER_PCH,
      PCI_DEVICE_NUMBER_PCH_LPC,
      PCI_FUNCTION_NUMBER_PCH_LPC,
      R_PCH_LPC_RCBA
    ),
    (UINT32)((RCBA_BASE_ADDRESS & B_PCH_LPC_RCBA_BAR) | B_PCH_LPC_RCBA_EN)
  );

  //
  // Set MPHY Base Address
  //
  MmioWrite32 (
    MmPciAddress (0,
      DEFAULT_PCI_BUS_NUMBER_PCH,
      PCI_DEVICE_NUMBER_PCH_LPC,
      PCI_FUNCTION_NUMBER_PCH_LPC,
      R_PCH_LPC_MPHY_BASE
    ),
    (UINT32)((MPHY_BASE_ADDRESS & B_PCH_LPC_MPHY_BASE_BAR) | B_PCH_LPC_MPHY_BASE_EN)
  );
  MmioWrite16 (
    MmPciAddress (0,
      DEFAULT_PCI_BUS_NUMBER_PCH,
      PCI_DEVICE_NUMBER_PCH_SMBUS,
      PCI_FUNCTION_NUMBER_PCH_SMBUS,
      R_PCH_SMBUS_BASE
    ),
    (UINT16)(SMBUS_BASE_ADDRESS & B_PCH_SMBUS_BASE_BAR)
  );

  MmioOr8 (
    MmPciAddress (0,
      DEFAULT_PCI_BUS_NUMBER_PCH,
      PCI_DEVICE_NUMBER_PCH_SMBUS,
      PCI_FUNCTION_NUMBER_PCH_SMBUS,
      R_PCH_SMBUS_PCICMD
    ),
    B_PCH_SMBUS_PCICMD_IOSE
  );

}

/**
  This is the entrypoint of PEIM

  @param  FileHandle  Handle of the file being invoked.
  @param  PeiServices Describes the list of possible PEI Services.

  @retval EFI_SUCCESS if it completed successfully.
**/
EFI_STATUS
EFIAPI
PeiInitPlatform (
  IN       EFI_PEI_FILE_HANDLE  FileHandle,
  IN CONST EFI_PEI_SERVICES     **PeiServices
  )
{
  UINTN                            SmbusRegBase;
  EFI_PLATFORM_INFO_HOB            PlatformInfo;
  EFI_STATUS                       Status= EFI_SUCCESS;
  EFI_PEI_READ_ONLY_VARIABLE2_PPI  *Variable = NULL;
  UINTN                            VariableSize;
  SYSTEM_CONFIGURATION             SystemConfiguration;
  UINT32                           GGC = 0;

  EFI_PEI_PPI_DESCRIPTOR          *mVlvMmioPolicyPpiDesc;
  VLV_MMIO_POLICY_PPI             *mVlvMmioPolicyPpi;

  ZeroMem (&PlatformInfo, sizeof(PlatformInfo));

  Status =  InstallMonoStatusCode(FileHandle, PeiServices);
  ASSERT_EFI_ERROR (Status);


  //
  // Initialize Stall PPIs
  //
  Status = (*PeiServices)->InstallPpi (PeiServices, &mInstallStallPpi[0]);
  ASSERT_EFI_ERROR (Status);

  Status = (*PeiServices)->NotifyPpi (PeiServices, &mMemoryDiscoveredNotifyList[0]);
  ASSERT_EFI_ERROR (Status);
  SmbusRegBase = PchPciDeviceMmBase (
                   DEFAULT_PCI_BUS_NUMBER_PCH,
                   PCI_DEVICE_NUMBER_PCH_SMBUS,
                   PCI_FUNCTION_NUMBER_PCH_SMBUS
                   );
  //
  // Since PEI has no PCI enumerator, set the BAR & I/O space enable ourselves
  //
  MmioAndThenOr32 (SmbusRegBase + R_PCH_SMBUS_BASE, B_PCH_SMBUS_BASE_BAR, SMBUS_BASE_ADDRESS);

  MmioOr8 (SmbusRegBase + R_PCH_SMBUS_PCICMD, B_PCH_SMBUS_PCICMD_IOSE);

  PchBaseInit();

  //
  //Todo: confirm if we need program 8254
  //
  // Setting 8254
  // Program timer 1 as refresh timer
  //
  IoWrite8 (0x43, 0x54);
  IoWrite8 (0x41, 0x12);

  //
  // RTC power failure handling
  //
  RtcPowerFailureHandler (PeiServices);


  PchMmPci32( 0, 0, 2, 0, 0x50) = 0x210;

  VariableSize = sizeof (SYSTEM_CONFIGURATION);
  ZeroMem (&SystemConfiguration, VariableSize);

  //
  // Obtain variable services
  //
  Status = (*PeiServices)->LocatePpi(
                             PeiServices,
                             &gEfiPeiReadOnlyVariable2PpiGuid,
                             0,
                             NULL,
                             (void **)&Variable
                             );
  ASSERT_EFI_ERROR(Status);
  Status = Variable->GetVariable (
                       Variable,
                       L"Setup",
                       &gEfiSetupVariableGuid,
                       NULL,
                       &VariableSize,
                       &SystemConfiguration
					   );
  if (EFI_ERROR (Status) || VariableSize != sizeof(SYSTEM_CONFIGURATION)) {
    //The setup variable is corrupted
    VariableSize = sizeof(SYSTEM_CONFIGURATION);
    Status = Variable->GetVariable(
              Variable,
              L"SetupRecovery",
              &gEfiSetupVariableGuid,
              NULL,
              &VariableSize,
              &SystemConfiguration
              );
    ASSERT_EFI_ERROR (Status);
  }
  
  if (EFI_ERROR (Status)) {
    GGC = ((2 << 3) | 0x200);
    PciCfg16Write(EC_BASE, 0, 2, 0, 0x50, GGC);
    GGC = PciCfg16Read(EC_BASE, 0, 2, 0, 0x50);
    DEBUG((EFI_D_INFO , "GGC: 0x%08x GMSsize:0x%08x\n", GGC, (GGC & (BIT7|BIT6|BIT5|BIT4|BIT3))>>3));
  } else {
    if (SystemConfiguration.Igd == 1 && SystemConfiguration.PrimaryVideoAdaptor != 2) {
      GGC = (SystemConfiguration.IgdDvmt50PreAlloc << 3) |
            (SystemConfiguration.GTTSize == GTT_SIZE_1MB ? 0x100: 0x200);
      PciCfg16Write(EC_BASE, 0, 2, 0, 0x50, GGC);
      GGC = PciCfg16Read(EC_BASE, 0, 2, 0, 0x50);
      DEBUG((EFI_D_INFO , "GGC: 0x%08x GMSsize:0x%08x\n", GGC, (GGC & (BIT7|BIT6|BIT5|BIT4|BIT3))>>3));
    }
  }

  //
  // Initialize PlatformInfo HOB
  //
  Status = ReadPlatformIds(PeiServices, &PlatformInfo);
  ASSERT_EFI_ERROR (Status);

  //
  // 0 -> Disable , 1 -> Enable
  //
  if(SystemConfiguration.CfioPnpSettings == 1) {
    DEBUG((EFI_D_INFO, "CheckCfioPnpSettings: CFIO Pnp Settings Enabled\n"));
    PlatformInfo.CfioEnabled = 1;
  } else {
    DEBUG((EFI_D_INFO, "CheckCfioPnpSettings: CFIO Pnp Settings Disabled\n"));
    PlatformInfo.CfioEnabled = 0;
  }

  //
  // Build HOB for PlatformInfo
  //
  BuildGuidDataHob (
    &gEfiPlatformInfoGuid,
    &PlatformInfo,
    sizeof (EFI_PLATFORM_INFO_HOB)
    );

  //
  // Set the new boot mode for MRC
  //
  Status = UpdateBootMode (PeiServices);
  ASSERT_EFI_ERROR (Status);

  DEBUG((EFI_D_INFO, "Setup MMIO size ... \n\n"));

  //
  // Setup MMIO size
  //
  Status = (*PeiServices)->AllocatePool(
                             PeiServices,
                             sizeof (EFI_PEI_PPI_DESCRIPTOR),
                             (void **)&mVlvMmioPolicyPpiDesc
                             );
  ASSERT_EFI_ERROR (Status);
  Status = (*PeiServices)->AllocatePool(
                             PeiServices,
                             sizeof (VLV_MMIO_POLICY_PPI),
                             (void **)&mVlvMmioPolicyPpi
                             );
  ASSERT_EFI_ERROR (Status);
  (*PeiServices)->SetMem (
                    (VOID *)mVlvMmioPolicyPpi,
                    sizeof (VLV_MMIO_POLICY_PPI),
                    0
                    );
  mVlvMmioPolicyPpiDesc->Flags = EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST;
  mVlvMmioPolicyPpiDesc->Guid = &gVlvMmioPolicyPpiGuid;
  mVlvMmioPolicyPpiDesc->Ppi = mVlvMmioPolicyPpi;
  switch (SystemConfiguration.MmioSize) {
    case 0:      // 768MB
      mVlvMmioPolicyPpi->MmioSize = 0x300;
      break;
    case 1:      // 1GB
      mVlvMmioPolicyPpi->MmioSize = 0x400;
      break;
    case 2:      // 1.25GB
      mVlvMmioPolicyPpi->MmioSize = 0x500;
      break;
    case 3:      // 1.5GB
      mVlvMmioPolicyPpi->MmioSize = 0x600;
      break;
    case 4:      // 2GB
      mVlvMmioPolicyPpi->MmioSize = 0x800;
      break;
    default:
      mVlvMmioPolicyPpi->MmioSize = 0x800;
      break;
  }
  Status = (*PeiServices)->InstallPpi(
                             PeiServices,
                             mVlvMmioPolicyPpiDesc
                             );
  ASSERT_EFI_ERROR (Status);

  return Status;
}

EFI_STATUS
ReadPlatformIds (
  IN CONST EFI_PEI_SERVICES             **PeiServices,
  IN OUT EFI_PLATFORM_INFO_HOB          *PlatformInfoHob
  )
{
  {
    EFI_STATUS                      Status = EFI_SUCCESS;
    UINT8                           FabId = 0;
    UINTN                           DataSize;
    EFI_PLATFORM_INFO_HOB           TmpHob;
    EFI_PEI_READ_ONLY_VARIABLE2_PPI *PeiVar;
    UINT32                          CompatibleBoard = 0; 

    Status = (**PeiServices).LocatePpi (
                               PeiServices,
                               &gEfiPeiReadOnlyVariable2PpiGuid,
                               0,
                               NULL,
                               (void **)&PeiVar
                               );
    ASSERT_EFI_ERROR (Status);

    DataSize = sizeof (EFI_PLATFORM_INFO_HOB);
    Status = PeiVar->GetVariable (
                       PeiVar,
                       L"PlatformInfo",
                       &gEfiVlv2VariableGuid,
                       NULL,
                       &DataSize,
                       &TmpHob
					   );

    if (Status == EFI_SUCCESS) {
      PlatformInfoHob->BoardId        = TmpHob.BoardId;
      PlatformInfoHob->MemCfgID       = TmpHob.MemCfgID;
      PlatformInfoHob->BoardRev       = TmpHob.BoardRev;
      PlatformInfoHob->PlatformFlavor = TmpHob.PlatformFlavor;
      return Status;
    }

    CompatibleBoard = DetermineTurbotBoard();
   if (1 == CompatibleBoard) {
     PlatformInfoHob->BoardId    = BOARD_ID_MINNOW2_TURBOT;
     DEBUG ((EFI_D_INFO,  "I'm MinnowBoard Turbot!\n"));
   } else {       
     PlatformInfoHob->BoardId    = BOARD_ID_MINNOW2;
     DEBUG ((EFI_D_INFO,  "I'm MinnowBoard Max!\n"));
   }
    

    PlatformInfoHob->MemCfgID   = 0;
    PlatformInfoHob->BoardRev   = FabId + 1;	// FabId = 0 means FAB1 (BoardRev = 1), FabId = 1 means FAB2 (BoardRev = 2)...
    PlatformInfoHob->PlatformFlavor = FlavorMobile;
  }

  return EFI_SUCCESS;
}

//
// Start::Alpine Valley platform
//
/**
  This routine reads SysCtl registers

  @param SmbusBase   SMBUS Base Address
  @param SlvAddr     Targeted Smbus Slave device address
  @param Operation   Which SMBus protocol will be used
  @param Offset      Offset of the register
  @param  Length     Number of bytes
  @param  Buffer     Buffer contains values read from registers

  @retval SUCCESS as passed
  @retval Others as failed

**/
EFI_STATUS
PeiSmbusExec (
  UINT16 SmbusBase,
  UINT8 SlvAddr,
  UINT8 Operation,
  UINT8 Offset,
  UINT8 *Length,
  UINT8 *Buffer
  )
{
  EFI_STATUS  Status=EFI_SUCCESS;
  UINT8       AuxcReg;
  UINT8       SmbusOperation = 0;
  UINT8       StsReg;
  UINT8       SlvAddrReg;
  UINT8       HostCmdReg;
  UINT8       BlockCount = 0;
  BOOLEAN     BufferTooSmall;
  UINT8       Index;
  UINT8       *CallBuffer;
  UINT8  	  RetryCount = BUS_TRIES;

  //
  // MrcSmbusExec supports byte and block read.
  // Only allow Byte or block access
  //
  if (!((*Length  == AV_SC_BYTE_LEN) || (*Length == AV_SC_BLOCK_LEN))) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // See if its ok to use the bus based upon INUSE_STS bit.
  //
  Status = AcquireBus (SmbusBase);
  ASSERT_EFI_ERROR(Status);

  CallBuffer = Buffer;

  //
  //SmbStatus Bits of interest
  //[6] = IUS (In Use Status)
  //[4] = FAIL
  //[3] = BERR (Bus Error = transaction collision)
  //[2] = DERR (Device Error = Illegal Command Field, Unclaimed Cycle, Host Device Timeout, CRC Error)
  //[1] = INTR (Successful completion of last command)
  //[0] = HOST BUSY
  //
  //
  // This is the main operation loop.  If the operation results in a Smbus
  // collision with another master on the bus, it attempts the requested
  // transaction again at least BUS_TRIES attempts.
  //
  while (RetryCount--) {
    //
    // Operation Specifics (pre-execution)
    //
    Status          = EFI_SUCCESS;
    SlvAddrReg      = SlvAddr;
    HostCmdReg      = Offset;
    AuxcReg         = 0;

	switch (Operation) {

	case SMBUS_WRITE_BYTE:
    IoWrite8 (SmbusBase+R_PCH_SMBUS_HD0, CallBuffer[0]);
		SmbusOperation = V_PCH_SMBUS_SMB_CMD_BYTE_DATA;
	break;

    case SMBUS_READ_BYTE:
      SmbusOperation = V_PCH_SMBUS_SMB_CMD_BYTE_DATA;
	  	SlvAddrReg |= B_PCH_SMBUS_RW_SEL_READ;
      if (*Length < 1) {
        Status = EFI_INVALID_PARAMETER;
      }
      	*Length = 1;
	break;

    case SMBUS_WRITE_BLOCK:
      SmbusOperation  = V_PCH_SMBUS_SMB_CMD_BLOCK;
      IoWrite8 (SmbusBase+R_PCH_SMBUS_HD0, *(UINT8 *) Length);
     	BlockCount = (UINT8) (*Length);
     	if ((*Length < 1) || (*Length > 32)) {
        Status = EFI_INVALID_PARAMETER;
        break;
    	}
      	AuxcReg |= B_PCH_SMBUS_E32B;
	break;

    case SMBUS_READ_BLOCK:
      SmbusOperation = V_PCH_SMBUS_SMB_CMD_BLOCK;
     	SlvAddrReg |= B_PCH_SMBUS_RW_SEL_READ;
     	if ((*Length < 1) || (*Length > 32)) {
        Status = EFI_INVALID_PARAMETER;
        break;
     	}
      	AuxcReg |= B_PCH_SMBUS_E32B;
	break;

    default:
      	Status = EFI_INVALID_PARAMETER;
	break;
    }

    //
    // Set Auxiliary Control register
    //
    IoWrite8 (SmbusBase+R_PCH_SMBUS_AUXC, AuxcReg);

    //
    // Reset the pointer of the internal buffer
    //
    IoRead8 (SmbusBase+R_PCH_SMBUS_HCTL);

    //
    // Now that the 32 byte buffer is turned on, we can write th block data
    // into it
    //
    if (Operation == SMBUS_WRITE_BLOCK) {
      for (Index = 0; Index < BlockCount; Index++) {
        //
        // Write next byte
        //
        IoWrite8 (SmbusBase+R_PCH_SMBUS_HBD, CallBuffer[Index]);
      }
    }

    //
    // Set SMBus slave address for the device to read
    //
    IoWrite8(SmbusBase+R_PCH_SMBUS_TSA, SlvAddrReg);

    //
    //
    // Set Command register for the offset to read
    //
    IoWrite8(SmbusBase+R_PCH_SMBUS_HCMD, HostCmdReg );

    //
    // Set Control Register to Set "operation command" protocol and start bit
    //
    IoWrite8(SmbusBase+R_PCH_SMBUS_HCTL, (UINT8) (SmbusOperation + B_PCH_SMBUS_START));

    //
    // Wait for IO to complete
    //
	do { StsReg = (UINT8) IoRead8(SmbusBase+0); } while ((StsReg & (BIT4|BIT3|BIT2|BIT1)) == 0);

	  if (StsReg & B_PCH_SMBUS_DERR) {
      Status = EFI_DEVICE_ERROR;
      break;
    } else if (StsReg & B_PCH_SMBUS_BERR) {
      //
      // Clear the Bus Error for another try
      //
      Status = EFI_DEVICE_ERROR;
      IoWrite8(SmbusBase+R_PCH_SMBUS_HSTS, B_PCH_SMBUS_BERR);

      //
      // Clear Status Registers
      //
      IoWrite8(SmbusBase+R_PCH_SMBUS_HSTS, B_PCH_SMBUS_HSTS_ALL);
      IoWrite8(SmbusBase+R_PCH_SMBUS_AUXS, B_PCH_SMBUS_CRCE);

      continue;
    }

    //
    // successfull completion
    // Operation Specifics (post-execution)
    //
    switch (Operation) {

    case SMBUS_READ_BYTE:
      CallBuffer[0] = (UINT8)(IoRead8 (SmbusBase+R_PCH_SMBUS_HD0));
      break;

    case SMBUS_WRITE_BLOCK:
      IoWrite8(SmbusBase+R_PCH_SMBUS_HSTS, B_PCH_SMBUS_BYTE_DONE_STS);
      break;

    case SMBUS_READ_BLOCK:
      BufferTooSmall = FALSE;

      //
      // Find out how many bytes will be in the block
      //
      BlockCount = (UINT8)(IoRead8 (SmbusBase+R_PCH_SMBUS_HD0));
      if (*Length < BlockCount) {
        BufferTooSmall = TRUE;
      } else {
        for (Index = 0; Index < BlockCount; Index++) {
          //
          // Read the byte
          //
          CallBuffer[Index] = (UINT8)IoRead8 (SmbusBase+R_PCH_SMBUS_HBD);
        }
      }

      *Length = BlockCount;
      if (BufferTooSmall) {
        Status = EFI_BUFFER_TOO_SMALL;
      }
      break;

    default:
      break;
    };

    if ((StsReg & B_PCH_SMBUS_BERR) && (Status == EFI_SUCCESS)) {
      //
      // Clear the Bus Error for another try
      //
      Status = EFI_DEVICE_ERROR;
      IoWrite8(SmbusBase+R_PCH_SMBUS_HSTS, B_PCH_SMBUS_BERR);

      continue;
    } else {
      break;
    }
  }

  //
  // Clear Status Registers and exit
  //
  IoWrite8(SmbusBase+R_PCH_SMBUS_HSTS, B_PCH_SMBUS_HSTS_ALL);
  IoWrite8(SmbusBase+R_PCH_SMBUS_AUXS, B_PCH_SMBUS_CRCE);
  IoWrite8(SmbusBase+R_PCH_SMBUS_AUXC, 0);
  return Status;
}
//
// End::Alpine Valley platform
//

