//------------------------------------------------------------------------------ /// Physically writes the status of a block inside its first page spare area. /// Returns 0 if successful; otherwise returns a NandCommon_ERROR_xx code. /// \param managed Pointer to a ManagedNandFlash instance. /// \param block Raw block number. /// \param pStatus Pointer to status data. /// \param spare Pointer to allocated spare area (must be assigned). //------------------------------------------------------------------------------ static unsigned char WriteBlockStatus( const struct ManagedNandFlash *managed, unsigned short block, struct NandBlockStatus *pStatus, unsigned char *spare) { ASSERT(spare, "ManagedNandFlash_WriteBlockStatus: spare\n\r"); memset(spare, 0xFF, NandCommon_MAXPAGESPARESIZE); NandSpareScheme_WriteExtra(NandFlashModel_GetScheme(MODEL(managed)), spare, pStatus, 4, 0); return RawNandFlash_WritePage(RAW(managed), block, 0, 0, spare); }
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; }
//------------------------------------------------------------------------------ /// 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. /// Returns 0 if the data has been read and is valid; otherwise returns either /// NandCommon_ERROR_CORRUPTEDDATA or ... /// \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. //------------------------------------------------------------------------------ unsigned char EccNandFlash_ReadPage( const struct EccNandFlash *ecc, unsigned short block, unsigned short page, void *data, void *spare) { unsigned char tmpData[NandCommon_MAXPAGEDATASIZE]; unsigned char tmpSpare[NandCommon_MAXPAGESPARESIZE]; unsigned char error; unsigned char hamming[NandCommon_MAXSPAREECCBYTES]; 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); // 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); if (error && (error != Hamming_ERROR_SINGLEBIT)) { TRACE_ERROR("EccNandFlash_ReadPage: Unrecoverable data\n\r"); return NandCommon_ERROR_CORRUPTEDDATA; } // Copy data and/or spare into final buffers if (data) { memcpy(data, tmpData, pageDataSize); } if (spare) { memcpy(spare, tmpSpare, pageSpareSize); } return 0; }
//------------------------------------------------------------------------------ /// 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. /// Returns 0 if successful; otherwise returns an error code. /// \param ecc Pointer to an EccNandFlash instance. /// \param block Number of the block to write in. /// \param page Number of the page to write inside the given block. /// \param data Data area buffer, can be 0. /// \param spare Spare area buffer, can be 0. //------------------------------------------------------------------------------ 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)); unsigned char hamming[NandCommon_MAXSPAREECCBYTES]; ASSERT(data || spare, "EccNandFlash_WritePage: At least one area must be written\n\r"); TRACE_DEBUG("EccNandFlash_WritePage(B#%d:P#%d)\n\r", block, page); // 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; } return 0; }
//------------------------------------------------------------------------------ /// Returns 1 if a nandflash device is virgin (i.e. has never been used as a /// managed nandflash); otherwise return 0. /// \param managed Pointer to a ManagedNandFlash instance. /// \param spare Pointer to allocated spare area (must be assigned) //------------------------------------------------------------------------------ static unsigned char IsDeviceVirgin(const struct ManagedNandFlash *managed, unsigned char *spare) { struct NandBlockStatus blockStatus; const struct NandSpareScheme *scheme = NandFlashModel_GetScheme(MODEL(managed)); unsigned short baseBlock = managed->baseBlock; unsigned char badBlockMarker; unsigned char error; ASSERT(spare, "ManagedNandFlash_IsDeviceVirgin: spare\n\r"); // Read spare area of page #0 error = RawNandFlash_ReadPage(RAW(managed), baseBlock, 0, 0, spare); ASSERT(!error, "ManagedNandFlash_IsDeviceVirgin: Failed to read page #0\n\r"); // Retrieve bad block marker and block status from spare area NandSpareScheme_ReadBadBlockMarker(scheme, spare, &badBlockMarker); NandSpareScheme_ReadExtra(scheme, spare, &blockStatus, 4, 0); // Check if block is marked as bad if (badBlockMarker != 0xFF) { // Device is not virgin, since page #0 is guaranteed to be good return 0; } // If device is not virgin, then block status will be set to either // FREE, DIRTY or LIVE else if (blockStatus.status != NandBlockStatus_DEFAULT) { // Device is not virgin return 0; } return 1; }
/** * \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; }
//------------------------------------------------------------------------------ /// Copy the data & spare area of one page to another page. The source block /// can be either LIVE or DIRTY, and the destination block must be LIVE; they /// must both have the same parity. /// Returns 0 if successful; NandCommon_ERROR_WRONGSTATUS if one or more page /// is not live; otherwise returns an NandCommon_ERROR_xxx code. /// \param managed Pointer to a ManagedNandFlash instance. /// \param sourceBlock Source block number based on managed area. /// \param sourcePage Number of source page inside the source block. /// \param destBlock Destination block number based on managed area. /// \param destPage Number of destination page inside the dest block. //------------------------------------------------------------------------------ unsigned char ManagedNandFlash_CopyPage( const struct ManagedNandFlash *managed, unsigned short sourceBlock, unsigned short sourcePage, unsigned short destBlock, unsigned short destPage) { unsigned char *pDataBuffer = RawNandFlash_GetDataBuffer(RAW(managed)); unsigned char *pSpareBuffer = RawNandFlash_GetSpareBuffer(RAW(managed)); unsigned char error; ASSERT((sourcePage & 1) == (destPage & 1), "ManagedNandFlash_CopyPage: source & dest pages must have the same parity\n\r"); TRACE_INFO("ManagedNandFlash_CopyPage(B#%d:P#%d -> B#%d:P#%d)\n\r", sourceBlock, sourcePage, destBlock, destPage); // Check block statuses if ((managed->blockStatuses[sourceBlock].status != NandBlockStatus_LIVE) && (managed->blockStatuses[sourceBlock].status != NandBlockStatus_DIRTY)) { TRACE_ERROR("ManagedNandFlash_CopyPage: Source block must be LIVE or DIRTY.\n\r"); return NandCommon_ERROR_WRONGSTATUS; } if (managed->blockStatuses[destBlock].status != NandBlockStatus_LIVE) { TRACE_ERROR("ManagedNandFlash_CopyPage: Destination block must be LIVE.\n\r"); return NandCommon_ERROR_WRONGSTATUS; } // If destination page is page #0, block status information must not be // overwritten if (destPage == 0) { // Read data & spare to copy error = EccNandFlash_ReadPage(ECC(managed), managed->baseBlock + sourceBlock, sourcePage, pDataBuffer, pSpareBuffer); if (error) goto error; // Write destination block status information in spare NandSpareScheme_WriteExtra(NandFlashModel_GetScheme(MODEL(managed)), pSpareBuffer, &(managed->blockStatuses[destBlock]), 4, 0); // Write page error = RawNandFlash_WritePage(RAW(managed), managed->baseBlock + destBlock, destPage, pDataBuffer, pSpareBuffer); if (error) goto error; } // Otherwise, a normal copy can be done else { error = RawNandFlash_CopyPage(RAW(managed), managed->baseBlock + sourceBlock, sourcePage, managed->baseBlock + destBlock, destPage); } error: RawNandFlash_ReleaseDataBuffer(RAW(managed)); RawNandFlash_ReleaseSpareBuffer(RAW(managed)); return error; }
//------------------------------------------------------------------------------ /// Initializes a ManagedNandFlash instance. Scans the device to retrieve or /// create block status information. /// \param managed Pointer to a ManagedNandFlash 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 Base physical block address of managed area, managed 0. /// \param sizeInBlocks Number of blocks that is managed. //------------------------------------------------------------------------------ unsigned char ManagedNandFlash_Initialize( struct ManagedNandFlash *managed, 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 char spare[NandCommon_MAXPAGESPARESIZE]; unsigned int numBlocks; const struct NandSpareScheme *scheme; unsigned int block, phyBlock; struct NandBlockStatus blockStatus; unsigned char badBlockMarker; unsigned int eraseCount, minEraseCount, maxEraseCount; TRACE_DEBUG("ManagedNandFlash_Initialize()\n\r"); // Initialize EccNandFlash error = EccNandFlash_Initialize(ECC(managed), model, commandAddress, addressAddress, dataAddress, pinChipEnable, pinReadyBusy); if (error) { return error; } // Retrieve model information numBlocks = NandFlashModel_GetDeviceSizeInBlocks(MODEL(managed)); scheme = NandFlashModel_GetScheme(MODEL(managed)); // Initialize base & size if (sizeInBlocks == 0) sizeInBlocks = numBlocks; if (baseBlock > numBlocks) { baseBlock = 0; } else if (baseBlock + sizeInBlocks > numBlocks) { sizeInBlocks = numBlocks - baseBlock; } TRACE_INFO("Managed NF area: %d + %d\n\r", baseBlock, sizeInBlocks); if (sizeInBlocks > NandCommon_MAXNUMBLOCKS) { TRACE_ERROR("Out of Maxmized Managed Size: %d > %d\n\r", sizeInBlocks, NandCommon_MAXNUMBLOCKS); TRACE_INFO("Change NandCommon_MAXNUMBLOCKS or sizeInBlocks\n\r"); return NandCommon_ERROR_OUTOFBOUNDS; } managed->baseBlock = baseBlock; managed->sizeInBlocks = sizeInBlocks; // Initialize block statuses // First, check if device is virgin if (IsDeviceVirgin(managed, spare)) { TRACE_WARNING("Device is virgin, doing initial block scanning ...\n\r"); // Perform initial scan of the device area for (block=0; block < sizeInBlocks; block++) { phyBlock = baseBlock + block; // Check if physical block is bad error = CheckBlock(managed, phyBlock, spare); if (error == BADBLOCK) { // Mark block as bad TRACE_DEBUG("Block #%d is bad\n\r", block); managed->blockStatuses[block].status = NandBlockStatus_BAD; } else if (error == GOODBLOCK) { // Mark block as free with erase count 0 TRACE_DEBUG("Block #%d is free\n\r", block); managed->blockStatuses[block].status = NandBlockStatus_FREE; managed->blockStatuses[block].eraseCount = 0; // Write status in spare of block first page error = WriteBlockStatus(managed, phyBlock, &(managed->blockStatuses[block]), spare); if (error) { TRACE_ERROR("ManagedNandFlash_Initialize: WR spare\n\r"); return error; } } else { TRACE_ERROR("ManagedNandFlash_Initialize: Scan device\n\r"); return error; } } } else { TRACE_INFO("Managed, retrieving information ...\n\r"); // Retrieve block statuses from their first page spare area // (find maximum and minimum wear at the same time) minEraseCount = 0xFFFFFFFF; maxEraseCount = 0; for (block=0; block < sizeInBlocks; block++) { phyBlock = baseBlock + block; // Read spare of first page error = RawNandFlash_ReadPage(RAW(managed), phyBlock, 0, 0, spare); if (error) { TRACE_ERROR("ManagedNandFlash_Initialize: Read block #%d(%d)\n\r", block, phyBlock); } // Retrieve bad block marker and block status NandSpareScheme_ReadBadBlockMarker(scheme, spare, &badBlockMarker); NandSpareScheme_ReadExtra(scheme, spare, &blockStatus, 4, 0); // If they do not match, block must be bad if ( (badBlockMarker != 0xFF) && (blockStatus.status != NandBlockStatus_BAD)) { TRACE_DEBUG("Block #%d(%d) is bad\n\r", block, phyBlock); managed->blockStatuses[block].status = NandBlockStatus_BAD; } // Check that block status is not default // (meaning block is not managed) else if (blockStatus.status == NandBlockStatus_DEFAULT) { TRACE_ERROR("Block #%d(%d) is not managed\n\r", block, phyBlock); return NandCommon_ERROR_NOMAPPING; } // Otherwise block status is accurate else { TRACE_DEBUG("Block #%03d(%d) : status = %2d | eraseCount = %d\n\r", block, phyBlock, blockStatus.status, blockStatus.eraseCount); managed->blockStatuses[block] = blockStatus; // Check for min/max erase counts if (blockStatus.eraseCount < minEraseCount) { minEraseCount = blockStatus.eraseCount; } if (blockStatus.eraseCount > maxEraseCount) { maxEraseCount = blockStatus.eraseCount; } //// Clean block //// Release LIVE blocks //if (managed->blockStatuses[block].status == NandBlockStatus_LIVE) { // // ManagedNandFlash_ReleaseBlock(managed, block); //} //// Erase DIRTY blocks //if (managed->blockStatuses[block].status == NandBlockStatus_DIRTY) { // // ManagedNandFlash_EraseBlock(managed, block); //} } } // Display erase count information TRACE_ERROR_WP("|--------|------------|--------|--------|--------|\n\r"); TRACE_ERROR_WP("| Wear | Count | Free | Live | Dirty |\n\r"); TRACE_ERROR_WP("|--------|------------|--------|--------|--------|\n\r"); for (eraseCount=minEraseCount; eraseCount <= maxEraseCount; eraseCount++) { unsigned int count = 0, live = 0, dirty = 0, free = 0; for (block=0; block < sizeInBlocks; block++) { if ((managed->blockStatuses[block].eraseCount == eraseCount) && (managed->blockStatuses[block].status != NandBlockStatus_BAD)) { count++; switch (managed->blockStatuses[block].status) { case NandBlockStatus_LIVE: live++; break; case NandBlockStatus_DIRTY: dirty++; break; case NandBlockStatus_FREE: free++; break; } } } if (count > 0) { TRACE_ERROR_WP("| %4d | %8d | %4d | %4d | %4d |\n\r", eraseCount, count, free, live, dirty); } } TRACE_ERROR_WP("|--------|------------|--------|--------|--------|\n\r"); } return 0; }
//------------------------------------------------------------------------------ /// 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. /// Returns 0 if successful; otherwise returns an error code. /// \param ecc Pointer to an EccNandFlash instance. /// \param block Number of the block to write in. /// \param page Number of the page to write inside the given block. /// \param data Data area buffer, can be 0. /// \param spare Spare area buffer, can be 0. //------------------------------------------------------------------------------ unsigned char EccNandFlash_WritePage( struct EccNandFlash *ecc, unsigned short block, unsigned short page, void *data, void *spare) { unsigned char error; unsigned short pageDataSize = NandFlashModel_GetPageDataSize(MODEL(ecc)); unsigned short pageSpareSize = NandFlashModel_GetPageSpareSize(MODEL(ecc)); unsigned char *pSpareBuffer = RawNandFlash_GetSpareBuffer(RAW(ecc)); ASSERT(data || spare, "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(ecc->hamming, 0xFF, NandCommon_MAXSPAREECCBYTES); if (data) { // Compute hamming code on data Hamming_Compute256x(data, pageDataSize, ecc->hamming); } // Store code in spare buffer (if no buffer provided, use a temp. one) if (!spare) { spare = pSpareBuffer; memset(spare, 0xFF, pageSpareSize); } NandSpareScheme_WriteEcc(NandFlashModel_GetScheme(MODEL(ecc)), spare, ecc->hamming); // Perform write operation error = RawNandFlash_WritePage(RAW(ecc), block, page, data, spare); if (error) goto error; #else // Store code in spare buffer (if no buffer provided, use a temp. one) if (!spare) { spare = pSpareBuffer; memset(spare, 0xFF, pageSpareSize); } // Perform write operation error = RawNandFlash_WritePage(RAW(ecc), block, page, data, spare); if (error) goto error; HSMC4_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) goto error; #endif RawNandFlash_ReleaseSpareBuffer(RAW(ecc)); return 0; error: RawNandFlash_ReleaseSpareBuffer(RAW(ecc)); TRACE_ERROR("EccNandFlash_WritePage: Failed to write page\n\r"); return error; }
//------------------------------------------------------------------------------ /// 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. /// Returns 0 if the data has been read and is valid; otherwise returns either /// NandCommon_ERROR_CORRUPTEDDATA or ... /// \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. //------------------------------------------------------------------------------ unsigned char EccNandFlash_ReadPage( struct EccNandFlash *ecc, unsigned short block, unsigned short page, void *data, void *spare) { unsigned char error; unsigned short pageDataSize = NandFlashModel_GetPageDataSize(MODEL(ecc)); unsigned char pageSpareSize = NandFlashModel_GetPageSpareSize(MODEL(ecc)); unsigned char *pDataBuffer; unsigned char *pSpareBuffer; TRACE_DEBUG("EccNandFlash_ReadPage(B#%d:P#%d)\n\r", block, page); pDataBuffer = (data) ? data : RawNandFlash_GetDataBuffer(RAW(ecc)); pSpareBuffer = (spare) ? spare : RawNandFlash_GetSpareBuffer(RAW(ecc)); #ifndef HARDWARE_ECC // Start by reading the spare and the data error = RawNandFlash_ReadPage(RAW(ecc), block, page, pDataBuffer, pSpareBuffer); if (error) { TRACE_ERROR("EccNandFlash_ReadPage: $page %d.%d\n\r", block, page); goto error; } // Retrieve ECC information from page and verify the data NandSpareScheme_ReadEcc(NandFlashModel_GetScheme(MODEL(ecc)), pSpareBuffer, ecc->hamming); error = Hamming_Verify256x(pDataBuffer, pageDataSize, ecc->hamming); #else error = RawNandFlash_ReadPage(RAW(ecc), block, page, (unsigned char*)data, tmpSpare); if (error) { TRACE_ERROR("EccNandFlash_ReadPage: $page %d.%d\n\r", block, page); goto error; } // Retrieve ECC information from page NandSpareScheme_ReadEcc(NandFlashModel_GetScheme(MODEL(ecc)), tmpSpare, ecc->hsiaoInSpare); HSMC4_GetEccParity(pageDataSize, hsiao, NandFlashModel_GetDataBusWidth(MODEL(ecc))); // Verify the data error = HSMC4_VerifyHsiao((unsigned char*) data, pageDataSize, ecc->hsiaoInSpare, ecc->hsiao, NandFlashModel_GetDataBusWidth(MODEL(ecc))); #endif if (error && (error != Hamming_ERROR_SINGLEBIT)) { error = NandCommon_ERROR_CORRUPTEDDATA; TRACE_ERROR("EccNandFlash_ReadPage: $page %d.%d\n\r", block, page); goto error; } #ifndef HARDWARE_ECC #else if (spare) { memcpy(spare, pSpareBuffer, pageSpareSize); } #endif error: if (data == (unsigned char *) 0) RawNandFlash_ReleaseDataBuffer(RAW(ecc)); if (spare == (unsigned char *) 0) RawNandFlash_ReleaseSpareBuffer(RAW(ecc)); return error; }
//------------------------------------------------------------------------------ /// Applet main entry. This function decodes received command and executes it. /// \param argc always 1 /// \param argv Address of the argument area. //------------------------------------------------------------------------------ int main(int argc, char **argv) { struct _Mailbox *pMailbox = (struct _Mailbox *) argv; unsigned int bufferSize, bufferAddr, memoryOffset; unsigned int bytesToWrite; unsigned int bytesRead = 0; unsigned int nbBadBlocks = 0; unsigned int nbBlocks = 0; // Temporary buffer used for non block aligned read / write unsigned int tempBufferAddr; unsigned short block, page, offset, i; // Index in source buffer during buffer copy unsigned int offsetInSourceBuff; // Index in destination buffer during buffer copy unsigned int offsetInTargetBuff; // Errors returned by SkipNandFlash functions unsigned char error = 0; // Configure the DBGU TRACE_CONFIGURE_ISP(DBGU_STANDARD, 115200, BOARD_MCK); // Configure SMC for Nandflash accesses (done each time applet is launched because of old ROM codes) BOARD_ConfigureNandFlash(nfBusWidth); PIO_Configure(pPinsNf, PIO_LISTSIZE(pPinsNf)); // ---------------------------------------------------------- // INIT: // ---------------------------------------------------------- if (pMailbox->command == APPLET_CMD_INIT) { // Save info of communication link comType = pMailbox->argument.inputInit.comType; #if (DYN_TRACES == 1) traceLevel = pMailbox->argument.inputInit.traceLevel; #endif TRACE_INFO("-- NandFlash applet %s --\n\r", SAM_BA_APPLETS_VERSION); TRACE_INFO("-- %s\n\r", BOARD_NAME); TRACE_INFO("INIT command\n\r"); if (pPinsNf->pio == 0) { pMailbox->status = APPLET_NO_DEV; pMailbox->argument.outputInit.bufferSize = 0; pMailbox->argument.outputInit.memorySize = 0; pMailbox->argument.outputInit.bufferAddress = (unsigned int) &end; TRACE_INFO("INIT command: No Nandflash defined for this board\n\r"); } else { memset(&skipBlockNf, 0, sizeof(skipBlockNf)); NandDisableInternalEcc(((struct RawNandFlash *) &skipBlockNf)); if (SkipBlockNandFlash_Initialize(&skipBlockNf, 0, cmdBytesAddr, addrBytesAddr, dataBytesAddr, nfCePin, nfRbPin)) { pMailbox->status = APPLET_DEV_UNKNOWN; pMailbox->argument.outputInit.bufferSize = 0; pMailbox->argument.outputInit.memorySize = 0; TRACE_INFO("\tDevice Unknown\n\r"); } else { // Check the data bus width of the NandFlash nfBusWidth = NandFlashModel_GetDataBusWidth((struct NandFlashModel *)&skipBlockNf); // Reconfigure bus width BOARD_ConfigureNandFlash(nfBusWidth); TRACE_INFO("\tNandflash driver initialized\n\r"); #if defined (at91sam3u) pMailbox->argument.outputInit.bufferAddress = 0x60000000; #elif defined (at91sam7se) pMailbox->argument.outputInit.bufferAddress = 0x21000000; #else pMailbox->argument.outputInit.bufferAddress = (unsigned int) &end; #endif // Get device parameters memSize = NandFlashModel_GetDeviceSizeInBytes(&skipBlockNf.ecc.raw.model); blockSize = NandFlashModel_GetBlockSizeInBytes(&skipBlockNf.ecc.raw.model); numBlocks = NandFlashModel_GetDeviceSizeInBlocks(&skipBlockNf.ecc.raw.model); pageSize = NandFlashModel_GetPageDataSize(&skipBlockNf.ecc.raw.model); numPagesPerBlock = NandFlashModel_GetBlockSizeInPages(&skipBlockNf.ecc.raw.model); pMailbox->status = APPLET_SUCCESS; pMailbox->argument.outputInit.bufferSize = blockSize; pMailbox->argument.outputInit.memorySize = memSize; TRACE_INFO("\t pageSize : 0x%x blockSize : 0x%x blockNb : 0x%x bus width : %d\n\r", pageSize, blockSize, numBlocks, nfBusWidth); TRACE_INFO("\t bufferAddr : 0x%x\n\r", (unsigned int) &end); } } } // ---------------------------------------------------------- // WRITE: // ---------------------------------------------------------- else if (pMailbox->command == APPLET_CMD_WRITE) { memoryOffset = pMailbox->argument.inputWrite.memoryOffset; bufferAddr = pMailbox->argument.inputWrite.bufferAddr; tempBufferAddr = bufferAddr + blockSize; bytesToWrite = pMailbox->argument.inputWrite.bufferSize; TRACE_INFO("WRITE arguments : offset 0x%x, buffer at 0x%x, of 0x%x Bytes\n\r", memoryOffset, bufferAddr, bytesToWrite); pMailbox->argument.outputWrite.bytesWritten = 0; // Check word alignment if (memoryOffset % 4) { pMailbox->status = APPLET_ALIGN_ERROR; goto exit; } // Retrieve page and block addresses if (NandFlashModel_TranslateAccess(&(skipBlockNf.ecc.raw.model), memoryOffset, bytesToWrite, &block, &page, &offset)) { pMailbox->status = APPLET_FAIL; goto exit; } TRACE_INFO("WRITE at block 0x%x, page 0x%x, offset in page 0x%x\n\r", block, page, offset); if (page || offset || (bytesToWrite < blockSize)) { // We are not block aligned, retrieve block content to update it memset((unsigned int *)tempBufferAddr, 0xFF, blockSize); error = SkipBlockNandFlash_ReadBlock(&skipBlockNf, block, (unsigned int *)tempBufferAddr); if (error == NandCommon_ERROR_BADBLOCK) { pMailbox->status = APPLET_BAD_BLOCK; goto exit; } if (error) { pMailbox->status = APPLET_FAIL; goto exit; } // Fill retrieved block with data to be programmed offsetInTargetBuff = (page * pageSize) + offset; offsetInSourceBuff = 0; while ((offsetInTargetBuff < blockSize) && (bytesToWrite > 0)) { *(unsigned int *)(tempBufferAddr + offsetInTargetBuff) = *(unsigned int *)(bufferAddr + offsetInSourceBuff); offsetInSourceBuff += 4; offsetInTargetBuff += 4; bytesToWrite -= 4; } } else { // Write a full and aligned block tempBufferAddr = bufferAddr; bytesToWrite = 0; } // Erase target block error = SkipBlockNandFlash_EraseBlock(&skipBlockNf, block, NORMAL_ERASE); if (error == NandCommon_ERROR_BADBLOCK) { pMailbox->status = APPLET_BAD_BLOCK; goto exit; } if (error) { pMailbox->status = APPLET_FAIL; goto exit; } // Write target block error = SkipBlockNandFlash_WriteBlock(&skipBlockNf, block, (unsigned int *)tempBufferAddr); if (error == NandCommon_ERROR_BADBLOCK) { pMailbox->status = APPLET_BAD_BLOCK; goto exit; } if (error) { pMailbox->status = APPLET_FAIL; goto exit; } pMailbox->argument.outputWrite.bytesWritten = pMailbox->argument.inputWrite.bufferSize - bytesToWrite; pMailbox->status = APPLET_SUCCESS; } // ---------------------------------------------------------- // READ: // ---------------------------------------------------------- else if (pMailbox->command == APPLET_CMD_READ) { memoryOffset = pMailbox->argument.inputRead.memoryOffset; bufferAddr = pMailbox->argument.inputRead.bufferAddr; tempBufferAddr = bufferAddr + blockSize; bufferSize = pMailbox->argument.inputRead.bufferSize; TRACE_INFO("READ at offset: 0x%x buffer at : 0x%x of: 0x%x Bytes\n\r", memoryOffset, bufferAddr, bufferSize); pMailbox->argument.outputRead.bytesRead = 0; // Check word alignment if (memoryOffset % 4) { pMailbox->status = APPLET_ALIGN_ERROR; goto exit; } // Retrieve page and block addresses if (NandFlashModel_TranslateAccess(&(skipBlockNf.ecc.raw.model), memoryOffset, bufferSize, &block, &page, &offset)) { pMailbox->status = APPLET_FAIL; goto exit; } TRACE_INFO("READ at block 0x%x, page 0x%x, offset in page 0x%x\n\r", block, page, offset); if (page || offset) { memset((unsigned int *)tempBufferAddr, 0xFF, blockSize); error = SkipBlockNandFlash_ReadBlock(&skipBlockNf, block, (unsigned int *)tempBufferAddr); if (error == NandCommon_ERROR_BADBLOCK) { pMailbox->status = APPLET_BAD_BLOCK; goto exit; } if (error) { pMailbox->status = APPLET_FAIL; goto exit; } // Fill dest buffer with read data offsetInSourceBuff = (page * pageSize) + offset; offsetInTargetBuff = 0; while ((offsetInSourceBuff < blockSize) && (offsetInTargetBuff < blockSize) && (bytesRead < bufferSize)) { *(unsigned int *)(bufferAddr + offsetInTargetBuff) = *(unsigned int *)(tempBufferAddr + offsetInSourceBuff); offsetInSourceBuff += 4; offsetInTargetBuff += 4; bytesRead += 4; } pMailbox->argument.outputRead.bytesRead = bytesRead; pMailbox->status = APPLET_SUCCESS; } else { memset((unsigned int *)bufferAddr, 0xFF, blockSize); error = SkipBlockNandFlash_ReadBlock(&skipBlockNf, block, (unsigned int *)bufferAddr); if (error == NandCommon_ERROR_BADBLOCK) { pMailbox->status = APPLET_BAD_BLOCK; goto exit; } if (error) { pMailbox->status = APPLET_FAIL; goto exit; } pMailbox->argument.outputRead.bytesRead = bufferSize; pMailbox->status = APPLET_SUCCESS; } } // ---------------------------------------------------------- // FULL ERASE: // ---------------------------------------------------------- else if (pMailbox->command == APPLET_CMD_FULL_ERASE) { TRACE_INFO("FULL ERASE command\n\r"); TRACE_INFO("\tForce erase flag: 0x%x\n\r", pMailbox->argument.inputFullErase.eraseType); for (i = 0; i < numBlocks; i++) { // Erase the block if (SkipBlockNandFlash_EraseBlock(&skipBlockNf, i, pMailbox->argument.inputFullErase.eraseType)) { TRACE_INFO("Found block #%d BAD, skip it\n\r", i); } } TRACE_INFO("Full Erase achieved\n\r"); pMailbox->status = APPLET_SUCCESS; } // ---------------------------------------------------------- // BATCH FULL ERASE: // ---------------------------------------------------------- else if (pMailbox->command == APPLET_CMD_BATCH_ERASE) { TRACE_INFO("BATCH ERASE command\n\r"); block = pMailbox->argument.inputBatchErase.batch * (numBlocks / ERASE_BATCH); TRACE_INFO("Erase block from #%d to #%d\n\r", block, block + (numBlocks / ERASE_BATCH)); for (i = block ; i < block + (numBlocks / ERASE_BATCH) ; i++) { // Erase the block if (SkipBlockNandFlash_EraseBlock(&skipBlockNf, i, pMailbox->argument.inputBatchErase.eraseType)) { TRACE_INFO("Found block #%d BAD, skip it\n\r", i); } } if ((pMailbox->argument.inputBatchErase.batch + 1) == ERASE_BATCH) { TRACE_INFO("Full Erase achieved, erase type is %d\n\r", pMailbox->argument.inputBatchErase.eraseType); pMailbox->argument.outputBatchErase.nextBatch = 0; } else { pMailbox->argument.outputBatchErase.nextBatch = pMailbox->argument.inputBatchErase.batch + 1; TRACE_INFO("Batch Erase achieved\n\r"); } pMailbox->status = APPLET_SUCCESS; } // ---------------------------------------------------------- // FULL ERASE: // ---------------------------------------------------------- else if (pMailbox->command == APPLET_CMD_ERASE_BLOCKS) { TRACE_INFO("BLOCKS ERASE command\n\r"); memoryOffset = pMailbox->argument.inputBlocksErase.memoryOffsetStart; if ((pMailbox->argument.inputBlocksErase.memoryOffsetEnd > memSize) || (pMailbox->argument.inputBlocksErase.memoryOffsetEnd < memoryOffset) ) { TRACE_INFO("Out of memory space\n\r"); pMailbox->status = APPLET_ERASE_FAIL; goto exit; } nbBlocks = ((pMailbox->argument.inputBlocksErase.memoryOffsetEnd- memoryOffset)/ blockSize) + 1; TRACE_INFO("Erase blocks from %d to %d \n\r", memoryOffset / blockSize, (memoryOffset / blockSize)+ nbBlocks ); // Erase blocks for (i = memoryOffset / blockSize; i < memoryOffset / blockSize + nbBlocks ; i++) { if (SkipBlockNandFlash_EraseBlock(&skipBlockNf, i , NORMAL_ERASE)) { TRACE_INFO("Found block #%d BAD, skip it\n\r", i); } } TRACE_INFO("Blocks Erase achieved\n\r"); pMailbox->status = APPLET_SUCCESS; } // ---------------------------------------------------------- // LIST BAD BLOCKS: // ---------------------------------------------------------- else if (pMailbox->command == APPLET_CMD_LIST_BAD_BLOCKS) { TRACE_INFO("LIST BAD BLOCKS command\n\r"); nbBadBlocks = 0; bufferAddr = (unsigned int) &end; pMailbox->argument.outputListBadBlocks.bufferAddress = bufferAddr; for (i = 0; i < numBlocks; i++) { // Erase the page if (SkipBlockNandFlash_CheckBlock(&skipBlockNf, i) == BADBLOCK) { nbBadBlocks++; *((unsigned int *)bufferAddr) = i; bufferAddr += 4; TRACE_INFO("Found block #%d BAD\n\r", i); } } TRACE_INFO("LIST BAD BLOCKS achieved\n\r"); pMailbox->argument.outputListBadBlocks.nbBadBlocks = nbBadBlocks; pMailbox->status = APPLET_SUCCESS; } // ---------------------------------------------------------- // TAG BLOCK: // ---------------------------------------------------------- else if (pMailbox->command == APPLET_CMD_TAG_BLOCK) { TRACE_INFO("TAG BLOCK command\n\r"); bufferAddr = (unsigned int) &end; block = pMailbox->argument.inputTagBlock.blockId; // To tag the block as good, just erase it without bad block check if ((unsigned char)pMailbox->argument.inputTagBlock.tag == 0xFF) { if (SkipBlockNandFlash_EraseBlock(&skipBlockNf, block, SCRUB_ERASE)) { TRACE_INFO("Cannot erase block %d\n\r", block); pMailbox->status = APPLET_FAIL; goto exit; } } else { for (i = 0; i < 2; i++) { // Start by reading the spare memset((unsigned char *)bufferAddr, 0xFF, NandCommon_MAXSPAREECCBYTES); TRACE_INFO("Tag to write : 0x%x\n\r", (unsigned char)pMailbox->argument.inputTagBlock.tag); NandSpareScheme_WriteBadBlockMarker((struct NandSpareScheme *)(NandFlashModel_GetScheme((struct NandFlashModel *)(&skipBlockNf))), (unsigned char *)bufferAddr, ((unsigned char)pMailbox->argument.inputTagBlock.tag)); if (RawNandFlash_WritePage((struct RawNandFlash *)(&skipBlockNf), block, i, 0, (unsigned char *)bufferAddr)) { TRACE_ERROR("Failed to write spare data of page %d of block %d\n\r", i, block); pMailbox->status = APPLET_FAIL; goto exit; } } } TRACE_INFO("TAG BLOCK achieved\n\r"); pMailbox->status = APPLET_SUCCESS; } exit : // Acknowledge the end of command TRACE_INFO("\tEnd of applet (command : %x --- status : %x)\n\r", pMailbox->command, pMailbox->status); // Notify the host application of the end of the command processing pMailbox->command = ~(pMailbox->command); // Send ACK character if (comType == DBGU_COM_TYPE) { DBGU_PutChar(0x6); } return 0; }
//------------------------------------------------------------------------------ /// 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. /// Returns 0 if the data has been read and is valid; otherwise returns either /// NandCommon_ERROR_CORRUPTEDDATA or ... /// \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. //------------------------------------------------------------------------------ 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 char tmpNoEcc; 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 data error = RawNandFlash_ReadPage(RAW(ecc), block, page, 0, tmpSpare); if (error) { TRACE_ERROR("EccNandFlash_ReadPage: Failed to read page\n\r"); return error; } // Then reading the data error = RawNandFlash_ReadPage(RAW(ecc), block, page, tmpData, 0); if (error) { TRACE_ERROR("EccNandFlash_ReadPage: Failed to read page\n\r"); return error; } tmpNoEcc = EccNandlfash_GetNoECC(); if(!tmpNoEcc){ // 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; } HSMC4_GetEccParity(pageDataSize, hsiao, NandFlashModel_GetDataBusWidth(MODEL(ecc))); error = HSMC4_VerifyHsiao((unsigned char*) data, pageDataSize, hsiaoInSpare, hsiao, NandFlashModel_GetDataBusWidth(MODEL(ecc))); #endif if (error && (error != Hamming_ERROR_SINGLEBIT) && (!tmpNoEcc)) { 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 Applet main entry. This function decodes received command and executes it. * * \param argc always 1 * \param argv Address of the argument area.. */ int main(int argc, char **argv) { struct _Mailbox *pMailbox = (struct _Mailbox *) argv; uint32_t bufferSize, bufferAddr, memoryOffset, bytesToWrite; uint16_t pagesToWrite, pagesToRead; uint32_t bytesWritten = 0; uint32_t bytesRead = 0; uint32_t nbBadBlocks = 0; uint32_t nbBlocks = 0; /* Temporary buffer used for non block aligned read / write */ uint32_t tempBufferAddr; uint16_t block, page, offset, i; uint8_t unAlignedPage; /* Index in source buffer during buffer copy */ uint32_t offsetInSourceBuff; /* Index in destination buffer during buffer copy */ uint32_t offsetInTargetBuff; /* Errors returned by SkipNandFlash functions */ uint8_t error = 0; /* Global DMA driver instance for all DMA transfers in application. */ sDmad dmad; /*---------------------------------------------------------- * INIT: *----------------------------------------------------------*/ if (pMailbox->command == APPLET_CMD_INIT) { /* Save info of communication link */ comType = pMailbox->argument.inputInit.comType; /* Re-configurate UART (MCK maybe change in LowLevelInit()) */ UART_Configure(115200, BOARD_MCK); #if (DYN_TRACES == 1) dwTraceLevel = pMailbox->argument.inputInit.traceLevel; #endif TRACE_INFO("-- NandFlash SAM-BA applet %s --\n\r", SAM_BA_APPLETS_VERSION); TRACE_INFO("-- %s\n\r", BOARD_NAME); TRACE_INFO("-- Compiled: %s %s --\n\r", __DATE__, __TIME__); TRACE_INFO("INIT command\n\r"); /* Initialize DMA driver instance with polling mode */ DMAD_Initialize( &dmad, 1 ); if ( NandFlashConfigureDmaChannels( &dmad )) { TRACE_INFO ("-E- Initialize DMA failed !"); } TRACE_INFO ("-I- Initialize DMA done.\n\r"); /* Configure SMC for Nandflash accesses */ BOARD_ConfigureNandFlash(SMC); PIO_PinConfigure(pPinsNf, PIO_LISTSIZE(pPinsNf)); if (pPinsNf->pio == 0) { pMailbox->status = APPLET_NO_DEV; pMailbox->argument.outputInit.bufferSize = 0; pMailbox->argument.outputInit.memorySize = 0; pMailbox->argument.outputInit.bufferAddress = (uint32_t) &end; TRACE_INFO("INIT command: No Nandflash defined for this board\n\r"); } else { memset(&skipBlockNf, 0, sizeof(skipBlockNf)); if (SkipBlockNandFlash_Initialize(&skipBlockNf, 0, cmdBytesAddr, addrBytesAddr, dataBytesAddr, nfCePin, nfRbPin)) { pMailbox->status = APPLET_DEV_UNKNOWN; pMailbox->argument.outputInit.bufferSize = 0; pMailbox->argument.outputInit.memorySize = 0; TRACE_INFO("\tDevice Unknown\n\r"); } else { TRACE_INFO("\tNandflash driver initialized\n\r"); /* Get device parameters */ memSize = NandFlashModel_GetDeviceSizeInBytes(&skipBlockNf.ecc.raw.model); blockSize = NandFlashModel_GetBlockSizeInBytes(&skipBlockNf.ecc.raw.model); numBlocks = NandFlashModel_GetDeviceSizeInBlocks(&skipBlockNf.ecc.raw.model); pageSize = NandFlashModel_GetPageDataSize(&skipBlockNf.ecc.raw.model); numPagesPerBlock = NandFlashModel_GetBlockSizeInPages(&skipBlockNf.ecc.raw.model); latestErasedBlock = 0xFFFF; pMailbox->status = APPLET_SUCCESS; pMailbox->argument.outputInit.bufferAddress = (uint32_t)EBI_SDRAMC_ADDR; pMailbox->argument.outputInit.bufferSize = blockSize; pMailbox->argument.outputInit.memorySize = memSize; TRACE_INFO("\t pageSize : 0x%x blockSize : 0x%x blockNb : 0x%x \n\r", (unsigned int)pageSize, (unsigned int)blockSize, (unsigned int)numBlocks); } } } /*---------------------------------------------------------- * WRITE: *----------------------------------------------------------*/ else if (pMailbox->command == APPLET_CMD_WRITE) { memoryOffset = pMailbox->argument.inputWrite.memoryOffset; bufferAddr = pMailbox->argument.inputWrite.bufferAddr; tempBufferAddr = bufferAddr + blockSize; bytesToWrite = pMailbox->argument.inputWrite.bufferSize; unAlignedPage = 0; TRACE_INFO("WRITE arguments : offset 0x%x, buffer at 0x%x, of 0x%x Bytes\n\r", (unsigned int)memoryOffset, (unsigned int)bufferAddr, (unsigned int)bytesToWrite); pMailbox->argument.outputWrite.bytesWritten = 0; /* Check word alignment */ if (memoryOffset % 4) { pMailbox->status = APPLET_ALIGN_ERROR; goto exit; } /* Retrieve page and block addresses */ if (NandFlashModel_TranslateAccess(&(skipBlockNf.ecc.raw.model), memoryOffset, bytesToWrite, &block, &page, &offset)) { pMailbox->status = APPLET_FAIL; goto exit; } if (block != latestErasedBlock ){ /* Erase target block */ error = SkipBlockNandFlash_EraseBlock(&skipBlockNf, block, NORMAL_ERASE); if (error == NandCommon_ERROR_BADBLOCK) { pMailbox->status = APPLET_BAD_BLOCK; goto exit; } if (error) { pMailbox->status = APPLET_FAIL; goto exit; } latestErasedBlock = block; latestErasedPage = 0xff; } if (page <= ((uint8_t)(latestErasedPage + 1))){ error = SkipBlockNandFlash_EraseBlock(&skipBlockNf, block, NORMAL_ERASE); if (error == NandCommon_ERROR_BADBLOCK) { pMailbox->status = APPLET_BAD_BLOCK; goto exit; } if (error) { pMailbox->status = APPLET_FAIL; goto exit; } latestErasedBlock = block; latestErasedPage = page; } offsetInSourceBuff = 0; TRACE_INFO("WRITE at block 0x%x, page 0x%x, offset 0x%x in page \n\r", (unsigned int)block, (unsigned int)page, (unsigned int)offset); if (offset ) { /* We are not page aligned. */ offsetInTargetBuff = offset; memset((uint32_t *)tempBufferAddr, 0xFF, pageSize); while (offsetInTargetBuff < pageSize) { *(uint32_t *)(tempBufferAddr + offsetInTargetBuff) = *(uint32_t *)(bufferAddr + offsetInSourceBuff); offsetInSourceBuff += 4; offsetInTargetBuff += 4; bytesWritten += 4; } error = SkipBlockNandFlash_WritePage(&skipBlockNf, block, page, ( void *)tempBufferAddr ,0); if (error == NandCommon_ERROR_BADBLOCK) { pMailbox->status = APPLET_BAD_BLOCK; goto exit; } if (error) { pMailbox->status = APPLET_FAIL; goto exit; } unAlignedPage++; } pagesToWrite = (bytesToWrite - bytesWritten) / pageSize; if (bytesToWrite - ((pagesToWrite + unAlignedPage) * pageSize)) { pagesToWrite++; } if ((pagesToWrite + page ) > numPagesPerBlock) { pagesToWrite = numPagesPerBlock - page; } /* Write target block */ error = SkipBlockNandFlash_WriteBlockUnaligned(&skipBlockNf, block, (page + unAlignedPage), pagesToWrite, ( void *)(bufferAddr + offsetInSourceBuff)); bytesWritten += pagesToWrite * pageSize; if (bytesWritten > bytesToWrite) { bytesWritten = bytesToWrite; } if (error == NandCommon_ERROR_BADBLOCK) { pMailbox->status = APPLET_BAD_BLOCK; goto exit; } if (error) { pMailbox->status = APPLET_FAIL; goto exit; } pMailbox->argument.outputWrite.bytesWritten = bytesWritten; pMailbox->status = APPLET_SUCCESS; } /*---------------------------------------------------------- * READ: *----------------------------------------------------------*/ else if (pMailbox->command == APPLET_CMD_READ) { memoryOffset = pMailbox->argument.inputRead.memoryOffset; bufferAddr = pMailbox->argument.inputRead.bufferAddr; tempBufferAddr = bufferAddr + blockSize; bufferSize = pMailbox->argument.inputRead.bufferSize; TRACE_INFO("READ at offset: 0x%x buffer at : 0x%x of: 0x%x Bytes\n\r", (unsigned int)memoryOffset, (unsigned int)bufferAddr, (unsigned int)bufferSize); pMailbox->argument.outputRead.bytesRead = 0; unAlignedPage = 0; /* Check word alignment */ if (memoryOffset % 4) { pMailbox->status = APPLET_ALIGN_ERROR; goto exit; } /* Retrieve page and block addresses */ if (NandFlashModel_TranslateAccess(&(skipBlockNf.ecc.raw.model), memoryOffset, bufferSize, &block, &page, &offset)) { pMailbox->status = APPLET_FAIL; goto exit; } TRACE_INFO("READ at block 0x%x, page 0x%x, offset in page 0x%x\n\r", (unsigned int)block, (unsigned int)page, (unsigned int)offset); offsetInTargetBuff = 0; if (offset) { memset((uint32_t *)tempBufferAddr, 0xFF, pageSize); error = SkipBlockNandFlash_ReadPage(&skipBlockNf, block, page, ( void *)tempBufferAddr ,0); if (error == NandCommon_ERROR_BADBLOCK) { pMailbox->status = APPLET_BAD_BLOCK; goto exit; } if (error) { pMailbox->status = APPLET_FAIL; goto exit; } /* Fill dest buffer with read data */ offsetInSourceBuff = offset; while (offsetInSourceBuff < pageSize) { *(uint32_t *)(bufferAddr + offsetInTargetBuff) = *(uint32_t *)(tempBufferAddr + offsetInSourceBuff); offsetInSourceBuff += 4; offsetInTargetBuff += 4; bytesRead += 4; } unAlignedPage++; } pagesToRead = (bufferSize - bytesRead) / pageSize; if (bufferSize - ((pagesToRead + unAlignedPage)* pageSize)) { pagesToRead++; } if ((pagesToRead + page ) > numPagesPerBlock) { pagesToRead = numPagesPerBlock - page; } /* Read target block */ error = SkipBlockNandFlash_ReadBlockUnaligned(&skipBlockNf, block, (page + unAlignedPage), pagesToRead, ( void *)(bufferAddr + offsetInTargetBuff)); bytesRead += pagesToRead * pageSize; if (bytesRead > bufferSize) { bytesRead = bufferSize; } if (error == NandCommon_ERROR_BADBLOCK) { pMailbox->status = APPLET_BAD_BLOCK; goto exit; } if (error) { pMailbox->status = APPLET_FAIL; goto exit; } pMailbox->argument.outputRead.bytesRead = bytesRead; pMailbox->status = APPLET_SUCCESS; } /*---------------------------------------------------------- * FULL ERASE: *----------------------------------------------------------*/ else if (pMailbox->command == APPLET_CMD_FULL_ERASE) { TRACE_INFO("FULL ERASE command\n\r"); TRACE_INFO("\tForce erase flag: 0x%x\n\r", (unsigned int)pMailbox->argument.inputFullErase.eraseType); for (i = 0; i < numBlocks; i++) { /* Erase the page */ if (SkipBlockNandFlash_EraseBlock(&skipBlockNf, i, pMailbox->argument.inputFullErase.eraseType)) { TRACE_INFO("Found block #%d BAD, skip it\n\r", (unsigned int)i); } } TRACE_INFO("Full Erase achieved\n\r"); pMailbox->status = APPLET_SUCCESS; } /*---------------------------------------------------------- * BATCH FULL ERASE: *----------------------------------------------------------*/ else if (pMailbox->command == APPLET_CMD_BATCH_ERASE) { TRACE_INFO("BATCH ERASE command\n\r"); block = pMailbox->argument.inputBatchErase.batch * (numBlocks / ERASE_BATCH); TRACE_INFO("Erase block from #%d to #%d\n\r", (unsigned int)block, (unsigned int)(block + (numBlocks / ERASE_BATCH))); for (i = block ; i < block + (numBlocks / ERASE_BATCH) ; i++) { /* Erase the block */ if (SkipBlockNandFlash_EraseBlock(&skipBlockNf, i, pMailbox->argument.inputBatchErase.eraseType)) { TRACE_INFO("Found block #%d BAD, skip it\n\r", (unsigned int)i); } } if ((pMailbox->argument.inputBatchErase.batch + 1) == ERASE_BATCH) { TRACE_INFO("Full Erase achieved, erase type is %d\n\r", (unsigned int)pMailbox->argument.inputBatchErase.eraseType); pMailbox->argument.outputBatchErase.nextBatch = 0; } else { pMailbox->argument.outputBatchErase.nextBatch = pMailbox->argument.inputBatchErase.batch + 1; TRACE_INFO("Batch Erase achieved\n\r"); } pMailbox->status = APPLET_SUCCESS; } /*---------------------------------------------------------- * ERASE_BLOCKS: *----------------------------------------------------------*/ else if (pMailbox->command == APPLET_CMD_ERASE_BLOCKS) { TRACE_INFO("BLOCKS ERASE command\n\r"); memoryOffset = pMailbox->argument.inputBlocksErase.memoryOffsetStart; if ((pMailbox->argument.inputBlocksErase.memoryOffsetEnd > memSize) || (pMailbox->argument.inputBlocksErase.memoryOffsetEnd < memoryOffset) ) { TRACE_INFO("Out of memory space\n\r"); pMailbox->status = APPLET_ERASE_FAIL; goto exit; } nbBlocks = ((pMailbox->argument.inputBlocksErase.memoryOffsetEnd- memoryOffset)/ blockSize) + 1; TRACE_INFO("Erase blocks from %d to %d \n\r", (unsigned int)(memoryOffset / blockSize),(unsigned int)((memoryOffset / blockSize)+ nbBlocks) ); /* Erase blocks */ for (i = memoryOffset / blockSize; i < memoryOffset / blockSize + nbBlocks ; i++) { if (SkipBlockNandFlash_EraseBlock(&skipBlockNf, i , NORMAL_ERASE)) { TRACE_INFO("Found block #%d BAD, skip it\n\r", (unsigned int)i); } } TRACE_INFO("Blocks Erase achieved\n\r"); pMailbox->status = APPLET_SUCCESS; } /*---------------------------------------------------------- * LIST BAD BLOCKS: *----------------------------------------------------------*/ else if (pMailbox->command == APPLET_CMD_LIST_BAD_BLOCKS) { TRACE_INFO("LIST BAD BLOCKS command\n\r"); nbBadBlocks = 0; bufferAddr = (uint32_t) &end; pMailbox->argument.outputListBadBlocks.bufferAddress = bufferAddr; for (i = 0; i < numBlocks; i++) { /* Erase the page */ if (SkipBlockNandFlash_CheckBlock(&skipBlockNf, i) == BADBLOCK) { nbBadBlocks++; *((uint32_t *)bufferAddr) = i; bufferAddr += 4; TRACE_INFO("Found block #%d BAD\n\r", i); } } TRACE_INFO("LIST BAD BLOCKS achieved\n\r"); pMailbox->argument.outputListBadBlocks.nbBadBlocks = nbBadBlocks; pMailbox->status = APPLET_SUCCESS; } /*---------------------------------------------------------- * TAG BLOCK: *----------------------------------------------------------*/ else if (pMailbox->command == APPLET_CMD_TAG_BLOCK) { TRACE_INFO("TAG BLOCK command\n\r"); bufferAddr = (uint32_t) &end; block = pMailbox->argument.inputTagBlock.blockId; /* To tag the block as good, just erase it without bad block check */ if ((uint8_t)pMailbox->argument.inputTagBlock.tag == 0xFF) { if (SkipBlockNandFlash_EraseBlock(&skipBlockNf, block, SCRUB_ERASE)) { TRACE_INFO("Cannot erase block %d\n\r", (unsigned int)block); pMailbox->status = APPLET_FAIL; goto exit; } } else { for (i = 0; i < 2; i++) { /* Start by reading the spare */ memset((uint8_t *)bufferAddr, 0xFF, NandCommon_MAXSPAREECCBYTES); TRACE_INFO("Tag to write : 0x%x\n\r", (unsigned int)pMailbox->argument.inputTagBlock.tag); NandSpareScheme_WriteBadBlockMarker((struct NandSpareScheme *)(NandFlashModel_GetScheme((struct NandFlashModel *)(&skipBlockNf))), (uint8_t *)bufferAddr, ((uint8_t)pMailbox->argument.inputTagBlock.tag)); if (RawNandFlash_WritePage((struct RawNandFlash *)(&skipBlockNf), block, i, 0, (uint8_t *)bufferAddr)) { TRACE_ERROR("Failed to write spare data of page %d of block %d\n\r", i, block); pMailbox->status = APPLET_FAIL; goto exit; } } } TRACE_INFO("TAG BLOCK achieved\n\r"); pMailbox->status = APPLET_SUCCESS; } exit : /* Acknowledge the end of command */ TRACE_INFO("\tEnd of applet (command : %x --- status : %x)\n\r", (unsigned int)pMailbox->command, (unsigned int)pMailbox->status); /* Notify the host application of the end of the command processing */ pMailbox->command = ~(pMailbox->command); if (comType == DBGU_COM_TYPE) { UART_PutChar(0x6); } 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; } HSMC4_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; }
/** * \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, gdwNandFlashTempBuffer, 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(gdwNandFlashTempBuffer, pageDataSize, hamming); #else error = RawNandFlash_ReadPage(RAW(ecc), block, page, (unsigned char*)data, tmpSpare); if (error) { TRACE_ERROR("EccNandFlash_ReadPage: $page %d.%d\n\r", block, page); return error; } /* Retrieve ECC information from page */ NandSpareScheme_ReadEcc(NandFlashModel_GetScheme(MODEL(ecc)), tmpSpare, hsiaoInSpare); HSMC4_GetEccParity(pageDataSize, hsiao, NandFlashModel_GetDataBusWidth(MODEL(ecc))); /* Verify the data */ error = HSMC4_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, gdwNandFlashTempBuffer, pageDataSize); } if (spare) { memcpy(spare, tmpSpare, pageSpareSize); } #else if (spare) { memcpy(spare, tmpSpare, pageSpareSize); } #endif return 0; }