Home | History | Annotate | Download | only in Flash
      1 /** @file
      2 
      3   Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
      4 
      5   This program and the accompanying materials
      6   are licensed and made available under the terms and conditions of the BSD License
      7   which accompanies this distribution.  The full text of the license may be found at
      8   http://opensource.org/licenses/bsd-license.php
      9 
     10   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13 **/
     14 
     15 #include "Flash.h"
     16 
     17 NAND_PART_INFO_TABLE gNandPartInfoTable[1] = {
     18   { 0x2C, 0xBA, 17, 11 }
     19 };
     20 
     21 NAND_FLASH_INFO *gNandFlashInfo = NULL;
     22 UINT8           *gEccCode;
     23 UINTN           gNum512BytesChunks = 0;
     24 
     25 //
     26 
     27 // Device path for SemiHosting. It contains our autogened Caller ID GUID.
     28 
     29 //
     30 
     31 typedef struct {
     32 
     33   VENDOR_DEVICE_PATH        Guid;
     34 
     35   EFI_DEVICE_PATH_PROTOCOL  End;
     36 
     37 } FLASH_DEVICE_PATH;
     38 
     39 
     40 
     41 FLASH_DEVICE_PATH gDevicePath = {
     42   {
     43     { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { sizeof (VENDOR_DEVICE_PATH), 0 } },
     44     EFI_CALLER_ID_GUID
     45   },
     46   { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0} }
     47 };
     48 
     49 
     50 
     51 //Actual page address = Column address + Page address + Block address.
     52 UINTN
     53 GetActualPageAddressInBytes (
     54   UINTN BlockIndex,
     55   UINTN PageIndex
     56 )
     57 {
     58   //BlockAddressStart = Start of the Block address in actual NAND
     59   //PageAddressStart = Start of the Page address in actual NAND
     60   return ((BlockIndex << gNandFlashInfo->BlockAddressStart) + (PageIndex << gNandFlashInfo->PageAddressStart));
     61 }
     62 
     63 VOID
     64 NandSendCommand (
     65   UINT8 Command
     66 )
     67 {
     68   MmioWrite16(GPMC_NAND_COMMAND_0, Command);
     69 }
     70 
     71 VOID
     72 NandSendAddress (
     73   UINT8 Address
     74 )
     75 {
     76   MmioWrite16(GPMC_NAND_ADDRESS_0, Address);
     77 }
     78 
     79 UINT16
     80 NandReadStatus (
     81   VOID
     82   )
     83 {
     84   //Send READ STATUS command
     85   NandSendCommand(READ_STATUS_CMD);
     86 
     87   //Read status.
     88   return MmioRead16(GPMC_NAND_DATA_0);
     89 }
     90 
     91 VOID
     92 NandSendAddressCycles (
     93   UINTN Address
     94 )
     95 {
     96   //Column address
     97   NandSendAddress(Address & 0xff);
     98   Address >>= 8;
     99 
    100   //Column address
    101   NandSendAddress(Address & 0x07);
    102   Address >>= 3;
    103 
    104   //Page and Block address
    105   NandSendAddress(Address & 0xff);
    106   Address >>= 8;
    107 
    108   //Block address
    109   NandSendAddress(Address & 0xff);
    110   Address >>= 8;
    111 
    112   //Block address
    113   NandSendAddress(Address & 0x01);
    114 }
    115 
    116 VOID
    117 GpmcInit (
    118   VOID
    119   )
    120 {
    121   //Enable Smart-idle mode.
    122   MmioWrite32 (GPMC_SYSCONFIG, SMARTIDLEMODE);
    123 
    124   //Set IRQSTATUS and IRQENABLE to the reset value
    125   MmioWrite32 (GPMC_IRQSTATUS, 0x0);
    126   MmioWrite32 (GPMC_IRQENABLE, 0x0);
    127 
    128   //Disable GPMC timeout control.
    129   MmioWrite32 (GPMC_TIMEOUT_CONTROL, TIMEOUTDISABLE);
    130 
    131   //Set WRITEPROTECT bit to enable write access.
    132   MmioWrite32 (GPMC_CONFIG, WRITEPROTECT_HIGH);
    133 
    134   //NOTE: Following GPMC_CONFIGi_0 register settings are taken from u-boot memory dump.
    135   MmioWrite32 (GPMC_CONFIG1_0, DEVICETYPE_NAND | DEVICESIZE_X16);
    136   MmioWrite32 (GPMC_CONFIG2_0, CSRDOFFTIME | CSWROFFTIME);
    137   MmioWrite32 (GPMC_CONFIG3_0, ADVRDOFFTIME | ADVWROFFTIME);
    138   MmioWrite32 (GPMC_CONFIG4_0, OEONTIME | OEOFFTIME | WEONTIME | WEOFFTIME);
    139   MmioWrite32 (GPMC_CONFIG5_0, RDCYCLETIME | WRCYCLETIME | RDACCESSTIME | PAGEBURSTACCESSTIME);
    140   MmioWrite32 (GPMC_CONFIG6_0, WRACCESSTIME | WRDATAONADMUXBUS | CYCLE2CYCLEDELAY | CYCLE2CYCLESAMECSEN);
    141   MmioWrite32 (GPMC_CONFIG7_0, MASKADDRESS_128MB | CSVALID | BASEADDRESS);
    142 }
    143 
    144 EFI_STATUS
    145 NandDetectPart (
    146   VOID
    147 )
    148 {
    149   UINT8      NandInfo = 0;
    150   UINT8      PartInfo[5];
    151   UINTN      Index;
    152   BOOLEAN    Found = FALSE;
    153 
    154   //Send READ ID command
    155   NandSendCommand(READ_ID_CMD);
    156 
    157   //Send one address cycle.
    158   NandSendAddress(0);
    159 
    160   //Read 5-bytes to idenfity code programmed into the NAND flash devices.
    161   //BYTE 0 = Manufacture ID
    162   //Byte 1 = Device ID
    163   //Byte 2, 3, 4 = Nand part specific information (Page size, Block size etc)
    164   for (Index = 0; Index < sizeof(PartInfo); Index++) {
    165     PartInfo[Index] = MmioRead16(GPMC_NAND_DATA_0);
    166   }
    167 
    168   //Check if the ManufactureId and DeviceId are part of the currently supported nand parts.
    169   for (Index = 0; Index < sizeof(gNandPartInfoTable)/sizeof(NAND_PART_INFO_TABLE); Index++) {
    170     if (gNandPartInfoTable[Index].ManufactureId == PartInfo[0] && gNandPartInfoTable[Index].DeviceId == PartInfo[1]) {
    171       gNandFlashInfo->BlockAddressStart = gNandPartInfoTable[Index].BlockAddressStart;
    172       gNandFlashInfo->PageAddressStart = gNandPartInfoTable[Index].PageAddressStart;
    173       Found = TRUE;
    174       break;
    175     }
    176   }
    177 
    178   if (Found == FALSE) {
    179     DEBUG ((EFI_D_ERROR, "Nand part is not currently supported. Manufacture id: %x, Device id: %x\n", PartInfo[0], PartInfo[1]));
    180     return EFI_NOT_FOUND;
    181   }
    182 
    183   //Populate NAND_FLASH_INFO based on the result of READ ID command.
    184   gNandFlashInfo->ManufactureId = PartInfo[0];
    185   gNandFlashInfo->DeviceId = PartInfo[1];
    186   NandInfo = PartInfo[3];
    187 
    188   if (PAGE_SIZE(NandInfo) == PAGE_SIZE_2K_VAL) {
    189     gNandFlashInfo->PageSize = PAGE_SIZE_2K;
    190   } else {
    191     DEBUG ((EFI_D_ERROR, "Unknown Page size.\n"));
    192     return EFI_DEVICE_ERROR;
    193   }
    194 
    195   if (SPARE_AREA_SIZE(NandInfo) == SPARE_AREA_SIZE_64B_VAL) {
    196     gNandFlashInfo->SparePageSize = SPARE_AREA_SIZE_64B;
    197   } else {
    198     DEBUG ((EFI_D_ERROR, "Unknown Spare area size.\n"));
    199     return EFI_DEVICE_ERROR;
    200   }
    201 
    202   if (BLOCK_SIZE(NandInfo) == BLOCK_SIZE_128K_VAL) {
    203     gNandFlashInfo->BlockSize = BLOCK_SIZE_128K;
    204   } else {
    205     DEBUG ((EFI_D_ERROR, "Unknown Block size.\n"));
    206     return EFI_DEVICE_ERROR;
    207   }
    208 
    209   if (ORGANIZATION(NandInfo) == ORGANIZATION_X8) {
    210     gNandFlashInfo->Organization = 0;
    211   } else if (ORGANIZATION(NandInfo) == ORGANIZATION_X16) {
    212     gNandFlashInfo->Organization = 1;
    213   }
    214 
    215   //Calculate total number of blocks.
    216   gNandFlashInfo->NumPagesPerBlock = DivU64x32(gNandFlashInfo->BlockSize, gNandFlashInfo->PageSize);
    217 
    218   return EFI_SUCCESS;
    219 }
    220 
    221 VOID
    222 NandConfigureEcc (
    223   VOID
    224   )
    225 {
    226   //Define ECC size 0 and size 1 to 512 bytes
    227   MmioWrite32 (GPMC_ECC_SIZE_CONFIG, (ECCSIZE0_512BYTES | ECCSIZE1_512BYTES));
    228 }
    229 
    230 VOID
    231 NandEnableEcc (
    232   VOID
    233   )
    234 {
    235   //Clear all the ECC result registers and select ECC result register 1
    236   MmioWrite32 (GPMC_ECC_CONTROL, (ECCCLEAR | ECCPOINTER_REG1));
    237 
    238   //Enable ECC engine on CS0
    239   MmioWrite32 (GPMC_ECC_CONFIG, (ECCENABLE | ECCCS_0 | ECC16B));
    240 }
    241 
    242 VOID
    243 NandDisableEcc (
    244   VOID
    245   )
    246 {
    247   //Turn off ECC engine.
    248   MmioWrite32 (GPMC_ECC_CONFIG, ECCDISABLE);
    249 }
    250 
    251 VOID
    252 NandCalculateEcc (
    253   VOID
    254   )
    255 {
    256   UINTN Index;
    257   UINTN EccResultRegister;
    258   UINTN EccResult;
    259 
    260   //Capture 32-bit ECC result for each 512-bytes chunk.
    261   //In our case PageSize is 2K so read ECC1-ECC4 result registers and
    262   //generate total of 12-bytes of ECC code for the particular page.
    263 
    264   EccResultRegister = GPMC_ECC1_RESULT;
    265 
    266   for (Index = 0; Index < gNum512BytesChunks; Index++) {
    267 
    268     EccResult = MmioRead32 (EccResultRegister);
    269 
    270     //Calculate ECC code from 32-bit ECC result value.
    271     //NOTE: Following calculation is not part of TRM. We got this information
    272     //from Beagleboard mailing list.
    273     gEccCode[Index * 3] = EccResult & 0xFF;
    274     gEccCode[(Index * 3) + 1] = (EccResult >> 16) & 0xFF;
    275     gEccCode[(Index * 3) + 2] = (((EccResult >> 20) & 0xF0) | ((EccResult >> 8) & 0x0F));
    276 
    277     //Point to next ECC result register.
    278     EccResultRegister += 4;
    279   }
    280 }
    281 
    282 EFI_STATUS
    283 NandReadPage (
    284   IN  UINTN                         BlockIndex,
    285   IN  UINTN                         PageIndex,
    286   OUT VOID                          *Buffer,
    287   OUT UINT8                         *SpareBuffer
    288 )
    289 {
    290   UINTN      Address;
    291   UINTN      Index;
    292   UINTN      NumMainAreaWords = (gNandFlashInfo->PageSize/2);
    293   UINTN      NumSpareAreaWords = (gNandFlashInfo->SparePageSize/2);
    294   UINT16     *MainAreaWordBuffer = Buffer;
    295   UINT16     *SpareAreaWordBuffer = (UINT16 *)SpareBuffer;
    296   UINTN      Timeout = MAX_RETRY_COUNT;
    297 
    298   //Generate device address in bytes to access specific block and page index
    299   Address = GetActualPageAddressInBytes(BlockIndex, PageIndex);
    300 
    301   //Send READ command
    302   NandSendCommand(PAGE_READ_CMD);
    303 
    304   //Send 5 Address cycles to access specific device address
    305   NandSendAddressCycles(Address);
    306 
    307   //Send READ CONFIRM command
    308   NandSendCommand(PAGE_READ_CONFIRM_CMD);
    309 
    310   //Poll till device is busy.
    311   while (Timeout) {
    312     if ((NandReadStatus() & NAND_READY) == NAND_READY) {
    313       break;
    314     }
    315     Timeout--;
    316   }
    317 
    318   if (Timeout == 0) {
    319     DEBUG ((EFI_D_ERROR, "Read page timed out.\n"));
    320     return EFI_TIMEOUT;
    321   }
    322 
    323   //Reissue READ command
    324   NandSendCommand(PAGE_READ_CMD);
    325 
    326   //Enable ECC engine.
    327   NandEnableEcc();
    328 
    329   //Read data into the buffer.
    330   for (Index = 0; Index < NumMainAreaWords; Index++) {
    331     *MainAreaWordBuffer++ = MmioRead16(GPMC_NAND_DATA_0);
    332   }
    333 
    334   //Read spare area into the buffer.
    335   for (Index = 0; Index < NumSpareAreaWords; Index++) {
    336     *SpareAreaWordBuffer++ = MmioRead16(GPMC_NAND_DATA_0);
    337   }
    338 
    339   //Calculate ECC.
    340   NandCalculateEcc();
    341 
    342   //Turn off ECC engine.
    343   NandDisableEcc();
    344 
    345   //Perform ECC correction.
    346   //Need to implement..
    347 
    348   return EFI_SUCCESS;
    349 }
    350 
    351 EFI_STATUS
    352 NandWritePage (
    353   IN  UINTN                         BlockIndex,
    354   IN  UINTN                         PageIndex,
    355   OUT VOID                          *Buffer,
    356   IN  UINT8                         *SpareBuffer
    357 )
    358 {
    359   UINTN      Address;
    360   UINT16     *MainAreaWordBuffer = Buffer;
    361   UINT16     *SpareAreaWordBuffer = (UINT16 *)SpareBuffer;
    362   UINTN      Index;
    363   UINTN      NandStatus;
    364   UINTN      Timeout = MAX_RETRY_COUNT;
    365 
    366   //Generate device address in bytes to access specific block and page index
    367   Address = GetActualPageAddressInBytes(BlockIndex, PageIndex);
    368 
    369   //Send SERIAL DATA INPUT command
    370   NandSendCommand(PROGRAM_PAGE_CMD);
    371 
    372   //Send 5 Address cycles to access specific device address
    373   NandSendAddressCycles(Address);
    374 
    375   //Enable ECC engine.
    376   NandEnableEcc();
    377 
    378   //Data input from Buffer
    379   for (Index = 0; Index < (gNandFlashInfo->PageSize/2); Index++) {
    380     MmioWrite16(GPMC_NAND_DATA_0, *MainAreaWordBuffer++);
    381 
    382     //After each write access, device has to wait to accept data.
    383     //Currently we may not be programming proper timing parameters to
    384     //the GPMC_CONFIGi_0 registers and we would need to figure that out.
    385     //Without following delay, page programming fails.
    386     gBS->Stall(1);
    387   }
    388 
    389   //Calculate ECC.
    390   NandCalculateEcc();
    391 
    392   //Turn off ECC engine.
    393   NandDisableEcc();
    394 
    395   //Prepare Spare area buffer with ECC codes.
    396   SetMem(SpareBuffer, gNandFlashInfo->SparePageSize, 0xFF);
    397   CopyMem(&SpareBuffer[ECC_POSITION], gEccCode, gNum512BytesChunks * 3);
    398 
    399   //Program spare area with calculated ECC.
    400   for (Index = 0; Index < (gNandFlashInfo->SparePageSize/2); Index++) {
    401     MmioWrite16(GPMC_NAND_DATA_0, *SpareAreaWordBuffer++);
    402   }
    403 
    404   //Send PROGRAM command
    405   NandSendCommand(PROGRAM_PAGE_CONFIRM_CMD);
    406 
    407   //Poll till device is busy.
    408   NandStatus = 0;
    409   while (Timeout) {
    410     NandStatus = NandReadStatus();
    411     if ((NandStatus & NAND_READY) == NAND_READY) {
    412       break;
    413     }
    414     Timeout--;
    415   }
    416 
    417   if (Timeout == 0) {
    418     DEBUG ((EFI_D_ERROR, "Program page timed out.\n"));
    419     return EFI_TIMEOUT;
    420   }
    421 
    422   //Bit0 indicates Pass/Fail status
    423   if (NandStatus & NAND_FAILURE) {
    424     return EFI_DEVICE_ERROR;
    425   }
    426 
    427   return EFI_SUCCESS;
    428 }
    429 
    430 EFI_STATUS
    431 NandEraseBlock (
    432   IN UINTN BlockIndex
    433 )
    434 {
    435   UINTN      Address;
    436   UINTN      NandStatus;
    437   UINTN      Timeout = MAX_RETRY_COUNT;
    438 
    439   //Generate device address in bytes to access specific block and page index
    440   Address = GetActualPageAddressInBytes(BlockIndex, 0);
    441 
    442   //Send ERASE SETUP command
    443   NandSendCommand(BLOCK_ERASE_CMD);
    444 
    445   //Send 3 address cycles to device to access Page address and Block address
    446   Address >>= 11; //Ignore column addresses
    447 
    448   NandSendAddress(Address & 0xff);
    449   Address >>= 8;
    450 
    451   NandSendAddress(Address & 0xff);
    452   Address >>= 8;
    453 
    454   NandSendAddress(Address & 0xff);
    455 
    456   //Send ERASE CONFIRM command
    457   NandSendCommand(BLOCK_ERASE_CONFIRM_CMD);
    458 
    459   //Poll till device is busy.
    460   NandStatus = 0;
    461   while (Timeout) {
    462     NandStatus = NandReadStatus();
    463     if ((NandStatus & NAND_READY) == NAND_READY) {
    464       break;
    465     }
    466     Timeout--;
    467     gBS->Stall(1);
    468   }
    469 
    470   if (Timeout == 0) {
    471     DEBUG ((EFI_D_ERROR, "Erase block timed out for Block: %d.\n", BlockIndex));
    472     return EFI_TIMEOUT;
    473   }
    474 
    475   //Bit0 indicates Pass/Fail status
    476   if (NandStatus & NAND_FAILURE) {
    477     return EFI_DEVICE_ERROR;
    478   }
    479 
    480   return EFI_SUCCESS;
    481 }
    482 
    483 EFI_STATUS
    484 NandReadBlock (
    485   IN UINTN                          StartBlockIndex,
    486   IN UINTN                          EndBlockIndex,
    487   OUT VOID                          *Buffer,
    488   OUT VOID                          *SpareBuffer
    489 )
    490 {
    491   UINTN      BlockIndex;
    492   UINTN      PageIndex;
    493   EFI_STATUS Status = EFI_SUCCESS;
    494 
    495   for (BlockIndex = StartBlockIndex; BlockIndex <= EndBlockIndex; BlockIndex++) {
    496     //For each block read number of pages
    497     for (PageIndex = 0; PageIndex < gNandFlashInfo->NumPagesPerBlock; PageIndex++) {
    498       Status = NandReadPage(BlockIndex, PageIndex, Buffer, SpareBuffer);
    499       if (EFI_ERROR(Status)) {
    500         return Status;
    501       }
    502       Buffer = ((UINT8 *)Buffer + gNandFlashInfo->PageSize);
    503     }
    504   }
    505 
    506   return Status;
    507 }
    508 
    509 EFI_STATUS
    510 NandWriteBlock (
    511   IN UINTN                          StartBlockIndex,
    512   IN UINTN                          EndBlockIndex,
    513   OUT VOID                          *Buffer,
    514   OUT VOID                          *SpareBuffer
    515   )
    516 {
    517   UINTN      BlockIndex;
    518   UINTN      PageIndex;
    519   EFI_STATUS Status = EFI_SUCCESS;
    520 
    521   for (BlockIndex = StartBlockIndex; BlockIndex <= EndBlockIndex; BlockIndex++) {
    522     //Page programming.
    523     for (PageIndex = 0; PageIndex < gNandFlashInfo->NumPagesPerBlock; PageIndex++) {
    524       Status = NandWritePage(BlockIndex, PageIndex, Buffer, SpareBuffer);
    525       if (EFI_ERROR(Status)) {
    526         return Status;
    527       }
    528       Buffer = ((UINT8 *)Buffer + gNandFlashInfo->PageSize);
    529     }
    530   }
    531 
    532   return Status;
    533 }
    534 
    535 EFI_STATUS
    536 EFIAPI
    537 NandFlashReset (
    538   IN EFI_BLOCK_IO_PROTOCOL          *This,
    539   IN BOOLEAN                        ExtendedVerification
    540   )
    541 {
    542   UINTN BusyStall = 50;                            // microSeconds
    543   UINTN ResetBusyTimeout = (1000000 / BusyStall);  // 1 Second
    544 
    545   //Send RESET command to device.
    546   NandSendCommand(RESET_CMD);
    547 
    548   //Wait for 1ms before we check status register.
    549   gBS->Stall(1000);
    550 
    551   //Check BIT#5 & BIT#6 in Status register to make sure RESET is done.
    552   while ((NandReadStatus() & NAND_RESET_STATUS) != NAND_RESET_STATUS) {
    553 
    554     //In case of extended verification, wait for extended amount of time
    555     //to make sure device is reset.
    556     if (ExtendedVerification) {
    557       if (ResetBusyTimeout == 0) {
    558         return EFI_DEVICE_ERROR;
    559       }
    560 
    561       gBS->Stall(BusyStall);
    562       ResetBusyTimeout--;
    563     }
    564   }
    565 
    566   return EFI_SUCCESS;
    567 }
    568 
    569 EFI_STATUS
    570 EFIAPI
    571 NandFlashReadBlocks (
    572   IN EFI_BLOCK_IO_PROTOCOL          *This,
    573   IN UINT32                         MediaId,
    574   IN EFI_LBA                        Lba,
    575   IN UINTN                          BufferSize,
    576   OUT VOID                          *Buffer
    577   )
    578 {
    579   UINTN      NumBlocks;
    580   UINTN      EndBlockIndex;
    581   EFI_STATUS Status;
    582   UINT8      *SpareBuffer = NULL;
    583 
    584   if (Buffer == NULL) {
    585     Status = EFI_INVALID_PARAMETER;
    586     goto exit;
    587   }
    588 
    589   if (Lba > LAST_BLOCK) {
    590     Status = EFI_INVALID_PARAMETER;
    591     goto exit;
    592   }
    593 
    594   if ((BufferSize % gNandFlashInfo->BlockSize) != 0) {
    595     Status = EFI_BAD_BUFFER_SIZE;
    596     goto exit;
    597   }
    598 
    599   NumBlocks = DivU64x32(BufferSize, gNandFlashInfo->BlockSize);
    600   EndBlockIndex = ((UINTN)Lba + NumBlocks) - 1;
    601 
    602   SpareBuffer = (UINT8 *)AllocatePool(gNandFlashInfo->SparePageSize);
    603   if (SpareBuffer == NULL) {
    604     Status = EFI_OUT_OF_RESOURCES;
    605     goto exit;
    606   }
    607 
    608   //Read block
    609   Status = NandReadBlock((UINTN)Lba, EndBlockIndex, Buffer, SpareBuffer);
    610   if (EFI_ERROR(Status)) {
    611     DEBUG((EFI_D_ERROR, "Read block fails: %x\n", Status));
    612     goto exit;
    613   }
    614 
    615 exit:
    616   if (SpareBuffer != NULL) {
    617     FreePool (SpareBuffer);
    618   }
    619 
    620   return Status;
    621 }
    622 
    623 EFI_STATUS
    624 EFIAPI
    625 NandFlashWriteBlocks (
    626   IN EFI_BLOCK_IO_PROTOCOL          *This,
    627   IN UINT32                         MediaId,
    628   IN EFI_LBA                        Lba,
    629   IN UINTN                          BufferSize,
    630   IN VOID                           *Buffer
    631   )
    632 {
    633   UINTN      BlockIndex;
    634   UINTN      NumBlocks;
    635   UINTN      EndBlockIndex;
    636   EFI_STATUS Status;
    637   UINT8      *SpareBuffer = NULL;
    638 
    639   if (Buffer == NULL) {
    640     Status = EFI_INVALID_PARAMETER;
    641     goto exit;
    642   }
    643 
    644   if (Lba > LAST_BLOCK) {
    645     Status = EFI_INVALID_PARAMETER;
    646     goto exit;
    647   }
    648 
    649   if ((BufferSize % gNandFlashInfo->BlockSize) != 0) {
    650     Status = EFI_BAD_BUFFER_SIZE;
    651     goto exit;
    652   }
    653 
    654   NumBlocks = DivU64x32(BufferSize, gNandFlashInfo->BlockSize);
    655   EndBlockIndex = ((UINTN)Lba + NumBlocks) - 1;
    656 
    657   SpareBuffer = (UINT8 *)AllocatePool(gNandFlashInfo->SparePageSize);
    658   if (SpareBuffer == NULL) {
    659     Status = EFI_OUT_OF_RESOURCES;
    660     goto exit;
    661   }
    662 
    663   // Erase block
    664   for (BlockIndex = (UINTN)Lba; BlockIndex <= EndBlockIndex; BlockIndex++) {
    665     Status = NandEraseBlock(BlockIndex);
    666     if (EFI_ERROR(Status)) {
    667       DEBUG((EFI_D_ERROR, "Erase block failed. Status: %x\n", Status));
    668       goto exit;
    669     }
    670   }
    671 
    672   // Program data
    673   Status = NandWriteBlock((UINTN)Lba, EndBlockIndex, Buffer, SpareBuffer);
    674   if (EFI_ERROR(Status)) {
    675     DEBUG((EFI_D_ERROR, "Block write fails: %x\n", Status));
    676     goto exit;
    677   }
    678 
    679 exit:
    680   if (SpareBuffer != NULL) {
    681     FreePool (SpareBuffer);
    682   }
    683 
    684   return Status;
    685 }
    686 
    687 EFI_STATUS
    688 EFIAPI
    689 NandFlashFlushBlocks (
    690   IN EFI_BLOCK_IO_PROTOCOL  *This
    691   )
    692 {
    693   return EFI_SUCCESS;
    694 }
    695 
    696 
    697 
    698 EFI_BLOCK_IO_MEDIA gNandFlashMedia = {
    699   SIGNATURE_32('n','a','n','d'),            // MediaId
    700   FALSE,                                    // RemovableMedia
    701   TRUE,                                     // MediaPresent
    702   FALSE,                                    // LogicalPartition
    703   FALSE,                                    // ReadOnly
    704   FALSE,                                    // WriteCaching
    705   0,                                        // BlockSize
    706   2,                                        // IoAlign
    707   0,                                        // Pad
    708   0                                         // LastBlock
    709 };
    710 
    711 EFI_BLOCK_IO_PROTOCOL BlockIo =
    712 {
    713   EFI_BLOCK_IO_INTERFACE_REVISION,  // Revision
    714   &gNandFlashMedia,                  // *Media
    715   NandFlashReset,                   // Reset
    716   NandFlashReadBlocks,              // ReadBlocks
    717   NandFlashWriteBlocks,             // WriteBlocks
    718   NandFlashFlushBlocks              // FlushBlocks
    719 };
    720 
    721 EFI_STATUS
    722 NandFlashInitialize (
    723   IN EFI_HANDLE         ImageHandle,
    724   IN EFI_SYSTEM_TABLE   *SystemTable
    725   )
    726 {
    727   EFI_STATUS  Status;
    728 
    729   gNandFlashInfo = (NAND_FLASH_INFO *)AllocateZeroPool (sizeof(NAND_FLASH_INFO));
    730 
    731   //Initialize GPMC module.
    732   GpmcInit();
    733 
    734   //Reset NAND part
    735   NandFlashReset(&BlockIo, FALSE);
    736 
    737   //Detect NAND part and populate gNandFlashInfo structure
    738   Status = NandDetectPart ();
    739   if (EFI_ERROR(Status)) {
    740     DEBUG((EFI_D_ERROR, "Nand part id detection failure: Status: %x\n", Status));
    741     return Status;
    742   }
    743 
    744   //Count total number of 512Bytes chunk based on the page size.
    745   if (gNandFlashInfo->PageSize == PAGE_SIZE_512B) {
    746     gNum512BytesChunks = 1;
    747   } else if (gNandFlashInfo->PageSize == PAGE_SIZE_2K) {
    748     gNum512BytesChunks = 4;
    749   } else if (gNandFlashInfo->PageSize == PAGE_SIZE_4K) {
    750     gNum512BytesChunks = 8;
    751   }
    752 
    753   gEccCode = (UINT8 *)AllocatePool(gNum512BytesChunks * 3);
    754   if (gEccCode == NULL) {
    755     return EFI_OUT_OF_RESOURCES;
    756   }
    757 
    758   //Configure ECC
    759   NandConfigureEcc ();
    760 
    761   //Patch EFI_BLOCK_IO_MEDIA structure.
    762   gNandFlashMedia.BlockSize = gNandFlashInfo->BlockSize;
    763   gNandFlashMedia.LastBlock = LAST_BLOCK;
    764 
    765   //Publish BlockIO.
    766   Status = gBS->InstallMultipleProtocolInterfaces (
    767                   &ImageHandle,
    768                   &gEfiBlockIoProtocolGuid, &BlockIo,
    769                   &gEfiDevicePathProtocolGuid, &gDevicePath,
    770                   NULL
    771                   );
    772   return Status;
    773 }
    774 
    775