diff options
author | abnchang <abnchang@amd.com> | 2023-06-18 05:44:35 +0800 |
---|---|---|
committer | mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> | 2024-04-20 08:26:40 +0000 |
commit | 8b02ecc5f04cac5eb1b7c4a79a800e97bcf205fa (patch) | |
tree | 418cdbd11704aa6d98b43fc32b8792adb1c4ea5c /MdeModulePkg/Bus | |
parent | 6dc09fda0484a4404cca2432a64ec8368d2f9424 (diff) | |
download | edk2-8b02ecc5f04cac5eb1b7c4a79a800e97bcf205fa.tar.gz edk2-8b02ecc5f04cac5eb1b7c4a79a800e97bcf205fa.tar.bz2 edk2-8b02ecc5f04cac5eb1b7c4a79a800e97bcf205fa.zip |
MdeModulePkg/SpiNorFlashJedecSfdp: SPI NOR Flash JEDEC SFDP
BZ#: 4471
SPI NOR Flash JEDEC Serial Flash Discoverable Driver
implementation.
Signed-off-by: Abner Chang <abner.chang@amd.com>
Cc: Hao A Wu <hao.a.wu@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Abdul Lateef Attar <abdattar@amd.com>
Cc: Brit Chesley <brit.chesley@amd.com>
Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn>
Diffstat (limited to 'MdeModulePkg/Bus')
11 files changed, 4166 insertions, 0 deletions
diff --git a/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlash.c b/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlash.c new file mode 100644 index 0000000000..3ac5420fbf --- /dev/null +++ b/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlash.c @@ -0,0 +1,1141 @@ +/** @file
+ SPI NOR Flash operation functions.
+
+ Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/TimerLib.h>
+#include <Protocol/SpiConfiguration.h>
+#include <Protocol/SpiIo.h>
+#include <IndustryStandard/SpiNorFlashJedecSfdp.h>
+#include "SpiNorFlash.h"
+
+/**
+ Fill Write Buffer with Opcode, Address, Dummy Bytes, and Data.
+
+ @param[in] Instance The instance of SPI_NOR_FLASH
+ @param[in] Opcode Opcode for transaction
+ @param[in] DummyBytes The dummy bytes send to SPI flash device
+ @param[in] AddressBytesSupported Bytes of address supported by SPI flash device
+ @param[in] UseAddress Send the address for SPI flash command
+ @param[in] Address SPI Offset Start Address
+ @param[in] WriteBytes Number of bytes to write to SPI device
+ @param[in] WriteBuffer Buffer containing bytes to write to SPI device
+
+ @retval Size of Data in Buffer
+**/
+UINT32
+FillWriteBuffer (
+ IN SPI_NOR_FLASH_INSTANCE *Instance,
+ IN UINT8 Opcode,
+ IN UINT32 DummyBytes,
+ IN UINT8 AddressBytesSupported,
+ IN BOOLEAN UseAddress,
+ IN UINT32 Address,
+ IN UINT32 WriteBytes,
+ IN UINT8 *WriteBuffer
+ )
+{
+ UINT32 AddressSize;
+ UINT32 BigEndianAddress;
+ UINT32 Index;
+ UINT8 SfdpAddressBytes;
+
+ SfdpAddressBytes = (UINT8)Instance->SfdpBasicFlash->AddressBytes;
+
+ // Copy Opcode into Write Buffer
+ Instance->SpiTransactionWriteBuffer[0] = Opcode;
+ Index = 1;
+ if (UseAddress) {
+ if (AddressBytesSupported == SPI_ADDR_3BYTE_ONLY) {
+ if (SfdpAddressBytes != 0) {
+ // Check if the supported address length is already initiated.
+ if ((SfdpAddressBytes != SPI_ADDR_3BYTE_ONLY) && (SfdpAddressBytes != SPI_ADDR_3OR4BYTE)) {
+ DEBUG ((DEBUG_ERROR, "%a: Unsupported Address Bytes: 0x%x, SFDP is: 0x%x\n", __func__, AddressBytesSupported, SfdpAddressBytes));
+ ASSERT (FALSE);
+ }
+ }
+
+ AddressSize = 3;
+ } else if (AddressBytesSupported == SPI_ADDR_4BYTE_ONLY) {
+ if (SfdpAddressBytes != 0) {
+ // Check if the supported address length is already initiated.
+ if ((SfdpAddressBytes != SPI_ADDR_4BYTE_ONLY) && (SfdpAddressBytes != SPI_ADDR_3OR4BYTE)) {
+ DEBUG ((DEBUG_ERROR, "%a: Unsupported Address Bytes: 0x%x, SFDP is: 0x%x\n", __func__, AddressBytesSupported, SfdpAddressBytes));
+ ASSERT (FALSE);
+ }
+ }
+
+ AddressSize = 4;
+ } else if (AddressBytesSupported == SPI_ADDR_3OR4BYTE) {
+ if (SfdpAddressBytes != 0) {
+ // Check if the supported address length is already initiated.
+ if (SfdpAddressBytes != SPI_ADDR_3OR4BYTE) {
+ DEBUG ((DEBUG_ERROR, "%a: Unsupported Address Bytes: 0x%x, SFDP is: 0x%x\n", __func__, AddressBytesSupported, SfdpAddressBytes));
+ ASSERT (FALSE);
+ }
+ }
+
+ if (Instance->Protocol.FlashSize <= SIZE_16MB) {
+ AddressSize = 3;
+ } else {
+ // SPI part is > 16MB use 4-byte addressing.
+ AddressSize = 4;
+ }
+ } else {
+ DEBUG ((DEBUG_ERROR, "%a: Invalid Address Bytes\n", __func__));
+ ASSERT (FALSE);
+ }
+
+ BigEndianAddress = SwapBytes32 ((UINT32)Address);
+ BigEndianAddress >>= ((sizeof (UINT32) - AddressSize) * 8);
+ CopyMem (
+ &Instance->SpiTransactionWriteBuffer[Index],
+ &BigEndianAddress,
+ AddressSize
+ );
+ Index += AddressSize;
+ }
+
+ if (SfdpAddressBytes == SPI_ADDR_3OR4BYTE) {
+ //
+ // TODO:
+ // We may need to enter/exit 4-Byte mode if SPI flash
+ // device is currently operated in 3-Bytes mode.
+ //
+ }
+
+ // Fill DummyBytes
+ if (DummyBytes != 0) {
+ SetMem (
+ &Instance->SpiTransactionWriteBuffer[Index],
+ DummyBytes,
+ 0
+ );
+ Index += DummyBytes;
+ }
+
+ // Fill Data
+ if (WriteBytes > 0) {
+ CopyMem (
+ &Instance->SpiTransactionWriteBuffer[Index],
+ WriteBuffer,
+ WriteBytes
+ );
+ Index += WriteBytes;
+ }
+
+ return Index;
+}
+
+/**
+ Internal Read the flash status register.
+
+ This routine reads the flash part status register.
+
+ @param[in] Instance SPI_NOR_FLASH_INSTANCE
+ structure.
+ @param[in] LengthInBytes Number of status bytes to read.
+ @param[out] FlashStatus Pointer to a buffer to receive the flash status.
+
+ @retval EFI_SUCCESS The status register was read successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+InternalReadStatus (
+ IN SPI_NOR_FLASH_INSTANCE *Instance,
+ IN UINT32 LengthInBytes,
+ OUT UINT8 *FlashStatus
+ )
+{
+ EFI_STATUS Status;
+ UINT32 TransactionBufferLength;
+
+ // Read Status register
+ TransactionBufferLength = FillWriteBuffer (
+ Instance,
+ SPI_FLASH_RDSR,
+ SPI_FLASH_RDSR_DUMMY,
+ SPI_FLASH_RDSR_ADDR_BYTES,
+ FALSE,
+ 0,
+ 0,
+ NULL
+ );
+ Status = Instance->SpiIo->Transaction (
+ Instance->SpiIo,
+ SPI_TRANSACTION_WRITE_THEN_READ,
+ FALSE,
+ 0,
+ 1,
+ 8,
+ TransactionBufferLength,
+ Instance->SpiTransactionWriteBuffer,
+ 1,
+ FlashStatus
+ );
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
+
+/**
+ Set Write Enable Latch.
+
+ @param[in] Instance SPI NOR instance with all protocols, etc.
+
+ @retval EFI_SUCCESS SPI Write Enable succeeded
+ @retval EFI_DEVICE_ERROR SPI Flash part did not respond properly
+**/
+EFI_STATUS
+SetWel (
+ IN SPI_NOR_FLASH_INSTANCE *Instance
+ )
+{
+ EFI_STATUS Status;
+ UINT32 TransactionBufferLength;
+
+ TransactionBufferLength = FillWriteBuffer (
+ Instance,
+ Instance->WriteEnableLatchCommand,
+ SPI_FLASH_WREN_DUMMY,
+ SPI_FLASH_WREN_ADDR_BYTES,
+ FALSE,
+ 0,
+ 0,
+ NULL
+ );
+ Status = Instance->SpiIo->Transaction (
+ Instance->SpiIo,
+ SPI_TRANSACTION_WRITE_ONLY,
+ FALSE,
+ 0,
+ 1,
+ 8,
+ TransactionBufferLength,
+ Instance->SpiTransactionWriteBuffer,
+ 0,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Set WEL fail.\n", __func__));
+ ASSERT (FALSE);
+ }
+
+ return Status;
+}
+
+/**
+ Check for not device write in progress.
+
+ @param[in] SpiNorFlashInstance SPI NOR instance with all protocols, etc.
+ @param[in] Timeout Timeout in microsecond
+ @param[in] RetryCount The retry count
+
+ @retval EFI_SUCCESS Device does not have a write in progress
+ @retval EFI_DEVICE_ERROR SPI Flash part did not respond properly
+**/
+EFI_STATUS
+WaitNotWip (
+ IN SPI_NOR_FLASH_INSTANCE *SpiNorFlashInstance,
+ IN UINT32 Timeout,
+ IN UINT32 RetryCount
+ )
+{
+ EFI_STATUS Status;
+ UINT8 DeviceStatus;
+ UINT32 AlreadyDelayedInMicroseconds;
+
+ if (Timeout == 0) {
+ return EFI_SUCCESS;
+ }
+
+ if (RetryCount == 0) {
+ RetryCount = 1;
+ }
+
+ do {
+ AlreadyDelayedInMicroseconds = 0;
+ while (AlreadyDelayedInMicroseconds < Timeout) {
+ Status = InternalReadStatus (SpiNorFlashInstance, 1, &DeviceStatus);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Read status error\n", __func__));
+ ASSERT (FALSE);
+ return Status;
+ }
+
+ if ((DeviceStatus & SPI_FLASH_SR_WIP) == SPI_FLASH_SR_NOT_WIP) {
+ return Status;
+ }
+
+ MicroSecondDelay (FixedPcdGet32 (PcdSpiNorFlashOperationDelayMicroseconds));
+ AlreadyDelayedInMicroseconds += FixedPcdGet32 (PcdSpiNorFlashOperationDelayMicroseconds);
+ }
+
+ RetryCount--;
+ } while (RetryCount > 0);
+
+ DEBUG ((DEBUG_ERROR, "%a: Timeout error\n", __func__));
+ return EFI_DEVICE_ERROR;
+}
+
+/**
+ Check for write enable latch set and not device write in progress.
+
+ @param[in] SpiNorFlashInstance SPI NOR instance with all protocols, etc.
+ @param[in] Timeout Timeout in microsecond
+ @param[in] RetryCount The retry count
+
+ @retval EFI_SUCCESS Device does not have a write in progress and
+ write enable latch is set
+ @retval EFI_DEVICE_ERROR SPI Flash part did not respond properly
+**/
+EFI_STATUS
+WaitWelNotWip (
+ IN SPI_NOR_FLASH_INSTANCE *SpiNorFlashInstance,
+ IN UINT32 Timeout,
+ IN UINT32 RetryCount
+ )
+{
+ EFI_STATUS Status;
+ UINT8 DeviceStatus;
+ UINT32 AlreadyDelayedInMicroseconds;
+
+ if (Timeout == 0) {
+ return EFI_SUCCESS;
+ }
+
+ if (RetryCount == 0) {
+ RetryCount = 1;
+ }
+
+ do {
+ AlreadyDelayedInMicroseconds = 0;
+ while (AlreadyDelayedInMicroseconds < Timeout) {
+ Status = InternalReadStatus (SpiNorFlashInstance, 1, &DeviceStatus);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Fail to read WEL.\n", __func__));
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+ }
+
+ if ((DeviceStatus & (SPI_FLASH_SR_WIP | SPI_FLASH_SR_WEL)) == SPI_FLASH_SR_WEL) {
+ return Status;
+ }
+
+ MicroSecondDelay (FixedPcdGet32 (PcdSpiNorFlashOperationDelayMicroseconds));
+ AlreadyDelayedInMicroseconds += FixedPcdGet32 (PcdSpiNorFlashOperationDelayMicroseconds);
+ }
+
+ RetryCount--;
+ } while (RetryCount > 0);
+
+ DEBUG ((DEBUG_ERROR, "%a: Timeout error\n", __func__));
+ return EFI_DEVICE_ERROR;
+}
+
+/**
+ Check for not write enable latch set and not device write in progress.
+
+ @param[in] SpiNorFlashInstance SPI NOR instance with all protocols, etc.
+ @param[in] Timeout Timeout in microsecond
+ @param[in] RetryCount The retry count
+
+ @retval EFI_SUCCESS Device does not have a write in progress and
+ write enable latch is not set
+ @retval EFI_DEVICE_ERROR SPI Flash part did not respond properly
+**/
+EFI_STATUS
+WaitNotWelNotWip (
+ IN SPI_NOR_FLASH_INSTANCE *SpiNorFlashInstance,
+ IN UINT32 Timeout,
+ IN UINT32 RetryCount
+ )
+{
+ EFI_STATUS Status;
+ UINT8 DeviceStatus;
+ UINT32 AlreadyDelayedInMicroseconds;
+
+ if (Timeout == 0) {
+ return EFI_SUCCESS;
+ }
+
+ if (RetryCount == 0) {
+ RetryCount = 1;
+ }
+
+ do {
+ AlreadyDelayedInMicroseconds = 0;
+ while (AlreadyDelayedInMicroseconds < Timeout) {
+ Status = InternalReadStatus (SpiNorFlashInstance, 1, &DeviceStatus);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status) ||
+ ((DeviceStatus & (SPI_FLASH_SR_WIP | SPI_FLASH_SR_WEL)) == SPI_FLASH_SR_NOT_WIP))
+ {
+ return Status;
+ }
+
+ MicroSecondDelay (FixedPcdGet32 (PcdSpiNorFlashOperationDelayMicroseconds));
+ AlreadyDelayedInMicroseconds += FixedPcdGet32 (PcdSpiNorFlashOperationDelayMicroseconds);
+ }
+
+ RetryCount--;
+ } while (RetryCount > 0);
+
+ DEBUG ((DEBUG_ERROR, "SpiNorFlash:%a: Timeout error\n", __func__));
+ return EFI_DEVICE_ERROR;
+}
+
+/**
+ Read the 3 byte manufacture and device ID from the SPI flash.
+
+ This routine must be called at or below TPL_NOTIFY.
+ This routine reads the 3 byte manufacture and device ID from the flash part
+ filling the buffer provided.
+
+ @param[in] This Pointer to an EFI_SPI_NOR_FLASH_PROTOCOL data structure.
+ @param[out] Buffer Pointer to a 3 byte buffer to receive the manufacture and
+ device ID.
+
+ @retval EFI_SUCCESS The manufacture and device ID was read
+ successfully.
+ @retval EFI_INVALID_PARAMETER Buffer is NULL
+ @retval EFI_DEVICE_ERROR Invalid data received from SPI flash part.
+
+**/
+EFI_STATUS
+EFIAPI
+GetFlashId (
+ IN CONST EFI_SPI_NOR_FLASH_PROTOCOL *This,
+ OUT UINT8 *Buffer
+ )
+{
+ EFI_STATUS Status;
+ SPI_NOR_FLASH_INSTANCE *Instance;
+ UINT32 TransactionBufferLength;
+
+ DEBUG ((DEBUG_INFO, "%a: Entry\n", __func__));
+
+ if (Buffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = SPI_NOR_FLASH_FROM_THIS (This);
+
+ // Check not WIP
+ Status = WaitNotWip (Instance, FixedPcdGet32 (PcdSpiNorFlashOperationDelayMicroseconds), FixedPcdGet32 (PcdSpiNorFlashFixedTimeoutRetryCount));
+
+ if (!EFI_ERROR (Status)) {
+ TransactionBufferLength = FillWriteBuffer (
+ Instance,
+ SPI_FLASH_RDID,
+ SPI_FLASH_RDID_DUMMY,
+ SPI_FLASH_RDID_ADDR_BYTES,
+ FALSE,
+ 0,
+ 0,
+ NULL
+ );
+ Status = Instance->SpiIo->Transaction (
+ Instance->SpiIo,
+ SPI_TRANSACTION_WRITE_THEN_READ,
+ FALSE,
+ 0,
+ 1,
+ 8,
+ TransactionBufferLength,
+ Instance->SpiTransactionWriteBuffer,
+ 3,
+ Buffer
+ );
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ return Status;
+}
+
+/**
+ Read data from the SPI flash at not fast speed.
+
+ This routine must be called at or below TPL_NOTIFY.
+ This routine reads data from the SPI part in the buffer provided.
+
+ @param[in] This Pointer to an EFI_SPI_NOR_FLASH_PROTOCOL data
+ structure.
+ @param[in] FlashAddress Address in the flash to start reading
+ @param[in] LengthInBytes Read length in bytes
+ @param[out] Buffer Address of a buffer to receive the data
+
+ @retval EFI_SUCCESS The data was read successfully.
+ @retval EFI_INVALID_PARAMETER Buffer is NULL, or
+ FlashAddress >= This->FlashSize, or
+ LengthInBytes > This->FlashSize - FlashAddress
+
+**/
+EFI_STATUS
+EFIAPI
+LfReadData (
+ IN CONST EFI_SPI_NOR_FLASH_PROTOCOL *This,
+ IN UINT32 FlashAddress,
+ IN UINT32 LengthInBytes,
+ OUT UINT8 *Buffer
+ )
+{
+ EFI_STATUS Status;
+ SPI_NOR_FLASH_INSTANCE *Instance;
+ UINT32 ByteCounter;
+ UINT32 CurrentAddress;
+ UINT8 *CurrentBuffer;
+ UINT32 Length;
+ UINT32 TransactionBufferLength;
+ UINT32 MaximumTransferBytes;
+
+ DEBUG ((DEBUG_INFO, "%a: Entry\n", __func__));
+
+ Status = EFI_DEVICE_ERROR;
+ if ((Buffer == NULL) ||
+ (FlashAddress >= This->FlashSize) ||
+ (LengthInBytes > This->FlashSize - FlashAddress))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = SPI_NOR_FLASH_FROM_THIS (This);
+ MaximumTransferBytes = Instance->SpiIo->MaximumTransferBytes;
+
+ CurrentBuffer = Buffer;
+ Length = 0;
+ for (ByteCounter = 0; ByteCounter < LengthInBytes;) {
+ CurrentAddress = FlashAddress + ByteCounter;
+ CurrentBuffer = Buffer + ByteCounter;
+ Length = LengthInBytes - ByteCounter;
+ // Length must be MaximumTransferBytes or less
+ if (Length > MaximumTransferBytes) {
+ Length = MaximumTransferBytes;
+ }
+
+ // Check not WIP
+ Status = WaitNotWip (Instance, FixedPcdGet32 (PcdSpiNorFlashOperationDelayMicroseconds), FixedPcdGet32 (PcdSpiNorFlashFixedTimeoutRetryCount));
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ TransactionBufferLength = FillWriteBuffer (
+ Instance,
+ SPI_FLASH_READ,
+ SPI_FLASH_READ_DUMMY,
+ SPI_FLASH_READ_ADDR_BYTES,
+ TRUE,
+ CurrentAddress,
+ 0,
+ NULL
+ );
+ Status = Instance->SpiIo->Transaction (
+ Instance->SpiIo,
+ SPI_TRANSACTION_WRITE_THEN_READ,
+ FALSE,
+ 0,
+ 1,
+ 8,
+ TransactionBufferLength,
+ Instance->SpiTransactionWriteBuffer,
+ Length,
+ CurrentBuffer
+ );
+ ASSERT_EFI_ERROR (Status);
+ ByteCounter += Length;
+ }
+
+ return Status;
+}
+
+/**
+ Read data from the SPI flash.
+
+ This routine must be called at or below TPL_NOTIFY.
+ This routine reads data from the SPI part in the buffer provided.
+
+ @param[in] This Pointer to an EFI_SPI_NOR_FLASH_PROTOCOL data
+ structure.
+ @param[in] FlashAddress Address in the flash to start reading
+ @param[in] LengthInBytes Read length in bytes
+ @param[out] Buffer Address of a buffer to receive the data
+
+ @retval EFI_SUCCESS The data was read successfully.
+ @retval EFI_INVALID_PARAMETER Buffer is NULL, or
+ FlashAddress >= This->FlashSize, or
+ LengthInBytes > This->FlashSize - FlashAddress
+
+**/
+EFI_STATUS
+EFIAPI
+ReadData (
+ IN CONST EFI_SPI_NOR_FLASH_PROTOCOL *This,
+ IN UINT32 FlashAddress,
+ IN UINT32 LengthInBytes,
+ OUT UINT8 *Buffer
+ )
+{
+ EFI_STATUS Status;
+ SPI_NOR_FLASH_INSTANCE *Instance;
+ UINT32 ByteCounter;
+ UINT32 CurrentAddress;
+ UINT8 *CurrentBuffer;
+ UINT32 Length;
+ UINT32 TransactionBufferLength;
+ UINT32 MaximumTransferBytes;
+ UINT8 FastReadInstruction;
+ UINT8 FastReadWaitStateDummyClocks;
+ UINT8 FastReadModeClock;
+
+ DEBUG ((DEBUG_INFO, "%a: Entry, Read address = 0x%08x, Length = 0x%08x\n", __func__, FlashAddress, LengthInBytes));
+
+ Status = EFI_DEVICE_ERROR;
+ if ((Buffer == NULL) ||
+ (FlashAddress >= This->FlashSize) ||
+ (LengthInBytes > This->FlashSize - FlashAddress))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = SPI_NOR_FLASH_FROM_THIS (This);
+ MaximumTransferBytes = Instance->SpiIo->MaximumTransferBytes;
+
+ //
+ // Initial the default read operation parameters.
+ //
+ FastReadInstruction = SPI_FLASH_FAST_READ;
+ FastReadWaitStateDummyClocks = SPI_FLASH_FAST_READ_DUMMY * 8;
+ FastReadModeClock = 0;
+ //
+ // Override by the Fast Read capabiity table.
+ //
+ // Get the first supported fast read comamnd.
+ // This will be the standard fast read command (0x0b),
+ // which is the first fast read command added to the
+ // supported list.
+ // TODO: The mechanism to choose the advanced fast read
+ // is not determined yet in this version of
+ // SpiNorFlash driver.
+ Status = GetFastReadParameter (
+ Instance,
+ &FastReadInstruction,
+ &FastReadModeClock,
+ &FastReadWaitStateDummyClocks
+ );
+ if (!EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_VERBOSE, " Use below Fast Read mode:\n"));
+ } else {
+ DEBUG ((DEBUG_VERBOSE, " Use the default Fast Read mode:\n"));
+ }
+
+ DEBUG ((DEBUG_VERBOSE, " Instruction : 0x%x\n", FastReadInstruction));
+ DEBUG ((DEBUG_VERBOSE, " Mode Clock : 0x%x\n", FastReadModeClock));
+ DEBUG ((DEBUG_VERBOSE, " Wait States (Dummy Clocks) in clock: 0x%x\n", FastReadWaitStateDummyClocks));
+ DEBUG ((DEBUG_VERBOSE, " Supported erase address bytes by device: 0x%02x.\n", Instance->SfdpBasicFlash->AddressBytes));
+ DEBUG ((DEBUG_VERBOSE, " (00: 3-Byte, 01: 3 or 4-Byte. 10: 4-Byte)\n"));
+
+ CurrentBuffer = Buffer;
+ Length = 0;
+ for (ByteCounter = 0; ByteCounter < LengthInBytes;) {
+ CurrentAddress = FlashAddress + ByteCounter;
+ CurrentBuffer = Buffer + ByteCounter;
+ Length = LengthInBytes - ByteCounter;
+ // Length must be MaximumTransferBytes or less
+ if (Length > MaximumTransferBytes) {
+ Length = MaximumTransferBytes;
+ }
+
+ // Check not WIP
+ Status = WaitNotWip (Instance, FixedPcdGet32 (PcdSpiNorFlashOperationDelayMicroseconds), FixedPcdGet32 (PcdSpiNorFlashFixedTimeoutRetryCount));
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ TransactionBufferLength = FillWriteBuffer (
+ Instance,
+ FastReadInstruction,
+ FastReadWaitStateDummyClocks / 8,
+ (UINT8)Instance->SfdpBasicFlash->AddressBytes,
+ TRUE,
+ CurrentAddress,
+ 0,
+ NULL
+ );
+ Status = Instance->SpiIo->Transaction (
+ Instance->SpiIo,
+ SPI_TRANSACTION_WRITE_THEN_READ,
+ FALSE,
+ 0,
+ 1,
+ 8,
+ TransactionBufferLength,
+ Instance->SpiTransactionWriteBuffer,
+ Length,
+ CurrentBuffer
+ );
+ ASSERT_EFI_ERROR (Status);
+ ByteCounter += Length;
+ }
+
+ return Status;
+}
+
+/**
+ Read the flash status register.
+
+ This routine must be called at or below TPL_NOTIFY.
+ This routine reads the flash part status register.
+
+ @param[in] This Pointer to an EFI_SPI_NOR_FLASH_PROTOCOL data
+ structure.
+ @param[in] LengthInBytes Number of status bytes to read.
+ @param[out] FlashStatus Pointer to a buffer to receive the flash status.
+
+ @retval EFI_SUCCESS The status register was read successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+ReadStatus (
+ IN CONST EFI_SPI_NOR_FLASH_PROTOCOL *This,
+ IN UINT32 LengthInBytes,
+ OUT UINT8 *FlashStatus
+ )
+{
+ EFI_STATUS Status;
+ SPI_NOR_FLASH_INSTANCE *Instance;
+
+ if (LengthInBytes != 1) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = SPI_NOR_FLASH_FROM_THIS (This);
+
+ Status = InternalReadStatus (Instance, LengthInBytes, FlashStatus);
+
+ return Status;
+}
+
+/**
+ Write the flash status register.
+
+ This routine must be called at or below TPL_N OTIFY.
+ This routine writes the flash part status register.
+
+ @param[in] This Pointer to an EFI_SPI_NOR_FLASH_PROTOCOL data
+ structure.
+ @param[in] LengthInBytes Number of status bytes to write.
+ @param[in] FlashStatus Pointer to a buffer containing the new status.
+
+ @retval EFI_SUCCESS The status write was successful.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the write buffer.
+
+**/
+EFI_STATUS
+EFIAPI
+WriteStatus (
+ IN CONST EFI_SPI_NOR_FLASH_PROTOCOL *This,
+ IN UINT32 LengthInBytes,
+ IN UINT8 *FlashStatus
+ )
+{
+ EFI_STATUS Status;
+ SPI_NOR_FLASH_INSTANCE *Instance;
+ UINT32 TransactionBufferLength;
+
+ if (LengthInBytes != 1) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = SPI_NOR_FLASH_FROM_THIS (This);
+
+ // Check not WIP
+ Status = WaitNotWip (Instance, FixedPcdGet32 (PcdSpiNorFlashOperationDelayMicroseconds), FixedPcdGet32 (PcdSpiNorFlashFixedTimeoutRetryCount));
+
+ // Set Write Enable
+ if (!EFI_ERROR (Status)) {
+ if (Instance->WriteEnableLatchRequired) {
+ Status = SetWel (Instance);
+ DEBUG ((DEBUG_ERROR, "%a: set Write Enable Error.\n", __func__));
+ ASSERT_EFI_ERROR (Status);
+ // Check not WIP & WEL enabled
+ Status = WaitWelNotWip (Instance, FixedPcdGet32 (PcdSpiNorFlashOperationDelayMicroseconds), FixedPcdGet32 (PcdSpiNorFlashFixedTimeoutRetryCount));
+ }
+
+ // Write the Status Register
+ if (!EFI_ERROR (Status)) {
+ TransactionBufferLength = FillWriteBuffer (
+ Instance,
+ SPI_FLASH_WRSR,
+ SPI_FLASH_WRSR_DUMMY,
+ SPI_FLASH_WRSR_ADDR_BYTES,
+ FALSE,
+ 0,
+ 0,
+ NULL
+ );
+ Status = Instance->SpiIo->Transaction (
+ Instance->SpiIo,
+ SPI_TRANSACTION_WRITE_ONLY,
+ FALSE,
+ 0,
+ 1,
+ 8,
+ TransactionBufferLength,
+ Instance->SpiTransactionWriteBuffer,
+ 0,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Write data to the SPI flash.
+
+ This routine must be called at or below TPL_NOTIFY.
+ This routine breaks up the write operation as necessary to write the data to
+ the SPI part.
+
+ @param[in] This Pointer to an EFI_SPI_NOR_FLASH_PROTOCOL data
+ structure.
+ @param[in] FlashAddress Address in the flash to start writing
+ @param[in] LengthInBytes Write length in bytes
+ @param[in] Buffer Address of a buffer containing the data
+
+ @retval EFI_SUCCESS The data was written successfully.
+ @retval EFI_INVALID_PARAMETER Buffer is NULL, or
+ FlashAddress >= This->FlashSize, or
+ LengthInBytes > This->FlashSize - FlashAddress
+ @retval EFI_OUT_OF_RESOURCES Insufficient memory to copy buffer.
+
+**/
+EFI_STATUS
+EFIAPI
+WriteData (
+ IN CONST EFI_SPI_NOR_FLASH_PROTOCOL *This,
+ IN UINT32 FlashAddress,
+ IN UINT32 LengthInBytes,
+ IN UINT8 *Buffer
+ )
+{
+ EFI_STATUS Status;
+ SPI_NOR_FLASH_INSTANCE *Instance;
+ UINT32 ByteCounter;
+ UINT32 CurrentAddress;
+ UINT32 Length;
+ UINT32 BytesUntilBoundary;
+ UINT8 *CurrentBuffer;
+ UINT32 TransactionBufferLength;
+ UINT32 MaximumTransferBytes;
+ UINT32 SpiFlashPageSize;
+
+ DEBUG ((DEBUG_INFO, "%a: Entry: Write address = 0x%08x, Length = 0x%08x\n", __func__, FlashAddress, LengthInBytes));
+
+ Status = EFI_DEVICE_ERROR;
+ if ((Buffer == NULL) ||
+ (LengthInBytes == 0) ||
+ (FlashAddress >= This->FlashSize) ||
+ (LengthInBytes > This->FlashSize - FlashAddress))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = SPI_NOR_FLASH_FROM_THIS (This);
+ MaximumTransferBytes = Instance->SpiIo->MaximumTransferBytes;
+ if (Instance->SfdpBasicFlashByteCount >= 11 * 4) {
+ // JESD216C spec DWORD 11
+ SpiFlashPageSize = 1 << Instance->SfdpBasicFlash->PageSize;
+ } else {
+ SpiFlashPageSize = 256;
+ }
+
+ CurrentBuffer = Buffer;
+ Length = 0;
+ for (ByteCounter = 0; ByteCounter < LengthInBytes;) {
+ CurrentAddress = FlashAddress + ByteCounter;
+ CurrentBuffer = Buffer + ByteCounter;
+ Length = LengthInBytes - ByteCounter;
+ // Length must be MaximumTransferBytes or less
+ if (Length > MaximumTransferBytes) {
+ Length = MaximumTransferBytes;
+ }
+
+ // Cannot cross SpiFlashPageSize boundary
+ BytesUntilBoundary = SpiFlashPageSize
+ - (CurrentAddress % SpiFlashPageSize);
+ if ((BytesUntilBoundary != 0) && (Length > BytesUntilBoundary)) {
+ Length = BytesUntilBoundary;
+ }
+
+ // Check not WIP
+ Status = WaitNotWip (Instance, FixedPcdGet32 (PcdSpiNorFlashOperationDelayMicroseconds), FixedPcdGet32 (PcdSpiNorFlashFixedTimeoutRetryCount));
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ if (Instance->WriteEnableLatchRequired) {
+ // Set Write Enable
+ Status = SetWel (Instance);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ // Check not WIP & WEL enabled
+ Status = WaitWelNotWip (Instance, FixedPcdGet32 (PcdSpiNorFlashOperationDelayMicroseconds), FixedPcdGet32 (PcdSpiNorFlashFixedTimeoutRetryCount));
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ }
+
+ // Write Data
+ TransactionBufferLength = FillWriteBuffer (
+ Instance,
+ SPI_FLASH_PP,
+ SPI_FLASH_PP_DUMMY,
+ SPI_FLASH_PP_ADDR_BYTES,
+ TRUE,
+ CurrentAddress,
+ Length,
+ CurrentBuffer
+ );
+ Status = Instance->SpiIo->Transaction (
+ Instance->SpiIo,
+ SPI_TRANSACTION_WRITE_ONLY,
+ FALSE,
+ 0,
+ 1,
+ 8,
+ TransactionBufferLength,
+ Instance->SpiTransactionWriteBuffer,
+ 0,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ if (Instance->WriteEnableLatchRequired) {
+ // Check not WIP & not WEL
+ Status = WaitNotWelNotWip (Instance, FixedPcdGet32 (PcdSpiNorFlashOperationDelayMicroseconds), FixedPcdGet32 (PcdSpiNorFlashFixedTimeoutRetryCount));
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ } else {
+ Status = WaitNotWip (Instance, FixedPcdGet32 (PcdSpiNorFlashOperationDelayMicroseconds), FixedPcdGet32 (PcdSpiNorFlashFixedTimeoutRetryCount));
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ }
+
+ ByteCounter += Length;
+ }
+
+ return Status;
+}
+
+/**
+ Efficiently erases blocks in the SPI flash.
+
+ This routine must be called at or below TPL_NOTIFY.
+ This routine may use the combination of variable earse sizes to erase the
+ specified area accroding to the flash region.
+
+ @param[in] This Pointer to an EFI_SPI_NOR_FLASH_PROTOCOL data
+ structure.
+ @param[in] FlashAddress Address to start erasing
+ @param[in] BlockCount Number of blocks to erase. The block size is indicated
+ in EraseBlockBytes in EFI_SPI_NOR_FLASH_PROTOCOL.
+
+ @retval EFI_SUCCESS The erase was completed successfully.
+ @retval EFI_DEVICE_ERROR The flash devices has problems.
+ @retval EFI_INVALID_PARAMETER The given FlashAddress and/or BlockCount
+ is invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+Erase (
+ IN CONST EFI_SPI_NOR_FLASH_PROTOCOL *This,
+ IN UINT32 FlashAddress,
+ IN UINT32 BlockCount
+ )
+{
+ EFI_STATUS Status;
+ SPI_NOR_FLASH_INSTANCE *Instance;
+ UINT8 Opcode;
+ UINT32 Dummy;
+ UINT32 ByteCounter;
+ UINT32 EraseLength;
+ UINT32 TotalEraseLength;
+ UINT32 CurrentAddress;
+ UINT32 TransactionBufferLength;
+ UINT32 BlockCountToErase;
+ UINT32 BlockSizeToErase;
+ UINT8 BlockEraseCommand;
+ UINT32 TypicalEraseTime;
+ UINT64 MaximumEraseTimeout;
+ SFDP_SECTOR_REGION_RECORD *FlashRegion;
+
+ DEBUG ((DEBUG_INFO, "%a: Entry: Erase address = 0x%08x, Block count = 0x%x\n", __func__, FlashAddress, BlockCount));
+
+ Status = EFI_DEVICE_ERROR;
+ Instance = SPI_NOR_FLASH_FROM_THIS (This);
+
+ // Get the region of this flash address.
+ Status = GetRegionByFlashAddress (Instance, FlashAddress, &FlashRegion);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, " Failed to get the flash region of this flash address.\n"));
+ ASSERT (FALSE);
+ return Status;
+ }
+
+ CurrentAddress = FlashAddress;
+ BlockCountToErase = BlockCount;
+ BlockSizeToErase = FlashRegion->SectorSize; // This is also the minimum block erase size.
+ TotalEraseLength = BlockCountToErase * FlashRegion->SectorSize;
+ if ((FlashAddress + TotalEraseLength) > (FlashRegion->RegionAddress + FlashRegion->RegionTotalSize)) {
+ DEBUG ((DEBUG_ERROR, " The blocks to erase exceeds the region boundary.\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DEBUG ((DEBUG_VERBOSE, " Region starting address: 0x%08x.\n", FlashRegion->RegionAddress));
+ DEBUG ((DEBUG_VERBOSE, " Region size : 0x%08x.\n", FlashRegion->RegionTotalSize));
+ DEBUG ((DEBUG_VERBOSE, " Region sector size : 0x%08x.\n", FlashRegion->SectorSize));
+ DEBUG ((DEBUG_VERBOSE, " Supported erase address bytes by device: 0x%02x.\n", Instance->SfdpBasicFlash->AddressBytes));
+ DEBUG ((DEBUG_VERBOSE, " (00: 3-Byte, 01: 3 or 4-Byte. 10: 4-Byte)\n"));
+
+ // Loop until all blocks are erased.
+ ByteCounter = 0;
+ while (ByteCounter < TotalEraseLength) {
+ CurrentAddress = FlashAddress + ByteCounter;
+
+ // Is this the whole device erase.
+ if (TotalEraseLength == This->FlashSize) {
+ Opcode = SPI_FLASH_CE;
+ Dummy = SPI_FLASH_CE_DUMMY;
+ EraseLength = TotalEraseLength;
+ DEBUG ((DEBUG_VERBOSE, " This is the chip erase.\n"));
+ } else {
+ //
+ // Get the erase block attributes.
+ //
+ Status = GetEraseBlockAttribute (
+ Instance,
+ FlashRegion,
+ CurrentAddress,
+ TotalEraseLength - ByteCounter,
+ &BlockSizeToErase,
+ &BlockCountToErase,
+ &BlockEraseCommand,
+ &TypicalEraseTime,
+ &MaximumEraseTimeout
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, " Failed to get erase block attribute.\n"));
+ ASSERT (FALSE);
+ }
+
+ Opcode = BlockEraseCommand;
+ Dummy = SPI_FLASH_BE_DUMMY;
+ EraseLength = BlockCountToErase * BlockSizeToErase;
+ DEBUG ((
+ DEBUG_VERBOSE,
+ " Erase command 0x%02x at adddress 0x%08x for length 0x%08x.\n",
+ BlockEraseCommand,
+ CurrentAddress,
+ EraseLength
+ ));
+ }
+
+ //
+ // Process the erase command.
+ //
+
+ // Check not WIP
+ Status = WaitNotWip (Instance, FixedPcdGet32 (PcdSpiNorFlashOperationDelayMicroseconds), FixedPcdGet32 (PcdSpiNorFlashFixedTimeoutRetryCount));
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ if (Instance->WriteEnableLatchRequired) {
+ // Set Write Enable
+ Status = SetWel (Instance);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ // Check not WIP & WEL enabled
+ Status = WaitWelNotWip (Instance, FixedPcdGet32 (PcdSpiNorFlashOperationDelayMicroseconds), FixedPcdGet32 (PcdSpiNorFlashFixedTimeoutRetryCount));
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ }
+
+ // Erase Block
+ TransactionBufferLength = FillWriteBuffer (
+ Instance,
+ Opcode,
+ Dummy,
+ (UINT8)Instance->SfdpBasicFlash->AddressBytes,
+ TRUE,
+ CurrentAddress,
+ 0,
+ NULL
+ );
+ Status = Instance->SpiIo->Transaction (
+ Instance->SpiIo,
+ SPI_TRANSACTION_WRITE_ONLY,
+ FALSE,
+ 0,
+ 1,
+ 8,
+ TransactionBufferLength,
+ Instance->SpiTransactionWriteBuffer,
+ 0,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ break;
+ } else {
+ DEBUG ((DEBUG_VERBOSE, "Erase command sucessfully.\n"));
+ }
+
+ if (Instance->WriteEnableLatchRequired) {
+ //
+ // Check not WIP & not WEL
+ // Use the timeout value calculated by SPI NOR flash SFDP.
+ //
+ Status = WaitNotWelNotWip (Instance, (UINT32)MaximumEraseTimeout * 1000, FixedPcdGet32 (PcdSpiNorFlashOperationRetryCount));
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ } else {
+ //
+ // Use the timeout value calculated by SPI NOR flash SFDP.
+ //
+ Status = WaitNotWip (Instance, (UINT32)MaximumEraseTimeout * 1000, FixedPcdGet32 (PcdSpiNorFlashOperationRetryCount));
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ }
+
+ ByteCounter += EraseLength;
+ }
+
+ return Status;
+}
diff --git a/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlash.h b/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlash.h new file mode 100644 index 0000000000..fb71e8d56f --- /dev/null +++ b/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlash.h @@ -0,0 +1,286 @@ +/** @file
+ Definitions of SPI NOR flash operation functions.
+
+ Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef SPI_NOR_FLASH_H_
+#define SPI_NOR_FLASH_H_
+
+#include <PiDxe.h>
+#include <Protocol/SpiNorFlash.h>
+#include <Protocol/SpiIo.h>
+#include "SpiNorFlashJedecSfdpInternal.h"
+
+/**
+ Fill Write Buffer with Opcode, Address, Dummy Bytes, and Data
+
+ @param[in] Opcode - Opcode for transaction
+ @param[in] Address - SPI Offset Start Address
+ @param[in] WriteBytes - Number of bytes to write to SPI device
+ @param[in] WriteBuffer - Buffer containing bytes to write to SPI device
+
+ @retval Size of Data in Buffer
+**/
+UINT32
+FillWriteBuffer (
+ IN SPI_NOR_FLASH_INSTANCE *SpiNorFlashInstance,
+ IN UINT8 Opcode,
+ IN UINT32 DummyBytes,
+ IN UINT8 AddressBytesSupported,
+ IN BOOLEAN UseAddress,
+ IN UINT32 Address,
+ IN UINT32 WriteBytes,
+ IN UINT8 *WriteBuffer
+ );
+
+/**
+ Set Write Enable Latch
+
+ @param[in] Instance SPI NOR instance with all protocols, etc.
+
+ @retval EFI_SUCCESS SPI Write Enable succeeded
+ @retval EFI_DEVICE_ERROR SPI Flash part did not respond properly
+**/
+EFI_STATUS
+SetWel (
+ IN SPI_NOR_FLASH_INSTANCE *SpiNorFlashInstance
+ );
+
+/**
+ Check for not device write in progress
+
+ @param[in] Instance SPI NOR instance with all protocols, etc.
+ @param[in] Timeout Timeout in microsecond
+ @param[in] RetryCount The retry count
+
+ @retval EFI_SUCCESS Device does not have a write in progress
+ @retval EFI_DEVICE_ERROR SPI Flash part did not respond properly
+**/
+EFI_STATUS
+WaitNotWip (
+ IN SPI_NOR_FLASH_INSTANCE *SpiNorFlashInstance,
+ IN UINT32 Timeout,
+ IN UINT32 RetryCount
+ );
+
+/**
+ Check for write enable latch set and not device write in progress
+
+ @param[in] Instance SPI NOR instance with all protocols, etc.
+ @param[in] Timeout Timeout in microsecond
+ @param[in] RetryCount The retry count
+
+ @retval EFI_SUCCESS Device does not have a write in progress and
+ write enable latch is set
+ @retval EFI_DEVICE_ERROR SPI Flash part did not respond properly
+**/
+EFI_STATUS
+WaitWelNotWip (
+ IN SPI_NOR_FLASH_INSTANCE *SpiNorFlashInstance,
+ IN UINT32 Timeout,
+ IN UINT32 RetryCount
+ );
+
+/**
+ Check for not write enable latch set and not device write in progress
+
+ @param[in] Instance SPI NOR instance with all protocols, etc.
+ @param[in] Timeout Timeout in microsecond
+ @param[in] RetryCount The retry count
+
+ @retval EFI_SUCCESS Device does not have a write in progress and
+ write enable latch is not set
+ @retval EFI_DEVICE_ERROR SPI Flash part did not respond properly
+**/
+EFI_STATUS
+WaitNotWelNotWip (
+ IN SPI_NOR_FLASH_INSTANCE *SpiNorFlashInstance,
+ IN UINT32 Timeout,
+ IN UINT32 RetryCount
+ );
+
+/**
+ Read the 3 byte manufacture and device ID from the SPI flash.
+
+ This routine must be called at or below TPL_NOTIFY.
+ This routine reads the 3 byte manufacture and device ID from the flash part
+ filling the buffer provided.
+
+ @param[in] This Pointer to an EFI_SPI_NOR_FLASH_PROTOCOL data structure.
+ @param[out] Buffer Pointer to a 3 byte buffer to receive the manufacture and
+ device ID.
+
+
+
+ @retval EFI_SUCCESS The manufacture and device ID was read
+ successfully.
+ @retval EFI_INVALID_PARAMETER Buffer is NULL
+ @retval EFI_DEVICE_ERROR Invalid data received from SPI flash part.
+
+**/
+EFI_STATUS
+EFIAPI
+GetFlashId (
+ IN CONST EFI_SPI_NOR_FLASH_PROTOCOL *This,
+ OUT UINT8 *Buffer
+ );
+
+/**
+ Read data from the SPI flash.
+
+ This routine must be called at or below TPL_NOTIFY.
+ This routine reads data from the SPI part in the buffer provided.
+
+ @param[in] This Pointer to an EFI_SPI_NOR_FLASH_PROTOCOL data
+ structure.
+ @param[in] FlashAddress Address in the flash to start reading
+ @param[in] LengthInBytes Read length in bytes
+ @param[out] Buffer Address of a buffer to receive the data
+
+ @retval EFI_SUCCESS The data was read successfully.
+ @retval EFI_INVALID_PARAMETER Buffer is NULL, or
+ FlashAddress >= This->FlashSize, or
+ LengthInBytes > This->FlashSize - FlashAddress
+
+**/
+EFI_STATUS
+EFIAPI
+ReadData (
+ IN CONST EFI_SPI_NOR_FLASH_PROTOCOL *This,
+ IN UINT32 FlashAddress,
+ IN UINT32 LengthInBytes,
+ OUT UINT8 *Buffer
+ );
+
+/**
+ Read data from the SPI flash at not fast speed
+
+ This routine must be called at or below TPL_NOTIFY.
+ This routine reads data from the SPI part in the buffer provided.
+
+ @param[in] This Pointer to an EFI_SPI_NOR_FLASH_PROTOCOL data
+ structure.
+ @param[in] FlashAddress Address in the flash to start reading
+ @param[in] LengthInBytes Read length in bytes
+ @param[out] Buffer Address of a buffer to receive the data
+
+ @retval EFI_SUCCESS The data was read successfully.
+ @retval EFI_INVALID_PARAMETER Buffer is NULL, or
+ FlashAddress >= This->FlashSize, or
+ LengthInBytes > This->FlashSize - FlashAddress
+
+**/
+EFI_STATUS
+EFIAPI
+LfReadData (
+ IN CONST EFI_SPI_NOR_FLASH_PROTOCOL *This,
+ IN UINT32 FlashAddress,
+ IN UINT32 LengthInBytes,
+ OUT UINT8 *Buffer
+ );
+
+/**
+ Read the flash status register.
+
+ This routine must be called at or below TPL_NOTIFY.
+ This routine reads the flash part status register.
+
+ @param[in] This Pointer to an EFI_SPI_NOR_FLASH_PROTOCOL data
+ structure.
+ @param[in] LengthInBytes Number of status bytes to read.
+ @param[out] FlashStatus Pointer to a buffer to receive the flash status.
+
+ @retval EFI_SUCCESS The status register was read successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+ReadStatus (
+ IN CONST EFI_SPI_NOR_FLASH_PROTOCOL *This,
+ IN UINT32 LengthInBytes,
+ OUT UINT8 *FlashStatus
+ );
+
+/**
+ Write the flash status register.
+
+ This routine must be called at or below TPL_N OTIFY.
+ This routine writes the flash part status register.
+
+ @param[in] This Pointer to an EFI_SPI_NOR_FLASH_PROTOCOL data
+ structure.
+ @param[in] LengthInBytes Number of status bytes to write.
+ @param[in] FlashStatus Pointer to a buffer containing the new status.
+
+ @retval EFI_SUCCESS The status write was successful.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate the write buffer.
+
+**/
+EFI_STATUS
+EFIAPI
+WriteStatus (
+ IN CONST EFI_SPI_NOR_FLASH_PROTOCOL *This,
+ IN UINT32 LengthInBytes,
+ IN UINT8 *FlashStatus
+ );
+
+/**
+ Write data to the SPI flash.
+
+ This routine must be called at or below TPL_NOTIFY.
+ This routine breaks up the write operation as necessary to write the data to
+ the SPI part.
+
+ @param[in] This Pointer to an EFI_SPI_NOR_FLASH_PROTOCOL data
+ structure.
+ @param[in] FlashAddress Address in the flash to start writing
+ @param[in] LengthInBytes Write length in bytes
+ @param[in] Buffer Address of a buffer containing the data
+
+ @retval EFI_SUCCESS The data was written successfully.
+ @retval EFI_INVALID_PARAMETER Buffer is NULL, or
+ FlashAddress >= This->FlashSize, or
+ LengthInBytes > This->FlashSize - FlashAddress
+ @retval EFI_OUT_OF_RESOURCES Insufficient memory to copy buffer.
+
+**/
+EFI_STATUS
+EFIAPI
+WriteData (
+ IN CONST EFI_SPI_NOR_FLASH_PROTOCOL *This,
+ IN UINT32 FlashAddress,
+ IN UINT32 LengthInBytes,
+ IN UINT8 *Buffer
+ );
+
+/**
+ Efficiently erases one or more 4KiB regions in the SPI flash.
+
+ This routine must be called at or below TPL_NOTIFY.
+ This routine uses a combination of 4 KiB and larger blocks to erase the
+ specified area.
+
+ @param[in] This Pointer to an EFI_SPI_NOR_FLASH_PROTOCOL data
+ structure.
+ @param[in] FlashAddress Address within a 4 KiB block to start erasing
+ @param[in] BlockCount Number of 4 KiB blocks to erase
+
+ @retval EFI_SUCCESS The erase was completed successfully.
+ @retval EFI_INVALID_PARAMETER FlashAddress >= This->FlashSize, or
+ BlockCount * 4 KiB
+ > This->FlashSize - FlashAddress
+
+**/
+EFI_STATUS
+EFIAPI
+Erase (
+ IN CONST EFI_SPI_NOR_FLASH_PROTOCOL *This,
+ IN UINT32 FlashAddress,
+ IN UINT32 BlockCount
+ );
+
+#endif // SPI_NOR_FLASH_H_
diff --git a/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdp.c b/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdp.c new file mode 100644 index 0000000000..284567d1f4 --- /dev/null +++ b/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdp.c @@ -0,0 +1,1780 @@ +/** @file
+ SPI NOR Flash JEDEC Serial Flash Discoverable Parameters (SFDP)
+ common functions.
+
+ Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Revision Reference:
+ - JEDEC Standard, JESD216F.02
+ https://www.jedec.org/document_search?search_api_views_fulltext=JESD216
+
+ @par Glossary:
+ - SFDP - Serial Flash Discoverable Parameters
+ - PTP - Parameter Table Pointer
+**/
+
+#include <Base.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Protocol/SpiConfiguration.h>
+#include <Protocol/SpiIo.h>
+#include <IndustryStandard/SpiNorFlashJedecSfdp.h>
+#include "SpiNorFlash.h"
+#include "SpiNorFlashJedecSfdpInternal.h"
+
+/**
+ Build up the Fast Read capability entry and link it to
+ the linked list.
+
+ @param[in] Instance SPI Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and
+ EFI_SPI_IO_PROTOCOL.
+ @param[in] FastReadInstruction The string of fast read instruction.
+ @param[in] FastReadModeClk The string of fast read mode clock.
+ @param[in] FastReadDummyClk The string of fast read dummy clock.
+
+**/
+VOID
+CreateSpiFastReadTableEntry (
+ IN SPI_NOR_FLASH_INSTANCE *Instance,
+ IN UINT32 FastReadInstruction,
+ IN UINT32 FastReadModeClk,
+ IN UINT32 FastReadDummyClk
+ )
+{
+ SFPD_FAST_READ_CAPBILITY_RECORD *CapabilityEntry;
+
+ CapabilityEntry = AllocateZeroPool (sizeof (SFPD_FAST_READ_CAPBILITY_RECORD));
+ if (CapabilityEntry == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a: Failed to create fast read table\n", __func__));
+ ASSERT (FALSE);
+ return;
+ }
+
+ InitializeListHead (&CapabilityEntry->NextFastReadCap);
+ CapabilityEntry->FastReadInstruction = (UINT8)FastReadInstruction;
+ CapabilityEntry->ModeClocks = (UINT8)FastReadModeClk;
+ CapabilityEntry->WaitStates = (UINT8)FastReadDummyClk;
+ InsertTailList (&Instance->FastReadTableList, &CapabilityEntry->NextFastReadCap);
+ DEBUG ((DEBUG_VERBOSE, "%a: Create and link table.\n", __func__));
+ DEBUG ((DEBUG_VERBOSE, " Instruction : 0x%x\n", FastReadInstruction));
+ DEBUG ((DEBUG_VERBOSE, " Mode bits : 0x%x\n", FastReadModeClk));
+ DEBUG ((DEBUG_VERBOSE, " Wait States (Dummy Clocks): 0x%x\n", FastReadDummyClk));
+}
+
+/**
+ Calculate erase type typical time.
+
+ @param[in] SfdpEraseTypicalTime Erase type typical time indicated in
+ Basic Flash Parameter Table.
+ EraseTypicalTime [0:4] - Count
+ EraseTypicalTime [5:6] - Unit
+ 00b: 1ms
+ 01b: 16ms
+ 10b: 128ms
+ 11b: 1s
+ @param[in] SfdpEraseTimeMultiplier Multiplier from erase typical time.
+ @param[out] EraseTypicalTime Pointer to receive Erase typical time in milliseconds.
+ @param[out] EraseTimeout Pointer to receive Erase timeout in milliseconds.
+
+**/
+VOID
+CalculateEraseTiming (
+ IN UINT32 SfdpEraseTypicalTime,
+ IN UINT32 SfdpEraseTimeMultiplier,
+ OUT UINT32 *EraseTypicalTime,
+ OUT UINT64 *EraseTimeout
+ )
+{
+ UINT32 UnitInMs;
+
+ UnitInMs = (SfdpEraseTypicalTime & ERASE_TYPICAL_TIME_UNITS_MASK) >> ERASE_TYPICAL_TIME_BIT_POSITION;
+ switch (UnitInMs) {
+ case ERASE_TYPICAL_TIME_UNIT_1_MS_BITMAP:
+ UnitInMs = ERASE_TYPICAL_TIME_UNIT_1_MS;
+ break;
+
+ case ERASE_TYPICAL_TIME_UNIT_16_MS_BITMAP:
+ UnitInMs = ERASE_TYPICAL_TIME_UNIT_16_MS;
+ break;
+
+ case ERASE_TYPICAL_TIME_UNIT_128_MS_BITMAP:
+ UnitInMs = ERASE_TYPICAL_TIME_UNIT_128_MS;
+ break;
+
+ case ERASE_TYPICAL_TIME_UNIT_1000_MS_BITMAP:
+ UnitInMs = ERASE_TYPICAL_TIME_UNIT_1000_MS;
+ break;
+ default:
+ DEBUG ((DEBUG_ERROR, "%a: Unsupported Erase Typical time.\n", __func__));
+ ASSERT (FALSE);
+ }
+
+ *EraseTypicalTime = UnitInMs * ((SfdpEraseTypicalTime & ERASE_TYPICAL_TIME_COUNT_MASK) + 1);
+ *EraseTimeout = 2 * (SfdpEraseTimeMultiplier + 1) * *EraseTypicalTime;
+ return;
+}
+
+/**
+ Print out the erase type information.
+
+ @param[in] SupportedEraseType Pointer to SFDP_SUPPORTED_ERASE_TYPE_RECORD.
+**/
+VOID
+DebugPrintEraseType (
+ IN SFDP_SUPPORTED_ERASE_TYPE_RECORD *SupportedEraseType
+ )
+{
+ DEBUG ((DEBUG_VERBOSE, " Erase Type %d\n", SupportedEraseType->EraseType));
+ DEBUG ((DEBUG_VERBOSE, " Erase Type instruction: 0x%x\n", SupportedEraseType->EraseInstruction));
+ DEBUG ((DEBUG_VERBOSE, " Erase size: 0x%x bytes\n", SupportedEraseType->EraseSizeInByte));
+ DEBUG ((DEBUG_VERBOSE, " Erase time: %d Milliseconds\n", SupportedEraseType->EraseTypicalTime));
+ DEBUG ((DEBUG_VERBOSE, " Erase timeout: %d Milliseconds:\n", SupportedEraseType->EraseTimeout));
+}
+
+/**
+ Insert supported erase type entry.
+
+ @param[in] Instance SPI Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and
+ EFI_SPI_IO_PROTOCOL.
+ @param[in] SupportedEraseType Pointer to SFDP_SUPPORTED_ERASE_TYPE_RECORD.
+**/
+VOID
+CreateEraseTypeEntry (
+ IN SPI_NOR_FLASH_INSTANCE *Instance,
+ IN SFDP_SUPPORTED_ERASE_TYPE_RECORD *SupportedEraseType
+ )
+{
+ InitializeListHead (&SupportedEraseType->NextEraseType);
+ InsertTailList (&Instance->SupportedEraseTypes, &SupportedEraseType->NextEraseType);
+
+ DEBUG ((DEBUG_VERBOSE, "%a: Erase Type 0x%x is supported:\n", __func__, SupportedEraseType->EraseType));
+ DebugPrintEraseType (SupportedEraseType);
+}
+
+/**
+ Build up the erase type tables.
+
+ @param[in] Instance SPI Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and
+ EFI_SPI_IO_PROTOCOL.
+
+**/
+VOID
+BuildUpEraseTypeTable (
+ IN SPI_NOR_FLASH_INSTANCE *Instance
+ )
+{
+ SFDP_SUPPORTED_ERASE_TYPE_RECORD *SupportedEraseType;
+
+ // Build up erase type 1 entry.
+ if (Instance->SfdpBasicFlash->Erase1Size != 0) {
+ SupportedEraseType = AllocateZeroPool (sizeof (SFDP_SUPPORTED_ERASE_TYPE_RECORD));
+ if (SupportedEraseType != NULL) {
+ SupportedEraseType->EraseType = SFDP_ERASE_TYPE_1;
+ SupportedEraseType->EraseInstruction = (UINT8)Instance->SfdpBasicFlash->Erase1Instr;
+ SupportedEraseType->EraseSizeInByte = (UINT32)1 << Instance->SfdpBasicFlash->Erase1Size;
+ CalculateEraseTiming (
+ Instance->SfdpBasicFlash->Erase1Time,
+ Instance->SfdpBasicFlash->EraseMultiplier,
+ &SupportedEraseType->EraseTypicalTime,
+ &SupportedEraseType->EraseTimeout
+ );
+ CreateEraseTypeEntry (Instance, SupportedEraseType);
+ } else {
+ DEBUG ((DEBUG_ERROR, "%a: Memory allocated failed for SFDP_SUPPORTED_ERASE_TYPE_RECORD (Type 1).\n", __func__));
+ ASSERT (FALSE);
+ }
+ }
+
+ // Build up erase type 2 entry.
+ if (Instance->SfdpBasicFlash->Erase2Size != 0) {
+ SupportedEraseType = AllocateZeroPool (sizeof (SFDP_SUPPORTED_ERASE_TYPE_RECORD));
+ if (SupportedEraseType != NULL) {
+ SupportedEraseType->EraseType = SFDP_ERASE_TYPE_2;
+ SupportedEraseType->EraseInstruction = (UINT8)Instance->SfdpBasicFlash->Erase2Instr;
+ SupportedEraseType->EraseSizeInByte = (UINT32)1 << Instance->SfdpBasicFlash->Erase2Size;
+ CalculateEraseTiming (
+ Instance->SfdpBasicFlash->Erase2Time,
+ Instance->SfdpBasicFlash->EraseMultiplier,
+ &SupportedEraseType->EraseTypicalTime,
+ &SupportedEraseType->EraseTimeout
+ );
+ CreateEraseTypeEntry (Instance, SupportedEraseType);
+ } else {
+ DEBUG ((DEBUG_ERROR, "%a: Memory allocated failed for SFDP_SUPPORTED_ERASE_TYPE_RECORD (Type 2).\n", __func__));
+ ASSERT (FALSE);
+ }
+ }
+
+ // Build up erase type 3 entry.
+ if (Instance->SfdpBasicFlash->Erase3Size != 0) {
+ SupportedEraseType = AllocateZeroPool (sizeof (SFDP_SUPPORTED_ERASE_TYPE_RECORD));
+ if (SupportedEraseType != NULL) {
+ SupportedEraseType->EraseType = SFDP_ERASE_TYPE_3;
+ SupportedEraseType->EraseInstruction = (UINT8)Instance->SfdpBasicFlash->Erase3Instr;
+ SupportedEraseType->EraseSizeInByte = (UINT32)1 << Instance->SfdpBasicFlash->Erase3Size;
+ CalculateEraseTiming (
+ Instance->SfdpBasicFlash->Erase3Time,
+ Instance->SfdpBasicFlash->EraseMultiplier,
+ &SupportedEraseType->EraseTypicalTime,
+ &SupportedEraseType->EraseTimeout
+ );
+ CreateEraseTypeEntry (Instance, SupportedEraseType);
+ } else {
+ DEBUG ((DEBUG_ERROR, "%a: Memory allocated failed for SFDP_SUPPORTED_ERASE_TYPE_RECORD (Type 3).\n", __func__));
+ ASSERT (FALSE);
+ }
+ }
+
+ // Build up erase type 4 entry.
+ if (Instance->SfdpBasicFlash->Erase4Size != 0) {
+ SupportedEraseType = AllocateZeroPool (sizeof (SFDP_SUPPORTED_ERASE_TYPE_RECORD));
+ if (SupportedEraseType != NULL) {
+ SupportedEraseType->EraseType = SFDP_ERASE_TYPE_4;
+ SupportedEraseType->EraseInstruction = (UINT8)Instance->SfdpBasicFlash->Erase4Instr;
+ SupportedEraseType->EraseSizeInByte = (UINT32)1 << Instance->SfdpBasicFlash->Erase4Size;
+ CalculateEraseTiming (
+ Instance->SfdpBasicFlash->Erase4Time,
+ Instance->SfdpBasicFlash->EraseMultiplier,
+ &SupportedEraseType->EraseTypicalTime,
+ &SupportedEraseType->EraseTimeout
+ );
+ CreateEraseTypeEntry (Instance, SupportedEraseType);
+ } else {
+ DEBUG ((DEBUG_ERROR, "%a: Memory allocated failed for SFDP_SUPPORTED_ERASE_TYPE_RECORD (Type 4).\n", __func__));
+ ASSERT (FALSE);
+ }
+ }
+}
+
+/**
+ This function check if the erase type is one of the target erase types.
+
+ @param[in] EraseType The erase type.
+ @param[in] TargetTypeNum Number of target search types.
+ @param[in] TargetTypes Target types.
+
+
+ @retval TRUE Yes, this is the target erase type.
+ @retval FALSE No, this is not the target erase type.
+
+**/
+BOOLEAN
+IsTargetEraseType (
+ IN UINT16 EraseType,
+ IN UINT8 TargetTypeNum,
+ IN UINT8 *TargetTypes
+ )
+{
+ UINT8 Index;
+
+ for (Index = 0; Index < TargetTypeNum; Index++) {
+ if (EraseType == *(TargetTypes + Index)) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ Search the erase type record according to the given search type and value.
+
+ @param[in] Instance SPI Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and
+ EFI_SPI_IO_PROTOCOL.
+ @param[in] SearchType Search type.
+ @param[in] SearchValue The value of according to search type.
+ - For SearchEraseTypeByCommand:
+ SearchValue is the erase instruction.
+ - For SearchEraseTypeBySize:
+ SearchValue is the erase block size.
+ - For SearchEraseTypeBySmallestSize:
+ SearchValue is not used.
+ - For SearchEraseTypeByBiggestSize:
+ SearchValue is not used.
+ @param[in] SupportedTypeTargetNum Only search the specific erase types.
+ @param[in] SupportedTypeTarget Pointer to SupportedTypeTargetNum of
+ supported erase types.
+ @param[out] EraseTypeRecord Pointer to receive the erase type record.
+
+ @retval EFI_SUCCESS Pointer to erase type record is returned.
+ EFI_INVALID_PARAMETER Invalid SearchType.
+ EFI_NOT_FOUND Erase type not found.
+**/
+EFI_STATUS
+GetEraseTypeRecord (
+ IN SPI_NOR_FLASH_INSTANCE *Instance,
+ IN SFDP_SEARCH_ERASE_TYPE SearchType,
+ IN UINT32 SearchValue,
+ IN UINT8 SupportedTypeTargetNum,
+ IN UINT8 *SupportedTypeTarget OPTIONAL,
+ OUT SFDP_SUPPORTED_ERASE_TYPE_RECORD **EraseTypeRecord
+ )
+{
+ SFDP_SUPPORTED_ERASE_TYPE_RECORD *EraseType;
+ UINT32 ValueToCompare;
+ BOOLEAN ExitSearching;
+
+ if (IsListEmpty (&Instance->SupportedEraseTypes)) {
+ return EFI_NOT_FOUND;
+ }
+
+ *EraseTypeRecord = NULL;
+
+ //
+ // Initial the comapre value.
+ //
+ switch (SearchType) {
+ case SearchEraseTypeByType:
+ case SearchEraseTypeByCommand:
+ case SearchEraseTypeBySize:
+ break;
+ case SearchEraseTypeBySmallestSize:
+ ValueToCompare = (UINT32)-1;
+ break;
+ case SearchEraseTypeByBiggestSize:
+ ValueToCompare = 0;
+ break;
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ExitSearching = FALSE;
+ EraseType = (SFDP_SUPPORTED_ERASE_TYPE_RECORD *)GetFirstNode (&Instance->SupportedEraseTypes);
+ while (TRUE) {
+ if ((SupportedTypeTarget == NULL) || IsTargetEraseType (EraseType->EraseType, SupportedTypeTargetNum, SupportedTypeTarget)) {
+ switch (SearchType) {
+ case SearchEraseTypeByType:
+ if (EraseType->EraseType == SearchValue) {
+ *EraseTypeRecord = EraseType;
+ ExitSearching = TRUE;
+ }
+
+ break;
+
+ case SearchEraseTypeBySize:
+ if (EraseType->EraseSizeInByte == SearchValue) {
+ *EraseTypeRecord = EraseType;
+ ExitSearching = TRUE;
+ }
+
+ break;
+
+ case SearchEraseTypeByCommand:
+ if (EraseType->EraseInstruction == (UINT8)SearchValue) {
+ *EraseTypeRecord = EraseType;
+ ExitSearching = TRUE;
+ }
+
+ break;
+
+ case SearchEraseTypeBySmallestSize:
+ if (EraseType->EraseSizeInByte < ValueToCompare) {
+ ValueToCompare = EraseType->EraseSizeInByte;
+ *EraseTypeRecord = EraseType;
+ }
+
+ break;
+
+ case SearchEraseTypeByBiggestSize:
+ if (EraseType->EraseSizeInByte > ValueToCompare) {
+ ValueToCompare = EraseType->EraseSizeInByte;
+ *EraseTypeRecord = EraseType;
+ }
+
+ break;
+
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ if (IsNodeAtEnd (&Instance->SupportedEraseTypes, &EraseType->NextEraseType) || ExitSearching) {
+ break;
+ }
+
+ EraseType = (SFDP_SUPPORTED_ERASE_TYPE_RECORD *)GetNextNode (&Instance->SupportedEraseTypes, &EraseType->NextEraseType);
+ }
+
+ if (*EraseTypeRecord == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Get the erase block attribute for the target address.
+
+ @param[in] Instance Spi Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and EFI_SPI_IO_PROTOCOL
+ @param[in] FlashRegion The region the flash address belong.
+ @param[in] FlashAddress The target flash address.
+ @param[in] RemainingSize Remaining size to erase.
+ @param[in, out] BlockSizeToErase Input - The block erase size for this continious blocks.
+ Output - The determined block size for erasing.
+ @param[in, out] BlockCountToErase Input - The expected blocks to erase.
+ Output - The determined number of blocks to erase.
+ @param[out] BlockEraseCommand The erase command used for this continious blocks.
+ @param[out] TypicalTime Pointer to receive the typical time in millisecond
+ to erase this erase type size.
+ @param[out] MaximumTimeout Pointer to receive the maximum timeout in millisecond
+ to erase this erase type size.
+
+ @retval EFI_SUCCESS The erase block attribute is returned.
+ @retval EFI_DEVICE_ERROR No valid SFDP discovered.
+ @retval EFI_NOT_FOUND No valud erase block attribute found.
+
+**/
+EFI_STATUS
+GetEraseBlockAttribute (
+ IN SPI_NOR_FLASH_INSTANCE *Instance,
+ IN SFDP_SECTOR_REGION_RECORD *FlashRegion,
+ IN UINT32 FlashAddress,
+ IN UINT32 RemainingSize,
+ IN OUT UINT32 *BlockSizeToErase,
+ IN OUT UINT32 *BlockCountToErase,
+ OUT UINT8 *BlockEraseCommand,
+ OUT UINT32 *TypicalTime,
+ OUT UINT64 *MaximumTimeout
+ )
+{
+ EFI_STATUS Status;
+ SFDP_SUPPORTED_ERASE_TYPE_RECORD *EraseType;
+ UINT32 EraseSize;
+
+ DEBUG ((DEBUG_VERBOSE, "%a: Entry\n", __func__));
+
+ for (EraseSize = SIZE_2GB; EraseSize != 0; EraseSize = EraseSize >> 1) {
+ Status = GetEraseTypeRecord (Instance, SearchEraseTypeBySize, EraseSize, 0, NULL, &EraseType);
+ if (!EFI_ERROR (Status)) {
+ // Validate this erase type.
+ if (((FlashAddress & (EraseType->EraseSizeInByte - 1)) == 0) &&
+ (RemainingSize >= EraseType->EraseSizeInByte))
+ {
+ *BlockSizeToErase = EraseType->EraseSizeInByte;
+ *BlockCountToErase = 1;
+ *BlockEraseCommand = EraseType->EraseInstruction;
+ *TypicalTime = EraseType->EraseTypicalTime;
+ *MaximumTimeout = EraseType->EraseTimeout;
+ Status = EFI_SUCCESS;
+ break;
+ }
+ }
+ }
+
+ if (EraseType == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ DEBUG ((DEBUG_VERBOSE, " Erase address at 0x%08x.\n", FlashAddress));
+ DEBUG ((DEBUG_VERBOSE, " - Erase block size : 0x%08x.\n", *BlockSizeToErase));
+ DEBUG ((DEBUG_VERBOSE, " - Erase block count : 0x%08x.\n", *BlockCountToErase));
+ DEBUG ((DEBUG_VERBOSE, " - Erase block command: 0x%02x.\n", *BlockEraseCommand));
+ DEBUG ((DEBUG_VERBOSE, " - Remaining size to erase: 0x%08x.\n", RemainingSize));
+ DEBUG ((DEBUG_VERBOSE, " - Erase typical time: %d milliseconds.\n", *TypicalTime));
+ DEBUG ((DEBUG_VERBOSE, " - Erase timeout: %d milliseconds.\n", *MaximumTimeout));
+ return EFI_SUCCESS;
+}
+
+/**
+ Get the erase block attribute for the target address.
+
+ @param[in] Instance Spi Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and EFI_SPI_IO_PROTOCOL
+ @param[in] FlashAddress The target flash address.
+ @param[out] FlashRegion The target flash address.
+
+ @retval EFI_SUCCESS The region is returned.
+ @retval EFI_INVALID_PARAMETER FlashAddress is not belong to any region.
+ @retval Otherwise Other errors.
+
+**/
+EFI_STATUS
+GetRegionByFlashAddress (
+ IN SPI_NOR_FLASH_INSTANCE *Instance,
+ IN UINT32 FlashAddress,
+ OUT SFDP_SECTOR_REGION_RECORD **FlashRegion
+ )
+{
+ SFDP_SECTOR_MAP_RECORD *SectorMapRecord;
+ SFDP_SECTOR_REGION_RECORD *RegionRecord;
+
+ DEBUG ((DEBUG_VERBOSE, "%a: Entry\n", __func__));
+
+ SectorMapRecord = Instance->CurrentSectorMap;
+ if (SectorMapRecord == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ RegionRecord = (SFDP_SECTOR_REGION_RECORD *)GetFirstNode (&SectorMapRecord->RegionList);
+ while (TRUE) {
+ if ((FlashAddress >= RegionRecord->RegionAddress) &&
+ (FlashAddress < RegionRecord->RegionAddress + RegionRecord->RegionTotalSize))
+ {
+ *FlashRegion = RegionRecord;
+ return EFI_SUCCESS;
+ }
+
+ if (IsNodeAtEnd (&SectorMapRecord->RegionList, &RegionRecord->NextRegion)) {
+ break;
+ }
+
+ RegionRecord = (SFDP_SECTOR_REGION_RECORD *)GetNextNode (&SectorMapRecord->RegionList, &RegionRecord->NextRegion);
+ }
+
+ return EFI_INVALID_PARAMETER;
+}
+
+/**
+ Build up the Fast Read capability tables. The earlier linked table
+ in the linked list has the faster transfer.
+ NOTE: 1. The Quad input instructions mentioned in 21th DWOWRD
+ are not considered yet.
+ 2. Maximum speed options for certain Fast Read modes are
+ not considered yet. (e.g., 8D-8D-8D or 4S-4D-4D)
+
+ @param[in] Instance SPI Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and
+ EFI_SPI_IO_PROTOCOL.
+
+**/
+VOID
+BuildUpFastReadTable (
+ IN SPI_NOR_FLASH_INSTANCE *Instance
+ )
+{
+ // Build up the standard Fast Read
+ // This will be first picked for the ReadData.
+ // TODO: The mechanism to choose the advance fast read
+ // is not determined yet in this version of
+ // SpiNorFlash driver.
+ CreateSpiFastReadTableEntry (
+ Instance,
+ SPI_FLASH_FAST_READ,
+ 0,
+ SPI_FLASH_FAST_READ_DUMMY * 8
+ );
+
+ // Build up Fast Read table 1S-1S-4S
+ if (Instance->SfdpBasicFlash->FastRead114 != 0) {
+ CreateSpiFastReadTableEntry (
+ Instance,
+ Instance->SfdpBasicFlash->FastRead114Instr,
+ Instance->SfdpBasicFlash->FastRead114ModeClk,
+ Instance->SfdpBasicFlash->FastRead114Dummy
+ );
+ }
+
+ // Build up Fast Read table 1S-2S-2S
+ if (Instance->SfdpBasicFlash->FastRead122 != 0) {
+ CreateSpiFastReadTableEntry (
+ Instance,
+ Instance->SfdpBasicFlash->FastRead122Instr,
+ Instance->SfdpBasicFlash->FastRead122ModeClk,
+ Instance->SfdpBasicFlash->FastRead122Dummy
+ );
+ }
+
+ // Build up Fast Read table 2S-2S-2S
+ if (Instance->SfdpBasicFlash->FastRead222 != 0) {
+ CreateSpiFastReadTableEntry (
+ Instance,
+ Instance->SfdpBasicFlash->FastRead222Instr,
+ Instance->SfdpBasicFlash->FastRead222ModeClk,
+ Instance->SfdpBasicFlash->FastRead222Dummy
+ );
+ }
+
+ // Build up Fast Read table 1S-4S-4S
+ if (Instance->SfdpBasicFlash->FastRead144 != 0) {
+ CreateSpiFastReadTableEntry (
+ Instance,
+ Instance->SfdpBasicFlash->FastRead144Instr,
+ Instance->SfdpBasicFlash->FastRead144ModeClk,
+ Instance->SfdpBasicFlash->FastRead144Dummy
+ );
+ }
+
+ // Build up Fast Read table 4S-4S-4S
+ if (Instance->SfdpBasicFlash->FastRead444 != 0) {
+ CreateSpiFastReadTableEntry (
+ Instance,
+ Instance->SfdpBasicFlash->FastRead444Instr,
+ Instance->SfdpBasicFlash->FastRead444ModeClk,
+ Instance->SfdpBasicFlash->FastRead444Dummy
+ );
+ }
+
+ // Build up Fast Read table 1S-1S-8S
+ if (Instance->SfdpBasicFlash->FastRead118Instr != 0) {
+ CreateSpiFastReadTableEntry (
+ Instance,
+ Instance->SfdpBasicFlash->FastRead118Instr,
+ Instance->SfdpBasicFlash->FastRead118ModeClk,
+ Instance->SfdpBasicFlash->FastRead118Dummy
+ );
+ }
+
+ // Build up Fast Read table 1S-8S-8S
+ if (Instance->SfdpBasicFlash->FastRead188Instr != 0) {
+ CreateSpiFastReadTableEntry (
+ Instance,
+ Instance->SfdpBasicFlash->FastRead188Instr,
+ Instance->SfdpBasicFlash->FastRead188ModeClk,
+ Instance->SfdpBasicFlash->FastRead188Dummy
+ );
+ }
+}
+
+/**
+ This function sets up the erase types supported
+ by this region.
+
+ @param[in] Instance SPI Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and
+ EFI_SPI_IO_PROTOCOL.
+ @param[in] RegionRecord Pointer to SFDP_SECTOR_REGION_RECORD of this
+ regions.
+ @retval EFI_SUCCESS Current sector map configuration is determined.
+ EFI_DEVICE_ERROR Current sector map configuration is not found.
+
+**/
+EFI_STATUS
+SetupRegionEraseInfo (
+ IN SPI_NOR_FLASH_INSTANCE *Instance,
+ IN SFDP_SECTOR_REGION_RECORD *RegionRecord
+ )
+{
+ SFDP_SUPPORTED_ERASE_TYPE_RECORD *SupportedEraseType;
+ UINT32 MinimumEraseSize;
+
+ if (IsListEmpty (&Instance->SupportedEraseTypes)) {
+ DEBUG ((DEBUG_ERROR, "%a: No erase type suppoted on the flash device.\n", __func__));
+ ASSERT (FALSE);
+ return EFI_DEVICE_ERROR;
+ }
+
+ MinimumEraseSize = (UINT32)-1;
+ SupportedEraseType = (SFDP_SUPPORTED_ERASE_TYPE_RECORD *)GetFirstNode (&Instance->SupportedEraseTypes);
+ while (TRUE) {
+ RegionRecord->SupportedEraseType[RegionRecord->SupportedEraseTypeNum] = (UINT8)SupportedEraseType->EraseType;
+ RegionRecord->SupportedEraseTypeNum++;
+ RegionRecord->EraseTypeBySizeBitmap |= SupportedEraseType->EraseSizeInByte;
+ if (MinimumEraseSize > SupportedEraseType->EraseSizeInByte) {
+ MinimumEraseSize = SupportedEraseType->EraseSizeInByte;
+ }
+
+ if (IsNodeAtEnd (&Instance->SupportedEraseTypes, &SupportedEraseType->NextEraseType)) {
+ break;
+ }
+
+ SupportedEraseType = (SFDP_SUPPORTED_ERASE_TYPE_RECORD *)GetNextNode (&Instance->SupportedEraseTypes, &SupportedEraseType->NextEraseType);
+ }
+
+ RegionRecord->SectorSize = MinimumEraseSize;
+ RegionRecord->RegionTotalSize = Instance->FlashDeviceSize;
+ RegionRecord->RegionSectors = RegionRecord->RegionTotalSize / RegionRecord->SectorSize;
+ return EFI_SUCCESS;
+}
+
+/**
+ Create a single flash sector map.
+
+ @param[in] Instance SPI Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and
+ EFI_SPI_IO_PROTOCOL.
+ @retval EFI_SUCCESS Current sector map configuration is determined.
+ EFI_DEVICE_ERROR Current sector map configuration is not found.
+
+**/
+EFI_STATUS
+CreateSingleFlashSectorMap (
+ IN SPI_NOR_FLASH_INSTANCE *Instance
+ )
+{
+ SFDP_SECTOR_MAP_RECORD *SectorMapRecord;
+ SFDP_SECTOR_REGION_RECORD *RegionRecord;
+ UINTN EraseIndex;
+
+ DEBUG ((DEBUG_VERBOSE, "%a: Entry:\n", __func__));
+ SectorMapRecord = (SFDP_SECTOR_MAP_RECORD *)AllocateZeroPool (sizeof (SFDP_SECTOR_MAP_RECORD));
+ if (SectorMapRecord == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a: No memory resource for SFDP_SECTOR_MAP_DETECTION_RECORD.\n", __func__));
+ ASSERT (FALSE);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ // Create SFDP_SECTOR_MAP_RECORD.
+ InitializeListHead (&SectorMapRecord->NextDescriptor);
+ InitializeListHead (&SectorMapRecord->RegionList);
+ SectorMapRecord->ConfigurationId = 0;
+ SectorMapRecord->RegionCount = 1;
+ InsertTailList (&Instance->ConfigurationMapList, &SectorMapRecord->NextDescriptor);
+ DEBUG ((DEBUG_VERBOSE, " Sector map configurations ID : 0x%x\n", SectorMapRecord->ConfigurationId));
+ DEBUG ((DEBUG_VERBOSE, " Sector map configurations regions: %d\n", SectorMapRecord->RegionCount));
+
+ // Create SFDP_SECTOR_MAP_RECORD region record.
+ RegionRecord = (SFDP_SECTOR_REGION_RECORD *)AllocateZeroPool (sizeof (SFDP_SECTOR_REGION_RECORD));
+ if (RegionRecord == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a: No memory resource for SFDP_SECTOR_REGION_RECORD.\n", __func__));
+ ASSERT (FALSE);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InitializeListHead (&RegionRecord->NextRegion);
+
+ RegionRecord->RegionAddress = 0;
+ //
+ // Setup erase information in the region record.
+ //
+ SetupRegionEraseInfo (Instance, RegionRecord);
+
+ InsertTailList (&SectorMapRecord->RegionList, &RegionRecord->NextRegion);
+
+ Instance->CurrentSectorMap = SectorMapRecord;
+
+ DEBUG ((DEBUG_VERBOSE, " Region totoal size : 0x%x\n", RegionRecord->RegionTotalSize));
+ DEBUG ((DEBUG_VERBOSE, " Region sector size : 0x%x\n", RegionRecord->SectorSize));
+ DEBUG ((DEBUG_VERBOSE, " Region sectors : 0x%x\n", RegionRecord->RegionSectors));
+
+ for (EraseIndex = 0; EraseIndex < RegionRecord->SupportedEraseTypeNum; EraseIndex++) {
+ DEBUG ((DEBUG_VERBOSE, " Region erase type supported: 0x%x\n", RegionRecord->SupportedEraseType[EraseIndex]));
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Set EraseBlockBytes in SPI NOR Flash Protocol.
+
+ @param[in] Instance Spi Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and EFI_SPI_IO_PROTOCOL
+
+ @retval EFI_SUCCESS The erase block size is returned.
+ @retval Otherwise Failed to get erase block size.
+
+**/
+EFI_STATUS
+SetSectorEraseBlockSize (
+ IN SPI_NOR_FLASH_INSTANCE *Instance
+ )
+{
+ EFI_STATUS Status;
+ SFDP_SUPPORTED_ERASE_TYPE_RECORD *EraseTypeRecord;
+
+ // Use the smallest size for the sector erase.
+ Status = GetEraseTypeRecord (Instance, SearchEraseTypeBySmallestSize, 0, 0, NULL, &EraseTypeRecord);
+ if (!EFI_ERROR (Status)) {
+ Instance->Protocol.EraseBlockBytes = EraseTypeRecord->EraseSizeInByte;
+ DEBUG ((DEBUG_VERBOSE, " Erase block size = 0x%08x\n", EraseTypeRecord->EraseSizeInByte));
+ }
+
+ return Status;
+}
+
+/**
+ Get the current sector map configuration.
+
+ @param[in] Instance SPI Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and
+ EFI_SPI_IO_PROTOCOL.
+
+ @retval EFI_SUCCESS Current sector map configuration is determined.
+ EFI_DEVICE_ERROR Current sector map configuration is not found.
+
+**/
+EFI_STATUS
+GetCurrentSectorMapConfiguration (
+ IN SPI_NOR_FLASH_INSTANCE *Instance
+ )
+{
+ EFI_STATUS Status;
+ UINT32 TransactionBufferLength;
+ UINT8 AddressLength;
+ BOOLEAN UseAddress;
+ UINT32 DummyBytes;
+ UINT8 ReturnByte;
+ UINT8 ConfigurationId;
+ SFDP_SECTOR_MAP_RECORD *SectorMap;
+ SFDP_SECTOR_MAP_DETECTION_RECORD *CommandEntry;
+
+ Instance->CurrentSectorMap = NULL;
+ if (!Instance->ConfigurationCommandsNeeded) {
+ // No command needed measn only one configuration for the flash device sector map.
+ Instance->CurrentSectorMap = (SFDP_SECTOR_MAP_RECORD *)GetFirstNode (&Instance->ConfigurationMapList);
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Send the command to collect interest bit.
+ //
+ ConfigurationId = 0;
+ CommandEntry = (SFDP_SECTOR_MAP_DETECTION_RECORD *)GetFirstNode (&Instance->ConfigurationCommandList);
+ while (TRUE) {
+ // Check not WIP
+ Status = WaitNotWip (Instance, FixedPcdGet32 (PcdSpiNorFlashOperationDelayMicroseconds), FixedPcdGet32 (PcdSpiNorFlashFixedTimeoutRetryCount));
+
+ // Read configuration byte.
+ AddressLength = SPI_ADDR_3BYTE_ONLY;
+ DummyBytes = 1;
+ if (CommandEntry->CommandAddressLength == SpdfConfigurationCommandAddress4Byte) {
+ AddressLength = SPI_ADDR_4BYTE_ONLY;
+ DummyBytes = 0;
+ }
+
+ UseAddress = TRUE;
+ if (CommandEntry->CommandAddress == SpdfConfigurationCommandAddressNone) {
+ UseAddress = FALSE;
+ }
+
+ TransactionBufferLength = FillWriteBuffer (
+ Instance,
+ CommandEntry->CommandInstruction,
+ DummyBytes,
+ AddressLength,
+ UseAddress,
+ CommandEntry->CommandAddress,
+ 0,
+ NULL
+ );
+ Status = Instance->SpiIo->Transaction (
+ Instance->SpiIo,
+ SPI_TRANSACTION_WRITE_THEN_READ,
+ FALSE,
+ 0,
+ 1,
+ 8,
+ TransactionBufferLength,
+ Instance->SpiTransactionWriteBuffer,
+ 1,
+ &ReturnByte
+ );
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Fails to read the configuration byte.\n", __func__));
+ ASSERT (FALSE);
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Retrieve the interest bit.
+ //
+ if ((ReturnByte & CommandEntry->ConfigurationBitMask) != 0) {
+ ConfigurationId |= 0x01;
+ }
+
+ if (IsNodeAtEnd (&Instance->ConfigurationCommandList, &CommandEntry->NextCommand)) {
+ break;
+ }
+
+ CommandEntry = (SFDP_SECTOR_MAP_DETECTION_RECORD *)GetNextNode (&Instance->ConfigurationCommandList, &CommandEntry->NextCommand);
+ ConfigurationId = ConfigurationId << 1;
+ }
+
+ //
+ // Now we have current activated configuration ID in ConfigurationId.
+ // Walk through ConfigurationMapList to record the activated flash sector
+ // map configuration.
+ //
+ SectorMap = (SFDP_SECTOR_MAP_RECORD *)GetFirstNode (&Instance->ConfigurationMapList);
+ while (TRUE) {
+ if (SectorMap->ConfigurationId == ConfigurationId) {
+ Instance->CurrentSectorMap = SectorMap;
+ break;
+ }
+
+ if (IsNodeAtEnd (&Instance->ConfigurationMapList, &SectorMap->NextDescriptor)) {
+ break;
+ }
+
+ SectorMap = (SFDP_SECTOR_MAP_RECORD *)GetNextNode (&Instance->ConfigurationMapList, &SectorMap->NextDescriptor);
+ }
+
+ if (Instance->CurrentSectorMap == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a: Activated flash sector map is not found!\n", __func__));
+ ASSERT (FALSE);
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Build sector map configurations.
+
+ @param[in] Instance SPI Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and
+ EFI_SPI_IO_PROTOCOL.
+
+ @retval EFI_SUCCESS Records of sector map configuration command and map
+ descriptor are built up successfully.
+ EFI_OUT_OF_RESOURCES Not enough memory resource.
+ EFI_DEVICE_ERROR SFDP Sector Map Parameter is not
+ constructed correctly.
+
+**/
+EFI_STATUS
+BuildSectorMapCommandAndMap (
+ IN SPI_NOR_FLASH_INSTANCE *Instance
+ )
+{
+ SFDP_SECTOR_MAP_TABLE *SfdpSectorMapTable;
+ SFDP_SECTOR_CONFIGURATION_COMMAND *SfdpDetectionCommand;
+ SFDP_SECTOR_MAP_DETECTION_RECORD *CommandEntry;
+ SFDP_SECTOR_CONFIGURATION_MAP *SfdpConfigurationMap;
+ SFDP_SECTOR_MAP_RECORD *SectorMapRecord;
+ SFDP_SECTOR_REGION *SpdfSectorRegion;
+ SFDP_SECTOR_REGION_RECORD *RegionRecord;
+ SFDP_SUPPORTED_ERASE_TYPE_RECORD *SupportedEraseType;
+ UINT8 RegionCount;
+ UINT8 EraseTypeCount;
+ UINT32 MinimumEraseSize;
+ UINT32 RegionAddress;
+
+ SfdpSectorMapTable = Instance->SfdpFlashSectorMap;
+ SfdpConfigurationMap = &SfdpSectorMapTable->ConfigurationMap;
+ SfdpDetectionCommand = &SfdpSectorMapTable->ConfigurationCommand;
+
+ if (SfdpSectorMapTable->GenericHeader.DescriptorType == SFDP_SECTOR_MAP_TABLE_ENTRY_TYPE_MAP) {
+ // No configuration detection commands are needs.
+ Instance->ConfigurationCommandsNeeded = FALSE;
+ } else {
+ DEBUG ((DEBUG_VERBOSE, "%a: Sector map configuration detection command is needed\n", __func__));
+ Instance->ConfigurationCommandsNeeded = TRUE;
+
+ // Go through the section map detection commands.
+ while (TRUE) {
+ CommandEntry = (SFDP_SECTOR_MAP_DETECTION_RECORD *)AllocateZeroPool (sizeof (SFDP_SECTOR_MAP_DETECTION_RECORD));
+ if (CommandEntry == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a: No memory resource for SFDP_SECTOR_MAP_DETECTION_RECORD.\n", __func__));
+ ASSERT (FALSE);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InitializeListHead (&CommandEntry->NextCommand);
+ CommandEntry->CommandAddress = SfdpDetectionCommand->CommandAddress;
+ CommandEntry->CommandAddressLength = (SPDF_CONFIGURATION_COMMAND_ADDR_LENGTH)SfdpDetectionCommand->DetectionCommandAddressLen;
+ CommandEntry->CommandInstruction = (UINT8)SfdpDetectionCommand->DetectionInstruction;
+ CommandEntry->ConfigurationBitMask = (UINT8)SfdpDetectionCommand->ReadDataMask;
+ CommandEntry->LatencyInClock = (UINT8)SfdpDetectionCommand->DetectionLatency;
+ InsertTailList (&Instance->ConfigurationCommandList, &CommandEntry->NextCommand);
+ DEBUG ((DEBUG_VERBOSE, " Command instruction : 0x%x\n", CommandEntry->CommandInstruction));
+ DEBUG ((DEBUG_VERBOSE, " Bit selection : 0x%x\n", CommandEntry->ConfigurationBitMask));
+ DEBUG ((DEBUG_VERBOSE, " Command address : 0x%x\n", CommandEntry->CommandAddress));
+ DEBUG ((DEBUG_VERBOSE, " Command address length: %d\n", CommandEntry->CommandAddressLength));
+ DEBUG ((DEBUG_VERBOSE, " Command latency clocks: %d\n\n", CommandEntry->LatencyInClock));
+ if (SfdpDetectionCommand->DescriptorEnd == SFDP_SECTOR_MAP_TABLE_ENTRY_LAST) {
+ break;
+ }
+
+ SfdpDetectionCommand++;
+ }
+
+ SfdpConfigurationMap = (SFDP_SECTOR_CONFIGURATION_MAP *)SfdpDetectionCommand++;
+ }
+
+ //
+ // Go through the region table pointed in SfdpConfigurationMap.
+ //
+ if (SfdpConfigurationMap->DescriptorType != SFDP_SECTOR_MAP_TABLE_ENTRY_TYPE_MAP) {
+ DEBUG ((DEBUG_ERROR, "%a: Incorrect format of Sector Map Parameter.\n", __func__));
+ ASSERT (FALSE);
+ return EFI_DEVICE_ERROR;
+ }
+
+ while (TRUE) {
+ DEBUG ((DEBUG_VERBOSE, "%a: Sector map configurations:\n", __func__));
+ SectorMapRecord = (SFDP_SECTOR_MAP_RECORD *)AllocateZeroPool (sizeof (SFDP_SECTOR_MAP_RECORD));
+ if (SectorMapRecord == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a: No memory resource for SFDP_SECTOR_MAP_DETECTION_RECORD.\n", __func__));
+ ASSERT (FALSE);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InitializeListHead (&SectorMapRecord->NextDescriptor);
+ InitializeListHead (&SectorMapRecord->RegionList);
+ SectorMapRecord->ConfigurationId = (UINT8)SfdpConfigurationMap->ConfigurationID;
+ SectorMapRecord->RegionCount = (UINT8)SfdpConfigurationMap->RegionCount;
+ InsertTailList (&Instance->ConfigurationMapList, &SectorMapRecord->NextDescriptor);
+ DEBUG ((DEBUG_VERBOSE, " Sector map configurations ID : 0x%x\n", SectorMapRecord->ConfigurationId));
+ DEBUG ((DEBUG_VERBOSE, " Sector map configurations regions: %d\n", SectorMapRecord->RegionCount));
+ SpdfSectorRegion = (SFDP_SECTOR_REGION *)SfdpConfigurationMap + 1;
+ RegionAddress = 0;
+ for (RegionCount = 0; RegionCount < SectorMapRecord->RegionCount; RegionCount++) {
+ RegionRecord = (SFDP_SECTOR_REGION_RECORD *)AllocateZeroPool (sizeof (SFDP_SECTOR_REGION_RECORD));
+ if (RegionRecord == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a: No memory resource for SFDP_SECTOR_MAP_DETECTION_RECORD.\n", __func__));
+ ASSERT (FALSE);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InitializeListHead (&RegionRecord->NextRegion);
+ RegionRecord->RegionTotalSize = (SpdfSectorRegion->RegionSize + 1) * SFDP_SECTOR_REGION_SIZE_UNIT;
+ //
+ // Construct erase type supported for this region.
+ //
+ if (SpdfSectorRegion->EraseType1 != 0) {
+ RegionRecord->SupportedEraseType[RegionRecord->SupportedEraseTypeNum] = SFDP_ERASE_TYPE_1;
+ RegionRecord->SupportedEraseTypeNum++;
+ }
+
+ if (SpdfSectorRegion->EraseType2 != 0) {
+ RegionRecord->SupportedEraseType[RegionRecord->SupportedEraseTypeNum] = SFDP_ERASE_TYPE_2;
+ RegionRecord->SupportedEraseTypeNum++;
+ }
+
+ if (SpdfSectorRegion->EraseType3 != 0) {
+ RegionRecord->SupportedEraseType[RegionRecord->SupportedEraseTypeNum] = SFDP_ERASE_TYPE_3;
+ RegionRecord->SupportedEraseTypeNum++;
+ }
+
+ if (SpdfSectorRegion->EraseType4 != 0) {
+ RegionRecord->SupportedEraseType[RegionRecord->SupportedEraseTypeNum] = SFDP_ERASE_TYPE_4;
+ RegionRecord->SupportedEraseTypeNum++;
+ }
+
+ //
+ // Calculate the sector size and total sectors.
+ //
+ if (IsListEmpty (&Instance->SupportedEraseTypes)) {
+ DEBUG ((DEBUG_ERROR, "%a: No erase type suppoted on the flash device.\n", __func__));
+ ASSERT (FALSE);
+ return EFI_DEVICE_ERROR;
+ }
+
+ MinimumEraseSize = (UINT32)-1;
+ for (EraseTypeCount = 0; EraseTypeCount < RegionRecord->SupportedEraseTypeNum++; EraseTypeCount++) {
+ //
+ // Walk through Instance->SupportedEraseTypes to find the matching erase type and
+ // Use the minimum erase size as the sector size;
+ //
+ SupportedEraseType = (SFDP_SUPPORTED_ERASE_TYPE_RECORD *)GetFirstNode (&Instance->SupportedEraseTypes);
+ while (TRUE) {
+ if (RegionRecord->SupportedEraseType[EraseTypeCount] == SupportedEraseType->EraseType) {
+ // Set erase size bitmap.
+ RegionRecord->EraseTypeBySizeBitmap |= SupportedEraseType->EraseSizeInByte;
+
+ if (MinimumEraseSize > SupportedEraseType->EraseSizeInByte) {
+ MinimumEraseSize = SupportedEraseType->EraseSizeInByte;
+ break;
+ }
+ }
+
+ if (IsNodeAtEnd (&Instance->SupportedEraseTypes, &SupportedEraseType->NextEraseType)) {
+ break;
+ }
+
+ SupportedEraseType = (SFDP_SUPPORTED_ERASE_TYPE_RECORD *)GetNextNode (&Instance->SupportedEraseTypes, &SupportedEraseType->NextEraseType);
+ }
+ }
+
+ RegionRecord->SectorSize = MinimumEraseSize;
+ RegionRecord->RegionSectors = RegionRecord->RegionTotalSize / RegionRecord->SectorSize;
+ RegionRecord->RegionAddress = RegionAddress;
+
+ // Insert to link.
+ InsertTailList (&SectorMapRecord->RegionList, &RegionRecord->NextRegion);
+ DEBUG ((DEBUG_VERBOSE, " Region: %d\n", RegionCount));
+ DEBUG ((DEBUG_VERBOSE, " Region totoal size: 0x%x\n", RegionRecord->RegionTotalSize));
+ DEBUG ((DEBUG_VERBOSE, " Region sector size: 0x%x\n", RegionRecord->SectorSize));
+ DEBUG ((DEBUG_VERBOSE, " Region sectors : 0x%x\n", RegionRecord->RegionSectors));
+ DEBUG ((DEBUG_VERBOSE, " Region erase supported bitmap: 0x%x\n", RegionRecord->EraseTypeBySizeBitmap));
+
+ SpdfSectorRegion++;
+ RegionAddress += RegionRecord->RegionTotalSize;
+ }
+
+ if (SfdpConfigurationMap->DescriptorEnd == SFDP_SECTOR_MAP_TABLE_ENTRY_LAST) {
+ break;
+ }
+
+ SfdpConfigurationMap = (SFDP_SECTOR_CONFIGURATION_MAP *)SpdfSectorRegion;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This routine get Write Enable latch command.
+
+ @param[in] Instance SPI Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and
+ EFI_SPI_IO_PROTOCOL.
+
+**/
+VOID
+GetWriteEnableCommand (
+ IN SPI_NOR_FLASH_INSTANCE *Instance
+ )
+{
+ //
+ // Set Wrtie Enable command.
+ //
+ Instance->WriteEnableLatchRequired = TRUE;
+ Instance->WriteEnableLatchCommand = SPI_FLASH_WREN;
+ if (Instance->SfdpBasicFlash->VolatileStatusBlockProtect == 1) {
+ if (Instance->SfdpBasicFlash->WriteEnableVolatileStatus == 0) {
+ Instance->WriteEnableLatchCommand = SPI_FLASH_WREN_50H;
+ }
+ }
+
+ DEBUG ((DEBUG_ERROR, "%a: Use Write Enable Command 0x%x.\n", __func__, Instance->WriteEnableLatchCommand));
+}
+
+/**
+ This routine returns the desired Fast Read mode.
+
+ @param[in] Instance Spi Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and EFI_SPI_IO_PROTOCOL
+ @param[in,out] FastReadInstruction Fast Read instruction, the input is
+ the default value.
+ @param[in,out] FastReadModeBits The operational mode bits.
+ @param[in,out] FastReadDummyClocks Fast Read wait state (Dummy clocks), the
+ input is the default value.
+
+ @retval EFI_SUCCESS The parameters are updated.
+ @retval EFI_NOT_FOUND No desired Fas Read mode found.
+
+**/
+EFI_STATUS
+GetFastReadParameter (
+ IN SPI_NOR_FLASH_INSTANCE *Instance,
+ IN OUT UINT8 *FastReadInstruction,
+ IN OUT UINT8 *FastReadModeBits,
+ IN OUT UINT8 *FastReadDummyClocks
+ )
+{
+ SFPD_FAST_READ_CAPBILITY_RECORD *FastReadEntry;
+
+ if (IsListEmpty (&Instance->FastReadTableList)) {
+ return EFI_NOT_FOUND;
+ }
+
+ FastReadEntry = (SFPD_FAST_READ_CAPBILITY_RECORD *)GetFirstNode (&Instance->FastReadTableList);
+ *FastReadInstruction = FastReadEntry->FastReadInstruction;
+ *FastReadDummyClocks = FastReadEntry->WaitStates;
+ *FastReadModeBits = FastReadEntry->ModeClocks;
+
+ //
+ // *FastReadOperationClock may be replaced by 8D-8D-8D or 4S-4D-4D Fast Read
+ // mode clock operation mode. Which is not cosidered in the implementation yet.
+ //
+ return EFI_SUCCESS;
+}
+
+/**
+ Return the flash device size from SFDP Basic Flash Parameter Table DWORD 2.
+
+ @param[in] Instance Spi Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and
+ EFI_SPI_IO_PROTOCOL.
+
+ @retval UINT32 Flash device size in byte, zero indicates error.
+
+**/
+UINT32
+SfdpGetFlashSize (
+ IN SPI_NOR_FLASH_INSTANCE *Instance
+ )
+{
+ if (Instance == NULL) {
+ return 0;
+ }
+
+ if ((Instance->SfdpBasicFlash->Density & SFDP_FLASH_MEMORY_DENSITY_4GBIT) == 0) {
+ //
+ // The flash device size is <= 256MB.
+ //
+ return (Instance->SfdpBasicFlash->Density + 1) / 8;
+ }
+
+ //
+ // The flash deivce size is >= 512MB.
+ // Bit [0:30] defines 'N' where the density is computed as 2^N bits.
+ // N must be >=32 according to the SFDP specification.
+ //
+ if ((Instance->SfdpBasicFlash->Density & ~SFDP_FLASH_MEMORY_DENSITY_4GBIT) < 32) {
+ return 0;
+ }
+
+ return (UINT32)RShiftU64 (LShiftU64 (1, Instance->SfdpBasicFlash->Density & ~SFDP_FLASH_MEMORY_DENSITY_4GBIT), 3);
+}
+
+/**
+ Read SFDP Header
+
+ This routine reads the JEDEC SPI Flash Discoverable Parameter header from the
+ SPI chip. Fails if Major Revision is not = 1
+
+ @param[in] Instance Spi Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and EFI_SPI_IO_PROTOCOL
+
+ @retval EFI_SUCCESS Header is filled in
+ @retval EFI_DEVICE_ERROR Invalid data received from SPI flash part.
+
+**/
+EFI_STATUS
+EFIAPI
+ReadSfdpHeader (
+ IN SPI_NOR_FLASH_INSTANCE *Instance
+ )
+{
+ EFI_STATUS Status;
+ UINT32 TransactionBufferLength;
+
+ // Check not WIP
+ Status = WaitNotWip (Instance, FixedPcdGet32 (PcdSpiNorFlashOperationDelayMicroseconds), FixedPcdGet32 (PcdSpiNorFlashFixedTimeoutRetryCount));
+
+ // Read SFDP Header
+ TransactionBufferLength = FillWriteBuffer (
+ Instance,
+ SPI_FLASH_RDSFDP,
+ SPI_FLASH_RDSFDP_DUMMY,
+ SPI_FLASH_RDSFDP_ADDR_BYTES,
+ TRUE,
+ 0,
+ 0,
+ NULL
+ );
+ Status = Instance->SpiIo->Transaction (
+ Instance->SpiIo,
+ SPI_TRANSACTION_WRITE_THEN_READ,
+ FALSE,
+ 0,
+ 1,
+ 8,
+ TransactionBufferLength,
+ Instance->SpiTransactionWriteBuffer,
+ sizeof (SFDP_HEADER),
+ (UINT8 *)&Instance->SfdpHeader
+ );
+ ASSERT_EFI_ERROR (Status);
+ if (!EFI_ERROR (Status)) {
+ // Read Basic Flash Parameter Header
+ if ((Instance->SfdpHeader.Signature != SFDP_HEADER_SIGNATURE) ||
+ (Instance->SfdpHeader.MajorRev != SFDP_SUPPORTED_MAJOR_REVISION))
+ {
+ Status = EFI_DEVICE_ERROR;
+ } else {
+ DEBUG ((DEBUG_VERBOSE, "Total %d parameter headers\n", Instance->SfdpHeader.NumParameterHeaders + 1));
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Read SFDP
+ This routine reads the JEDEC SPI Flash Discoverable Parameters. We just
+ read the necessary tables in this routine.
+
+ @param[in] Instance Spi Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and EFI_SPI_IO_PROTOCOL
+
+ @retval EFI_SUCCESS Header is filled in
+ @retval EFI_DEVICE_ERROR Invalid data received from SPI flash part.
+
+**/
+EFI_STATUS
+ReadSfdp (
+ IN SPI_NOR_FLASH_INSTANCE *Instance
+ )
+{
+ EFI_STATUS Status;
+ SFDP_SUPPORTED_ERASE_TYPE_RECORD *EraseTypeRecord;
+
+ InitializeListHead (&Instance->FastReadTableList);
+ InitializeListHead (&Instance->SupportedEraseTypes);
+ InitializeListHead (&Instance->ConfigurationCommandList);
+ InitializeListHead (&Instance->ConfigurationMapList);
+
+ DEBUG ((DEBUG_VERBOSE, "%a: Entry\n", __func__));
+
+ Status = ReadSfdpHeader (Instance);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Failed to read SFDP header\n", __func__));
+ ASSERT (FALSE);
+ return Status;
+ }
+
+ Status = ReadSfdpBasicParameterTable (Instance);
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ DEBUG ((DEBUG_ERROR, "%a: Failed to read SFDP Basic Parameter Table\n", __func__));
+ ASSERT (FALSE);
+ return Status;
+ }
+
+ Instance->FlashDeviceSize = SfdpGetFlashSize (Instance);
+ DEBUG ((DEBUG_VERBOSE, "%a: Flash Size=0x%X\n", __func__, Instance->FlashDeviceSize));
+ if (Instance->FlashDeviceSize == 0) {
+ ASSERT (FALSE);
+ return Status;
+ }
+
+ Status = ReadSfdpSectorMapParameterTable (Instance);
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ DEBUG ((DEBUG_ERROR, "%a: Failed to read SFDP Sector Map Parameter Table\n", __func__));
+ ASSERT (FALSE);
+ } else if (Status == EFI_NOT_FOUND) {
+ DEBUG ((DEBUG_VERBOSE, "%a: The SPI NOR flash device doesn't have SFDP Sector Map Parameter Table implemented:\n", __func__));
+
+ //
+ // No SFDP Sector Map Parameter Table exist.
+ // Check if device support the uniform 4K erase size.
+ //
+ Instance->Uniform4KEraseSupported = FALSE;
+ if (Instance->SfdpBasicFlash->EraseSizes == SPI_UNIFORM_4K_ERASE_SUPPORTED) {
+ DEBUG ((DEBUG_VERBOSE, "%a: The SPI NOR flash device supports uniform 4K erase.\n", __func__));
+
+ // Check if 4K erase type supported?
+ Status = GetEraseTypeRecord (Instance, SearchEraseTypeBySize, SIZE_4KB, 0, NULL, &EraseTypeRecord);
+ if (Status == EFI_NOT_FOUND) {
+ DEBUG ((DEBUG_ERROR, "However, no corresponding 4K size erase type found.\n"));
+ ASSERT (FALSE);
+ }
+
+ Instance->Uniform4KEraseSupported = TRUE;
+ } else {
+ // Uniform 4K erase unsupported, get the smallest erase block size.
+ DEBUG ((DEBUG_VERBOSE, "%a: The SPI NOR flash device doesn't support uniform 4K erase.\n", __func__));
+ }
+
+ //
+ // Build flash map
+ // Instance->ConfigurationMapList is an empty list because no FDP Sector Map Parameter Table.
+ //
+ CreateSingleFlashSectorMap (Instance);
+ Status = EFI_SUCCESS;
+ }
+
+ return Status;
+}
+
+/**
+ Read SFDP Specific Parameter Header.
+
+ This routine reads the JEDEC SPI Flash Discoverable Parameter header from the
+ SPI chip. Fails if Major Revision is not = SFDP_SUPPORTED_MAJOR_REVISION.
+
+ @param[in] Instance Spi Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and EFI_SPI_IO_PROTOCOL
+ @param[in] SfdpParameterHeader SFDP Header Buffer Pointer
+ @param[in] ParameterIdMsb Most significant byte of parameter ID.
+ @param[in] ParameterIdLsb Lowest significant byte of parameter ID.
+
+ @retval EFI_SUCCESS Header is filled in
+ @retval EFI_DEVICE_ERROR Invalid data received from SPI flash part.
+ @retval EFI_NOT_FOUND Unsupported Parameter Header.
+
+**/
+EFI_STATUS
+EFIAPI
+ReadSfdpParameterHeader (
+ IN SPI_NOR_FLASH_INSTANCE *Instance,
+ IN SFDP_PARAMETER_HEADER *SfdpParameterHeader,
+ IN UINT8 ParameterIdMsb,
+ IN UINT8 ParameterIdLsb
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Index;
+ SFDP_PARAMETER_HEADER LocalSfdpParameterHeader;
+ UINT32 TransactionBufferLength;
+
+ DEBUG ((DEBUG_VERBOSE, "%a: Entry\n", __func__));
+ DEBUG ((DEBUG_VERBOSE, " Looking for Parameter Header %02x:%02x\n", ParameterIdMsb, ParameterIdLsb));
+
+ //
+ // Parse Parameter Headers Starting at size eof SFDP_HEADER.
+ // SfdpHeader.NumParameterHeaders is zero based, 0 means 1 parameter header.
+ //
+ ZeroMem (SfdpParameterHeader, sizeof (SFDP_PARAMETER_HEADER));
+ for (Index = 0; Index < Instance->SfdpHeader.NumParameterHeaders + 1; Index++) {
+ // Check not WIP
+ Status = WaitNotWip (Instance, FixedPcdGet32 (PcdSpiNorFlashOperationDelayMicroseconds), FixedPcdGet32 (PcdSpiNorFlashFixedTimeoutRetryCount));
+ if (!EFI_ERROR (Status)) {
+ TransactionBufferLength = FillWriteBuffer (
+ Instance,
+ SPI_FLASH_RDSFDP,
+ SPI_FLASH_RDSFDP_DUMMY,
+ SPI_FLASH_RDSFDP_ADDR_BYTES,
+ TRUE,
+ sizeof (SFDP_HEADER) + Index * 8, // Parameter Header Index
+ 0,
+ NULL
+ );
+ Status = Instance->SpiIo->Transaction (
+ Instance->SpiIo,
+ SPI_TRANSACTION_WRITE_THEN_READ,
+ FALSE,
+ 0,
+ 1,
+ 8,
+ TransactionBufferLength,
+ Instance->SpiTransactionWriteBuffer,
+ sizeof (LocalSfdpParameterHeader),
+ (UINT8 *)&LocalSfdpParameterHeader
+ );
+ ASSERT_EFI_ERROR (Status);
+ if (!EFI_ERROR (Status)) {
+ // Break if SfdParamHeader is Type 0, Basic SPI Protocol Parameters
+ DEBUG ((
+ DEBUG_VERBOSE,
+ " #%d Parameter Header: %02x:%02x, revision: %d.%d\n",
+ Index,
+ LocalSfdpParameterHeader.IdMsb,
+ LocalSfdpParameterHeader.IdLsb,
+ LocalSfdpParameterHeader.MajorRev,
+ LocalSfdpParameterHeader.MinorRev >= SfdpParameterHeader->MinorRev
+ ));
+ if ((LocalSfdpParameterHeader.IdLsb == ParameterIdLsb) &&
+ (LocalSfdpParameterHeader.IdMsb == ParameterIdMsb) &&
+ (LocalSfdpParameterHeader.MajorRev == (UINT32)SFDP_SUPPORTED_MAJOR_REVISION) &&
+ (LocalSfdpParameterHeader.MinorRev >= SfdpParameterHeader->MinorRev))
+ {
+ CopyMem (
+ (VOID **)SfdpParameterHeader,
+ (VOID **)&LocalSfdpParameterHeader,
+ sizeof (SFDP_PARAMETER_HEADER)
+ );
+ }
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+
+ if (Status != EFI_DEVICE_ERROR) {
+ if ((SfdpParameterHeader->IdLsb != ParameterIdLsb) ||
+ (SfdpParameterHeader->IdMsb != ParameterIdMsb))
+ {
+ DEBUG ((DEBUG_ERROR, " Parameter Header: %02x:%02x is not found.\n", ParameterIdMsb, ParameterIdLsb));
+ Status = EFI_NOT_FOUND;
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Read from SFDP table pointer.
+
+ This routine sends SPI_FLASH_RDSFDP command and reads parameter from the
+ given TablePointer.
+
+ @param[in] Instance Spi Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and EFI_SPI_IO_PROTOCOL
+ @param[in] TablePointer Pointer to read data from SFDP.
+ @param[in] DestBuffer Destination buffer.
+ @param[in] LengthInBytes Length to read.
+
+ @retval EFI_SUCCESS The SPI part size is filled.
+ @retval EFI_DEVICE_ERROR Invalid data received from SPI flash part.
+ @retval Other errors
+
+**/
+EFI_STATUS
+EFIAPI
+SpiReadSfdpPtp (
+ IN SPI_NOR_FLASH_INSTANCE *Instance,
+ IN UINT32 TablePointer,
+ IN VOID *DestBuffer,
+ IN UINT32 LengthInBytes
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Length;
+ UINT8 *CurrentBuffer;
+ UINT32 ByteCounter;
+ UINT32 CurrentAddress;
+ UINT32 MaximumTransferBytes;
+ UINT32 TransactionBufferLength;
+
+ Length = 0;
+ MaximumTransferBytes = Instance->SpiIo->MaximumTransferBytes;
+ CurrentBuffer = (UINT8 *)DestBuffer;
+ for (ByteCounter = 0; ByteCounter < LengthInBytes; ByteCounter += Length) {
+ CurrentAddress = TablePointer + ByteCounter;
+ Length = LengthInBytes - ByteCounter;
+
+ // Length must be MaximumTransferBytes or less
+ if (Length > MaximumTransferBytes) {
+ Length = MaximumTransferBytes;
+ }
+
+ // Check not WIP
+ Status = WaitNotWip (Instance, FixedPcdGet32 (PcdSpiNorFlashOperationDelayMicroseconds), FixedPcdGet32 (PcdSpiNorFlashFixedTimeoutRetryCount));
+
+ // Read Data
+ if (!EFI_ERROR (Status)) {
+ TransactionBufferLength = FillWriteBuffer (
+ Instance,
+ SPI_FLASH_RDSFDP,
+ SPI_FLASH_RDSFDP_DUMMY,
+ SPI_FLASH_RDSFDP_ADDR_BYTES,
+ TRUE,
+ CurrentAddress,
+ 0,
+ NULL
+ );
+ Status = Instance->SpiIo->Transaction (
+ Instance->SpiIo,
+ SPI_TRANSACTION_WRITE_THEN_READ,
+ FALSE,
+ 0,
+ 1,
+ 8,
+ TransactionBufferLength,
+ Instance->SpiTransactionWriteBuffer,
+ Length,
+ CurrentBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Fails to read SFDP parameter.\n", __func__));
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ CurrentBuffer += Length;
+ } else {
+ break;
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Read SFDP Sector Map Parameter into buffer.
+
+ This routine reads the JEDEC SPI Flash Discoverable Parameters from the SPI
+ chip.
+
+ @param[in] Instance Spi Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and EFI_SPI_IO_PROTOCOL
+
+ @retval EFI_SUCCESS The SPI part size is filled.
+ @retval EFI_DEVICE_ERROR Invalid data received from SPI flash part.
+
+**/
+EFI_STATUS
+ReadSfdpSectorMapParameterTable (
+ IN SPI_NOR_FLASH_INSTANCE *Instance
+ )
+{
+ EFI_STATUS Status;
+ SFDP_PARAMETER_HEADER SfdpParamHeader;
+
+ Status = ReadSfdpParameterHeader (
+ Instance,
+ &SfdpParamHeader,
+ SFDP_SECTOR_MAP_PARAMETER_ID_MSB,
+ SFDP_SECTOR_MAP_PARAMETER_ID_LSB
+ );
+ if (!EFI_ERROR (Status)) {
+ // Read Sector Map Parameters. Already know it is MajorRev = SFDP_SUPPORTED_MAJOR_REVISION
+ Instance->SfdpSectorMapByteCount = SfdpParamHeader.Length * sizeof (UINT32);
+ Instance->SfdpFlashSectorMap = AllocateZeroPool (Instance->SfdpSectorMapByteCount);
+ if (Instance->SfdpFlashSectorMap != NULL) {
+ // Read from SFDP Parameter Table Pointer (PTP).
+ Status = SpiReadSfdpPtp (
+ Instance,
+ SfdpParamHeader.TablePointer,
+ (VOID *)Instance->SfdpFlashSectorMap,
+ Instance->SfdpSectorMapByteCount
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = BuildSectorMapCommandAndMap (Instance);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Fails to build sector map command and descriptor.\n", __func__));
+ ASSERT (FALSE);
+ Status = GetCurrentSectorMapConfiguration (Instance);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Fails to get current sector map configuration.\n", __func__));
+ ASSERT (FALSE);
+ }
+ }
+ } else {
+ FreePool (Instance->SfdpFlashSectorMap);
+ Instance->SfdpFlashSectorMap = NULL;
+ DEBUG ((DEBUG_ERROR, "%a: Fails to read SFDP Sector Map Parameter.\n", __func__));
+ ASSERT (FALSE);
+ }
+ } else {
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Fails to allocate memory for reading SFDP Sector Map Parameter.\n", __func__));
+ ASSERT (FALSE);
+ }
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Read SFDP Basic Parameters into buffer.
+
+ This routine reads the JEDEC SPI Flash Discoverable Parameters from the SPI
+ chip.
+
+ @param[in] Instance Spi Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and EFI_SPI_IO_PROTOCOL
+
+ @retval EFI_SUCCESS The SPI part size is filled.
+ @retval EFI_DEVICE_ERROR Invalid data received from SPI flash part.
+ @retval EFI_NOT_FOUND Parameter header is not found.
+
+**/
+EFI_STATUS
+ReadSfdpBasicParameterTable (
+ IN SPI_NOR_FLASH_INSTANCE *Instance
+ )
+{
+ EFI_STATUS Status;
+ SFDP_PARAMETER_HEADER SfdpBasicFlashParamHeader;
+
+ Status = ReadSfdpParameterHeader (
+ Instance,
+ &SfdpBasicFlashParamHeader,
+ SFDP_BASIC_PARAMETER_ID_MSB,
+ SFDP_BASIC_PARAMETER_ID_LSB
+ );
+ if (!EFI_ERROR (Status)) {
+ // Read Basic Flash Parameters. Already know it is MajorRev = SFDP_SUPPORTED_MAJOR_REVISION
+ Instance->SfdpBasicFlashByteCount = SfdpBasicFlashParamHeader.Length * sizeof (UINT32);
+ Instance->SfdpBasicFlash = AllocateZeroPool (Instance->SfdpBasicFlashByteCount);
+ if (Instance->SfdpBasicFlash != NULL) {
+ // Read from SFDP Parameter Table Pointer (PTP).
+ Status = SpiReadSfdpPtp (
+ Instance,
+ SfdpBasicFlashParamHeader.TablePointer,
+ (VOID *)Instance->SfdpBasicFlash,
+ Instance->SfdpBasicFlashByteCount
+ );
+ if (!EFI_ERROR (Status)) {
+ GetWriteEnableCommand (Instance);
+ //
+ // Build the Fast Read capability table according to
+ // the Basic Flash Parameter Table.
+ //
+ BuildUpFastReadTable (Instance);
+ BuildUpEraseTypeTable (Instance); // Build up erase type and size.
+
+ // Set current address bytes to 3-Bytes.
+ Instance->CurrentAddressBytes = 3;
+ } else {
+ FreePool (Instance->SfdpBasicFlash);
+ Instance->SfdpBasicFlash = NULL;
+ DEBUG ((DEBUG_ERROR, "%a: Fails to read SFDP Basic Parameter.\n", __func__));
+ ASSERT (FALSE);
+ }
+ } else {
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Fails to allocate memory for reading SFDP Basic Parameter.\n", __func__));
+ ASSERT (FALSE);
+ }
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Initial SPI_NOR_FLASH_INSTANCE structure.
+
+ @param[in] Instance Pointer to SPI_NOR_FLASH_INSTANCE.
+ EFI_SPI_NOR_FLASH_PROTOCOL and EFI_SPI_IO_PROTOCOL
+
+ @retval EFI_SUCCESS SPI_NOR_FLASH_INSTANCE is initialized according to
+ SPI NOR Flash SFDP specification.
+ @retval EFI_INVALID_PARAMETER Instance = NULL or
+ Instance->SpiIo == NULL or
+ Instance->SpiIo->SpiPeripheral == NULL or
+ Instance->SpiIo->SpiPeripheral->SpiBus == NULL or
+ Instance->SpiIo->SpiPeripheral->SpiBus->ControllerPath.
+ @retval Otherwise Failed to initial SPI_NOR_FLASH_INSTANCE structure.
+
+**/
+EFI_STATUS
+InitialSpiNorFlashSfdpInstance (
+ IN SPI_NOR_FLASH_INSTANCE *Instance
+ )
+{
+ EFI_STATUS Status;
+ EFI_SPI_NOR_FLASH_PROTOCOL *Protocol;
+
+ if (Instance == NULL) {
+ DEBUG ((DEBUG_ERROR, "%a: Instance is NULL.\n", __func__));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Instance->SpiIo == NULL) ||
+ (Instance->SpiIo->SpiPeripheral == NULL) ||
+ (Instance->SpiIo->SpiPeripheral->SpiBus == NULL)
+ )
+ {
+ DEBUG ((DEBUG_ERROR, "%a: One of SpiIo, SpiPeripheral and SpiBus is NULL.\n", __func__));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance->Signature = SPI_NOR_FLASH_SIGNATURE;
+
+ // Allocate write buffer for SPI IO transactions with extra room for Opcode
+ // and Address with 10 bytes extra room.
+ Instance->SpiTransactionWriteBuffer =
+ AllocatePool (Instance->SpiIo->MaximumTransferBytes + 10);
+
+ Protocol = &Instance->Protocol;
+ Protocol->SpiPeripheral = Instance->SpiIo->SpiPeripheral;
+ Protocol->GetFlashid = GetFlashId;
+ Protocol->ReadData = ReadData; // Fast Read transfer
+ Protocol->LfReadData = LfReadData; // Normal Read transfer
+ Protocol->ReadStatus = ReadStatus;
+ Protocol->WriteStatus = WriteStatus;
+ Protocol->WriteData = WriteData;
+ Protocol->Erase = Erase;
+ Status = Protocol->GetFlashid (Protocol, (UINT8 *)&Protocol->Deviceid);
+ ASSERT_EFI_ERROR (Status);
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: Flash ID: Manufacturer=0x%02X, Device=0x%02X%02X\n",
+ __func__,
+ Protocol->Deviceid[0],
+ Protocol->Deviceid[1],
+ Protocol->Deviceid[2]
+ )
+ );
+
+ Status = ReadSfdp (Instance);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Failed to Read SFDP\n", __func__));
+ ASSERT (FALSE);
+ }
+
+ // Get flash deivce size from SFDP.
+ Protocol->FlashSize = SfdpGetFlashSize (Instance);
+ DEBUG ((DEBUG_VERBOSE, "%a: Flash Size=0x%X\n", __func__, Protocol->FlashSize));
+ if (Protocol->FlashSize == 0) {
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ // Set flash erase block size.
+ Status = SetSectorEraseBlockSize (Instance);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Fails to get the smallest erase block size.\n", __func__));
+ ASSERT (FALSE);
+ }
+
+ return Status;
+}
diff --git a/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpDxe.c b/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpDxe.c new file mode 100644 index 0000000000..d8b86ddd8a --- /dev/null +++ b/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpDxe.c @@ -0,0 +1,261 @@ +/** @file
+ SPI NOR Flash JEDEC Serial Flash Discoverable Parameters (SFDP)
+ DXE driver.
+
+ Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Revision Reference:
+ - JEDEC Standard, JESD216F.02
+ https://www.jedec.org/document_search?search_api_views_fulltext=JESD216
+
+ @par Glossary:
+ - SFDP - Serial Flash Discoverable Parameters
+ - PTP - Parameter Table Pointer
+**/
+
+#include <Base.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Protocol/SpiConfiguration.h>
+#include <Protocol/SpiNorFlash.h>
+#include <Protocol/SpiIo.h>
+#include <IndustryStandard/SpiNorFlashJedecSfdp.h>
+#include "SpiNorFlash.h"
+#include "SpiNorFlashJedecSfdpInternal.h"
+
+/**
+ Function to create SPI_NOR_FLASH_INSTANCE for this SPI part.
+
+ @param[in] SpiIoHandle The handle with SPI I/O protocol installed.
+
+ @retval EFI_SUCCESS Succeed.
+ @retval EFI_OUT_OF_RESOURCES Not enough resource to create SPI_NOR_FLASH_INSTANCE.
+ @retval otherwise Fail to create SPI NOR Flash SFDP Instance
+**/
+EFI_STATUS
+CreateSpiNorFlashSfdpInstance (
+ IN EFI_HANDLE SpiIoHandle
+ )
+{
+ EFI_STATUS Status;
+ SPI_NOR_FLASH_INSTANCE *Instance;
+
+ // Allocate SPI_NOR_FLASH_INSTANCE Instance.
+ Instance = AllocateZeroPool (sizeof (SPI_NOR_FLASH_INSTANCE));
+ ASSERT (Instance != NULL);
+ if (Instance == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ // Locate the SPI IO Protocol
+ Status = gBS->HandleProtocol (
+ SpiIoHandle,
+ &gEdk2JedecSfdpSpiDxeDriverGuid,
+ (VOID **)&Instance->SpiIo
+ );
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Fail to locate SPI I/O protocol\n", __func__));
+ FreePool (Instance);
+ } else {
+ Status = InitialSpiNorFlashSfdpInstance (Instance);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Fail to initial SPI_NOR_FLASH_INSTANCE.\n", __func__));
+ FreePool (Instance);
+ } else {
+ // Install SPI NOR Flash Protocol.
+ Status = gBS->InstallProtocolInterface (
+ &Instance->Handle,
+ &gEfiSpiNorFlashProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &Instance->Protocol
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Fail to Install gEfiSpiNorFlashProtocolGuid protocol.\n", __func__));
+ FreePool (Instance);
+ }
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Callback function executed when the EFI_SPI_IO_PROTOCOL
+ protocol interface is installed.
+
+ @param[in] Event Event whose notification function is being invoked.
+ @param[out] Context Pointer to SPI I/O protocol GUID.
+
+**/
+VOID
+EFIAPI
+SpiIoProtocolInstalledCallback (
+ IN EFI_EVENT Event,
+ OUT VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ UINTN InstanceBufferSize;
+ EFI_HANDLE InstanceBuffer;
+
+ DEBUG ((DEBUG_INFO, "%a: Entry.\n", __func__));
+ InstanceBufferSize = sizeof (EFI_HANDLE);
+ Status = gBS->LocateHandle (
+ ByRegisterNotify,
+ (EFI_GUID *)Context,
+ NULL,
+ &InstanceBufferSize,
+ &InstanceBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Can't locate SPI I/O protocol.\n"));
+ DEBUG ((DEBUG_INFO, "%a: Exit.\n", __func__));
+ return;
+ }
+
+ CreateSpiNorFlashSfdpInstance (InstanceBuffer);
+ DEBUG ((DEBUG_INFO, "%a: Exit.\n", __func__));
+ return;
+}
+
+/**
+ Register for the later installed SPI I/O protocol notification.
+
+ @retval EFI_SUCCESS Succeed.
+ @retval otherwise Fail to register SPI I/O protocol installed
+ notification.
+**/
+EFI_STATUS
+RegisterSpioProtocolNotification (
+ VOID
+ )
+{
+ EFI_EVENT Event;
+ EFI_STATUS Status;
+ VOID *Registration;
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ SpiIoProtocolInstalledCallback,
+ NULL,
+ &Event
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Fail to create event for the SPI I/O Protocol installation.", __func__));
+ return Status;
+ }
+
+ Status = gBS->RegisterProtocolNotify (
+ &gEdk2JedecSfdpSpiDxeDriverGuid,
+ Event,
+ &Registration
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Fail to register event for the SPI I/O Protocol installation.", __func__));
+ } else {
+ DEBUG ((DEBUG_INFO, "%a: Notification for SPI I/O Protocol installation was registered.", __func__));
+ }
+
+ return Status;
+}
+
+/**
+ Entry point of the Macronix SPI NOR Flash driver.
+
+ @param ImageHandle Image handle of this driver.
+ @param SystemTable Pointer to standard EFI system table.
+
+ @retval EFI_SUCCESS Succeed.
+ @retval EFI_NOT_FOUND No gEdk2JedecSfdpSpiSmmDriverGuid installed on
+ system yet.
+ @retval EFI_OUT_OF_RESOURCES Not enough resource for SPI NOR Flash JEDEC SFDP
+ initialization.
+ @retval Otherwise Other errors.
+**/
+EFI_STATUS
+EFIAPI
+SpiNorFlashJedecSfdpDxeEntry (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE *InstanceBuffer;
+ UINTN InstanceIndex;
+ UINTN InstanceBufferSize;
+
+ DEBUG ((DEBUG_INFO, "%a - ENTRY\n", __func__));
+
+ //
+ // Register notification for the later SPI I/O protocol installation.
+ //
+ RegisterSpioProtocolNotification ();
+ DEBUG ((DEBUG_INFO, "Check if there were already some gEdk2JedecSfdpSpiDxeDriverGuid handles installed.\n"));
+
+ //
+ // Check if there were already some gEdk2JedecSfdpSpiDxeDriverGuid
+ // handles installed.
+ //
+ // Locate the SPI I/O Protocol for the SPI flash part
+ // that supports JEDEC SFDP specification.
+ //
+ InstanceBufferSize = 0;
+ InstanceBuffer = NULL;
+ Status = gBS->LocateHandle (
+ ByProtocol,
+ &gEdk2JedecSfdpSpiDxeDriverGuid,
+ NULL,
+ &InstanceBufferSize,
+ InstanceBuffer
+ );
+ if (Status == EFI_NOT_FOUND) {
+ DEBUG ((
+ DEBUG_INFO,
+ "No gEdk2JedecSfdpSpiSmmDriverGuid handles found at the moment, wait for the notification of SPI I/O protocol installation.\n"
+ ));
+ DEBUG ((DEBUG_INFO, "%a: EXIT - Status=%r\n", __func__, Status));
+ return EFI_SUCCESS;
+ } else if (Status == EFI_BUFFER_TOO_SMALL) {
+ InstanceBuffer = (EFI_HANDLE *)AllocateZeroPool (InstanceBufferSize);
+ ASSERT (InstanceBuffer != NULL);
+ if (InstanceBuffer == NULL) {
+ DEBUG ((DEBUG_ERROR, "Not enough resource for gEdk2JedecSfdpSpiDxeDriverGuid handles.\n"));
+ DEBUG ((DEBUG_INFO, "%a: EXIT - Status=%r\n", __func__, Status));
+ return EFI_OUT_OF_RESOURCES;
+ }
+ } else if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Error to locate gEdk2JedecSfdpSpiDxeDriverGuid - Status = %r.\n", Status));
+ DEBUG ((DEBUG_INFO, "%a: EXIT - Status=%r\n", __func__, Status));
+ return Status;
+ }
+
+ Status = gBS->LocateHandle (
+ ByProtocol,
+ &gEdk2JedecSfdpSpiDxeDriverGuid,
+ NULL,
+ &InstanceBufferSize,
+ InstanceBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Fail to locate all gEdk2JedecSfdpSpiDxeDriverGuid handles.\n"));
+ DEBUG ((DEBUG_INFO, "%a: EXIT - Status=%r\n", __func__, Status));
+ return Status;
+ }
+
+ DEBUG ((DEBUG_INFO, "%d of gEdk2JedecSfdpSpiDxeDriverGuid are found.\n", InstanceBufferSize / sizeof (EFI_HANDLE)));
+ for (InstanceIndex = 0; InstanceIndex < InstanceBufferSize / sizeof (EFI_HANDLE); InstanceIndex++) {
+ Status = CreateSpiNorFlashSfdpInstance (*(InstanceBuffer + InstanceIndex));
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Fail to create SPI NOR Flash SFDP instance #%d.\n", InstanceIndex));
+ }
+ }
+
+ DEBUG ((DEBUG_INFO, "%a: EXIT - Status=%r\n", __func__, Status));
+ return Status;
+}
diff --git a/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpDxe.inf b/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpDxe.inf new file mode 100644 index 0000000000..26dbff3c8d --- /dev/null +++ b/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpDxe.inf @@ -0,0 +1,64 @@ +## @file
+# The SPI NOR Flash JEDEC Serial Flash Discoverable Parameters (SFDP)
+# DXE driver INF file.
+#
+# Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+# @par Revision Reference:
+# - JEDEC Standard, JESD216F.02
+# https://www.jedec.org/document_search?search_api_views_fulltext=JESD216
+#
+# @par Glossary:
+# - SFDP - Serial Flash Discoverable Parameters
+# - PTP - Parameter Table Pointer
+##
+
+[Defines]
+ INF_VERSION = 1.25
+ BASE_NAME = SpiNorFlashJedecSfdpDxe
+ FILE_GUID = 0DC9C2C7-D450-41BA-9CF7-D2090C35A797
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 0.1
+ PI_SPECIFICATION_VERSION = 1.10
+ ENTRY_POINT = SpiNorFlashJedecSfdpDxeEntry
+ MODULE_UNI_FILE = SpiNorFlashJedecSfdpDxe.uni
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ DevicePathLib
+ MemoryAllocationLib
+ TimerLib
+ UefiDriverEntryPoint
+ UefiBootServicesTableLib
+
+[Sources]
+ SpiNorFlashJedecSfdpDxe.c
+ SpiNorFlash.c
+ SpiNorFlashJedecSfdp.c
+ SpiNorFlashJedecSfdpInternal.h
+ SpiNorFlash.h
+
+[Protocols]
+ gEfiSpiNorFlashProtocolGuid ## PROCUDES
+
+[FixedPcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdSpiNorFlashOperationRetryCount
+ gEfiMdeModulePkgTokenSpaceGuid.PcdSpiNorFlashFixedTimeoutRetryCount
+ gEfiMdeModulePkgTokenSpaceGuid.PcdSpiNorFlashOperationDelayMicroseconds
+
+[Guids]
+ gEdk2JedecSfdpSpiDxeDriverGuid
+
+[Depex]
+ gEdk2JedecSfdpSpiDxeDriverGuid
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ SpiNorFlashJedecSfdpExtra.uni
diff --git a/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpDxe.uni b/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpDxe.uni new file mode 100644 index 0000000000..130e958cdb --- /dev/null +++ b/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpDxe.uni @@ -0,0 +1,13 @@ +// /** @file
+// SPI NOR Flash SFDP Localized Strings and Content.
+//
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "EDK2 SPI NOR FLASH SFDP DXE driver"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This driver provides SPI NOR FLASH Serial Flash Discoverable Parameter (SFDP) compatible flash device capability discovery."
+
diff --git a/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpExtra.uni b/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpExtra.uni new file mode 100644 index 0000000000..4703cf8b23 --- /dev/null +++ b/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpExtra.uni @@ -0,0 +1,11 @@ +// /** @file
+// SPI NOR Flash SFDP Localized Strings and Content.
+//
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US "SPI NOR Flash driver for JEDEC Serial Flash Discoverable Parameters (SFDP) compliant SPI Flash Memory"
diff --git a/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpInternal.h b/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpInternal.h new file mode 100644 index 0000000000..309d8dcea7 --- /dev/null +++ b/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpInternal.h @@ -0,0 +1,299 @@ +/** @file
+ SPI NOR flash driver internal definitions.
+
+ Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef SPI_NOR_FLASH_INSTANCE_H_
+#define SPI_NOR_FLASH_INSTANCE_H_
+
+#include <PiDxe.h>
+#include <Protocol/SpiNorFlash.h>
+#include <Protocol/SpiIo.h>
+#include <IndustryStandard/SpiNorFlashJedecSfdp.h>
+
+#define SPI_NOR_FLASH_SIGNATURE SIGNATURE_32 ('s', 'n', 'f', 'm')
+
+#define SPI_NOR_FLASH_FROM_THIS(a) CR (a, SPI_NOR_FLASH_INSTANCE, Protocol, SPI_NOR_FLASH_SIGNATURE)
+
+typedef struct {
+ LIST_ENTRY NextFastReadCap; ///< Link list to next Fast read capability
+ UINT8 FastReadInstruction; ///< Fast read instruction.
+ UINT8 ModeClocks; ///< Fast read clock.
+ UINT8 WaitStates; ///< Fast read wait dummy clocks
+} SFPD_FAST_READ_CAPBILITY_RECORD;
+
+typedef struct {
+ LIST_ENTRY NextEraseType; ///< Link list to next erase type.
+ UINT16 EraseType; ///< Erase type this flash device supports.
+ UINT8 EraseInstruction; ///< Erase instruction
+ UINT32 EraseSizeInByte; ///< The size of byte in 2^EraseSize the erase type command
+ ///< can erase.
+ UINT32 EraseTypicalTime; ///< Time the device typically takes to erase this type
+ ///< size.
+ UINT64 EraseTimeout; ///< Maximum typical erase timeout.
+} SFDP_SUPPORTED_ERASE_TYPE_RECORD;
+
+typedef enum {
+ SearchEraseTypeByType = 1,
+ SearchEraseTypeByCommand,
+ SearchEraseTypeBySize,
+ SearchEraseTypeBySmallestSize,
+ SearchEraseTypeByBiggestSize
+} SFDP_SEARCH_ERASE_TYPE;
+
+typedef struct {
+ LIST_ENTRY NextCommand; ///< Link list to next detection command.
+ UINT32 CommandAddress; ///< Address to issue the command.
+ UINT8 CommandInstruction; ///< Detection command instruction.
+ UINT8 LatencyInClock; ///< Command latency in clocks.
+ SPDF_CONFIGURATION_COMMAND_ADDR_LENGTH CommandAddressLength; ///< Adddress length of detection command.
+ UINT8 ConfigurationBitMask; ///< The interest bit of the byte data retunred
+ ///< after sending the detection command.
+} SFDP_SECTOR_MAP_DETECTION_RECORD;
+
+typedef struct {
+ LIST_ENTRY NextRegion; ///< Link list to the next region.
+ UINT32 RegionAddress; ///< Region starting address.
+ UINT32 RegionTotalSize; ///< Region total size in bytes.
+ UINT32 RegionSectors; ///< Sectors in this region.
+ UINT32 SectorSize; ///< Sector size in byte (Minimum blcok erase size)
+ UINT8 SupportedEraseTypeNum; ///< Number of erase type supported.
+ UINT8 SupportedEraseType[SFDP_ERASE_TYPES_NUMBER]; ///< Erase types supported.
+ UINT32 EraseTypeBySizeBitmap; ///< The bitmap of supoprted srase block sizes.
+ ///< from big to small.
+} SFDP_SECTOR_REGION_RECORD;
+
+typedef struct {
+ LIST_ENTRY NextDescriptor; ///< Link list to next flash map descriptor.
+ UINT8 ConfigurationId; ///< The ID of this configuration.
+ UINT8 RegionCount; ///< The regions of this sector map configuration.
+ LIST_ENTRY RegionList; ///< The linked list of the regions.
+} SFDP_SECTOR_MAP_RECORD;
+
+typedef struct {
+ UINTN Signature;
+ EFI_HANDLE Handle;
+ EFI_SPI_NOR_FLASH_PROTOCOL Protocol;
+ EFI_SPI_IO_PROTOCOL *SpiIo;
+ UINT32 SfdpBasicFlashByteCount;
+ UINT32 SfdpSectorMapByteCount;
+ SFDP_BASIC_FLASH_PARAMETER *SfdpBasicFlash;
+ SFDP_SECTOR_MAP_TABLE *SfdpFlashSectorMap;
+ UINT8 *SpiTransactionWriteBuffer;
+ UINT32 SpiTransactionWriteBufferIndex;
+ //
+ // SFDP information.
+ //
+ SFDP_HEADER SfdpHeader; ///< SFDP header.
+ UINT32 FlashDeviceSize; ///< The total size of this flash device.
+ UINT8 CurrentAddressBytes; ///< The current address bytes.
+
+ //
+ // This is a linked list in which the Fast Read capability tables
+ // are linked from the low performance transfer to higher performance
+ // transfer. The SPI read would use the first Fast Read entry for
+ // SPI read operation.
+ //
+ LIST_ENTRY FastReadTableList;
+
+ LIST_ENTRY SupportedEraseTypes; ///< The linked list of supported erase types.
+ BOOLEAN Uniform4KEraseSupported; ///< The flash device supoprts uniform 4K erase.
+ BOOLEAN WriteEnableLatchRequired; ///< Wether Write Enable Latch is supported.
+ UINT8 WriteEnableLatchCommand; ///< Write Enable Latch command.
+ //
+ // Below is the linked list of flash device sector
+ // map configuration detection command and map descriptors.
+ //
+ BOOLEAN ConfigurationCommandsNeeded; ///< Indicates whether sector map
+ ///< configuration detection is
+ ///< required.
+ LIST_ENTRY ConfigurationCommandList; ///< The linked list of configuration
+ ///< detection command sequence.
+ LIST_ENTRY ConfigurationMapList; ///< The linked list of configuration
+ ///< map descriptors.
+ SFDP_SECTOR_MAP_RECORD *CurrentSectorMap; ///< The current activated flash device
+ ///< sector map.
+} SPI_NOR_FLASH_INSTANCE;
+
+/**
+ This routine returns the desired Fast Read mode.
+
+ @param[in] Instance Spi Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and EFI_SPI_IO_PROTOCOL
+ @param[in,out] FastReadInstruction Fast Read instruction, the input is
+ the default value.
+ @param[in,out] FastReadOperationClock Fast Read operation clock, the input is
+ the default value.
+ @param[in,out] FastReadDummyClocks Fast Read wait state (Dummy clocks), the
+ input is the default value.
+ @retval EFI_SUCCESS The parameters are updated.
+ @retval EFI_NOT_FOUND No desired Fas Read mode found.
+
+**/
+EFI_STATUS
+GetFastReadParameter (
+ IN SPI_NOR_FLASH_INSTANCE *Instance,
+ IN OUT UINT8 *FastReadInstruction,
+ IN OUT UINT8 *FastReadOperationClock,
+ IN OUT UINT8 *FastReadDummyClocks
+ );
+
+/**
+ Read SFDP parameters into buffer
+
+ This routine reads the JEDEC SPI Flash Discoverable Parameters from the SPI
+ chip.
+
+ @param[in] Instance Spi Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and EFI_SPI_IO_PROTOCOL
+
+ @retval EFI_SUCCESS The SPI part size is filled.
+ @retval EFI_DEVICE_ERROR Invalid data received from SPI flash part.
+
+**/
+EFI_STATUS
+ReadSfdpBasicParameterTable (
+ IN SPI_NOR_FLASH_INSTANCE *Instance
+ );
+
+/**
+ Read SFDP Sector Map Parameter into buffer
+
+ This routine reads the JEDEC SPI Flash Discoverable Parameters from the SPI
+ chip.
+
+ @param[in] Instance Spi Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and EFI_SPI_IO_PROTOCOL
+
+ @retval EFI_SUCCESS The SPI part size is filled.
+ @retval EFI_DEVICE_ERROR Invalid data received from SPI flash part.
+
+**/
+EFI_STATUS
+ReadSfdpSectorMapParameterTable (
+ IN SPI_NOR_FLASH_INSTANCE *Instance
+ );
+
+/**
+ Return flash device size from SFDP Basic Flash Parameter Table DWORD 2
+
+ @param[in] Instance Spi Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and
+ EFI_SPI_IO_PROTOCOL.
+
+* @retval UINT32 Flash device size in byte, zero indicates error.
+
+**/
+UINT32
+SfdpGetFlashSize (
+ IN SPI_NOR_FLASH_INSTANCE *Instance
+ );
+
+/**
+ Read SFDP
+ This routine reads the JEDEC SPI Flash Discoverable Parameters. We just
+ read the necessary tables in this routine.
+
+ @param[in] Instance Spi Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and EFI_SPI_IO_PROTOCOL
+
+ @retval EFI_SUCCESS Header is filled in
+ @retval EFI_DEVICE_ERROR Invalid data received from SPI flash part.
+
+**/
+EFI_STATUS
+ReadSfdp (
+ IN SPI_NOR_FLASH_INSTANCE *Instance
+ );
+
+/**
+ Set EraseBlockBytes in SPI NOR Flash Protocol
+
+ @param[in] Instance Spi Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and EFI_SPI_IO_PROTOCOL
+
+ @retval EFI_SUCCESS The erase block size is returned.
+ @retval Otherwise Failed to get erase block size.
+
+**/
+EFI_STATUS
+SetSectorEraseBlockSize (
+ IN SPI_NOR_FLASH_INSTANCE *Instance
+ );
+
+/**
+ Get the erase block attribute for the target address.
+
+ @param[in] Instance Spi Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and EFI_SPI_IO_PROTOCOL
+ @param[in] FlashRegion The region the flash address belong.
+ @param[in] FlashAddress The target flash address.
+ @param[in] RemainingSize Remaining size to erase.
+ @param[in, out] BlockSizeToErase Input - The block erase size for this continious blocks.
+ Output - The determined block size for erasing.
+ @param[in, out] BlockCountToErase Input - The expected blocks to erase.
+ Output - The determined number of blocks to erase.
+ @param[out] BlockEraseCommand The erase command used for this continious blocks.
+ @param[out] TypicalTime Pointer to receive the typical time in millisecond
+ to erase this erase type size.
+ @param[out] MaximumTimeout Pointer to receive the maximum timeout in millisecond
+ to erase this erase type size.
+ @retval EFI_SUCCESS The erase block attribute is returned.
+ @retval EFI_DEVICE_ERROR No valid SFDP discovered.
+ @retval EFI_NOT_FOUND No valud erase block attribute found.
+
+**/
+EFI_STATUS
+GetEraseBlockAttribute (
+ IN SPI_NOR_FLASH_INSTANCE *Instance,
+ IN SFDP_SECTOR_REGION_RECORD *FlashRegion,
+ IN UINT32 FlashAddress,
+ IN UINT32 RemainingSize,
+ IN OUT UINT32 *BlockSizeToErase,
+ IN OUT UINT32 *BlockCountToErase,
+ OUT UINT8 *BlockEraseCommand,
+ OUT UINT32 *TypicalTime,
+ OUT UINT64 *MaximumTimeout
+ );
+
+/**
+ Get the erase block attribute for the target address.
+
+ @param[in] Instance Spi Nor Flash Instance data with pointer to
+ EFI_SPI_NOR_FLASH_PROTOCOL and EFI_SPI_IO_PROTOCOL
+ @param[in] FlashAddress The target flash address.
+ @param[out] FlashRegion The target flash address.
+
+ @retval EFI_SUCCESS The region is returned.
+ @retval EFI_INVALID_PARAMETER FlashAddress is not belong to any region.
+ @retval EFI_INVALID_PARAMETER Other errors.
+
+**/
+EFI_STATUS
+GetRegionByFlashAddress (
+ IN SPI_NOR_FLASH_INSTANCE *Instance,
+ IN UINT32 FlashAddress,
+ OUT SFDP_SECTOR_REGION_RECORD **FlashRegion
+ );
+
+/**
+ Initial SPI_NOR_FLASH_INSTANCE structure.
+
+ @param[in] Instance Pointer to SPI_NOR_FLASH_INSTANCE.
+ EFI_SPI_NOR_FLASH_PROTOCOL and EFI_SPI_IO_PROTOCOL
+
+ @retval EFI_SUCCESS SPI_NOR_FLASH_INSTANCE is initialized according to
+ SPI NOR Flash SFDP specification.
+ @retval Otherwisw Failed to initial SPI_NOR_FLASH_INSTANCE structure.
+
+**/
+EFI_STATUS
+InitialSpiNorFlashSfdpInstance (
+ IN SPI_NOR_FLASH_INSTANCE *Instance
+ );
+
+#endif // SPI_NOR_FLASH_INSTANCE_H_
diff --git a/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpSmm.c b/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpSmm.c new file mode 100644 index 0000000000..c9fe44704f --- /dev/null +++ b/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpSmm.c @@ -0,0 +1,234 @@ +/** @file
+ SPI NOR Flash JEDEC Serial Flash Discoverable Parameters (SFDP)
+ SMM driver.
+
+ Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Revision Reference:
+ - JEDEC Standard, JESD216F.02
+ https://www.jedec.org/document_search?search_api_views_fulltext=JESD216
+
+ @par Glossary:
+ - SFDP - Serial Flash Discoverable Parameters
+ - PTP - Parameter Table Pointer
+**/
+#include <Base.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/SmmServicesTableLib.h>
+#include <Protocol/SpiSmmConfiguration.h>
+#include <Protocol/SpiSmmNorFlash.h>
+#include <Protocol/SpiIo.h>
+#include <IndustryStandard/SpiNorFlashJedecSfdp.h>
+#include "SpiNorFlash.h"
+#include "SpiNorFlashJedecSfdpInternal.h"
+
+/**
+ Function to create SPI_NOR_FLASH_INSTANCE for this SPI part.
+
+ @param[in] SpiIoHandle The handle with SPI I/O protocol installed.
+
+ @retval EFI_SUCCESS Succeed.
+ @retval EFI_OUT_OF_RESOURCES Not enough resource to create SPI_NOR_FLASH_INSTANCE.
+ @retval otherwise Fail to create SPI NOR Flash SFDP Instance
+**/
+EFI_STATUS
+CreateSpiNorFlashSfdpInstance (
+ IN EFI_HANDLE SpiIoHandle
+ )
+{
+ EFI_STATUS Status;
+ SPI_NOR_FLASH_INSTANCE *Instance;
+
+ // Allocate SPI_NOR_FLASH_INSTANCE Instance.
+ Instance = AllocateZeroPool (sizeof (SPI_NOR_FLASH_INSTANCE));
+ ASSERT (Instance != NULL);
+ if (Instance == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ // Locate the SPI IO Protocol.
+ Status = gSmst->SmmHandleProtocol (
+ SpiIoHandle,
+ &gEdk2JedecSfdpSpiSmmDriverGuid,
+ (VOID **)&Instance->SpiIo
+ );
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Fail to locate SPI I/O protocol.\n", __func__));
+ FreePool (Instance);
+ } else {
+ Status = InitialSpiNorFlashSfdpInstance (Instance);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Fail to initial SPI_NOR_FLASH_INSTANCE.\n", __func__));
+ FreePool (Instance);
+ } else {
+ // Install SPI NOR Flash Protocol.
+ Status = gSmst->SmmInstallProtocolInterface (
+ &Instance->Handle,
+ &gEfiSpiSmmNorFlashProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &Instance->Protocol
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Fail to Install gEfiSpiSmmNorFlashProtocolGuid protocol.\n", __func__));
+ FreePool (Instance);
+ }
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Callback function executed when the EFI_SPI_IO_PROTOCOL
+ protocol interface is installed.
+
+ @param[in] Protocol Points to the protocol's unique identifier.
+ @param[in] Interface Points to the interface instance.
+ @param[in] Handle The handle on which the interface was installed.
+
+ @return Status Code
+
+**/
+EFI_STATUS
+EFIAPI
+SpiIoProtocolInstalledCallback (
+ IN CONST EFI_GUID *Protocol,
+ IN VOID *Interface,
+ IN EFI_HANDLE Handle
+ )
+{
+ EFI_STATUS Status;
+
+ DEBUG ((DEBUG_INFO, "%a: Entry.\n", __func__));
+ Status = CreateSpiNorFlashSfdpInstance (Handle);
+ return Status;
+}
+
+/**
+ Register notification for the later installed SPI I/O protocol.
+
+ @retval EFI_SUCCESS Succeed.
+ @retval otherwise Fail to register the notification of
+ SPI I/O protocol installation.
+
+**/
+EFI_STATUS
+RegisterSpioProtocolNotification (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ VOID *Registration;
+
+ Status = gSmst->SmmRegisterProtocolNotify (
+ &gEdk2JedecSfdpSpiSmmDriverGuid,
+ SpiIoProtocolInstalledCallback,
+ &Registration
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: Fail to register event for the SPI I/O Protocol installation.", __func__));
+ } else {
+ DEBUG ((DEBUG_INFO, "%a: Notification for SPI I/O Protocol installation was registered.", __func__));
+ }
+
+ return Status;
+}
+
+/**
+ Entry point of the SPI NOR Flash SFDP SMM driver.
+
+ @param ImageHandle Image handle of this driver.
+ @param SystemTable Pointer to standard EFI system table.
+
+ @retval EFI_SUCCESS Succeed.
+ @retval EFI_NOT_FOUND No gEdk2JedecSfdpSpiSmmDriverGuid installed on
+ system yet.
+ @retval EFI_OUT_OF_RESOURCES Not enough resource for SPI NOR Flash JEDEC SFDP
+ initialization.
+ @retval Otherwise Other errors.
+**/
+EFI_STATUS
+EFIAPI
+SpiNorFlashJedecSfdpSmmEntry (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE *InstanceBuffer;
+ UINTN InstanceIndex;
+ UINTN InstanceBufferSize;
+
+ DEBUG ((DEBUG_INFO, "%a - ENTRY.\n", __func__));
+
+ //
+ // Register notification for the later SPI I/O protocol installation.
+ //
+ RegisterSpioProtocolNotification ();
+ DEBUG ((DEBUG_INFO, "Check if there were already some gEdk2JedecSfdpSpiSmmDriverGuid handles installed.\n"));
+ //
+ // Check if there were already some gEdk2JedecSfdpSpiSmmDriverGuid
+ // handles installed.
+ //
+ // Locate the SPI I/O Protocol for the SPI flash part
+ // that supports JEDEC SFDP specification.
+ //
+ InstanceBufferSize = 0;
+ InstanceBuffer = NULL;
+ Status = gSmst->SmmLocateHandle (
+ ByProtocol,
+ &gEdk2JedecSfdpSpiSmmDriverGuid,
+ NULL,
+ &InstanceBufferSize,
+ InstanceBuffer
+ );
+ if (Status == EFI_NOT_FOUND) {
+ DEBUG ((
+ DEBUG_INFO,
+ "No gEdk2JedecSfdpSpiSmmDriverGuid handles found at the moment, wait for the notification of SPI I/O protocol installation.\n"
+ ));
+ DEBUG ((DEBUG_INFO, "%a: EXIT - Status=%r\n", __func__, Status));
+ return EFI_SUCCESS;
+ } else if (Status == EFI_BUFFER_TOO_SMALL) {
+ InstanceBuffer = (EFI_HANDLE *)AllocateZeroPool (InstanceBufferSize);
+ ASSERT (InstanceBuffer != NULL);
+ if (InstanceBuffer == NULL) {
+ DEBUG ((DEBUG_ERROR, "Not enough resource for gEdk2JedecSfdpSpiSmmDriverGuid handles.\n"));
+ DEBUG ((DEBUG_INFO, "%a: EXIT - Status=%r\n", __func__, Status));
+ return EFI_OUT_OF_RESOURCES;
+ }
+ } else if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Error to locate gEdk2JedecSfdpSpiSmmDriverGuid - Status = %r.\n", Status));
+ DEBUG ((DEBUG_INFO, "%a: EXIT - Status=%r\n", __func__, Status));
+ return Status;
+ }
+
+ Status = gSmst->SmmLocateHandle (
+ ByProtocol,
+ &gEdk2JedecSfdpSpiSmmDriverGuid,
+ NULL,
+ &InstanceBufferSize,
+ InstanceBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Fail to locate all gEdk2JedecSfdpSpiSmmDriverGuid handles.\n"));
+ DEBUG ((DEBUG_INFO, "%a: EXIT - Status=%r\n", __func__, Status));
+ return Status;
+ }
+
+ DEBUG ((DEBUG_INFO, "%d of gEdk2JedecSfdpSpiSmmDriverGuid handles are found.\n", InstanceBufferSize / sizeof (EFI_HANDLE)));
+ for (InstanceIndex = 0; InstanceIndex < InstanceBufferSize / sizeof (EFI_HANDLE); InstanceIndex++) {
+ Status = CreateSpiNorFlashSfdpInstance (*(InstanceBuffer + InstanceIndex));
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Fail to create SPI NOR Flash SFDP instance #%d.\n", InstanceIndex));
+ }
+ }
+
+ DEBUG ((DEBUG_INFO, "%a: EXIT - Status=%r\n", __func__, Status));
+ return Status;
+}
diff --git a/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpSmm.inf b/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpSmm.inf new file mode 100644 index 0000000000..89aceb0684 --- /dev/null +++ b/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpSmm.inf @@ -0,0 +1,64 @@ +## @file
+# The SPI NOR Flash JEDEC Serial Flash Discoverable Parameters (SFDP)
+# SMM driver INF file.
+#
+# Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+# @par Revision Reference:
+# - JEDEC Standard, JESD216F.02
+# https://www.jedec.org/document_search?search_api_views_fulltext=JESD216
+#
+# @par Glossary:
+# - SFDP - Serial Flash Discoverable Parameters
+# - PTP - Parameter Table Pointer
+##
+
+[Defines]
+ INF_VERSION = 1.25
+ BASE_NAME = SpiNorFlashJedecSfdpSmm
+ FILE_GUID = AC7884C7-35A2-40AC-B9E0-AD67298E3BBA
+ MODULE_TYPE = DXE_SMM_DRIVER
+ VERSION_STRING = 0.1
+ PI_SPECIFICATION_VERSION = 1.10
+ ENTRY_POINT = SpiNorFlashJedecSfdpSmmEntry
+ MODULE_UNI_FILE = SpiNorFlashJedecSfdpSmm.uni
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ DevicePathLib
+ MemoryAllocationLib
+ SmmServicesTableLib
+ TimerLib
+ UefiDriverEntryPoint
+
+[Sources]
+ SpiNorFlashJedecSfdpSmm.c
+ SpiNorFlash.c
+ SpiNorFlashJedecSfdp.c
+ SpiNorFlashJedecSfdpInternal.h
+ SpiNorFlash.h
+
+[Protocols]
+ gEfiSpiSmmNorFlashProtocolGuid ## PROCUDES
+
+[FixedPcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdSpiNorFlashOperationRetryCount
+ gEfiMdeModulePkgTokenSpaceGuid.PcdSpiNorFlashFixedTimeoutRetryCount
+ gEfiMdeModulePkgTokenSpaceGuid.PcdSpiNorFlashOperationDelayMicroseconds
+
+[Guids]
+ gEdk2JedecSfdpSpiSmmDriverGuid
+
+[Depex]
+ gEdk2JedecSfdpSpiSmmDriverGuid
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ SpiNorFlashJedecSfdpExtra.uni
diff --git a/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpSmm.uni b/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpSmm.uni new file mode 100644 index 0000000000..757da3ddbd --- /dev/null +++ b/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpSmm.uni @@ -0,0 +1,13 @@ +// /** @file
+// SPI NOR Flash SFDP Localized Strings and Content.
+//
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "EDK2 SPI NOR FLASH SFDP SMM driver"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This driver provides SPI NOR FLASH Serial Flash Discoverable Parameter (SFDP) compatible flash device capability discovery."
+
|