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