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; }
/** * The following function presumes that the block has already been unlocked. **/ STATIC 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; }
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 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; }
UINT32 NorFlashReadStatusRegister ( IN NOR_FLASH_INSTANCE *Instance, IN UINTN SR_Address ) { // Prepare to read the status register SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_STATUS_REGISTER); return MmioRead32 (Instance->DeviceBaseAddress); }
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->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; }
STATIC EFI_STATUS NorFlashUnlockSingleBlock ( IN NOR_FLASH_INSTANCE *Instance, IN UINTN BlockAddress ) { 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\n", BlockAddress)); return EFI_SUCCESS; }
STATIC BOOLEAN NorFlashBlockIsLocked ( IN NOR_FLASH_INSTANCE *Instance, IN UINTN BlockAddress ) { UINT32 LockStatus; // 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")); } return ((LockStatus & 0x1) != 0); }
/* * 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++) { MmioWrite32 ((UINTN)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; }