diff options
Diffstat (limited to 'ReferenceCode/Chipset/LynxPoint/Spi/Common/SpiCommon.c')
-rw-r--r-- | ReferenceCode/Chipset/LynxPoint/Spi/Common/SpiCommon.c | 1425 |
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; +} |