summaryrefslogtreecommitdiff
path: root/memories/nandflash/ManagedNandFlash.c
diff options
context:
space:
mode:
Diffstat (limited to 'memories/nandflash/ManagedNandFlash.c')
-rw-r--r--memories/nandflash/ManagedNandFlash.c862
1 files changed, 862 insertions, 0 deletions
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 <utility/trace.h>
+#include <utility/assert.h>
+
+#include <string.h>
+
+//------------------------------------------------------------------------------
+// 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;
+}
personal git repositories of Harald Welte. Your mileage may vary