/** @file
  SPI library for abstraction of SPI HW registers accesses

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

#include <Base.h>
#include <Uefi/UefiBaseType.h>
#include <IndustryStandard/Pci22.h>
#include <Library/BaseLib.h>
#include <Library/IoLib.h>
#include <Library/DebugLib.h>
#include <Library/PciSegmentLib.h>
#include <Library/SpiAccessLib.h>
#include <Library/PchPciBdfLib.h>
#include <Library/PchPcrLib.h>
#include <Register/SpiRegs.h>
#include <Register/FlashRegs.h>
#include <Register/PchRegs.h>
#include <Register/PchPcrRegs.h>
#include <Register/PchDmiRegs.h>

/**
  Checks if PCH SPI Controler is present and available

  @retval TRUE    PCH SPI controller is avaialable
  @retval FALSE   PCH SPI controller is not available
**/
BOOLEAN
SpiIsControllerAvailable (
  VOID
  )
{
  //
  // Checks for SPI controller
  //
  return (PciSegmentRead16 (SpiPciCfgBase () + PCI_VENDOR_ID_OFFSET) != 0xFFFF);
}

/**
  Returns PCH SPI BAR0 value

  @retval  UINT32  PCH SPI BAR0 value
**/
UINT32
SpiGetBar0 (
  VOID
  )
{
  UINT32  SpiBar0;

  ASSERT (SpiIsControllerAvailable ());
  SpiBar0 = PciSegmentRead32 (SpiPciCfgBase () + R_SPI_CFG_BAR0) & ~B_SPI_CFG_BAR0_MASK;
  ASSERT (SpiBar0 != 0);

  return SpiBar0;
}

/**
  Reads given descriptor section and returns value

  @param[in] UINT16   Descriptor section
  @param[in] UINT16   Offset

  @retval UINT32      Read value from a section under given offset
**/
STATIC
UINT32
SpiReadDescriptor (
  IN  UINT16  DescriptorSection,
  IN  UINT16  Offset
  )
{
  UINT32  SpiBar0;
  SpiBar0 = SpiGetBar0 ();

  MmioWrite32 (SpiBar0 + R_SPI_MEM_FDOC, (DescriptorSection | Offset));
  return MmioRead32 (SpiBar0 + R_SPI_MEM_FDOD);
}

/**
  Returns descriptor signature

  @retval UINT32    Descriptor signature
**/
UINT32
SpiGetDescriptorSignature (
  VOID
  )
{
  //
  // Read Descriptor offset 0x10 - To get Descriptor Signature
  // Signature section 0x0000 + offset 0x0 which points to Descriptor offset 0x10
  //
  return SpiReadDescriptor (V_SPI_MEM_FDOC_FDSS_FSDM, 0x0);
}

/**
  Returns supported features and R/W frequencies of Flash Component

  @retval UINT32    Flash Component features descriptor
**/
UINT32
SpiGetFlashComponentDescription (
  VOID
  )
{
  //
  // Read Descriptor offset 0x30 - To get supported features and R/W frequencies
  // Component section 0x1000 + offset 0x0 which points to Descriptor offset 0x30
  //
  return SpiReadDescriptor (V_SPI_MEM_FDOC_FDSS_COMP, 0x0);
}

/**
  Returns number of Flash Components

  @retval UINT32    Flash components number
**/
UINT32
SpiGetFlashComponentsNumber (
  VOID
  )
{
  //
  // Read Descriptor offset 0x14 - To get number of components
  // Signature section 0x0000 + offset 0x4 which points to Descriptor offset 0x14
  //
  return ((SpiReadDescriptor (V_SPI_MEM_FDOC_FDSS_FSDM, R_FLASH_FDBAR_FLASH_MAP0) & B_FLASH_FDBAR_NC) >> N_FLASH_FDBAR_NC);
}

/**
  Returns total Flash size with regards to number of flash components

  @retval UINT32    Total Flash Memory size
**/
UINT32
SpiGetTotalFlashSize (
  VOID
  )
{
  UINT32  Data32;
  UINT32  ComponentsNumber;
  UINT32  TotalFlashSize;

  Data32 = SpiGetFlashComponentDescription ();
  ComponentsNumber = SpiGetFlashComponentsNumber ();

  TotalFlashSize = (V_FLASH_FLCOMP_COMP_512KB << ((UINT8) (Data32 & B_FLASH_FLCOMP_COMP0_MASK)));
  if (ComponentsNumber == 1) {
    TotalFlashSize += (V_FLASH_FLCOMP_COMP_512KB << ((UINT8) ((Data32 & B_FLASH_FLCOMP_COMP1_MASK) >> 4)));
  }

  return TotalFlashSize;
}

