/* ---------------------------------------------------------------------------- | |
* 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 | |
* | |
* EccNandFlash layer is called by SkipBlockNandFlash driver, it will call the bl driver (RawNandFlash) | |
* to do write/read operations, and do ECC check to the write/read result, it then will feedback the | |
* ecc check result to the upper SkipBlockNandFlash layer driver. | |
* | |
* -# EccNandFlash_Initialize is used to initializes an EccNandFlash instance. | |
* -# EccNandFlash_WritePage is used to write a Nandflash page with ecc result, the function | |
* will calculate ecc for the data that is going to be written, and write data and spare(with | |
* calculated ecc) to Nandflash device. | |
* -# EccNandFlash_ReadPage is uese to read a Nandflash page with ecc check, the function | |
* will read out data and spare first, then it calculates ecc with data and then compare with | |
* the readout ecc, and feedback the ecc check result to dl driver. | |
*/ | |
/*---------------------------------------------------------------------------- | |
* Headers | |
*----------------------------------------------------------------------------*/ | |
#include <stdint.h> | |
#include "memories.h" | |
#include <string.h> | |
#include <assert.h> | |
/*---------------------------------------------------------------------------- | |
* Internal definitions | |
*----------------------------------------------------------------------------*/ | |
/** Casts */ | |
#define MODEL(ecc) ((struct NandFlashModel *) ecc) | |
#define RAW(ecc) ((struct RawNandFlash *) ecc) | |
/*---------------------------------------------------------------------------- | |
* Exported functions | |
*----------------------------------------------------------------------------*/ | |
/** | |
* \brief Initializes an EccNandFlash instance. | |
* | |
* \param ecc Pointer to an EccNandFlash 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. | |
* \return 0 if the initialization is done; or returns a error code. | |
*/ | |
unsigned char EccNandFlash_Initialize( | |
struct EccNandFlash *ecc, | |
const struct NandFlashModel *model, | |
unsigned int commandAddress, | |
unsigned int addressAddress, | |
unsigned int dataAddress, | |
const Pin pinChipEnable, | |
const Pin pinReadyBusy) | |
{ | |
unsigned char rc; | |
rc = RawNandFlash_Initialize(RAW(ecc), | |
model, | |
commandAddress, | |
addressAddress, | |
dataAddress, | |
pinChipEnable, | |
pinReadyBusy); | |
#if defined(HARDWARE_ECC) | |
{ | |
unsigned int ecc_page; | |
switch(NandFlashModel_GetPageDataSize(MODEL(ecc))) { | |
case 512: ecc_page = SMC_CFG_PAGESIZE_PS512_16; break; | |
case 1024: ecc_page = SMC_CFG_PAGESIZE_PS1024_32; break; | |
case 2048: ecc_page = SMC_CFG_PAGESIZE_PS2048_64; break; | |
case 4096: ecc_page = SMC_CFG_PAGESIZE_PS4096_128; break; | |
default: | |
TRACE_ERROR("PageSize %d not compatible with ECC\n\r", | |
NandFlashModel_GetPageDataSize(MODEL(ecc))); | |
return NandCommon_ERROR_ECC_NOT_COMPATIBLE; | |
} | |
SMC_ECC_Configure(SMC, SMC_ECC_MD_TYPCORREC_C256B, | |
ecc_page); | |
} | |
#endif | |
return rc; | |
} | |
/** | |
* \brief Reads the data and/or spare of a page of a nandflash chip, and verify that | |
* the data is valid using the ECC information contained in the spare. If one | |
* buffer pointer is 0, the corresponding area is not saved. | |
* \param ecc Pointer to an EccNandFlash instance. | |
* \param block Number of block to read from. | |
* \param page Number of page to read inside given block. | |
* \param data Data area buffer. | |
* \param spare Spare area buffer. | |
* \return 0 if the data has been read and is valid; otherwise returns either | |
* NandCommon_ERROR_CORRUPTEDDATA or ... | |
*/ | |
unsigned char EccNandFlash_ReadPage( | |
const struct EccNandFlash *ecc, | |
unsigned short block, | |
unsigned short page, | |
void *data, | |
void *spare) | |
{ | |
unsigned char tmpSpare[NandCommon_MAXPAGESPARESIZE]; | |
unsigned char error; | |
#ifndef HARDWARE_ECC | |
unsigned char tmpData[NandCommon_MAXPAGEDATASIZE]; | |
unsigned char hamming[NandCommon_MAXSPAREECCBYTES]; | |
#else | |
unsigned char hsiaoInSpare[NandCommon_MAXSPAREECCBYTES]; | |
unsigned char hsiao[NandCommon_MAXSPAREECCBYTES]; | |
#endif | |
unsigned short pageDataSize = NandFlashModel_GetPageDataSize(MODEL(ecc)); | |
unsigned char pageSpareSize = NandFlashModel_GetPageSpareSize(MODEL(ecc)); | |
TRACE_DEBUG("EccNandFlash_ReadPage(B#%d:P#%d)\n\r", block, page); | |
#ifndef HARDWARE_ECC | |
/* Start by reading the spare and the data */ | |
error = RawNandFlash_ReadPage(RAW(ecc), block, page, tmpData, tmpSpare); | |
if (error) { | |
TRACE_ERROR("EccNandFlash_ReadPage: Failed to read page\n\r"); | |
return error; | |
} | |
/* Retrieve ECC information from page and verify the data */ | |
NandSpareScheme_ReadEcc(NandFlashModel_GetScheme(MODEL(ecc)), tmpSpare, hamming); | |
error = Hamming_Verify256x(tmpData, pageDataSize, hamming); | |
#else | |
/* Start by reading the spare area */ | |
/* Note: Can't read data and spare at the same time, otherwise, the ECC parity generation will be incorrect. */ | |
error = RawNandFlash_ReadPage(RAW(ecc), block, page, 0, tmpSpare); | |
if (error) { | |
TRACE_ERROR("EccNandFlash_ReadPage: $page %d.%d\n\r", | |
block, page); | |
return error; | |
} | |
/* Retrieve ECC information from page and verify the data */ | |
NandSpareScheme_ReadEcc(NandFlashModel_GetScheme(MODEL(ecc)), tmpSpare, hsiaoInSpare); | |
/* Reading the main data area */ | |
error = RawNandFlash_ReadPage(RAW(ecc), block, page, (unsigned char*)data, 0); | |
if (error) { | |
TRACE_ERROR("EccNandFlash_ReadPage: $page %d.%d\n\r", | |
block, page); | |
return error; | |
} | |
SMC_ECC_GetEccParity(pageDataSize, hsiao, NandFlashModel_GetDataBusWidth(MODEL(ecc))); | |
/* Verify the data */ | |
error = SMC_ECC_VerifyHsiao((unsigned char*) data, | |
pageDataSize, | |
hsiaoInSpare, | |
hsiao, | |
NandFlashModel_GetDataBusWidth(MODEL(ecc))); | |
#endif | |
if (error && (error != Hamming_ERROR_SINGLEBIT)) { | |
TRACE_ERROR("EccNandFlash_ReadPage: at B%d.P%d Unrecoverable data\n\r", | |
block, page); | |
return NandCommon_ERROR_CORRUPTEDDATA; | |
} | |
#ifndef HARDWARE_ECC | |
/* Copy data and/or spare into final buffers */ | |
if (data) { | |
memcpy(data, tmpData, pageDataSize); | |
} | |
if (spare) { | |
memcpy(spare, tmpSpare, pageSpareSize); | |
} | |
#else | |
if (spare) { | |
memcpy(spare, tmpSpare, pageSpareSize); | |
} | |
#endif | |
return 0; | |
} | |
/** | |
* \brief Writes the data and/or spare area of a nandflash page, after calculating an | |
* ECC for the data area and storing it in the spare. If no data buffer is | |
* provided, the ECC is read from the existing page spare. If no spare buffer | |
* is provided, the spare area is still written with the ECC information | |
* calculated on the data buffer. | |
* \param ecc Pointer to an EccNandFlash instance. | |
* \param block Number of block to read from. | |
* \param page Number of page to read inside given block. | |
* \param data Data area buffer. | |
* \param spare Spare area buffer. | |
* \return 0 if successful; otherwise returns an error code. | |
*/ | |
unsigned char EccNandFlash_WritePage( | |
const struct EccNandFlash *ecc, | |
unsigned short block, | |
unsigned short page, | |
void *data, | |
void *spare) | |
{ | |
unsigned char error; | |
unsigned char tmpSpare[NandCommon_MAXPAGESPARESIZE]; | |
unsigned short pageDataSize = NandFlashModel_GetPageDataSize(MODEL(ecc)); | |
unsigned short pageSpareSize = NandFlashModel_GetPageSpareSize(MODEL(ecc)); | |
#ifndef HARDWARE_ECC | |
unsigned char hamming[NandCommon_MAXSPAREECCBYTES]; | |
#else | |
unsigned char hsiao[NandCommon_MAXSPAREECCBYTES]; | |
#endif | |
assert( (data != NULL) || (spare != NULL) ) ; | |
// TRACE_DEBUG( "EccNandFlash_WritePage: At least one area must be written\n\r" ) ; | |
TRACE_DEBUG("EccNandFlash_WritePage(B#%d:P#%d)\n\r", block, page); | |
#ifndef HARDWARE_ECC | |
/* Compute ECC on the new data, if provided */ | |
/* If not provided, hamming code set to 0xFFFF.. to keep existing bytes */ | |
memset(hamming, 0xFF, NandCommon_MAXSPAREECCBYTES); | |
if (data) { | |
/* Compute hamming code on data */ | |
Hamming_Compute256x(data, pageDataSize, hamming); | |
} | |
/* Store code in spare buffer (if no buffer provided, use a temp. one) */ | |
if (!spare) { | |
spare = tmpSpare; | |
memset(spare, 0xFF, pageSpareSize); | |
} | |
NandSpareScheme_WriteEcc(NandFlashModel_GetScheme(MODEL(ecc)), spare, hamming); | |
/* Perform write operation */ | |
error = RawNandFlash_WritePage(RAW(ecc), block, page, data, spare); | |
if (error) { | |
TRACE_ERROR("EccNandFlash_WritePage: Failed to write page\n\r"); | |
return error; | |
} | |
#else | |
/* Store code in spare buffer (if no buffer provided, use a temp. one) */ | |
if (!spare) { | |
spare = tmpSpare; | |
memset(spare, 0xFF, pageSpareSize); | |
} | |
/* Perform write operation */ | |
error = RawNandFlash_WritePage(RAW(ecc), block, page, data, spare); | |
if (error) { | |
TRACE_ERROR("EccNandFlash_WritePage: Failed to write page\n\r"); | |
return error; | |
} | |
SMC_ECC_GetEccParity(pageDataSize, hsiao, NandFlashModel_GetDataBusWidth(MODEL(ecc))); | |
/* Perform write operation */ | |
NandSpareScheme_WriteEcc(NandFlashModel_GetScheme(MODEL(ecc)), spare, hsiao); | |
error = RawNandFlash_WritePage(RAW(ecc), block, page, 0, spare); | |
if (error) { | |
TRACE_ERROR("EccNandFlash_WritePage: Failed to write page\n\r"); | |
return error; | |
} | |
#endif | |
return 0; | |
} | |