From 044ad7c3987460ede48ff27afd6bdb0ca05a0432 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 4 Jul 2011 20:52:54 +0200 Subject: import at91lib from at91lib_20100901_softpack_1_9_v_1_0_svn_v15011 it's sad to see that atmel doesn't publish their svn repo or has a centralized location or even puts proper version/release info into the library itself --- memories/nandflash/ManagedNandFlash.c | 862 ++++++++++++++++++++++++++++++++++ 1 file changed, 862 insertions(+) create mode 100644 memories/nandflash/ManagedNandFlash.c (limited to 'memories/nandflash/ManagedNandFlash.c') diff --git a/memories/nandflash/ManagedNandFlash.c b/memories/nandflash/ManagedNandFlash.c new file mode 100644 index 0000000..dcc39d6 --- /dev/null +++ b/memories/nandflash/ManagedNandFlash.c @@ -0,0 +1,862 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, 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. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "ManagedNandFlash.h" +#include "NandSpareScheme.h" +#include "NandFlashModel.h" +#include "RawNandFlash.h" +#include +#include + +#include + +//------------------------------------------------------------------------------ +// Internal 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 + +//------------------------------------------------------------------------------ +// Internal functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Returns 1 if a nandflash device is virgin (i.e. has never been used as a +/// managed nandflash); otherwise return 0. +/// \param managed Pointer to a ManagedNandFlash instance. +/// \param spare Pointer to allocated spare area (must be assigned) +//------------------------------------------------------------------------------ +static unsigned char IsDeviceVirgin(const struct ManagedNandFlash *managed, + unsigned char *spare) +{ + struct NandBlockStatus blockStatus; + const struct NandSpareScheme *scheme = + NandFlashModel_GetScheme(MODEL(managed)); + unsigned short baseBlock = managed->baseBlock; + unsigned char badBlockMarker; + + unsigned char error; + + ASSERT(spare, "ManagedNandFlash_IsDeviceVirgin: spare\n\r"); + + // Read spare area of page #0 + error = RawNandFlash_ReadPage(RAW(managed), baseBlock, 0, 0, spare); + ASSERT(!error, "ManagedNandFlash_IsDeviceVirgin: Failed to read page #0\n\r"); + + // 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; +} + +//------------------------------------------------------------------------------ +/// Returns BADBLOCK if the given block of a nandflash device is bad; returns +/// GOODBLOCK if the block is good; or returns a NandCommon_ERROR code. +/// \param managed Pointer to a ManagedNandFlash instance. +/// \param block Raw block to check. +/// \param spare Pointer to allocated spare area (must be assigned) +//------------------------------------------------------------------------------ +static unsigned char CheckBlock( + const struct ManagedNandFlash *managed, + unsigned short block, + unsigned char *spare) +{ + unsigned char error; + unsigned int i; + unsigned char pageSpareSize = NandFlashModel_GetPageSpareSize(MODEL(managed)); + + ASSERT(spare, "ManagedNandFlash_CheckBlock: spare\n\r"); + + // 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; +} + +//------------------------------------------------------------------------------ +/// Physically writes the status of a block inside its first page spare area. +/// Returns 0 if successful; otherwise returns a NandCommon_ERROR_xx code. +/// \param managed Pointer to a ManagedNandFlash instance. +/// \param block Raw block number. +/// \param pStatus Pointer to status data. +/// \param spare Pointer to allocated spare area (must be assigned). +//------------------------------------------------------------------------------ +static unsigned char WriteBlockStatus( + const struct ManagedNandFlash *managed, + unsigned short block, + struct NandBlockStatus *pStatus, + unsigned char *spare) +{ + ASSERT(spare, "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 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// 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; + unsigned int pageSpareSize; + 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)); + 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++) { + + 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; +} + +//------------------------------------------------------------------------------ +/// Allocates a FREE block of a managed nandflash and marks it as LIVE. +/// Returns 0 if successful; otherwise returns NandCommon_ERROR_WRONGSTATUS if +/// the block is not FREE. +/// \param managed Pointer to a ManagedNandFlash instance. +/// \param block Block to allocate, in managed area. +//------------------------------------------------------------------------------ +unsigned char ManagedNandFlash_AllocateBlock( + struct ManagedNandFlash *managed, + unsigned short block) +{ + unsigned char 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); +} + +//------------------------------------------------------------------------------ +/// Releases a LIVE block of a nandflash and marks it as DIRTY. +/// Returns 0 if successful; otherwise returns NandCommon_ERROR_WRONGSTATUS if +/// the block is not LIVE, or a RawNandFlash_WritePage error. +/// \param managed Pointer to a ManagedNandFlash instance. +/// \param block Block to release, based on managed area. +//------------------------------------------------------------------------------ +unsigned char ManagedNandFlash_ReleaseBlock( + struct ManagedNandFlash *managed, + unsigned short block) +{ + unsigned char 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); +} + +//------------------------------------------------------------------------------ +/// Erases a DIRTY block of a managed NandFlash. +/// Returns the RawNandFlash_EraseBlock code or NandCommon_ERROR_WRONGSTATUS. +/// \param managed Pointer to a ManagedNandFlash instance. +/// \param block Block to erase, in managed area. +//------------------------------------------------------------------------------ +unsigned char ManagedNandFlash_EraseBlock( + struct ManagedNandFlash *managed, + unsigned short block) +{ + unsigned int phyBlock = managed->baseBlock + block; + unsigned char spare[NandCommon_MAXPAGESPARESIZE]; + unsigned char 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); +} + +//------------------------------------------------------------------------------ +/// 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); +} + +//------------------------------------------------------------------------------ +/// 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); +} + +//------------------------------------------------------------------------------ +/// 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 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) { + + unsigned char data[NandCommon_MAXPAGEDATASIZE]; + unsigned char 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; +} + +//------------------------------------------------------------------------------ +/// Copies the data from a whole block to another block on a nandflash. Both +/// blocks must be LIVE. +/// Returns 0 if successful; otherwise returns NandCommon_ERROR_WRONGSTATUS if +/// at least one of the blocks is not free, or a NandCommon_ERROR_xxx code. +/// \param managed Pointer to a ManagedNandFlash instance. +/// \param sourceBlock Source block number. +/// \param destBlock Destination block number. +//------------------------------------------------------------------------------ +unsigned char ManagedNandFlash_CopyBlock( + const struct ManagedNandFlash *managed, + unsigned short sourceBlock, + unsigned short destBlock) +{ + unsigned short numPages = NandFlashModel_GetBlockSizeInPages(MODEL(managed)); + unsigned char error; + unsigned short 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; +} + +//------------------------------------------------------------------------------ +/// Erases all the blocks which are currently marked as DIRTY. +/// Returns 0 if successful; otherwise, returns a NandCommon_ERROR code. +/// \param managed Pointer to a ManagedNandFlash instance. +//------------------------------------------------------------------------------ +unsigned char ManagedNandFlash_EraseDirtyBlocks( + struct ManagedNandFlash *managed) +{ + unsigned int i; + unsigned char 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; +} + +//------------------------------------------------------------------------------ +/// 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). +/// Returns 0 if a block has been found; otherwise returns either +/// NandCommon_ERROR_NOBLOCKFOUND if there are no blocks having the desired +/// status. +/// \param managed Pointer to a ManagedNandFlash instance. +/// \param block Pointer to the block number variable, based on managed area. +//------------------------------------------------------------------------------ +unsigned char ManagedNandFlash_FindYoungestBlock( + const struct ManagedNandFlash *managed, + unsigned char status, + unsigned short *block) +{ + unsigned char found = 0; + unsigned short bestBlock = 0; + unsigned int 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; + } +} + +//------------------------------------------------------------------------------ +/// 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. +//------------------------------------------------------------------------------ +unsigned short ManagedNandFlash_CountBlocks( + const struct ManagedNandFlash *managed, + unsigned char status) +{ + unsigned int i; + unsigned short count = 0; + + // Examine each block + for (i=0; i < managed->sizeInBlocks; i++) { + + if (managed->blockStatuses[i].status == status) { + + count++; + } + } + + return count; +} + +//------------------------------------------------------------------------------ +/// Returns the number of available blocks in a managed nandflash. +/// \param managed Pointer to a ManagedNandFlash instance. +//------------------------------------------------------------------------------ +unsigned short ManagedNandFlash_GetDeviceSizeInBlocks( + const struct ManagedNandFlash *managed) +{ + return managed->sizeInBlocks; +} + +//------------------------------------------------------------------------------ +/// Erase all blocks in the managed area of nand flash. +/// \param managed Pointer to a ManagedNandFlash instance. +/// \param level Erase level. +//------------------------------------------------------------------------------ +unsigned char ManagedNandFlash_EraseAll(struct ManagedNandFlash *managed, + unsigned char level) +{ + unsigned int i; + unsigned char 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: %d(%d)\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: %d(%d)\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: %d(%d)\n\r", + i, managed->baseBlock + i); + } + } + } + } + + return error; +} -- cgit v1.2.3