/* ---------------------------------------------------------------------------- | |
* ATMEL Microcontroller Software Support | |
* ---------------------------------------------------------------------------- | |
* Copyright (c) 2010, 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. | |
* ---------------------------------------------------------------------------- | |
*/ | |
/** | |
* \file | |
* | |
* SkipBlockNandFlash layer supplies application a set of interface to operate nandflash. | |
* which include initialize, block erase, block write/read, page write/read. \n | |
* This layer is to just skip over the bad blocks and place the data in the known good blocks. | |
* The algorithm starts by reading the entire spare area of the entire memory. The addresses | |
* of the factory-marked bad blocks are then collected in the programmer RAM. Next, the image is | |
* sequentially programmed (page by page) into the target device. When the target address | |
* corresponds to a bad block address, these pages are stored in the next good block, skipping | |
* the bad block. | |
* The SkipBlocks method is a very generic and well-performing strategy. When a bad block is | |
* encountered, the algorithm simply skips ahead to the next good block. | |
*/ | |
/*---------------------------------------------------------------------------- | |
* Headers | |
*----------------------------------------------------------------------------*/ | |
#include "memories.h" | |
#include <assert.h> | |
#include <string.h> | |
/*---------------------------------------------------------------------------- | |
* Internal definitions | |
*----------------------------------------------------------------------------*/ | |
/** Casts */ | |
#define ECC(skipBlock) ((struct EccNandFlash *) skipBlock) | |
#define RAW(skipBlock) ((struct RawNandFlash *) skipBlock) | |
#define MODEL(skipBlock) ((struct NandFlashModel *) skipBlock) | |
/*---------------------------------------------------------------------------- | |
* Exported functions | |
*----------------------------------------------------------------------------*/ | |
/** | |
* \brief Check if the given block of a nandflash device is bad. | |
* | |
* \param skipBlock Pointer to a SkipBlockNandFlash instance. | |
* \param block Number of block to check. | |
* \return GOODBLOCK if the block is good; or returns a NandCommon_ERROR code. | |
*/ | |
uint8_t SkipBlockNandFlash_CheckBlock( | |
const struct SkipBlockNandFlash *skipBlock, | |
uint16_t block) | |
{ | |
#if !defined (OP_BOOTSTRAP_on) | |
uint8_t spare[NandCommon_MAXPAGESPARESIZE]; | |
uint8_t error; | |
uint8_t 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; | |
} | |
/** | |
* \brief Initializes a SkipBlockNandFlash instance. Scans the device to retrieve or | |
* create block status information. | |
* | |
* \param skipBlock Pointer to a SkipBlockNandFlash instance. | |
* \param model Pointer to the underlying nand chip model. Can be 0. | |
* \param commandAddress Address at which commands are sent. | |
* \param addressAddress Address at which addresses are sent. | |
* \param dataAddress Address at which data is sent. | |
* \param pinChipEnable Pin controlling the CE signal of the NandFlash. | |
* \param pinReadyBusy Pin used to monitor the ready/busy signal of the Nand. | |
*/ | |
uint8_t SkipBlockNandFlash_Initialize( | |
struct SkipBlockNandFlash *skipBlock, | |
const struct NandFlashModel *model, | |
uint32_t commandAddress, | |
uint32_t addressAddress, | |
uint32_t dataAddress, | |
const Pin pinChipEnable, | |
const Pin pinReadyBusy) | |
{ | |
uint8_t error; | |
#if !defined(OP_BOOTSTRAP_on) | |
uint32_t numBlocks; | |
uint32_t block; | |
#endif | |
TRACE_DEBUG("SkipBlockNandFlash_Initialize()\n\r"); | |
/* Initialize SkipBlockNandFlash */ | |
#if !defined(OP_BOOTSTRAP_on) | |
error = EccNandFlash_Initialize(ECC(skipBlock), | |
model, | |
commandAddress, | |
addressAddress, | |
dataAddress, | |
pinChipEnable, | |
pinReadyBusy); | |
#else | |
error = RawNandFlash_Initialize(RAW(skipBlock), | |
model, | |
commandAddress, | |
addressAddress, | |
dataAddress, | |
pinChipEnable, | |
pinReadyBusy); | |
#endif | |
#if !defined(OP_BOOTSTRAP_on) | |
if (error) { | |
return error; | |
} | |
/* Retrieve model information */ | |
numBlocks = NandFlashModel_GetDeviceSizeInBlocks(MODEL(skipBlock)); | |
/* Initialize block statuses */ | |
TRACE_DEBUG("Retrieving bad block information ...\n\r"); | |
/* Retrieve block status from their first page spare area */ | |
for (block = 0; block < numBlocks; block++) { | |
/* Read spare of first page */ | |
error = SkipBlockNandFlash_CheckBlock(skipBlock, block); | |
if (error != GOODBLOCK) { | |
if (error == BADBLOCK) { | |
TRACE_DEBUG("Block #%d is bad\n\r", block); | |
} | |
else { | |
TRACE_ERROR( | |
"SkipBlockNandFlash_Initialize: Cannot retrieve info from block #%u\n\r", block); | |
} | |
} | |
} | |
#endif | |
return 0; | |
} | |
/** | |
* \brief Erases a block of a SkipBlock NandFlash. | |
* | |
* \param skipBlock Pointer to a SkipBlockNandFlash instance. | |
* \param block Number of block to erase. | |
* \return the RawNandFlash_EraseBlock code or NandCommon_ERROR_WRONGSTATUS. | |
*/ | |
uint8_t SkipBlockNandFlash_EraseBlock( | |
struct SkipBlockNandFlash *skipBlock, | |
uint16_t block, | |
uint32_t eraseType) | |
{ | |
uint8_t error; | |
const struct NandSpareScheme *scheme; | |
uint8_t 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_skip); | |
return RawNandFlash_WritePage(RAW(skipBlock), block, 0, 0, spare); | |
} | |
return 0; | |
} | |
/** | |
* \brief Reads the data and/or the spare area of a page on a SkipBlock nandflash. | |
* | |
* \param skipBlock Pointer to a SkipBlockNandFlash instance. | |
* \param block Number of block to read page from. | |
* \param page Number of page to read inside the given block. | |
* \param data Data area buffer, can be 0. | |
* \param spare Spare area buffer, can be 0. | |
* \note If one of the buffer pointer is 0, then the block MUST not be BAD. | |
* \return NandCommon_ERROR_BADBLOCK if the block is BAD; Otherwise, returns EccNandFlash_ReadPage(). | |
*/ | |
uint8_t SkipBlockNandFlash_ReadPage( | |
const struct SkipBlockNandFlash *skipBlock, | |
uint16_t block, | |
uint16_t page, | |
void *data, | |
void *spare) | |
{ | |
#if !defined(OP_BOOTSTRAP_on) | |
/* Check that the block is not BAD if data is requested */ | |
if (SkipBlockNandFlash_CheckBlock(skipBlock, block) != GOODBLOCK) { | |
TRACE_ERROR("SkipBlockNandFlash_ReadPage: Block is BAD.\n\r"); | |
return NandCommon_ERROR_BADBLOCK; | |
} | |
/* Read data with ECC verification */ | |
return EccNandFlash_ReadPage(ECC(skipBlock), block, page, data, spare); | |
#else | |
return RawNandFlash_ReadPage(RAW(skipBlock), block, page, data, spare); | |
#endif | |
} | |
/** | |
* \brief Reads the data of a whole block on a SkipBlock nandflash. | |
* | |
* \param skipBlock Pointer to a SkipBlockNandFlash instance. | |
* \param block Number of block to read page from. | |
* \param page Number of page to read inside the given block. | |
* \param data Data area buffer, can be 0. | |
* \param spare Spare area buffer, can be 0. | |
* \return NandCommon_ERROR_BADBLOCK if the block is BAD; Otherwise, returns EccNandFlash_ReadPage(). | |
*/ | |
uint8_t SkipBlockNandFlash_ReadBlock( | |
const struct SkipBlockNandFlash *skipBlock, | |
uint16_t block, | |
void *data) | |
{ | |
/* Number of pages per block */ | |
uint32_t numPagesPerBlock, pageSize; | |
/* Page index */ | |
uint16_t i; | |
/* Error returned by SkipBlockNandFlash_WritePage */ | |
uint8_t error = 0; | |
/* Retrieve model information */ | |
pageSize = NandFlashModel_GetPageDataSize(MODEL(skipBlock)); | |
numPagesPerBlock = NandFlashModel_GetBlockSizeInPages(MODEL(skipBlock)); | |
/* Check that the block is 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 *) ((uint8_t *) data + pageSize); | |
} | |
return 0; | |
} | |
/** | |
* \brief Writes the data and/or spare area of a page on a SkipBlock NandFlash. | |
* | |
* \param skipBlock Pointer to a SkipBlockNandFlash instance. | |
* \param block Number of the block to write. | |
* \param page Number of the page to write inside the given block. | |
* \param data Data area buffer. | |
* \param spare Spare area buffer. | |
* \return NandCommon_ERROR_BADBLOCK if the block is BAD; Otherwise, returns EccNandFlash_WritePage(). | |
*/ | |
uint8_t SkipBlockNandFlash_WritePage( | |
const struct SkipBlockNandFlash *skipBlock, | |
uint16_t block, | |
uint16_t page, | |
void *data, | |
void *spare) | |
{ | |
/* Check that the block is LIVE */ | |
if (SkipBlockNandFlash_CheckBlock(skipBlock, block) != GOODBLOCK) { | |
TRACE_ERROR("SkipBlockNandFlash_WritePage: Block is BAD.\n\r"); | |
return NandCommon_ERROR_BADBLOCK; | |
} | |
/* Write data with ECC calculation */ | |
return EccNandFlash_WritePage(ECC(skipBlock), block, page, data, spare); | |
} | |
/** | |
* \brief Writes the data of a whole block on a SkipBlock nandflash. | |
* | |
* \param skipBlock Pointer to a SkipBlockNandFlash instance. | |
* \param block Number of the block to write. | |
* \param data Data area buffer. | |
* \return NandCommon_ERROR_BADBLOCK if the block is BAD; Otherwise, returns EccNandFlash_WritePage(). | |
*/ | |
uint8_t SkipBlockNandFlash_WriteBlock( | |
const struct SkipBlockNandFlash *skipBlock, | |
uint16_t block, | |
void *data) | |
{ | |
/* Number of pages per block */ | |
uint32_t numPagesPerBlock; | |
/* Page size */ | |
uint32_t pageSize; | |
/* Page index*/ | |
uint16_t i; | |
/* Error returned by SkipBlockNandFlash_WritePage*/ | |
uint8_t error = 0; | |
/* Retrieve model information*/ | |
pageSize = NandFlashModel_GetPageDataSize(MODEL(skipBlock)); | |
numPagesPerBlock = NandFlashModel_GetBlockSizeInPages(MODEL(skipBlock)); | |
/* Check that the block is LIVE*/ | |
if (SkipBlockNandFlash_CheckBlock(skipBlock, block) != GOODBLOCK) { | |
TRACE_ERROR("SkipBlockNandFlash_WriteBlock: Block is BAD.\n\r"); | |
return NandCommon_ERROR_BADBLOCK; | |
} | |
for (i = 0; i < numPagesPerBlock; i++) { | |
error = EccNandFlash_WritePage(ECC(skipBlock), block, i, data, 0); | |
if (error) { | |
TRACE_ERROR("SkipBlockNandFlash_WriteBlock: Cannot write page %d of block %d.\n\r", i, block); | |
return NandCommon_ERROR_CANNOTWRITE; | |
} | |
data = (void *) ((uint8_t *) data + pageSize); | |
} | |
return 0; | |
} | |
/** | |
* \brief Writes the data of a whole block on a SkipBlock nandflash. | |
* | |
* \param skipBlock Pointer to a SkipBlockNandFlash instance. | |
* \param block Number of the block to write. | |
* \param pageOffsetInBlock Number of the page to write inside the given block. | |
* \param data Data area buffer. | |
* \param numPages number of pages to write. | |
* \return NandCommon_ERROR_BADBLOCK if the block is BAD; Otherwise, returns EccNandFlash_WritePage(). | |
*/ | |
uint8_t SkipBlockNandFlash_WriteBlockUnaligned( | |
const struct SkipBlockNandFlash *skipBlock, | |
uint16_t block, | |
uint16_t pageOffsetInBlock, | |
uint16_t numPages, | |
void *data | |
) | |
{ | |
/* Page size*/ | |
uint32_t pageSize; | |
/* Page index */ | |
uint16_t i; | |
/* Error returned by SkipBlockNandFlash_WritePage */ | |
uint8_t error = 0; | |
/* Retrieve model information */ | |
pageSize = NandFlashModel_GetPageDataSize(MODEL(skipBlock)); | |
for (i = pageOffsetInBlock; i < pageOffsetInBlock + numPages; i++) { | |
error = SkipBlockNandFlash_WritePage(skipBlock, block, i, data, 0); | |
if (error == NandCommon_ERROR_BADBLOCK) { | |
TRACE_ERROR("SkipBlockNandFlash_WriteBlock: Block is BAD.\n\r"); | |
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 = (void *) ((uint8_t *) data + pageSize); | |
} | |
return 0; | |
} | |
/** | |
* \brief Read the data of a whole block on a SkipBlock nandflash. | |
* | |
* \param skipBlock Pointer to a SkipBlockNandFlash instance. | |
* \param block Number of the block to read. | |
* \param pageOffsetInBlock Number of the page to read inside the given block. | |
* \param data Data area buffer. | |
* \param numPages number of pages to read. | |
* \return NandCommon_ERROR_BADBLOCK if the block is BAD; Otherwise, returns EccNandFlash_WritePage(). | |
*/ | |
uint8_t SkipBlockNandFlash_ReadBlockUnaligned( | |
const struct SkipBlockNandFlash *skipBlock, | |
uint16_t block, | |
uint16_t pageOffsetInBlock, | |
uint16_t numPages, | |
void *data | |
) | |
{ | |
/* Page size*/ | |
uint32_t pageSize; | |
/* Page index*/ | |
uint16_t i; | |
/* Error returned by SkipBlockNandFlash_ReadPage*/ | |
uint8_t error = 0; | |
/* Retrieve model information*/ | |
pageSize = NandFlashModel_GetPageDataSize(MODEL(skipBlock)); | |
for (i = pageOffsetInBlock; i < pageOffsetInBlock + numPages; i++) { | |
error = SkipBlockNandFlash_ReadPage(skipBlock, block, i, data, 0); | |
if (error == NandCommon_ERROR_BADBLOCK) { | |
TRACE_ERROR("SkipBlockNandFlash_ReadBlock: Block is BAD.\n\r"); | |
return NandCommon_ERROR_BADBLOCK; | |
} | |
else if (error) { | |
TRACE_ERROR("SkipBlockNandFlash_ReadBlock: Cannot read page %d of block %d.\n\r", i, block); | |
return NandCommon_ERROR_CANNOTREAD; | |
} | |
data = (void *) ((uint8_t *) data + pageSize); | |
} | |
return 0; | |
} | |