From 8b02ecc5f04cac5eb1b7c4a79a800e97bcf205fa Mon Sep 17 00:00:00 2001 From: abnchang Date: Sun, 18 Jun 2023 05:44:35 +0800 Subject: MdeModulePkg/SpiNorFlashJedecSfdp: SPI NOR Flash JEDEC SFDP BZ#: 4471 SPI NOR Flash JEDEC Serial Flash Discoverable Driver implementation. Signed-off-by: Abner Chang Cc: Hao A Wu Cc: Ray Ni Cc: Abdul Lateef Attar Cc: Brit Chesley Reviewed-by: Liming Gao --- .../Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlash.c | 1141 +++++++++++++ .../Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlash.h | 286 ++++ .../SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdp.c | 1780 ++++++++++++++++++++ .../SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpDxe.c | 261 +++ .../SpiNorFlashJedecSfdpDxe.inf | 64 + .../SpiNorFlashJedecSfdpDxe.uni | 13 + .../SpiNorFlashJedecSfdpExtra.uni | 11 + .../SpiNorFlashJedecSfdpInternal.h | 299 ++++ .../SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpSmm.c | 234 +++ .../SpiNorFlashJedecSfdpSmm.inf | 64 + .../SpiNorFlashJedecSfdpSmm.uni | 13 + 11 files changed, 4166 insertions(+) create mode 100644 MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlash.c create mode 100644 MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlash.h create mode 100644 MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdp.c create mode 100644 MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpDxe.c create mode 100644 MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpDxe.inf create mode 100644 MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpDxe.uni create mode 100644 MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpExtra.uni create mode 100644 MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpInternal.h create mode 100644 MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpSmm.c create mode 100644 MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpSmm.inf create mode 100644 MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlashJedecSfdpSmm.uni (limited to 'MdeModulePkg') diff --git a/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlash.c b/MdeModulePkg/Bus/Spi/SpiNorFlashJedecSfdp/SpiNorFlash.c new file mode 100644 index 0000000..3ac5420 --- /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 +#include +#include +#include +#include +#include +#include +#include +#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 0000000..fb71e8d --- /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 +#include +#include +#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 0000000..284567d --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 0000000..d8b86dd --- /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 +#include +#include +#include +#include +#include +#include +#include +#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 0000000..26dbff3 --- /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 0000000..130e958 --- /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 0000000..4703cf8 --- /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 0000000..309d8dc --- /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 +#include +#include +#include + +#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 0000000..c9fe447 --- /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 +#include +#include +#include +#include +#include +#include +#include +#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 0000000..89aceb0 --- /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 0000000..757da3d --- /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." + -- cgit v1.1