audk/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlash.c

1142 lines
39 KiB
C

/** @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;
}