/* ---------------------------------------------------------------------------- | |
* ATMEL Microcontroller Software Support | |
* ---------------------------------------------------------------------------- | |
* Copyright (c) 2010, Atmel Corporation | |
* | |
* All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions are met: | |
* | |
* - Redistributions of source code must retain the above copyright notice, | |
* this list of conditions and the disclaimer below. | |
* | |
* Atmel's name may not be used to endorse or promote products derived from | |
* this software without specific prior written permission. | |
* | |
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR | |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE | |
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, | |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, | |
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | |
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
* ---------------------------------------------------------------------------- | |
*/ | |
/** | |
* \file | |
* | |
* The lower layer of nandflash block management, it is called by MappedNandFlash layer, and | |
* it will call EccNandFlash layer. | |
*/ | |
/*---------------------------------------------------------------------------- | |
* Headers | |
*----------------------------------------------------------------------------*/ | |
#include "memories.h" | |
#include <string.h> | |
#include <assert.h> | |
/*---------------------------------------------------------------------------- | |
* Local definitions | |
*----------------------------------------------------------------------------*/ | |
/** Casts */ | |
#define ECC(managed) ((struct EccNandFlash *) managed) | |
#define RAW(managed) ((struct RawNandFlash *) managed) | |
#define MODEL(managed) ((struct NandFlashModel *) managed) | |
/** Values returned by the CheckBlock() function */ | |
#define BADBLOCK 255 | |
#define GOODBLOCK 254 | |
/*---------------------------------------------------------------------------- | |
* Local functions | |
*----------------------------------------------------------------------------*/ | |
/** | |
* \brief Check if the device is virgin. | |
* | |
* \param managed Pointer to a ManagedNandFlash instance. | |
* \param spare Pointer to allocated spare area (must be assigned) | |
* \return 1 if a nandflash device is virgin (i.e. has never been used as a | |
* managed nandflash); otherwise return 0. | |
*/ | |
static uint8_t IsDeviceVirgin( const struct ManagedNandFlash *managed, uint8_t* spare ) | |
{ | |
struct NandBlockStatus blockStatus; | |
const struct NandSpareScheme *scheme = | |
NandFlashModel_GetScheme(MODEL(managed)); | |
uint16_t baseBlock = managed->baseBlock; | |
uint8_t badBlockMarker; | |
uint8_t error; | |
/* Read spare area of page #0. */ | |
error = RawNandFlash_ReadPage(RAW(managed), baseBlock, 0, 0, spare); | |
if( error ) | |
{ | |
TRACE_ERROR("ManagedNandFlash_IsDeviceVirgin: Failed to read page #0\n\r");\ | |
return 0; | |
} | |
/* Retrieve bad block marker and block status from spare area*/ | |
NandSpareScheme_ReadBadBlockMarker(scheme, spare, &badBlockMarker); | |
NandSpareScheme_ReadExtra(scheme, spare, &blockStatus, 4, 0); | |
/* Check if block is marked as bad*/ | |
if ( badBlockMarker != 0xFF ) | |
{ | |
/* Device is not virgin, since page #0 is guaranteed to be good*/ | |
return 0 ; | |
} | |
/* If device is not virgin, then block status will be set to either | |
FREE, DIRTY or LIVE */ | |
else | |
{ | |
if ( blockStatus.status != NandBlockStatus_DEFAULT ) | |
{ | |
/* Device is not virgin */ | |
return 0 ; | |
} | |
} | |
return 1 ; | |
} | |
/** | |
* \brief Check if the given block is bad. | |
* | |
* \param managed Pointer to a ManagedNandFlash instance. | |
* \param block Raw block to check. | |
* \param spare Pointer to allocated spare area (must be assigned) | |
* \return 1 if a nandflash device is virgin (i.e. has never been used as a | |
* managed nandflash); otherwise return 0. | |
*/ | |
static uint8_t CheckBlock( const struct ManagedNandFlash *managed, uint16_t block, uint8_t* spare ) | |
{ | |
uint8_t error ; | |
uint32_t i ; | |
uint8_t pageSpareSize = NandFlashModel_GetPageSpareSize( MODEL( managed ) ) ; | |
/* Read spare area of first page of block */ | |
error = RawNandFlash_ReadPage( RAW( managed ), block, 0, 0, spare ) ; | |
if ( error ) | |
{ | |
TRACE_ERROR( "CheckBlock: Cannot read page #0 of block #%d\n\r", block ) ; | |
return error ; | |
} | |
/* Make sure it is all 0xFF */ | |
for ( i=0 ; i < pageSpareSize ; i++ ) | |
{ | |
if ( spare[i] != 0xFF ) | |
{ | |
return BADBLOCK ; | |
} | |
} | |
/* Read spare area of second page of block */ | |
error = RawNandFlash_ReadPage( RAW( managed ), block, 1, 0, spare ) ; | |
if ( error ) | |
{ | |
TRACE_ERROR( "CheckBlock: Cannot read page #1 of block #%d\n\r", block ) ; | |
return error ; | |
} | |
/* Make sure it is all 0xFF */ | |
for ( i=0 ; i < pageSpareSize ; i++ ) | |
{ | |
if ( spare[i] != 0xFF ) | |
{ | |
return BADBLOCK ; | |
} | |
} | |
return GOODBLOCK ; | |
} | |
/** | |
* \brief Physically writes the status of a block inside its first page spare area. | |
* | |
* \param managed Pointer to a ManagedNandFlash instance. | |
* \param block Raw block to check. | |
* \param pStatus Pointer to status data. | |
* \param spare Pointer to allocated spare area (must be assigned) | |
* \return 0 if successful; otherwise returns a NandCommon_ERROR_xx code. | |
*/ | |
static uint8_t WriteBlockStatus( const struct ManagedNandFlash *managed, uint16_t block, struct NandBlockStatus *pStatus, uint8_t *spare ) | |
{ | |
assert( spare != NULL ) ; /* "ManagedNandFlash_WriteBlockStatus: spare\n\r" */ | |
memset( spare, 0xFF, NandCommon_MAXPAGESPARESIZE ) ; | |
NandSpareScheme_WriteExtra( NandFlashModel_GetScheme( MODEL( managed ) ), spare, pStatus, 4, 0 ) ; | |
return RawNandFlash_WritePage( RAW( managed ), block, 0, 0, spare ) ; | |
} | |
/*---------------------------------------------------------------------------- | |
* Exported functions | |
*----------------------------------------------------------------------------*/ | |
/** | |
* \brief Initializes a ManagedNandFlash instance. Scans the device to retrieve or | |
* create block status information. | |
* | |
* \param managed Pointer to a ManagedNandFlash instance. | |
* \param model Pointer to the underlying nand chip model. Can be 0. | |
* \param commandAddress Address at which commands are sent. | |
* \param addressAddress Address at which addresses are sent. | |
* \param dataAddress Address at which data is sent. | |
* \param pinChipEnable Pin controlling the CE signal of the NandFlash. | |
* \param pinReadyBusy Pin used to monitor the ready/busy signal of the Nand. | |
* \param baseBlock Base physical block address of managed area, managed 0. | |
* \param sizeInBlocks Number of blocks that is managed. | |
* \return 0 if the initialization is done; or returns a error code. | |
*/ | |
uint8_t ManagedNandFlash_Initialize( | |
struct ManagedNandFlash *managed, | |
const struct NandFlashModel *model, | |
uint32_t commandAddress, | |
uint32_t addressAddress, | |
uint32_t dataAddress, | |
const Pin pinChipEnable, | |
const Pin pinReadyBusy, | |
uint16_t baseBlock, | |
uint16_t sizeInBlocks) | |
{ | |
uint8_t error; | |
uint8_t spare[NandCommon_MAXPAGESPARESIZE]; | |
uint32_t numBlocks; | |
//uint32_t pageSpareSize; | |
const struct NandSpareScheme *scheme; | |
int32_t block, phyBlock; | |
struct NandBlockStatus blockStatus; | |
uint8_t badBlockMarker; | |
uint32_t eraseCount, minEraseCount, maxEraseCount; | |
TRACE_DEBUG("ManagedNandFlash_Initialize()\n\r"); | |
/* Initialize EccNandFlash */ | |
error = EccNandFlash_Initialize(ECC(managed), | |
model, | |
commandAddress, | |
addressAddress, | |
dataAddress, | |
pinChipEnable, | |
pinReadyBusy); | |
if (error) { | |
return error; | |
} | |
/* Retrieve model information */ | |
numBlocks = NandFlashModel_GetDeviceSizeInBlocks(MODEL(managed)); | |
//pageSpareSize = NandFlashModel_GetPageSpareSize(MODEL(managed)); | |
scheme = NandFlashModel_GetScheme(MODEL(managed)); | |
/* Initialize base & size */ | |
if (sizeInBlocks == 0) sizeInBlocks = numBlocks; | |
if (baseBlock > numBlocks) { | |
baseBlock = 0; | |
} | |
else if (baseBlock + sizeInBlocks > numBlocks) { | |
sizeInBlocks = numBlocks - baseBlock; | |
} | |
TRACE_INFO("Managed NF area: %d + %d\n\r", baseBlock, sizeInBlocks); | |
if (sizeInBlocks > NandCommon_MAXNUMBLOCKS) { | |
TRACE_ERROR("Out of Maxmized Managed Size: %d > %d\n\r", | |
sizeInBlocks, NandCommon_MAXNUMBLOCKS); | |
TRACE_INFO("Change NandCommon_MAXNUMBLOCKS or sizeInBlocks\n\r"); | |
return NandCommon_ERROR_OUTOFBOUNDS; | |
} | |
managed->baseBlock = baseBlock; | |
managed->sizeInBlocks = sizeInBlocks; | |
/* Initialize block statuses */ | |
/* First, check if device is virgin*/ | |
if (IsDeviceVirgin(managed, spare)) { | |
TRACE_WARNING("Device is virgin, doing initial block scanning ...\n\r"); | |
/* Perform initial scan of the device area */ | |
for (block=0; block < sizeInBlocks; block++) { | |
phyBlock = baseBlock + block; | |
/* Check if physical block is bad */ | |
error = CheckBlock(managed, phyBlock, spare); | |
if (error == BADBLOCK) { | |
/* Mark block as bad */ | |
TRACE_DEBUG("Block #%d is bad\n\r", block); | |
managed->blockStatuses[block].status = NandBlockStatus_BAD; | |
} | |
else if (error == GOODBLOCK) { | |
/* Mark block as free with erase count 0 */ | |
TRACE_DEBUG("Block #%d is free\n\r", block); | |
managed->blockStatuses[block].status = NandBlockStatus_FREE; | |
managed->blockStatuses[block].eraseCount = 0; | |
/* Write status in spare of block first page */ | |
error = WriteBlockStatus(managed, | |
phyBlock, | |
&(managed->blockStatuses[block]), | |
spare); | |
if (error) { | |
TRACE_ERROR("ManagedNandFlash_Initialize: WR spare\n\r"); | |
return error; | |
} | |
} | |
else { | |
TRACE_ERROR("ManagedNandFlash_Initialize: Scan device\n\r"); | |
return error; | |
} | |
} | |
} | |
else { | |
TRACE_INFO("Managed, retrieving information ...\n\r"); | |
/* Retrieve block statuses from their first page spare area | |
(find maximum and minimum wear at the same time) */ | |
minEraseCount = 0xFFFFFFFF; | |
maxEraseCount = 0; | |
for ( block=0 ; block < sizeInBlocks; block++ ) | |
{ | |
phyBlock = baseBlock + block; | |
/* Read spare of first page */ | |
error = RawNandFlash_ReadPage(RAW(managed), phyBlock, 0, 0, spare); | |
if ( error ) | |
{ | |
TRACE_ERROR("ManagedNandFlash_Initialize: Read block #%d(%d)\n\r", | |
block, phyBlock); | |
} | |
/* Retrieve bad block marker and block status */ | |
NandSpareScheme_ReadBadBlockMarker(scheme, spare, &badBlockMarker); | |
NandSpareScheme_ReadExtra(scheme, spare, &blockStatus, 4, 0); | |
/* If they do not match, block must be bad */ | |
if ( (badBlockMarker != 0xFF) && (blockStatus.status != NandBlockStatus_BAD) ) | |
{ | |
TRACE_DEBUG("Block #%d(%d) is bad\n\r", block, phyBlock); | |
managed->blockStatuses[block].status = NandBlockStatus_BAD; | |
} | |
/* Check that block status is not default (meaning block is not managed) */ | |
else | |
{ | |
if ( blockStatus.status == NandBlockStatus_DEFAULT ) | |
{ | |
assert( 0 ) ; /* "Block #%d(%d) is not managed\n\r", block, phyBlock */ | |
} | |
/* Otherwise block status is accurate */ | |
else | |
{ | |
TRACE_DEBUG("Block #%03d(%d) : status = %2d | eraseCount = %d\n\r", | |
block, phyBlock, | |
blockStatus.status, blockStatus.eraseCount); | |
managed->blockStatuses[block] = blockStatus; | |
/* Check for min/max erase counts */ | |
if ( blockStatus.eraseCount < minEraseCount ) | |
{ | |
minEraseCount = blockStatus.eraseCount; | |
} | |
if ( blockStatus.eraseCount > maxEraseCount ) | |
{ | |
maxEraseCount = blockStatus.eraseCount; | |
} | |
/* Clean block*/ | |
/*Release LIVE blocks */ | |
/* | |
if (managed->blockStatuses[block].status == NandBlockStatus_LIVE) { | |
ManagedNandFlash_ReleaseBlock(managed, block); | |
} | |
Erase DIRTY blocks | |
if (managed->blockStatuses[block].status == NandBlockStatus_DIRTY) { | |
ManagedNandFlash_EraseBlock(managed, block); | |
}*/ | |
} | |
} | |
} | |
/* Display erase count information*/ | |
TRACE_ERROR_WP("|--------|------------|--------|--------|--------|\n\r"); | |
TRACE_ERROR_WP("| Wear | Count | Free | Live | Dirty |\n\r"); | |
TRACE_ERROR_WP("|--------|------------|--------|--------|--------|\n\r"); | |
for ( eraseCount=minEraseCount ; eraseCount <= maxEraseCount ; eraseCount++ ) | |
{ | |
uint32_t count = 0 ; | |
uint32_t live = 0 ; | |
uint32_t dirty = 0 ; | |
uint32_t free = 0 ; | |
for ( block=0 ; block < sizeInBlocks ; block++ ) | |
{ | |
if ( (managed->blockStatuses[block].eraseCount == eraseCount) && (managed->blockStatuses[block].status != NandBlockStatus_BAD) ) | |
{ | |
count++ ; | |
switch ( managed->blockStatuses[block].status ) | |
{ | |
case NandBlockStatus_LIVE: | |
live++ ; | |
break ; | |
case NandBlockStatus_DIRTY: | |
dirty++ ; | |
break ; | |
case NandBlockStatus_FREE: | |
free++ ; | |
break ; | |
} | |
} | |
} | |
if ( count > 0 ) | |
{ | |
TRACE_ERROR_WP( "| %4u | %8u | %4u | %4u | %4u |\n\r", | |
eraseCount, count, free, live, dirty); | |
} | |
} | |
TRACE_ERROR_WP("|--------|------------|--------|--------|--------|\n\r"); | |
} | |
return 0; | |
} | |
/** | |
* \brief Allocates a FREE block of a managed nandflash and marks it as LIVE. | |
* create block status information. | |
* | |
* \param managed Pointer to a ManagedNandFlash instance. | |
* \param block Block to allocate, in managed area. | |
* \return 0 if successful; otherwise returns NandCommon_ERROR_WRONGSTATUS if | |
* the block is not FREE. | |
*/ | |
uint8_t ManagedNandFlash_AllocateBlock( | |
struct ManagedNandFlash *managed, | |
uint16_t block) | |
{ | |
uint8_t spare[NandCommon_MAXPAGESPARESIZE]; | |
TRACE_INFO("ManagedNandFlash_AllocateBlock(%d)\n\r", block); | |
/* Check that block is FREE*/ | |
if (managed->blockStatuses[block].status != NandBlockStatus_FREE) { | |
TRACE_ERROR("ManagedNandFlash_AllocateBlock: Block must be FREE\n\r"); | |
return NandCommon_ERROR_WRONGSTATUS; | |
} | |
/* Change block status to LIVE*/ | |
managed->blockStatuses[block].status = NandBlockStatus_LIVE; | |
return WriteBlockStatus(managed, | |
managed->baseBlock + block, | |
&(managed->blockStatuses[block]), | |
spare); | |
} | |
/** | |
* \brief Releases a LIVE block of a nandflash and marks it as DIRTY. | |
* create block status information. | |
* | |
* \param managed Pointer to a ManagedNandFlash instance. | |
* \param block Block to release, based on managed area. | |
* \return 0 if successful; otherwise returns NandCommon_ERROR_WRONGSTATUS if | |
* the block is not LIVE, or a RawNandFlash_WritePage error. | |
*/ | |
uint8_t ManagedNandFlash_ReleaseBlock( | |
struct ManagedNandFlash *managed, | |
uint16_t block) | |
{ | |
uint8_t spare[NandCommon_MAXPAGESPARESIZE]; | |
TRACE_INFO("ManagedNandFlash_ReleaseBlock(%d)\n\r", block); | |
/* Check that block is LIVE*/ | |
if (managed->blockStatuses[block].status != NandBlockStatus_LIVE) { | |
TRACE_ERROR("ManagedNandFlash_ReleaseBlock: Block must be LIVE\n\r"); | |
return NandCommon_ERROR_WRONGSTATUS; | |
} | |
/* Change block status to DIRTY*/ | |
managed->blockStatuses[block].status = NandBlockStatus_DIRTY; | |
return WriteBlockStatus(managed, | |
managed->baseBlock + block, | |
&(managed->blockStatuses[block]), | |
spare); | |
} | |
/** | |
* \brief Erases a DIRTY block of a managed NandFlash. | |
* create block status information. | |
* | |
* \param managed Pointer to a ManagedNandFlash instance. | |
* \param block Block to erase, in managed area. | |
* \return the RawNandFlash_EraseBlock code or NandCommon_ERROR_WRONGSTATUS. | |
*/ | |
uint8_t ManagedNandFlash_EraseBlock( | |
struct ManagedNandFlash *managed, | |
uint16_t block) | |
{ | |
uint32_t phyBlock = managed->baseBlock + block; | |
uint8_t spare[NandCommon_MAXPAGESPARESIZE]; | |
uint8_t error; | |
TRACE_INFO("ManagedNandFlash_EraseBlock(%d)\n\r", block); | |
/* Check block status*/ | |
if (managed->blockStatuses[block].status != NandBlockStatus_DIRTY) { | |
TRACE_ERROR("ManagedNandFlash_EraseBlock: Block must be DIRTY\n\r"); | |
return NandCommon_ERROR_WRONGSTATUS; | |
} | |
/* Erase block*/ | |
error = RawNandFlash_EraseBlock(RAW(managed), phyBlock); | |
if (error) { | |
return error; | |
} | |
/* Update block status*/ | |
managed->blockStatuses[block].status = NandBlockStatus_FREE; | |
managed->blockStatuses[block].eraseCount++; | |
return WriteBlockStatus(managed, | |
phyBlock, | |
&(managed->blockStatuses[block]), | |
spare); | |
} | |
/** | |
* \brief Reads the data and/or the spare area of a page on a managed nandflash. If | |
* the data pointer is not 0, then the block MUST be LIVE. | |
* \param managed Pointer to a ManagedNandFlash instance. | |
* \param block Number of block to read from. | |
* \param page Number of page to read inside given block. | |
* \param data Data area buffer, can be 0. | |
* \param spare Spare area buffer, can be 0. | |
* \return NandCommon_ERROR_WRONGSTATUS if the block is not LIVE and the data | |
* pointer is not null; Otherwise, returns EccNandFlash_ReadPage(). | |
*/ | |
uint8_t ManagedNandFlash_ReadPage( | |
const struct ManagedNandFlash *managed, | |
uint16_t block, | |
uint16_t page, | |
void *data, | |
void *spare) | |
{ | |
/* Check that the block is LIVE if data is requested*/ | |
if ((managed->blockStatuses[block].status != NandBlockStatus_LIVE) | |
&& (managed->blockStatuses[block].status != NandBlockStatus_DIRTY)) { | |
TRACE_ERROR("ManagedNandFlash_ReadPage: Block must be LIVE or DIRTY.\n\r"); | |
return NandCommon_ERROR_WRONGSTATUS; | |
} | |
/* Read data with ECC verification*/ | |
return EccNandFlash_ReadPage(ECC(managed), | |
managed->baseBlock + block, | |
page, data, spare); | |
} | |
/** | |
* \brief Writes the data and/or spare area of a LIVE page on a managed NandFlash. | |
* ECC for the data area and storing it in the spare. If no data buffer is | |
* provided, the ECC is read from the existing page spare. If no spare buffer | |
* is provided, the spare area is still written with the ECC information | |
* calculated on the data buffer. | |
* \param managed Pointer to a ManagedNandFlash instance. | |
* \param block Number of block to read from. | |
* \param page Number of page to read inside given block. | |
* \param data Data area buffer. | |
* \param spare Spare area buffer. | |
* \return NandCommon_ERROR_WRONGSTATUS if the page is not LIVE; otherwise, | |
* returns EccNandFlash_WritePage(). | |
*/ | |
uint8_t ManagedNandFlash_WritePage( | |
const struct ManagedNandFlash *managed, | |
uint16_t block, | |
uint16_t page, | |
void *data, | |
void *spare) | |
{ | |
/* Check that the block is LIVE*/ | |
if (managed->blockStatuses[block].status != NandBlockStatus_LIVE) { | |
TRACE_ERROR("ManagedNandFlash_WritePage: Block must be LIVE.\n\r"); | |
return NandCommon_ERROR_WRONGSTATUS; | |
} | |
/* Write data with ECC calculation*/ | |
return EccNandFlash_WritePage(ECC(managed), | |
managed->baseBlock + block, | |
page, data, spare); | |
} | |
/** | |
* \brief Copy the data & spare area of one page to another page. The source block | |
* can be either LIVE or DIRTY, and the destination block must be LIVE; they | |
* must both have the same parity. | |
* \param managed Pointer to a ManagedNandFlash instance. | |
* \param sourceBlock Source block number based on managed area. | |
* \param sourcePage Number of source page inside the source block. | |
* \param destBlock Destination block number based on managed area. | |
* \param destPage Number of destination page inside the dest block. | |
* \return 0 if successful; NandCommon_ERROR_WRONGSTATUS if one or more page | |
* is not live; otherwise returns an NandCommon_ERROR_xxx code. | |
*/ | |
uint8_t ManagedNandFlash_CopyPage( | |
const struct ManagedNandFlash *managed, | |
uint16_t sourceBlock, | |
uint16_t sourcePage, | |
uint16_t destBlock, | |
uint16_t destPage) | |
{ | |
uint8_t error; | |
assert( (sourcePage & 1) == (destPage & 1) ) ; /* "ManagedNandFlash_CopyPage: source & dest pages must have the same parity\n\r" */ | |
TRACE_INFO("ManagedNandFlash_CopyPage(B#%d:P#%d -> B#%d:P#%d)\n\r", | |
sourceBlock, sourcePage, destBlock, destPage); | |
/* Check block statuses */ | |
if ((managed->blockStatuses[sourceBlock].status != NandBlockStatus_LIVE) | |
&& (managed->blockStatuses[sourceBlock].status != NandBlockStatus_DIRTY)) { | |
TRACE_ERROR("ManagedNandFlash_CopyPage: Source block must be LIVE or DIRTY.\n\r"); | |
return NandCommon_ERROR_WRONGSTATUS; | |
} | |
if (managed->blockStatuses[destBlock].status != NandBlockStatus_LIVE) { | |
TRACE_ERROR("ManagedNandFlash_CopyPage: Destination block must be LIVE.\n\r"); | |
return NandCommon_ERROR_WRONGSTATUS; | |
} | |
/* If destination page is page #0, block status information must not be | |
overwritten*/ | |
if (destPage == 0) { | |
uint8_t data[NandCommon_MAXPAGEDATASIZE]; | |
uint8_t spare[NandCommon_MAXPAGESPARESIZE]; | |
/* Read data & spare to copy*/ | |
error = EccNandFlash_ReadPage(ECC(managed), | |
managed->baseBlock + sourceBlock, | |
sourcePage, | |
data, spare); | |
if (error) { | |
return error; | |
} | |
/* Write destination block status information in spare*/ | |
NandSpareScheme_WriteExtra(NandFlashModel_GetScheme(MODEL(managed)), | |
spare, | |
&(managed->blockStatuses[destBlock]), | |
4, | |
0); | |
/* Write page*/ | |
error = RawNandFlash_WritePage(RAW(managed), | |
managed->baseBlock + destBlock, | |
destPage, | |
data, spare); | |
if (error) { | |
return error; | |
} | |
} | |
/* Otherwise, a normal copy can be done*/ | |
else { | |
return RawNandFlash_CopyPage(RAW(managed), | |
managed->baseBlock + sourceBlock, | |
sourcePage, | |
managed->baseBlock + destBlock, | |
destPage); | |
} | |
return 0; | |
} | |
/** | |
* \brief Copies the data from a whole block to another block on a nandflash. Both | |
* blocks must be LIVE. | |
* \param managed Pointer to a ManagedNandFlash instance. | |
* \param sourceBlock Source block number. | |
* \param destBlock Destination block number. | |
* \return 0 if successful; NandCommon_ERROR_WRONGSTATUS if one or more page | |
* is not live; otherwise returns an NandCommon_ERROR_xxx code. | |
*/ | |
uint8_t ManagedNandFlash_CopyBlock( | |
const struct ManagedNandFlash *managed, | |
uint16_t sourceBlock, | |
uint16_t destBlock) | |
{ | |
uint16_t numPages = NandFlashModel_GetBlockSizeInPages(MODEL(managed)); | |
uint8_t error; | |
uint16_t page; | |
assert( sourceBlock != destBlock ) ; /* "ManagedNandFlash_CopyBlock: Source block must be different from dest. block\n\r" */ | |
TRACE_INFO( "ManagedNandFlash_CopyBlock(B#%d->B#%d)\n\r", sourceBlock, destBlock ) ; | |
/* Copy all pages*/ | |
for ( page=0 ; page < numPages ; page++ ) | |
{ | |
error = ManagedNandFlash_CopyPage( managed, sourceBlock, page, destBlock, page ) ; | |
if ( error ) | |
{ | |
TRACE_ERROR( "ManagedNandFlash_CopyPage: Failed to copy page %d\n\r", page ) ; | |
return error ; | |
} | |
} | |
return 0; | |
} | |
/** | |
* \brief Erases all the blocks which are currently marked as DIRTY. | |
* \param managed Pointer to a ManagedNandFlash instance. | |
* \return 0 if successful; otherwise, returns a NandCommon_ERROR code. | |
* is not live; otherwise returns an NandCommon_ERROR_xxx code. | |
*/ | |
uint8_t ManagedNandFlash_EraseDirtyBlocks( struct ManagedNandFlash *managed ) | |
{ | |
uint32_t i ; | |
uint8_t error ; | |
/* Erase all dirty blocks*/ | |
for ( i=0 ; i < managed->sizeInBlocks ; i++ ) | |
{ | |
if ( managed->blockStatuses[i].status == NandBlockStatus_DIRTY ) | |
{ | |
error = ManagedNandFlash_EraseBlock( managed, i ) ; | |
if ( error ) | |
{ | |
return error ; | |
} | |
} | |
} | |
return 0 ; | |
} | |
/** | |
* \brief Looks for the youngest block having the desired status among the blocks | |
* of a managed nandflash. If a block is found, its index is stored inside | |
* the provided variable (if pointer is not 0). | |
* | |
* \param managed Pointer to a ManagedNandFlash instance. | |
* \param block Pointer to the block number variable, based on managed area. | |
* \return 0 if a block has been found; otherwise returns either status. | |
*/ | |
uint8_t ManagedNandFlash_FindYoungestBlock( const struct ManagedNandFlash *managed, uint8_t status, uint16_t *block ) | |
{ | |
uint8_t found = 0; | |
uint16_t bestBlock = 0; | |
uint32_t i; | |
/* Go through the block array*/ | |
for ( i=0 ; i < managed->sizeInBlocks ; i++ ) | |
{ | |
/* Check status*/ | |
if ( managed->blockStatuses[i].status == status ) | |
{ | |
/* If no block was found, i becomes the best block*/ | |
if ( !found ) | |
{ | |
found = 1; | |
bestBlock = i; | |
} | |
/* Compare the erase counts otherwise*/ | |
else | |
{ | |
if ( managed->blockStatuses[i].eraseCount < managed->blockStatuses[bestBlock].eraseCount ) | |
{ | |
bestBlock = i; | |
} | |
} | |
} | |
} | |
if ( found ) | |
{ | |
if ( block ) | |
{ | |
*block = bestBlock ; | |
} | |
return 0 ; | |
} | |
else | |
{ | |
return NandCommon_ERROR_NOBLOCKFOUND ; | |
} | |
} | |
/** | |
* \brief Counts and returns the number of blocks having the given status in a | |
* managed nandflash. | |
* | |
* \param managed Pointer to a ManagedNandFlash instance. | |
* \param status Desired block status. | |
* \return the number of blocks. | |
*/ | |
uint16_t ManagedNandFlash_CountBlocks( | |
const struct ManagedNandFlash *managed, | |
uint8_t status) | |
{ | |
uint32_t i; | |
uint16_t count = 0; | |
/* Examine each block*/ | |
for (i=0; i < managed->sizeInBlocks; i++) { | |
if (managed->blockStatuses[i].status == status) { | |
count++; | |
} | |
} | |
return count; | |
} | |
/** | |
* \brief Returns the number of available blocks in a managed nandflash. | |
* | |
* \param managed Pointer to a ManagedNandFlash instance. | |
* \return the number of blocks. | |
*/ | |
uint16_t ManagedNandFlash_GetDeviceSizeInBlocks( | |
const struct ManagedNandFlash *managed) | |
{ | |
return managed->sizeInBlocks; | |
} | |
/** | |
* \brief Erase all blocks in the managed area of nand flash. | |
* | |
* \param managed Pointer to a ManagedNandFlash instance. | |
* \param level Erase level. | |
* \return the RawNandFlash_EraseBlock code or NandCommon_ERROR_WRONGSTATUS. | |
*/ | |
uint8_t ManagedNandFlash_EraseAll(struct ManagedNandFlash *managed, | |
uint8_t level) | |
{ | |
uint32_t i; | |
uint8_t error = 0; | |
if (level == NandEraseFULL) { | |
for (i=0; i < managed->sizeInBlocks; i++) { | |
error = RawNandFlash_EraseBlock(RAW(managed), | |
managed->baseBlock + i); | |
/* Reset block status*/ | |
managed->blockStatuses[i].eraseCount = 0; | |
if (error) { | |
TRACE_WARNING("Managed_FullErase: %u(%u)\n\r", | |
i, managed->baseBlock + i); | |
managed->blockStatuses[i].status = NandBlockStatus_BAD; | |
continue; | |
} | |
managed->blockStatuses[i].status = NandBlockStatus_FREE; | |
} | |
} | |
else if (level == NandEraseDATA) { | |
for (i=0; i < managed->sizeInBlocks; i++) { | |
error = ManagedNandFlash_EraseBlock(managed, i); | |
if (error) { | |
TRACE_WARNING("Managed_DataErase: %u(%u)\n\r", | |
i, managed->baseBlock + i); | |
} | |
} | |
} | |
else { | |
for (i=0; i < managed->sizeInBlocks; i++) { | |
if (managed->blockStatuses[i].status == NandBlockStatus_DIRTY) { | |
error = ManagedNandFlash_EraseBlock(managed, i); | |
if (error) { | |
TRACE_WARNING("Managed_DirtyErase: %u(%u)\n\r", | |
i, managed->baseBlock + i); | |
} | |
} | |
} | |
} | |
return error; | |
} |