mirror of https://github.com/acidanthera/audk.git
1781 lines
66 KiB
C
1781 lines
66 KiB
C
/** @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;
|
|
}
|