/* ---------------------------------------------------------------------------- * 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 "SkipBlockNandFlash.h" #include "NandSpareScheme.h" #include "NandFlashModel.h" #include "RawNandFlash.h" #include #include #include //------------------------------------------------------------------------------ // Internal definitions //------------------------------------------------------------------------------ // Casts #define ECC(skipBlock) ((struct EccNandFlash *) skipBlock) #define RAW(skipBlock) ((struct RawNandFlash *) skipBlock) #define MODEL(skipBlock) ((struct NandFlashModel *) skipBlock) //------------------------------------------------------------------------------ // Internal functions //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // Exported functions //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ /// 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 skipBlock Pointer to a SkipBlockNandFlash instance. /// \param block Number of block to check. //------------------------------------------------------------------------------ unsigned char SkipBlockNandFlash_CheckBlock( const struct SkipBlockNandFlash *skipBlock, unsigned short block) { #if !defined (OP_BOOTSTRAP_on) unsigned char spare[NandCommon_MAXPAGESPARESIZE]; unsigned char error; unsigned char badBlockMarker; const struct NandSpareScheme *scheme; // Retrieve model scheme scheme = NandFlashModel_GetScheme(MODEL(skipBlock)); // Read spare area of first page of block error = RawNandFlash_ReadPage(RAW(skipBlock), block, 0, 0, spare); if (error) { TRACE_ERROR("CheckBlock: Cannot read page #0 of block #%d\n\r", block); return error; } NandSpareScheme_ReadBadBlockMarker(scheme, spare, &badBlockMarker); if (badBlockMarker != 0xFF) { return BADBLOCK; } // Read spare area of second page of block error = RawNandFlash_ReadPage(RAW(skipBlock), block, 1, 0, spare); if (error) { TRACE_ERROR("CheckBlock: Cannot read page #1 of block #%d\n\r", block); return error; } NandSpareScheme_ReadBadBlockMarker(scheme, spare, &badBlockMarker); if (badBlockMarker != 0xFF) { return BADBLOCK; } #endif return GOODBLOCK; } //------------------------------------------------------------------------------ /// 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. //------------------------------------------------------------------------------ unsigned char SkipBlockNandFlash_Initialize( struct SkipBlockNandFlash *skipBlock, const struct NandFlashModel *model, unsigned int commandAddress, unsigned int addressAddress, unsigned int dataAddress, const Pin pinChipEnable, const Pin pinReadyBusy) { unsigned char error; #if !defined(OP_BOOTSTRAP_on) unsigned int numBlocks; unsigned int 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", block); } else { TRACE_ERROR( "SkipBlockNandFlash_Initialize: Cannot retrieve info from block #%u\n\r", block); } } } #endif return 0; } //------------------------------------------------------------------------------ /// Erases a block of a SkipBlock NandFlash. /// Returns the RawNandFlash_EraseBlock code or NandCommon_ERROR_WRONGSTATUS. /// \param skipBlock Pointer to a SkipBlockNandFlash instance. /// \param block Number of block to erase. //------------------------------------------------------------------------------ unsigned char SkipBlockNandFlash_EraseBlock( struct SkipBlockNandFlash *skipBlock, unsigned short block, unsigned int eraseType) { unsigned char error; const struct NandSpareScheme *scheme; unsigned char spare[NandCommon_MAXPAGESPARESIZE]; // TRACE_INFO("SkipBlockNandFlash_EraseBlock(%d)\n\r", block); if (eraseType != SCRUB_ERASE) { // Check block status if (SkipBlockNandFlash_CheckBlock(skipBlock, block) != GOODBLOCK) { TRACE_INFO("SkipBlockNandFlash_EraseBlock: Block is BAD\n\r"); return NandCommon_ERROR_BADBLOCK; } } // Erase block error = RawNandFlash_EraseBlock(RAW(skipBlock), block); if (error) { // Try to mark the block as BAD TRACE_ERROR("SkipBlockNandFlash_EraseBlock: Cannot erase block, try to mark it BAD\n\r"); // Retrieve model scheme scheme = NandFlashModel_GetScheme(MODEL(skipBlock)); memset(spare, 0xFF, NandCommon_MAXPAGESPARESIZE); NandSpareScheme_WriteBadBlockMarker(scheme, spare, NandBlockStatus_BAD); return RawNandFlash_WritePage(RAW(skipBlock), block, 0, 0, spare); } return 0; } //------------------------------------------------------------------------------ /// Reads the data and/or the spare area of a page on a SkipBlock nandflash. If /// the data pointer is not 0, then the block MUST not be BAD /// Returns NandCommon_ERROR_BADBLOCK if the block is BAD; Otherwise, returns /// EccNandFlash_ReadPage(). /// \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. //------------------------------------------------------------------------------ unsigned char SkipBlockNandFlash_ReadPage( const struct SkipBlockNandFlash *skipBlock, unsigned short block, unsigned short 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 } //------------------------------------------------------------------------------ /// Reads the data of a whole block on a SkipBlock nandflash. /// Returns NandCommon_ERROR_BADBLOCK if the block is BAD; Otherwise, returns /// EccNandFlash_ReadPage(). /// \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. //------------------------------------------------------------------------------ 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; } //------------------------------------------------------------------------------ /// Writes the data and/or spare area of a page on a SkipBlock NandFlash. /// Returns NandCommon_ERROR_BADBLOCK if the page is BAD; otherwise, /// returns EccNandFlash_WritePage(). /// \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. //------------------------------------------------------------------------------ unsigned char SkipBlockNandFlash_WritePage( const struct SkipBlockNandFlash *skipBlock, unsigned short block, unsigned short 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 of a whole block on a SkipBlock nandflash. /// Returns NandCommon_ERROR_BADBLOCK if the block is BAD; Otherwise, returns /// EccNandFlash_ReadPage(). /// \param skipBlock Pointer to a SkipBlockNandFlash instance. /// \param block Number of block to read page from. /// \param data Data area buffer, can be 0. //------------------------------------------------------------------------------ unsigned char SkipBlockNandFlash_WriteBlock( const struct SkipBlockNandFlash *skipBlock, unsigned short block, void *data) { // Number of pages per block unsigned int numPagesPerBlock; // Page size unsigned int 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 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 *) ((unsigned char *) data + pageSize); } return 0; } //------------------------------------------------------------------------------ /// Writes the data of a whole block on a SkipBlock nandflash. /// Returns NandCommon_ERROR_BADBLOCK if the block is BAD; Otherwise, returns /// EccNandFlash_ReadPage(). /// \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. //------------------------------------------------------------------------------ //unsigned char SkipBlockNandFlash_WriteBlockUnaligned( // const struct SkipBlockNandFlash *skipBlock, // unsigned short block, // void *data, // void *tmpBuffer, // ) //{ // // Number of pages per block // unsigned int numPagesPerBlock; // // Page size // unsigned int 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)); // // for (i = 0; i < numPagesPerBlock; i++) { // error = SkipBlockNandFlash_WritePage(skipBlock, block, i, data, 0); // if (error == NandCommon_ERROR_BADBLOCK) { // // TRACE_ERROR("SkipBlockNandFlash_WriteBlock: Cannot write page %d of block %d.\n\r", i, block); // return NandCommon_ERROR_BADBLOCK; // } // else if (error) { // // TRACE_ERROR("SkipBlockNandFlash_WriteBlock: Cannot write page %d of block %d.\n\r", i, block); // return NandCommon_ERROR_CANNOTWRITE; // } // data += pageSize; // } // // return 0; //}