/** * \brief Writes the data and/or spare area of a page on a SkipBlock NandFlash. * * \param skipBlock Pointer to a SkipBlockNandFlash instance. * \param block Number of the block to write. * \param page Number of the page to write inside the given block. * \param data Data area buffer. * \param spare Spare area buffer. * \return NandCommon_ERROR_BADBLOCK if the block is BAD; Otherwise, returns EccNandFlash_WritePage(). */ uint8_t SkipBlockNandFlash_WritePage( const struct SkipBlockNandFlash *skipBlock, uint16_t block, uint16_t page, void *data, void *spare) { /* Check that the block is LIVE */ if (SkipBlockNandFlash_CheckBlock(skipBlock, block) != GOODBLOCK) { TRACE_ERROR("SkipBlockNandFlash_WritePage: Block is BAD.\n\r"); return NandCommon_ERROR_BADBLOCK; } /* Write data with ECC calculation */ return EccNandFlash_WritePage(ECC(skipBlock), block, page, data, spare); }
//------------------------------------------------------------------------------ /// Writes the data and/or spare area of a LIVE page on a managed NandFlash. /// Returns NandCommon_ERROR_WRONGSTATUS if the page is not LIVE; otherwise, /// returns EccNandFlash_WritePage(). /// \param managed Pointer to a ManagedNandFlash instance. /// \param block The block to write, in managed area. /// \param page Number of the page to write inside the given block. /// \param data Data area buffer. /// \param spare Spare area buffer. //------------------------------------------------------------------------------ unsigned char ManagedNandFlash_WritePage( const struct ManagedNandFlash *managed, unsigned short block, unsigned short 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); }
//------------------------------------------------------------------------------ /// 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. /// Returns NandCommon_ERROR_WRONGSTATUS if the block is not LIVE and the data /// pointer is not null; Otherwise, returns EccNandFlash_ReadPage(). /// \param managed Pointer to a ManagedNandFlash instance. /// \param block Block to read page from, based on managed area. /// \param page Number of page to read inside the given block. /// \param data Data area buffer, can be 0. /// \param spare Spare area buffer, can be 0. //------------------------------------------------------------------------------ unsigned char ManagedNandFlash_ReadPage( const struct ManagedNandFlash *managed, unsigned short block, unsigned short 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 Reads the data and/or the spare area of a page on a SkipBlock nandflash. * * \param skipBlock Pointer to a SkipBlockNandFlash instance. * \param block Number of block to read page from. * \param page Number of page to read inside the given block. * \param data Data area buffer, can be 0. * \param spare Spare area buffer, can be 0. * \note If one of the buffer pointer is 0, then the block MUST not be BAD. * \return NandCommon_ERROR_BADBLOCK if the block is BAD; Otherwise, returns EccNandFlash_ReadPage(). */ uint8_t SkipBlockNandFlash_ReadPage( const struct SkipBlockNandFlash *skipBlock, uint16_t block, uint16_t page, void *data, void *spare) { #if !defined(OP_BOOTSTRAP_on) /* Check that the block is not BAD if data is requested */ if (SkipBlockNandFlash_CheckBlock(skipBlock, block) != GOODBLOCK) { TRACE_ERROR("SkipBlockNandFlash_ReadPage: Block is BAD.\n\r"); return NandCommon_ERROR_BADBLOCK; } /* Read data with ECC verification */ return EccNandFlash_ReadPage(ECC(skipBlock), block, page, data, spare); #else return RawNandFlash_ReadPage(RAW(skipBlock), block, page, data, spare); #endif }
/** * \brief Writes the data of a whole block on a SkipBlock nandflash. * * \param skipBlock Pointer to a SkipBlockNandFlash instance. * \param block Number of the block to write. * \param data Data area buffer. * \return NandCommon_ERROR_BADBLOCK if the block is BAD; Otherwise, returns EccNandFlash_WritePage(). */ uint8_t SkipBlockNandFlash_WriteBlock( const struct SkipBlockNandFlash *skipBlock, uint16_t block, void *data) { /* Number of pages per block */ uint32_t numPagesPerBlock; /* Page size */ uint32_t pageSize; /* Page index*/ uint16_t i; /* Error returned by SkipBlockNandFlash_WritePage*/ uint8_t error = 0; /* Retrieve model information*/ pageSize = NandFlashModel_GetPageDataSize(MODEL(skipBlock)); numPagesPerBlock = NandFlashModel_GetBlockSizeInPages(MODEL(skipBlock)); /* Check that the block is LIVE*/ if (SkipBlockNandFlash_CheckBlock(skipBlock, block) != GOODBLOCK) { TRACE_ERROR("SkipBlockNandFlash_WriteBlock: Block is BAD.\n\r"); return NandCommon_ERROR_BADBLOCK; } for (i = 0; i < numPagesPerBlock; i++) { error = EccNandFlash_WritePage(ECC(skipBlock), block, i, data, 0); if (error) { TRACE_ERROR("SkipBlockNandFlash_WriteBlock: Cannot write page %d of block %d.\n\r", i, block); return NandCommon_ERROR_CANNOTWRITE; } data = (void *) ((uint8_t *) data + pageSize); } return 0; }
/** * \brief Reads the data of a whole block on a SkipBlock nandflash. * * \param skipBlock Pointer to a SkipBlockNandFlash instance. * \param block Number of block to read page from. * \param page Number of page to read inside the given block. * \param data Data area buffer, can be 0. * \param spare Spare area buffer, can be 0. * \return NandCommon_ERROR_BADBLOCK if the block is BAD; Otherwise, returns EccNandFlash_ReadPage(). */ unsigned char SkipBlockNandFlash_ReadBlock( const struct SkipBlockNandFlash *skipBlock, unsigned short block, void *data) { /* Number of pages per block */ unsigned int numPagesPerBlock, pageSize; /* Page index */ unsigned short i; /* Error returned by SkipBlockNandFlash_WritePage */ unsigned char error = 0; /* Retrieve model information */ pageSize = NandFlashModel_GetPageDataSize(MODEL(skipBlock)); numPagesPerBlock = NandFlashModel_GetBlockSizeInPages(MODEL(skipBlock)); /* Check that the block is not BAD if data is requested */ if (SkipBlockNandFlash_CheckBlock(skipBlock, block) != GOODBLOCK) { TRACE_ERROR("SkipBlockNandFlash_ReadBlock: Block is BAD.\n\r"); return NandCommon_ERROR_BADBLOCK; } /* Read all the pages of the block */ for (i = 0; i < numPagesPerBlock; i++) { error = EccNandFlash_ReadPage(ECC(skipBlock), block, i, data, 0); if (error) { TRACE_ERROR("SkipBlockNandFlash_ReadBlock: Cannot read page %d of block %d.\n\r", i, block); return error; } data = (void *) ((unsigned char *) data + pageSize); } return 0; }
//------------------------------------------------------------------------------ /// 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. /// Returns 0 if successful; NandCommon_ERROR_WRONGSTATUS if one or more page /// is not live; otherwise returns an NandCommon_ERROR_xxx code. /// \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. //------------------------------------------------------------------------------ unsigned char ManagedNandFlash_CopyPage( const struct ManagedNandFlash *managed, unsigned short sourceBlock, unsigned short sourcePage, unsigned short destBlock, unsigned short destPage) { unsigned char *pDataBuffer = RawNandFlash_GetDataBuffer(RAW(managed)); unsigned char *pSpareBuffer = RawNandFlash_GetSpareBuffer(RAW(managed)); unsigned char 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) { // Read data & spare to copy error = EccNandFlash_ReadPage(ECC(managed), managed->baseBlock + sourceBlock, sourcePage, pDataBuffer, pSpareBuffer); if (error) goto error; // Write destination block status information in spare NandSpareScheme_WriteExtra(NandFlashModel_GetScheme(MODEL(managed)), pSpareBuffer, &(managed->blockStatuses[destBlock]), 4, 0); // Write page error = RawNandFlash_WritePage(RAW(managed), managed->baseBlock + destBlock, destPage, pDataBuffer, pSpareBuffer); if (error) goto error; } // Otherwise, a normal copy can be done else { error = RawNandFlash_CopyPage(RAW(managed), managed->baseBlock + sourceBlock, sourcePage, managed->baseBlock + destBlock, destPage); } error: RawNandFlash_ReleaseDataBuffer(RAW(managed)); RawNandFlash_ReleaseSpareBuffer(RAW(managed)); return error; }
//------------------------------------------------------------------------------ /// 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. //------------------------------------------------------------------------------ unsigned char ManagedNandFlash_Initialize( struct ManagedNandFlash *managed, const struct NandFlashModel *model, unsigned int commandAddress, unsigned int addressAddress, unsigned int dataAddress, const Pin pinChipEnable, const Pin pinReadyBusy, unsigned short baseBlock, unsigned short sizeInBlocks) { unsigned char error; unsigned char spare[NandCommon_MAXPAGESPARESIZE]; unsigned int numBlocks; const struct NandSpareScheme *scheme; unsigned int block, phyBlock; struct NandBlockStatus blockStatus; unsigned char badBlockMarker; unsigned int 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)); 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) { TRACE_ERROR("Block #%d(%d) is not managed\n\r", block, phyBlock); return NandCommon_ERROR_NOMAPPING; } // 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++) { unsigned int count = 0, live = 0, dirty = 0, 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("| %4d | %8d | %4d | %4d | %4d |\n\r", eraseCount, count, free, live, dirty); } } TRACE_ERROR_WP("|--------|------------|--------|--------|--------|\n\r"); } return 0; }
/** * \brief Initializes a SkipBlockNandFlash instance. Scans the device to retrieve or * create block status information. * * \param skipBlock Pointer to a SkipBlockNandFlash 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. */ uint8_t SkipBlockNandFlash_Initialize( struct SkipBlockNandFlash *skipBlock, const struct NandFlashModel *model, uint32_t commandAddress, uint32_t addressAddress, uint32_t dataAddress, const Pin pinChipEnable, const Pin pinReadyBusy) { uint8_t error; #if !defined(OP_BOOTSTRAP_on) uint32_t numBlocks; uint32_t block; #endif TRACE_DEBUG("SkipBlockNandFlash_Initialize()\n\r"); /* Initialize SkipBlockNandFlash */ #if !defined(OP_BOOTSTRAP_on) error = EccNandFlash_Initialize(ECC(skipBlock), model, commandAddress, addressAddress, dataAddress, pinChipEnable, pinReadyBusy); #else error = RawNandFlash_Initialize(RAW(skipBlock), model, commandAddress, addressAddress, dataAddress, pinChipEnable, pinReadyBusy); #endif #if !defined(OP_BOOTSTRAP_on) if (error) { return error; } /* Retrieve model information */ numBlocks = NandFlashModel_GetDeviceSizeInBlocks(MODEL(skipBlock)); /* Initialize block statuses */ TRACE_DEBUG("Retrieving bad block information ...\n\r"); /* Retrieve block status from their first page spare area */ for (block = 0; block < numBlocks; block++) { /* Read spare of first page */ error = SkipBlockNandFlash_CheckBlock(skipBlock, block); if (error != GOODBLOCK) { if (error == BADBLOCK) { TRACE_DEBUG("Block #%d is bad\n\r", (unsigned int)block); } else { TRACE_ERROR( "SkipBlockNandFlash_Initialize: Cannot retrieve info from block #%u\n\r",(unsigned int) block); } } } #endif return 0; }