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

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1781 lines
66 KiB
C
Raw Normal View History

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