/**
  Checks BIOS lock bits for proper value and checks if write protection is enabled
  Expected vales are: LE bit set, EISS bit set and WPD bit cleared

  @retval  TRUE    All protection bits are set correctly
  @retval  FALSE   Not all protection bits had exepcted values
**/
BOOLEAN
SpiIsWriteProtectionEnabled (
  VOID
  )
{
  UINT32  BiosControl;
  BiosControl = PciSegmentRead32 (SpiPciCfgBase () + R_SPI_CFG_BC);

  DEBUG ((DEBUG_INFO, "SPI BIOS CONTROL LE: %x\n", (BiosControl & B_SPI_CFG_BC_LE) != 0 ));
  DEBUG ((DEBUG_INFO, "SPI BIOS CONTROL WPD: %x\n", (BiosControl & B_SPI_CFG_BC_WPD) != 0 ));
  DEBUG ((DEBUG_INFO, "SPI BIOS CONTROL EISS: %x\n", (BiosControl & B_SPI_CFG_BC_EISS) != 0 ));

  return (((BiosControl & B_SPI_CFG_BC_LE) != 0) &&
          ((BiosControl & B_SPI_CFG_BC_WPD) == 0) &&
          ((BiosControl & B_SPI_CFG_BC_EISS) != 0));
}

/**
  Returns status of BIOS Interface Lockdown

  @retval TRUE  BIOS Interface Lockdown is enabled
  @retval FALSE BIOS Interface Lockdown is disabled
**/
BOOLEAN
SpiIsBiosInterfaceLockdownEnabled (
  VOID
  )
{
  return !!(PciSegmentRead32 (SpiPciCfgBase () + R_SPI_CFG_BC) & B_SPI_CFG_BC_BILD);
}

/**
  Returns Flash Descriptor Override Pin Strap status

  @retval TRUE     Flash Descriptor override is enabled
  @retval FALSE    Flash Descriptor override is disabled
**/
BOOLEAN
SpiIsFlashDescriptorOverrideEnabled (
  VOID
  )
{
  UINT32  SpiBar0;
  SpiBar0 = SpiGetBar0 ();

  return !!(MmioRead16 (SpiBar0 + R_SPI_MEM_HSFSC) & B_SPI_MEM_HSFSC_FDOPSS);
}

/**
  Returns Flash Configuration Lock Down bit status

  @retval TRUE    Flash Configuration Lock Down bit is set
  @retval FALSE   Flash Configuration Lock Down bit is not set
**/
BOOLEAN
SpiIsFlashConfigurationLockDownEnabled (
  VOID
  )
{
  UINT32  SpiBar0;
  SpiBar0 = SpiGetBar0 ();

  return !!(MmioRead16 (SpiBar0 + R_SPI_MEM_HSFSC) & B_SPI_MEM_HSFSC_FLOCKDN);
}

/**
  Returns Top Swap functionality enable state

  @retval TRUE    Top Swap is enabled
  @retval FALSE   Top Swap is disabled
**/
BOOLEAN
SpiIsTopSwapEnabled (
  VOID
  )
{
  return !!(PciSegmentRead32 (SpiPciCfgBase () + R_SPI_CFG_BC) & B_SPI_CFG_BC_TSS);
}

/**
  Return Component Property Parameter Table for a given component number

  @param[in]  ComponentNumber   SPI Component number
  @param[out] CppTable          Component Poperty Parameter Table value

  @retval TRUE  Vendor Specific Component Capabilities Register value was read
  @reval  FALSE Vendor Specific Component Capabilities Register value was not present
**/
BOOLEAN
SpiGetComponentPropertyParameterTable (
  IN  UINT8  ComponentNumber,
  OUT UINT32 *CppTable
  )
{
  UINT32 SpiBar0;
  UINT32 Data32;
  SpiBar0 = SpiGetBar0 ();

  //
  // More than 2 components not supported
  //
  switch (ComponentNumber) {
    case 0:
      *CppTable = MmioRead32 (SpiBar0 + R_SPI_MEM_SFDP0_VSCC0);
      return TRUE;
    case 1:
      Data32    = SpiReadDescriptor (V_SPI_MEM_FDOC_FDSS_FSDM, R_FLASH_FDBAR_FLASH_MAP0);
      *CppTable = MmioRead32 (SpiBar0 + R_SPI_MEM_SFDP1_VSCC1);
      return !!(Data32 & BIT8);
    default:
      return FALSE;
  }
}

/**
  Returns valid bit status in given Component Property Parameter Table

  @param[in] CppTable          Component Poperty Parameter Table value

  @retval TRUE    Valid bit is set
  @reval  FALSE   Valid bit is not set
**/
BOOLEAN
SpiIsCppValidBitSet (
  IN UINT32  CppTable
  )
{
  return !!(CppTable & B_SPI_MEM_SFDPX_VSCCX_CPPTV);
}

