| /** @file | |
| SPI NOR Flash JEDEC Serial Flash Discoverable Parameters (SFDP) | |
| common functions. | |
| Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved. | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| @par Revision Reference: | |
| - JEDEC Standard, JESD216F.02 | |
| https://www.jedec.org/document_search?search_api_views_fulltext=JESD216 | |
| @par Glossary: | |
| - SFDP - Serial Flash Discoverable Parameters | |
| - PTP - Parameter Table Pointer | |
| **/ | |
| #include <Base.h> | |
| #include <Library/BaseLib.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/DevicePathLib.h> | |
| #include <Library/MemoryAllocationLib.h> | |
| #include <Library/UefiBootServicesTableLib.h> | |
| #include <Protocol/SpiConfiguration.h> | |
| #include <Protocol/SpiIo.h> | |
| #include <IndustryStandard/SpiNorFlashJedecSfdp.h> | |
| #include "SpiNorFlash.h" | |
| #include "SpiNorFlashJedecSfdpInternal.h" | |
| /** | |
| Build up the Fast Read capability entry and link it to | |
| the linked list. | |
| @param[in] Instance SPI Nor Flash Instance data with pointer to | |
| EFI_SPI_NOR_FLASH_PROTOCOL and | |
| EFI_SPI_IO_PROTOCOL. | |
| @param[in] FastReadInstruction The string of fast read instruction. | |
| @param[in] FastReadModeClk The string of fast read mode clock. | |
| @param[in] FastReadDummyClk The string of fast read dummy clock. | |
| **/ | |
| VOID | |
| CreateSpiFastReadTableEntry ( | |
| IN SPI_NOR_FLASH_INSTANCE *Instance, | |
| IN UINT32 FastReadInstruction, | |
| IN UINT32 FastReadModeClk, | |
| IN UINT32 FastReadDummyClk | |
| ) | |
| { | |
| SFPD_FAST_READ_CAPBILITY_RECORD *CapabilityEntry; | |
| CapabilityEntry = AllocateZeroPool (sizeof (SFPD_FAST_READ_CAPBILITY_RECORD)); | |
| if (CapabilityEntry == NULL) { | |
| DEBUG ((DEBUG_ERROR, "%a: Failed to create fast read table\n", __func__)); | |
| ASSERT (FALSE); | |
| return; | |
| } | |
| InitializeListHead (&CapabilityEntry->NextFastReadCap); | |
| CapabilityEntry->FastReadInstruction = (UINT8)FastReadInstruction; | |
| CapabilityEntry->ModeClocks = (UINT8)FastReadModeClk; | |
| CapabilityEntry->WaitStates = (UINT8)FastReadDummyClk; | |
| InsertTailList (&Instance->FastReadTableList, &CapabilityEntry->NextFastReadCap); | |
| DEBUG ((DEBUG_VERBOSE, "%a: Create and link table.\n", __func__)); | |
| DEBUG ((DEBUG_VERBOSE, " Instruction : 0x%x\n", FastReadInstruction)); | |
| DEBUG ((DEBUG_VERBOSE, " Mode bits : 0x%x\n", FastReadModeClk)); | |
| DEBUG ((DEBUG_VERBOSE, " Wait States (Dummy Clocks): 0x%x\n", FastReadDummyClk)); | |
| } | |
| /** | |
| Calculate erase type typical time. | |
| @param[in] SfdpEraseTypicalTime Erase type typical time indicated in | |
| Basic Flash Parameter Table. | |
| EraseTypicalTime [0:4] - Count | |
| EraseTypicalTime [5:6] - Unit | |
| 00b: 1ms | |
| 01b: 16ms | |
| 10b: 128ms | |
| 11b: 1s | |
| @param[in] SfdpEraseTimeMultiplier Multiplier from erase typical time. | |
| @param[out] EraseTypicalTime Pointer to receive Erase typical time in milliseconds. | |
| @param[out] EraseTimeout Pointer to receive Erase timeout in milliseconds. | |
| **/ | |
| VOID | |
| CalculateEraseTiming ( | |
| IN UINT32 SfdpEraseTypicalTime, | |
| IN UINT32 SfdpEraseTimeMultiplier, | |
| OUT UINT32 *EraseTypicalTime, | |
| OUT UINT64 *EraseTimeout | |
| ) | |
| { | |
| UINT32 UnitInMs; | |
| UnitInMs = (SfdpEraseTypicalTime & ERASE_TYPICAL_TIME_UNITS_MASK) >> ERASE_TYPICAL_TIME_BIT_POSITION; | |
| switch (UnitInMs) { | |
| case ERASE_TYPICAL_TIME_UNIT_1_MS_BITMAP: | |
| UnitInMs = ERASE_TYPICAL_TIME_UNIT_1_MS; | |
| break; | |
| case ERASE_TYPICAL_TIME_UNIT_16_MS_BITMAP: | |
| UnitInMs = ERASE_TYPICAL_TIME_UNIT_16_MS; | |
| break; | |
| case ERASE_TYPICAL_TIME_UNIT_128_MS_BITMAP: | |
| UnitInMs = ERASE_TYPICAL_TIME_UNIT_128_MS; | |
| break; | |
| case ERASE_TYPICAL_TIME_UNIT_1000_MS_BITMAP: | |
| UnitInMs = ERASE_TYPICAL_TIME_UNIT_1000_MS; | |
| break; | |
| default: | |
| DEBUG ((DEBUG_ERROR, "%a: Unsupported Erase Typical time.\n", __func__)); | |
| ASSERT (FALSE); | |
| } | |
| *EraseTypicalTime = UnitInMs * ((SfdpEraseTypicalTime & ERASE_TYPICAL_TIME_COUNT_MASK) + 1); | |
| *EraseTimeout = 2 * (SfdpEraseTimeMultiplier + 1) * *EraseTypicalTime; | |
| return; | |
| } | |
| /** | |
| Print out the erase type information. | |
| @param[in] SupportedEraseType Pointer to SFDP_SUPPORTED_ERASE_TYPE_RECORD. | |
| **/ | |
| VOID | |
| DebugPrintEraseType ( | |
| IN SFDP_SUPPORTED_ERASE_TYPE_RECORD *SupportedEraseType | |
| ) | |
| { | |
| DEBUG ((DEBUG_VERBOSE, " Erase Type %d\n", SupportedEraseType->EraseType)); | |
| DEBUG ((DEBUG_VERBOSE, " Erase Type instruction: 0x%x\n", SupportedEraseType->EraseInstruction)); | |
| DEBUG ((DEBUG_VERBOSE, " Erase size: 0x%x bytes\n", SupportedEraseType->EraseSizeInByte)); | |
| DEBUG ((DEBUG_VERBOSE, " Erase time: %d Milliseconds\n", SupportedEraseType->EraseTypicalTime)); | |
| DEBUG ((DEBUG_VERBOSE, " Erase timeout: %d Milliseconds:\n", SupportedEraseType->EraseTimeout)); | |
| } | |
| /** | |
| Insert supported erase type entry. | |
| @param[in] Instance SPI Nor Flash Instance data with pointer to | |
| EFI_SPI_NOR_FLASH_PROTOCOL and | |
| EFI_SPI_IO_PROTOCOL. | |
| @param[in] SupportedEraseType Pointer to SFDP_SUPPORTED_ERASE_TYPE_RECORD. | |
| **/ | |
| VOID | |
| CreateEraseTypeEntry ( | |
| IN SPI_NOR_FLASH_INSTANCE *Instance, | |
| IN SFDP_SUPPORTED_ERASE_TYPE_RECORD *SupportedEraseType | |
| ) | |
| { | |
| InitializeListHead (&SupportedEraseType->NextEraseType); | |
| InsertTailList (&Instance->SupportedEraseTypes, &SupportedEraseType->NextEraseType); | |
| DEBUG ((DEBUG_VERBOSE, "%a: Erase Type 0x%x is supported:\n", __func__, SupportedEraseType->EraseType)); | |
| DebugPrintEraseType (SupportedEraseType); | |
| } | |
| /** | |
| Build up the erase type tables. | |
| @param[in] Instance SPI Nor Flash Instance data with pointer to | |
| EFI_SPI_NOR_FLASH_PROTOCOL and | |
| EFI_SPI_IO_PROTOCOL. | |
| **/ | |
| VOID | |
| BuildUpEraseTypeTable ( | |
| IN SPI_NOR_FLASH_INSTANCE *Instance | |
| ) | |
| { | |
| SFDP_SUPPORTED_ERASE_TYPE_RECORD *SupportedEraseType; | |
| // Build up erase type 1 entry. | |
| if (Instance->SfdpBasicFlash->Erase1Size != 0) { | |
| SupportedEraseType = AllocateZeroPool (sizeof (SFDP_SUPPORTED_ERASE_TYPE_RECORD)); | |
| if (SupportedEraseType != NULL) { | |
| SupportedEraseType->EraseType = SFDP_ERASE_TYPE_1; | |
| SupportedEraseType->EraseInstruction = (UINT8)Instance->SfdpBasicFlash->Erase1Instr; | |
| SupportedEraseType->EraseSizeInByte = (UINT32)1 << Instance->SfdpBasicFlash->Erase1Size; | |
| CalculateEraseTiming ( | |
| Instance->SfdpBasicFlash->Erase1Time, | |
| Instance->SfdpBasicFlash->EraseMultiplier, | |
| &SupportedEraseType->EraseTypicalTime, | |
| &SupportedEraseType->EraseTimeout | |
| ); | |
| CreateEraseTypeEntry (Instance, SupportedEraseType); | |
| } else { | |
| DEBUG ((DEBUG_ERROR, "%a: Memory allocated failed for SFDP_SUPPORTED_ERASE_TYPE_RECORD (Type 1).\n", __func__)); | |
| ASSERT (FALSE); | |
| } | |
| } | |
| // Build up erase type 2 entry. | |
| if (Instance->SfdpBasicFlash->Erase2Size != 0) { | |
| SupportedEraseType = AllocateZeroPool (sizeof (SFDP_SUPPORTED_ERASE_TYPE_RECORD)); | |
| if (SupportedEraseType != NULL) { | |
| SupportedEraseType->EraseType = SFDP_ERASE_TYPE_2; | |
| SupportedEraseType->EraseInstruction = (UINT8)Instance->SfdpBasicFlash->Erase2Instr; | |
| SupportedEraseType->EraseSizeInByte = (UINT32)1 << Instance->SfdpBasicFlash->Erase2Size; | |
| CalculateEraseTiming ( | |
| Instance->SfdpBasicFlash->Erase2Time, | |
| Instance->SfdpBasicFlash->EraseMultiplier, | |
| &SupportedEraseType->EraseTypicalTime, | |
| &SupportedEraseType->EraseTimeout | |
| ); | |
| CreateEraseTypeEntry (Instance, SupportedEraseType); | |
| } else { | |
| DEBUG ((DEBUG_ERROR, "%a: Memory allocated failed for SFDP_SUPPORTED_ERASE_TYPE_RECORD (Type 2).\n", __func__)); | |
| ASSERT (FALSE); | |
| } | |
| } | |
| // Build up erase type 3 entry. | |
| if (Instance->SfdpBasicFlash->Erase3Size != 0) { | |
| SupportedEraseType = AllocateZeroPool (sizeof (SFDP_SUPPORTED_ERASE_TYPE_RECORD)); | |
| if (SupportedEraseType != NULL) { | |
| SupportedEraseType->EraseType = SFDP_ERASE_TYPE_3; | |
| SupportedEraseType->EraseInstruction = (UINT8)Instance->SfdpBasicFlash->Erase3Instr; | |
| SupportedEraseType->EraseSizeInByte = (UINT32)1 << Instance->SfdpBasicFlash->Erase3Size; | |
| CalculateEraseTiming ( | |
| Instance->SfdpBasicFlash->Erase3Time, | |
| Instance->SfdpBasicFlash->EraseMultiplier, | |
| &SupportedEraseType->EraseTypicalTime, | |
| &SupportedEraseType->EraseTimeout | |
| ); | |
| CreateEraseTypeEntry (Instance, SupportedEraseType); | |
| } else { | |
| DEBUG ((DEBUG_ERROR, "%a: Memory allocated failed for SFDP_SUPPORTED_ERASE_TYPE_RECORD (Type 3).\n", __func__)); | |
| ASSERT (FALSE); | |
| } | |
| } | |
| // Build up erase type 4 entry. | |
| if (Instance->SfdpBasicFlash->Erase4Size != 0) { | |
| SupportedEraseType = AllocateZeroPool (sizeof (SFDP_SUPPORTED_ERASE_TYPE_RECORD)); | |
| if (SupportedEraseType != NULL) { | |
| SupportedEraseType->EraseType = SFDP_ERASE_TYPE_4; | |
| SupportedEraseType->EraseInstruction = (UINT8)Instance->SfdpBasicFlash->Erase4Instr; | |
| SupportedEraseType->EraseSizeInByte = (UINT32)1 << Instance->SfdpBasicFlash->Erase4Size; | |
| CalculateEraseTiming ( | |
| Instance->SfdpBasicFlash->Erase4Time, | |
| Instance->SfdpBasicFlash->EraseMultiplier, | |
| &SupportedEraseType->EraseTypicalTime, | |
| &SupportedEraseType->EraseTimeout | |
| ); | |
| CreateEraseTypeEntry (Instance, SupportedEraseType); | |
| } else { | |
| DEBUG ((DEBUG_ERROR, "%a: Memory allocated failed for SFDP_SUPPORTED_ERASE_TYPE_RECORD (Type 4).\n", __func__)); | |
| ASSERT (FALSE); | |
| } | |
| } | |
| } | |
| /** | |
| This function check if the erase type is one of the target erase types. | |
| @param[in] EraseType The erase type. | |
| @param[in] TargetTypeNum Number of target search types. | |
| @param[in] TargetTypes Target types. | |
| @retval TRUE Yes, this is the target erase type. | |
| @retval FALSE No, this is not the target erase type. | |
| **/ | |
| BOOLEAN | |
| IsTargetEraseType ( | |
| IN UINT16 EraseType, | |
| IN UINT8 TargetTypeNum, | |
| IN UINT8 *TargetTypes | |
| ) | |
| { | |
| UINT8 Index; | |
| for (Index = 0; Index < TargetTypeNum; Index++) { | |
| if (EraseType == *(TargetTypes + Index)) { | |
| return TRUE; | |
| } | |
| } | |
| return FALSE; | |
| } | |
| /** | |
| Search the erase type record according to the given search type and value. | |
| @param[in] Instance SPI Nor Flash Instance data with pointer to | |
| EFI_SPI_NOR_FLASH_PROTOCOL and | |
| EFI_SPI_IO_PROTOCOL. | |
| @param[in] SearchType Search type. | |
| @param[in] SearchValue The value of according to search type. | |
| - For SearchEraseTypeByCommand: | |
| SearchValue is the erase instruction. | |
| - For SearchEraseTypeBySize: | |
| SearchValue is the erase block size. | |
| - For SearchEraseTypeBySmallestSize: | |
| SearchValue is not used. | |
| - For SearchEraseTypeByBiggestSize: | |
| SearchValue is not used. | |
| @param[in] SupportedTypeTargetNum Only search the specific erase types. | |
| @param[in] SupportedTypeTarget Pointer to SupportedTypeTargetNum of | |
| supported erase types. | |
| @param[out] EraseTypeRecord Pointer to receive the erase type record. | |
| @retval EFI_SUCCESS Pointer to erase type record is returned. | |
| EFI_INVALID_PARAMETER Invalid SearchType. | |
| EFI_NOT_FOUND Erase type not found. | |
| **/ | |
| EFI_STATUS | |
| GetEraseTypeRecord ( | |
| IN SPI_NOR_FLASH_INSTANCE *Instance, | |
| IN SFDP_SEARCH_ERASE_TYPE SearchType, | |
| IN UINT32 SearchValue, | |
| IN UINT8 SupportedTypeTargetNum, | |
| IN UINT8 *SupportedTypeTarget OPTIONAL, | |
| OUT SFDP_SUPPORTED_ERASE_TYPE_RECORD **EraseTypeRecord | |
| ) | |
| { | |
| SFDP_SUPPORTED_ERASE_TYPE_RECORD *EraseType; | |
| UINT32 ValueToCompare; | |
| BOOLEAN ExitSearching; | |
| if (IsListEmpty (&Instance->SupportedEraseTypes)) { | |
| return EFI_NOT_FOUND; | |
| } | |
| *EraseTypeRecord = NULL; | |
| // | |
| // Initial the compare value. | |
| // | |
| switch (SearchType) { | |
| case SearchEraseTypeByType: | |
| case SearchEraseTypeByCommand: | |
| case SearchEraseTypeBySize: | |
| // | |
| // Although ValueToCompare is not used for these types, it must be | |
| // initialized to avoid "variable may be used uninitialized" compiler warning. | |
| // | |
| ValueToCompare = 0; | |
| 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); | |
| return Status; | |
| } | |
| CurrentBuffer += Length; | |
| } else { | |
| return Status; | |
| } | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| 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; | |
| } |