/** @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; | |
} | |