/**
  Checks if Flash Descriptor is valid

  @retval TRUE  Flash Descriptor is valid
  @retval FALSE Flash Descriptor is invalid
**/
BOOLEAN
SpiIsFlashDescriptorValid (
  VOID
  )
{
  UINT32 SpiBar0;
  SpiBar0 = SpiGetBar0 ();

  return !!(MmioRead32 (SpiBar0 + R_SPI_MEM_HSFSC) & B_SPI_MEM_HSFSC_FDV);
}

/**
  Reads and returns value from Flash Region Access Permissions Register (FRAP)

  @retval UINT32  Flash Region Access Permissions Register value
**/
STATIC
UINT32
SpiGetFlashRegionAccessPermissions (
  VOID
  )
{
  return MmioRead32 (SpiGetBar0 () + R_SPI_MEM_FRAP);
}

/**
  Returns masked BIOS Master Read Access

  @retval UINT32    Already masked BIOS Master Read Access
**/
UINT32
SpiGetMasterReadAccess (
  VOID
  )
{
  UINT32 Data32;
  Data32 = SpiGetFlashRegionAccessPermissions () & B_SPI_MEM_FRAP_BMRAG_MASK;
  DEBUG ((DEBUG_INFO, "BMRAG 0x%x\n", Data32));

  return Data32;
}

/**
  Returns masked BIOS Master Write Access

  @retval UINT32    Already masked BIOS Master Write Access
**/
UINT32
SpiGetMasterWriteAccess (
  VOID
  )
{
  UINT32 Data32;
  Data32 = SpiGetFlashRegionAccessPermissions () & B_SPI_MEM_FRAP_BMWAG_MASK;
  DEBUG ((DEBUG_INFO, "BMWAG 0x%x\n", Data32));

  return Data32;
}

/**
  Returns GbE Region Access rights

  @retval UINT32    GbE Region access rights
**/
UINT32
SpiGetGbeRegionAccess (
  VOID
  )
{
  UINT32 Data32;

  Data32 = SpiReadDescriptor (V_SPI_MEM_FDOC_FDSS_MSTR, 0x8);
  DEBUG ((DEBUG_INFO, "GbE Region Access 0x%x\n", Data32));

  return Data32;
}

/**
  Returns CSME region access rights

  @retval UINT32    CSME Region Access rights
**/
UINT32
SpiGetCsmeRegionAccess (
  VOID
  )
{
  UINT32 Data32;

  Data32 = SpiReadDescriptor (V_SPI_MEM_FDOC_FDSS_MSTR, 0x4);
  DEBUG ((DEBUG_INFO, "CSME Region Access 0x%x\n", Data32));

  return Data32;
}

/**
  Returns EC region access right

  @retval UINT32     EC Region access rights
**/
UINT32
SpiGetEcRegionAccess (
  VOID
  )
{
  UINT32 Data32;

  Data32 = SpiReadDescriptor (V_SPI_MEM_FDOC_FDSS_MSTR, 0x10);
  DEBUG ((DEBUG_INFO, "EC Region Access 0x%x\n", Data32));

  return Data32;
}

/**
  Checks if Slave Attached Flash (SAF) mode is active

  @retval TRUE    SAF mode is active
  @retval FALSE   SAF mode is not active
**/
BOOLEAN
SpiIsSafModeActive (
  VOID
  )
{
  UINT32 SpiBar0;
  SpiBar0 = SpiGetBar0 ();

  return !!(MmioRead32 (SpiBar0 + R_SPI_MEM_HSFSC) & B_SPI_MEM_HSFSC_SAF_MODE_ACTIVE);
}

/**
  Checks validity of GbE region

  @retval TRUE    GbE region is valid
  @retval FALSE   GbE regios in invalid
**/
BOOLEAN
SpiIsGbeRegionValid (
  VOID
  )
{
  UINT32  SpiBar0;
  SpiBar0 = SpiGetBar0 ();

  if (MmioRead32 (SpiBar0 + R_SPI_MEM_FREG3_GBE) != B_SPI_MEM_FREGX_BASE_MASK) {
    return TRUE;
  }
  return FALSE;
}

/**
  Returns TRUE if BIOS Boot Strap is set to SPI

  @retval TRUE    BIOS Boot strap is set to SPI
  @retval FALSE   BIOS Boot strap is set to LPC/eSPI
**/
BOOLEAN
SpiIsBiosBootFromSpi (
  VOID
  )
{
  return !!(((PciSegmentRead8 (SpiPciCfgBase () + R_SPI_CFG_BC) & B_SPI_CFG_BC_BBS) >> N_SPI_CFG_BC_BBS) == V_SPI_CFG_BC_BBS_SPI);
}

/**
  Check SPI write status disable is set

  @retval TRUE    Write status disable is set
  @retval FALSE   Write status disable is not set
**/
BOOLEAN
SpiIsWriteStatusDisable (
  VOID
  )
{
  return !!(MmioRead32 (SpiGetBar0 () + R_SPI_MEM_HSFSC) & B_SPI_MEM_HSFSC_WRSDIS);
}
