blob: f954b80165b011846c3a96cc4b30759b71f77ea2 [file] [log] [blame]
/* ----------------------------------------------------------------------------
* 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;
}