]> git.proxmox.com Git - mirror_edk2.git/blobdiff - Omap35xxPkg/Flash/Flash.c
ARM Packages: Fixed line endings
[mirror_edk2.git] / Omap35xxPkg / Flash / Flash.c
index 0056acd6b94683e71f25e59c9aa2f00bc4f4efcf..c63e0a3395b2c6813245ab4023744452118d3a4e 100644 (file)
-/** @file
-
-  Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
-  
-  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 "Flash.h"
-
-NAND_PART_INFO_TABLE gNandPartInfoTable[1] = {
-  { 0x2C, 0xBA, 17, 11 }
-};
-
-NAND_FLASH_INFO *gNandFlashInfo = NULL;
-UINT8           *gEccCode;
-UINTN           gNum512BytesChunks = 0;
-
-//
-
-// Device path for SemiHosting. It contains our autogened Caller ID GUID.
-
-//
-
-typedef struct {
-
-  VENDOR_DEVICE_PATH        Guid;
-
-  EFI_DEVICE_PATH_PROTOCOL  End;
-
-} FLASH_DEVICE_PATH;
-
-
-
-FLASH_DEVICE_PATH gDevicePath = {
-
-  {
-
-    { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, sizeof (VENDOR_DEVICE_PATH), 0 },
-
-    EFI_CALLER_ID_GUID
-
-  },
-
-  { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, sizeof (EFI_DEVICE_PATH_PROTOCOL), 0}
-
-};
-
-
-
-//Actual page address = Column address + Page address + Block address.
-UINTN
-GetActualPageAddressInBytes (
-  UINTN BlockIndex,
-  UINTN PageIndex
-)
-{
-  //BlockAddressStart = Start of the Block address in actual NAND
-  //PageAddressStart = Start of the Page address in actual NAND
-  return ((BlockIndex << gNandFlashInfo->BlockAddressStart) + (PageIndex << gNandFlashInfo->PageAddressStart));
-}
-
-VOID
-NandSendCommand (
-  UINT8 Command
-)
-{
-  MmioWrite16(GPMC_NAND_COMMAND_0, Command);
-}
-
-VOID
-NandSendAddress (
-  UINT8 Address
-)
-{
-  MmioWrite16(GPMC_NAND_ADDRESS_0, Address);
-}
-
-UINT16
-NandReadStatus (
-  VOID
-  )
-{
-  //Send READ STATUS command
-  NandSendCommand(READ_STATUS_CMD);
-
-  //Read status.
-  return MmioRead16(GPMC_NAND_DATA_0);
-}
-
-VOID
-NandSendAddressCycles (
-  UINTN Address
-)
-{
-  //Column address
-  NandSendAddress(Address & 0xff);
-  Address >>= 8;
-
-  //Column address
-  NandSendAddress(Address & 0x07);
-  Address >>= 3;
-
-  //Page and Block address
-  NandSendAddress(Address & 0xff);
-  Address >>= 8;
-
-  //Block address
-  NandSendAddress(Address & 0xff);
-  Address >>= 8;
-
-  //Block address
-  NandSendAddress(Address & 0x01);
-}
-
-VOID
-GpmcInit (
-  VOID
-  )
-{
-  //Enable Smart-idle mode.
-  MmioWrite32 (GPMC_SYSCONFIG, SMARTIDLEMODE);
-
-  //Set IRQSTATUS and IRQENABLE to the reset value
-  MmioWrite32 (GPMC_IRQSTATUS, 0x0);
-  MmioWrite32 (GPMC_IRQENABLE, 0x0);
-
-  //Disable GPMC timeout control.
-  MmioWrite32 (GPMC_TIMEOUT_CONTROL, TIMEOUTDISABLE);
-
-  //Set WRITEPROTECT bit to enable write access.
-  MmioWrite32 (GPMC_CONFIG, WRITEPROTECT_HIGH);
-
-  //NOTE: Following GPMC_CONFIGi_0 register settings are taken from u-boot memory dump.
-  MmioWrite32 (GPMC_CONFIG1_0, DEVICETYPE_NAND | DEVICESIZE_X16);
-  MmioWrite32 (GPMC_CONFIG2_0, CSRDOFFTIME | CSWROFFTIME);
-  MmioWrite32 (GPMC_CONFIG3_0, ADVRDOFFTIME | ADVWROFFTIME);
-  MmioWrite32 (GPMC_CONFIG4_0, OEONTIME | OEOFFTIME | WEONTIME | WEOFFTIME);
-  MmioWrite32 (GPMC_CONFIG5_0, RDCYCLETIME | WRCYCLETIME | RDACCESSTIME | PAGEBURSTACCESSTIME);
-  MmioWrite32 (GPMC_CONFIG6_0, WRACCESSTIME | WRDATAONADMUXBUS | CYCLE2CYCLEDELAY | CYCLE2CYCLESAMECSEN);
-  MmioWrite32 (GPMC_CONFIG7_0, MASKADDRESS_128MB | CSVALID | BASEADDRESS);
-}
-
-EFI_STATUS
-NandDetectPart (
-  VOID
-)
-{
-  UINT8      NandInfo = 0;
-  UINT8      PartInfo[5];
-  UINTN      Index;
-  BOOLEAN    Found = FALSE;
-
-  //Send READ ID command
-  NandSendCommand(READ_ID_CMD);
-
-  //Send one address cycle.
-  NandSendAddress(0);
-
-  //Read 5-bytes to idenfity code programmed into the NAND flash devices.
-  //BYTE 0 = Manufacture ID
-  //Byte 1 = Device ID
-  //Byte 2, 3, 4 = Nand part specific information (Page size, Block size etc)
-  for (Index = 0; Index < sizeof(PartInfo); Index++) {
-    PartInfo[Index] = MmioRead16(GPMC_NAND_DATA_0);
-  }
-
-  //Check if the ManufactureId and DeviceId are part of the currently supported nand parts.
-  for (Index = 0; Index < sizeof(gNandPartInfoTable)/sizeof(NAND_PART_INFO_TABLE); Index++) {
-    if (gNandPartInfoTable[Index].ManufactureId == PartInfo[0] && gNandPartInfoTable[Index].DeviceId == PartInfo[1]) {
-      gNandFlashInfo->BlockAddressStart = gNandPartInfoTable[Index].BlockAddressStart;
-      gNandFlashInfo->PageAddressStart = gNandPartInfoTable[Index].PageAddressStart;
-      Found = TRUE;
-      break;
-    }
-  }
-
-  if (Found == FALSE) {
-    DEBUG ((EFI_D_ERROR, "Nand part is not currently supported. Manufacture id: %x, Device id: %x\n", PartInfo[0], PartInfo[1]));
-    return EFI_NOT_FOUND;
-  }
-
-  //Populate NAND_FLASH_INFO based on the result of READ ID command.
-  gNandFlashInfo->ManufactureId = PartInfo[0];
-  gNandFlashInfo->DeviceId = PartInfo[1];
-  NandInfo = PartInfo[3];
-
-  if (PAGE_SIZE(NandInfo) == PAGE_SIZE_2K_VAL) {
-    gNandFlashInfo->PageSize = PAGE_SIZE_2K;
-  } else {
-    DEBUG ((EFI_D_ERROR, "Unknown Page size.\n"));
-    return EFI_DEVICE_ERROR;
-  }
-
-  if (SPARE_AREA_SIZE(NandInfo) == SPARE_AREA_SIZE_64B_VAL) {
-    gNandFlashInfo->SparePageSize = SPARE_AREA_SIZE_64B;
-  } else {
-    DEBUG ((EFI_D_ERROR, "Unknown Spare area size.\n"));
-    return EFI_DEVICE_ERROR;
-  }
-
-  if (BLOCK_SIZE(NandInfo) == BLOCK_SIZE_128K_VAL) {
-    gNandFlashInfo->BlockSize = BLOCK_SIZE_128K;
-  } else {
-    DEBUG ((EFI_D_ERROR, "Unknown Block size.\n"));
-    return EFI_DEVICE_ERROR;
-  }
-
-  if (ORGANIZATION(NandInfo) == ORGANIZATION_X8) {
-    gNandFlashInfo->Organization = 0;
-  } else if (ORGANIZATION(NandInfo) == ORGANIZATION_X16) {
-    gNandFlashInfo->Organization = 1;
-  }
-
-  //Calculate total number of blocks.
-  gNandFlashInfo->NumPagesPerBlock = DivU64x32(gNandFlashInfo->BlockSize, gNandFlashInfo->PageSize);
-
-  return EFI_SUCCESS;
-}
-
-VOID
-NandConfigureEcc (
-  VOID
-  )
-{
-  //Define ECC size 0 and size 1 to 512 bytes
-  MmioWrite32 (GPMC_ECC_SIZE_CONFIG, (ECCSIZE0_512BYTES | ECCSIZE1_512BYTES));
-}
-
-VOID 
-NandEnableEcc (
-  VOID
-  )
-{
-  //Clear all the ECC result registers and select ECC result register 1
-  MmioWrite32 (GPMC_ECC_CONTROL, (ECCCLEAR | ECCPOINTER_REG1));
-
-  //Enable ECC engine on CS0
-  MmioWrite32 (GPMC_ECC_CONFIG, (ECCENABLE | ECCCS_0 | ECC16B));
-}
-
-VOID
-NandDisableEcc (
-  VOID
-  )
-{
-  //Turn off ECC engine.
-  MmioWrite32 (GPMC_ECC_CONFIG, ECCDISABLE);
-}
-
-VOID
-NandCalculateEcc (
-  VOID
-  )
-{
-  UINTN Index;
-  UINTN EccResultRegister;
-  UINTN EccResult;
-
-  //Capture 32-bit ECC result for each 512-bytes chunk.
-  //In our case PageSize is 2K so read ECC1-ECC4 result registers and
-  //generate total of 12-bytes of ECC code for the particular page.
-
-  EccResultRegister = GPMC_ECC1_RESULT;
-
-  for (Index = 0; Index < gNum512BytesChunks; Index++) {
-
-    EccResult = MmioRead32 (EccResultRegister);
-
-    //Calculate ECC code from 32-bit ECC result value.
-    //NOTE: Following calculation is not part of TRM. We got this information 
-    //from Beagleboard mailing list.
-    gEccCode[Index * 3] = EccResult & 0xFF;
-    gEccCode[(Index * 3) + 1] = (EccResult >> 16) & 0xFF;
-    gEccCode[(Index * 3) + 2] = (((EccResult >> 20) & 0xF0) | ((EccResult >> 8) & 0x0F));
-
-    //Point to next ECC result register.
-    EccResultRegister += 4;
-  }
-}
-
-EFI_STATUS
-NandReadPage (
-  IN  UINTN                         BlockIndex,
-  IN  UINTN                         PageIndex,
-  OUT VOID                          *Buffer,
-  OUT UINT8                         *SpareBuffer
-)
-{
-  UINTN      Address;
-  UINTN      Index;
-  UINTN      NumMainAreaWords = (gNandFlashInfo->PageSize/2);
-  UINTN      NumSpareAreaWords = (gNandFlashInfo->SparePageSize/2);
-  UINT16     *MainAreaWordBuffer = Buffer;
-  UINT16     *SpareAreaWordBuffer = (UINT16 *)SpareBuffer;
-  UINTN      Timeout = MAX_RETRY_COUNT;
-
-  //Generate device address in bytes to access specific block and page index
-  Address = GetActualPageAddressInBytes(BlockIndex, PageIndex);
-
-  //Send READ command
-  NandSendCommand(PAGE_READ_CMD);
-
-  //Send 5 Address cycles to access specific device address
-  NandSendAddressCycles(Address);
-
-  //Send READ CONFIRM command
-  NandSendCommand(PAGE_READ_CONFIRM_CMD);
-
-  //Poll till device is busy.
-  while (Timeout) {
-    if ((NandReadStatus() & NAND_READY) == NAND_READY) {
-      break;
-    }
-    Timeout--;
-  }
-
-  if (Timeout == 0) {
-    DEBUG ((EFI_D_ERROR, "Read page timed out.\n"));
-    return EFI_TIMEOUT;
-  }
-
-  //Reissue READ command
-  NandSendCommand(PAGE_READ_CMD);
-
-  //Enable ECC engine.
-  NandEnableEcc();
-
-  //Read data into the buffer.
-  for (Index = 0; Index < NumMainAreaWords; Index++) {
-    *MainAreaWordBuffer++ = MmioRead16(GPMC_NAND_DATA_0);
-  }
-
-  //Read spare area into the buffer.
-  for (Index = 0; Index < NumSpareAreaWords; Index++) {
-    *SpareAreaWordBuffer++ = MmioRead16(GPMC_NAND_DATA_0);
-  }
-
-  //Calculate ECC.
-  NandCalculateEcc();
-
-  //Turn off ECC engine.
-  NandDisableEcc();
-
-  //Perform ECC correction.
-  //Need to implement..
-
-  return EFI_SUCCESS;
-}
-
-EFI_STATUS
-NandWritePage (
-  IN  UINTN                         BlockIndex,
-  IN  UINTN                         PageIndex,
-  OUT VOID                          *Buffer,
-  IN  UINT8                         *SpareBuffer
-)
-{
-  UINTN      Address;
-  UINT16     *MainAreaWordBuffer = Buffer;
-  UINT16     *SpareAreaWordBuffer = (UINT16 *)SpareBuffer;
-  UINTN      Index;
-  UINTN      NandStatus;
-  UINTN      Timeout = MAX_RETRY_COUNT;
-
-  //Generate device address in bytes to access specific block and page index
-  Address = GetActualPageAddressInBytes(BlockIndex, PageIndex);
-
-  //Send SERIAL DATA INPUT command
-  NandSendCommand(PROGRAM_PAGE_CMD);
-
-  //Send 5 Address cycles to access specific device address
-  NandSendAddressCycles(Address);
-
-  //Enable ECC engine.
-  NandEnableEcc();
-
-  //Data input from Buffer
-  for (Index = 0; Index < (gNandFlashInfo->PageSize/2); Index++) {
-    MmioWrite16(GPMC_NAND_DATA_0, *MainAreaWordBuffer++);
-
-    //After each write access, device has to wait to accept data.
-    //Currently we may not be programming proper timing parameters to 
-    //the GPMC_CONFIGi_0 registers and we would need to figure that out.
-    //Without following delay, page programming fails.
-    gBS->Stall(1);
-  }
-
-  //Calculate ECC.
-  NandCalculateEcc();
-
-  //Turn off ECC engine.
-  NandDisableEcc();
-
-  //Prepare Spare area buffer with ECC codes.
-  SetMem(SpareBuffer, gNandFlashInfo->SparePageSize, 0xFF);
-  CopyMem(&SpareBuffer[ECC_POSITION], gEccCode, gNum512BytesChunks * 3);
-
-  //Program spare area with calculated ECC.
-  for (Index = 0; Index < (gNandFlashInfo->SparePageSize/2); Index++) {
-    MmioWrite16(GPMC_NAND_DATA_0, *SpareAreaWordBuffer++);
-  }
-
-  //Send PROGRAM command
-  NandSendCommand(PROGRAM_PAGE_CONFIRM_CMD);
-
-  //Poll till device is busy.
-  NandStatus = 0;
-  while (Timeout) {
-    NandStatus = NandReadStatus();
-    if ((NandStatus & NAND_READY) == NAND_READY) {
-      break;
-    }
-    Timeout--;
-  }
-
-  if (Timeout == 0) {
-    DEBUG ((EFI_D_ERROR, "Program page timed out.\n"));
-    return EFI_TIMEOUT;
-  }
-
-  //Bit0 indicates Pass/Fail status
-  if (NandStatus & NAND_FAILURE) {
-    return EFI_DEVICE_ERROR;
-  }
-
-  return EFI_SUCCESS;
-}
-
-EFI_STATUS
-NandEraseBlock (
-  IN UINTN BlockIndex
-)
-{
-  UINTN      Address;
-  UINTN      NandStatus;
-  UINTN      Timeout = MAX_RETRY_COUNT;
-
-  //Generate device address in bytes to access specific block and page index
-  Address = GetActualPageAddressInBytes(BlockIndex, 0);
-
-  //Send ERASE SETUP command
-  NandSendCommand(BLOCK_ERASE_CMD);
-
-  //Send 3 address cycles to device to access Page address and Block address
-  Address >>= 11; //Ignore column addresses
-
-  NandSendAddress(Address & 0xff);
-  Address >>= 8;
-
-  NandSendAddress(Address & 0xff);
-  Address >>= 8;
-
-  NandSendAddress(Address & 0xff);
-
-  //Send ERASE CONFIRM command
-  NandSendCommand(BLOCK_ERASE_CONFIRM_CMD);
-
-  //Poll till device is busy.
-  NandStatus = 0;
-  while (Timeout) {
-    NandStatus = NandReadStatus();
-    if ((NandStatus & NAND_READY) == NAND_READY) {
-      break;
-    }
-    Timeout--;
-    gBS->Stall(1);
-  }
-
-  if (Timeout == 0) {
-    DEBUG ((EFI_D_ERROR, "Erase block timed out for Block: %d.\n", BlockIndex));
-    return EFI_TIMEOUT;
-  }
-
-  //Bit0 indicates Pass/Fail status
-  if (NandStatus & NAND_FAILURE) {
-    return EFI_DEVICE_ERROR;
-  }
-
-  return EFI_SUCCESS;
-}
-
-EFI_STATUS
-NandReadBlock (
-  IN UINTN                          StartBlockIndex,
-  IN UINTN                          EndBlockIndex,
-  OUT VOID                          *Buffer,
-  OUT VOID                          *SpareBuffer
-)
-{
-  UINTN      BlockIndex;
-  UINTN      PageIndex;
-  EFI_STATUS Status = EFI_SUCCESS;
-
-  for (BlockIndex = StartBlockIndex; BlockIndex <= EndBlockIndex; BlockIndex++) {
-    //For each block read number of pages
-    for (PageIndex = 0; PageIndex < gNandFlashInfo->NumPagesPerBlock; PageIndex++) {
-      Status = NandReadPage(BlockIndex, PageIndex, Buffer, SpareBuffer);
-      if (EFI_ERROR(Status)) {
-        return Status;
-      }
-      Buffer = ((UINT8 *)Buffer + gNandFlashInfo->PageSize);
-    }
-  }
-
-  return Status;
-}
-
-EFI_STATUS
-NandWriteBlock (
-  IN UINTN                          StartBlockIndex,
-  IN UINTN                          EndBlockIndex,
-  OUT VOID                          *Buffer,
-  OUT VOID                          *SpareBuffer
-  )
-{
-  UINTN      BlockIndex;
-  UINTN      PageIndex;
-  EFI_STATUS Status = EFI_SUCCESS;
-
-  for (BlockIndex = StartBlockIndex; BlockIndex <= EndBlockIndex; BlockIndex++) {
-    //Page programming.
-    for (PageIndex = 0; PageIndex < gNandFlashInfo->NumPagesPerBlock; PageIndex++) {
-      Status = NandWritePage(BlockIndex, PageIndex, Buffer, SpareBuffer);
-      if (EFI_ERROR(Status)) {
-        return Status;
-      }
-      Buffer = ((UINT8 *)Buffer + gNandFlashInfo->PageSize);
-    }
-  }
-
-  return Status;
-}
-
-EFI_STATUS
-EFIAPI
-NandFlashReset (
-  IN EFI_BLOCK_IO_PROTOCOL          *This,
-  IN BOOLEAN                        ExtendedVerification
-  )
-{
-  UINTN BusyStall = 50;                            // microSeconds
-  UINTN ResetBusyTimeout = (1000000 / BusyStall);  // 1 Second
-
-  //Send RESET command to device.
-  NandSendCommand(RESET_CMD);
-  
-  //Wait for 1ms before we check status register.
-  gBS->Stall(1000);
-
-  //Check BIT#5 & BIT#6 in Status register to make sure RESET is done.
-  while ((NandReadStatus() & NAND_RESET_STATUS) != NAND_RESET_STATUS) {
-
-    //In case of extended verification, wait for extended amount of time
-    //to make sure device is reset.
-    if (ExtendedVerification) {
-      if (ResetBusyTimeout == 0) {
-        return EFI_DEVICE_ERROR;
-      }
-
-      gBS->Stall(BusyStall);
-      ResetBusyTimeout--;
-    }
-  }
-  
-  return EFI_SUCCESS;
-}
-
-EFI_STATUS
-EFIAPI
-NandFlashReadBlocks (
-  IN EFI_BLOCK_IO_PROTOCOL          *This,
-  IN UINT32                         MediaId,
-  IN EFI_LBA                        Lba,
-  IN UINTN                          BufferSize,
-  OUT VOID                          *Buffer
-  )
-{
-  UINTN      NumBlocks;
-  UINTN      EndBlockIndex;
-  EFI_STATUS Status;
-  UINT8      *SpareBuffer = NULL;
-
-  if (Buffer == NULL) {
-    Status = EFI_INVALID_PARAMETER;
-    goto exit;
-  }
-  
-  if (Lba > LAST_BLOCK) {
-    Status = EFI_INVALID_PARAMETER;
-    goto exit;
-  }
-  
-  if ((BufferSize % gNandFlashInfo->BlockSize) != 0) {
-    Status = EFI_BAD_BUFFER_SIZE;
-    goto exit;
-  }
-
-  NumBlocks = DivU64x32(BufferSize, gNandFlashInfo->BlockSize);
-  EndBlockIndex = ((UINTN)Lba + NumBlocks) - 1;
-
-  SpareBuffer = (UINT8 *)AllocatePool(gNandFlashInfo->SparePageSize);
-  if (SpareBuffer == NULL) {
-    Status = EFI_OUT_OF_RESOURCES;
-    goto exit;
-  }
-
-  //Read block
-  Status = NandReadBlock((UINTN)Lba, EndBlockIndex, Buffer, SpareBuffer);
-  if (EFI_ERROR(Status)) {
-    DEBUG((EFI_D_ERROR, "Read block fails: %x\n", Status));
-    goto exit;
-  }
-
-exit:
-  if (SpareBuffer != NULL) {
-    FreePool (SpareBuffer);
-  }
-
-  return Status;
-}
-
-EFI_STATUS
-EFIAPI
-NandFlashWriteBlocks (
-  IN EFI_BLOCK_IO_PROTOCOL          *This,
-  IN UINT32                         MediaId,
-  IN EFI_LBA                        Lba,
-  IN UINTN                          BufferSize,
-  IN VOID                           *Buffer
-  )
-{
-  UINTN      BlockIndex;
-  UINTN      NumBlocks;
-  UINTN      EndBlockIndex;
-  EFI_STATUS Status;
-  UINT8      *SpareBuffer = NULL;
-
-  if (Buffer == NULL) {
-    Status = EFI_INVALID_PARAMETER;
-    goto exit;
-  }
-  
-  if (Lba > LAST_BLOCK) {
-    Status = EFI_INVALID_PARAMETER;
-    goto exit;
-  }
-  
-  if ((BufferSize % gNandFlashInfo->BlockSize) != 0) {
-    Status = EFI_BAD_BUFFER_SIZE;
-    goto exit;
-  }
-
-  NumBlocks = DivU64x32(BufferSize, gNandFlashInfo->BlockSize);
-  EndBlockIndex = ((UINTN)Lba + NumBlocks) - 1;
-
-  SpareBuffer = (UINT8 *)AllocatePool(gNandFlashInfo->SparePageSize);
-  if (SpareBuffer == NULL) {
-    Status = EFI_OUT_OF_RESOURCES;
-    goto exit;
-  }
-
-  // Erase block
-  for (BlockIndex = (UINTN)Lba; BlockIndex <= EndBlockIndex; BlockIndex++) {
-    Status = NandEraseBlock(BlockIndex);
-    if (EFI_ERROR(Status)) {
-      DEBUG((EFI_D_ERROR, "Erase block failed. Status: %x\n", Status));
-      goto exit;
-    }
-  }
-  
-  // Program data
-  Status = NandWriteBlock((UINTN)Lba, EndBlockIndex, Buffer, SpareBuffer);
-  if (EFI_ERROR(Status)) {
-    DEBUG((EFI_D_ERROR, "Block write fails: %x\n", Status));
-    goto exit;
-  }
-
-exit:
-  if (SpareBuffer != NULL) {
-    FreePool (SpareBuffer);
-  }
-
-  return Status;
-}
-
-EFI_STATUS
-EFIAPI
-NandFlashFlushBlocks (
-  IN EFI_BLOCK_IO_PROTOCOL  *This
-  )
-{
-  return EFI_SUCCESS;
-}
-
-
-
-EFI_BLOCK_IO_MEDIA gNandFlashMedia = {
-  SIGNATURE_32('n','a','n','d'),            // MediaId
-  FALSE,                                    // RemovableMedia
-  TRUE,                                     // MediaPresent
-  FALSE,                                    // LogicalPartition
-  FALSE,                                    // ReadOnly
-  FALSE,                                    // WriteCaching
-  0,                                        // BlockSize
-  2,                                        // IoAlign
-  0,                                        // Pad
-  0                                         // LastBlock
-};
-
-EFI_BLOCK_IO_PROTOCOL BlockIo = 
-{
-  EFI_BLOCK_IO_INTERFACE_REVISION,  // Revision
-  &gNandFlashMedia,                  // *Media
-  NandFlashReset,                   // Reset
-  NandFlashReadBlocks,              // ReadBlocks
-  NandFlashWriteBlocks,             // WriteBlocks
-  NandFlashFlushBlocks              // FlushBlocks
-};
-
-EFI_STATUS
-NandFlashInitialize (
-  IN EFI_HANDLE         ImageHandle,
-  IN EFI_SYSTEM_TABLE   *SystemTable
-  )
-{
-  EFI_STATUS  Status;
-  
-  gNandFlashInfo = (NAND_FLASH_INFO *)AllocateZeroPool (sizeof(NAND_FLASH_INFO));
-
-  //Initialize GPMC module.
-  GpmcInit();
-
-  //Reset NAND part
-  NandFlashReset(&BlockIo, FALSE);
-
-  //Detect NAND part and populate gNandFlashInfo structure
-  Status = NandDetectPart ();
-  if (EFI_ERROR(Status)) {
-    DEBUG((EFI_D_ERROR, "Nand part id detection failure: Status: %x\n", Status));
-    return Status;
-  }
-
-  //Count total number of 512Bytes chunk based on the page size.
-  if (gNandFlashInfo->PageSize == PAGE_SIZE_512B) {
-    gNum512BytesChunks = 1;
-  } else if (gNandFlashInfo->PageSize == PAGE_SIZE_2K) {
-    gNum512BytesChunks = 4;
-  } else if (gNandFlashInfo->PageSize == PAGE_SIZE_4K) {
-    gNum512BytesChunks = 8;
-  }
-
-  gEccCode = (UINT8 *)AllocatePool(gNum512BytesChunks * 3);
-  if (gEccCode == NULL) {
-    return EFI_OUT_OF_RESOURCES;
-  }
-
-  //Configure ECC
-  NandConfigureEcc ();
-
-  //Patch EFI_BLOCK_IO_MEDIA structure.
-  gNandFlashMedia.BlockSize = gNandFlashInfo->BlockSize;
-  gNandFlashMedia.LastBlock = LAST_BLOCK;
-
-  //Publish BlockIO.
-  Status = gBS->InstallMultipleProtocolInterfaces (
-                  &ImageHandle, 
-                  &gEfiBlockIoProtocolGuid, &BlockIo,
-                  &gEfiDevicePathProtocolGuid, &gDevicePath, 
-                  NULL
-                  );
-  return Status;
-}
-
+/** @file\r
+\r
+  Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>\r
+  \r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Flash.h"\r
+\r
+NAND_PART_INFO_TABLE gNandPartInfoTable[1] = {\r
+  { 0x2C, 0xBA, 17, 11 }\r
+};\r
+\r
+NAND_FLASH_INFO *gNandFlashInfo = NULL;\r
+UINT8           *gEccCode;\r
+UINTN           gNum512BytesChunks = 0;\r
+\r
+//\r
+\r
+// Device path for SemiHosting. It contains our autogened Caller ID GUID.\r
+\r
+//\r
+\r
+typedef struct {\r
+\r
+  VENDOR_DEVICE_PATH        Guid;\r
+\r
+  EFI_DEVICE_PATH_PROTOCOL  End;\r
+\r
+} FLASH_DEVICE_PATH;\r
+\r
+\r
+\r
+FLASH_DEVICE_PATH gDevicePath = {\r
+\r
+  {\r
+\r
+    { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, sizeof (VENDOR_DEVICE_PATH), 0 },\r
+\r
+    EFI_CALLER_ID_GUID\r
+\r
+  },\r
+\r
+  { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, sizeof (EFI_DEVICE_PATH_PROTOCOL), 0}\r
+\r
+};\r
+\r
+\r
+\r
+//Actual page address = Column address + Page address + Block address.\r
+UINTN\r
+GetActualPageAddressInBytes (\r
+  UINTN BlockIndex,\r
+  UINTN PageIndex\r
+)\r
+{\r
+  //BlockAddressStart = Start of the Block address in actual NAND\r
+  //PageAddressStart = Start of the Page address in actual NAND\r
+  return ((BlockIndex << gNandFlashInfo->BlockAddressStart) + (PageIndex << gNandFlashInfo->PageAddressStart));\r
+}\r
+\r
+VOID\r
+NandSendCommand (\r
+  UINT8 Command\r
+)\r
+{\r
+  MmioWrite16(GPMC_NAND_COMMAND_0, Command);\r
+}\r
+\r
+VOID\r
+NandSendAddress (\r
+  UINT8 Address\r
+)\r
+{\r
+  MmioWrite16(GPMC_NAND_ADDRESS_0, Address);\r
+}\r
+\r
+UINT16\r
+NandReadStatus (\r
+  VOID\r
+  )\r
+{\r
+  //Send READ STATUS command\r
+  NandSendCommand(READ_STATUS_CMD);\r
+\r
+  //Read status.\r
+  return MmioRead16(GPMC_NAND_DATA_0);\r
+}\r
+\r
+VOID\r
+NandSendAddressCycles (\r
+  UINTN Address\r
+)\r
+{\r
+  //Column address\r
+  NandSendAddress(Address & 0xff);\r
+  Address >>= 8;\r
+\r
+  //Column address\r
+  NandSendAddress(Address & 0x07);\r
+  Address >>= 3;\r
+\r
+  //Page and Block address\r
+  NandSendAddress(Address & 0xff);\r
+  Address >>= 8;\r
+\r
+  //Block address\r
+  NandSendAddress(Address & 0xff);\r
+  Address >>= 8;\r
+\r
+  //Block address\r
+  NandSendAddress(Address & 0x01);\r
+}\r
+\r
+VOID\r
+GpmcInit (\r
+  VOID\r
+  )\r
+{\r
+  //Enable Smart-idle mode.\r
+  MmioWrite32 (GPMC_SYSCONFIG, SMARTIDLEMODE);\r
+\r
+  //Set IRQSTATUS and IRQENABLE to the reset value\r
+  MmioWrite32 (GPMC_IRQSTATUS, 0x0);\r
+  MmioWrite32 (GPMC_IRQENABLE, 0x0);\r
+\r
+  //Disable GPMC timeout control.\r
+  MmioWrite32 (GPMC_TIMEOUT_CONTROL, TIMEOUTDISABLE);\r
+\r
+  //Set WRITEPROTECT bit to enable write access.\r
+  MmioWrite32 (GPMC_CONFIG, WRITEPROTECT_HIGH);\r
+\r
+  //NOTE: Following GPMC_CONFIGi_0 register settings are taken from u-boot memory dump.\r
+  MmioWrite32 (GPMC_CONFIG1_0, DEVICETYPE_NAND | DEVICESIZE_X16);\r
+  MmioWrite32 (GPMC_CONFIG2_0, CSRDOFFTIME | CSWROFFTIME);\r
+  MmioWrite32 (GPMC_CONFIG3_0, ADVRDOFFTIME | ADVWROFFTIME);\r
+  MmioWrite32 (GPMC_CONFIG4_0, OEONTIME | OEOFFTIME | WEONTIME | WEOFFTIME);\r
+  MmioWrite32 (GPMC_CONFIG5_0, RDCYCLETIME | WRCYCLETIME | RDACCESSTIME | PAGEBURSTACCESSTIME);\r
+  MmioWrite32 (GPMC_CONFIG6_0, WRACCESSTIME | WRDATAONADMUXBUS | CYCLE2CYCLEDELAY | CYCLE2CYCLESAMECSEN);\r
+  MmioWrite32 (GPMC_CONFIG7_0, MASKADDRESS_128MB | CSVALID | BASEADDRESS);\r
+}\r
+\r
+EFI_STATUS\r
+NandDetectPart (\r
+  VOID\r
+)\r
+{\r
+  UINT8      NandInfo = 0;\r
+  UINT8      PartInfo[5];\r
+  UINTN      Index;\r
+  BOOLEAN    Found = FALSE;\r
+\r
+  //Send READ ID command\r
+  NandSendCommand(READ_ID_CMD);\r
+\r
+  //Send one address cycle.\r
+  NandSendAddress(0);\r
+\r
+  //Read 5-bytes to idenfity code programmed into the NAND flash devices.\r
+  //BYTE 0 = Manufacture ID\r
+  //Byte 1 = Device ID\r
+  //Byte 2, 3, 4 = Nand part specific information (Page size, Block size etc)\r
+  for (Index = 0; Index < sizeof(PartInfo); Index++) {\r
+    PartInfo[Index] = MmioRead16(GPMC_NAND_DATA_0);\r
+  }\r
+\r
+  //Check if the ManufactureId and DeviceId are part of the currently supported nand parts.\r
+  for (Index = 0; Index < sizeof(gNandPartInfoTable)/sizeof(NAND_PART_INFO_TABLE); Index++) {\r
+    if (gNandPartInfoTable[Index].ManufactureId == PartInfo[0] && gNandPartInfoTable[Index].DeviceId == PartInfo[1]) {\r
+      gNandFlashInfo->BlockAddressStart = gNandPartInfoTable[Index].BlockAddressStart;\r
+      gNandFlashInfo->PageAddressStart = gNandPartInfoTable[Index].PageAddressStart;\r
+      Found = TRUE;\r
+      break;\r
+    }\r
+  }\r
+\r
+  if (Found == FALSE) {\r
+    DEBUG ((EFI_D_ERROR, "Nand part is not currently supported. Manufacture id: %x, Device id: %x\n", PartInfo[0], PartInfo[1]));\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  //Populate NAND_FLASH_INFO based on the result of READ ID command.\r
+  gNandFlashInfo->ManufactureId = PartInfo[0];\r
+  gNandFlashInfo->DeviceId = PartInfo[1];\r
+  NandInfo = PartInfo[3];\r
+\r
+  if (PAGE_SIZE(NandInfo) == PAGE_SIZE_2K_VAL) {\r
+    gNandFlashInfo->PageSize = PAGE_SIZE_2K;\r
+  } else {\r
+    DEBUG ((EFI_D_ERROR, "Unknown Page size.\n"));\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  if (SPARE_AREA_SIZE(NandInfo) == SPARE_AREA_SIZE_64B_VAL) {\r
+    gNandFlashInfo->SparePageSize = SPARE_AREA_SIZE_64B;\r
+  } else {\r
+    DEBUG ((EFI_D_ERROR, "Unknown Spare area size.\n"));\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  if (BLOCK_SIZE(NandInfo) == BLOCK_SIZE_128K_VAL) {\r
+    gNandFlashInfo->BlockSize = BLOCK_SIZE_128K;\r
+  } else {\r
+    DEBUG ((EFI_D_ERROR, "Unknown Block size.\n"));\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  if (ORGANIZATION(NandInfo) == ORGANIZATION_X8) {\r
+    gNandFlashInfo->Organization = 0;\r
+  } else if (ORGANIZATION(NandInfo) == ORGANIZATION_X16) {\r
+    gNandFlashInfo->Organization = 1;\r
+  }\r
+\r
+  //Calculate total number of blocks.\r
+  gNandFlashInfo->NumPagesPerBlock = DivU64x32(gNandFlashInfo->BlockSize, gNandFlashInfo->PageSize);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+VOID\r
+NandConfigureEcc (\r
+  VOID\r
+  )\r
+{\r
+  //Define ECC size 0 and size 1 to 512 bytes\r
+  MmioWrite32 (GPMC_ECC_SIZE_CONFIG, (ECCSIZE0_512BYTES | ECCSIZE1_512BYTES));\r
+}\r
+\r
+VOID \r
+NandEnableEcc (\r
+  VOID\r
+  )\r
+{\r
+  //Clear all the ECC result registers and select ECC result register 1\r
+  MmioWrite32 (GPMC_ECC_CONTROL, (ECCCLEAR | ECCPOINTER_REG1));\r
+\r
+  //Enable ECC engine on CS0\r
+  MmioWrite32 (GPMC_ECC_CONFIG, (ECCENABLE | ECCCS_0 | ECC16B));\r
+}\r
+\r
+VOID\r
+NandDisableEcc (\r
+  VOID\r
+  )\r
+{\r
+  //Turn off ECC engine.\r
+  MmioWrite32 (GPMC_ECC_CONFIG, ECCDISABLE);\r
+}\r
+\r
+VOID\r
+NandCalculateEcc (\r
+  VOID\r
+  )\r
+{\r
+  UINTN Index;\r
+  UINTN EccResultRegister;\r
+  UINTN EccResult;\r
+\r
+  //Capture 32-bit ECC result for each 512-bytes chunk.\r
+  //In our case PageSize is 2K so read ECC1-ECC4 result registers and\r
+  //generate total of 12-bytes of ECC code for the particular page.\r
+\r
+  EccResultRegister = GPMC_ECC1_RESULT;\r
+\r
+  for (Index = 0; Index < gNum512BytesChunks; Index++) {\r
+\r
+    EccResult = MmioRead32 (EccResultRegister);\r
+\r
+    //Calculate ECC code from 32-bit ECC result value.\r
+    //NOTE: Following calculation is not part of TRM. We got this information \r
+    //from Beagleboard mailing list.\r
+    gEccCode[Index * 3] = EccResult & 0xFF;\r
+    gEccCode[(Index * 3) + 1] = (EccResult >> 16) & 0xFF;\r
+    gEccCode[(Index * 3) + 2] = (((EccResult >> 20) & 0xF0) | ((EccResult >> 8) & 0x0F));\r
+\r
+    //Point to next ECC result register.\r
+    EccResultRegister += 4;\r
+  }\r
+}\r
+\r
+EFI_STATUS\r
+NandReadPage (\r
+  IN  UINTN                         BlockIndex,\r
+  IN  UINTN                         PageIndex,\r
+  OUT VOID                          *Buffer,\r
+  OUT UINT8                         *SpareBuffer\r
+)\r
+{\r
+  UINTN      Address;\r
+  UINTN      Index;\r
+  UINTN      NumMainAreaWords = (gNandFlashInfo->PageSize/2);\r
+  UINTN      NumSpareAreaWords = (gNandFlashInfo->SparePageSize/2);\r
+  UINT16     *MainAreaWordBuffer = Buffer;\r
+  UINT16     *SpareAreaWordBuffer = (UINT16 *)SpareBuffer;\r
+  UINTN      Timeout = MAX_RETRY_COUNT;\r
+\r
+  //Generate device address in bytes to access specific block and page index\r
+  Address = GetActualPageAddressInBytes(BlockIndex, PageIndex);\r
+\r
+  //Send READ command\r
+  NandSendCommand(PAGE_READ_CMD);\r
+\r
+  //Send 5 Address cycles to access specific device address\r
+  NandSendAddressCycles(Address);\r
+\r
+  //Send READ CONFIRM command\r
+  NandSendCommand(PAGE_READ_CONFIRM_CMD);\r
+\r
+  //Poll till device is busy.\r
+  while (Timeout) {\r
+    if ((NandReadStatus() & NAND_READY) == NAND_READY) {\r
+      break;\r
+    }\r
+    Timeout--;\r
+  }\r
+\r
+  if (Timeout == 0) {\r
+    DEBUG ((EFI_D_ERROR, "Read page timed out.\n"));\r
+    return EFI_TIMEOUT;\r
+  }\r
+\r
+  //Reissue READ command\r
+  NandSendCommand(PAGE_READ_CMD);\r
+\r
+  //Enable ECC engine.\r
+  NandEnableEcc();\r
+\r
+  //Read data into the buffer.\r
+  for (Index = 0; Index < NumMainAreaWords; Index++) {\r
+    *MainAreaWordBuffer++ = MmioRead16(GPMC_NAND_DATA_0);\r
+  }\r
+\r
+  //Read spare area into the buffer.\r
+  for (Index = 0; Index < NumSpareAreaWords; Index++) {\r
+    *SpareAreaWordBuffer++ = MmioRead16(GPMC_NAND_DATA_0);\r
+  }\r
+\r
+  //Calculate ECC.\r
+  NandCalculateEcc();\r
+\r
+  //Turn off ECC engine.\r
+  NandDisableEcc();\r
+\r
+  //Perform ECC correction.\r
+  //Need to implement..\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+NandWritePage (\r
+  IN  UINTN                         BlockIndex,\r
+  IN  UINTN                         PageIndex,\r
+  OUT VOID                          *Buffer,\r
+  IN  UINT8                         *SpareBuffer\r
+)\r
+{\r
+  UINTN      Address;\r
+  UINT16     *MainAreaWordBuffer = Buffer;\r
+  UINT16     *SpareAreaWordBuffer = (UINT16 *)SpareBuffer;\r
+  UINTN      Index;\r
+  UINTN      NandStatus;\r
+  UINTN      Timeout = MAX_RETRY_COUNT;\r
+\r
+  //Generate device address in bytes to access specific block and page index\r
+  Address = GetActualPageAddressInBytes(BlockIndex, PageIndex);\r
+\r
+  //Send SERIAL DATA INPUT command\r
+  NandSendCommand(PROGRAM_PAGE_CMD);\r
+\r
+  //Send 5 Address cycles to access specific device address\r
+  NandSendAddressCycles(Address);\r
+\r
+  //Enable ECC engine.\r
+  NandEnableEcc();\r
+\r
+  //Data input from Buffer\r
+  for (Index = 0; Index < (gNandFlashInfo->PageSize/2); Index++) {\r
+    MmioWrite16(GPMC_NAND_DATA_0, *MainAreaWordBuffer++);\r
+\r
+    //After each write access, device has to wait to accept data.\r
+    //Currently we may not be programming proper timing parameters to \r
+    //the GPMC_CONFIGi_0 registers and we would need to figure that out.\r
+    //Without following delay, page programming fails.\r
+    gBS->Stall(1);\r
+  }\r
+\r
+  //Calculate ECC.\r
+  NandCalculateEcc();\r
+\r
+  //Turn off ECC engine.\r
+  NandDisableEcc();\r
+\r
+  //Prepare Spare area buffer with ECC codes.\r
+  SetMem(SpareBuffer, gNandFlashInfo->SparePageSize, 0xFF);\r
+  CopyMem(&SpareBuffer[ECC_POSITION], gEccCode, gNum512BytesChunks * 3);\r
+\r
+  //Program spare area with calculated ECC.\r
+  for (Index = 0; Index < (gNandFlashInfo->SparePageSize/2); Index++) {\r
+    MmioWrite16(GPMC_NAND_DATA_0, *SpareAreaWordBuffer++);\r
+  }\r
+\r
+  //Send PROGRAM command\r
+  NandSendCommand(PROGRAM_PAGE_CONFIRM_CMD);\r
+\r
+  //Poll till device is busy.\r
+  NandStatus = 0;\r
+  while (Timeout) {\r
+    NandStatus = NandReadStatus();\r
+    if ((NandStatus & NAND_READY) == NAND_READY) {\r
+      break;\r
+    }\r
+    Timeout--;\r
+  }\r
+\r
+  if (Timeout == 0) {\r
+    DEBUG ((EFI_D_ERROR, "Program page timed out.\n"));\r
+    return EFI_TIMEOUT;\r
+  }\r
+\r
+  //Bit0 indicates Pass/Fail status\r
+  if (NandStatus & NAND_FAILURE) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+NandEraseBlock (\r
+  IN UINTN BlockIndex\r
+)\r
+{\r
+  UINTN      Address;\r
+  UINTN      NandStatus;\r
+  UINTN      Timeout = MAX_RETRY_COUNT;\r
+\r
+  //Generate device address in bytes to access specific block and page index\r
+  Address = GetActualPageAddressInBytes(BlockIndex, 0);\r
+\r
+  //Send ERASE SETUP command\r
+  NandSendCommand(BLOCK_ERASE_CMD);\r
+\r
+  //Send 3 address cycles to device to access Page address and Block address\r
+  Address >>= 11; //Ignore column addresses\r
+\r
+  NandSendAddress(Address & 0xff);\r
+  Address >>= 8;\r
+\r
+  NandSendAddress(Address & 0xff);\r
+  Address >>= 8;\r
+\r
+  NandSendAddress(Address & 0xff);\r
+\r
+  //Send ERASE CONFIRM command\r
+  NandSendCommand(BLOCK_ERASE_CONFIRM_CMD);\r
+\r
+  //Poll till device is busy.\r
+  NandStatus = 0;\r
+  while (Timeout) {\r
+    NandStatus = NandReadStatus();\r
+    if ((NandStatus & NAND_READY) == NAND_READY) {\r
+      break;\r
+    }\r
+    Timeout--;\r
+    gBS->Stall(1);\r
+  }\r
+\r
+  if (Timeout == 0) {\r
+    DEBUG ((EFI_D_ERROR, "Erase block timed out for Block: %d.\n", BlockIndex));\r
+    return EFI_TIMEOUT;\r
+  }\r
+\r
+  //Bit0 indicates Pass/Fail status\r
+  if (NandStatus & NAND_FAILURE) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+NandReadBlock (\r
+  IN UINTN                          StartBlockIndex,\r
+  IN UINTN                          EndBlockIndex,\r
+  OUT VOID                          *Buffer,\r
+  OUT VOID                          *SpareBuffer\r
+)\r
+{\r
+  UINTN      BlockIndex;\r
+  UINTN      PageIndex;\r
+  EFI_STATUS Status = EFI_SUCCESS;\r
+\r
+  for (BlockIndex = StartBlockIndex; BlockIndex <= EndBlockIndex; BlockIndex++) {\r
+    //For each block read number of pages\r
+    for (PageIndex = 0; PageIndex < gNandFlashInfo->NumPagesPerBlock; PageIndex++) {\r
+      Status = NandReadPage(BlockIndex, PageIndex, Buffer, SpareBuffer);\r
+      if (EFI_ERROR(Status)) {\r
+        return Status;\r
+      }\r
+      Buffer = ((UINT8 *)Buffer + gNandFlashInfo->PageSize);\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+EFI_STATUS\r
+NandWriteBlock (\r
+  IN UINTN                          StartBlockIndex,\r
+  IN UINTN                          EndBlockIndex,\r
+  OUT VOID                          *Buffer,\r
+  OUT VOID                          *SpareBuffer\r
+  )\r
+{\r
+  UINTN      BlockIndex;\r
+  UINTN      PageIndex;\r
+  EFI_STATUS Status = EFI_SUCCESS;\r
+\r
+  for (BlockIndex = StartBlockIndex; BlockIndex <= EndBlockIndex; BlockIndex++) {\r
+    //Page programming.\r
+    for (PageIndex = 0; PageIndex < gNandFlashInfo->NumPagesPerBlock; PageIndex++) {\r
+      Status = NandWritePage(BlockIndex, PageIndex, Buffer, SpareBuffer);\r
+      if (EFI_ERROR(Status)) {\r
+        return Status;\r
+      }\r
+      Buffer = ((UINT8 *)Buffer + gNandFlashInfo->PageSize);\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+NandFlashReset (\r
+  IN EFI_BLOCK_IO_PROTOCOL          *This,\r
+  IN BOOLEAN                        ExtendedVerification\r
+  )\r
+{\r
+  UINTN BusyStall = 50;                            // microSeconds\r
+  UINTN ResetBusyTimeout = (1000000 / BusyStall);  // 1 Second\r
+\r
+  //Send RESET command to device.\r
+  NandSendCommand(RESET_CMD);\r
+  \r
+  //Wait for 1ms before we check status register.\r
+  gBS->Stall(1000);\r
+\r
+  //Check BIT#5 & BIT#6 in Status register to make sure RESET is done.\r
+  while ((NandReadStatus() & NAND_RESET_STATUS) != NAND_RESET_STATUS) {\r
+\r
+    //In case of extended verification, wait for extended amount of time\r
+    //to make sure device is reset.\r
+    if (ExtendedVerification) {\r
+      if (ResetBusyTimeout == 0) {\r
+        return EFI_DEVICE_ERROR;\r
+      }\r
+\r
+      gBS->Stall(BusyStall);\r
+      ResetBusyTimeout--;\r
+    }\r
+  }\r
+  \r
+  return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+NandFlashReadBlocks (\r
+  IN EFI_BLOCK_IO_PROTOCOL          *This,\r
+  IN UINT32                         MediaId,\r
+  IN EFI_LBA                        Lba,\r
+  IN UINTN                          BufferSize,\r
+  OUT VOID                          *Buffer\r
+  )\r
+{\r
+  UINTN      NumBlocks;\r
+  UINTN      EndBlockIndex;\r
+  EFI_STATUS Status;\r
+  UINT8      *SpareBuffer = NULL;\r
+\r
+  if (Buffer == NULL) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto exit;\r
+  }\r
+  \r
+  if (Lba > LAST_BLOCK) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto exit;\r
+  }\r
+  \r
+  if ((BufferSize % gNandFlashInfo->BlockSize) != 0) {\r
+    Status = EFI_BAD_BUFFER_SIZE;\r
+    goto exit;\r
+  }\r
+\r
+  NumBlocks = DivU64x32(BufferSize, gNandFlashInfo->BlockSize);\r
+  EndBlockIndex = ((UINTN)Lba + NumBlocks) - 1;\r
+\r
+  SpareBuffer = (UINT8 *)AllocatePool(gNandFlashInfo->SparePageSize);\r
+  if (SpareBuffer == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto exit;\r
+  }\r
+\r
+  //Read block\r
+  Status = NandReadBlock((UINTN)Lba, EndBlockIndex, Buffer, SpareBuffer);\r
+  if (EFI_ERROR(Status)) {\r
+    DEBUG((EFI_D_ERROR, "Read block fails: %x\n", Status));\r
+    goto exit;\r
+  }\r
+\r
+exit:\r
+  if (SpareBuffer != NULL) {\r
+    FreePool (SpareBuffer);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+NandFlashWriteBlocks (\r
+  IN EFI_BLOCK_IO_PROTOCOL          *This,\r
+  IN UINT32                         MediaId,\r
+  IN EFI_LBA                        Lba,\r
+  IN UINTN                          BufferSize,\r
+  IN VOID                           *Buffer\r
+  )\r
+{\r
+  UINTN      BlockIndex;\r
+  UINTN      NumBlocks;\r
+  UINTN      EndBlockIndex;\r
+  EFI_STATUS Status;\r
+  UINT8      *SpareBuffer = NULL;\r
+\r
+  if (Buffer == NULL) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto exit;\r
+  }\r
+  \r
+  if (Lba > LAST_BLOCK) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto exit;\r
+  }\r
+  \r
+  if ((BufferSize % gNandFlashInfo->BlockSize) != 0) {\r
+    Status = EFI_BAD_BUFFER_SIZE;\r
+    goto exit;\r
+  }\r
+\r
+  NumBlocks = DivU64x32(BufferSize, gNandFlashInfo->BlockSize);\r
+  EndBlockIndex = ((UINTN)Lba + NumBlocks) - 1;\r
+\r
+  SpareBuffer = (UINT8 *)AllocatePool(gNandFlashInfo->SparePageSize);\r
+  if (SpareBuffer == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto exit;\r
+  }\r
+\r
+  // Erase block\r
+  for (BlockIndex = (UINTN)Lba; BlockIndex <= EndBlockIndex; BlockIndex++) {\r
+    Status = NandEraseBlock(BlockIndex);\r
+    if (EFI_ERROR(Status)) {\r
+      DEBUG((EFI_D_ERROR, "Erase block failed. Status: %x\n", Status));\r
+      goto exit;\r
+    }\r
+  }\r
+  \r
+  // Program data\r
+  Status = NandWriteBlock((UINTN)Lba, EndBlockIndex, Buffer, SpareBuffer);\r
+  if (EFI_ERROR(Status)) {\r
+    DEBUG((EFI_D_ERROR, "Block write fails: %x\n", Status));\r
+    goto exit;\r
+  }\r
+\r
+exit:\r
+  if (SpareBuffer != NULL) {\r
+    FreePool (SpareBuffer);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+EFI_STATUS\r
+EFIAPI\r
+NandFlashFlushBlocks (\r
+  IN EFI_BLOCK_IO_PROTOCOL  *This\r
+  )\r
+{\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+\r
+EFI_BLOCK_IO_MEDIA gNandFlashMedia = {\r
+  SIGNATURE_32('n','a','n','d'),            // MediaId\r
+  FALSE,                                    // RemovableMedia\r
+  TRUE,                                     // MediaPresent\r
+  FALSE,                                    // LogicalPartition\r
+  FALSE,                                    // ReadOnly\r
+  FALSE,                                    // WriteCaching\r
+  0,                                        // BlockSize\r
+  2,                                        // IoAlign\r
+  0,                                        // Pad\r
+  0                                         // LastBlock\r
+};\r
+\r
+EFI_BLOCK_IO_PROTOCOL BlockIo = \r
+{\r
+  EFI_BLOCK_IO_INTERFACE_REVISION,  // Revision\r
+  &gNandFlashMedia,                  // *Media\r
+  NandFlashReset,                   // Reset\r
+  NandFlashReadBlocks,              // ReadBlocks\r
+  NandFlashWriteBlocks,             // WriteBlocks\r
+  NandFlashFlushBlocks              // FlushBlocks\r
+};\r
+\r
+EFI_STATUS\r
+NandFlashInitialize (\r
+  IN EFI_HANDLE         ImageHandle,\r
+  IN EFI_SYSTEM_TABLE   *SystemTable\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  \r
+  gNandFlashInfo = (NAND_FLASH_INFO *)AllocateZeroPool (sizeof(NAND_FLASH_INFO));\r
+\r
+  //Initialize GPMC module.\r
+  GpmcInit();\r
+\r
+  //Reset NAND part\r
+  NandFlashReset(&BlockIo, FALSE);\r
+\r
+  //Detect NAND part and populate gNandFlashInfo structure\r
+  Status = NandDetectPart ();\r
+  if (EFI_ERROR(Status)) {\r
+    DEBUG((EFI_D_ERROR, "Nand part id detection failure: Status: %x\n", Status));\r
+    return Status;\r
+  }\r
+\r
+  //Count total number of 512Bytes chunk based on the page size.\r
+  if (gNandFlashInfo->PageSize == PAGE_SIZE_512B) {\r
+    gNum512BytesChunks = 1;\r
+  } else if (gNandFlashInfo->PageSize == PAGE_SIZE_2K) {\r
+    gNum512BytesChunks = 4;\r
+  } else if (gNandFlashInfo->PageSize == PAGE_SIZE_4K) {\r
+    gNum512BytesChunks = 8;\r
+  }\r
+\r
+  gEccCode = (UINT8 *)AllocatePool(gNum512BytesChunks * 3);\r
+  if (gEccCode == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //Configure ECC\r
+  NandConfigureEcc ();\r
+\r
+  //Patch EFI_BLOCK_IO_MEDIA structure.\r
+  gNandFlashMedia.BlockSize = gNandFlashInfo->BlockSize;\r
+  gNandFlashMedia.LastBlock = LAST_BLOCK;\r
+\r
+  //Publish BlockIO.\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &ImageHandle, \r
+                  &gEfiBlockIoProtocolGuid, &BlockIo,\r
+                  &gEfiDevicePathProtocolGuid, &gDevicePath, \r
+                  NULL\r
+                  );\r
+  return Status;\r
+}\r
+\r