X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=ArmPlatformPkg%2FDrivers%2FNorFlashDxe%2FNorFlashDxe.c;h=7030a3c866c342dc2a6eb4afbbe3329fb592761c;hp=a3d5bf604080d5d5ba68da23f1ae24de4d0554c1;hb=452a9ee1a637baf7894943c9f850175f679f8ce6;hpb=d5e12da4fea90f1293d4731aa8a5da0097f268d5 diff --git a/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.c b/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.c index a3d5bf6040..7030a3c866 100644 --- a/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.c +++ b/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.c @@ -1,50 +1,54 @@ -/** @file NorFlashDxe.c - - Copyright (c) 2011, ARM Ltd. All rights reserved.
- This program and the accompanying materials - are licensed and made available under the terms and conditions of the BSD License - which accompanies this distribution. The full text of the license may be found at - http://opensource.org/licenses/bsd-license.php - - THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, - WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -**/ - -#include -#include -#include -#include -#include - -#include "NorFlashDxe.h" - - -// -// Global variable declarations -// -NOR_FLASH_INSTANCE **mNorFlashInstances; - -NOR_FLASH_INSTANCE mNorFlashInstanceTemplate = { - NOR_FLASH_SIGNATURE, // Signature - NULL, // Handle ... NEED TO BE FILLED - - FALSE, // Initialized - NULL, // Initialize - - 0, // BaseAddress ... NEED TO BE FILLED - 0, // Size ... NEED TO BE FILLED - 0, // StartLba - +/** @file NorFlashDxe.c + + Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include +#include + +#include "NorFlashDxe.h" + +STATIC EFI_EVENT mNorFlashVirtualAddrChangeEvent; + +// +// Global variable declarations +// +NOR_FLASH_INSTANCE **mNorFlashInstances; +UINT32 mNorFlashDeviceCount; + +NOR_FLASH_INSTANCE mNorFlashInstanceTemplate = { + NOR_FLASH_SIGNATURE, // Signature + NULL, // Handle ... NEED TO BE FILLED + + FALSE, // Initialized + NULL, // Initialize + + 0, // DeviceBaseAddress ... NEED TO BE FILLED + 0, // RegionBaseAddress ... NEED TO BE FILLED + 0, // Size ... NEED TO BE FILLED + 0, // StartLba + { EFI_BLOCK_IO_PROTOCOL_REVISION2, // Revision NULL, // Media ... NEED TO BE FILLED NorFlashBlockIoReset, // Reset; - NorFlashBlockIoReadBlocks, // ReadBlocks - NorFlashBlockIoWriteBlocks, // WriteBlocks + NorFlashBlockIoReadBlocks, // ReadBlocks + NorFlashBlockIoWriteBlocks, // WriteBlocks NorFlashBlockIoFlushBlocks // FlushBlocks - }, // BlockIoProtocol - + }, // BlockIoProtocol + { 0, // MediaId ... NEED TO BE FILLED FALSE, // RemovableMedia @@ -57,714 +61,1246 @@ NOR_FLASH_INSTANCE mNorFlashInstanceTemplate = { 0, // LastBlock ... NEED TO BE FILLED 0, // LowestAlignedLba 1, // LogicalBlocksPerPhysicalBlock - }, //Media; - - FALSE, // SupportFvb ... NEED TO BE FILLED + }, //Media; + { - FvbGetAttributes, // GetAttributes - FvbSetAttributes, // SetAttributes - FvbGetPhysicalAddress, // GetPhysicalAddress - FvbGetBlockSize, // GetBlockSize - FvbRead, // Read - FvbWrite, // Write + EFI_DISK_IO_PROTOCOL_REVISION, // Revision + NorFlashDiskIoReadDisk, // ReadDisk + NorFlashDiskIoWriteDisk // WriteDisk + }, + + FALSE, // SupportFvb ... NEED TO BE FILLED + { + FvbGetAttributes, // GetAttributes + FvbSetAttributes, // SetAttributes + FvbGetPhysicalAddress, // GetPhysicalAddress + FvbGetBlockSize, // GetBlockSize + FvbRead, // Read + FvbWrite, // Write FvbEraseBlocks, // EraseBlocks NULL, //ParentHandle - }, // FvbProtoccol; - - { - { - { - HARDWARE_DEVICE_PATH, - HW_VENDOR_DP, - (UINT8)( sizeof(VENDOR_DEVICE_PATH) ), - (UINT8)((sizeof(VENDOR_DEVICE_PATH)) >> 8), - }, - { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, // GUID ... NEED TO BE FILLED - }, - { - END_DEVICE_PATH_TYPE, - END_ENTIRE_DEVICE_PATH_SUBTYPE, - sizeof (EFI_DEVICE_PATH_PROTOCOL), - 0 - } - } // DevicePath -}; - -EFI_STATUS -NorFlashCreateInstance ( - IN UINTN NorFlashBase, - IN UINTN NorFlashSize, - IN UINT32 MediaId, - IN UINT32 BlockSize, - IN BOOLEAN SupportFvb, - IN CONST GUID *NorFlashGuid, - OUT NOR_FLASH_INSTANCE** NorFlashInstance - ) -{ - EFI_STATUS Status; - NOR_FLASH_INSTANCE* Instance; - - ASSERT(NorFlashInstance != NULL); - - Instance = AllocateCopyPool (sizeof(NOR_FLASH_INSTANCE),&mNorFlashInstanceTemplate); - if (Instance == NULL) { - return EFI_OUT_OF_RESOURCES; - } - - Instance->BaseAddress = NorFlashBase; - Instance->Size = NorFlashSize; - - Instance->BlockIoProtocol.Media = &Instance->Media; - Instance->Media.MediaId = MediaId; - Instance->Media.BlockSize = BlockSize; - Instance->Media.LastBlock = (NorFlashSize / BlockSize)-1; - - CopyGuid (&Instance->DevicePath.Vendor.Guid,NorFlashGuid); - - if (SupportFvb) { - Instance->SupportFvb = TRUE; - Instance->Initialize = NorFlashFvbInitialize; - - Status = gBS->InstallMultipleProtocolInterfaces ( - &Instance->Handle, - &gEfiDevicePathProtocolGuid, &Instance->DevicePath, - &gEfiBlockIoProtocolGuid, &Instance->BlockIoProtocol, - &gEfiFirmwareVolumeBlockProtocolGuid, &Instance->FvbProtocol, - NULL - ); - if (EFI_ERROR(Status)) { - FreePool(Instance); - return Status; - } - } else { - Instance->Initialize = NorFlashBlkIoInitialize; - - Status = gBS->InstallMultipleProtocolInterfaces ( - &Instance->Handle, - &gEfiDevicePathProtocolGuid, &Instance->DevicePath, - &gEfiBlockIoProtocolGuid, &Instance->BlockIoProtocol, - NULL - ); - if (EFI_ERROR(Status)) { - FreePool(Instance); - return Status; - } - } + }, // FvbProtoccol; + NULL, // ShadowBuffer + { + { + { + HARDWARE_DEVICE_PATH, + HW_VENDOR_DP, + (UINT8)( sizeof(VENDOR_DEVICE_PATH) ), + (UINT8)((sizeof(VENDOR_DEVICE_PATH)) >> 8), + }, + { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }, // GUID ... NEED TO BE FILLED + }, + { + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + sizeof (EFI_DEVICE_PATH_PROTOCOL), + 0 + } + } // DevicePath +}; + +EFI_STATUS +NorFlashCreateInstance ( + IN UINTN NorFlashDeviceBase, + IN UINTN NorFlashRegionBase, + IN UINTN NorFlashSize, + IN UINT32 MediaId, + IN UINT32 BlockSize, + IN BOOLEAN SupportFvb, + IN CONST GUID *NorFlashGuid, + OUT NOR_FLASH_INSTANCE** NorFlashInstance + ) +{ + EFI_STATUS Status; + NOR_FLASH_INSTANCE* Instance; + + ASSERT(NorFlashInstance != NULL); + + Instance = AllocateRuntimeCopyPool (sizeof(NOR_FLASH_INSTANCE),&mNorFlashInstanceTemplate); + if (Instance == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Instance->DeviceBaseAddress = NorFlashDeviceBase; + Instance->RegionBaseAddress = NorFlashRegionBase; + Instance->Size = NorFlashSize; + + Instance->BlockIoProtocol.Media = &Instance->Media; + Instance->Media.MediaId = MediaId; + Instance->Media.BlockSize = BlockSize; + Instance->Media.LastBlock = (NorFlashSize / BlockSize)-1; + + CopyGuid (&Instance->DevicePath.Vendor.Guid, NorFlashGuid); + + Instance->ShadowBuffer = AllocateRuntimePool (BlockSize);; + if (Instance->ShadowBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (SupportFvb) { + Instance->SupportFvb = TRUE; + Instance->Initialize = NorFlashFvbInitialize; + + Status = gBS->InstallMultipleProtocolInterfaces ( + &Instance->Handle, + &gEfiDevicePathProtocolGuid, &Instance->DevicePath, + &gEfiBlockIoProtocolGuid, &Instance->BlockIoProtocol, + &gEfiFirmwareVolumeBlockProtocolGuid, &Instance->FvbProtocol, + NULL + ); + if (EFI_ERROR(Status)) { + FreePool (Instance); + return Status; + } + } else { + Instance->Initialized = TRUE; + + Status = gBS->InstallMultipleProtocolInterfaces ( + &Instance->Handle, + &gEfiDevicePathProtocolGuid, &Instance->DevicePath, + &gEfiBlockIoProtocolGuid, &Instance->BlockIoProtocol, + &gEfiDiskIoProtocolGuid, &Instance->DiskIoProtocol, + NULL + ); + if (EFI_ERROR(Status)) { + FreePool (Instance); + return Status; + } + } *NorFlashInstance = Instance; return Status; -} - -EFI_STATUS -NorFlashReadCfiData ( - IN UINTN BaseAddress, - IN UINTN CfiOffset, - IN UINT32 NumberOfBytes, - OUT UINT32 *Data - ) -{ - UINT32 CurrentByte; - UINTN ReadAddress; - UINT32 ReadData; - UINT32 Byte1; - UINT32 Byte2; - UINT32 CombinedData = 0; - EFI_STATUS Status = EFI_SUCCESS; - - - if (NumberOfBytes > 4) { - // Using 32 bit variable so can only read 4 bytes - return EFI_INVALID_PARAMETER; - } - - // First combine the base address with the offset address to create an absolute read address. - // However, because we are in little endian, read from the last address down to the first - ReadAddress = CREATE_NOR_ADDRESS (BaseAddress, CfiOffset) + (NumberOfBytes - 1) * sizeof(UINT32); - - // Although each read returns 32 bits, because of the NOR Flash structure, - // each 16 bits (16 MSB and 16 LSB) come from two different chips. - // When in CFI mode, each chip read returns valid data in only the 8 LSBits; - // the 8 MSBits are invalid and can be ignored. - // Therefore, each read address returns one byte from each chip. - // - // Also note: As we are in little endian notation and we are reading - // bytes from incremental addresses, we should assemble them in little endian order. - for (CurrentByte=0; CurrentByteDeviceBaseAddress, 0, P30_CMD_READ_STATUS_REGISTER); + return MmioRead32 (Instance->DeviceBaseAddress); +} + + +BOOLEAN +NorFlashBlockIsLocked ( + IN NOR_FLASH_INSTANCE *Instance, + IN UINTN BlockAddress + ) +{ + UINT32 LockStatus; + BOOLEAN BlockIsLocked; + + BlockIsLocked = TRUE; + + // Send command for reading device id + SEND_NOR_COMMAND (BlockAddress, 2, P30_CMD_READ_DEVICE_ID); + + // Read block lock status + LockStatus = MmioRead32 (CREATE_NOR_ADDRESS(BlockAddress, 2)); + + // Decode block lock status + LockStatus = FOLD_32BIT_INTO_16BIT(LockStatus); + + if ((LockStatus & 0x2) != 0) { + DEBUG((EFI_D_ERROR, "NorFlashBlockIsLocked: WARNING: Block LOCKED DOWN\n")); + } + + if ((LockStatus & 0x1) == 0) { + // This means the block is unlocked + DEBUG((DEBUG_BLKIO, "UnlockSingleBlock: Block 0x%08x unlocked\n", BlockAddress)); + BlockIsLocked = FALSE; + } + + return BlockIsLocked; +} + + +EFI_STATUS +NorFlashUnlockSingleBlock ( + IN NOR_FLASH_INSTANCE *Instance, + IN UINTN BlockAddress + ) +{ + EFI_STATUS Status = EFI_SUCCESS; + UINT32 LockStatus; + + // Raise the Task Priority Level to TPL_NOTIFY to serialise all its operations + // and to protect shared data structures. + + if (FeaturePcdGet (PcdNorFlashCheckBlockLocked) == TRUE) { + do { + // Request a lock setup + SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_LOCK_BLOCK_SETUP); + + // Request an unlock + SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_UNLOCK_BLOCK); + + // Send command for reading device id + SEND_NOR_COMMAND (BlockAddress, 2, P30_CMD_READ_DEVICE_ID); + + // Read block lock status + LockStatus = MmioRead32 (CREATE_NOR_ADDRESS(BlockAddress, 2)); + + // Decode block lock status + LockStatus = FOLD_32BIT_INTO_16BIT(LockStatus); + } while ((LockStatus & 0x1) == 1); + } else { + // Request a lock setup + SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_LOCK_BLOCK_SETUP); + + // Request an unlock + SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_UNLOCK_BLOCK); + + // Wait until the status register gives us the all clear + do { + LockStatus = NorFlashReadStatusRegister (Instance, BlockAddress); + } while ((LockStatus & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE); + } + + // Put device back into Read Array mode + SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_READ_ARRAY); + + DEBUG((DEBUG_BLKIO, "UnlockSingleBlock: BlockAddress=0x%08x, Exit Status = \"%r\".\n", BlockAddress, Status)); + + return Status; +} + + +EFI_STATUS +NorFlashUnlockSingleBlockIfNecessary ( + IN NOR_FLASH_INSTANCE *Instance, + IN UINTN BlockAddress + ) +{ + EFI_STATUS Status = EFI_SUCCESS; + + if (NorFlashBlockIsLocked (Instance, BlockAddress) == TRUE) { + Status = NorFlashUnlockSingleBlock (Instance, BlockAddress); + } + + return Status; +} + + +/** + * The following function presumes that the block has already been unlocked. + **/ +EFI_STATUS +NorFlashEraseSingleBlock ( + IN NOR_FLASH_INSTANCE *Instance, + IN UINTN BlockAddress + ) +{ + EFI_STATUS Status; + UINT32 StatusRegister; + + Status = EFI_SUCCESS; + + // Request a block erase and then confirm it + SEND_NOR_COMMAND(BlockAddress, 0, P30_CMD_BLOCK_ERASE_SETUP); + SEND_NOR_COMMAND(BlockAddress, 0, P30_CMD_BLOCK_ERASE_CONFIRM); + + // Wait until the status register gives us the all clear + do { + StatusRegister = NorFlashReadStatusRegister (Instance, BlockAddress); + } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE); + + if (StatusRegister & P30_SR_BIT_VPP) { + DEBUG((EFI_D_ERROR,"EraseSingleBlock(BlockAddress=0x%08x: VPP Range Error\n", BlockAddress)); + Status = EFI_DEVICE_ERROR; + } + + if ((StatusRegister & (P30_SR_BIT_ERASE | P30_SR_BIT_PROGRAM)) == (P30_SR_BIT_ERASE | P30_SR_BIT_PROGRAM)) { + DEBUG((EFI_D_ERROR,"EraseSingleBlock(BlockAddress=0x%08x: Command Sequence Error\n", BlockAddress)); + Status = EFI_DEVICE_ERROR; + } + + if (StatusRegister & P30_SR_BIT_ERASE) { + DEBUG((EFI_D_ERROR,"EraseSingleBlock(BlockAddress=0x%08x: Block Erase Error StatusRegister:0x%X\n", BlockAddress, StatusRegister)); + Status = EFI_DEVICE_ERROR; + } + + if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) { + // The debug level message has been reduced because a device lock might happen. In this case we just retry it ... + DEBUG((EFI_D_INFO,"EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error\n", BlockAddress)); + Status = EFI_WRITE_PROTECTED; + } + + if (EFI_ERROR(Status)) { + // Clear the Status Register + SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER); + } + + // Put device back into Read Array mode + SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY); + + return Status; +} + +/** + * The following function presumes that the block has already been unlocked. + **/ +EFI_STATUS +NorFlashUnlockAndEraseSingleBlock ( + IN NOR_FLASH_INSTANCE *Instance, + IN UINTN BlockAddress + ) +{ + EFI_STATUS Status; + UINTN Index; + EFI_TPL OriginalTPL; + + if (!EfiAtRuntime ()) { + // Raise TPL to TPL_HIGH to stop anyone from interrupting us. + OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL); + } else { + // This initialization is only to prevent the compiler to complain about the + // use of uninitialized variables + OriginalTPL = TPL_HIGH_LEVEL; + } + + Index = 0; + // The block erase might fail a first time (SW bug ?). Retry it ... + do { + // Unlock the block if we have to + Status = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress); + if (!EFI_ERROR(Status)) { + Status = NorFlashEraseSingleBlock (Instance, BlockAddress); + } + Index++; + } while ((Index < NOR_FLASH_ERASE_RETRY) && (Status == EFI_WRITE_PROTECTED)); + + if (Index == NOR_FLASH_ERASE_RETRY) { + DEBUG((EFI_D_ERROR,"EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error (try to erase %d times)\n", BlockAddress,Index)); + } + + if (!EfiAtRuntime ()) { + // Interruptions can resume. + gBS->RestoreTPL (OriginalTPL); + } + + return Status; +} + + +STATIC +EFI_STATUS +NorFlashWriteSingleWord ( + IN NOR_FLASH_INSTANCE *Instance, + IN UINTN WordAddress, + IN UINT32 WriteData + ) +{ + EFI_STATUS Status; + UINT32 StatusRegister; + + Status = EFI_SUCCESS; + + // Request a write single word command + SEND_NOR_COMMAND(WordAddress, 0, P30_CMD_WORD_PROGRAM_SETUP); + + // Store the word into NOR Flash; + MmioWrite32 (WordAddress, WriteData); + + // Wait for the write to complete and then check for any errors; i.e. check the Status Register + do { + // Prepare to read the status register + StatusRegister = NorFlashReadStatusRegister (Instance, WordAddress); + // The chip is busy while the WRITE bit is not asserted + } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE); + + + // Perform a full status check: + // Mask the relevant bits of Status Register. + // Everything should be zero, if not, we have a problem + + if (StatusRegister & P30_SR_BIT_VPP) { + DEBUG((EFI_D_ERROR,"NorFlashWriteSingleWord(WordAddress:0x%X): VPP Range Error\n",WordAddress)); + Status = EFI_DEVICE_ERROR; + } + + if (StatusRegister & P30_SR_BIT_PROGRAM) { + DEBUG((EFI_D_ERROR,"NorFlashWriteSingleWord(WordAddress:0x%X): Program Error\n",WordAddress)); + Status = EFI_DEVICE_ERROR; + } + + if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) { + DEBUG((EFI_D_ERROR,"NorFlashWriteSingleWord(WordAddress:0x%X): Device Protect Error\n",WordAddress)); + Status = EFI_DEVICE_ERROR; + } + + if (!EFI_ERROR(Status)) { + // Clear the Status Register + SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER); + } + + // Put device back into Read Array mode + SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY); + + return Status; +} + +/* + * Writes data to the NOR Flash using the Buffered Programming method. + * + * The maximum size of the on-chip buffer is 32-words, because of hardware restrictions. + * Therefore this function will only handle buffers up to 32 words or 128 bytes. + * To deal with larger buffers, call this function again. + * + * This function presumes that both the TargetAddress and the TargetAddress+BufferSize + * exist entirely within the NOR Flash. Therefore these conditions will not be checked here. + * + * In buffered programming, if the target address not at the beginning of a 32-bit word boundary, + * then programming time is doubled and power consumption is increased. + * Therefore, it is a requirement to align buffer writes to 32-bit word boundaries. + * i.e. the last 4 bits of the target start address must be zero: 0x......00 + */ +EFI_STATUS +NorFlashWriteBuffer ( + IN NOR_FLASH_INSTANCE *Instance, + IN UINTN TargetAddress, + IN UINTN BufferSizeInBytes, + IN UINT32 *Buffer + ) +{ + EFI_STATUS Status; + UINTN BufferSizeInWords; + UINTN Count; + volatile UINT32 *Data; + UINTN WaitForBuffer; + BOOLEAN BufferAvailable; + UINT32 StatusRegister; + + WaitForBuffer = MAX_BUFFERED_PROG_ITERATIONS; + BufferAvailable = FALSE; + + // Check that the target address does not cross a 32-word boundary. + if ((TargetAddress & BOUNDARY_OF_32_WORDS) != 0) { + return EFI_INVALID_PARAMETER; + } + + // Check there are some data to program + if (BufferSizeInBytes == 0) { + return EFI_BUFFER_TOO_SMALL; + } + + // Check that the buffer size does not exceed the maximum hardware buffer size on chip. + if (BufferSizeInBytes > P30_MAX_BUFFER_SIZE_IN_BYTES) { + return EFI_BAD_BUFFER_SIZE; + } + + // Check that the buffer size is a multiple of 32-bit words + if ((BufferSizeInBytes % 4) != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + // Pre-programming conditions checked, now start the algorithm. + + // Prepare the data destination address + Data = (UINT32 *)TargetAddress; + + // Check the availability of the buffer + do { + // Issue the Buffered Program Setup command + SEND_NOR_COMMAND(TargetAddress, 0, P30_CMD_BUFFERED_PROGRAM_SETUP); + + // Read back the status register bit#7 from the same address + if (((*Data) & P30_SR_BIT_WRITE) == P30_SR_BIT_WRITE) { + BufferAvailable = TRUE; + } + + // Update the loop counter + WaitForBuffer--; + + } while ((WaitForBuffer > 0) && (BufferAvailable == FALSE)); + + // The buffer was not available for writing + if (WaitForBuffer == 0) { + Status = EFI_DEVICE_ERROR; + goto EXIT; + } + + // From now on we work in 32-bit words + BufferSizeInWords = BufferSizeInBytes / (UINTN)4; + + // Write the word count, which is (buffer_size_in_words - 1), + // because word count 0 means one word. + SEND_NOR_COMMAND(TargetAddress, 0, (BufferSizeInWords - 1)); + + // Write the data to the NOR Flash, advancing each address by 4 bytes + for(Count=0; Count < BufferSizeInWords; Count++, Data++, Buffer++) { + *Data = *Buffer; + } + + // Issue the Buffered Program Confirm command, to start the programming operation + SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_BUFFERED_PROGRAM_CONFIRM); + + // Wait for the write to complete and then check for any errors; i.e. check the Status Register + do { + StatusRegister = NorFlashReadStatusRegister (Instance, TargetAddress); + // The chip is busy while the WRITE bit is not asserted + } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE); + + + // Perform a full status check: + // Mask the relevant bits of Status Register. + // Everything should be zero, if not, we have a problem + + Status = EFI_SUCCESS; + + if (StatusRegister & P30_SR_BIT_VPP) { + DEBUG((EFI_D_ERROR,"NorFlashWriteBuffer(TargetAddress:0x%X): VPP Range Error\n", TargetAddress)); + Status = EFI_DEVICE_ERROR; + } + + if (StatusRegister & P30_SR_BIT_PROGRAM) { + DEBUG((EFI_D_ERROR,"NorFlashWriteBuffer(TargetAddress:0x%X): Program Error\n", TargetAddress)); + Status = EFI_DEVICE_ERROR; + } + + if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) { + DEBUG((EFI_D_ERROR,"NorFlashWriteBuffer(TargetAddress:0x%X): Device Protect Error\n",TargetAddress)); + Status = EFI_DEVICE_ERROR; + } + + if (!EFI_ERROR(Status)) { + // Clear the Status Register + SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER); + } + +EXIT: + // Put device back into Read Array mode + SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY); + + return Status; +} + +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 BufferIndex; + UINTN BlockAddress; + UINTN BuffersInBlock; + UINTN RemainingWords; + EFI_TPL OriginalTPL; + UINTN Cnt; + + 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; + + if (!EfiAtRuntime ()) { + // Raise TPL to TPL_HIGH to stop anyone from interrupting us. + OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL); + } else { + // This initialization is only to prevent the compiler to complain about the + // use of uninitialized variables + OriginalTPL = TPL_HIGH_LEVEL; + } + + Status = NorFlashUnlockAndEraseSingleBlock (Instance, BlockAddress); + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_ERROR, "WriteSingleBlock: ERROR - Failed to Unlock and Erase the single block at 0x%X\n", BlockAddress)); + goto EXIT; + } + + // To speed up the programming operation, NOR Flash is programmed using the Buffered Programming method. + + // Check that the address starts at a 32-word boundary, i.e. last 7 bits must be zero + if ((WordAddress & BOUNDARY_OF_32_WORDS) == 0x00) { + + // First, break the entire block into buffer-sized chunks. + BuffersInBlock = (UINTN)(BlockSizeInWords * 4) / P30_MAX_BUFFER_SIZE_IN_BYTES; + + // Then feed each buffer chunk to the NOR Flash + // If a buffer does not contain any data, don't write it. + for(BufferIndex=0; + BufferIndex < BuffersInBlock; + BufferIndex++, WordAddress += P30_MAX_BUFFER_SIZE_IN_BYTES, DataBuffer += P30_MAX_BUFFER_SIZE_IN_WORDS + ) { + // Check the buffer to see if it contains any data (not set all 1s). + for (Cnt = 0; Cnt < P30_MAX_BUFFER_SIZE_IN_WORDS; Cnt++) { + if (~DataBuffer[Cnt] != 0 ) { + // Some data found, write the buffer. + Status = NorFlashWriteBuffer (Instance, WordAddress, P30_MAX_BUFFER_SIZE_IN_BYTES, + DataBuffer); + if (EFI_ERROR(Status)) { + goto EXIT; + } + break; + } + } + } + + // Finally, finish off any remaining words that are less than the maximum size of the buffer + RemainingWords = BlockSizeInWords % P30_MAX_BUFFER_SIZE_IN_WORDS; + + if(RemainingWords != 0) { + Status = NorFlashWriteBuffer (Instance, WordAddress, (RemainingWords * 4), DataBuffer); + if (EFI_ERROR(Status)) { + goto EXIT; + } + } + + } else { + // For now, use the single word programming algorithm + // It is unlikely that the NOR Flash will exist in an address which falls within a 32 word boundary range, + // i.e. which ends in the range 0x......01 - 0x......7F. + for(WordIndex=0; WordIndexRestoreTPL (OriginalTPL); + } + + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_ERROR, "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = \"%r\".\n", WordAddress, Status)); + } + return Status; +} + + +EFI_STATUS +NorFlashWriteBlocks ( + IN NOR_FLASH_INSTANCE *Instance, + IN EFI_LBA Lba, + IN UINTN BufferSizeInBytes, + IN VOID *Buffer + ) +{ + UINT32 *pWriteBuffer; + EFI_STATUS Status = EFI_SUCCESS; + EFI_LBA CurrentBlock; + UINT32 BlockSizeInWords; + UINT32 NumBlocks; + UINT32 BlockCount; + + // The buffer must be valid + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if(Instance->Media.ReadOnly == TRUE) { + return EFI_WRITE_PROTECTED; + } + + // We must have some bytes to read + DEBUG((DEBUG_BLKIO, "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_BLKIO, "NorFlashWriteBlocks: BlockSize in bytes =0x%x\n", Instance->Media.BlockSize)); + if ((BufferSizeInBytes % Instance->Media.BlockSize) != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + // All blocks must be within the device + NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize ; + + DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld.\n", NumBlocks, Instance->Media.LastBlock, Lba)); + + if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) { + DEBUG((EFI_D_ERROR, "NorFlashWriteBlocks: ERROR - Write will exceed last block.\n")); + return EFI_INVALID_PARAMETER; + } + + BlockSizeInWords = Instance->Media.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 = pWriteBuffer + BlockSizeInWords) { + + DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: Writing block #%d\n", (UINTN)CurrentBlock)); + + Status = NorFlashWriteFullBlock (Instance, CurrentBlock, pWriteBuffer, BlockSizeInWords); + + if (EFI_ERROR(Status)) { + break; + } + } + + DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: Exit Status = \"%r\".\n", Status)); + return Status; +} + +EFI_STATUS +NorFlashReadBlocks ( + IN NOR_FLASH_INSTANCE *Instance, + IN EFI_LBA Lba, + IN UINTN BufferSizeInBytes, + OUT VOID *Buffer + ) +{ + UINT32 NumBlocks; + UINTN StartAddress; + + DEBUG((DEBUG_BLKIO, "NorFlashReadBlocks: BufferSize=0x%xB BlockSize=0x%xB LastBlock=%ld, Lba=%ld.\n", + BufferSizeInBytes, Instance->Media.BlockSize, Instance->Media.LastBlock, Lba)); + + // The buffer must be valid + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Return if we have not 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->Media.BlockSize) != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + // All blocks must be within the device + NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize ; + + if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) { + DEBUG((EFI_D_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->Media.BlockSize + ); + + // Put the device into Read Array mode + SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY); + + // Readout the data + CopyMem(Buffer, (UINTN *)StartAddress, BufferSizeInBytes); + + return EFI_SUCCESS; +} + +EFI_STATUS +NorFlashRead ( + IN NOR_FLASH_INSTANCE *Instance, + IN EFI_LBA Lba, + IN UINTN Offset, + IN UINTN BufferSizeInBytes, + OUT VOID *Buffer + ) +{ + UINT32 NumBlocks; + UINTN StartAddress; + + // The buffer must be valid + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Return if we have not any byte to read + if (BufferSizeInBytes == 0) { + return EFI_SUCCESS; + } + + // All blocks must be within the device + NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize ; + + if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) { + DEBUG ((EFI_D_ERROR, "NorFlashRead: ERROR - Read will exceed last block\n")); + return EFI_INVALID_PARAMETER; + } + + if (Offset + BufferSizeInBytes >= Instance->Size) { + DEBUG ((EFI_D_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->Media.BlockSize + ); + + // Put the device into Read Array mode + SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY); + + // Readout the data + CopyMem (Buffer, (UINTN *)(StartAddress + Offset), BufferSizeInBytes); + + return EFI_SUCCESS; +} + +/* + Write a full or portion of a block. It must not span block boundaries; that is, + Offset + *NumBytes <= Instance->Media.BlockSize. +*/ +EFI_STATUS +NorFlashWriteSingleBlock ( + IN NOR_FLASH_INSTANCE *Instance, + IN EFI_LBA Lba, + IN UINTN Offset, + IN OUT UINTN *NumBytes, + IN UINT8 *Buffer + ) +{ + EFI_STATUS TempStatus; + UINT32 Tmp; + UINT32 TmpBuf; + UINT32 WordToWrite; + UINT32 Mask; + BOOLEAN DoErase; + UINTN BytesToWrite; + UINTN CurOffset; + UINTN WordAddr; + UINTN BlockSize; + UINTN BlockAddress; + UINTN PrevBlockAddress; + + PrevBlockAddress = 0; + + if (!Instance->Initialized && Instance->Initialize) { + Instance->Initialize(Instance); + } + + DEBUG ((DEBUG_BLKIO, "NorFlashWriteSingleBlock(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", Instance->StartLba + Lba, Offset, *NumBytes, Buffer)); + + // Detect WriteDisabled state + if (Instance->Media.ReadOnly == TRUE) { + DEBUG ((EFI_D_ERROR, "NorFlashWriteSingleBlock: ERROR - Can not write: Device is in WriteDisabled state.\n")); + // It is in WriteDisabled state, return an error right away + return EFI_ACCESS_DENIED; + } + + // Cache the block size to avoid de-referencing pointers all the time + BlockSize = Instance->Media.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 ((EFI_D_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 ((EFI_D_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. + TempStatus = NorFlashRead (Instance, Instance->StartLba + Lba, + CurOffset & ~(0x3), sizeof(Tmp), &Tmp); + if (EFI_ERROR (TempStatus)) { + 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 = *((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; + } + } + + // + // Write the word to NOR. + // + + BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSize); + if (BlockAddress != PrevBlockAddress) { + TempStatus = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress); + if (EFI_ERROR (TempStatus)) { + return EFI_DEVICE_ERROR; + } + PrevBlockAddress = BlockAddress; + } + TempStatus = NorFlashWriteSingleWord (Instance, WordAddr, WordToWrite); + if (EFI_ERROR (TempStatus)) { + 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 ((EFI_D_ERROR, "FvbWrite: ERROR - Buffer not ready\n")); + return EFI_DEVICE_ERROR; + } + + // Read NOR Flash data into shadow buffer + TempStatus = NorFlashReadBlocks (Instance, Instance->StartLba + Lba, BlockSize, Instance->ShadowBuffer); + if (EFI_ERROR (TempStatus)) { + // 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 + TempStatus = NorFlashWriteBlocks (Instance, Instance->StartLba + Lba, BlockSize, Instance->ShadowBuffer); + if (EFI_ERROR (TempStatus)) { + // Return one of the pre-approved error statuses + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/* + Although DiskIoDxe will automatically install the DiskIO protocol whenever + we install the BlockIO protocol, its implementation is sub-optimal as it reads + and writes entire blocks using the BlockIO protocol. In fact we can access + NOR flash with a finer granularity than that, so we can improve performance + by directly producing the DiskIO protocol. +*/ + +/** + Read BufferSize bytes from Offset into Buffer. + + @param This Protocol instance pointer. + @param MediaId Id of the media, changes every time the media is replaced. + @param Offset The starting byte offset to read from + @param BufferSize Size of Buffer + @param Buffer Buffer containing read data + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while performing the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not + valid for the device. + +**/ +EFI_STATUS +EFIAPI +NorFlashDiskIoReadDisk ( + IN EFI_DISK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 DiskOffset, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + NOR_FLASH_INSTANCE *Instance; + UINT32 BlockSize; + UINT32 BlockOffset; + EFI_LBA Lba; + + Instance = INSTANCE_FROM_DISKIO_THIS(This); + + if (MediaId != Instance->Media.MediaId) { + return EFI_MEDIA_CHANGED; + } + + BlockSize = Instance->Media.BlockSize; + Lba = (EFI_LBA) DivU64x32Remainder (DiskOffset, BlockSize, &BlockOffset); + + return NorFlashRead (Instance, Lba, BlockOffset, BufferSize, Buffer); +} + +/** + Writes a specified number of bytes to a device. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to be written. + @param Offset The starting byte offset on the logical block I/O device to write. + @param BufferSize The size in bytes of Buffer. The number of bytes to write to the device. + @param Buffer A pointer to the buffer containing the data to be written. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_INVALID_PARAMETER The write request contains device addresses that are not + valid for the device. + +**/ +EFI_STATUS +EFIAPI +NorFlashDiskIoWriteDisk ( + IN EFI_DISK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 DiskOffset, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + NOR_FLASH_INSTANCE *Instance; + UINT32 BlockSize; + UINT32 BlockOffset; + EFI_LBA Lba; + UINTN RemainingBytes; + UINTN WriteSize; + EFI_STATUS Status; + + Instance = INSTANCE_FROM_DISKIO_THIS(This); + + if (MediaId != Instance->Media.MediaId) { + return EFI_MEDIA_CHANGED; + } + + BlockSize = Instance->Media.BlockSize; + Lba = (EFI_LBA) DivU64x32Remainder (DiskOffset, BlockSize, &BlockOffset); + + RemainingBytes = BufferSize; + + // Write either all the remaining bytes, or the number of bytes that bring + // us up to a block boundary, whichever is less. + // (DiskOffset | (BlockSize - 1)) + 1) rounds DiskOffset up to the next + // block boundary (even if it is already on one). + WriteSize = MIN (RemainingBytes, ((DiskOffset | (BlockSize - 1)) + 1) - DiskOffset); + + do { + if (WriteSize == BlockSize) { + // Write a full block + Status = NorFlashWriteFullBlock (Instance, Lba, Buffer, BlockSize / sizeof (UINT32)); } else { - DEBUG((EFI_D_ERROR,"NorFlashReadStatusRegister: Error (0x%X)\n",Status)); - } - - // If an error is detected we must clear the Status Register - SEND_NOR_COMMAND(SR_Address, 0, P30_CMD_CLEAR_STATUS_REGISTER); - Status = EFI_DEVICE_ERROR; - } - - SEND_NOR_COMMAND(SR_Address, 0, P30_CMD_READ_ARRAY); - - return Status; -} - - -BOOLEAN -NorFlashBlockIsLocked ( - IN UINTN BlockAddress - ) -{ - UINT32 LockStatus; - BOOLEAN BlockIsLocked = TRUE; - - // Send command for reading device id - SEND_NOR_COMMAND (BlockAddress, 2, P30_CMD_READ_DEVICE_ID); - - // Read block lock status - LockStatus = MmioRead32 (CREATE_NOR_ADDRESS( BlockAddress, 2 )); - - // Decode block lock status - LockStatus = FOLD_32BIT_INTO_16BIT(LockStatus); - - if((LockStatus & 0x2) != 0) { - DEBUG((EFI_D_ERROR, "UnlockSingleBlock: WARNING: Block LOCKED DOWN\n")); - } - - if((LockStatus & 0x1) == 0) { - // This means the block is unlocked - DEBUG((DEBUG_BLKIO, "UnlockSingleBlock: Block 0x%08x unlocked\n", BlockAddress )); - BlockIsLocked = FALSE; - } - - return BlockIsLocked; -} - - -EFI_STATUS -NorFlashUnlockSingleBlock ( - IN UINTN BlockAddress - ) -{ - EFI_STATUS Status = EFI_SUCCESS; - - // Raise the Task Priority Level to TPL_NOTIFY to serialise all its operations - // and to protect shared data structures. - - // Request a lock setup - SEND_NOR_COMMAND(BlockAddress, 0, P30_CMD_LOCK_BLOCK_SETUP); - - // Request an unlock - SEND_NOR_COMMAND(BlockAddress, 0, P30_CMD_UNLOCK_BLOCK); - - // Put device back into Read Array mode - SEND_NOR_COMMAND(BlockAddress, 0, P30_CMD_READ_ARRAY); - - DEBUG((DEBUG_BLKIO, "UnlockSingleBlock: BlockAddress=0x%08x, Exit Status = \"%r\".\n", BlockAddress, Status)); - - return Status; -} - - -EFI_STATUS -NorFlashUnlockSingleBlockIfNecessary ( - IN UINTN BlockAddress - ) -{ - EFI_STATUS Status = EFI_SUCCESS; - - if ( NorFlashBlockIsLocked(BlockAddress) == TRUE ) { - Status = NorFlashUnlockSingleBlock(BlockAddress); - } - - return Status; -} - - -/** - * The following function presumes that the block has already been unlocked. - **/ -EFI_STATUS -NorFlashEraseSingleBlock ( - IN UINTN BlockAddress - ) -{ - EFI_STATUS Status = EFI_SUCCESS; - - // Request a block erase and then confirm it - SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_BLOCK_ERASE_SETUP); - SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_BLOCK_ERASE_CONFIRM); - // Wait until the status register gives us the all clear - Status = NorFlashReadStatusRegister( BlockAddress ); - - if (EFI_ERROR(Status)) { - DEBUG((DEBUG_BLKIO, "EraseSingleBlock(BlockAddress=0x%08x) = '%r'\n", BlockAddress, Status)); - } - return Status; -} - -/** - * The following function presumes that the block has already been unlocked. - **/ -EFI_STATUS -NorFlashUnlockAndEraseSingleBlock ( - IN UINTN BlockAddress - ) -{ - EFI_STATUS Status; - - // Unlock the block if we have to - Status = NorFlashUnlockSingleBlockIfNecessary (BlockAddress); - if (!EFI_ERROR(Status)) { - Status = NorFlashEraseSingleBlock(BlockAddress); - } - - return Status; -} - - -EFI_STATUS -NorFlashWriteSingleWord ( - IN UINTN WordAddress, - IN UINT32 WriteData - ) -{ - EFI_STATUS Status; - volatile UINT32 *Data; - - // Prepare the read address - Data = (UINT32 *)WordAddress; - - // Request a write single word command - SEND_NOR_COMMAND( WordAddress, 0, P30_CMD_WORD_PROGRAM_SETUP ); - - // Store the word into NOR Flash; - *Data = WriteData; - - // Wait for the write to complete and then check for any errors; i.e. check the Status Register - Status = NorFlashReadStatusRegister( WordAddress ); - - return Status; -} - -/* - * Writes data to the NOR Flash using the Buffered Programming method. - * - * The maximum size of the on-chip buffer is 32-words, because of hardware restrictions. - * Therefore this function will only handle buffers up to 32 words or 128 bytes. - * To deal with larger buffers, call this function again. - * - * This function presumes that both the TargetAddress and the TargetAddress+BufferSize - * exist entirely within the NOR Flash. Therefore these conditions will not be checked here. - * - * In buffered programming, if the target address not at the beginning of a 32-bit word boundary, - * then programming time is doubled and power consumption is increased. - * Therefore, it is a requirement to align buffer writes to 32-bit word boundaries. - * i.e. the last 4 bits of the target start address must be zero: 0x......00 - */ -EFI_STATUS -NorFlashWriteBuffer ( - IN UINTN TargetAddress, - IN UINTN BufferSizeInBytes, - IN UINT32 *Buffer - ) -{ - EFI_STATUS Status; - UINTN BufferSizeInWords; - UINTN Count; - volatile UINT32 *Data; - UINTN WaitForBuffer = MAX_BUFFERED_PROG_ITERATIONS; - BOOLEAN BufferAvailable = FALSE; - - - // Check that the target address does not cross a 32-word boundary. - if ( (TargetAddress & BOUNDARY_OF_32_WORDS) != 0 ) { - return EFI_INVALID_PARAMETER; - } - - // Check there are some data to program - if ( BufferSizeInBytes == 0 ) { - return EFI_BUFFER_TOO_SMALL; - } - - // Check that the buffer size does not exceed the maximum hardware buffer size on chip. - if ( BufferSizeInBytes > P30_MAX_BUFFER_SIZE_IN_BYTES ) { - return EFI_BAD_BUFFER_SIZE; - } - - // Check that the buffer size is a multiple of 32-bit words - if ( (BufferSizeInBytes % 4) != 0 ) { - return EFI_BAD_BUFFER_SIZE; - } - - // Pre-programming conditions checked, now start the algorithm. - - // Prepare the data destination address - Data = (UINT32 *)TargetAddress; - - // Check the availability of the buffer - do { - // Issue the Buffered Program Setup command - SEND_NOR_COMMAND( TargetAddress, 0, P30_CMD_BUFFERED_PROGRAM_SETUP ); - - // Read back the status register bit#7 from the same address - if ( ((*Data) & P30_SR_BIT_WRITE) == P30_SR_BIT_WRITE ) { - BufferAvailable = TRUE; - } - - // Update the loop counter - WaitForBuffer--; - - } while (( WaitForBuffer > 0 ) && ( BufferAvailable == FALSE )); - - // The buffer was not available for writing - if ( WaitForBuffer == 0 ) { - return EFI_DEVICE_ERROR; - } - - // From now on we work in 32-bit words - BufferSizeInWords = BufferSizeInBytes / (UINTN)4; - - // Write the word count, which is (buffer_size_in_words - 1), - // because word count 0 means one word. - SEND_NOR_COMMAND( TargetAddress, 0, (BufferSizeInWords - 1) ); - - // Write the data to the NOR Flash, advancing each address by 4 bytes - for( Count=0; CountMedia.ReadOnly == TRUE ) { - return EFI_WRITE_PROTECTED; - } - - // We must have some bytes to read - DEBUG((DEBUG_BLKIO, "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_BLKIO, "NorFlashWriteBlocks: BlockSize in bytes =0x%x\n", Instance->Media.BlockSize )); - if ((BufferSizeInBytes % Instance->Media.BlockSize) != 0) { - return EFI_BAD_BUFFER_SIZE; - } - - // All blocks must be within the device - NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize ; - - DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld.\n", NumBlocks, Instance->Media.LastBlock, Lba)); - - if ( ( Lba + NumBlocks ) > ( Instance->Media.LastBlock + 1 ) ) { - DEBUG((EFI_D_ERROR, "NorFlashWriteBlocks: ERROR - Write will exceed last block.\n")); - return EFI_INVALID_PARAMETER; - } - - BlockSizeInWords = Instance->Media.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; BlockCountBaseAddress, CurrentBlock, pWriteBuffer, BlockSizeInWords ); - - if (EFI_ERROR(Status)) { - break; - } - - } - - DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: Exit Status = \"%r\".\n", Status)); - return Status; -} - - -EFI_STATUS -NorFlashReadBlocks ( - IN NOR_FLASH_INSTANCE *Instance, - IN EFI_LBA Lba, - IN UINTN BufferSizeInBytes, - OUT VOID *Buffer - ) -{ - UINT32 NumBlocks; - UINTN StartAddress; - - // The buffer must be valid - if (Buffer == NULL) { - return EFI_INVALID_PARAMETER; - } - - // We must have some bytes to read - DEBUG((DEBUG_BLKIO, "NorFlashReadBlocks: BufferSize=0x%x bytes.\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_BLKIO, "NorFlashReadBlocks: BlockSize=0x%x bytes.\n", Instance->Media.BlockSize )); - if ((BufferSizeInBytes % Instance->Media.BlockSize) != 0) { - return EFI_BAD_BUFFER_SIZE; - } - - // All blocks must be within the device - NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize ; - - DEBUG((DEBUG_BLKIO, "NorFlashReadBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld\n", NumBlocks, Instance->Media.LastBlock, Lba)); - - if ( ( Lba + NumBlocks ) > (Instance->Media.LastBlock + 1) ) { - DEBUG((EFI_D_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->BaseAddress, - Lba, - Instance->Media.BlockSize - ); - - // Put the device into Read Array mode - SEND_NOR_COMMAND (Instance->BaseAddress, 0, P30_CMD_READ_ARRAY); - - // Readout the data - CopyMem(Buffer, (UINTN *)StartAddress, BufferSizeInBytes); - - return EFI_SUCCESS; -} - - -EFI_STATUS -NorFlashReset ( - IN NOR_FLASH_INSTANCE *Instance - ) -{ - // As there is no specific RESET to perform, ensure that the devices is in the default Read Array mode - SEND_NOR_COMMAND( Instance->BaseAddress, 0, P30_CMD_READ_ARRAY); - return EFI_SUCCESS; -} - - - -EFI_STATUS -EFIAPI -NorFlashInitialise ( - IN EFI_HANDLE ImageHandle, - IN EFI_SYSTEM_TABLE *SystemTable - ) -{ - EFI_STATUS Status; - UINT32 Index; - NOR_FLASH_DESCRIPTION* NorFlashDevices; - UINT32 NorFlashDeviceCount; - BOOLEAN ContainVariableStorage; - - Status = NorFlashPlatformInitialization (); - if (EFI_ERROR(Status)) { - DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to initialize Nor Flash devices\n")); - return Status; - } - - Status = NorFlashPlatformGetDevices (&NorFlashDevices,&NorFlashDeviceCount); - if (EFI_ERROR(Status)) { - DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to get Nor Flash devices\n")); - return Status; - } - - mNorFlashInstances = AllocatePool(sizeof(NOR_FLASH_INSTANCE*) * NorFlashDeviceCount); - - for (Index = 0; Index < NorFlashDeviceCount; Index++) { - // Check if this NOR Flash device contain the variable storage region - ContainVariableStorage = - (NorFlashDevices[Index].BaseAddress <= PcdGet32 (PcdFlashNvStorageVariableBase)) && - (PcdGet32 (PcdFlashNvStorageVariableBase) + PcdGet32 (PcdFlashNvStorageVariableSize) <= NorFlashDevices[Index].BaseAddress + NorFlashDevices[Index].Size); - - Status = NorFlashCreateInstance ( - NorFlashDevices[Index].BaseAddress, - NorFlashDevices[Index].Size, - Index, - NorFlashDevices[Index].BlockSize, - ContainVariableStorage, - &NorFlashDevices[Index].Guid, - &mNorFlashInstances[Index] - ); - if (EFI_ERROR(Status)) { - DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to create instance for NorFlash[%d]\n",Index)); - } - } - - return Status; -} + // Write a partial block + Status = NorFlashWriteSingleBlock (Instance, Lba, BlockOffset, &WriteSize, Buffer); + } + if (EFI_ERROR (Status)) { + return Status; + } + // Now continue writing either all the remaining bytes or single blocks. + RemainingBytes -= WriteSize; + Buffer = (UINT8 *) Buffer + WriteSize; + Lba++; + BlockOffset = 0; + WriteSize = MIN (RemainingBytes, BlockSize); + } while (RemainingBytes); + + return Status; +} + +EFI_STATUS +NorFlashReset ( + IN NOR_FLASH_INSTANCE *Instance + ) +{ + // As there is no specific RESET to perform, ensure that the devices is in the default Read Array mode + SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY); + return EFI_SUCCESS; +} + +/** + Fixup internal data so that EFI can be call in virtual mode. + Call the passed in Child Notify event and convert any pointers in + lib to virtual mode. + + @param[in] Event The Event that is being processed + @param[in] Context Event Context +**/ +VOID +EFIAPI +NorFlashVirtualNotifyEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + UINTN Index; + + for (Index = 0; Index < mNorFlashDeviceCount; Index++) { + EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->DeviceBaseAddress); + EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->RegionBaseAddress); + + // Convert BlockIo protocol + EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.FlushBlocks); + EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.ReadBlocks); + EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.Reset); + EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.WriteBlocks); + + // 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); + } + } + + return; +} + +EFI_STATUS +EFIAPI +NorFlashInitialise ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + UINT32 Index; + NOR_FLASH_DESCRIPTION* NorFlashDevices; + BOOLEAN ContainVariableStorage; + + Status = NorFlashPlatformInitialization (); + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to initialize Nor Flash devices\n")); + return Status; + } + + Status = NorFlashPlatformGetDevices (&NorFlashDevices, &mNorFlashDeviceCount); + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to get Nor Flash devices\n")); + return Status; + } + + mNorFlashInstances = AllocateRuntimePool (sizeof(NOR_FLASH_INSTANCE*) * mNorFlashDeviceCount); + + 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 ( + NorFlashDevices[Index].DeviceBaseAddress, + NorFlashDevices[Index].RegionBaseAddress, + NorFlashDevices[Index].Size, + Index, + NorFlashDevices[Index].BlockSize, + ContainVariableStorage, + &NorFlashDevices[Index].Guid, + &mNorFlashInstances[Index] + ); + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to create instance for NorFlash[%d]\n",Index)); + } + } + + // + // 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; +}