/** @file
PCH SPI PEI Library implements the SPI Host Controller Interface.
Copyright (c) 2019, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef struct {
EFI_PEI_PPI_DESCRIPTOR PpiDescriptor;
SPI_INSTANCE SpiInstance;
} PEI_SPI_INSTANCE;
/**
Initializes the SPI BAR0 value to a default value and enables memory space decoding.
The SPI BAR0 will be assigned later in PCI enumeration.
**/
VOID
InitSpiBar0 (
VOID
)
{
UINT64 PchSpiBase;
PchSpiBase = PCI_SEGMENT_LIB_ADDRESS (
0,
0,
PCI_DEVICE_NUMBER_PCH_SPI,
PCI_FUNCTION_NUMBER_PCH_SPI,
0
);
PciSegmentWrite32 (PchSpiBase + R_PCH_SPI_BAR0, PCH_SPI_BASE_ADDRESS);
PciSegmentOr32 (PchSpiBase + PCI_COMMAND_OFFSET, EFI_PCI_COMMAND_MEMORY_SPACE);
}
/**
Initializes SPI for access from future services.
@retval EFI_SUCCESS The SPI service was initialized successfully.
@retval EFI_OUT_OF_RESOUCES Insufficient memory available to allocate structures required for initialization.
@retval Others An error occurred initializing SPI services.
**/
EFI_STATUS
EFIAPI
SpiServiceInit (
VOID
)
{
EFI_STATUS Status;
PEI_SPI_INSTANCE *PeiSpiInstance;
SPI_INSTANCE *SpiInstance;
PCH_SPI_PPI *SpiPpi;
UINT16 AcpiBase;
AcpiBase = 0;
Status = PeiServicesLocatePpi (
&gPchSpiPpiGuid,
0,
NULL,
(VOID **) &SpiPpi
);
if (Status != EFI_SUCCESS) {
//
// Initialize the ACPI base address so the ACPI PM timer can be used to wait for
// SPI cycle completion
//
PchAcpiBaseGet (&AcpiBase);
if (AcpiBase == 0) {
PchAcpiBaseSet (PcdGet16 (PcdAcpiBaseAddress));
}
//
// Prior to PCI enumeration, initialize SPI BAR0 to a default value
// and also enable memory space decoding for SPI
//
InitSpiBar0 ();
PeiSpiInstance = (PEI_SPI_INSTANCE *) AllocateZeroPool (sizeof (PEI_SPI_INSTANCE));
if (NULL == PeiSpiInstance) {
return EFI_OUT_OF_RESOURCES;
}
SpiInstance = &(PeiSpiInstance->SpiInstance);
SpiProtocolConstructor (SpiInstance);
PeiSpiInstance->PpiDescriptor.Flags = EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST;
PeiSpiInstance->PpiDescriptor.Guid = &gPchSpiPpiGuid;
PeiSpiInstance->PpiDescriptor.Ppi = &(SpiInstance->SpiProtocol);
Status = PeiServicesInstallPpi (&PeiSpiInstance->PpiDescriptor);
}
return Status;
}
/**
Acquires the PCH SPI BAR0 MMIO address.
@param[in] SpiInstance Pointer to SpiInstance to initialize
@retval UINTN The SPIO BAR0 MMIO address
**/
UINTN
AcquireSpiBar0 (
IN SPI_INSTANCE *SpiInstance
)
{
return MmioRead32 (SpiInstance->PchSpiBase + R_PCH_SPI_BAR0) & ~(B_PCH_SPI_BAR0_MASK);
}
/**
Release the PCH SPI BAR0 MMIO address.
@param[in] SpiInstance Pointer to SpiInstance to initialize
@retval None
**/
VOID
ReleaseSpiBar0 (
IN SPI_INSTANCE *SpiInstance
)
{
return;
}
/**
Disables BIOS Write Protect
@retval EFI_SUCCESS BIOS Write Protect was disabled successfully
**/
EFI_STATUS
EFIAPI
DisableBiosWriteProtect (
VOID
)
{
UINT64 SpiBaseAddress;
SpiBaseAddress = PCI_SEGMENT_LIB_ADDRESS (
0,
0,
PCI_DEVICE_NUMBER_PCH_SPI,
PCI_FUNCTION_NUMBER_PCH_SPI,
0
);
//
// Clear EISS bit to allow for SPI use
//
PciSegmentAnd8 (SpiBaseAddress + R_PCH_SPI_BC, (UINT8) ~B_PCH_SPI_BC_EISS);
//
// Write clear BC_SYNC_SS prior to change WPD from 0 to 1.
//
PciSegmentOr8 (
SpiBaseAddress + R_PCH_SPI_BC + 1,
(B_PCH_SPI_BC_SYNC_SS >> 8)
);
//
// Set BIOSWE bit (SPI PCI Offset DCh [0]) = 1b
// Enable the access to the BIOS space for both read and write cycles
//
PciSegmentOr8 (
SpiBaseAddress + R_PCH_SPI_BC,
B_PCH_SPI_BC_WPD
);
ASSERT ((PciSegmentRead8 (SpiBaseAddress + R_PCH_SPI_BC) & B_PCH_SPI_BC_EISS) == 0);
return EFI_SUCCESS;
}
/**
Enables BIOS Write Protect
**/
VOID
EFIAPI
EnableBiosWriteProtect (
VOID
)
{
UINT64 SpiBaseAddress;
SpiBaseAddress = PCI_SEGMENT_LIB_ADDRESS (
0,
0,
PCI_DEVICE_NUMBER_PCH_SPI,
PCI_FUNCTION_NUMBER_PCH_SPI,
0
);
//
// Disable the access to the BIOS space for write cycles
//
PciSegmentAnd8 (
SpiBaseAddress + R_PCH_SPI_BC,
(UINT8) (~B_PCH_SPI_BC_WPD)
);
}