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/MappedNandFlash.c | 661 +++++++++++++++++++++++++++++++++++ 1 file changed, 661 insertions(+) create mode 100644 memories/nandflash/MappedNandFlash.c (limited to 'memories/nandflash/MappedNandFlash.c') diff --git a/memories/nandflash/MappedNandFlash.c b/memories/nandflash/MappedNandFlash.c new file mode 100644 index 0000000..9baf83e --- /dev/null +++ b/memories/nandflash/MappedNandFlash.c @@ -0,0 +1,661 @@ +/* ---------------------------------------------------------------------------- + * 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 "MappedNandFlash.h" +#include +#include +#include + +#include + +//------------------------------------------------------------------------------ +// Internal definitions +//------------------------------------------------------------------------------ + +/// Casts +#define MANAGED(mapped) ((struct ManagedNandFlash *) mapped) +#define ECC(mapped) ((struct EccNandFlash *) mapped) +#define RAW(mapped) ((struct RawNandFlash *) mapped) +#define MODEL(mapped) ((struct NandFlashModel *) mapped) + +/// Logical block mapping pattern +#define PATTERN(i) ((i << 1) & 0x73) + +//------------------------------------------------------------------------------ +// Internal functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Scans a mapped nandflash to find an existing logical block mapping. If a +/// block contains the mapping, its index is stored in the provided variable (if +/// pointer is not 0). +/// Returns 0 if mapping has been found; otherwise returns +/// NandCommon_ERROR_NOMAPPING if no mapping exists, or another +/// NandCommon_ERROR_xxx code. +/// \param mapped Pointer to a MappedNandFlash instance. +/// \param logicalMappingBlock Pointer to a variable for storing the block +/// number. +//------------------------------------------------------------------------------ +static unsigned char FindLogicalMappingBlock( + const struct MappedNandFlash *mapped, + signed short *logicalMappingBlock) +{ + unsigned short block; + unsigned char found; + unsigned short numBlocks = ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)); + unsigned short pageDataSize = NandFlashModel_GetPageDataSize(MODEL(mapped)); + unsigned char error; + unsigned char data[NandCommon_MAXPAGEDATASIZE]; + unsigned int i; + + TRACE_INFO("FindLogicalMappingBlock()~%d\n\r", numBlocks); + + // Search each LIVE block + found = 0; + block = 0; + while (!found && (block < numBlocks)) { + + // Check that block is LIVE + if (MANAGED(mapped)->blockStatuses[block].status == NandBlockStatus_LIVE) { + + // Read block + TRACE_INFO("Checking LIVE block #%d\n\r", block); + error = ManagedNandFlash_ReadPage(MANAGED(mapped), block, 0, data, 0); + if (!error) { + + // Compare data with logical mapping pattern + i = 0; + found = 1; + while ((i < pageDataSize) && found) { + + if (data[i] != PATTERN(i)) { + + found = 0; + } + i++; + } + + // If this is the mapping, stop looking + if (found) { + + TRACE_WARNING_WP("-I- Logical mapping in block #%d\n\r", + block); + if (logicalMappingBlock) { + + *logicalMappingBlock = block; + } + return 0; + } + } + else if (error != NandCommon_ERROR_WRONGSTATUS) { + + TRACE_ERROR( + "FindLogicalMappingBlock: Failed to scan block #%d\n\r", + block); + return error; + } + } + + block++; + } + + TRACE_WARNING("No logical mapping found in device\n\r"); + return NandCommon_ERROR_NOMAPPING; +} + +//------------------------------------------------------------------------------ +/// Loads the logical mapping contained in the given physical block. +/// Returns 0 if successful; otherwise, returns a NandCommon_ERROR code. +/// \param mapped Pointer to a MappedNandFlash instance. +/// \param physicalBlock Physical block number. +//------------------------------------------------------------------------------ +static unsigned char LoadLogicalMapping( + struct MappedNandFlash *mapped, + unsigned short physicalBlock) +{ + unsigned char error; + unsigned char data[NandCommon_MAXPAGEDATASIZE]; + unsigned short pageDataSize = + NandFlashModel_GetPageDataSize(MODEL(mapped)); + unsigned short numBlocks = + ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)); + unsigned int remainingSize; + unsigned char *currentBuffer; + unsigned short currentPage; + unsigned int readSize; + unsigned int i; + unsigned char status; + signed short logicalBlock; + //signed short firstBlock, lastBlock; + + TRACE_INFO("LoadLogicalMapping(B#%d)\n\r", physicalBlock); + + // Load mapping from pages #1 - #XXX of block + currentBuffer = (unsigned char *) mapped->logicalMapping; + remainingSize = sizeof(mapped->logicalMapping); + currentPage = 1; + while (remainingSize > 0) { + + // Read page + readSize = min(remainingSize, pageDataSize); + error = ManagedNandFlash_ReadPage(MANAGED(mapped), + physicalBlock, + currentPage, + data, + 0); + if (error) { + + TRACE_ERROR( + "LoadLogicalMapping: Failed to load mapping\n\r"); + return error; + } + + // Copy page info + memcpy(currentBuffer, data, readSize); + + currentBuffer += readSize; + remainingSize -= readSize; + currentPage++; + } + + // Store mapping block index + mapped->logicalMappingBlock = physicalBlock; + + // Power-loss recovery + for (i=0; i < numBlocks; i++) { + + // Check that this is not the logical mapping block + if (i != physicalBlock) { + + status = mapped->managed.blockStatuses[i].status; + logicalBlock = MappedNandFlash_PhysicalToLogical(mapped, i); + + // Block is LIVE + if (status == NandBlockStatus_LIVE) { + + // Block is not mapped -> release it + if (logicalBlock == -1) { + + TRACE_WARNING_WP("-I- Release unmapped LIVE #%d\n\r", + i); + ManagedNandFlash_ReleaseBlock(MANAGED(mapped), i); + } + } + // Block is DIRTY + else if (status == NandBlockStatus_DIRTY) { + + // Block is mapped -> fake it as live + if (logicalBlock != -1) { + + TRACE_WARNING_WP("-I- Mark mapped DIRTY #%d -> LIVE\n\r", + i); + mapped->managed.blockStatuses[i].status = + NandBlockStatus_LIVE; + } + } + // Block is FREE or BAD + else { + + // Block is mapped -> remove it from mapping + if (logicalBlock != -1) { + + TRACE_WARNING_WP("-I- Unmap FREE or BAD #%d\n\r", i); + mapped->logicalMapping[logicalBlock] = -1; + } + } + } + } + + TRACE_WARNING_WP("-I- Mapping loaded from block #%d\n\r", physicalBlock); + + return 0; +} + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes a MappedNandFlash instance. Scans the device to look for and +/// existing logical block mapping; otherwise starts from scratch (no block +/// mapped). +/// Returns 0 if successful; otherwise returns a NandCommon_ERROR_xxx code. +/// \param mapped Pointer to a MappedNandFlash 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 Basic physical block address of mapped area. +/// \param sizeInBlocks Number of blocks that is mapped. +//------------------------------------------------------------------------------ +unsigned char MappedNandFlash_Initialize( + struct MappedNandFlash *mapped, + 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 short numBlocks; + unsigned short block; + signed short logicalMappingBlock = 0; + + TRACE_INFO("MappedNandFlash_Initialize()\n\r"); + + // Initialize ManagedNandFlash + error = ManagedNandFlash_Initialize(MANAGED(mapped), + model, + commandAddress, + addressAddress, + dataAddress, + pinChipEnable, + pinReadyBusy, + baseBlock, + sizeInBlocks); + if (error) { + + return error; + } + + // Scan to find logical mapping + mapped->mappingModified = 0; + error = FindLogicalMappingBlock(mapped, &logicalMappingBlock); + if (!error) { + + // Extract mapping from block + mapped->logicalMappingBlock = logicalMappingBlock; + return LoadLogicalMapping(mapped, logicalMappingBlock); + } + else if (error == NandCommon_ERROR_NOMAPPING) { + + // Start with no block mapped + mapped->logicalMappingBlock = -1; + numBlocks = ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)); + for (block=0; block < numBlocks; block++) { + + mapped->logicalMapping[block] = -1; + } + } + else { + + TRACE_ERROR("MappedNandFlash_Initialize: Initialize device\n\r"); + return error; + } + + return 0; +} + +//------------------------------------------------------------------------------ +/// Reads the data and/or spare area of a page in a mapped logical block. +/// Returns 0 if successful; otherwise, returns NandCommon_ERROR_BLOCKNOTMAPPED +/// if the block is not mapped, or a NandCommon_ERROR_xxx code. +/// \param mapped Pointer to a MappedNandFlash instance. +/// \param block Number of logical block to read page 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. +//------------------------------------------------------------------------------ +unsigned char MappedNandFlash_ReadPage( + const struct MappedNandFlash *mapped, + unsigned short block, + unsigned short page, + void *data, + void *spare) +{ + signed short physicalBlock; + + TRACE_INFO("MappedNandFlash_ReadPage(LB#%d:P#%d)\n\r", block, page); + + // Check if block is mapped + physicalBlock = mapped->logicalMapping[block]; + if (physicalBlock == -1) { + + TRACE_INFO( "MappedNandFlash_ReadPage: Block %d not mapped\n\r", block); + return NandCommon_ERROR_BLOCKNOTMAPPED; + } + + // Read page from corresponding physical block + return ManagedNandFlash_ReadPage(MANAGED(mapped), + physicalBlock, + page, + data, + spare); +} + +//------------------------------------------------------------------------------ +/// Writes the data and/or spare area of a page in the given mapped logical +/// block. +/// Returns 0 if successful; otherwise, returns NandCommon_ERROR_BLOCKNOTMAPPED +/// (indicating the block is not logically mapped) or another NandCommon_ERROR +/// code. +/// \param mapped Pointer to a MappedNandFlash instance. +/// \param block Number of logical block to write on. +/// \param page Number of page to write inside given block. +/// \param data Data area buffer, can be 0. +/// \param spare Spare area buffer, can be 0. +//------------------------------------------------------------------------------ +unsigned char MappedNandFlash_WritePage( + const struct MappedNandFlash *mapped, + unsigned short block, + unsigned short page, + void *data, + void *spare) +{ + signed short physicalBlock; + + TRACE_INFO("MappedNandFlash_WritePage(LB#%d:P#%d)\n\r", block, page); + + // Check if block is mapped + physicalBlock = mapped->logicalMapping[block]; + if (physicalBlock == -1) { + + TRACE_ERROR("MappedNandFlash_WritePage: Block must be mapped\n\r"); + return NandCommon_ERROR_BLOCKNOTMAPPED; + } + + // Write page on physical block + return ManagedNandFlash_WritePage(MANAGED(mapped), + physicalBlock, + page, + data, + spare); +} + +//------------------------------------------------------------------------------ +/// Maps a logical block number to an actual physical block. This allocates +/// the physical block (meaning it must be FREE), and releases the previous +/// block being replaced (if any). +/// Returns 0 if successful; otherwise returns a NandCommon_ERROR_xxx code. +/// \param mapped Pointer to a MappedNandFlash instance. +/// \param logicalBlock Logical block number to map. +/// \param physicalBlock Physical block to map to the logical one. +//------------------------------------------------------------------------------ +unsigned char MappedNandFlash_Map( + struct MappedNandFlash *mapped, + unsigned short logicalBlock, + unsigned short physicalBlock) +{ + unsigned char error; + signed short oldPhysicalBlock; + + TRACE_INFO("MappedNandFlash_Map(LB#%d -> PB#%d)\n\r", + logicalBlock, physicalBlock); + ASSERT( + logicalBlock < ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)), + "MappedNandFlash_Map: logicalBlock out-of-range\n\r"); + ASSERT( + physicalBlock < ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)), + "MappedNandFlash_Map: physicalBlock out-of-range\n\r"); + + // Allocate physical block + error = ManagedNandFlash_AllocateBlock(MANAGED(mapped), physicalBlock); + if (error) { + + return error; + } + + // Release currently mapped block (if any) + oldPhysicalBlock = mapped->logicalMapping[logicalBlock]; + if (oldPhysicalBlock != -1) { + + error = + ManagedNandFlash_ReleaseBlock(MANAGED(mapped), oldPhysicalBlock); + if (error) { + + return error; + } + } + + // Set mapping + mapped->logicalMapping[logicalBlock] = physicalBlock; + mapped->mappingModified = 1; + + return 0; +} + +//------------------------------------------------------------------------------ +/// Unmaps a logical block by releasing the corresponding physical block (if +/// any). +/// Returns 0 if successful; otherwise returns a NandCommon_ERROR code. +/// \param mapped Pointer to a MappedNandFlash instance. +/// \param logicalBlock Number of logical block to unmap. +//------------------------------------------------------------------------------ +unsigned char MappedNandFlash_Unmap( + struct MappedNandFlash *mapped, + unsigned short logicalBlock) +{ + signed short physicalBlock = mapped->logicalMapping[logicalBlock]; + unsigned char error; + + TRACE_INFO("MappedNandFlash_Unmap(LB#%d)\n\r", logicalBlock); + ASSERT( + logicalBlock < ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)), + "MappedNandFlash_Unmap: logicalBlock out-of-range\n\r"); + + if (physicalBlock != -1) { + + error = ManagedNandFlash_ReleaseBlock(MANAGED(mapped), physicalBlock); + if (error) { + + return error; + } + } + mapped->logicalMapping[logicalBlock] = -1; + mapped->mappingModified = 1; + + return 0; +} + +//------------------------------------------------------------------------------ +/// Returns the physical block mapped with the given logical block, or -1 if it +/// is not mapped. +/// \param mapped Pointer to a MappedNandFlash instance. +/// \param logicalBlock Logical block number. +//------------------------------------------------------------------------------ +signed short MappedNandFlash_LogicalToPhysical( + const struct MappedNandFlash *mapped, + unsigned short logicalBlock) +{ + ASSERT( + logicalBlock < ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)), + "MappedNandFlash_LogicalToPhysical: logicalBlock out-of-range\n\r"); + + return mapped->logicalMapping[logicalBlock]; +} + +//------------------------------------------------------------------------------ +/// Returns the logical block which is mapped to given physical block, or -1 if +/// the latter is not mapped. +/// \param mapped Pointer to a MappedNandFlash instance. +/// \param physicalBlock Physical block number. +//------------------------------------------------------------------------------ +signed short MappedNandFlash_PhysicalToLogical( + const struct MappedNandFlash *mapped, + unsigned short physicalBlock) +{ + unsigned short numBlocks = + ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)); + signed short logicalBlock; + + ASSERT( + physicalBlock < ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)), + "MappedNandFlash_PhysicalToLogical: physicalBlock out-of-range\n\r"); + + // Search the mapping for the desired physical block + for (logicalBlock=0; logicalBlock < numBlocks; logicalBlock++) { + + if (mapped->logicalMapping[logicalBlock] == physicalBlock) { + + return logicalBlock; + } + } + + return -1; +} + +//------------------------------------------------------------------------------ +/// Saves the logical mapping on a FREE, unmapped physical block. Allocates the +/// new block, releases the previous one (if any) and save the mapping. +/// Returns 0 if successful; otherwise, returns NandCommon_ERROR_WRONGSTATUS +/// if the block is not LIVE, or a NandCommon_ERROR code. +/// \param mapped Pointer to a MappedNandFlash instance. +/// \param physicalBlock Physical block number. +//------------------------------------------------------------------------------ +unsigned char MappedNandFlash_SaveLogicalMapping( + struct MappedNandFlash *mapped, + unsigned short physicalBlock) +{ + unsigned char error; + unsigned char data[NandCommon_MAXPAGEDATASIZE]; + unsigned short pageDataSize = + NandFlashModel_GetPageDataSize(MODEL(mapped)); + //unsigned short numBlocks = + // ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)); + unsigned int i; + unsigned int remainingSize; + unsigned char *currentBuffer; + unsigned short currentPage; + unsigned int writeSize; + signed short previousPhysicalBlock; + + TRACE_INFO("MappedNandFlash_SaveLogicalMapping(B#%d)\n\r", physicalBlock); + + // If mapping has not been modified, do nothing + if (!mapped->mappingModified) { + + return 0; + } + + // Allocate new block + error = ManagedNandFlash_AllocateBlock(MANAGED(mapped), physicalBlock); + if (error) { + + return error; + } + + // Save mapping + previousPhysicalBlock = mapped->logicalMappingBlock; + mapped->logicalMappingBlock = physicalBlock; + + // Save actual mapping in pages #1-#XXX + currentBuffer = (unsigned char *) mapped->logicalMapping; + remainingSize = sizeof(mapped->logicalMapping); + currentPage = 1; + while (remainingSize > 0) { + + writeSize = min(remainingSize, pageDataSize); + memset(data, 0xFF, pageDataSize); + memcpy(data, currentBuffer, writeSize); + error = ManagedNandFlash_WritePage(MANAGED(mapped), + physicalBlock, + currentPage, + data, + 0); + if (error) { + + TRACE_ERROR( + "MappedNandFlash_SaveLogicalMapping: Failed to write mapping\n\r"); + return error; + } + + currentBuffer += writeSize; + remainingSize -= writeSize; + currentPage++; + } + + // Mark page #0 of block with a distinguishible pattern, so the mapping can + // be retrieved at startup + for (i=0; i < pageDataSize; i++) { + + data[i] = PATTERN(i); + } + error = ManagedNandFlash_WritePage(MANAGED(mapped), + physicalBlock, 0, + data, 0); + if (error) { + + TRACE_ERROR( + "MappedNandFlash_SaveLogicalMapping: Failed to write pattern\n\r"); + return error; + } + + // Mapping is not modified anymore + mapped->mappingModified = 0; + + // Release previous block (if any) + if (previousPhysicalBlock != -1) { + + TRACE_DEBUG("Previous physical block was #%d\n\r", + previousPhysicalBlock); + error = ManagedNandFlash_ReleaseBlock(MANAGED(mapped), + previousPhysicalBlock); + if (error) { + + return error; + } + } + + TRACE_INFO("Mapping saved on block #%d\n\r", physicalBlock); + + return 0; +} + +//------------------------------------------------------------------------------ +/// Erase all blocks in the mapped area of nand flash. +/// \param managed Pointer to a MappedNandFlash instance. +/// \param level Erase level. +//------------------------------------------------------------------------------ +unsigned char MappedNandFlash_EraseAll( + struct MappedNandFlash *mapped, + unsigned char level) +{ + unsigned int block; + ManagedNandFlash_EraseAll(MANAGED(mapped), level); + // Reset to no block mapped + if (level > NandEraseDIRTY) { + mapped->logicalMappingBlock = -1; + mapped->mappingModified = 0; + for (block=0; + block < ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)); + block++) { + mapped->logicalMapping[block] = -1; + } + } + return 0; +} -- cgit v1.2.3