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