]> git.proxmox.com Git - mirror_edk2.git/blobdiff - Omap35xxPkg/Flash/Flash.c
Moving OMAP 3530 code out of BeagleBoard package into its own package
[mirror_edk2.git] / Omap35xxPkg / Flash / Flash.c
diff --git a/Omap35xxPkg/Flash/Flash.c b/Omap35xxPkg/Flash/Flash.c
new file mode 100644 (file)
index 0000000..698003b
--- /dev/null
@@ -0,0 +1,763 @@
+/** @file
+
+  Copyright (c) 2008-2009, Apple Inc. All rights reserved.
+  
+  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 "Flash.h"
+
+NAND_PART_INFO_TABLE gNandPartInfoTable[1] = {
+  { 0x2C, 0xBA, 17, 11 }
+};
+
+NAND_FLASH_INFO *gNandFlashInfo = NULL;
+UINT8           *gEccCode;
+UINTN           gNum512BytesChunks = 0;
+
+//\r
+// Device path for SemiHosting. It contains our autogened Caller ID GUID.\r
+//\r
+typedef struct {\r
+  VENDOR_DEVICE_PATH        Guid;\r
+  EFI_DEVICE_PATH_PROTOCOL  End;\r
+} FLASH_DEVICE_PATH;\r
+\r
+FLASH_DEVICE_PATH gDevicePath = {\r
+  {\r
+    { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, sizeof (VENDOR_DEVICE_PATH), 0 },\r
+    EFI_CALLER_ID_GUID\r
+  },\r
+  { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, sizeof (EFI_DEVICE_PATH_PROTOCOL), 0}\r
+};\r
+
+
+//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.
+  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.
+  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;
+}
+