summaryrefslogtreecommitdiff
path: root/ReferenceCode/Chipset/LynxPoint/Spi/Common/SpiCommon.c
diff options
context:
space:
mode:
Diffstat (limited to 'ReferenceCode/Chipset/LynxPoint/Spi/Common/SpiCommon.c')
-rw-r--r--ReferenceCode/Chipset/LynxPoint/Spi/Common/SpiCommon.c1425
1 files changed, 1425 insertions, 0 deletions
diff --git a/ReferenceCode/Chipset/LynxPoint/Spi/Common/SpiCommon.c b/ReferenceCode/Chipset/LynxPoint/Spi/Common/SpiCommon.c
new file mode 100644
index 0000000..2797bd9
--- /dev/null
+++ b/ReferenceCode/Chipset/LynxPoint/Spi/Common/SpiCommon.c
@@ -0,0 +1,1425 @@
+/** @file
+ PCH SPI Common Driver implements the SPI Host Controller Compatibility Interface.
+
+@copyright
+ Copyright (c) 2008 - 2012 Intel Corporation. All rights reserved
+ This software and associated documentation (if any) is furnished
+ under a license and may only be used or copied in accordance
+ with the terms of the license. Except as permitted by such
+ license, no part of this software or documentation may be
+ reproduced, stored in a retrieval system, or transmitted in any
+ form or by any means without the express written consent of
+ Intel Corporation.
+
+ This file contains an 'Intel Peripheral Driver' and uniquely
+ identified as "Intel Reference Module" and is
+ licensed for Intel CPUs and chipsets under the terms of your
+ license agreement with Intel or your vendor. This file may
+ be modified by the user, subject to additional terms of the
+ license agreement
+
+**/
+#include "PchSpi.h"
+
+/**
+ Initialize an SPI protocol instance.
+ The function will assert in debug if PCH RCBA has not been initialized
+
+ @param[in] SpiInstance Pointer to SpiInstance to initialize
+
+ @retval EFI_SUCCESS The protocol instance was properly initialized
+ @exception EFI_UNSUPPORTED The PCH is not supported by this module
+**/
+EFI_STATUS
+SpiProtocolConstructor (
+ SPI_INSTANCE *SpiInstance
+ )
+{
+ ///
+ /// Check if the current PCH is known and supported by this code
+ ///
+ if (!IsPchSupported ()) {
+ DEBUG ((EFI_D_ERROR, "PCH SPI Protocol not supported due to no proper PCH LPC found!\n"));
+ return EFI_UNSUPPORTED;
+ }
+ ///
+ /// Initialize the SPI protocol instance
+ ///
+ SpiInstance->Signature = PCH_SPI_PRIVATE_DATA_SIGNATURE;
+ SpiInstance->Handle = NULL;
+ SpiInstance->SpiProtocol.ReadId = SpiProtocolReadId;
+ SpiInstance->SpiProtocol.Init = SpiProtocolInit;
+ SpiInstance->SpiProtocol.Execute = SpiProtocolExecute;
+
+ ///
+ /// Sanity check to ensure PCH RCBA initialization has occurred previously.
+ ///
+ SpiInstance->PchRootComplexBar = PCH_RCRB_BASE;
+ ASSERT (SpiInstance->PchRootComplexBar != 0);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ JEDEC Read IDs from SPI flash part, this function will return 1-byte Vendor ID and 2-byte Device ID
+
+ @param[in] This Pointer to the EFI_SPI_PROTOCOL instance.
+ @param[in] Address This value is for determines the command is sent to SPI Component 1 or 2
+ @param[in, out] Buffer Pointer to caller-allocated buffer containing the dada received or sent during the SPI cycle.
+
+ @retval EFI_SUCCESS Read Jedec Id completed.
+ @retval EFI_DEVICE_ERROR Device error, operation failed.
+ @exception EFI_UNSUPPORTED This function is unsupport after SpiProtocolInit is called
+**/
+EFI_STATUS
+EFIAPI
+SpiProtocolReadId (
+ IN EFI_SPI_PROTOCOL *This,
+ IN UINTN Address,
+ IN OUT UINT8 *Buffer
+ )
+{
+ EFI_STATUS Status;
+ SPI_INSTANCE *SpiInstance;
+ UINTN PchRootComplexBar;
+ UINT16 OpcodeType;
+ UINT8 Code;
+
+ SpiInstance = SPI_INSTANCE_FROM_SPIPROTOCOL (This);
+ if (SpiInstance->SpiIdTable[0].VendorId != 0) {
+ DEBUG ((EFI_D_ERROR, "This function is unsupport after SpiProtocolInit is called, please use SpiProtocolExecute to get Jedec ID!\n"));
+ return EFI_UNSUPPORTED;
+ }
+
+ PchRootComplexBar = SpiInstance->PchRootComplexBar;
+ SpiInstance->SpiInitTable.SpiCmdConfig[0].Operation = EnumSpiOperationJedecId;
+ SpiInstance->SpiInitTable.SpiCmdConfig[0].Frequency = EnumSpiCycle50MHz;
+ OpcodeType = (UINT16) (V_PCH_SPI_OPTYPE_RDNOADDR);
+ Code = PCH_SPI_COMMAND_READ_ID;
+
+ ///
+ /// Set Opcode Menu Configuration registers.
+ /// Need to be done before sending any SPI command
+ ///
+ MmioWrite8 (PchRootComplexBar + R_PCH_SPI_OPMENU, Code);
+ MmioRead8 (PchRootComplexBar + R_PCH_SPI_OPMENU);
+ ///
+ /// Set Opcode Type Configuration registers.
+ ///
+ MmioWrite16 (PchRootComplexBar + R_PCH_SPI_OPTYPE, OpcodeType);
+ MmioRead16 (PchRootComplexBar + R_PCH_SPI_OPTYPE);
+
+ Status = SpiProtocolExecute (
+ This,
+ 0,
+ 0,
+ TRUE,
+ TRUE,
+ FALSE,
+ Address,
+ 3,
+ Buffer,
+ EnumSpiRegionAll
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Get Descriptor values from the Descriptor Region.
+
+ @param[in] This A pointer to "EFI_SPI_PROTOCOL" for issuing commands
+
+ @retval None
+**/
+VOID
+EFIAPI
+GetDescriptorValues (
+ IN EFI_SPI_PROTOCOL *This
+ )
+{
+ SPI_INSTANCE *SpiInstance;
+ UINTN PchRootComplexBar;
+
+ SpiInstance = SPI_INSTANCE_FROM_SPIPROTOCOL (This);
+ PchRootComplexBar = SpiInstance->PchRootComplexBar;
+
+ ///
+ /// Select to Flash Map 0 Register to get the number of flash Component
+ ///
+ MmioAndThenOr32 (
+ PchRootComplexBar + R_PCH_SPI_FDOC,
+ (UINT32) (~(B_PCH_SPI_FDOC_FDSS_MASK | B_PCH_SPI_FDOC_FDSI_MASK)),
+ (UINT32) (V_PCH_SPI_FDOC_FDSS_FSDM | R_PCH_SPI_FDBAR_FLASH_MAP0)
+ );
+
+ ///
+ /// Copy Zero based Number Of Components
+ ///
+ SpiInstance->SpiDescriptor.NumberComponents = (UINT8) ((MmioRead16 (PchRootComplexBar + R_PCH_SPI_FDOD) & B_PCH_SPI_FDBAR_NC) >> N_PCH_SPI_FDBAR_NC);
+
+ ///
+ /// Select to Flash Components Register to get the Component 1 Density
+ ///
+ MmioAndThenOr32 (
+ PchRootComplexBar + R_PCH_SPI_FDOC,
+ (UINT32) (~(B_PCH_SPI_FDOC_FDSS_MASK | B_PCH_SPI_FDOC_FDSI_MASK)),
+ (UINT32) (V_PCH_SPI_FDOC_FDSS_COMP | R_PCH_SPI_FCBA_FLCOMP)
+ );
+
+ ///
+ /// Copy Component 1 Density
+ ///
+ SpiInstance->SpiDescriptor.Comp1Density = (UINT8) MmioRead32 (PchRootComplexBar + R_PCH_SPI_FDOD) & B_PCH_SPI_FLCOMP_COMP1_MASK;
+}
+
+/**
+ Get VSCC values from the Descriptor Region (VSCC Table).
+
+ @param[in] This A pointer to "EFI_SPI_PROTOCOL" for issuing commands
+ @param[in] ReadDataCmdOpcodeIndex The index of the opcode - "PCH_SPI_COMMAND_READ_DATA"
+ @param[in, out] Vscc0Value VSCC0 (Vendor Specific Component Capabilities) Value
+ @param[in, out] Vscc1Value VSCC1 (Vendor Specific Component Capabilities) Value
+
+ @retval EFI_SUCCESS Found the VSCC values on Descriptor Region
+ @retval EFI_NOT_FOUND Couldn't find the VSCC values on Descriptor Region
+ @exception EFI_UNSUPPORTED ReadDataCmdOpcodeIndex is out of range
+**/
+EFI_STATUS
+EFIAPI
+GetDescriptorVsccValues (
+ IN EFI_SPI_PROTOCOL *This,
+ IN UINT8 ReadDataCmdOpcodeIndex,
+ IN OUT UINT32 *Vscc0Value,
+ IN OUT UINT32 *Vscc1Value
+ )
+{
+ UINT32 SpiDescFlashUpperMap1;
+ UINT32 VsccTableBaseAddr;
+ UINT32 VsccTableLength;
+ UINT32 JedecIdRegIndex;
+ EFI_STATUS Status;
+ UINT32 FlashDescriptor;
+ SPI_INSTANCE *SpiInstance;
+ BOOLEAN MatchedVtbEntryFound;
+ UINT8 SpiIndex;
+ UINT32 Data32;
+ Data32 = 0;
+
+ if (ReadDataCmdOpcodeIndex >= SPI_NUM_OPCODE) {
+ return EFI_UNSUPPORTED;
+ }
+
+ SpiInstance = SPI_INSTANCE_FROM_SPIPROTOCOL (This);
+
+ Status = SpiProtocolExecute (
+ This,
+ ReadDataCmdOpcodeIndex,
+ 0,
+ TRUE,
+ TRUE,
+ FALSE,
+ (UINTN) R_PCH_SPI_FLASH_UMAP1,
+ sizeof (SpiDescFlashUpperMap1),
+ (UINT8 *) &SpiDescFlashUpperMap1,
+ EnumSpiRegionDescriptor
+ );
+ if ((EFI_ERROR (Status)) || (SpiDescFlashUpperMap1 == 0xFFFFFFFF)) {
+ return EFI_NOT_FOUND;
+ }
+ ///
+ /// B_PCH_SPI_FLASH_UMAP1_VTBA represents address bits [11:4]
+ ///
+ VsccTableBaseAddr = ((SpiDescFlashUpperMap1 & B_PCH_SPI_FLASH_UMAP1_VTBA) << 4);
+ ///
+ /// Multiplied by 4? B_PCH_SPI_FDBAR_VTL is the 1-based number of DWORDs.
+ ///
+ VsccTableLength = (((SpiDescFlashUpperMap1 & B_PCH_SPI_FLASH_UMAP1_VTL) >> 8) << 2);
+ if (VsccTableLength < SIZE_OF_SPI_VTBA_ENTRY) {
+ ///
+ /// Non-existent or invalid Vscc Table
+ ///
+ return EFI_NOT_FOUND;
+ }
+
+ for (SpiIndex = 0; SpiIndex <= SpiInstance->SpiDescriptor.NumberComponents; SpiIndex++) {
+ JedecIdRegIndex = 0;
+ MatchedVtbEntryFound = FALSE;
+ while (JedecIdRegIndex <= (VsccTableLength - SIZE_OF_SPI_VTBA_ENTRY)) {
+ Status = SpiProtocolExecute (
+ This,
+ ReadDataCmdOpcodeIndex,
+ 0,
+ TRUE,
+ TRUE,
+ FALSE,
+ (UINTN) (VsccTableBaseAddr + JedecIdRegIndex),
+ sizeof (UINT32),
+ (UINT8 *) &FlashDescriptor,
+ EnumSpiRegionDescriptor
+ );
+
+ if ((EFI_ERROR (Status)) || (FlashDescriptor == 0xFFFFFFFF)) {
+ break;
+ }
+
+ if (((FlashDescriptor & B_PCH_SPI_VTBA_JID0_VID) != SpiInstance->SpiIdTable[SpiIndex].VendorId) ||
+ (((FlashDescriptor & B_PCH_SPI_VTBA_JID0_DID0) >> N_PCH_SPI_VTBA_JID0_DID0)
+ != SpiInstance->SpiIdTable[SpiIndex].DeviceId0) ||
+ (((FlashDescriptor & B_PCH_SPI_VTBA_JID0_DID1) >> N_PCH_SPI_VTBA_JID0_DID1)
+ != SpiInstance->SpiIdTable[SpiIndex].DeviceId1)) {
+ JedecIdRegIndex += SIZE_OF_SPI_VTBA_ENTRY;
+ } else {
+ MatchedVtbEntryFound = TRUE;
+ break;
+ }
+ }
+
+ if (!MatchedVtbEntryFound) {
+ return EFI_NOT_FOUND;
+ }
+
+ Status = SpiProtocolExecute (
+ This,
+ ReadDataCmdOpcodeIndex,
+ 0,
+ TRUE,
+ TRUE,
+ FALSE,
+ (UINTN) (VsccTableBaseAddr + JedecIdRegIndex + R_PCH_SPI_VTBA_VSCC0),
+ sizeof (UINT32),
+ (UINT8 *) &Data32,
+ EnumSpiRegionDescriptor
+ );
+ if ((EFI_ERROR (Status)) || (Data32 == 0xFFFFFFFF)) {
+ return EFI_NOT_FOUND;
+ }
+ ///
+ /// Copy correct VSCCn value
+ ///
+ if (SpiIndex == 0) {
+ *Vscc0Value = Data32;
+ } else {
+ *Vscc1Value = Data32;
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Discover if the flash parts supports 4KB Block/Sector erase size.
+
+ @param[in] This A pointer to "EFI_SPI_PROTOCOL" for issuing commands
+
+ @retval EFI_SUCCESS The flash part supports 4KB erase size.
+ @exception EFI_UNSUPPORTED The flash part does not support 4KB erase size.
+**/
+EFI_STATUS
+EFIAPI
+SpiDiscoveryParameters (
+ IN EFI_SPI_PROTOCOL *This
+ )
+{
+ UINT16 ParameterTableIndex;
+ UINT32 Data32;
+ SPI_INSTANCE *SpiInstance;
+ UINT8 SpiIndex;
+ UINTN PchRootComplexBar;
+
+ SpiInstance = SPI_INSTANCE_FROM_SPIPROTOCOL (This);
+ PchRootComplexBar = SpiInstance->PchRootComplexBar;
+
+ ///
+ /// Check if valid SFDP table is present for SPI0
+ ///
+ Data32 = MmioRead32 ((UINTN) (PchRootComplexBar + R_PCH_SPI_VSCC0)) & B_PCH_SPI_VSCC0_CPPTV;
+ if (Data32 == 0) {
+ return EFI_UNSUPPORTED;
+ }
+
+ SpiInstance = SPI_INSTANCE_FROM_SPIPROTOCOL (This);
+
+ ///
+ /// Check if valid SFDP table is present for SPI1
+ ///
+ if (SpiInstance->SpiDescriptor.NumberComponents) {
+ Data32 = MmioRead32 ((UINTN) (PchRootComplexBar + R_PCH_SPI_VSCC1)) & B_PCH_SPI_VSCC1_CPPTV;
+ if (Data32 == 0) {
+ ///
+ /// Program VSCCn values from Flash Descriptor or internal BIOS table
+ /// if both parts do not support SFDP
+ ///
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ for (SpiIndex = 0; SpiIndex <= SpiInstance->SpiDescriptor.NumberComponents; SpiIndex++) {
+ ///
+ /// Read Block/Sector Erase Size in 1st dword in the Flash Parameter Table.
+ ///
+ ParameterTableIndex = (SpiIndex << N_PCH_SPI_PINTX_SPT) | (V_PCH_SPI_PINTX_HORD_DATA << N_PCH_SPI_PINTX_HORD);
+ MmioWrite32 (PchRootComplexBar + R_PCH_SPI_PINTX, ParameterTableIndex);
+ Data32 = MmioRead32 (PchRootComplexBar + R_PCH_SPI_PTDATA) & B_PCH_SPI_VSCC0_BSES_MASK;
+
+ ///
+ /// Program VSCCn.EO from Flash Descriptor or internal BIOS table
+ /// if erase size other than 4KB.
+ ///
+ if (Data32 != V_PCH_SPI_VSCC0_BSES_4K) {
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Initialize the host controller to execute SPI command.
+
+ @param[in] This Pointer to the EFI_SPI_PROTOCOL instance.
+ @param[in] InitData Initialization data to be programmed into the SPI host controller.
+
+ @retval EFI_SUCCESS Initialization completed.
+ @retval EFI_ACCESS_DENIED The SPI static configuration interface has been locked-down.
+ @retval EFI_INVALID_PARAMETER Bad input parameters.
+ @exception EFI_UNSUPPORTED Can't get Descriptor mode VSCC values
+**/
+EFI_STATUS
+EFIAPI
+SpiProtocolInit (
+ IN EFI_SPI_PROTOCOL *This,
+ IN SPI_INIT_DATA *InitData
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Index;
+ UINT16 OpcodeType;
+ SPI_INSTANCE *SpiInstance;
+ SPI_SPECIAL_OPCODE_ENTRY *SpecialOpcodeEntry;
+ UINT32 Vscc0Value;
+ UINT32 Vscc1Value;
+ UINTN PchRootComplexBar;
+ UINT8 UnlockCmdOpcodeIndex;
+ UINT8 ReadDataCmdOpcodeIndex;
+ UINT8 JedecIdCmdOpcodeIndex;
+ UINT8 Code;
+ UINT8 FlashPartId[3];
+ UINT32 Data32;
+
+ SpiInstance = SPI_INSTANCE_FROM_SPIPROTOCOL (This);
+ PchRootComplexBar = SpiInstance->PchRootComplexBar;
+ Vscc0Value = 0;
+ Vscc1Value = 0;
+
+ if (InitData == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ ///
+ /// Check if the SPI interface has been locked-down.
+ ///
+ if ((MmioRead16 (PchRootComplexBar + R_PCH_SPI_HSFS) & B_PCH_SPI_HSFS_FLOCKDN) != 0) {
+ ASSERT_EFI_ERROR (EFI_ACCESS_DENIED);
+ return EFI_ACCESS_DENIED;
+ }
+ ///
+ /// Copy Flash Descriptor Values into SPI driver Private data structure
+ ///
+ SpiInstance->DescriptorMode = PchIsSpiDescriptorMode (PchRootComplexBar);
+ if (SpiInstance->DescriptorMode == TRUE) {
+ GetDescriptorValues (This);
+ }
+ ///
+ /// Clear all the status bits for hardware regs.
+ ///
+ MmioOr16 (
+ (UINTN) (PchRootComplexBar + R_PCH_SPI_HSFS),
+ (UINT16) ((B_PCH_SPI_HSFS_AEL | B_PCH_SPI_HSFS_FCERR | B_PCH_SPI_HSFS_FDONE))
+ );
+ MmioRead16 (PchRootComplexBar + R_PCH_SPI_HSFS);
+
+ ///
+ /// Clear all the status bits for software regs.
+ ///
+ MmioOr8 (
+ (UINTN) (PchRootComplexBar + R_PCH_SPI_SSFS),
+ (UINT8) ((B_PCH_SPI_SSFS_FCERR | B_PCH_SPI_SSFS_CDS))
+ );
+ MmioRead8 (PchRootComplexBar + R_PCH_SPI_SSFS);
+
+ ReadDataCmdOpcodeIndex = SPI_NUM_OPCODE;
+ UnlockCmdOpcodeIndex = SPI_NUM_OPCODE;
+ JedecIdCmdOpcodeIndex = SPI_NUM_OPCODE;
+ ///
+ /// Set Opcode Type Configuration registers.
+ /// Need to be done before sending any SPI command
+ ///
+ for (Index = 0, OpcodeType = 0; Index < SPI_NUM_OPCODE; Index++) {
+ ///
+ /// Copy Operation and Frequency into SPI driver Private data structure
+ ///
+ SpiInstance->SpiInitTable.SpiCmdConfig[Index].Operation = InitData->SpiCmdConfig[Index].Operation;
+ SpiInstance->SpiInitTable.SpiCmdConfig[Index].Frequency = InitData->SpiCmdConfig[Index].Frequency;
+
+ switch (SpiInstance->SpiInitTable.SpiCmdConfig[Index].Operation) {
+ case EnumSpiOperationReadData:
+ OpcodeType |= (UINT16) (V_PCH_SPI_OPTYPE_RDADDR << (Index * 2));
+ Code = PCH_SPI_COMMAND_READ_DATA;
+ ReadDataCmdOpcodeIndex = Index;
+ break;
+
+ case EnumSpiOperationFastRead:
+ OpcodeType |= (UINT16) (V_PCH_SPI_OPTYPE_RDADDR << (Index * 2));
+ Code = PCH_SPI_COMMAND_FAST_READ;
+ ReadDataCmdOpcodeIndex = Index;
+ break;
+
+ case EnumSpiOperationDualOutputFastRead:
+ OpcodeType |= (UINT16) (V_PCH_SPI_OPTYPE_RDADDR << (Index * 2));
+ Code = PCH_SPI_COMMAND_DUAL_FAST_READ;
+ ReadDataCmdOpcodeIndex = Index;
+ break;
+
+ case EnumSpiOperationDiscoveryParameters:
+ OpcodeType |= (UINT16) (V_PCH_SPI_OPTYPE_RDADDR << (Index * 2));
+ Code = PCH_SPI_COMMAND_DISCOVERY_PARAMETERS;
+ break;
+
+ case EnumSpiOperationProgramData_1_Byte:
+ case EnumSpiOperationProgramData_64_Byte:
+ OpcodeType |= (UINT16) (V_PCH_SPI_OPTYPE_WRADDR << (Index * 2));
+ Code = PCH_SPI_COMMAND_PROGRAM_BYTE;
+ break;
+
+ case EnumSpiOperationErase_256_Byte:
+ OpcodeType |= (UINT16) (V_PCH_SPI_OPTYPE_WRADDR << (Index * 2));
+ Code = PCH_SPI_COMMAND_256B_ERASE;
+ break;
+
+ case EnumSpiOperationErase_4K_Byte:
+ OpcodeType |= (UINT16) (V_PCH_SPI_OPTYPE_WRADDR << (Index * 2));
+ Code = PCH_SPI_COMMAND_4KB_ERASE;
+ break;
+
+ case EnumSpiOperationErase_64K_Byte:
+ OpcodeType |= (UINT16) (V_PCH_SPI_OPTYPE_WRADDR << (Index * 2));
+ Code = PCH_SPI_COMMAND_64KB_ERASE;
+ break;
+
+ case EnumSpiOperationWriteStatus:
+ OpcodeType |= (UINT16) (V_PCH_SPI_OPTYPE_WRNOADDR << (Index * 2));
+ Code = PCH_SPI_COMMAND_WRITE_STATUS;
+ UnlockCmdOpcodeIndex = Index;
+ break;
+
+ case EnumSpiOperationWriteDisable:
+ OpcodeType |= (UINT16) (V_PCH_SPI_OPTYPE_WRNOADDR << (Index * 2));
+ Code = PCH_SPI_COMMAND_WRITE_DISABLE;
+ break;
+
+ case EnumSpiOperationWriteEnable:
+ OpcodeType |= (UINT16) (V_PCH_SPI_OPTYPE_WRNOADDR << (Index * 2));
+ Code = PCH_SPI_COMMAND_WRITE_ENABLE;
+ break;
+
+ case EnumSpiOperationEnableWriteStatus:
+ OpcodeType |= (UINT16) (V_PCH_SPI_OPTYPE_WRNOADDR << (Index * 2));
+ Code = PCH_SPI_COMMAND_ENABLE_WRITE_STATUS;
+ break;
+
+ case EnumSpiOperationFullChipErase:
+ OpcodeType |= (UINT16) (V_PCH_SPI_OPTYPE_WRNOADDR << (Index * 2));
+ Code = PCH_SPI_COMMAND_FULL_CHIP_ERASE;
+ break;
+
+ case EnumSpiOperationReadStatus:
+ OpcodeType |= (UINT16) (V_PCH_SPI_OPTYPE_RDNOADDR << (Index * 2));
+ Code = PCH_SPI_COMMAND_READ_STATUS;
+ break;
+
+ case EnumSpiOperationJedecId:
+ OpcodeType |= (UINT16) (V_PCH_SPI_OPTYPE_RDNOADDR << (Index * 2));
+ Code = PCH_SPI_COMMAND_READ_ID;
+ JedecIdCmdOpcodeIndex = Index;
+ break;
+
+ case EnumSpiOperationErase_8K_Byte:
+ case EnumSpiOperationOther:
+ Code = 0;
+ break;
+
+ default:
+ Code = 0;
+ ASSERT (FALSE);
+ break;
+ }
+ ///
+ /// Overrided Opcode Type and Menu Configuration registers per SpecialOpcodeEntry
+ ///
+ if (InitData->SpecialOpcodeEntry != NULL) {
+ SpecialOpcodeEntry = InitData->SpecialOpcodeEntry;
+
+ while (SpecialOpcodeEntry->OpcodeIndex != 0xFF) {
+ if (SpecialOpcodeEntry->OpcodeIndex == Index) {
+ OpcodeType &= (UINT16)~(B_PCH_SPI_OPTYPE0_MASK << (Index * 2));
+ OpcodeType |= (UINT16) (SpecialOpcodeEntry->Type << (Index * 2));
+ Code = SpecialOpcodeEntry->Code;
+ }
+
+ SpecialOpcodeEntry++;
+ }
+ }
+ ///
+ /// Set Opcode Menu Configuration registers.
+ /// Need to be done before sending any SPI command
+ ///
+ MmioWrite8 (
+ PchRootComplexBar + R_PCH_SPI_OPMENU + Index,
+ Code
+ );
+ MmioRead8 (PchRootComplexBar + R_PCH_SPI_OPMENU + Index);
+ }
+ ///
+ /// Set Opcode Type Configuration registers.
+ ///
+ MmioWrite16 (PchRootComplexBar + R_PCH_SPI_OPTYPE, OpcodeType);
+ MmioRead16 (PchRootComplexBar + R_PCH_SPI_OPTYPE);
+
+ if (JedecIdCmdOpcodeIndex >= SPI_NUM_OPCODE) {
+ return EFI_UNSUPPORTED;
+ } else {
+ ///
+ /// Read VendorId/DeviceId from SPI Component 1
+ ///
+ Status = SpiProtocolExecute (
+ This,
+ JedecIdCmdOpcodeIndex,
+ 0,
+ TRUE,
+ TRUE,
+ FALSE,
+ (UINTN) 0,
+ 3,
+ FlashPartId,
+ EnumSpiRegionDescriptor
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ ///
+ /// Copy VendorId, DeviceId0, DeviceId1 and BiosStartOffset into SPI
+ /// driver Private data structure
+ ///
+ SpiInstance->SpiIdTable[0].VendorId = FlashPartId[0];
+ SpiInstance->SpiIdTable[0].DeviceId0 = FlashPartId[1];
+ SpiInstance->SpiIdTable[0].DeviceId1 = FlashPartId[2];
+ SpiInstance->SpiInitTable.BiosStartOffset = InitData->BiosStartOffset;
+ if (SpiInstance->SpiDescriptor.NumberComponents == 0x01) {
+ ///
+ /// If SPI Descriptor indicates two SPI components then
+ /// read VendorId/DeviceId from SPI Component 2.
+ /// Calculate SPI Component 2 address. The secondary SPI's address is equal to the first SPI's size
+ /// Note: 512KB (BIT19) is the minimum Componenty Density.
+ ///
+ Data32 = (UINT32) (UINTN) (V_PCH_SPI_FLCOMP_COMP_512KB << SpiInstance->SpiDescriptor.Comp1Density);
+ Status = SpiProtocolExecute (
+ This,
+ JedecIdCmdOpcodeIndex,
+ 0,
+ TRUE,
+ TRUE,
+ FALSE,
+ (UINTN) Data32,
+ 3,
+ FlashPartId,
+ EnumSpiRegionAll
+ );
+ if (!EFI_ERROR (Status)) {
+ ///
+ /// Copy VendorId, DeviceId0, DeviceId1 into SPI
+ /// driver Private data structure
+ ///
+ SpiInstance->SpiIdTable[1].VendorId = FlashPartId[0];
+ SpiInstance->SpiIdTable[1].DeviceId0 = FlashPartId[1];
+ SpiInstance->SpiIdTable[1].DeviceId1 = FlashPartId[2];
+ }
+ }
+ ///
+ /// Set the Prefix Opcode registers.
+ ///
+ MmioWrite16 (
+ PchRootComplexBar + R_PCH_SPI_PREOP,
+ (InitData->PrefixOpcode[1] << 8) | InitData->PrefixOpcode[0]
+ );
+ MmioRead16 (PchRootComplexBar + R_PCH_SPI_PREOP);
+
+ ///
+ /// Copy PrefixOpcode into SPI driver Private data structure
+ ///
+ for (Index = 0; Index < SPI_NUM_PREFIX_OPCODE; Index++) {
+ SpiInstance->SpiInitTable.PrefixOpcode[Index] = InitData->PrefixOpcode[Index];
+ }
+ ///
+ /// Copy BiosSize into SPI driver Private data structure
+ ///
+ SpiInstance->SpiInitTable.BiosSize = InitData->BiosSize;
+ }
+ ///
+ /// Get VSCC values from VTBA table in the Descriptor.
+ ///
+ if (SpiInstance->DescriptorMode == TRUE) {
+ Status = GetDescriptorVsccValues (
+ This,
+ ReadDataCmdOpcodeIndex,
+ &Vscc0Value,
+ &Vscc1Value
+ );
+
+ if (EFI_ERROR (Status)) {
+ ///
+ /// Program the VSCC0 & VSCC1 registers by getting the data from SpiInitTable
+ ///
+ for (Index = 0; Index < SPI_NUM_OPCODE; Index++) {
+ ///
+ /// For every platform that supports ME, only 4 KB erase is supported
+ /// Get the opcode from SpiInitTable if the operation is 4 KB erase
+ ///
+ if (SpiInstance->SpiInitTable.SpiCmdConfig[Index].Operation == EnumSpiOperationErase_4K_Byte) {
+ Vscc0Value = Vscc0Value | (UINT32) (V_PCH_SPI_VSCC0_BSES_4K);
+ Vscc0Value = Vscc0Value | (UINT32) (PCH_SPI_COMMAND_4KB_ERASE << 8);
+ } else if (SpiInstance->SpiInitTable.SpiCmdConfig[Index].Operation == EnumSpiOperationProgramData_64_Byte) {
+ Vscc0Value = Vscc0Value | (UINT32) (B_PCH_SPI_VSCC0_WG_64B);
+ }
+ }
+ ///
+ /// Bit WSR and WEWS should NOT be both set to 1, so we check if there is any "Write enable on Write status" prefix opcode
+ /// from SpiInitTable at first, then check "Write Status Enable" prefix opcode
+ ///
+ if ((SpiInstance->SpiInitTable.PrefixOpcode[0] == PCH_SPI_COMMAND_WRITE_ENABLE) ||
+ (SpiInstance->SpiInitTable.PrefixOpcode[1] == PCH_SPI_COMMAND_WRITE_ENABLE)) {
+ Vscc0Value = Vscc0Value | (UINT32) (B_PCH_SPI_VSCC0_WEWS);
+ } else if ((SpiInstance->SpiInitTable.PrefixOpcode[0] == PCH_SPI_COMMAND_WRITE_STATUS_EN) ||
+ (SpiInstance->SpiInitTable.PrefixOpcode[1] == PCH_SPI_COMMAND_WRITE_STATUS_EN)) {
+ Vscc0Value = Vscc0Value | (UINT32) (B_PCH_SPI_VSCC0_WSR);
+ }
+
+ Vscc1Value = Vscc0Value;
+ }
+ ///
+ /// The VCL locks itself when set, it will assert because we have no way to update VSCC value
+ ///
+ if ((MmioRead32 ((UINTN) (PchRootComplexBar + R_PCH_SPI_VSCC0)) & B_PCH_SPI_VSCC0_VCL) != 0) {
+ ASSERT_EFI_ERROR (EFI_ACCESS_DENIED);
+ return EFI_ACCESS_DENIED;
+ }
+
+ ASSERT (Vscc0Value != 0);
+ MmioWrite32 ((UINTN) (PchRootComplexBar + R_PCH_SPI_VSCC0), Vscc0Value);
+ if (SpiInstance->SpiDescriptor.NumberComponents) {
+ MmioWrite32 ((UINTN) (PchRootComplexBar + R_PCH_SPI_VSCC1), Vscc1Value);
+ }
+ }
+
+ SpiPhaseInit ();
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Execute SPI commands from the host controller.
+ This function would be called by runtime driver, please do not use any MMIO marco here
+
+ @param[in] This Pointer to the EFI_SPI_PROTOCOL instance.
+ @param[in] OpcodeIndex Index of the command in the OpCode Menu.
+ @param[in] PrefixOpcodeIndex Index of the first command to run when in an atomic cycle sequence.
+ @param[in] DataCycle TRUE if the SPI cycle contains data
+ @param[in] Atomic TRUE if the SPI cycle is atomic and interleave cycles are not allowed.
+ @param[in] ShiftOut If DataByteCount is not zero, TRUE to shift data out and FALSE to shift data in.
+ @param[in] Address In Descriptor Mode, for Descriptor Region, GbE Region, ME Region and Platform
+ Region, this value specifies the offset from the Region Base; for BIOS Region,
+ this value specifies the offset from the start of the BIOS Image. In Non
+ Descriptor Mode, this value specifies the offset from the start of the BIOS Image.
+ Please note BIOS Image size may be smaller than BIOS Region size (in Descriptor
+ Mode) or the flash size (in Non Descriptor Mode), and in this case, BIOS Image is
+ supposed to be placed at the top end of the BIOS Region (in Descriptor Mode) or
+ the flash (in Non Descriptor Mode)
+ @param[in] DataByteCount Number of bytes in the data portion of the SPI cycle. This function may break the
+ data transfer into multiple operations. This function ensures each operation does
+ not cross 256 byte flash address boundary.
+ *NOTE: if there is some SPI chip that has a stricter address boundary requirement
+ (e.g., its write page size is < 256 byte), then the caller cannot rely on this
+ function to cut the data transfer at proper address boundaries, and it's the
+ caller's reponsibility to pass in a properly cut DataByteCount parameter.
+ @param[in, out] Buffer Pointer to caller-allocated buffer containing the dada received or sent during the
+ SPI cycle.
+ @param[in] SpiRegionType SPI Region type. Values EnumSpiRegionBios, EnumSpiRegionGbE, EnumSpiRegionMe,
+ EnumSpiRegionDescriptor, and EnumSpiRegionPlatformData are only applicable in
+ Descriptor mode. Value EnumSpiRegionAll is applicable to both Descriptor Mode
+ and Non Descriptor Mode, which indicates "SpiRegionOffset" is actually relative
+ to base of the 1st flash device (i.e., it is a Flash Linear Address).
+
+ @retval EFI_SUCCESS Command succeed.
+ @retval EFI_INVALID_PARAMETER The parameters specified are not valid.
+ @exception EFI_UNSUPPORTED Command not supported.
+ @retval EFI_DEVICE_ERROR Device error, command aborts abnormally.
+**/
+EFI_STATUS
+EFIAPI
+SpiProtocolExecute (
+ IN EFI_SPI_PROTOCOL *This,
+ IN UINT8 OpcodeIndex,
+ IN UINT8 PrefixOpcodeIndex,
+ IN BOOLEAN DataCycle,
+ IN BOOLEAN Atomic,
+ IN BOOLEAN ShiftOut,
+ IN UINTN Address,
+ IN UINT32 DataByteCount,
+ IN OUT UINT8 *Buffer,
+ IN SPI_REGION_TYPE SpiRegionType
+ )
+{
+ EFI_STATUS Status;
+ UINT8 BiosCtlSave;
+ UINT32 Data32;
+ UINT32 PmBase;
+ UINT32 SmiEnSave;
+
+ BiosCtlSave = 0;
+ SmiEnSave = 0;
+
+ ///
+ /// Check if the parameters are valid.
+ ///
+ if ((OpcodeIndex >= SPI_NUM_OPCODE) || (PrefixOpcodeIndex >= SPI_NUM_PREFIX_OPCODE)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ ///
+ /// Make sure it's safe to program the command.
+ /// Poll both Hardware Sequencing and Software Sequencing Status
+ ///
+ if (!WaitForSpiCycleComplete (This, TRUE, FALSE)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (!WaitForSpiCycleComplete (This, FALSE, FALSE)) {
+ return EFI_DEVICE_ERROR;
+ }
+ ///
+ /// Acquire access to the SPI interface is not required any more.
+ ///
+ ///
+ /// Disable SMIs to make sure normal mode flash access is not interrupted by an SMI
+ /// whose SMI handler accesses flash (e.g. for error logging)
+ ///
+ /// *** NOTE: if the SMI_LOCK bit is set (i.e., B0:D31:F0:Offset A0h [4]='1'),
+ /// clearing B_GBL_SMI_EN will not have effect. In this situation, some other
+ /// synchronization methods must be applied either here or in the consumer of the
+ /// EFI_SPI_PROTOCOl.Execute(). An example method is disabling the specific SMI sources
+ /// whose SMI handlers access flash before calling Execute() and re-enabling the SMI
+ /// sources after the call.
+ ///
+ PmBase = PciRead32 (
+ PCI_LIB_ADDRESS (DEFAULT_PCI_BUS_NUMBER_PCH,
+ PCI_DEVICE_NUMBER_PCH_LPC,
+ PCI_FUNCTION_NUMBER_PCH_LPC,
+ R_PCH_LPC_ACPI_BASE)
+ ) & B_PCH_LPC_ACPI_BASE_BAR;
+
+ SmiEnSave = IoRead32 ((UINTN) (PmBase + R_PCH_SMI_EN));
+ Data32 = SmiEnSave &~B_PCH_SMI_EN_GBL_SMI;
+ IoWrite32 ((UINTN) (PmBase + R_PCH_SMI_EN), Data32);
+ ///
+ /// If shifts the data out, disable Prefetching and Caching.
+ ///
+ if (ShiftOut) {
+ BiosCtlSave = PciRead8 (
+ PCI_LIB_ADDRESS (DEFAULT_PCI_BUS_NUMBER_PCH,
+ PCI_DEVICE_NUMBER_PCH_LPC,
+ PCI_FUNCTION_NUMBER_PCH_LPC,
+ R_PCH_LPC_BIOS_CNTL)
+ ) & V_PCH_LPC_BIOS_CNTL_SRC;
+ PciAndThenOr8 (
+ PCI_LIB_ADDRESS (DEFAULT_PCI_BUS_NUMBER_PCH,
+ PCI_DEVICE_NUMBER_PCH_LPC,
+ PCI_FUNCTION_NUMBER_PCH_LPC,
+ R_PCH_LPC_BIOS_CNTL),
+ (UINT8) (~V_PCH_LPC_BIOS_CNTL_SRC),
+ (UINT8) ((V_PCH_SRC_PREF_DIS_CACHE_DIS))
+ );
+ }
+ ///
+ /// Sends the command to the SPI interface to execute.
+ ///
+ Status = SendSpiCmd (
+ This,
+ OpcodeIndex,
+ PrefixOpcodeIndex,
+ DataCycle,
+ Atomic,
+ ShiftOut,
+ Address,
+ DataByteCount,
+ Buffer,
+ SpiRegionType
+ );
+ ///
+ /// Restore the settings for SPI Prefetching and Caching.
+ ///
+ if (ShiftOut) {
+ PciAndThenOr8 (
+ PCI_LIB_ADDRESS (DEFAULT_PCI_BUS_NUMBER_PCH,
+ PCI_DEVICE_NUMBER_PCH_LPC,
+ PCI_FUNCTION_NUMBER_PCH_LPC,
+ R_PCH_LPC_BIOS_CNTL),
+ (UINT8) (~V_PCH_LPC_BIOS_CNTL_SRC),
+ (UINT8) (BiosCtlSave)
+ );
+ }
+ ///
+ /// Restore SMIs.
+ ///
+ IoWrite32 ((UINTN) (PmBase + R_PCH_SMI_EN), SmiEnSave);
+
+ return Status;
+}
+
+/**
+ Convert SPI offset to Physical address of SPI hardware
+
+ @param[in] This Pointer to the EFI_SPI_PROTOCOL instance.
+ @param[in] SpiRegionOffset In Descriptor Mode, for Descriptor Region, GbE Region, ME Region and Platform
+ Region, this value specifies the offset from the Region Base; for BIOS Region,
+ this value specifies the offset from the start of the BIOS Image. In Non
+ Descriptor Mode, this value specifies the offset from the start of the BIOS Image.
+ Please note BIOS Image size may be smaller than BIOS Region size (in Descriptor
+ Mode) or the flash size (in Non Descriptor Mode), and in this case, BIOS Image is
+ supposed to be placed at the top end of the BIOS Region (in Descriptor Mode) or
+ the flash (in Non Descriptor Mode)
+ @param[in] BaseAddress Base Address of the region.
+ @param[in] SpiRegionType SPI Region type. Values EnumSpiRegionBios, EnumSpiRegionGbE, EnumSpiRegionMe,
+ EnumSpiRegionDescriptor, and EnumSpiRegionPlatformData are only applicable in
+ Descriptor mode. Value EnumSpiRegionAll is applicable to both Descriptor Mode
+ and Non Descriptor Mode, which indicates "SpiRegionOffset" is actually relative
+ to base of the 1st flash device (i.e., it is a Flash Linear Address).
+ @param[in, out] HardwareSpiAddress Return absolution SPI address (i.e., Flash Linear Address)
+ @param[in, out] BaseAddress Return base address of the region type
+ @param[in, out] LimitAddress Return limit address of the region type
+
+ @retval EFI_SUCCESS Command succeed.
+**/
+VOID
+SpiOffset2Physical (
+ IN EFI_SPI_PROTOCOL *This,
+ IN UINTN SpiRegionOffset,
+ IN SPI_REGION_TYPE SpiRegionType,
+ OUT UINTN *HardwareSpiAddress,
+ OUT UINTN *BaseAddress,
+ OUT UINTN *LimitAddress
+ )
+{
+ SPI_INSTANCE *SpiInstance;
+ UINTN PchRootComplexBar;
+
+ SpiInstance = SPI_INSTANCE_FROM_SPIPROTOCOL (This);
+ PchRootComplexBar = SpiInstance->PchRootComplexBar;
+
+ if (SpiInstance->DescriptorMode == TRUE) {
+ switch (SpiRegionType) {
+
+ case EnumSpiRegionBios:
+ *LimitAddress = (((MmioRead32 (PchRootComplexBar + R_PCH_SPI_FREG1_BIOS)
+ & B_PCH_SPI_FREG1_LIMIT_MASK) >> 16) + 1) << 12;
+ *BaseAddress = (MmioRead32 (PchRootComplexBar + R_PCH_SPI_FREG1_BIOS)
+ & B_PCH_SPI_FREG1_BASE_MASK) << 12;
+ break;
+
+ case EnumSpiRegionGbE:
+ *BaseAddress = (MmioRead32 (PchRootComplexBar + R_PCH_SPI_FREG3_GBE) & B_PCH_SPI_FREG3_BASE_MASK) << 12;
+ *LimitAddress = (((MmioRead32 (PchRootComplexBar + R_PCH_SPI_FREG3_GBE)
+ & B_PCH_SPI_FREG3_LIMIT_MASK) >> 16) + 1) << 12;
+ break;
+
+ case EnumSpiRegionMe:
+ *BaseAddress = (MmioRead32 (PchRootComplexBar + R_PCH_SPI_FREG2_ME) & B_PCH_SPI_FREG2_BASE_MASK) << 12;
+ *LimitAddress = (((MmioRead32 (PchRootComplexBar + R_PCH_SPI_FREG2_ME)
+ & B_PCH_SPI_FREG2_LIMIT_MASK) >> 16) + 1) << 12;
+ break;
+
+ case EnumSpiRegionDescriptor:
+ *BaseAddress = (MmioRead32 (PchRootComplexBar + R_PCH_SPI_FREG0_FLASHD) & B_PCH_SPI_FREG0_BASE_MASK) << 12;
+ *LimitAddress = (((MmioRead32 (PchRootComplexBar + R_PCH_SPI_FREG0_FLASHD)
+ & B_PCH_SPI_FREG0_LIMIT_MASK) >> 16) + 1) << 12;
+ break;
+
+ case EnumSpiRegionPlatformData:
+ *BaseAddress = (MmioRead32 (PchRootComplexBar + R_PCH_SPI_FREG4_PLATFORM_DATA) & B_PCH_SPI_FREG4_BASE_MASK) << 12;
+ *LimitAddress = (((MmioRead32 (PchRootComplexBar + R_PCH_SPI_FREG4_PLATFORM_DATA)
+ & B_PCH_SPI_FREG4_LIMIT_MASK) >> 16) + 1) << 12;
+ break;
+
+ default:
+ ///
+ /// EnumSpiRegionAll indicates address is relative to flash device (i.e., address is Flash
+ /// Linear Address)
+ ///
+ *BaseAddress = 0;
+ *LimitAddress = 0;
+ break;
+ }
+
+ *HardwareSpiAddress = SpiRegionOffset +*BaseAddress;
+ } else {
+ if (SpiRegionType == EnumSpiRegionAll) {
+ ///
+ /// EnumSpiRegionAll indicates address is relative to flash device (i.e., address is Flash
+ /// Linear Address)
+ ///
+ *HardwareSpiAddress = SpiRegionOffset;
+ } else {
+ ///
+ /// Otherwise address is relative to BIOS image
+ ///
+ *HardwareSpiAddress = SpiRegionOffset + SpiInstance->SpiInitTable.BiosStartOffset;
+ }
+ }
+}
+
+/**
+ This function sends the programmed SPI command to the slave device.
+
+ @param[in] OpcodeIndex Index of the command in the OpCode Menu.
+ @param[in] PrefixOpcodeIndex Index of the first command to run when in an atomic cycle sequence.
+ @param[in] DataCycle TRUE if the SPI cycle contains data
+ @param[in] Atomic TRUE if the SPI cycle is atomic and interleave cycles are not allowed.
+ @param[in] ShiftOut If DataByteCount is not zero, TRUE to shift data out and FALSE to shift data in.
+ @param[in] Address In Descriptor Mode, for Descriptor Region, GbE Region, ME Region and Platform
+ Region, this value specifies the offset from the Region Base; for BIOS Region,
+ this value specifies the offset from the start of the BIOS Image. In Non
+ Descriptor Mode, this value specifies the offset from the start of the BIOS Image.
+ Please note BIOS Image size may be smaller than BIOS Region size (in Descriptor
+ Mode) or the flash size (in Non Descriptor Mode), and in this case, BIOS Image is
+ supposed to be placed at the top end of the BIOS Region (in Descriptor Mode) or
+ the flash (in Non Descriptor Mode)
+ @param[in] DataByteCount Number of bytes in the data portion of the SPI cycle. This function may break the
+ data transfer into multiple operations. This function ensures each operation does
+ not cross 256 byte flash address boundary.
+ *NOTE: if there is some SPI chip that has a stricter address boundary requirement
+ (e.g., its write page size is < 256 byte), then the caller cannot rely on this
+ function to cut the data transfer at proper address boundaries, and it's the
+ caller's reponsibility to pass in a properly cut DataByteCount parameter.
+ @param[in, out] Buffer Data received or sent during the SPI cycle.
+ @param[in] SpiRegionType SPI Region type. Values EnumSpiRegionBios, EnumSpiRegionGbE, EnumSpiRegionMe,
+ EnumSpiRegionDescriptor, and EnumSpiRegionPlatformData are only applicable in
+ Descriptor mode. Value EnumSpiRegionAll is applicable to both Descriptor Mode
+ and Non Descriptor Mode, which indicates "SpiRegionOffset" is actually relative
+ to base of the 1st flash device (i.e., it is a Flash Linear Address).
+
+ @retval EFI_SUCCESS SPI command completes successfully.
+ @retval EFI_DEVICE_ERROR Device error, the command aborts abnormally.
+ @retval EFI_ACCESS_DENIED Some unrecognized command encountered in hardware sequencing mode
+ @retval EFI_INVALID_PARAMETER The parameters specified are not valid.
+**/
+EFI_STATUS
+SendSpiCmd (
+ IN EFI_SPI_PROTOCOL *This,
+ IN UINT8 OpcodeIndex,
+ IN UINT8 PrefixOpcodeIndex,
+ IN BOOLEAN DataCycle,
+ IN BOOLEAN Atomic,
+ IN BOOLEAN ShiftOut,
+ IN UINTN Address,
+ IN UINT32 DataByteCount,
+ IN OUT UINT8 *Buffer,
+ IN SPI_REGION_TYPE SpiRegionType
+ )
+{
+ UINT32 Index;
+ SPI_INSTANCE *SpiInstance;
+ UINTN HardwareSpiAddr;
+ UINTN SpiBiosSize;
+ BOOLEAN UseSoftwareSequence;
+ UINTN BaseAddress;
+ UINTN LimitAddress;
+ UINT32 SpiDataCount;
+ UINT8 OpCode;
+ UINT16 OpType;
+ SPI_OPERATION Operation;
+ UINTN PchRootComplexBar;
+ UINT32 SpiSoftFreq;
+ UINT16 FlashCycle;
+ UINT8 BiosCntl;
+ BOOLEAN BiosWriteProtect;
+
+ SpiInstance = SPI_INSTANCE_FROM_SPIPROTOCOL (This);
+ PchRootComplexBar = SpiInstance->PchRootComplexBar;
+ SpiBiosSize = SpiInstance->SpiInitTable.BiosSize;
+ Operation = SpiInstance->SpiInitTable.SpiCmdConfig[OpcodeIndex].Operation;
+ OpCode = MmioRead8 (PchRootComplexBar + R_PCH_SPI_OPMENU + OpcodeIndex);
+ OpType = (MmioRead16 (PchRootComplexBar + R_PCH_SPI_OPTYPE) >> OpcodeIndex * 2) & (UINT16) (B_PCH_SPI_OPTYPE0_MASK);
+ ///
+ /// Please use PciRead here, it will link to MmioRead
+ /// if the caller is a Runtime driver, please use PchDxeRuntimePciLibPciExpress library, refer
+ /// PciExpressRead() on Library\DxeRuntimePciLibPciExpress\DxeRuntimePciLibPciExpress.c for the details.
+ /// For the rest please use EdkIIGlueBasePciLibPciExpress library
+ ///
+ BiosCntl = PciRead8 (
+ PCI_LIB_ADDRESS (DEFAULT_PCI_BUS_NUMBER_PCH,
+ PCI_DEVICE_NUMBER_PCH_LPC,
+ PCI_FUNCTION_NUMBER_PCH_LPC,
+ R_PCH_LPC_BIOS_CNTL));
+ ///
+ /// Check if the value of opcode register is 0 or the BIOS Size of SpiInitTable is 0 while SpiRegionType is EnumSpiRegionBios.
+ ///
+ if (OpCode == 0 || (SpiBiosSize == 0 && SpiRegionType == EnumSpiRegionBios)) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+ ///
+ /// Check if need to disable BIOS Write Protect
+ ///
+ if ((Operation == EnumSpiOperationProgramData_1_Byte) ||
+ (Operation == EnumSpiOperationProgramData_64_Byte) ||
+ (Operation == EnumSpiOperationErase_256_Byte) ||
+ (Operation == EnumSpiOperationErase_4K_Byte) ||
+ (Operation == EnumSpiOperationErase_8K_Byte) ||
+ (Operation == EnumSpiOperationErase_64K_Byte) ||
+ (Operation == EnumSpiOperationFullChipErase)) {
+ DisableBiosWriteProtect ();
+ BiosWriteProtect = FALSE;
+ } else {
+ BiosWriteProtect = TRUE;
+ }
+ ///
+ /// Per PCH BIOS Spec Section 3.7 BIOS Region SMM Protection Enabling,
+ /// If SMM_BWP bit (D31:F0:RegDCh[5]) is set, the BIOS Region can only be updated
+ /// by following the steps:
+ /// - Once all threads enter SMM
+ /// - Read memory location FED30880h OR with 00000001h, place the result in EAX,
+ /// and write data to lower 32 bits of MSR 1FEh. (This is done in
+ /// DisableBiosWriteProtect())
+ ///
+ if (((BiosCntl & B_PCH_LPC_BIOS_CNTL_SMM_BWP) == B_PCH_LPC_BIOS_CNTL_SMM_BWP) &&
+ (BiosWriteProtect == FALSE) &&
+ ((MmioRead32 ((UINTN) 0xFED30880) & (UINT32) BIT0) == 0)) {
+ EnableBiosWriteProtect ();
+ return EFI_ACCESS_DENIED;
+ }
+ ///
+ /// When current code is read id OR current is not descriptor mode, we will use compatible mode
+ ///
+ UseSoftwareSequence = FALSE;
+ if ((Operation == EnumSpiOperationJedecId) ||
+ (Operation == EnumSpiOperationReadStatus) ||
+ (Operation == EnumSpiOperationWriteStatus) ||
+ (Operation == EnumSpiOperationWriteDisable) ||
+ (Operation == EnumSpiOperationWriteEnable) ||
+ (Operation == EnumSpiOperationEnableWriteStatus) ||
+ (Operation == EnumSpiOperationOther) ||
+ (Operation == EnumSpiOperationDiscoveryParameters) ||
+ (SpiInstance->DescriptorMode == FALSE)
+ ) {
+ UseSoftwareSequence = TRUE;
+ }
+
+ SpiOffset2Physical (This, Address, SpiRegionType, &HardwareSpiAddr, &BaseAddress, &LimitAddress);
+ ///
+ /// Have direct access to BIOS region in Descriptor mode,
+ ///
+ if (OpType == EnumSpiOpcodeRead && SpiRegionType == EnumSpiRegionBios) {
+ CopyMem (
+ Buffer,
+ (UINT8 *) ((HardwareSpiAddr - BaseAddress) + (UINT32) (~(SpiBiosSize - 1))),
+ DataByteCount
+ );
+ return EFI_SUCCESS;
+ }
+ ///
+ /// DEBUG((EFI_D_ERROR, "SPIADDR %x, %x, %x, %x\n", Address, HardwareSpiAddr, BaseAddress,
+ /// LimitAddress));
+ ///
+ if ((DataCycle == FALSE) && (DataByteCount > 0)) {
+ DataByteCount = 0;
+ }
+
+ do {
+ ///
+ /// Trim at 256 byte boundary per operation,
+ /// - PCH SPI controller requires trimming at 4KB boundary
+ /// - Some SPI chips require trimming at 256 byte boundary for write operation
+ /// - Trimming has limited performance impact as we can read / write atmost 64 byte
+ /// per operation
+ ///
+ if (HardwareSpiAddr + DataByteCount > ((HardwareSpiAddr + BIT8) &~(BIT8 - 1))) {
+ SpiDataCount = (((UINT32) (HardwareSpiAddr) + BIT8) &~(BIT8 - 1)) - (UINT32) (HardwareSpiAddr);
+ } else {
+ SpiDataCount = DataByteCount;
+ }
+ ///
+ /// Calculate the number of bytes to shift in/out during the SPI data cycle.
+ /// Valid settings for the number of bytes duing each data portion of the
+ /// PCH SPI cycles are: 0, 1, 2, 3, 4, 5, 6, 7, 8, 16, 24, 32, 40, 48, 56, 64
+ ///
+ if ((SpiInstance->DescriptorMode == FALSE) &&
+ (OpCode == PCH_SPI_COMMAND_PROGRAM_BYTE || Operation == EnumSpiOperationProgramData_1_Byte)) {
+ SpiDataCount = 1;
+ } else if (SpiDataCount >= 64) {
+ SpiDataCount = 64;
+ } else if ((SpiDataCount &~0x07) != 0) {
+ SpiDataCount = SpiDataCount &~0x07;
+ }
+ ///
+ /// If shifts data out, load data into the SPI data buffer.
+ ///
+ if (ShiftOut) {
+ for (Index = 0; Index < SpiDataCount; Index++) {
+ MmioWrite8 (PchRootComplexBar + R_PCH_SPI_FDATA00 + Index, Buffer[Index]);
+ MmioRead8 (PchRootComplexBar + R_PCH_SPI_FDATA00 + Index);
+ }
+ }
+
+ MmioWrite32 (
+ (PchRootComplexBar + R_PCH_SPI_FADDR),
+ (UINT32) (HardwareSpiAddr & B_PCH_SPI_FADDR_MASK)
+ );
+ MmioRead32 (PchRootComplexBar + R_PCH_SPI_FADDR);
+
+ ///
+ /// Execute the command on the SPI compatible mode
+ ///
+ if (UseSoftwareSequence) {
+ ///
+ /// Software sequencing ...
+ ///
+ ///
+ /// Clear error flags
+ ///
+ MmioWrite16 (PchRootComplexBar + R_PCH_SPI_HSFS, (UINT16) B_PCH_SPI_HSFS_AEL);
+ MmioWrite8 (PchRootComplexBar + R_PCH_SPI_SSFS, (UINT8) B_PCH_SPI_SSFS_FCERR);
+
+ switch (SpiInstance->SpiInitTable.SpiCmdConfig[OpcodeIndex].Frequency) {
+ case EnumSpiCycle20MHz:
+ SpiSoftFreq = V_PCH_SPI_SSFC_SCF_20MHZ;
+ break;
+
+ case EnumSpiCycle33MHz:
+ SpiSoftFreq = V_PCH_SPI_SSFC_SCF_33MHZ;
+ break;
+
+ case EnumSpiCycle50MHz:
+ SpiSoftFreq = V_PCH_SPI_SSFC_SCF_50MHZ;
+ break;
+
+ default:
+ ///
+ /// This is an invalid use of the protocol
+ /// See definition, but caller must call with valid value
+ ///
+ SpiSoftFreq = 0;
+ ASSERT (!EFI_UNSUPPORTED);
+ break;
+ }
+ ///
+ /// PCH Chipset EDS 1.1, Section 22.1.19
+ /// SSFC BIT23:19 are reserved, BIOS must set this field to '11111'b
+ /// To change the offset to the right DWORD boundary, so use offset 0x90 as the operation address
+ ///
+ if (DataCycle) {
+ MmioWrite32 (
+ (PchRootComplexBar + R_PCH_SPI_SSFC - 1),
+ ( (UINT32) (BIT23 | BIT22 | BIT21 | BIT20 | BIT19) |
+ (UINT32) ((SpiSoftFreq << 16) & B_PCH_SPI_SSFC_SCF_MASK) |
+ (UINT32) (B_PCH_SPI_SSFC_DS) | (UINT32) (((SpiDataCount - 1) << 8) & B_PCH_SPI_SSFC_DBC_MASK) |
+ (UINT32) ((OpcodeIndex << 4) & B_PCH_SPI_SSFC_COP) |
+ (UINT32) ((PrefixOpcodeIndex << 3) & B_PCH_SPI_SSFC_SPOP) |
+ (UINT32) (Atomic ? B_PCH_SPI_SSFC_ACS : 0) |
+ (UINT32) (B_PCH_SPI_SSFC_SCGO)) << 8);
+ } else {
+ MmioWrite32 (
+ (PchRootComplexBar + R_PCH_SPI_SSFC - 1),
+ ( (UINT32) (BIT23 | BIT22 | BIT21 | BIT20 | BIT19) |
+ (UINT32) ((SpiSoftFreq << 16) & B_PCH_SPI_SSFC_SCF_MASK) |
+ (UINT32) ((OpcodeIndex << 4) & B_PCH_SPI_SSFC_COP) |
+ (UINT32) ((PrefixOpcodeIndex << 3) & B_PCH_SPI_SSFC_SPOP) |
+ (UINT32) (Atomic ? B_PCH_SPI_SSFC_ACS : 0) |
+ (UINT32) (B_PCH_SPI_SSFC_SCGO)) << 8);
+ }
+
+ MmioRead32 (PchRootComplexBar + R_PCH_SPI_SSFC - 1);
+ } else {
+ ///
+ /// Hardware sequencing ...
+ ///
+ ///
+ /// check for PCH SPI hardware sequencing required commands
+ ///
+ if (Operation == EnumSpiOperationReadData ||
+ Operation == EnumSpiOperationFastRead ||
+ Operation == EnumSpiOperationDualOutputFastRead) {
+ ///
+ /// Read Cycle
+ ///
+ FlashCycle = (UINT16) (V_PCH_SPI_HSFC_FCYCLE_READ << 1);
+ } else if (Operation == EnumSpiOperationProgramData_1_Byte ||
+ Operation == EnumSpiOperationProgramData_64_Byte) {
+ ///
+ /// Write Cycle
+ ///
+ FlashCycle = (UINT16) (V_PCH_SPI_HSFC_FCYCLE_WRITE << 1);
+ } else if (Operation == EnumSpiOperationErase_256_Byte ||
+ Operation == EnumSpiOperationErase_4K_Byte ||
+ Operation == EnumSpiOperationErase_8K_Byte ||
+ Operation == EnumSpiOperationErase_64K_Byte ||
+ Operation == EnumSpiOperationFullChipErase) {
+ ///
+ /// Erase Cycle
+ ///
+ FlashCycle = (UINT16) (V_PCH_SPI_HSFC_FCYCLE_ERASE << 1);
+ } else {
+ ///
+ /// Unrecognized Operation
+ ///
+ ASSERT (FALSE);
+ return EFI_ACCESS_DENIED;
+ }
+ ///
+ /// Clear error flags
+ ///
+ MmioWrite16 (
+ PchRootComplexBar + R_PCH_SPI_HSFS,
+ (UINT16) (B_PCH_SPI_HSFS_AEL | B_PCH_SPI_HSFS_FCERR)
+ );
+ ///
+ /// Send the command
+ ///
+ MmioWrite16 (
+ PchRootComplexBar + R_PCH_SPI_HSFC,
+ (UINT16) (((SpiDataCount - 1) << 8) & B_PCH_SPI_HSFC_FDBC_MASK) |
+ FlashCycle | B_PCH_SPI_HSFC_FCYCLE_FGO
+ );
+ ///
+ /// Read back for posted write to take effect
+ ///
+ MmioRead16 (PchRootComplexBar + R_PCH_SPI_HSFC);
+ }
+ ///
+ /// end of command execution
+ ///
+ /// Wait the SPI cycle to complete.
+ ///
+ if (!WaitForSpiCycleComplete (This, UseSoftwareSequence, TRUE)) {
+ if (BiosWriteProtect == FALSE) {
+ ///
+ /// Enable BIOS Write Protect
+ ///
+ EnableBiosWriteProtect ();
+ }
+ return EFI_DEVICE_ERROR;
+ }
+ ///
+ /// If shifts data in, get data from the SPI data bufffer.
+ ///
+ if (!ShiftOut) {
+ for (Index = 0; Index < SpiDataCount; Index++) {
+ Buffer[Index] = MmioRead8 (PchRootComplexBar + R_PCH_SPI_FDATA00 + Index);
+ }
+ }
+
+ HardwareSpiAddr += SpiDataCount;
+ Buffer += SpiDataCount;
+ DataByteCount -= SpiDataCount;
+ } while (DataByteCount > 0);
+
+ if (BiosWriteProtect == FALSE) {
+ ///
+ /// Enable BIOS Write Protect
+ ///
+ EnableBiosWriteProtect ();
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Wait execution cycle to complete on the SPI interface. Check both Hardware
+ and Software Sequencing status registers
+
+ @param[in] This The SPI protocol instance
+ @param[in] UseSoftwareSequence TRUE if Software Sequencing
+ @param[in] ErrorCheck TRUE if the SpiCycle needs to do the error check
+
+ @retval TRUE SPI cycle completed on the interface.
+ @retval FALSE Time out while waiting the SPI cycle to complete.
+ It's not safe to program the next command on the SPI interface.
+**/
+BOOLEAN
+WaitForSpiCycleComplete (
+ IN EFI_SPI_PROTOCOL *This,
+ IN BOOLEAN UseSoftwareSequence,
+ IN BOOLEAN ErrorCheck
+ )
+{
+ UINT64 WaitTicks;
+ UINT64 WaitCount;
+ UINT32 StatusRegAddr;
+ UINT32 CycleInProgressBit;
+ UINT16 AelBit;
+ UINT16 FcErrBit;
+ UINT16 FcycleDone;
+ UINT16 Data16;
+ SPI_INSTANCE *SpiInstance;
+ UINTN PchRootComplexBar;
+
+ SpiInstance = SPI_INSTANCE_FROM_SPIPROTOCOL (This);
+ PchRootComplexBar = SpiInstance->PchRootComplexBar;
+
+ if (UseSoftwareSequence) {
+ ///
+ /// This is Software Sequencing
+ ///
+ StatusRegAddr = R_PCH_SPI_SSFS;
+ CycleInProgressBit = B_PCH_SPI_SSFS_SCIP;
+ AelBit = B_PCH_SPI_SSFS_AEL;
+ FcErrBit = B_PCH_SPI_SSFS_FCERR;
+ FcycleDone = B_PCH_SPI_SSFS_CDS;
+ } else {
+ ///
+ /// This is Hardware Sequencing
+ ///
+ StatusRegAddr = R_PCH_SPI_HSFS;
+ CycleInProgressBit = B_PCH_SPI_HSFS_SCIP;
+ AelBit = B_PCH_SPI_HSFS_AEL;
+ FcErrBit = B_PCH_SPI_HSFS_FCERR;
+ FcycleDone = B_PCH_SPI_HSFS_FDONE;
+ }
+ ///
+ /// Convert the wait period allowed into to tick count
+ ///
+ WaitCount = WAIT_TIME / WAIT_PERIOD;
+
+ ///
+ /// Wait for the SPI cycle to complete.
+ ///
+ for (WaitTicks = 0; WaitTicks < WaitCount; WaitTicks++) {
+ Data16 = MmioRead16 (PchRootComplexBar + StatusRegAddr);
+ if ((Data16 & CycleInProgressBit) == 0) {
+ if (UseSoftwareSequence){
+ MmioWrite8 (PchRootComplexBar + StatusRegAddr, (UINT8)(AelBit | FcErrBit | FcycleDone));
+ }else{
+ MmioWrite16 (PchRootComplexBar + StatusRegAddr, (AelBit | FcErrBit | FcycleDone));
+ }
+ if (((Data16 & AelBit) || (Data16 & FcErrBit)) && (ErrorCheck == TRUE)) {
+ return FALSE;
+ } else {
+ return TRUE;
+ }
+ }
+
+ PchPmTimerStall (WAIT_PERIOD);
+ }
+
+ return FALSE;
+}