| From 726f4505970c82db1822b127059519044dc496c8 Mon Sep 17 00:00:00 2001 |
| From: sahil <sahil@arm.com> |
| Date: Mon, 2 May 2022 19:00:40 +0530 |
| Subject: [PATCH] Platform/ARM/N1Sdp: NOR flash Dxe Driver for N1Sdp |
| |
| Add NOR flash DXE driver, this brings up NV storage on |
| QSPI's flash device using FVB protocol. |
| |
| Upstream-Status: Pending |
| Signed-off-by: Xueliang Zhong <xueliang.zhong@arm.com> |
| Signed-off-by: sahil <sahil@arm.com> |
| Change-Id: Ica383c2be6d1805daa19afd98d28b943816218dd |
| --- |
| .../Drivers/CadenceQspiDxe/CadenceQspiDxe.c | 366 +++++++ |
| .../Drivers/CadenceQspiDxe/CadenceQspiDxe.inf | 70 ++ |
| .../Drivers/CadenceQspiDxe/CadenceQspiReg.h | 31 + |
| .../N1Sdp/Drivers/CadenceQspiDxe/NorFlash.c | 930 ++++++++++++++++++ |
| .../N1Sdp/Drivers/CadenceQspiDxe/NorFlash.h | 484 +++++++++ |
| .../Drivers/CadenceQspiDxe/NorFlashFvb.c | 573 +++++++++++ |
| Platform/ARM/N1Sdp/N1SdpPlatform.dec | 5 +- |
| 7 files changed, 2458 insertions(+), 1 deletion(-) |
| create mode 100644 Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiDxe.c |
| create mode 100644 Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiDxe.inf |
| create mode 100644 Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiReg.h |
| create mode 100644 Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlash.c |
| create mode 100644 Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlash.h |
| create mode 100644 Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlashFvb.c |
| |
| diff --git a/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiDxe.c b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiDxe.c |
| new file mode 100644 |
| index 00000000..fb1dff3e |
| --- /dev/null |
| +++ b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiDxe.c |
| @@ -0,0 +1,366 @@ |
| +/** @file |
| + NOR flash DXE |
| + |
| + Copyright (c) 2023, ARM Limited. All rights reserved.<BR> |
| + |
| + SPDX-License-Identifier: BSD-2-Clause-Patent |
| + |
| +**/ |
| + |
| +#include <Library/BaseMemoryLib.h> |
| +#include <Library/DxeServicesTableLib.h> |
| +#include <Library/HobLib.h> |
| +#include <Library/MemoryAllocationLib.h> |
| +#include <Library/NorFlashInfoLib.h> |
| +#include <Library/PcdLib.h> |
| +#include <Library/UefiBootServicesTableLib.h> |
| +#include <Library/UefiLib.h> |
| +#include <Library/UefiRuntimeLib.h> |
| +#include <Library/UefiRuntimeServicesTableLib.h> |
| + |
| +#include "NorFlash.h" |
| + |
| +STATIC NOR_FLASH_INSTANCE **mNorFlashInstances; |
| +STATIC UINT32 mNorFlashDeviceCount; |
| + |
| +STATIC EFI_EVENT mNorFlashVirtualAddrChangeEvent; |
| + |
| +/** |
| + Install Fv block onto variable store region |
| + |
| + @param[in] Instance Instance of Nor flash variable region. |
| + |
| + @retval EFI_SUCCESS The entry point is executed successfully. |
| +**/ |
| +EFI_STATUS |
| +EFIAPI |
| +NorFlashFvbInitialize ( |
| + IN NOR_FLASH_INSTANCE* Instance |
| + ) |
| +{ |
| + EFI_STATUS Status; |
| + UINT32 FvbNumLba; |
| + EFI_BOOT_MODE BootMode; |
| + UINTN RuntimeMmioRegionSize; |
| + UINTN RuntimeMmioDeviceSize; |
| + UINTN BlockSize; |
| + |
| + DEBUG ((DEBUG_INFO,"NorFlashFvbInitialize\n")); |
| + |
| + BlockSize = Instance->BlockSize; |
| + |
| + // FirmwareVolumeHeader->FvLength is declared to have the Variable area |
| + // AND the FTW working area AND the FTW Spare contiguous. |
| + ASSERT (PcdGet32 (PcdFlashNvStorageVariableBase) + |
| + PcdGet32 (PcdFlashNvStorageVariableSize) == |
| + PcdGet32 (PcdFlashNvStorageFtwWorkingBase)); |
| + ASSERT (PcdGet32 (PcdFlashNvStorageFtwWorkingBase) + |
| + PcdGet32 (PcdFlashNvStorageFtwWorkingSize) == |
| + PcdGet32 (PcdFlashNvStorageFtwSpareBase)); |
| + |
| + // Check if the size of the area is at least one block size. |
| + ASSERT ((PcdGet32 (PcdFlashNvStorageVariableSize) > 0) && |
| + (PcdGet32 (PcdFlashNvStorageVariableSize) / BlockSize > 0)); |
| + ASSERT ((PcdGet32 (PcdFlashNvStorageFtwWorkingSize) > 0) && |
| + (PcdGet32 (PcdFlashNvStorageFtwWorkingSize) / BlockSize > 0)); |
| + ASSERT ((PcdGet32 (PcdFlashNvStorageFtwSpareSize) > 0) && |
| + (PcdGet32 (PcdFlashNvStorageFtwSpareSize) / BlockSize > 0)); |
| + |
| + // Ensure the Variable areas are aligned on block size boundaries. |
| + ASSERT ((PcdGet32 (PcdFlashNvStorageVariableBase) % BlockSize) == 0); |
| + ASSERT ((PcdGet32 (PcdFlashNvStorageFtwWorkingBase) % BlockSize) == 0); |
| + ASSERT ((PcdGet32 (PcdFlashNvStorageFtwSpareBase) % BlockSize) == 0); |
| + |
| + Instance->Initialized = TRUE; |
| + mFlashNvStorageVariableBase = FixedPcdGet32 (PcdFlashNvStorageVariableBase); |
| + |
| + // Set the index of the first LBA for the FVB. |
| + Instance->StartLba = (PcdGet32 (PcdFlashNvStorageVariableBase) - |
| + Instance->RegionBaseAddress) / BlockSize; |
| + |
| + BootMode = GetBootModeHob (); |
| + if (BootMode == BOOT_WITH_DEFAULT_SETTINGS) { |
| + Status = EFI_INVALID_PARAMETER; |
| + } else { |
| + // Determine if there is a valid header at the beginning of the NorFlash. |
| + Status = ValidateFvHeader (Instance); |
| + } |
| + |
| + // Install the Default FVB header if required. |
| + if (EFI_ERROR(Status)) { |
| + // There is no valid header, so time to install one. |
| + DEBUG ((DEBUG_INFO, "%a: The FVB Header is not valid.\n", __FUNCTION__)); |
| + DEBUG ((DEBUG_INFO, "%a: Installing a correct one for this volume.\n", |
| + __FUNCTION__)); |
| + |
| + // Erase all the NorFlash that is reserved for variable storage. |
| + FvbNumLba = (PcdGet32 (PcdFlashNvStorageVariableSize) + |
| + PcdGet32 (PcdFlashNvStorageFtwWorkingSize) + |
| + PcdGet32 (PcdFlashNvStorageFtwSpareSize)) / |
| + Instance->BlockSize; |
| + |
| + Status = FvbEraseBlocks ( |
| + &Instance->FvbProtocol, |
| + (EFI_LBA)0, |
| + FvbNumLba, |
| + EFI_LBA_LIST_TERMINATOR |
| + ); |
| + if (EFI_ERROR(Status)) { |
| + return Status; |
| + } |
| + |
| + // Install all appropriate headers. |
| + Status = InitializeFvAndVariableStoreHeaders (Instance); |
| + if (EFI_ERROR(Status)) { |
| + return Status; |
| + } |
| + |
| + // validate FV header again if FV was created successfully. |
| + Status = ValidateFvHeader (Instance); |
| + if (EFI_ERROR(Status)) { |
| + DEBUG ((DEBUG_ERROR, "ValidateFvHeader is failed \n")); |
| + return Status; |
| + } |
| + } |
| + |
| + // The driver implementing the variable read service can now be dispatched; |
| + // the varstore headers are in place. |
| + Status = gBS->InstallProtocolInterface ( |
| + &gImageHandle, |
| + &gEdkiiNvVarStoreFormattedGuid, |
| + EFI_NATIVE_INTERFACE, |
| + NULL |
| + ); |
| + if (EFI_ERROR (Status)) { |
| + DEBUG ((DEBUG_ERROR, |
| + "%a: Failed to install gEdkiiNvVarStoreFormattedGuid\n", |
| + __FUNCTION__)); |
| + return Status; |
| + } |
| + |
| + // Declare the Non-Volatile storage as EFI_MEMORY_RUNTIME. |
| + RuntimeMmioRegionSize = Instance->Size; |
| + RuntimeMmioDeviceSize = Instance->RegionBaseAddress - Instance->DeviceBaseAddress; |
| + |
| + Status = gDS->AddMemorySpace ( |
| + EfiGcdMemoryTypeMemoryMappedIo, |
| + Instance->RegionBaseAddress, |
| + RuntimeMmioRegionSize, |
| + EFI_MEMORY_UC | EFI_MEMORY_RUNTIME |
| + ); |
| + ASSERT_EFI_ERROR (Status); |
| + |
| + Status = gDS->AddMemorySpace ( |
| + EfiGcdMemoryTypeMemoryMappedIo, |
| + Instance->DeviceBaseAddress, |
| + RuntimeMmioDeviceSize, |
| + EFI_MEMORY_UC | EFI_MEMORY_RUNTIME |
| + ); |
| + ASSERT_EFI_ERROR (Status); |
| + |
| + Status = gDS->SetMemorySpaceAttributes ( |
| + Instance->RegionBaseAddress, |
| + RuntimeMmioRegionSize, |
| + EFI_MEMORY_UC | EFI_MEMORY_RUNTIME |
| + ); |
| + ASSERT_EFI_ERROR (Status); |
| + |
| + Status = gDS->SetMemorySpaceAttributes ( |
| + Instance->DeviceBaseAddress, |
| + RuntimeMmioDeviceSize, |
| + EFI_MEMORY_UC | EFI_MEMORY_RUNTIME |
| + ); |
| + ASSERT_EFI_ERROR (Status); |
| + |
| + return Status; |
| +} |
| + |
| +/** |
| + Fixup internal data so that EFI can be called in virtual mode. |
| + convert any pointers in lib to virtual mode. |
| + |
| + @param[in] Event The Event that is being processed |
| + @param[in] Context Event Context |
| +**/ |
| +STATIC |
| +VOID |
| +EFIAPI |
| +NorFlashVirtualNotifyEvent ( |
| + IN EFI_EVENT Event, |
| + IN VOID *Context |
| + ) |
| +{ |
| + UINTN Index; |
| + |
| + EfiConvertPointer (0x0, (VOID**)&mFlashNvStorageVariableBase); |
| + |
| + for (Index = 0; Index < mNorFlashDeviceCount; Index++) { |
| + EfiConvertPointer (0x0, |
| + (VOID**)&mNorFlashInstances[Index]->HostRegisterBaseAddress); |
| + EfiConvertPointer (0x0, |
| + (VOID**)&mNorFlashInstances[Index]->DeviceBaseAddress); |
| + EfiConvertPointer (0x0, |
| + (VOID**)&mNorFlashInstances[Index]->RegionBaseAddress); |
| + |
| + // Convert Fvb. |
| + EfiConvertPointer (0x0, |
| + (VOID**)&mNorFlashInstances[Index]->FvbProtocol.EraseBlocks); |
| + EfiConvertPointer (0x0, |
| + (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetAttributes); |
| + EfiConvertPointer (0x0, |
| + (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetBlockSize); |
| + EfiConvertPointer (0x0, |
| + (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetPhysicalAddress); |
| + EfiConvertPointer (0x0, |
| + (VOID**)&mNorFlashInstances[Index]->FvbProtocol.Read); |
| + EfiConvertPointer (0x0, |
| + (VOID**)&mNorFlashInstances[Index]->FvbProtocol.SetAttributes); |
| + EfiConvertPointer (0x0, |
| + (VOID**)&mNorFlashInstances[Index]->FvbProtocol.Write); |
| + |
| + if (mNorFlashInstances[Index]->ShadowBuffer != NULL) { |
| + EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->ShadowBuffer); |
| + } |
| + } |
| +} |
| + |
| +/** |
| + Entrypoint of Platform Nor flash DXE driver |
| + |
| + @param[in] ImageHandle The firmware allocated handle for the EFI image. |
| + @param[in] SystemTable A pointer to the EFI System Table. |
| + |
| + @retval EFI_SUCCESS The entry point is executed successfully. |
| +**/ |
| +EFI_STATUS |
| +EFIAPI |
| +NorFlashInitialise ( |
| + IN EFI_HANDLE ImageHandle, |
| + IN EFI_SYSTEM_TABLE *SystemTable |
| + ) |
| +{ |
| + EFI_STATUS Status; |
| + EFI_PHYSICAL_ADDRESS HostRegisterBaseAddress; |
| + UINT32 Index; |
| + NOR_FLASH_DESCRIPTION* NorFlashDevices; |
| + BOOLEAN ContainVariableStorage; |
| + |
| + HostRegisterBaseAddress = PcdGet32 (PcdCadenceQspiDxeRegBaseAddress); |
| + |
| + Status = gDS->AddMemorySpace ( |
| + EfiGcdMemoryTypeMemoryMappedIo, |
| + HostRegisterBaseAddress, |
| + SIZE_64KB, |
| + EFI_MEMORY_UC | EFI_MEMORY_RUNTIME |
| + ); |
| + ASSERT_EFI_ERROR (Status); |
| + |
| + Status = gDS->SetMemorySpaceAttributes ( |
| + HostRegisterBaseAddress, |
| + SIZE_64KB, |
| + EFI_MEMORY_UC | EFI_MEMORY_RUNTIME |
| + ); |
| + ASSERT_EFI_ERROR (Status); |
| + |
| + // Initialize NOR flash instances. |
| + Status = NorFlashPlatformGetDevices (&NorFlashDevices, &mNorFlashDeviceCount); |
| + if (EFI_ERROR (Status)) { |
| + DEBUG ((DEBUG_ERROR,"NorFlashInitialise: Fail to get Nor Flash devices\n")); |
| + return Status; |
| + } |
| + |
| + mNorFlashInstances = AllocateRuntimePool (sizeof (NOR_FLASH_INSTANCE*) * |
| + mNorFlashDeviceCount); |
| + |
| + if(mNorFlashInstances == NULL) { |
| + DEBUG ((DEBUG_ERROR, |
| + "NorFlashInitialise: Failed to allocate mem for NorFlashInstance\n")); |
| + return EFI_OUT_OF_RESOURCES; |
| + } |
| + |
| + for (Index = 0; Index < mNorFlashDeviceCount; Index++) { |
| + // Check if this NOR Flash device contain the variable storage region. |
| + ContainVariableStorage = |
| + (NorFlashDevices[Index].RegionBaseAddress <= |
| + PcdGet32 (PcdFlashNvStorageVariableBase)) && |
| + (PcdGet32 (PcdFlashNvStorageVariableBase) + |
| + PcdGet32 (PcdFlashNvStorageVariableSize) <= |
| + NorFlashDevices[Index].RegionBaseAddress + NorFlashDevices[Index].Size); |
| + |
| + Status = NorFlashCreateInstance ( |
| + HostRegisterBaseAddress, |
| + NorFlashDevices[Index].DeviceBaseAddress, |
| + NorFlashDevices[Index].RegionBaseAddress, |
| + NorFlashDevices[Index].Size, |
| + Index, |
| + NorFlashDevices[Index].BlockSize, |
| + ContainVariableStorage, |
| + &mNorFlashInstances[Index] |
| + ); |
| + if (EFI_ERROR (Status)) { |
| + DEBUG ((DEBUG_ERROR, |
| + "NorFlashInitialise: Fail to create instance for NorFlash[%d]\n", |
| + Index)); |
| + continue; |
| + } |
| + Status = gBS->InstallMultipleProtocolInterfaces ( |
| + &mNorFlashInstances[Index]->Handle, |
| + &gEfiDevicePathProtocolGuid, |
| + &mNorFlashInstances[Index]->DevicePath, |
| + &gEfiFirmwareVolumeBlockProtocolGuid, |
| + &mNorFlashInstances[Index]->FvbProtocol, |
| + NULL |
| + ); |
| + ASSERT_EFI_ERROR (Status); |
| + } |
| + // Register for the virtual address change event. |
| + Status = gBS->CreateEventEx ( |
| + EVT_NOTIFY_SIGNAL, |
| + TPL_NOTIFY, |
| + NorFlashVirtualNotifyEvent, |
| + NULL, |
| + &gEfiEventVirtualAddressChangeGuid, |
| + &mNorFlashVirtualAddrChangeEvent |
| + ); |
| + ASSERT_EFI_ERROR (Status); |
| + |
| + return Status; |
| +} |
| + |
| +/** |
| + Lock all pending read/write to Nor flash device |
| + |
| + @param[in] Context Nor flash device context structure. |
| +**/ |
| +VOID |
| +EFIAPI |
| +NorFlashLock ( |
| + IN NOR_FLASH_LOCK_CONTEXT *Context |
| + ) |
| +{ |
| + if (!EfiAtRuntime ()) { |
| + // Raise TPL to TPL_HIGH to stop anyone from interrupting us. |
| + Context->OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL); |
| + } else { |
| + Context->InterruptsEnabled = SaveAndDisableInterrupts (); |
| + } |
| +} |
| + |
| +/** |
| + Unlock all pending read/write to Nor flash device |
| + |
| + @param[in] Context Nor flash device context structure. |
| +**/ |
| +VOID |
| +EFIAPI |
| +NorFlashUnlock ( |
| + IN NOR_FLASH_LOCK_CONTEXT *Context |
| + ) |
| +{ |
| + if (!EfiAtRuntime ()) { |
| + // Interruptions can resume. |
| + gBS->RestoreTPL (Context->OriginalTPL); |
| + } else if (Context->InterruptsEnabled) { |
| + SetInterruptState (TRUE); |
| + } |
| +} |
| diff --git a/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiDxe.inf b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiDxe.inf |
| new file mode 100644 |
| index 00000000..4f20c3ba |
| --- /dev/null |
| +++ b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiDxe.inf |
| @@ -0,0 +1,70 @@ |
| +## @file |
| +# NOR flash DXE |
| +# |
| +# Copyright (c) 2023, ARM Limited. All rights reserved.<BR> |
| +# |
| +# SPDX-License-Identifier: BSD-2-Clause-Patent |
| +# |
| +## |
| + |
| +[Defines] |
| + INF_VERSION = 0x0001001B |
| + BASE_NAME = CadenceQspiDxe |
| + FILE_GUID = CC8A9713-4442-4A6C-B389-8B46490A0641 |
| + MODULE_TYPE = DXE_RUNTIME_DRIVER |
| + VERSION_STRING = 0.1 |
| + ENTRY_POINT = NorFlashInitialise |
| + |
| +[Sources] |
| + CadenceQspiDxe.c |
| + NorFlash.c |
| + NorFlash.h |
| + NorFlashFvb.c |
| + |
| +[Packages] |
| + EmbeddedPkg/EmbeddedPkg.dec |
| + MdeModulePkg/MdeModulePkg.dec |
| + MdePkg/MdePkg.dec |
| + Platform/ARM/ARM.dec |
| + Platform/ARM/N1Sdp/N1SdpPlatform.dec |
| + |
| +[LibraryClasses] |
| + BaseLib |
| + BaseMemoryLib |
| + DebugLib |
| + DevicePathLib |
| + DxeServicesTableLib |
| + HobLib |
| + IoLib |
| + MemoryAllocationLib |
| + NorFlashInfoLib |
| + NorFlashPlatformLib |
| + UefiBootServicesTableLib |
| + UefiDriverEntryPoint |
| + UefiLib |
| + UefiRuntimeLib |
| + UefiRuntimeServicesTableLib |
| + |
| +[Guids] |
| + gEdkiiNvVarStoreFormattedGuid |
| + gEfiAuthenticatedVariableGuid |
| + gEfiEventVirtualAddressChangeGuid |
| + gEfiSystemNvDataFvGuid |
| + gEfiVariableGuid |
| + gEfiGlobalVariableGuid |
| + |
| +[Protocols] |
| + gEfiDevicePathProtocolGuid |
| + gEfiFirmwareVolumeBlockProtocolGuid |
| + |
| +[FixedPcd] |
| + gArmN1SdpTokenSpaceGuid.PcdCadenceQspiDxeRegBaseAddress |
| + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase |
| + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize |
| + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase |
| + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize |
| + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase |
| + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize |
| + |
| +[Depex] |
| + gEfiCpuArchProtocolGuid |
| diff --git a/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiReg.h b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiReg.h |
| new file mode 100644 |
| index 00000000..fe3b327c |
| --- /dev/null |
| +++ b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiReg.h |
| @@ -0,0 +1,31 @@ |
| +/** @file |
| + |
| + Copyright (c) 2023, ARM Limited. All rights reserved.<BR> |
| + |
| + SPDX-License-Identifier: BSD-2-Clause-Patent |
| + |
| +**/ |
| + |
| +#ifndef CADENCE_QSPI_REG_H_ |
| +#define CADENCE_QSPI_REG_H_ |
| + |
| +// QSPI Controller defines |
| +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_OFFSET 0x90 |
| +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_EXECUTE 0x01 |
| +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_ENABLE 0x01 |
| +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_BIT_POS 19 |
| +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_BYTE_BIT_POS 16 |
| +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_STATUS_BIT 0x02 |
| +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_BYTE_4B 0x03 |
| +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_BYTE_3B 0x02 |
| +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_OPCODE_BIT_POS 24 |
| +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_READ_ENABLE 0x01 |
| +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_READ_BYTE_3B 0x02 |
| +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_READEN_BIT_POS 23 |
| +#define CDNS_QSPI_FLASH_CMD_CTRL_REG_READBYTE_BIT_POS 20 |
| + |
| +#define CDNS_QSPI_FLASH_CMD_READ_DATA_REG_OFFSET 0xA0 |
| + |
| +#define CDNS_QSPI_FLASH_CMD_ADDR_REG_OFFSET 0x94 |
| + |
| +#endif /* CADENCE_QSPI_REG_H_ */ |
| diff --git a/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlash.c b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlash.c |
| new file mode 100644 |
| index 00000000..188c75e2 |
| --- /dev/null |
| +++ b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlash.c |
| @@ -0,0 +1,930 @@ |
| +/** @file |
| + |
| + Copyright (c) 2023 ARM Limited. All rights reserved.<BR> |
| + |
| + SPDX-License-Identifier: BSD-2-Clause-Patent |
| + |
| +**/ |
| + |
| +#include <Library/BaseMemoryLib.h> |
| +#include <Library/MemoryAllocationLib.h> |
| +#include <Library/NorFlashInfoLib.h> |
| +#include <Library/PcdLib.h> |
| +#include <Library/UefiBootServicesTableLib.h> |
| +#include <Library/UefiLib.h> |
| + |
| +#include "NorFlash.h" |
| + |
| +STATIC CONST NOR_FLASH_INSTANCE mNorFlashInstanceTemplate = { |
| + NOR_FLASH_SIGNATURE, // Signature |
| + NULL, // Handle |
| + |
| + FALSE, // Initialized |
| + NULL, // Initialize |
| + |
| + 0, // HostRegisterBaseAddress |
| + 0, // DeviceBaseAddress |
| + 0, // RegionBaseAddress |
| + 0, // Size |
| + 0, // BlockSize |
| + 0, // LastBlock |
| + 0, // StartLba |
| + 0, // OffsetLba |
| + |
| + { |
| + FvbGetAttributes, // GetAttributes |
| + FvbSetAttributes, // SetAttributes |
| + FvbGetPhysicalAddress, // GetPhysicalAddress |
| + FvbGetBlockSize, // GetBlockSize |
| + FvbRead, // Read |
| + FvbWrite, // Write |
| + FvbEraseBlocks, // EraseBlocks |
| + NULL, //ParentHandle |
| + }, // FvbProtoccol; |
| + NULL, // ShadowBuffer |
| + |
| + { |
| + { |
| + { |
| + HARDWARE_DEVICE_PATH, |
| + HW_VENDOR_DP, |
| + { |
| + (UINT8)(OFFSET_OF (NOR_FLASH_DEVICE_PATH, End)), |
| + (UINT8)(OFFSET_OF (NOR_FLASH_DEVICE_PATH, End) >> 8) |
| + } |
| + }, |
| + { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } }, |
| + }, |
| + 0, // Index |
| + |
| + { |
| + END_DEVICE_PATH_TYPE, |
| + END_ENTIRE_DEVICE_PATH_SUBTYPE, |
| + { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 } |
| + } |
| + |
| + }, // DevicePath |
| + 0 // Flags |
| +}; |
| + |
| +/** |
| + Execute Flash cmd ctrl and Read Status. |
| + |
| + @param[in] Instance NOR flash Instance. |
| + @param[in] Val Value to be written to Flash cmd ctrl Register. |
| + |
| + @retval EFI_SUCCESS Request is executed successfully. |
| + |
| +**/ |
| +STATIC |
| +EFI_STATUS |
| +CdnsQspiExecuteCommand ( |
| + IN NOR_FLASH_INSTANCE *Instance, |
| + IN UINT32 Val |
| + ) |
| +{ |
| + // Set the command |
| + MmioWrite32 (Instance->HostRegisterBaseAddress + CDNS_QSPI_FLASH_CMD_CTRL_REG_OFFSET, |
| + Val); |
| + // Execute the command |
| + MmioWrite32 (Instance->HostRegisterBaseAddress + CDNS_QSPI_FLASH_CMD_CTRL_REG_OFFSET, |
| + Val | CDNS_QSPI_FLASH_CMD_CTRL_REG_EXECUTE); |
| + |
| + // Wait until command has been executed |
| + while ((MmioRead32 (Instance->HostRegisterBaseAddress + CDNS_QSPI_FLASH_CMD_CTRL_REG_OFFSET) |
| + & CDNS_QSPI_FLASH_CMD_CTRL_REG_STATUS_BIT) == CDNS_QSPI_FLASH_CMD_CTRL_REG_STATUS_BIT) |
| + continue; |
| + |
| + return EFI_SUCCESS; |
| +} |
| + |
| +/** |
| + Create Nor flash Instance for given region. |
| + |
| + @param[in] HostRegisterBase Base address of Nor flash controller. |
| + @param[in] NorFlashDeviceBase Base address of flash device. |
| + @param[in] NorFlashRegionBase Base address of flash region on device. |
| + @param[in] NorFlashSize Size of flash region. |
| + @param[in] Index Index of given flash region. |
| + @param[in] BlockSize Block size of NOR flash device. |
| + @param[in] HasVarStore Boolean set for VarStore on given region. |
| + @param[out] NorFlashInstance Instance of given flash region. |
| + |
| + @retval EFI_SUCCESS On successful creation of NOR flash instance. |
| +**/ |
| +EFI_STATUS |
| +NorFlashCreateInstance ( |
| + IN UINTN HostRegisterBase, |
| + IN UINTN NorFlashDeviceBase, |
| + IN UINTN NorFlashRegionBase, |
| + IN UINTN NorFlashSize, |
| + IN UINT32 Index, |
| + IN UINT32 BlockSize, |
| + IN BOOLEAN HasVarStore, |
| + OUT NOR_FLASH_INSTANCE** NorFlashInstance |
| + ) |
| +{ |
| + EFI_STATUS Status; |
| + NOR_FLASH_INSTANCE* Instance; |
| + NOR_FLASH_INFO *FlashInfo; |
| + UINT8 JedecId[3]; |
| + |
| + ASSERT(NorFlashInstance != NULL); |
| + Instance = AllocateRuntimeCopyPool (sizeof (mNorFlashInstanceTemplate), |
| + &mNorFlashInstanceTemplate); |
| + if (Instance == NULL) { |
| + return EFI_OUT_OF_RESOURCES; |
| + } |
| + |
| + Instance->HostRegisterBaseAddress = HostRegisterBase; |
| + Instance->DeviceBaseAddress = NorFlashDeviceBase; |
| + Instance->RegionBaseAddress = NorFlashRegionBase; |
| + Instance->Size = NorFlashSize; |
| + Instance->BlockSize = BlockSize; |
| + Instance->LastBlock = (NorFlashSize / BlockSize) - 1; |
| + |
| + Instance->OffsetLba = (NorFlashRegionBase - NorFlashDeviceBase) / BlockSize; |
| + |
| + CopyGuid (&Instance->DevicePath.Vendor.Guid, &gEfiCallerIdGuid); |
| + Instance->DevicePath.Index = (UINT8)Index; |
| + |
| + Status = NorFlashReadID (Instance, JedecId); |
| + if (EFI_ERROR (Status)) { |
| + goto FreeInstance; |
| + } |
| + |
| + Status = NorFlashGetInfo (JedecId, &FlashInfo, TRUE); |
| + if (EFI_ERROR (Status)) { |
| + goto FreeInstance; |
| + } |
| + |
| + NorFlashPrintInfo (FlashInfo); |
| + |
| + Instance->Flags = 0; |
| + if (FlashInfo->Flags & NOR_FLASH_WRITE_FSR) { |
| + Instance->Flags = NOR_FLASH_POLL_FSR; |
| + } |
| + |
| + Instance->ShadowBuffer = AllocateRuntimePool (BlockSize); |
| + if (Instance->ShadowBuffer == NULL) { |
| + Status = EFI_OUT_OF_RESOURCES; |
| + goto FreeInstance; |
| + } |
| + |
| + if (HasVarStore) { |
| + Instance->Initialize = NorFlashFvbInitialize; |
| + } |
| + |
| + *NorFlashInstance = Instance; |
| + FreePool (FlashInfo); |
| + return EFI_SUCCESS; |
| + |
| +FreeInstance: |
| + FreePool (Instance); |
| + return Status; |
| +} |
| + |
| +/** |
| + Check whether NOR flash opertions are Locked. |
| + |
| + @param[in] Instance NOR flash Instance. |
| + @param[in] BlockAddress BlockAddress in NOR flash device. |
| + |
| + @retval FALSE If NOR flash is not locked. |
| +**/ |
| +STATIC |
| +BOOLEAN |
| +NorFlashBlockIsLocked ( |
| + IN NOR_FLASH_INSTANCE *Instance, |
| + IN UINTN BlockAddress |
| + ) |
| +{ |
| + return FALSE; |
| +} |
| + |
| +/** |
| + Unlock NOR flash operations on given block. |
| + |
| + @param[in] Instance NOR flash instance. |
| + @param[in] BlockAddress BlockAddress in NOR flash device. |
| + |
| + @retval EFI_SUCCESS NOR flash operations is unlocked. |
| +**/ |
| +STATIC |
| +EFI_STATUS |
| +NorFlashUnlockSingleBlock ( |
| + IN NOR_FLASH_INSTANCE *Instance, |
| + IN UINTN BlockAddress |
| + ) |
| +{ |
| + return EFI_SUCCESS; |
| +} |
| + |
| +/** |
| + Unlock NOR flash operations if it is necessary. |
| + |
| + @param[in] Instance NOR flash instance. |
| + @param[in] BlockAddress BlockAddress in NOR flash device. |
| + |
| + @retval EFI_SUCCESS Request is executed successfully. |
| +**/ |
| +STATIC |
| +EFI_STATUS |
| +NorFlashUnlockSingleBlockIfNecessary ( |
| + IN NOR_FLASH_INSTANCE *Instance, |
| + IN UINTN BlockAddress |
| + ) |
| +{ |
| + EFI_STATUS Status; |
| + |
| + Status = EFI_SUCCESS; |
| + |
| + if (!NorFlashBlockIsLocked (Instance, BlockAddress)) { |
| + Status = NorFlashUnlockSingleBlock (Instance, BlockAddress); |
| + } |
| + |
| + return Status; |
| +} |
| + |
| +/** |
| + Enable write to NOR flash device. |
| + |
| + @param[in] Instance NOR flash instance. |
| + |
| + @retval EFI_SUCCESS Request is executed successfully. |
| +**/ |
| +STATIC |
| +EFI_STATUS |
| +NorFlashEnableWrite ( |
| + IN NOR_FLASH_INSTANCE *Instance |
| + ) |
| +{ |
| + |
| + UINT32 val; |
| + |
| + DEBUG ((DEBUG_INFO, "NorFlashEnableWrite()\n")); |
| + val = (SPINOR_OP_WREN << CDNS_QSPI_FLASH_CMD_CTRL_REG_OPCODE_BIT_POS); |
| + if (EFI_ERROR (CdnsQspiExecuteCommand (Instance, val))) { |
| + return EFI_DEVICE_ERROR; |
| + } |
| + |
| + return EFI_SUCCESS; |
| +} |
| + |
| +/** |
| + The following function presumes that the block has already been unlocked. |
| + |
| + @param[in] Instance NOR flash instance. |
| + @param[in] BlockAddress Block address within the variable region. |
| + |
| + @retval EFI_SUCCESS Request is executed successfully. |
| + **/ |
| +EFI_STATUS |
| +NorFlashEraseSingleBlock ( |
| + IN NOR_FLASH_INSTANCE *Instance, |
| + IN UINTN BlockAddress |
| + ) |
| +{ |
| + |
| + UINT32 DevConfigVal; |
| + UINT32 EraseOffset; |
| + |
| + EraseOffset = 0x0; |
| + |
| + DEBUG ((DEBUG_INFO, "NorFlashEraseSingleBlock(BlockAddress=0x%08x)\n", |
| + BlockAddress)); |
| + |
| + if (EFI_ERROR (NorFlashEnableWrite (Instance))) { |
| + return EFI_DEVICE_ERROR; |
| + } |
| + |
| + EraseOffset = BlockAddress - Instance->DeviceBaseAddress; |
| + |
| + MmioWrite32 (Instance->HostRegisterBaseAddress + CDNS_QSPI_FLASH_CMD_ADDR_REG_OFFSET, |
| + EraseOffset); |
| + |
| + DevConfigVal = SPINOR_OP_BE_4K << CDNS_QSPI_FLASH_CMD_CTRL_REG_OPCODE_BIT_POS | |
| + CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_ENABLE << CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_BIT_POS | |
| + CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_BYTE_3B << CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_BYTE_BIT_POS; |
| + |
| + if (EFI_ERROR (CdnsQspiExecuteCommand (Instance, DevConfigVal))) { |
| + return EFI_DEVICE_ERROR; |
| + } |
| + |
| + return EFI_SUCCESS; |
| +} |
| + |
| +/** |
| + This function unlock and erase an entire NOR Flash block. |
| + |
| + @param[in] Instance NOR flash Instance of variable store region. |
| + @param[in] BlockAddress Block address within the variable store region. |
| + |
| + @retval EFI_SUCCESS The erase and unlock successfully completed. |
| +**/ |
| +EFI_STATUS |
| +NorFlashUnlockAndEraseSingleBlock ( |
| + IN NOR_FLASH_INSTANCE *Instance, |
| + IN UINTN BlockAddress |
| + ) |
| +{ |
| + EFI_STATUS Status; |
| + UINTN Index; |
| + NOR_FLASH_LOCK_CONTEXT Lock; |
| + NorFlashLock (&Lock); |
| + |
| + Index = 0; |
| + do { |
| + // Unlock the block if we have to |
| + Status = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress); |
| + if (EFI_ERROR (Status)) { |
| + break; |
| + } |
| + Status = NorFlashEraseSingleBlock (Instance, BlockAddress); |
| + if (EFI_ERROR (Status)) { |
| + break; |
| + } |
| + Index++; |
| + } while ((Index < NOR_FLASH_ERASE_RETRY) && (Status == EFI_WRITE_PROTECTED)); |
| + |
| + if (Index == NOR_FLASH_ERASE_RETRY) { |
| + DEBUG ((DEBUG_ERROR, |
| + "EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error (try to erase %d times)\n", |
| + BlockAddress,Index)); |
| + } |
| + |
| + NorFlashUnlock (&Lock); |
| + |
| + return Status; |
| +} |
| + |
| +/** |
| + Write a single word to given location. |
| + |
| + @param[in] Instance NOR flash Instance of variable store region. |
| + @param[in] WordAddress The address in NOR flash to write given word. |
| + @param[in] WriteData The data to write into NOR flash location. |
| + |
| + @retval EFI_SUCCESS The write is completed. |
| +**/ |
| +STATIC |
| +EFI_STATUS |
| +NorFlashWriteSingleWord ( |
| + IN NOR_FLASH_INSTANCE *Instance, |
| + IN UINTN WordAddress, |
| + IN UINT32 WriteData |
| + ) |
| +{ |
| + DEBUG ((DEBUG_INFO, |
| + "NorFlashWriteSingleWord(WordAddress=0x%08x, WriteData=0x%08x)\n", |
| + WordAddress, WriteData)); |
| + |
| + if (EFI_ERROR (NorFlashEnableWrite (Instance))) { |
| + return EFI_DEVICE_ERROR; |
| + } |
| + MmioWrite32 (WordAddress, WriteData); |
| + return EFI_SUCCESS; |
| +} |
| + |
| +/** |
| + Write a full block to given location. |
| + |
| + @param[in] Instance NOR flash Instance of variable store region. |
| + @param[in] Lba The logical block address in NOR flash. |
| + @param[in] DataBuffer The data to write into NOR flash location. |
| + @param[in] BlockSizeInWords The number of bytes to write. |
| + |
| + @retval EFI_SUCCESS The write is completed. |
| +**/ |
| +STATIC |
| +EFI_STATUS |
| +NorFlashWriteFullBlock ( |
| + IN NOR_FLASH_INSTANCE *Instance, |
| + IN EFI_LBA Lba, |
| + IN UINT32 *DataBuffer, |
| + IN UINT32 BlockSizeInWords |
| + ) |
| +{ |
| + EFI_STATUS Status; |
| + UINTN WordAddress; |
| + UINT32 WordIndex; |
| + UINTN BlockAddress; |
| + NOR_FLASH_LOCK_CONTEXT Lock; |
| + |
| + Status = EFI_SUCCESS; |
| + |
| + // Get the physical address of the block |
| + BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, |
| + BlockSizeInWords * 4); |
| + |
| + // Start writing from the first address at the start of the block |
| + WordAddress = BlockAddress; |
| + |
| + NorFlashLock (&Lock); |
| + |
| + Status = NorFlashUnlockAndEraseSingleBlock (Instance, BlockAddress); |
| + if (EFI_ERROR (Status)) { |
| + DEBUG ((DEBUG_ERROR, |
| + "WriteSingleBlock: ERROR - Failed to Unlock and Erase the single block at 0x%X\n", |
| + BlockAddress)); |
| + goto EXIT; |
| + } |
| + |
| + for (WordIndex=0; |
| + WordIndex < BlockSizeInWords; |
| + WordIndex++, DataBuffer++, WordAddress += 4) { |
| + Status = NorFlashWriteSingleWord (Instance, WordAddress, *DataBuffer); |
| + if (EFI_ERROR (Status)) { |
| + goto EXIT; |
| + } |
| + } |
| + |
| +EXIT: |
| + NorFlashUnlock (&Lock); |
| + |
| + if (EFI_ERROR (Status)) { |
| + DEBUG ((DEBUG_ERROR, |
| + "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = %r.\n", |
| + WordAddress, Status)); |
| + } |
| + return Status; |
| +} |
| + |
| +/** |
| + Write a full block. |
| + |
| + @param[in] Instance NOR flash Instance of variable store region. |
| + @param[in] Lba The starting logical block index. |
| + @param[in] BufferSizeInBytes The number of bytes to read. |
| + @param[in] Buffer The pointer to a caller-allocated buffer that |
| + contains the source for the write. |
| + |
| + @retval EFI_SUCCESS The write is completed. |
| +**/ |
| +EFI_STATUS |
| +NorFlashWriteBlocks ( |
| + IN NOR_FLASH_INSTANCE *Instance, |
| + IN EFI_LBA Lba, |
| + IN UINTN BufferSizeInBytes, |
| + IN VOID *Buffer |
| + ) |
| +{ |
| + UINT32 *pWriteBuffer; |
| + EFI_STATUS Status; |
| + EFI_LBA CurrentBlock; |
| + UINT32 BlockSizeInWords; |
| + UINT32 NumBlocks; |
| + UINT32 BlockCount; |
| + |
| + Status = EFI_SUCCESS; |
| + // The buffer must be valid |
| + if (Buffer == NULL) { |
| + return EFI_INVALID_PARAMETER; |
| + } |
| + |
| + // We must have some bytes to read |
| + DEBUG ((DEBUG_INFO, "NorFlashWriteBlocks: BufferSizeInBytes=0x%x\n", |
| + BufferSizeInBytes)); |
| + if (BufferSizeInBytes == 0) { |
| + return EFI_BAD_BUFFER_SIZE; |
| + } |
| + |
| + // The size of the buffer must be a multiple of the block size |
| + DEBUG ((DEBUG_INFO, "NorFlashWriteBlocks: BlockSize in bytes =0x%x\n", |
| + Instance->BlockSize)); |
| + if ((BufferSizeInBytes % Instance->BlockSize) != 0) { |
| + return EFI_BAD_BUFFER_SIZE; |
| + } |
| + |
| + // All blocks must be within the device |
| + NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->BlockSize; |
| + |
| + DEBUG ((DEBUG_INFO, |
| + "NorFlashWriteBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld.\n", NumBlocks, |
| + Instance->LastBlock, Lba)); |
| + |
| + if ((Lba + NumBlocks) > (Instance->LastBlock + 1)) { |
| + DEBUG ((DEBUG_ERROR, |
| + "NorFlashWriteBlocks: ERROR - Write will exceed last block.\n")); |
| + return EFI_INVALID_PARAMETER; |
| + } |
| + |
| + ASSERT (((UINTN)Buffer % sizeof (UINT32)) == 0); |
| + |
| + BlockSizeInWords = Instance->BlockSize / 4; |
| + |
| + // Because the target *Buffer is a pointer to VOID, we must put |
| + // all the data into a pointer to a proper data type, so use *ReadBuffer |
| + pWriteBuffer = (UINT32 *)Buffer; |
| + |
| + CurrentBlock = Lba; |
| + for (BlockCount = 0; |
| + BlockCount < NumBlocks; |
| + BlockCount++, CurrentBlock++, pWriteBuffer += BlockSizeInWords) { |
| + |
| + DEBUG ((DEBUG_INFO, "NorFlashWriteBlocks: Writing block #%d\n", |
| + (UINTN)CurrentBlock)); |
| + |
| + Status = NorFlashWriteFullBlock ( |
| + Instance, |
| + CurrentBlock, |
| + pWriteBuffer, |
| + BlockSizeInWords |
| + ); |
| + |
| + if (EFI_ERROR (Status)) { |
| + break; |
| + } |
| + } |
| + |
| + DEBUG ((DEBUG_INFO, "NorFlashWriteBlocks: Exit Status = %r.\n", Status)); |
| + return Status; |
| +} |
| + |
| +/** |
| + Read a full block. |
| + |
| + @param[in] Instance NOR flash Instance of variable store region. |
| + @param[in] Lba The starting logical block index to read from. |
| + @param[in] BufferSizeInBytes The number of bytes to read. |
| + @param[out] Buffer The pointer to a caller-allocated buffer that |
| + should be copied with read data. |
| + |
| + @retval EFI_SUCCESS The read is completed. |
| +**/ |
| +EFI_STATUS |
| +NorFlashReadBlocks ( |
| + IN NOR_FLASH_INSTANCE *Instance, |
| + IN EFI_LBA Lba, |
| + IN UINTN BufferSizeInBytes, |
| + OUT VOID *Buffer |
| + ) |
| +{ |
| + UINT32 NumBlocks; |
| + UINTN StartAddress; |
| + DEBUG ((DEBUG_INFO, |
| + "NorFlashReadBlocks: BufferSize=0x%xB BlockSize=0x%xB LastBlock=%ld, Lba=%ld.\n", |
| + BufferSizeInBytes, Instance->BlockSize, Instance->LastBlock, |
| + Lba)); |
| + |
| + // The buffer must be valid |
| + if (Buffer == NULL) { |
| + return EFI_INVALID_PARAMETER; |
| + } |
| + |
| + // Return if we do not have any byte to read |
| + if (BufferSizeInBytes == 0) { |
| + return EFI_SUCCESS; |
| + } |
| + |
| + // The size of the buffer must be a multiple of the block size |
| + if ((BufferSizeInBytes % Instance->BlockSize) != 0) { |
| + return EFI_BAD_BUFFER_SIZE; |
| + } |
| + |
| + NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->BlockSize; |
| + |
| + if ((Lba + NumBlocks) > (Instance->LastBlock + 1)) { |
| + DEBUG ((DEBUG_ERROR, |
| + "NorFlashReadBlocks: ERROR - Read will exceed last block\n")); |
| + return EFI_INVALID_PARAMETER; |
| + } |
| + |
| + // Get the address to start reading from |
| + StartAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, |
| + Instance->BlockSize); |
| + |
| + // Readout the data |
| + CopyMem(Buffer, (UINTN *)StartAddress, BufferSizeInBytes); |
| + |
| + return EFI_SUCCESS; |
| +} |
| + |
| +/** |
| + Read from nor flash. |
| + |
| + @param[in] Instance NOR flash Instance of variable store region. |
| + @param[in] Lba The starting logical block index to read from. |
| + @param[in] Offset Offset into the block at which to begin reading. |
| + @param[in] BufferSizeInBytes The number of bytes to read. |
| + @param[out] Buffer The pointer to a caller-allocated buffer that |
| + should copied with read data. |
| + |
| + @retval EFI_SUCCESS The read is completed. |
| +**/ |
| +EFI_STATUS |
| +NorFlashRead ( |
| + IN NOR_FLASH_INSTANCE *Instance, |
| + IN EFI_LBA Lba, |
| + IN UINTN Offset, |
| + IN UINTN BufferSizeInBytes, |
| + OUT VOID *Buffer |
| + ) |
| +{ |
| + UINTN StartAddress; |
| + // The buffer must be valid |
| + if (Buffer == NULL) { |
| + return EFI_INVALID_PARAMETER; |
| + } |
| + |
| + // Return if we do not have any byte to read |
| + if (BufferSizeInBytes == 0) { |
| + return EFI_SUCCESS; |
| + } |
| + |
| + if (((Lba * Instance->BlockSize) + Offset + BufferSizeInBytes) > |
| + Instance->Size) { |
| + DEBUG ((DEBUG_ERROR, |
| + "NorFlashRead: ERROR - Read will exceed device size.\n")); |
| + return EFI_INVALID_PARAMETER; |
| + } |
| + |
| + // Get the address to start reading from |
| + StartAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, |
| + Instance->BlockSize); |
| + |
| + // Readout the data |
| + CopyMem (Buffer, (UINTN *)(StartAddress + Offset), BufferSizeInBytes); |
| + |
| + return EFI_SUCCESS; |
| +} |
| + |
| +/** |
| + Write a full or portion of a block. |
| + |
| + @param[in] Instance NOR flash Instance of variable store region. |
| + @param[in] Lba The starting logical block index to write to. |
| + @param[in] Offset Offset into the block at which to begin writing. |
| + @param[in, out] NumBytes The total size of the buffer. |
| + @param[in] Buffer The pointer to a caller-allocated buffer that |
| + contains the source for the write. |
| + |
| + @retval EFI_SUCCESS The write is completed. |
| +**/ |
| +EFI_STATUS |
| +NorFlashWriteSingleBlock ( |
| + IN NOR_FLASH_INSTANCE *Instance, |
| + IN EFI_LBA Lba, |
| + IN UINTN Offset, |
| + IN OUT UINTN *NumBytes, |
| + IN UINT8 *Buffer |
| + ) |
| +{ |
| + EFI_STATUS Status; |
| + UINT32 Tmp; |
| + UINT32 TmpBuf; |
| + UINT32 WordToWrite; |
| + UINT32 Mask; |
| + BOOLEAN DoErase; |
| + UINTN BytesToWrite; |
| + UINTN CurOffset; |
| + UINTN WordAddr; |
| + UINTN BlockSize; |
| + UINTN BlockAddress; |
| + UINTN PrevBlockAddress; |
| + |
| + if (Buffer == NULL) { |
| + DEBUG ((DEBUG_ERROR, |
| + "NorFlashWriteSingleBlock: ERROR - Buffer is invalid\n" )); |
| + return EFI_OUT_OF_RESOURCES; |
| + } |
| + |
| + PrevBlockAddress = 0; |
| + if (!Instance->Initialized && Instance->Initialize) { |
| + Instance->Initialize(Instance); |
| + } |
| + |
| + DEBUG ((DEBUG_INFO, |
| + "NorFlashWriteSingleBlock(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", |
| + Lba, Offset, *NumBytes, Buffer)); |
| + |
| + // Localise the block size to avoid de-referencing pointers all the time |
| + BlockSize = Instance->BlockSize; |
| + |
| + // The write must not span block boundaries. |
| + // We need to check each variable individually because adding two large |
| + // values together overflows. |
| + if (Offset >= BlockSize || |
| + *NumBytes > BlockSize || |
| + (Offset + *NumBytes) > BlockSize) { |
| + DEBUG ((DEBUG_ERROR, |
| + "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", |
| + Offset, *NumBytes, BlockSize )); |
| + return EFI_BAD_BUFFER_SIZE; |
| + } |
| + |
| + // We must have some bytes to write |
| + if (*NumBytes == 0) { |
| + DEBUG ((DEBUG_ERROR, |
| + "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", |
| + Offset, *NumBytes, BlockSize )); |
| + return EFI_BAD_BUFFER_SIZE; |
| + } |
| + |
| + // Pick 128bytes as a good start for word operations as opposed to erasing the |
| + // block and writing the data regardless if an erase is really needed. |
| + // It looks like most individual NV variable writes are smaller than 128bytes. |
| + if (*NumBytes <= 128) { |
| + // Check to see if we need to erase before programming the data into NOR. |
| + // If the destination bits are only changing from 1s to 0s we can just write. |
| + // After a block is erased all bits in the block is set to 1. |
| + // If any byte requires us to erase we just give up and rewrite all of it. |
| + DoErase = FALSE; |
| + BytesToWrite = *NumBytes; |
| + CurOffset = Offset; |
| + |
| + while (BytesToWrite > 0) { |
| + // Read full word from NOR, splice as required. A word is the smallest |
| + // unit we can write. |
| + Status = NorFlashRead ( |
| + Instance, |
| + Lba, |
| + CurOffset & ~(0x3), |
| + sizeof(Tmp), |
| + &Tmp |
| + ); |
| + if (EFI_ERROR (Status)) { |
| + return EFI_DEVICE_ERROR; |
| + } |
| + |
| + // Physical address of word in NOR to write. |
| + WordAddr = (CurOffset & ~(0x3)) + |
| + GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, |
| + BlockSize); |
| + |
| + // The word of data that is to be written. |
| + TmpBuf = ReadUnaligned32 ((UINT32 *)(Buffer + (*NumBytes - BytesToWrite))); |
| + |
| + // First do word aligned chunks. |
| + if ((CurOffset & 0x3) == 0) { |
| + if (BytesToWrite >= 4) { |
| + // Is the destination still in 'erased' state? |
| + if (~Tmp != 0) { |
| + // Check to see if we are only changing bits to zero. |
| + if ((Tmp ^ TmpBuf) & TmpBuf) { |
| + DoErase = TRUE; |
| + break; |
| + } |
| + } |
| + // Write this word to NOR |
| + WordToWrite = TmpBuf; |
| + CurOffset += sizeof(TmpBuf); |
| + BytesToWrite -= sizeof(TmpBuf); |
| + } else { |
| + // BytesToWrite < 4. Do small writes and left-overs |
| + Mask = ~((~0) << (BytesToWrite * 8)); |
| + // Mask out the bytes we want. |
| + TmpBuf &= Mask; |
| + // Is the destination still in 'erased' state? |
| + if ((Tmp & Mask) != Mask) { |
| + // Check to see if we are only changing bits to zero. |
| + if ((Tmp ^ TmpBuf) & TmpBuf) { |
| + DoErase = TRUE; |
| + break; |
| + } |
| + } |
| + // Merge old and new data. Write merged word to NOR |
| + WordToWrite = (Tmp & ~Mask) | TmpBuf; |
| + CurOffset += BytesToWrite; |
| + BytesToWrite = 0; |
| + } |
| + } else { |
| + // Do multiple words, but starting unaligned. |
| + if (BytesToWrite > (4 - (CurOffset & 0x3))) { |
| + Mask = ((~0) << ((CurOffset & 0x3) * 8)); |
| + // Mask out the bytes we want. |
| + TmpBuf &= Mask; |
| + // Is the destination still in 'erased' state? |
| + if ((Tmp & Mask) != Mask) { |
| + // Check to see if we are only changing bits to zero. |
| + if ((Tmp ^ TmpBuf) & TmpBuf) { |
| + DoErase = TRUE; |
| + break; |
| + } |
| + } |
| + // Merge old and new data. Write merged word to NOR |
| + WordToWrite = (Tmp & ~Mask) | TmpBuf; |
| + BytesToWrite -= (4 - (CurOffset & 0x3)); |
| + CurOffset += (4 - (CurOffset & 0x3)); |
| + } else { |
| + // Unaligned and fits in one word. |
| + Mask = (~((~0) << (BytesToWrite * 8))) << ((CurOffset & 0x3) * 8); |
| + // Mask out the bytes we want. |
| + TmpBuf = (TmpBuf << ((CurOffset & 0x3) * 8)) & Mask; |
| + // Is the destination still in 'erased' state? |
| + if ((Tmp & Mask) != Mask) { |
| + // Check to see if we are only changing bits to zero. |
| + if ((Tmp ^ TmpBuf) & TmpBuf) { |
| + DoErase = TRUE; |
| + break; |
| + } |
| + } |
| + // Merge old and new data. Write merged word to NOR |
| + WordToWrite = (Tmp & ~Mask) | TmpBuf; |
| + CurOffset += BytesToWrite; |
| + BytesToWrite = 0; |
| + } |
| + } |
| + |
| + BlockAddress = GET_NOR_BLOCK_ADDRESS ( |
| + Instance->RegionBaseAddress, |
| + Lba, |
| + BlockSize |
| + ); |
| + if (BlockAddress != PrevBlockAddress) { |
| + Status = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress); |
| + if (EFI_ERROR (Status)) { |
| + return EFI_DEVICE_ERROR; |
| + } |
| + PrevBlockAddress = BlockAddress; |
| + } |
| + Status = NorFlashWriteSingleWord (Instance, WordAddr, WordToWrite); |
| + if (EFI_ERROR (Status)) { |
| + return EFI_DEVICE_ERROR; |
| + } |
| + } |
| + // Exit if we got here and could write all the data. Otherwise do the |
| + // Erase-Write cycle. |
| + if (!DoErase) { |
| + return EFI_SUCCESS; |
| + } |
| + } |
| + |
| + // Check we did get some memory. Buffer is BlockSize. |
| + if (Instance->ShadowBuffer == NULL) { |
| + DEBUG ((DEBUG_ERROR, "FvbWrite: ERROR - Buffer not ready\n")); |
| + return EFI_DEVICE_ERROR; |
| + } |
| + |
| + // Read NOR Flash data into shadow buffer |
| + Status = NorFlashReadBlocks ( |
| + Instance, |
| + Lba, |
| + BlockSize, |
| + Instance->ShadowBuffer |
| + ); |
| + if (EFI_ERROR (Status)) { |
| + // Return one of the pre-approved error statuses |
| + return EFI_DEVICE_ERROR; |
| + } |
| + |
| + // Put the data at the appropriate location inside the buffer area |
| + CopyMem ((VOID*)((UINTN)Instance->ShadowBuffer + Offset), Buffer, *NumBytes); |
| + |
| + // Write the modified buffer back to the NorFlash |
| + Status = NorFlashWriteBlocks ( |
| + Instance, |
| + Lba, |
| + BlockSize, |
| + Instance->ShadowBuffer |
| + ); |
| + if (EFI_ERROR (Status)) { |
| + // Return one of the pre-approved error statuses |
| + return EFI_DEVICE_ERROR; |
| + } |
| + |
| + return EFI_SUCCESS; |
| +} |
| + |
| +/** |
| + Read JEDEC ID of NOR flash device. |
| + |
| + @param[in] Instance NOR flash Instance of variable store region. |
| + @param[out] JedecId JEDEC ID of NOR flash device. |
| + |
| + @retval EFI_SUCCESS The write is completed. |
| +**/ |
| +EFI_STATUS |
| +NorFlashReadID ( |
| + IN NOR_FLASH_INSTANCE *Instance, |
| + OUT UINT8 JedecId[3] |
| + ) |
| +{ |
| + UINT32 val; |
| + if (Instance == NULL || JedecId == NULL) { |
| + return EFI_INVALID_PARAMETER; |
| + } |
| + |
| + val = SPINOR_OP_RDID << CDNS_QSPI_FLASH_CMD_CTRL_REG_OPCODE_BIT_POS | |
| + CDNS_QSPI_FLASH_CMD_CTRL_REG_READ_ENABLE << CDNS_QSPI_FLASH_CMD_CTRL_REG_READEN_BIT_POS | |
| + CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_BYTE_3B << CDNS_QSPI_FLASH_CMD_CTRL_REG_READBYTE_BIT_POS; |
| + |
| + if (EFI_ERROR (CdnsQspiExecuteCommand (Instance, val))) { |
| + return EFI_DEVICE_ERROR; |
| + } |
| + |
| + val = MmioRead32 (Instance->HostRegisterBaseAddress + CDNS_QSPI_FLASH_CMD_READ_DATA_REG_OFFSET); |
| + |
| + // Manu.ID field |
| + JedecId[0] = (UINT8) val; |
| + // Type field |
| + JedecId[1] = (UINT8) (val >> 8); |
| + // Capacity field |
| + JedecId[2] = (UINT8) (val >> 16); |
| + |
| + DEBUG ((DEBUG_INFO, |
| + "Nor flash detected, Jedec ID, Manu.Id=%x Type=%x Capacity=%x \n", |
| + JedecId[0],JedecId[1],JedecId[2])); |
| + |
| + return EFI_SUCCESS; |
| +} |
| diff --git a/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlash.h b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlash.h |
| new file mode 100644 |
| index 00000000..e720937e |
| --- /dev/null |
| +++ b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlash.h |
| @@ -0,0 +1,484 @@ |
| +/** @file |
| + |
| + Copyright (c) 2023, ARM Limited. All rights reserved.<BR> |
| + |
| + SPDX-License-Identifier: BSD-2-Clause-Patent |
| + |
| +**/ |
| + |
| +#ifndef NOR_FLASH_DXE_H_ |
| +#define NOR_FLASH_DXE_H_ |
| + |
| +#include <Guid/EventGroup.h> |
| +#include <Library/DebugLib.h> |
| +#include <Library/IoLib.h> |
| +#include <Library/NorFlashPlatformLib.h> |
| +#include <PiDxe.h> |
| +#include <Protocol/BlockIo.h> |
| +#include <Protocol/DiskIo.h> |
| +#include <Protocol/FirmwareVolumeBlock.h> |
| + |
| +#include "CadenceQspiReg.h" |
| + |
| +#define NOR_FLASH_ERASE_RETRY 10 |
| + |
| +#define GET_NOR_BLOCK_ADDRESS(BaseAddr, Lba, LbaSize) \ |
| + ((BaseAddr) + (UINTN)((Lba) * (LbaSize))) |
| + |
| +#define NOR_FLASH_SIGNATURE SIGNATURE_32('S', 'n', 'o', 'r') |
| +#define INSTANCE_FROM_FVB_THIS(a) CR(a, NOR_FLASH_INSTANCE, FvbProtocol, \ |
| + NOR_FLASH_SIGNATURE) |
| + |
| +#define NOR_FLASH_POLL_FSR BIT0 |
| + |
| +typedef struct _NOR_FLASH_INSTANCE NOR_FLASH_INSTANCE; |
| + |
| +typedef EFI_STATUS (*NOR_FLASH_INITIALIZE) (NOR_FLASH_INSTANCE* Instance); |
| + |
| +#pragma pack(1) |
| +typedef struct { |
| + VENDOR_DEVICE_PATH Vendor; |
| + UINT8 Index; |
| + EFI_DEVICE_PATH_PROTOCOL End; |
| +} NOR_FLASH_DEVICE_PATH; |
| +#pragma pack() |
| + |
| +struct _NOR_FLASH_INSTANCE { |
| + UINT32 Signature; |
| + EFI_HANDLE Handle; |
| + |
| + BOOLEAN Initialized; |
| + NOR_FLASH_INITIALIZE Initialize; |
| + |
| + UINTN HostRegisterBaseAddress; |
| + UINTN DeviceBaseAddress; |
| + UINTN RegionBaseAddress; |
| + UINTN Size; |
| + UINTN BlockSize; |
| + UINTN LastBlock; |
| + EFI_LBA StartLba; |
| + EFI_LBA OffsetLba; |
| + |
| + EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL FvbProtocol; |
| + VOID* ShadowBuffer; |
| + |
| + NOR_FLASH_DEVICE_PATH DevicePath; |
| + |
| + UINT32 Flags; |
| +}; |
| + |
| +typedef struct { |
| + EFI_TPL OriginalTPL; |
| + BOOLEAN InterruptsEnabled; |
| +} NOR_FLASH_LOCK_CONTEXT; |
| + |
| +/** |
| + Lock all pending read/write to Nor flash device |
| + |
| + @param[in] Context Nor flash device context structure. |
| +**/ |
| +VOID |
| +EFIAPI |
| +NorFlashLock ( |
| + IN NOR_FLASH_LOCK_CONTEXT *Context |
| + ); |
| + |
| +/** |
| + Unlock all pending read/write to Nor flash device |
| + |
| + @param[in] Context Nor flash device context structure. |
| +**/ |
| +VOID |
| +EFIAPI |
| +NorFlashUnlock ( |
| + IN NOR_FLASH_LOCK_CONTEXT *Context |
| + ); |
| + |
| +extern UINTN mFlashNvStorageVariableBase; |
| + |
| +/** |
| + Create Nor flash Instance for given region. |
| + |
| + @param[in] HostRegisterBase Base address of Nor flash controller. |
| + @param[in] NorFlashDeviceBase Base address of flash device. |
| + @param[in] NorFlashRegionBase Base address of flash region on device. |
| + @param[in] NorFlashSize Size of flash region. |
| + @param[in] Index Index of given flash region. |
| + @param[in] BlockSize Block size of NOR flash device. |
| + @param[in] HasVarStore Boolean set for VarStore on given region. |
| + @param[out] NorFlashInstance Instance of given flash region. |
| + |
| + @retval EFI_SUCCESS On successful creation of NOR flash instance. |
| +**/ |
| +EFI_STATUS |
| +NorFlashCreateInstance ( |
| + IN UINTN HostRegisterBase, |
| + IN UINTN NorFlashDeviceBase, |
| + IN UINTN NorFlashRegionBase, |
| + IN UINTN NorFlashSize, |
| + IN UINT32 Index, |
| + IN UINT32 BlockSize, |
| + IN BOOLEAN HasVarStore, |
| + OUT NOR_FLASH_INSTANCE** NorFlashInstance |
| + ); |
| + |
| +/** |
| + Install Fv block on to variable store region |
| + |
| + @param[in] Instance Instance of Nor flash variable region. |
| + |
| + @retval EFI_SUCCESS The entry point is executed successfully. |
| +**/ |
| +EFI_STATUS |
| +EFIAPI |
| +NorFlashFvbInitialize ( |
| + IN NOR_FLASH_INSTANCE* Instance |
| + ); |
| + |
| +/** |
| + Check the integrity of firmware volume header. |
| + |
| + @param[in] Instance Instance of Nor flash variable region. |
| + |
| + @retval EFI_SUCCESS The firmware volume is consistent. |
| + @retval EFI_NOT_FOUND The firmware volume has been corrupted. |
| + |
| +**/ |
| +EFI_STATUS |
| +ValidateFvHeader ( |
| + IN NOR_FLASH_INSTANCE *Instance |
| + ); |
| + |
| +/** |
| + Initialize the FV Header and Variable Store Header |
| + to support variable operations. |
| + |
| + @param[in] Instance Location to Initialize the headers |
| + |
| + @retval EFI_SUCCESS Fv init is done |
| + |
| +**/ |
| +EFI_STATUS |
| +InitializeFvAndVariableStoreHeaders ( |
| + IN NOR_FLASH_INSTANCE *Instance |
| + ); |
| + |
| +/** |
| + Retrieves the attributes and current settings of the block. |
| + |
| + @param[in] This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. |
| + |
| + @param[out] Attributes Pointer to EFI_FVB_ATTRIBUTES_2 in which the attributes and |
| + current settings are returned. |
| + Type EFI_FVB_ATTRIBUTES_2 is defined in |
| + EFI_FIRMWARE_VOLUME_HEADER. |
| + |
| + @retval EFI_SUCCESS The firmware volume attributes were returned. |
| + |
| +**/ |
| +EFI_STATUS |
| +EFIAPI |
| +FvbGetAttributes( |
| + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, |
| + OUT EFI_FVB_ATTRIBUTES_2 *Attributes |
| + ); |
| + |
| +/** |
| + Sets configurable firmware volume attributes and returns the |
| + new settings of the firmware volume. |
| + |
| + |
| + @param[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. |
| + |
| + @param[in, out] Attributes On input, Attributes is a pointer to |
| + EFI_FVB_ATTRIBUTES_2 that contains the desired |
| + firmware volume settings. |
| + On successful return, it contains the new |
| + settings of the firmware volume. |
| + |
| + @retval EFI_UNSUPPORTED The firmware volume attributes are not supported. |
| + |
| +**/ |
| +EFI_STATUS |
| +EFIAPI |
| +FvbSetAttributes( |
| + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, |
| + IN OUT EFI_FVB_ATTRIBUTES_2 *Attributes |
| + ); |
| + |
| +/** |
| + Retrieves the base address of a memory-mapped firmware volume. |
| + This function should be called only for memory-mapped firmware volumes. |
| + |
| + @param[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. |
| + |
| + @param[out] Address Pointer to a caller-allocated |
| + EFI_PHYSICAL_ADDRESS that, on successful |
| + return from GetPhysicalAddress(), contains the |
| + base address of the firmware volume. |
| + |
| + @retval EFI_SUCCESS The firmware volume base address was returned. |
| + |
| +**/ |
| +EFI_STATUS |
| +EFIAPI |
| +FvbGetPhysicalAddress( |
| + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, |
| + OUT EFI_PHYSICAL_ADDRESS *Address |
| + ); |
| + |
| +/** |
| + Retrieves the size of the requested block. |
| + It also returns the number of additional blocks with the identical size. |
| + The GetBlockSize() function is used to retrieve the block map |
| + (see EFI_FIRMWARE_VOLUME_HEADER). |
| + |
| + |
| + @param[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. |
| + |
| + @param[in] Lba Indicates the block whose size to return |
| + |
| + @param[out] BlockSize Pointer to a caller-allocated UINTN in which |
| + the size of the block is returned. |
| + |
| + @param[out] NumberOfBlocks Pointer to a caller-allocated UINTN in |
| + which the number of consecutive blocks, |
| + starting with Lba, is returned. All |
| + blocks in this range have a size of |
| + BlockSize. |
| + |
| + @retval EFI_SUCCESS The firmware volume base address was returned. |
| + |
| + @retval EFI_INVALID_PARAMETER The requested LBA is out of range. |
| + |
| +**/ |
| +EFI_STATUS |
| +EFIAPI |
| +FvbGetBlockSize( |
| + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, |
| + IN EFI_LBA Lba, |
| + OUT UINTN *BlockSize, |
| + OUT UINTN *NumberOfBlocks |
| + ); |
| + |
| +/** |
| + Reads the specified number of bytes into a buffer from the specified block. |
| + |
| + The Read() function reads the requested number of bytes from the |
| + requested block and stores them in the provided buffer. |
| + |
| + @param[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. |
| + |
| + @param[in] Lba The starting logical block index from which to read |
| + |
| + @param[in] Offset Offset into the block at which to begin reading. |
| + |
| + @param[in, out] NumBytes Pointer to a UINTN. |
| + At entry, *NumBytes contains the total size of the |
| + buffer. *NumBytes should have a non zero value. |
| + At exit, *NumBytes contains the total number of |
| + bytes read. |
| + |
| + @param[in out] Buffer Pointer to a caller-allocated buffer that will be |
| + used to hold the data that is read. |
| + |
| + @retval EFI_SUCCESS The firmware volume was read successfully, and |
| + contents are in Buffer. |
| + |
| + @retval EFI_BAD_BUFFER_SIZE Read attempted across an LBA boundary. |
| + |
| + @retval EFI_DEVICE_ERROR The block device is not functioning correctly and |
| + could not be read. |
| + |
| +**/ |
| +EFI_STATUS |
| +EFIAPI |
| +FvbRead( |
| + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, |
| + IN EFI_LBA Lba, |
| + IN UINTN Offset, |
| + IN OUT UINTN *NumBytes, |
| + IN OUT UINT8 *Buffer |
| + ); |
| + |
| +/** |
| + Writes the specified number of bytes from the input buffer to the block. |
| + |
| + @param[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. |
| + |
| + @param[in] Lba The starting logical block index to write to. |
| + |
| + @param[in] Offset Offset into the block at which to begin writing. |
| + |
| + @param[in, out] NumBytes The pointer to a UINTN. |
| + At entry, *NumBytes contains the total size of the |
| + buffer. |
| + At exit, *NumBytes contains the total number of |
| + bytes actually written. |
| + |
| + @param[in] Buffer The pointer to a caller-allocated buffer that |
| + contains the source for the write. |
| + |
| + @retval EFI_SUCCESS The firmware volume was written successfully. |
| + |
| +**/ |
| +EFI_STATUS |
| +EFIAPI |
| +FvbWrite( |
| + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, |
| + IN EFI_LBA Lba, |
| + IN UINTN Offset, |
| + IN OUT UINTN *NumBytes, |
| + IN UINT8 *Buffer |
| + ); |
| + |
| +/** |
| + Erases and initialises a firmware volume block. |
| + |
| + @param[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL |
| + |
| + @param[in] ... The variable argument list is a list of tuples. |
| + Each tuple describes a range of LBAs to erase |
| + and consists of the following: |
| + - An EFI_LBA that indicates the starting LBA |
| + - A UINTN that indicates the number of blocks |
| + to erase. |
| + |
| + The list is terminated with an |
| + EFI_LBA_LIST_TERMINATOR. |
| + |
| + @retval EFI_SUCCESS The erase request successfully completed. |
| + |
| + @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled |
| + state. |
| + |
| + @retval EFI_DEVICE_ERROR The block device is not functioning correctly |
| + and could not be written. |
| + The firmware device may have been partially |
| + erased. |
| + |
| + @retval EFI_INVALID_PARAMETER One or more of the LBAs listed in the variable |
| + argument list do not exist in the firmware |
| + volume. |
| + |
| +**/ |
| +EFI_STATUS |
| +EFIAPI |
| +FvbEraseBlocks( |
| + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, |
| + ... |
| + ); |
| + |
| +/** |
| + This function unlock and erase an entire NOR Flash block. |
| + |
| + @param[in] Instance NOR flash Instance of variable store region. |
| + @param[in] BlockAddress Block address within the variable store region. |
| + |
| + @retval EFI_SUCCESS The erase and unlock successfully completed. |
| +**/ |
| +EFI_STATUS |
| +NorFlashUnlockAndEraseSingleBlock ( |
| + IN NOR_FLASH_INSTANCE *Instance, |
| + IN UINTN BlockAddress |
| + ); |
| + |
| +/** |
| + Write a full or portion of a block. |
| + |
| + @param[in] Instance NOR flash Instance of variable store region. |
| + @param[in] Lba The starting logical block index to write to. |
| + @param[in] Offset Offset into the block at which to begin writing. |
| + @param[in,out] NumBytes The total size of the buffer. |
| + @param[in] Buffer The pointer to a caller-allocated buffer that |
| + contains the source for the write. |
| + |
| + @retval EFI_SUCCESS The write is completed. |
| +**/ |
| +EFI_STATUS |
| +NorFlashWriteSingleBlock ( |
| + IN NOR_FLASH_INSTANCE *Instance, |
| + IN EFI_LBA Lba, |
| + IN UINTN Offset, |
| + IN OUT UINTN *NumBytes, |
| + IN UINT8 *Buffer |
| + ); |
| + |
| +/** |
| + Write a full block. |
| + |
| + @param[in] Instance NOR flash Instance of variable store region. |
| + @param[in] Lba The starting logical block index to write to. |
| + @param[in] BufferSizeInBytes The number of bytes to write. |
| + @param[in] Buffer The pointer to a caller-allocated buffer that |
| + contains the source for the write. |
| + |
| + @retval EFI_SUCCESS The write is completed. |
| +**/ |
| +EFI_STATUS |
| +NorFlashWriteBlocks ( |
| + IN NOR_FLASH_INSTANCE *Instance, |
| + IN EFI_LBA Lba, |
| + IN UINTN BufferSizeInBytes, |
| + IN VOID *Buffer |
| + ); |
| + |
| +/** |
| + Read a full block. |
| + |
| + @param[in] Instance NOR flash Instance of variable store region. |
| + @param[in] Lba The starting logical block index to read from. |
| + @param[in] BufferSizeInBytes The number of bytes to read. |
| + @param[out] Buffer The pointer to a caller-allocated buffer that |
| + should be copied with read data. |
| + |
| + @retval EFI_SUCCESS The read is completed. |
| +**/ |
| +EFI_STATUS |
| +NorFlashReadBlocks ( |
| + IN NOR_FLASH_INSTANCE *Instance, |
| + IN EFI_LBA Lba, |
| + IN UINTN BufferSizeInBytes, |
| + OUT VOID *Buffer |
| + ); |
| + |
| +/** |
| + Read from nor flash. |
| + |
| + @param[in] Instance NOR flash Instance of variable store region. |
| + @param[in] Lba The starting logical block index to read from. |
| + @param[in] Offset Offset into the block at which to begin reading. |
| + @param[in] BufferSizeInBytes The number of bytes to read. |
| + @param[out] Buffer The pointer to a caller-allocated buffer that |
| + should copied with read data. |
| + |
| + @retval EFI_SUCCESS The read is completed. |
| +**/ |
| +EFI_STATUS |
| +NorFlashRead ( |
| + IN NOR_FLASH_INSTANCE *Instance, |
| + IN EFI_LBA Lba, |
| + IN UINTN Offset, |
| + IN UINTN BufferSizeInBytes, |
| + OUT VOID *Buffer |
| + ); |
| + |
| +/** |
| + Read JEDEC ID of NOR flash device. |
| + |
| + @param[in] Instance NOR flash Instance of variable store region. |
| + @param[out] JedecId JEDEC ID of NOR flash device. |
| + |
| + @retval EFI_SUCCESS The write is completed. |
| +**/ |
| +EFI_STATUS |
| +NorFlashReadID ( |
| + IN NOR_FLASH_INSTANCE *Instance, |
| + OUT UINT8 JedecId[3] |
| + ); |
| + |
| +#define SPINOR_OP_WREN 0x06 // Write enable |
| +#define SPINOR_OP_BE_4K 0x20 // Erase 4KiB block |
| +#define SPINOR_OP_RDID 0x9f // Read JEDEC ID |
| + |
| +#endif /* NOR_FLASH_DXE_H_ */ |
| diff --git a/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlashFvb.c b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlashFvb.c |
| new file mode 100644 |
| index 00000000..edd84c07 |
| --- /dev/null |
| +++ b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlashFvb.c |
| @@ -0,0 +1,573 @@ |
| +/** @file |
| + |
| + Copyright (c) 2023, ARM Limited. All rights reserved.<BR> |
| + |
| + SPDX-License-Identifier: BSD-2-Clause-Patent |
| + |
| +**/ |
| + |
| +#include <Guid/VariableFormat.h> |
| +#include <Guid/SystemNvDataGuid.h> |
| + |
| +#include <Library/BaseLib.h> |
| +#include <Library/BaseMemoryLib.h> |
| +#include <Library/MemoryAllocationLib.h> |
| +#include <Library/PcdLib.h> |
| +#include <Library/UefiBootServicesTableLib.h> |
| +#include <Library/UefiLib.h> |
| + |
| +#include <PiDxe.h> |
| + |
| +#include "NorFlash.h" |
| + |
| +UINTN mFlashNvStorageVariableBase; |
| + |
| +/** |
| + Initialize the FV Header and Variable Store Header |
| + to support variable operations. |
| + |
| + @param[in] Instance Location to initialise the headers. |
| + |
| + @retval EFI_SUCCESS Fv init is done. |
| + |
| +**/ |
| +EFI_STATUS |
| +InitializeFvAndVariableStoreHeaders ( |
| + IN NOR_FLASH_INSTANCE *Instance |
| + ) |
| +{ |
| + EFI_STATUS Status; |
| + VOID* Headers; |
| + UINTN HeadersLength; |
| + EFI_FIRMWARE_VOLUME_HEADER *FirmwareVolumeHeader; |
| + VARIABLE_STORE_HEADER *VariableStoreHeader; |
| + |
| + if (!Instance->Initialized && Instance->Initialize) { |
| + Instance->Initialize (Instance); |
| + } |
| + |
| + HeadersLength = sizeof (EFI_FIRMWARE_VOLUME_HEADER) + |
| + sizeof (EFI_FV_BLOCK_MAP_ENTRY) + |
| + sizeof (VARIABLE_STORE_HEADER); |
| + Headers = AllocateZeroPool (HeadersLength); |
| + |
| + FirmwareVolumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)Headers; |
| + CopyGuid (&FirmwareVolumeHeader->FileSystemGuid, &gEfiSystemNvDataFvGuid); |
| + FirmwareVolumeHeader->FvLength = |
| + PcdGet32 (PcdFlashNvStorageVariableSize) + |
| + PcdGet32 (PcdFlashNvStorageFtwWorkingSize) + |
| + PcdGet32 (PcdFlashNvStorageFtwSpareSize); |
| + FirmwareVolumeHeader->Signature = EFI_FVH_SIGNATURE; |
| + FirmwareVolumeHeader->Attributes = EFI_FVB2_READ_ENABLED_CAP | |
| + EFI_FVB2_READ_STATUS | |
| + EFI_FVB2_STICKY_WRITE | |
| + EFI_FVB2_MEMORY_MAPPED | |
| + EFI_FVB2_ERASE_POLARITY | |
| + EFI_FVB2_WRITE_STATUS | |
| + EFI_FVB2_WRITE_ENABLED_CAP; |
| + |
| + FirmwareVolumeHeader->HeaderLength = sizeof (EFI_FIRMWARE_VOLUME_HEADER) + |
| + sizeof (EFI_FV_BLOCK_MAP_ENTRY); |
| + FirmwareVolumeHeader->Revision = EFI_FVH_REVISION; |
| + FirmwareVolumeHeader->BlockMap[0].NumBlocks = Instance->LastBlock + 1; |
| + FirmwareVolumeHeader->BlockMap[0].Length = Instance->BlockSize; |
| + FirmwareVolumeHeader->BlockMap[1].NumBlocks = 0; |
| + FirmwareVolumeHeader->BlockMap[1].Length = 0; |
| + FirmwareVolumeHeader->Checksum = CalculateCheckSum16 ( |
| + (UINT16*)FirmwareVolumeHeader, |
| + FirmwareVolumeHeader->HeaderLength); |
| + |
| + VariableStoreHeader = (VOID *)((UINTN)Headers + |
| + FirmwareVolumeHeader->HeaderLength); |
| + CopyGuid (&VariableStoreHeader->Signature, &gEfiAuthenticatedVariableGuid); |
| + VariableStoreHeader->Size = PcdGet32 (PcdFlashNvStorageVariableSize) - |
| + FirmwareVolumeHeader->HeaderLength; |
| + VariableStoreHeader->Format = VARIABLE_STORE_FORMATTED; |
| + VariableStoreHeader->State = VARIABLE_STORE_HEALTHY; |
| + |
| + // Install the combined super-header in the NorFlash |
| + Status = FvbWrite (&Instance->FvbProtocol, 0, 0, &HeadersLength, Headers); |
| + |
| + FreePool (Headers); |
| + return Status; |
| +} |
| + |
| +/** |
| + Check the integrity of firmware volume header. |
| + |
| + @param[in] Instance Instance of Nor flash variable region. |
| + |
| + @retval EFI_SUCCESS The firmware volume is consistent. |
| + @retval EFI_NOT_FOUND The firmware volume has been corrupted. |
| + |
| +**/ |
| +EFI_STATUS |
| +ValidateFvHeader ( |
| + IN NOR_FLASH_INSTANCE *Instance |
| + ) |
| +{ |
| + EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; |
| + VARIABLE_STORE_HEADER *VariableStoreHeader; |
| + UINTN VariableStoreLength; |
| + UINTN FvLength; |
| + |
| + FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER*)Instance->RegionBaseAddress; |
| + |
| + FvLength = PcdGet32 (PcdFlashNvStorageVariableSize) + |
| + PcdGet32 (PcdFlashNvStorageFtwWorkingSize) + |
| + PcdGet32 (PcdFlashNvStorageFtwSpareSize); |
| + |
| + if ((FwVolHeader->Revision != EFI_FVH_REVISION) |
| + || (FwVolHeader->Signature != EFI_FVH_SIGNATURE) |
| + || (FwVolHeader->FvLength != FvLength) |
| + ) |
| + { |
| + DEBUG ((DEBUG_ERROR, "%a: No Firmware Volume header present\n", |
| + __FUNCTION__)); |
| + return EFI_NOT_FOUND; |
| + } |
| + |
| + // Check the Firmware Volume Guid |
| + if (!CompareGuid (&FwVolHeader->FileSystemGuid, &gEfiSystemNvDataFvGuid)) { |
| + DEBUG ((DEBUG_ERROR, "%a: Firmware Volume Guid non-compatible\n", |
| + __FUNCTION__)); |
| + return EFI_NOT_FOUND; |
| + } |
| + |
| + VariableStoreHeader = (VOID *)((UINTN)FwVolHeader + |
| + FwVolHeader->HeaderLength); |
| + |
| + // Check the Variable Store Guid |
| + if (!CompareGuid (&VariableStoreHeader->Signature, &gEfiVariableGuid) && |
| + !CompareGuid (&VariableStoreHeader->Signature, |
| + &gEfiAuthenticatedVariableGuid)) { |
| + DEBUG ((DEBUG_ERROR, "%a: Variable Store Guid non-compatible\n", |
| + __FUNCTION__)); |
| + return EFI_NOT_FOUND; |
| + } |
| + |
| + VariableStoreLength = PcdGet32 (PcdFlashNvStorageVariableSize) - |
| + FwVolHeader->HeaderLength; |
| + if (VariableStoreHeader->Size != VariableStoreLength) { |
| + DEBUG ((DEBUG_ERROR, "%a: Variable Store Length does not match\n", |
| + __FUNCTION__)); |
| + return EFI_NOT_FOUND; |
| + } |
| + return EFI_SUCCESS; |
| +} |
| + |
| +/** |
| + Retrieves the attributes and current settings of the block. |
| + |
| + @param[in] This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. |
| + |
| + @param[out] Attributes Pointer to EFI_FVB_ATTRIBUTES_2 in which the attributes and |
| + current settings are returned. |
| + Type EFI_FVB_ATTRIBUTES_2 is defined in |
| + EFI_FIRMWARE_VOLUME_HEADER. |
| + |
| + @retval EFI_SUCCESS The firmware volume attributes were returned. |
| + |
| +**/ |
| +EFI_STATUS |
| +EFIAPI |
| +FvbGetAttributes( |
| + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, |
| + OUT EFI_FVB_ATTRIBUTES_2 *Attributes |
| + ) |
| +{ |
| + EFI_FVB_ATTRIBUTES_2 FlashFvbAttributes; |
| + |
| + FlashFvbAttributes = EFI_FVB2_READ_ENABLED_CAP | EFI_FVB2_READ_STATUS | |
| + EFI_FVB2_WRITE_ENABLED_CAP | EFI_FVB2_WRITE_STATUS | |
| + EFI_FVB2_STICKY_WRITE | EFI_FVB2_MEMORY_MAPPED | |
| + EFI_FVB2_ERASE_POLARITY; |
| + |
| + *Attributes = FlashFvbAttributes; |
| + |
| + DEBUG ((DEBUG_INFO, "FvbGetAttributes(0x%X)\n", *Attributes)); |
| + |
| + return EFI_SUCCESS; |
| +} |
| + |
| +/** |
| + Sets configurable firmware volume attributes and returns the |
| + new settings of the firmware volume. |
| + |
| + |
| + @param[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. |
| + |
| + @param[in, out] Attributes On input, Attributes is a pointer to |
| + EFI_FVB_ATTRIBUTES_2 that contains the desired |
| + firmware volume settings. |
| + On successful return, it contains the new |
| + settings of the firmware volume. |
| + |
| + @retval EFI_UNSUPPORTED The firmware volume attributes are not supported. |
| + |
| +**/ |
| +EFI_STATUS |
| +EFIAPI |
| +FvbSetAttributes( |
| + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, |
| + IN OUT EFI_FVB_ATTRIBUTES_2 *Attributes |
| + ) |
| +{ |
| + DEBUG ((DEBUG_INFO, "FvbSetAttributes(0x%X) is not supported\n", |
| + *Attributes)); |
| + return EFI_UNSUPPORTED; |
| +} |
| + |
| +/** |
| + Retrieves the base address of a memory-mapped firmware volume. |
| + This function should be called only for memory-mapped firmware volumes. |
| + |
| + @param[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. |
| + |
| + @param[out] Address Pointer to a caller-allocated |
| + EFI_PHYSICAL_ADDRESS that, on successful |
| + return from GetPhysicalAddress(), contains the |
| + base address of the firmware volume. |
| + |
| + @retval EFI_SUCCESS The firmware volume base address was returned. |
| + |
| +**/ |
| +EFI_STATUS |
| +EFIAPI |
| +FvbGetPhysicalAddress ( |
| + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, |
| + OUT EFI_PHYSICAL_ADDRESS *Address |
| + ) |
| +{ |
| + NOR_FLASH_INSTANCE *Instance; |
| + |
| + Instance = INSTANCE_FROM_FVB_THIS (This); |
| + |
| + DEBUG ((DEBUG_INFO, "FvbGetPhysicalAddress(BaseAddress=0x%08x)\n", |
| + Instance->RegionBaseAddress)); |
| + |
| + ASSERT(Address != NULL); |
| + |
| + *Address = Instance->RegionBaseAddress; |
| + return EFI_SUCCESS; |
| +} |
| + |
| +/** |
| + Retrieves the size of the requested block. |
| + It also returns the number of additional blocks with the identical size. |
| + The GetBlockSize() function is used to retrieve the block map |
| + (see EFI_FIRMWARE_VOLUME_HEADER). |
| + |
| + |
| + @param[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. |
| + |
| + @param[in] Lba Indicates the block whose size to return |
| + |
| + @param[out] BlockSize Pointer to a caller-allocated UINTN in which |
| + the size of the block is returned. |
| + |
| + @param[out] NumberOfBlocks Pointer to a caller-allocated UINTN in |
| + which the number of consecutive blocks, |
| + starting with Lba, is returned. All |
| + blocks in this range have a size of |
| + BlockSize. |
| + |
| + @retval EFI_SUCCESS The firmware volume base address was returned. |
| + |
| + @retval EFI_INVALID_PARAMETER The requested LBA is out of range. |
| + |
| +**/ |
| +EFI_STATUS |
| +EFIAPI |
| +FvbGetBlockSize ( |
| + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, |
| + IN EFI_LBA Lba, |
| + OUT UINTN *BlockSize, |
| + OUT UINTN *NumberOfBlocks |
| + ) |
| +{ |
| + EFI_STATUS Status; |
| + NOR_FLASH_INSTANCE *Instance; |
| + |
| + Instance = INSTANCE_FROM_FVB_THIS (This); |
| + |
| + DEBUG ((DEBUG_INFO, |
| + "FvbGetBlockSize(Lba=%ld, BlockSize=0x%x, LastBlock=%ld)\n", Lba, |
| + Instance->BlockSize, Instance->LastBlock)); |
| + |
| + if (Lba > Instance->LastBlock) { |
| + DEBUG ((DEBUG_ERROR, |
| + "FvbGetBlockSize: ERROR - Parameter LBA %ld is beyond the last Lba (%ld).\n", |
| + Lba, Instance->LastBlock)); |
| + Status = EFI_INVALID_PARAMETER; |
| + } else { |
| + // This is easy because in this platform each NorFlash device has equal sized blocks. |
| + *BlockSize = (UINTN) Instance->BlockSize; |
| + *NumberOfBlocks = (UINTN) (Instance->LastBlock - Lba + 1); |
| + |
| + DEBUG ((DEBUG_INFO, |
| + "FvbGetBlockSize: *BlockSize=0x%x, *NumberOfBlocks=0x%x.\n", *BlockSize, |
| + *NumberOfBlocks)); |
| + |
| + Status = EFI_SUCCESS; |
| + } |
| + |
| + return Status; |
| +} |
| + |
| +/** |
| + Reads the specified number of bytes into a buffer from the specified block. |
| + |
| + The Read() function reads the requested number of bytes from the |
| + requested block and stores them in the provided buffer. |
| + |
| + @param[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. |
| + |
| + @param[in] Lba The starting logical block index from which to read |
| + |
| + @param[in] Offset Offset into the block at which to begin reading. |
| + |
| + @param[in, out] NumBytes Pointer to a UINTN. |
| + At entry, *NumBytes contains the total size of the |
| + buffer. *NumBytes should have a non zero value. |
| + At exit, *NumBytes contains the total number of |
| + bytes read. |
| + |
| + @param[in, out] Buffer Pointer to a caller-allocated buffer that will be |
| + used to hold the data that is read. |
| + |
| + @retval EFI_SUCCESS The firmware volume was read successfully, and |
| + contents are in Buffer. |
| + |
| + @retval EFI_BAD_BUFFER_SIZE Read attempted across an LBA boundary. |
| + |
| + @retval EFI_DEVICE_ERROR The block device is not functioning correctly and |
| + could not be read. |
| + |
| +**/ |
| +EFI_STATUS |
| +EFIAPI |
| +FvbRead ( |
| + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, |
| + IN EFI_LBA Lba, |
| + IN UINTN Offset, |
| + IN OUT UINTN *NumBytes, |
| + IN OUT UINT8 *Buffer |
| + ) |
| +{ |
| + EFI_STATUS Status; |
| + UINTN BlockSize; |
| + NOR_FLASH_INSTANCE *Instance; |
| + |
| + Instance = INSTANCE_FROM_FVB_THIS (This); |
| + |
| + DEBUG ((DEBUG_INFO, |
| + "FvbRead(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", |
| + Instance->StartLba + Lba, Offset, *NumBytes, Buffer)); |
| + |
| + if (!Instance->Initialized && Instance->Initialize) { |
| + Instance->Initialize(Instance); |
| + } |
| + |
| + BlockSize = Instance->BlockSize; |
| + |
| + DEBUG ((DEBUG_INFO, |
| + "FvbRead: Check if (Offset=0x%x + NumBytes=0x%x) <= BlockSize=0x%x\n", |
| + Offset, *NumBytes, BlockSize )); |
| + |
| + // The read must not span block boundaries. |
| + // We need to check each variable individually because adding two large |
| + // values together overflows. |
| + if (Offset >= BlockSize || |
| + *NumBytes > BlockSize || |
| + (Offset + *NumBytes) > BlockSize) { |
| + DEBUG ((DEBUG_ERROR, |
| + "FvbRead: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", |
| + Offset, *NumBytes, BlockSize )); |
| + return EFI_BAD_BUFFER_SIZE; |
| + } |
| + |
| + // We must have some bytes to read |
| + if (*NumBytes == 0) { |
| + return EFI_BAD_BUFFER_SIZE; |
| + } |
| + |
| + // Decide if we are doing full block reads or not. |
| + if (*NumBytes % BlockSize != 0) { |
| + Status = NorFlashRead (Instance, Instance->StartLba + Lba, Offset, |
| + *NumBytes, Buffer); |
| + } else { |
| + // Read NOR Flash data into shadow buffer |
| + Status = NorFlashReadBlocks (Instance, Instance->StartLba + Lba, |
| + BlockSize, Buffer); |
| + } |
| + if (EFI_ERROR (Status)) { |
| + // Return one of the pre-approved error statuses |
| + return EFI_DEVICE_ERROR; |
| + } |
| + return EFI_SUCCESS; |
| +} |
| + |
| +/** |
| + Writes the specified number of bytes from the input buffer to the block. |
| + |
| + @param[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance. |
| + |
| + @param[in] Lba The starting logical block index to write to. |
| + |
| + @param[in] Offset Offset into the block at which to begin writing. |
| + |
| + @param[in, out] NumBytes The pointer to a UINTN. |
| + At entry, *NumBytes contains the total size of the |
| + buffer. |
| + At exit, *NumBytes contains the total number of |
| + bytes actually written. |
| + |
| + @param[in] Buffer The pointer to a caller-allocated buffer that |
| + contains the source for the write. |
| + |
| + @retval EFI_SUCCESS The firmware volume was written successfully. |
| + |
| +**/ |
| +EFI_STATUS |
| +EFIAPI |
| +FvbWrite ( |
| + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, |
| + IN EFI_LBA Lba, |
| + IN UINTN Offset, |
| + IN OUT UINTN *NumBytes, |
| + IN UINT8 *Buffer |
| + ) |
| +{ |
| + NOR_FLASH_INSTANCE *Instance; |
| + |
| + Instance = INSTANCE_FROM_FVB_THIS (This); |
| + |
| + return NorFlashWriteSingleBlock (Instance, Instance->StartLba + Lba, Offset, |
| + NumBytes, Buffer); |
| +} |
| + |
| +/** |
| + Erases and initialises a firmware volume block. |
| + |
| + @param[in] This EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL |
| + |
| + @param[in] ... The variable argument list is a list of tuples. |
| + Each tuple describes a range of LBAs to erase |
| + and consists of the following: |
| + - An EFI_LBA that indicates the starting LBA |
| + - A UINTN that indicates the number of blocks |
| + to erase. |
| + |
| + The list is terminated with an |
| + EFI_LBA_LIST_TERMINATOR. |
| + |
| + @retval EFI_SUCCESS The erase request successfully completed. |
| + |
| + @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled |
| + state. |
| + |
| + @retval EFI_DEVICE_ERROR The block device is not functioning correctly |
| + and could not be written. |
| + The firmware device may have been partially |
| + erased. |
| + |
| + @retval EFI_INVALID_PARAMETER One or more of the LBAs listed in the variable |
| + argument list do not exist in the firmware |
| + volume. |
| + |
| +**/ |
| +EFI_STATUS |
| +EFIAPI |
| +FvbEraseBlocks ( |
| + IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This, |
| + ... |
| + ) |
| +{ |
| + EFI_STATUS Status; |
| + VA_LIST Args; |
| + UINTN BlockAddress; // Physical address of Lba to erase |
| + EFI_LBA StartingLba; // Lba from which we start erasing |
| + UINTN NumOfLba; // Number of Lba blocks to erase |
| + NOR_FLASH_INSTANCE *Instance; |
| + |
| + Instance = INSTANCE_FROM_FVB_THIS (This); |
| + |
| + DEBUG ((DEBUG_INFO, "FvbEraseBlocks()\n")); |
| + |
| + Status = EFI_SUCCESS; |
| + |
| + // Before erasing, check the entire list of parameters to ensure |
| + // all specified blocks are valid |
| + |
| + VA_START (Args, This); |
| + do { |
| + // Get the Lba from which we start erasing |
| + StartingLba = VA_ARG (Args, EFI_LBA); |
| + |
| + // Have we reached the end of the list? |
| + if (StartingLba == EFI_LBA_LIST_TERMINATOR) { |
| + break; |
| + } |
| + |
| + // How many Lba blocks are we requested to erase? |
| + NumOfLba = VA_ARG (Args, UINT32); |
| + |
| + // All blocks must be within range |
| + DEBUG ((DEBUG_INFO, |
| + "FvbEraseBlocks: Check if: ( StartingLba=%ld + NumOfLba=%d - 1 ) > LastBlock=%ld.\n", |
| + Instance->StartLba + StartingLba, NumOfLba, Instance->LastBlock)); |
| + if (NumOfLba == 0 || |
| + (Instance->StartLba + StartingLba + NumOfLba - 1) > |
| + Instance->LastBlock) { |
| + VA_END (Args); |
| + DEBUG ((DEBUG_ERROR, |
| + "FvbEraseBlocks: ERROR - Lba range goes past the last Lba.\n")); |
| + return EFI_INVALID_PARAMETER; |
| + } |
| + } while (TRUE); |
| + VA_END (Args); |
| + |
| + VA_START (Args, This); |
| + do { |
| + // Get the Lba from which we start erasing |
| + StartingLba = VA_ARG (Args, EFI_LBA); |
| + |
| + // Have we reached the end of the list? |
| + if (StartingLba == EFI_LBA_LIST_TERMINATOR) { |
| + // Exit the while loop |
| + break; |
| + } |
| + |
| + // How many Lba blocks are we requested to erase? |
| + NumOfLba = VA_ARG (Args, UINT32); |
| + |
| + // Go through each one and erase it |
| + while (NumOfLba > 0) { |
| + |
| + // Get the physical address of Lba to erase |
| + BlockAddress = GET_NOR_BLOCK_ADDRESS ( |
| + Instance->RegionBaseAddress, |
| + Instance->StartLba + StartingLba, |
| + Instance->BlockSize |
| + ); |
| + |
| + // Erase it |
| + DEBUG ((DEBUG_INFO, "FvbEraseBlocks: Erasing Lba=%ld @ 0x%08x.\n", |
| + Instance->StartLba + StartingLba, BlockAddress)); |
| + Status = NorFlashUnlockAndEraseSingleBlock (Instance, BlockAddress); |
| + if (EFI_ERROR(Status)) { |
| + VA_END (Args); |
| + return EFI_DEVICE_ERROR; |
| + } |
| + |
| + // Move to the next Lba |
| + StartingLba++; |
| + NumOfLba--; |
| + } |
| + } while (TRUE); |
| + VA_END (Args); |
| + |
| + return Status; |
| + |
| +} |
| diff --git a/Platform/ARM/N1Sdp/N1SdpPlatform.dec b/Platform/ARM/N1Sdp/N1SdpPlatform.dec |
| index 16937197..986a078f 100644 |
| --- a/Platform/ARM/N1Sdp/N1SdpPlatform.dec |
| +++ b/Platform/ARM/N1Sdp/N1SdpPlatform.dec |
| @@ -1,7 +1,7 @@ |
| ## @file
|
| # Describes the N1Sdp configuration.
|
| #
|
| -# Copyright (c) 2021, ARM Limited. All rights reserved.<BR>
|
| +# Copyright (c) 2021-2022, ARM Limited. All rights reserved.<BR>
|
| #
|
| # SPDX-License-Identifier: BSD-2-Clause-Patent
|
| ##
|
| @@ -89,3 +89,6 @@ |
| # unmapped reserved region results in a DECERR response.
|
| #
|
| gArmN1SdpTokenSpaceGuid.PcdCsComponentSize|0x1000|UINT32|0x00000049
|
| +
|
| + # Base address of Cadence QSPI controller configuration registers
|
| + gArmN1SdpTokenSpaceGuid.PcdCadenceQspiDxeRegBaseAddress|0x1C0C0000|UINT32|0x0000004A
|