/** @file

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

**/
#include "SpiCommon.h"

/**
  Acquire SPI MMIO BAR.

  @param[in] PchSpiBase           PCH SPI PCI Base Address

  @retval                         Return SPI BAR Address

**/
UINT32
AcquireSpiBar0 (
  IN  UINTN  PchSpiBase
  )
{
  return MmioRead32 (PchSpiBase + R_SPI_BASE) & ~(B_SPI_BAR0_MASK);
}

/**
  Release SPI MMIO BAR. Do nothing.

  @param[in] PchSpiBase           PCH SPI PCI Base Address

**/
VOID
ReleaseSpiBar0 (
  IN  UINTN  PchSpiBase
  )
{
}

/**
  This function is to enable/disable BIOS Write Protect in SMM phase.

  @param[in] EnableSmmSts        Flag to Enable/disable Bios write protect

**/
VOID
CpuSmmDisableBiosWriteProtect (
  IN  BOOLEAN  EnableSmmSts
  )
{
  UINT32  Data32;

  if (EnableSmmSts) {
    //
    // Disable BIOS Write Protect in SMM phase.
    //
    Data32 = MmioRead32 ((UINTN)(0xFED30880)) | (UINT32)(BIT0);
    AsmWriteMsr32 (0x000001FE, Data32);
  } else {
    //
    // Enable BIOS Write Protect in SMM phase
    //
    Data32 = MmioRead32 ((UINTN)(0xFED30880)) & (UINT32)(~BIT0);
    AsmWriteMsr32 (0x000001FE, Data32);
  }

  //
  // Read FED30880h back to ensure the setting went through.
  //
  Data32 = MmioRead32 (0xFED30880);
}

/**
  This function is a hook for Spi to disable BIOS Write Protect.

  @param[in] PchSpiBase           PCH SPI PCI Base Address
  @param[in] CpuSmmBwp            Need to disable CPU SMM Bios write protection or not

  @retval EFI_SUCCESS             The protocol instance was properly initialized
  @retval EFI_ACCESS_DENIED       The BIOS Region can only be updated in SMM phase

**/
EFI_STATUS
EFIAPI
DisableBiosWriteProtect (
  IN  UINTN  PchSpiBase,
  IN  UINT8  CpuSmmBwp
  )
{
  //
  // Write clear BC_SYNC_SS prior to change WPD from 0 to 1.
  //
  MmioOr8 (PchSpiBase + R_SPI_BCR + 1, (B_SPI_BCR_SYNC_SS >> 8));

  //
  // Enable the access to the BIOS space for both read and write cycles
  //
  MmioOr8 (PchSpiBase + R_SPI_BCR, B_SPI_BCR_BIOSWE);

  if (CpuSmmBwp != 0) {
    CpuSmmDisableBiosWriteProtect (TRUE);
  }

  return EFI_SUCCESS;
}

/**
  This function is a hook for Spi to enable BIOS Write Protect.

  @param[in] PchSpiBase           PCH SPI PCI Base Address
  @param[in] CpuSmmBwp            Need to disable CPU SMM Bios write protection or not

**/
VOID
EFIAPI
EnableBiosWriteProtect (
  IN  UINTN  PchSpiBase,
  IN  UINT8  CpuSmmBwp
  )
{
  //
  // Disable the access to the BIOS space for write cycles
  //
  MmioAnd8 (PchSpiBase + R_SPI_BCR, (UINT8)(~B_SPI_BCR_BIOSWE));

  if (CpuSmmBwp != 0) {
    CpuSmmDisableBiosWriteProtect (FALSE);
  }
}

/**
  This function disables SPI Prefetching and caching,
  and returns previous BIOS Control Register value before disabling.

  @param[in] PchSpiBase           PCH SPI PCI Base Address

  @retval                         Previous BIOS Control Register value

**/
UINT8
SaveAndDisableSpiPrefetchCache (
  IN  UINTN  PchSpiBase
  )
{
  UINT8  BiosCtlSave;

  BiosCtlSave = MmioRead8 (PchSpiBase + R_SPI_BCR) & B_SPI_BCR_SRC;

  MmioAndThenOr32 (
    PchSpiBase + R_SPI_BCR, \
    (UINT32)(~B_SPI_BCR_SRC), \
    (UINT32)(V_SPI_BCR_SRC_PREF_DIS_CACHE_DIS <<  B_SPI_BCR_SRC)
    );

  return BiosCtlSave;
}

/**
  This function updates BIOS Control Register with the given value.

  @param[in] PchSpiBase           PCH SPI PCI Base Address
  @param[in] BiosCtlValue         BIOS Control Register Value to be updated

**/
VOID
SetSpiBiosControlRegister (
  IN  UINTN  PchSpiBase,
  IN  UINT8  BiosCtlValue
  )
{
  MmioAndThenOr8 (PchSpiBase + R_SPI_BCR, (UINT8) ~B_SPI_BCR_SRC, BiosCtlValue);
}