blob: a41763ed814bfb5f0724b11983dbbeaf84783e5d [file] [log] [blame]
/******************************************************************************
** COPYRIGHT 2007 Marvell Inernational Ltd.
** All Rights Reserved
******************************************************************************/
#include "Typedef.h"
#include "FM.h"
#include "Partition.h"
#include "Errors.h"
#include "Flash.h"
#include "misc.h"
#include "loadoffsets.h"
#if COPYIMAGESTOFLASH
#include "BootLoader.h"
#endif
//keep a pointer that points the beginning of the UNUSED data space allocated for FM
UINT_T *pFM_SPACE = NULL;
UINT_T max_tim_size_byte = MAX_TIM_SIZE;
//routine to get a pointer to the FM properties
FMProperties_T FMProperties;
P_FMProperties_T GetFMProperties()
{
return &FMProperties;
}
void SCAN_FactoryBadBlock()
{
#ifdef BBM_LEGACY_EXT
return SCAN_FactoryBadBlockLegacyExt();
#else
UINT16 i, badblocklist[100];
UINT fBBNum, NewBlock, initial_blk, end_blk, num_relo;
P_FlashProperties_T pFlashProp = GetFlashProperties(BOOT_FLASH);
P_FMProperties_T pFMP = GetFMProperties();
P_ReloTable_T pBBT = pFMP->pLBBT;
GenerateFBBT_F Gen_FBBT = pFlashProp->GenerateFBBT;
//return early if not supported
if( (Gen_FBBT == NULL) || (pFlashProp->FlashSettings.UseBBM == 0) )
return;
if((pBBT == NULL) || ((UINT16)BBT_TYPE_LEGACY != pBBT->Header))
return;
//get RP boundaries
#if defined(NAND_CODE) || defined(SPINAND_CODE)
initial_blk = pFlashProp->NumBlocks - 1;
#endif
end_blk = initial_blk - (pFlashProp->NumBlocks * LEGACY_BBM_RELOC_PERCENTAGE + 99) / 100;
fBBNum = Gen_FBBT(badblocklist);
num_relo = pBBT->NumReloc;
for(i = 0; i < fBBNum; i++){
//if the block is in reserved pool, just mark it as bad block
if(badblocklist[i] > end_blk)
{
pBBT->Relo[pBBT->NumReloc].From = badblocklist[i];
pBBT->Relo[pBBT->NumReloc].To = 0xFFFF;
pBBT->NumReloc++;
}
}
if(num_relo != pBBT->NumReloc)
SetBBTState(BBT_CHANGED, BOOT_FLASH);
for(i = 0; i < fBBNum; i++){
if(badblocklist[i] <= end_blk)
RelocateBlock(badblocklist[i], &NewBlock, BOOT_FLASH);
}
#endif
}
/*
* IntializeFM
*
* This function intializes the Flash Management, so as to allow use of Bad Block Management
* and a Partition Table. This function supports legacy BBM, as well as the new Marvell method.
*
* Inputs:
* FM_type - tells us which type of Flash Management we should search for (Legacy, Marvell, both, etc.)
* Outputs: none
* Returns: none
*/
void InitializeFM(FM_Method_T FM_type, FlashBootType_T fbt)
{
UINT_T Retval, j, PageSize;
P_FMProperties_T pFMProp = GetFMProperties();
P_ReloTable_T pLBBT = NULL;
P_ABBT_Table_T pABBT = NULL;
UINT_T BBTMaxEntryNum = 0;
UINT_T DumpEntryNum = 0;
PlatformInitFM(&max_tim_size_byte);
UINT_T BlockOffset = 0;
#ifdef ENABLE_BBM
PageSize = GetPageSize(fbt);
pFM_SPACE = malloc(PageSize << 1);
if(pFM_SPACE == NULL)
return HeapExhaustedError;
#ifdef BBM_LEGACY_EXT
InitL1BBTBlocks(); /* Initialize the blocks which are handled by L1 BBT */
#endif
//if we only allow legacy method, just search for that
if(FM_type == LEGACY_METHOD)
{
#ifdef BBM_LEGACY_EXT
pFMProp->FindBBT = &FindBBT_LegacyExt;
#else
pFMProp->FindBBT = &FindBBT_Legacy;
#endif
#if DUAL_TIM
if(DualTimEnabled()) {
BlockOffset = GetCurTimBlock() * GetBlockSize(fbt);
}
#endif
Retval = pFMProp->FindBBT(BlockOffset, fbt);
#if COPYIMAGESTOFLASH
if(Retval != NoError)
{
#ifdef BBM_LEGACY_EXT
CreateBBT_LegacyExt(NULL);
#else
CreateBBT_Legacy(NULL);
#endif
#if defined(NAND_CODE) || defined(SPINAND_CODE)
//Retval = ScanNANDForBadBlocks(fbt);
SCAN_FactoryBadBlock();
#endif
}
#endif
}
UpdateBBT();
#ifdef BBM_LEGACY_EXT
pFMProp->ABBT_MaxNum = ( PageSize - ABBT_HEADER_SIZE) / sizeof(ReloPair_T) ;
obm_printf("ABBT max entry number: %d\n\r", pFMProp->ABBT_MaxNum);
pLBBT = pFMProp->pLBBT;
pABBT = pFMProp->pABBT;
BBTMaxEntryNum = ( PageSize - BBT_HEADER_SIZE) / sizeof(ReloPair_T) ;
if (pLBBT == NULL || pLBBT->NumReloc == 0) {
obm_printf("BBT: Empty\n\r");
}else{
obm_printf("BBT: %d entry\n\r", pLBBT->NumReloc);
DumpEntryNum = (BBTMaxEntryNum < pLBBT->NumReloc) ? BBTMaxEntryNum : pLBBT->NumReloc;
for(j = 0; j < DumpEntryNum; j++) {
if (pLBBT->Relo[j].From != BLOCK_BAD || pLBBT->Relo[j].To != BLOCK_BAD)
obm_printf("%d: block %d ---> %d\n\r", j+1, pLBBT->Relo[j].From, pLBBT->Relo[j].To);
}
}
if (pABBT == NULL || pABBT->NumReloc == 0) {
obm_printf("ABBT: Empty\n\r");
} else {
obm_printf("ABBT: %d entry\n\r", pABBT->NumReloc);
DumpEntryNum = (pFMProp->ABBT_MaxNum < pABBT->NumReloc) ? (pFMProp->ABBT_MaxNum) : pABBT->NumReloc;
for(j = 0; j < DumpEntryNum; j++) {
obm_printf("%d: block %d ---> %d\n\r", j+1, pABBT->Relo[j].From, pABBT->Relo[j].To);
}
}
#endif
#endif
}
//This function will write out any unwritten PT or BBT
void FinalizeFM(FlashBootType_T fbt)
{
UINT_T Retval, size;
P_FMProperties_T pFMProp = GetFMProperties();
//second, write out the PT - second conditional is a sanity check (can't overwrite TIM)
if( (pFMProp->PT_state == PT_NEW) && (pFMProp->PT_location > 0) )
{
size = sizeof(PartitionTable_T) + pFMProp->pMPT->NumPartitions * sizeof(PartitionInfo_T);
WriteFlash(pFMProp->PT_location, (UINT)pFMProp->pMPT, size, fbt);
}
#ifdef ENABLE_BBM
//third, make sure the BBT is written out. This is for the case that the partition was already
//set to 0, and the outstanding BBT did not get written. NOTE: it must work this way, since
//the PT in Block 0 MUST be written before any other BBT in block 0 (MLC restriction on page burning)
#ifdef BBM_LEGACY_EXT
//In case that bitflips happen during booting, flush the BBT out before recycle and erase the bitflip blocks
UpdateBBT();
RecyleBlocks_LegacyExt();
UpdateBBT();
RemapRecycledBlocks();
UpdateBBT();
#endif
//lastly, clear out the structure
ClearFM(fbt);
#endif
}
/*
* ClearFM
*
* This function clears out the Flash Management data space and
* sets all state variables to 'invalid', so as to invalidate the
* Flash Management until is has been intialized.
*/
void ClearFM(FlashBootType_T fbt)
{
#ifdef ENABLE_BBM
UINT_T tempPartNum, i;
P_FMProperties_T pFMProp = GetFMProperties();
//Step 1: Set variables to a default state
//Clear out the properties structure (deletes stale data)
// '>> 2' converts byte total to DWord total
tempPartNum = pFMProp->PI_num; // Save this value as it was determined from flash driver
for (i = 0; i < (sizeof(FMProperties_T) >> 2); i++)
((UINT_T*)pFMProp)[i] = 0;
pFMProp->PI_num = tempPartNum; // Restore value
//invalidate the BBT and PT
SetBBTState(BBT_INVALID, fbt);
pFMProp->PT_state = PT_INVALID;
//Clear out our global area space
if(pFM_SPACE) {
free(pFM_SPACE);
pFM_SPACE = NULL;
}
#endif
}
/* LocateBlock
*
* This function using FM structures to determine if the input block
* has been relocated. It will check to see if legacy BBM is being used (and
* therefore use that BBT) or if Marvell method is being used.
*
* NOTE: this function assumes user already called SetPartition at a higher level
*
* Inputs:
* BlockNum: The block number to check. This block num is relative to
* the partition number
* fbt: Flash boot type - BOOT_FLASH or SAVE_STATE_FLASH
* Outputs: none
* Returns: The block number that the input relocates to (or the same number if the
* original block was not relocated)
*/
UINT_T LocateBlock(UINT_T BlockNum, FlashBootType_T fbt)
{
#ifdef ENABLE_BBM
UINT_T RetBlock;
P_FMProperties_T pFMProp = GetFMProperties();
//make sure this device uses BBM
if(GetFlashProperties(fbt)->FlashSettings.UseBBM == 0)
return BlockNum;
//make sure a BBT was found, and scan function was linked
if(pFMProp->ScanBBT == NULL)
return BlockNum;
//scan the BBT
RetBlock = pFMProp->ScanBBT(BlockNum);
//Return the relocated block
return RetBlock;
#else
return BlockNum;
#endif
}
/*
* Relocate Block
*
* This function will take in a block number, call the correct RelocateBlock routine,
* and return with the new block num
*
* Inputs:
* BlockNum: The bad block to be put into the table
* fbt: Flash boot type
* Outputs:
* *NewBlock: sets the new block number
* Returns: No Error on a successful relocation
* NotSupportedError if the flash device does not use BBM
*/
UINT_T RelocateBlock(UINT_T BlockNum, UINT_T* NewBlock, FlashBootType_T fbt)
{
UINT_T Retval = NotSupportedError;
#ifdef ENABLE_BBM
#if COPYIMAGESTOFLASH
//if this flash device does not support BBM, return with an error
if(GetFlashProperties(fbt)->FlashSettings.UseBBM == 0)
return NotSupportedError;
//otherwise, call the relevant flash management Relocate routine
if(GetFMProperties()->BBT_type == BBT_TYPE_LEGACY)
#ifdef BBM_LEGACY_EXT
Retval = RelocateBlock_LegacyExt(BlockNum, NewBlock, 0);
#else
Retval = RelocateBlock_Legacy(BlockNum, NewBlock);
#endif
#endif
return Retval;
#else
*NewBlock = BlockNum;
return NoError;
#endif
}
#if DUAL_TIM
static INT_T TimBlockEraseFlag = 0;
#endif
void CheckFMEraseState(UINT_T BlockNum, FlashBootType_T fbt)
{
P_FMProperties_T pFMProp = GetFMProperties();
#ifdef ENABLE_BBM
//if we have a PT, and we are erasing block 0, mark that it needs to be reburnt
if((pFMProp->PT_state != PT_INVALID) && (BlockNum == 0))
pFMProp->PT_state = PT_NEW;
#if DUAL_TIM
UINT_T EraseTrigger = 1;
pAsrDualTimInfo pDualTimInfo = GetDualTimInfo();
if(DualTimEnabled()) {
EraseTrigger = (1 << 0) | (1 << pDualTimInfo->BackupTimBlock);
}
if((pFMProp->BBT_State != BBT_INVALID) && (0 == BlockNum || pDualTimInfo->BackupTimBlock == BlockNum) ) {
TimBlockEraseFlag |= (1 << BlockNum);
}
if(TimBlockEraseFlag == EraseTrigger) {
SetBBTState(BBT_ERASED, fbt);
TimBlockEraseFlag = 0;
}
#else
//if we have a BBT, check to see if the block containing the BBT is being erase
if((pFMProp->BBT_State != BBT_INVALID) && 0 == BlockNum )
SetBBTState(BBT_ERASED, fbt);
#endif
#endif
}
/*
* SetBBTState
*
* This function is used to change tbe BBT state. This allows FM to keep
* track of whether or not to use the BBT, and if it needs to be re-written
* when the flash is shutdown.
*/
void SetBBTState(ReloState_T newState, FlashBootType_T fbt)
{
#ifdef ENABLE_BBM
UINT_T BlkSize, PageSize, TIMpagesUsed;
P_FMProperties_T pFMProps = GetFMProperties();
//make sure this device uses BBM
if(GetFlashProperties(fbt)->FlashSettings.UseBBM == 0)
return;
// Now set some values that will work
PageSize = GetPageSize(fbt);
BlkSize = GetBlockSize(fbt);
//check if the BBT got erase. If so, set the slot numbers back to their starting slot
if ( newState == BBT_ERASED )
{
if (pFMProps->BBT_Reverse) {
pFMProps->BBT_Slot = (BlkSize / PageSize) - 1;
pFMProps->BBT_NextSlot = pFMProps->BBT_Slot - 1;
} else {
TIMpagesUsed = (max_tim_size_byte + PageSize - 1) / PageSize;
pFMProps->BBT_Slot = TIMpagesUsed;
pFMProps->BBT_NextSlot = pFMProps->BBT_Slot + 1;
}
pFMProps->BBT_location = pFMProps->BBT_Slot * PageSize;
}
pFMProps->BBT_State = newState;
#endif
}
//this function creates a read disturb table in FM_SPACE... space
P_ReadDisturbList_T CreateReadDisturbList()
{
P_FMProperties_T pFMProp = GetFMProperties();
//if the list already exists, just return it
if(pFMProp->pReadDisturbList != NULL)
return pFMProp->pReadDisturbList;
obm_printf("Create Read Disturb list\n\r");
pFMProp->pReadDisturbList = malloc(sizeof(ReadDisturbList_T));
if(pFMProp->pReadDisturbList == NULL)
return HeapExhaustedError;
//put structure into data (FM) space
//pFMProp->pReadDisturbList = (P_ReadDisturbList_T) DDR_NAND_DISTURBLIST_ADDR; // pFM_SPACE;
//pFM_SPACE += sizeof(ReadDisturbList_T);
//initialize structure
pFMProp->pReadDisturbList->RDID = READ_DISTURB_LIST_ID;
pFMProp->pReadDisturbList->num_entries = 0;
return pFMProp->pReadDisturbList;
}
unsigned int AddReadDisturbEntry(unsigned int partition, unsigned int block)
{
unsigned int i;
ReadDisturbEntry_T entry;
P_ReadDisturbList_T pRDlist = GetFMProperties()->pReadDisturbList;
//make sure we have a valid list
if(pRDlist == NULL)
pRDlist = CreateReadDisturbList();
entry.partition = partition;
entry.block = block;
//parse list and return if the entry is already listed
for(i = 0; i < pRDlist->num_entries; i++)
if(entry.value == pRDlist->entries[i].value)
return NoError;
//at this point, entry wasn't found. add it to list
pRDlist->entries[pRDlist->num_entries].value = entry.value;
pRDlist->num_entries++;
return NoError;
}
void ImportReadDisturbList(unsigned int import_list)
{
unsigned int i;
P_ReadDisturbList_T import = (P_ReadDisturbList_T) import_list;
//no need to check if an internal table exists, AddReadDisturbEntry will check for us
//need to parse thru entries in the import list
for (i = 0; i < import->num_entries; i++)
AddReadDisturbEntry(import->entries[i].partition, import->entries[i].block);
}
/* SetPartition
* This function sets the FM structures to point to a particular partition.
* This includes updating the Partition Info structure in memory, and the runtime
* BBT (both from FLASH). If there is not partition table, this function just returns.
*
* Inputs:
* PartitionNum - partition number to set to
* fbt - flash boot type to use
* Outputs: none
* Returns: none
*/
#if MMC_CODE
UINT_T SetPartition(UINT_T PartitionNum, FlashBootType_T fbt)
{
UINT_T Retval = NoError;
P_FlashProperties_T pFlashP = GetFlashProperties(fbt);
P_FMProperties_T pFMProp = GetFMProperties();
if(pFMProp->PI_num == PartitionNum)
return NoError;
if(pFlashP->ChangePartition != NULL)
{
Retval = pFlashP->ChangePartition(PartitionNum, fbt);
pFMProp->PI_num = PartitionNum;
}
return Retval;
}
#endif
/* GetPartitionOffset
*
* This function returns the logical offset of where the currently set partition begins.
* If no partition table exists OR no partition was set this function returns 0.
*
*/
UINT_T GetPartitionOffset(FlashBootType_T fbt)
{
P_FMProperties_T pFMProp = GetFMProperties();
//if we don't have a Partition table, return an offset of 0
if(pFMProp->PT_state == PT_INVALID)
return 0;
//if the partition was not set, return an offset of 0
if(pFMProp->pPI == NULL)
return 0;
return pFMProp->pPI->StartAddr;
}
/* CreateBBT_Legacy
* This function will creates a legacy BBT. If the Partition Info pointer is valid, use the values in
* it to create the BBT. If the Partition Info pointer is NULL, this function will create a default BBT
* that will get written after the TIM.
*
* NOTE!!:: To support older flash management, you can change the initial slot for the BBT from right after
* the TIM, to the last slot of block zero in this routine.
* Inputs:
* pPI
* Outputs:
* None
*
*/
void CreateBBT_Legacy(P_PartitionInfo_T pPI)
{
#ifdef ENABLE_BBM
#if COPYIMAGESTOFLASH
int i;
UINT_T Retval = NoError;
UINT_T PageSize, BlkSize, mask, TIMpagesUsed;
P_FMProperties_T pFMProp = GetFMProperties();
P_ReloTable_T pLBBT = pFMProp->pLBBT;
//If this flash device does not use BBM, just return
if(GetFlashProperties(BOOT_FLASH)->FlashSettings.UseBBM == 0)
return;
//Initialize FMProperties structure:
SetBBTState(BBT_INVALID, BOOT_FLASH);
PageSize = GetPageSize(BOOT_FLASH);
BlkSize = GetBlockSize(BOOT_FLASH);
memset(pFM_SPACE, 0xFF, PageSize);
pFMProp->pLBBT = (P_ReloTable_T) pFM_SPACE;
pFMProp->pLBBT->Header = (USHORT) BBT_TYPE_LEGACY;
pFMProp->pLBBT->NumReloc = 0;
pFMProp->BBT_type = BBT_TYPE_LEGACY;
//NOTE:: SET THE STARTING SLOT HERE!!!
// For now use the last page in block zero to match XDB
// pFMProp->BBT_Slot = (PageSize >= max_tim_size_byte) ? 1 : max_tim_size_byte / PageSize;
#ifdef BBT_ORDER_REVERSE
pFMProp->BBT_Slot = (BlkSize / PageSize) - 1;
pFMProp->BBT_NextSlot = pFMProp->BBT_Slot - 1;
#else
TIMpagesUsed = (max_tim_size_byte + PageSize - 1) / PageSize;
pFMProp->BBT_Slot = TIMpagesUsed;
pFMProp->BBT_NextSlot = pFMProp->BBT_Slot + 1;
#endif
pFMProp->BBT_location = pFMProp->BBT_Slot * PageSize;
// initialize the FMProps for the new BBT
SetBBTState(BBT_CHANGED, BOOT_FLASH);
//increment our memory pointer (DWORD pointer!!)
// pFM_SPACE = (UINT*) ((UINT)pFM_SPACE + PageSize);
#endif
#endif
return;
}
void UpdateBBT()
{
#ifdef ENABLE_BBM
#ifdef BBM_LEGACY_EXT
return UpdateBBT_LegacyExt();
#else
#if COPYIMAGESTOFLASH
UINT_T addr = 0;
UINT_T Retval, BlkSize, PageSize, NumPages, TIMpagesUsed, bbt;
P_FMProperties_T pFMProps = GetFMProperties();
P_ReloTable_T pBBT;
P_FlashProperties_T pFlashProp = GetFlashProperties(BOOT_FLASH);
UINT_T maxBlockNum, i;
UINT8_T *ptemp = NULL;
//If this flash device does not use BBM, just return
if(pFlashProp->FlashSettings.UseBBM == 0)
return;
//update BBT (only if there are changes)
if(pFMProps->BBT_State == BBT_UNCHANGED)
return;
//if we never found a BBT, just return
if(pFMProps->BBT_State == BBT_INVALID)
return;
// Now set some values that will work
PageSize = GetPageSize(BOOT_FLASH);
BlkSize = GetBlockSize(BOOT_FLASH);
ptemp = malloc(BlkSize);
if (ptemp == NULL)
return HeapExhaustedError;
//are we burning to block 0, where TIM resides??
if(pFMProps->BBT_location < BlkSize)
{
//Find and read first page after the TIM
TIMpagesUsed = (max_tim_size_byte + PageSize - 1) / PageSize;
NumPages = BlkSize / PageSize;
//Did we run out of BBT Slots?
if((pFMProps->BBT_Slot < TIMpagesUsed) || pFMProps->BBT_Slot >= NumPages)
{
// ASSUMES ONLY TIM and BBT are located in block 0
//save data in lower portion of block 0
Retval = ReadFlash(0, ptemp, max_tim_size_byte, BOOT_FLASH);
if(Retval != NoError){
free(ptemp);
return;
}
//erase all of block 0
Retval = EraseFlash(0, BlkSize, BOOT_FLASH);
if(Retval != NoError){
free(ptemp);
return;
}
//restore TIM data in lower portion of block 0
Retval = WriteFlash(0, ptemp, max_tim_size_byte, BOOT_FLASH);
if(Retval != NoError){
free(ptemp);
return;
}
if (pFMProps->BBT_Reverse) {
pFMProps->BBT_Slot = NumPages - 1;
pFMProps->BBT_NextSlot = NumPages - 2;
} else {
pFMProps->BBT_Slot = TIMpagesUsed;
pFMProps->BBT_NextSlot = pFMProps->BBT_Slot + 1;
}
pFMProps->BBT_location = pFMProps->BBT_Slot*PageSize;
}
}
if(pFMProps->BBT_type == BBT_TYPE_LEGACY)
bbt = (UINT)pFMProps->pLBBT;
else
bbt = (UINT)pFMProps->pRBBT;
//get the block offset from BBT_location
addr = pFMProps->BBT_location & ~(BlkSize - 1);
//get the page offset from SLot
addr |= pFMProps->BBT_Slot * PageSize;
//write out BBT
WriteFlash(addr, bbt, PageSize, BOOT_FLASH);
//update slot info
if (pFMProps->BBT_Reverse) {
pFMProps->BBT_Slot--;
pFMProps->BBT_NextSlot--;
}
else
{
pFMProps->BBT_Slot++;
pFMProps->BBT_NextSlot++;
}
//set the state back to unchanged
SetBBTState(BBT_UNCHANGED, BOOT_FLASH);
// make sure the related block is not out of range, 0 to max block number
#if defined(NAND_CODE) || defined(SPINAND_CODE)
maxBlockNum = pFlashProp->NumBlocks - 1;
#endif
if(pFMProps->BBT_type == BBT_TYPE_LEGACY)
{
pBBT = pFMProps->pLBBT;
for (i = 0; i < pBBT->NumReloc; i++)
{
if ((pBBT->Relo[i].From > maxBlockNum) || (pBBT->Relo[i].To > maxBlockNum))
{
obm_printf("BBT out of range, %s %d !!!\n\r", __FUNCTION__, __LINE__);
FatalError(BBTOUTOFRANGE, NULL, NULL);
}
}
}
free(ptemp);
#endif
return;
#endif
#endif
}
/* ScanBBT_Legacy - uses Legacy BBT structure
* This function will take in a Block Number and return with the relocated
* block number. If the block is not relocated, then it will simply return
* with the original block number
*
* Inputs:
* BlockNum: The block number to look up in the relocation table
* Outputs:
* None
* Retval value: Relocated block number
*/
UINT_T ScanBBT_Legacy(UINT_T BlockNum)
{
P_FMProperties_T pFMProp = GetFMProperties();
P_ReloTable_T pBBT = pFMProp->pLBBT;
UINT_T i;
//if no BBT was loaded, return original block num
if(pFMProp->BBT_State == BBT_INVALID)
return BlockNum;
//block 0 is guaranteed by manufacture
if(BlockNum == 0)
return 0;
// Has this block been relocated? If so we'll need to use the relocated block
for (i = 0; i < pBBT->NumReloc; )
{
if (pBBT->Relo[i].From == BlockNum)
{
BlockNum = pBBT->Relo[i].To;
i = 0; // Start over in case remapped block is now bad
continue;
}
i++;
}
return BlockNum;
}
/*
* RelocateBlock_Legacy
*/
UINT_T RelocateBlock_Legacy(UINT_T BlockNum, UINT_T* ReloBlock)
{
UINT_T BlockRelocatedTo;
UINT_T num_relo, BlkSize, initial_blk, end_blk, i;
P_FMProperties_T pFMP = GetFMProperties();
P_ReloTable_T pBBT = pFMP->pLBBT;
P_FlashProperties_T pFlashProp = GetFlashProperties(BOOT_FLASH);
//if our BBT is not valid
if((pBBT == NULL) || ((UINT16)BBT_TYPE_LEGACY != pBBT->Header))
return BBTReadError;
//block 0 is guaranteed by manufacture: DO NOT RELOCATE IT
if(BlockNum == 0)
{
*ReloBlock = 0;
return NoError;
}
BlkSize = GetBlockSize(BOOT_FLASH);
num_relo = pBBT->NumReloc;
//get RP boundaries
/*
*
* GigaDevice GD5F1GQ4RAYIG:
*
* Some blocks are reserved for security consideration. All operations to these
* blocks are prohibited. When erase or program command is issued to these blocks,
* the feature bit E_FAIL or P_FAIL will be set to high to reports the failure
* status of the two operations. If a page read operation is issued to these blocks,
* no data is read out from the blocks.
*
* These blocks are listed as follows:
*
* For 1Gbit: block 1024~1021.
*
*/
#if defined(NAND_CODE) || defined(SPINAND_CODE)
initial_blk = pFlashProp->NumBlocks - 1;
#endif
end_blk = initial_blk - (pFlashProp->NumBlocks * LEGACY_BBM_RELOC_PERCENTAGE + 99) / 100;
//start at first RP block
BlockRelocatedTo = initial_blk;
//if the table is not empty, we need to find the next available RP block
if (num_relo != 0)
{
//run through all the entries to find that smallest 'To' entry
for(i = 0; i < pBBT->NumReloc; i++)
if(BlockRelocatedTo > pBBT->Relo[i].To)
BlockRelocatedTo = pBBT->Relo[i].To;
//since we just found the last 'used' RP block, try the next one
BlockRelocatedTo--;
//now we have to make sure this block isn't marked factory bad [From X, To -1]
i = 0;
do {
if(pBBT->Relo[i].From == BlockRelocatedTo)
{ //this block is bad.
BlockRelocatedTo--; // try next one
i = 0; // restart search
continue;
}
i++;
} while(i < pBBT->NumReloc);
//lastly, make sure we didn't exceed RP
if(BlockRelocatedTo <= end_blk)
{
obm_printf("BBT Exhausted Error, %s %d\n\r", __FUNCTION__, __LINE__);
return BBTExhaustedError;
}
}
SetBBTState(BBT_CHANGED, BOOT_FLASH);
*ReloBlock = (UINT) BlockRelocatedTo;
// Relocate it
pBBT->Relo[num_relo].To = BlockRelocatedTo;
pBBT->Relo[num_relo].From = BlockNum;
pBBT->NumReloc++;
return NoError;
}
/* FindBBT_Legacy
* This function searchs for the Legacy Bad Block Table (LBBT) within a block
* It will start by checking the first and last page for BBTs. If both are found, it
* will load the one with the most relocations. if only one is found, it will setup a
* search in that direction. If neither are found, it will return with an error.
*
* If a BBT is found, the FM structure will be loaded with the relevant BBT data.
*
* Inputs:
* Flash Boot Type
* Outputs:
* None
*
* NOTE: This function will update the Flash Management Properties structure
*/
UINT_T FindBBT_Legacy(UINT_T BlockOffset, FlashBootType_T fbt)
{
#ifdef ENABLE_BBM
UINT_T Retval1, Retval2;
UINT_T Slot, StartSlot, EndSlot, Block2;
UINT_T PageSize, BlkSize, NumPages, id;
UINT_T pBuffer[MAX_PAGE_SIZE_WORDS];
P_ReloTable_T pTempBBT = (P_ReloTable_T)pBuffer;
P_FMProperties_T pFMProp = GetFMProperties();
P_ReloTable_T pBBT = pFMProp->pLBBT;
INT_T CrcError1, CrcError2;
UINT_T BRSlot;
#if DUAL_TIM
UINT_T Slot2, BlockOffset2;
pAsrDualTimInfo pDualTimInfo;
#endif
//If this flash device does not use BBM, just return
if(GetFlashProperties(fbt)->FlashSettings.UseBBM == 0)
return NoError;
CrcError1 = CrcError2 = 0;
// Now set some values that will work
PageSize = GetPageSize(fbt);
BlkSize = GetBlockSize(fbt);
NumPages = BlkSize / PageSize;
id = (UINT_T) BBT_TYPE_LEGACY;
//we need to get a pointer of where to put the BBT
//if we already had a BBT (from another partition), reuse the same pointer
if(pFMProp->pLBBT != NULL)
pBBT = pFMProp->pLBBT;
else
{//if we never had a BBT, get the next spot in the global space
pBBT = pFMProp->pLBBT = (P_ReloTable_T)pFM_SPACE;
}
//invalidate state
SetBBTState(BBT_INVALID, fbt);
//null out, to ignore stale data
pBBT->Header = NULL;
//set the scan to Legacy BBTs. If we dont' find a BBT, it'll get cleared below
pFMProp->ScanBBT = &ScanBBT_Legacy;
pFMProp->BBT_location = BlockOffset;
Slot = PageSize >= max_tim_size_byte ? 1 : max_tim_size_byte / PageSize;
//read the first slot
Retval1 = ReadFlash((Slot * PageSize) + BlockOffset, (UINT_T)pBBT, PageSize, fbt);
//read the last slot
Retval2 = ReadFlash((BlkSize - PageSize) + BlockOffset, (UINT_T)pTempBBT, PageSize, fbt);
pFMProp->BBT_Reverse = 1;
//first slot contains BBT
if((Retval1 == NoError) && (pBBT->Header == (USHORT)BBT_TYPE_LEGACY))
if((Retval2 == NoError) && (pTempBBT->Header == (USHORT)BBT_TYPE_LEGACY))
//start slot = good BBT end slot = good BBT
{ //figure out which one is "more" valid
if(pBBT->NumReloc < pTempBBT->NumReloc)
{ //grows upward
memcpy(pBBT, pTempBBT, PageSize);
pFMProp->BBT_location += Slot * PageSize;
pFMProp->BBT_Reverse = 0;
StartSlot = Slot;
EndSlot = NumPages - 1;
Slot = NumPages - 1;
}
else
{ //grows downward
pFMProp->BBT_location += (NumPages - 1) * PageSize;
pFMProp->BBT_Reverse = 1;
EndSlot = Slot;
StartSlot = NumPages - 1;
}
}
else //start slot = good BBT end slot = no BBT
{ //set search params for "upward" search
StartSlot = Slot;
EndSlot = NumPages - 2;
pFMProp->BBT_location += Slot * PageSize;
pFMProp->BBT_Reverse = 0;
}
else //first slot does NOT contain BBT
if((Retval2 == NoError) && (pTempBBT->Header == (USHORT)BBT_TYPE_LEGACY))
//start slot = NO BBT end slot = good BBT
{ //move the found BBT into the allocated space
memcpy(pBBT, pTempBBT, PageSize);
//set search params for "downward" search
StartSlot = NumPages - 1;
EndSlot = Slot + 1;
Slot = NumPages - 1; //remember where we found the first BBT
pFMProp->BBT_location += Slot * PageSize;
pFMProp->BBT_Reverse = 1;
}
else
{ //if neither slot have a good BBT, clear out pointers and return error
//pFMProp->ScanBBT = NULL;
#if DUAL_TIM
if (!DualTimEnabled()) { //for dual tim, still have another chance to get BBT
pFMProp->pLBBT = NULL;
return BBTReadError;
}
else{
/* TODO: Dual TIM use positive BBT by default */
#ifdef BBT_ORDER_REVERSE
pFMProp->BBT_Reverse = 1;
EndSlot = Slot;
StartSlot = NumPages - 1;
#else
pFMProp->BBT_Reverse = 0;
StartSlot = Slot;
EndSlot = NumPages - 1;
#endif
Retval1 = NotFoundError;
goto search_bbt_in_backup_tim;
}
#else
pFMProp->pLBBT = NULL;
return BBTReadError;
#endif
}
// do the binary search
Retval1 = BinarySearch((StartSlot * PageSize) + BlockOffset,
(EndSlot * PageSize) + BlockOffset,
(UINT_T)pTempBBT,
&Slot,
(UINT8_T*)&id,
2,
0,
fbt,
0); /* don't check crc first time */
if(Retval1 == NoError) {
/* This BBT works for BootROM to boot OBM here */
memcpy(pBBT, pTempBBT, PageSize);
BRSlot = Slot;
// do the binary search with CRC check
Retval1 = BinarySearch((StartSlot * PageSize) + BlockOffset,
(EndSlot * PageSize) + BlockOffset,
(UINT_T)pTempBBT,
&Slot,
(UINT8_T*)&id,
2,
0,
fbt,
&CrcError1);
//if binary search found a more recent BBT, copy it to the allocated space
if(Retval1 == NoError)
memcpy(pBBT, pTempBBT, PageSize);
else
obm_printf("All BBTs in boot TIM block CRC error\n\r");
}
//set slot information
if (pFMProp->BBT_Reverse) {
pFMProp->BBT_Slot = BRSlot - 1;
pFMProp->BBT_NextSlot = BRSlot - 2;
}
else {
pFMProp->BBT_Slot = BRSlot + 1;
pFMProp->BBT_NextSlot = BRSlot + 2;
}
SetBBTState(BBT_UNCHANGED, fbt);
#if DUAL_TIM
search_bbt_in_backup_tim:
pDualTimInfo = GetDualTimInfo();
Block2 = (BlockOffset == 0) ? (pDualTimInfo->BackupTimBlock) : 0;
if (DualTimEnabled() && !IsBlockRelocated(Block2)) {
BlockOffset2 = Block2 * BlkSize;
memset(pTempBBT, 0, sizeof(ReloTable_T));
Retval2 = BinarySearch((StartSlot * PageSize) + BlockOffset2,
(EndSlot * PageSize) + BlockOffset2,
(UINT_T)pTempBBT,
&Slot2,
(UINT8_T*)&id,
2,
0,
fbt,
&CrcError2);
if (Retval2 != NoError)
pTempBBT->Header = NULL;
if (Retval1 == NoError) {
if (Retval2 == NoError)
{
if ((pTempBBT->NumReloc != pBBT->NumReloc) ||
(Slot != Slot2) || CrcError1 || CrcError2)
{
SetBBTState(BBT_NEED_SYNC, fbt);
}
if (pTempBBT->NumReloc > pBBT->NumReloc)
memcpy(pBBT, pTempBBT, PageSize);
} else {
if (!IsBlockRelocated(pDualTimInfo->BackupTimBlock))
SetBBTState(BBT_NEED_SYNC, fbt);
}
} else {
if (Retval2 == NoError) {
memcpy(pBBT, pTempBBT, PageSize);
if (!IsBlockRelocated(0))
SetBBTState(BBT_NEED_SYNC, fbt);
} else {
if (pBBT->Header != (UINT16)BBT_TYPE_LEGACY){
return BBTReadError;
}
else {
/* TODO: live with a corrupt BBT */
SetBBTState(BBT_NEED_SYNC, fbt);
}
}
}
UpdateBBT();
}
#else
if (BRSlot != Slot || Retval1 != NoError) {
SetBBTState(BBT_CHANGED, fbt);
}
#endif
pFMProp->BBT_type = BBT_TYPE_LEGACY;
obm_printf("BBT order: %s\n\r", pFMProp->BBT_Reverse ? "reverse" : "positive");
if (pBBT->Header != (UINT16)BBT_TYPE_LEGACY)
return BBTReadError;
#endif
return NoError;
}
/* Binary Search Function
* This function searchs within a flash block for the "last" page with a matching MagicNumber at
* an offset into the page as specified by the MagicNumOffset input. The page is then loaded
* into memory at the address specified by the buffer pointer input. The direction
* of the search is determined by the values of the StartAddr and EndAddr inputs.
*
* Example
*
* Inputs:
* StartAddr: Absolute address of first possible entry location
* EndAddr: Absolute address of the last possible entry location
* MagicNumber: Pointer to a value to be checked when searching the block
* MagicNumSize: Size, in bytes, of the magic word.
* MagicNumOffset: Byte offset into the page at with to check for the MagicNumber
* fbt: Flash Boot Type that we are to search
* Outputs:
* buffer: Pointer to where the page will be loaded
* SlotNum: Page number of the last found page
*
* Return Value:
* NoError if page found
* SlotNotFound if no valid page found
*
* NOTE: buffer MUST be allocated to a full flash Page, or else data corruption will occur
*/
UINT_T BinarySearch(UINT_T StartAddr, UINT_T EndAddr, UINT_T buffer, UINT_T* SlotNum,
UINT8_T *pMagicNumber, UINT_T MagicNumSize, UINT_T MagicNumOffset, FlashBootType_T fbt,
INT_T *CrcError)
{
#ifdef ENABLE_BBM
UINT_T Retval;
UINT_T PageSize, BlkSize, BlkOffset, CurrentPageOffset;
INT_T MaxSlot, MinSlot, CurrentSlot, LastFoundSlot, LastFoundSlot2; //page number inside the block
UINT8_T direction; // 0 = up, 1 = down
UINT8_T found = 0; // used on each page check: 0 = not found, 1 = found
if (CrcError)
*CrcError = 0;
// Now set some values that will work
PageSize = GetPageSize(fbt);
BlkSize = GetBlockSize(fbt);
BlkOffset = StartAddr & ~(BlkSize - 1);
//chop off the block address, since we're only searching WITHIN a block
StartAddr &= (BlkSize - 1);
EndAddr &= (BlkSize - 1);
//set this to an invalid number. At the end, it will tell us if we found a good slot
LastFoundSlot = -1;
LastFoundSlot2 = -1;
if(StartAddr > EndAddr)
{
MaxSlot = StartAddr / PageSize;
MinSlot = EndAddr / PageSize;
CurrentSlot = MaxSlot;
direction = 1; //search downward
}
else
{
MaxSlot = EndAddr / PageSize;
MinSlot = StartAddr / PageSize;
CurrentSlot = MinSlot;
direction = 0; //search upward
}
//Now we must locate the last used slot. Do this by searching for the magic number.
// We start at the boundary (to minimize search time) and do a binary search to locate the last used slot.
// Note: found variable will be cleared on each iteration
for(;MinSlot <= MaxSlot; found = 0)
{
//get the flash address to read
CurrentPageOffset = ((UINT_T)CurrentSlot * PageSize) + BlkOffset;
Retval = ReadFlash(CurrentPageOffset, buffer, PageSize, fbt);
// Did we find a valid entry?
if((Retval == NoError) && (memcmp(((void *)(buffer + MagicNumOffset)), pMagicNumber, MagicNumSize) == 0))
{
if (CrcError) {
if (CheckMagicBBT(*(uint32_t *)pMagicNumber, buffer)) {
//save the slot number
LastFoundSlot2 = CurrentSlot;
} else {
*CrcError = 1;
}
}
found = 1;
//save the slot number
LastFoundSlot = CurrentSlot;
}
//if: searching upward and DO NOT find a slot -OR- searching downward and find a slot
// lower the max slot
//else: searching upward and find a slot -OR- downward and DO NOT find a slot
// raise minimum slot
if (found == direction)
//Set new Max slot to the next page below
MaxSlot = CurrentSlot - 1;
else
//Set new Min slot to the next page above
MinSlot = CurrentSlot + 1;
//adjust current slot to midpoint of max and min to get next offset
CurrentSlot = (MaxSlot + MinSlot) >> 1;
} // End while
//Check to make sure we even found a good slot
if (CrcError) {
LastFoundSlot = LastFoundSlot2;
}
if(LastFoundSlot == -1)
Retval = NotFoundError;
else
{
*SlotNum = (UINT_T)LastFoundSlot;
//The last valid slot probably got overwritten during the search
//So we need to re-read the last valid entry in the buffer
CurrentPageOffset = ((UINT_T)LastFoundSlot * PageSize) + BlkOffset;
Retval = ReadFlash(CurrentPageOffset, buffer, PageSize, fbt);
}
return Retval;
#endif
}
UINT_T GetBadBlockNum(void)
{
#ifdef BBM_LEGACY_EXT
return GetBadBlockNumExt();
#else
P_FMProperties_T pFMProps = GetFMProperties();
P_ReloTable_T pBBT;
return pBBT->NumReloc;
#endif
}