//------------------------------------------------------------------------------ /// Copies the data from a whole block to another block on a nandflash. Both /// blocks must be LIVE. /// Returns 0 if successful; otherwise returns NandCommon_ERROR_WRONGSTATUS if /// at least one of the blocks is not free, or a NandCommon_ERROR_xxx code. /// \param managed Pointer to a ManagedNandFlash instance. /// \param sourceBlock Source block number. /// \param destBlock Destination block number. //------------------------------------------------------------------------------ unsigned char ManagedNandFlash_CopyBlock( const struct ManagedNandFlash *managed, unsigned short sourceBlock, unsigned short destBlock) { unsigned short numPages = NandFlashModel_GetBlockSizeInPages(MODEL(managed)); unsigned char error; unsigned short page; ASSERT(sourceBlock != destBlock, "ManagedNandFlash_CopyBlock: Source block must be different from dest. block\n\r"); TRACE_INFO("ManagedNandFlash_CopyBlock(B#%d->B#%d)\n\r", sourceBlock, destBlock); // Copy all pages for (page=0; page < numPages; page++) { error = ManagedNandFlash_CopyPage(managed, sourceBlock, page, destBlock, page); if (error) { TRACE_ERROR("ManagedNandFlash_CopyPage: Failed to copy page %d\n\r", page); return error; } } return 0; }
/** * \brief Marks the given page as being dirty (i.e. written). * * \param translated Pointer to a TranslatedNandFlash instance. * \param page Page number. */ static void MarkPageDirty( struct TranslatedNandFlash *translated, unsigned short page) { assert( page < NandFlashModel_GetBlockSizeInPages(MODEL(translated)) ) ; /* "PageIsClean: Page out-of-bounds\n\r" */ translated->currentBlockPageStatuses[page / 8] |= 1 << (page % 8); }
/** * \brief Check the given page inside the currently written block is clean or not. * * \param translated Pointer to a TranslatedNandFlash instance. * \param page Page number. * \return 1 if the given page inside the currently written block is clean (has * not been written yet); otherwise returns 0. */ static unsigned char PageIsClean( const struct TranslatedNandFlash *translated, unsigned short page) { assert( page < NandFlashModel_GetBlockSizeInPages(MODEL(translated)) ) ; /* "PageIsClean: Page out-of-bounds\n\r" */ return ((translated->currentBlockPageStatuses[page / 8] >> (page % 8)) & 1) == 0; }
/** * \brief Terminates the current write operation by copying all the missing pages from * the previous physical block. * * \param translated Pointer to a TranslatedNandFlash instance. * \return 0 if successful; otherwise returns error code */ unsigned char TranslatedNandFlash_Flush(struct TranslatedNandFlash *translated) { unsigned int i; unsigned char error; unsigned int currentPhysicalBlock; /* Check if there is a current block and a previous block*/ if ((translated->currentLogicalBlock == -1) || (translated->previousPhysicalBlock == -1)) { return 0; } TRACE_INFO("TranslatedNandFlash_Flush(PB#%d -> LB#%d)\n\r", translated->previousPhysicalBlock, translated->currentLogicalBlock); /* Copy missing pages in the current block*/ currentPhysicalBlock = MappedNandFlash_LogicalToPhysical( MAPPED(translated), translated->currentLogicalBlock); for (i=0; i < NandFlashModel_GetBlockSizeInPages(MODEL(translated)); i++) { if (PageIsClean(translated, i)) { TRACE_DEBUG("Copying back page #%d of block #%d\n\r", i, translated->previousPhysicalBlock); /* Copy page*/ error = ManagedNandFlash_CopyPage(MANAGED(translated), translated->previousPhysicalBlock, i, currentPhysicalBlock, i); if (error) { TRACE_ERROR("FinishCurrentWrite: copy page #%d\n\r", i); return error; } } } translated->currentLogicalBlock = -1; translated->previousPhysicalBlock = -1; 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(). */ unsigned char SkipBlockNandFlash_ReadBlockUnaligned( const struct SkipBlockNandFlash *skipBlock, unsigned short block, unsigned short pageOffsetInBlock, unsigned short numPages, void *data ) { /* Number of pages per block*/ unsigned int numPagesPerBlock; /* Page size*/ unsigned int pageSize; /* Page index*/ unsigned short i; /* Error returned by SkipBlockNandFlash_ReadPage*/ unsigned char error = 0; /* Retrieve model information*/ pageSize = NandFlashModel_GetPageDataSize(MODEL(skipBlock)); numPagesPerBlock = NandFlashModel_GetBlockSizeInPages(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 *) ((unsigned char *) 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 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 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(). */ unsigned char SkipBlockNandFlash_ReadBlock( const struct SkipBlockNandFlash *skipBlock, unsigned short block, void *data) { /* Number of pages per block */ unsigned int numPagesPerBlock, pageSize; /* Page index */ unsigned short i; /* Error returned by SkipBlockNandFlash_WritePage */ unsigned char error = 0; /* Retrieve model information */ pageSize = NandFlashModel_GetPageDataSize(MODEL(skipBlock)); numPagesPerBlock = NandFlashModel_GetBlockSizeInPages(MODEL(skipBlock)); /* Check that the block is not BAD if data is requested */ if (SkipBlockNandFlash_CheckBlock(skipBlock, block) != GOODBLOCK) { TRACE_ERROR("SkipBlockNandFlash_ReadBlock: Block is BAD.\n\r"); return NandCommon_ERROR_BADBLOCK; } /* Read all the pages of the block */ for (i = 0; i < numPagesPerBlock; i++) { error = EccNandFlash_ReadPage(ECC(skipBlock), block, i, data, 0); if (error) { TRACE_ERROR("SkipBlockNandFlash_ReadBlock: Cannot read page %d of block %d.\n\r", i, block); return error; } data = (void *) ((unsigned char *) data + pageSize); } return 0; }
//------------------------------------------------------------------------------ /// 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; }
/** * \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; unsigned int bufferSize, bufferAddr, memoryOffset, 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; /* Communication type with SAM-BA GUI. */ unsigned char comType; /* current pmecc parameter header value */ unsigned int currentPmeccHeaderValue; /* Index and value of pmecc command */ unsigned int nIndex, nValue; /* Number of ECC bits required */ unsigned char eccBitReq2TT [5] = {2, 4, 8, 12, 24}; /* Ecc mode to be swtich */ unsigned int eccMode; unsigned int trimPage; /* Save communication link type */ comType = pMailbox->argument.inputInit.comType; /* ---------------------------------------------------------- */ /* INIT: */ /* ---------------------------------------------------------- */ if (pMailbox->command == APPLET_CMD_INIT) { #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"); /* 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)); /* Tries to detect NAND Flash device connected to EBI CS3, with data lines connected to D0-D7, then on NAND Flash connected to D16-D23. */ if (!NandEbiDetect()) { pMailbox->status = APPLET_DEV_UNKNOWN; TRACE_INFO("\tDevice Unknown\n\r"); goto exit; } memset(&skipBlockNf, 0, sizeof(skipBlockNf)); NandGetOnfiPageParam (&OnfiPageParameter); 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 */ if ( nfBusWidth != 8) { SMC->SMC_CS_NUMBER[3].SMC_MODE = SMC_MODE_READ_MODE | SMC_MODE_WRITE_MODE | SMC_MODE_DBW(nfBusWidth/16) | SMC_MODE_TDF_CYCLES(1); } TRACE_INFO("\tNandflash driver initialized\n\r"); pMailbox->argument.outputInit.bufferAddress = (unsigned int) &_end; /* 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); spareSize = NandFlashModel_GetPageSpareSize(&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; pMailbox->argument.outputInit.pmeccParamHeader = 0; TRACE_INFO("\tpageSize : 0x%x blockSize : 0x%x blockNb : 0x%x \n\r", pageSize, blockSize, numBlocks); } /* By default, we use pmecc, except MICRON MLC nand with internal ECC controller */ eccOffset = 2; /* By defaut, 2 error bit correction, eccOffset = 2 */ PMECC_Initialize(&pmeccDesc, 0, eccCorrectability, pageSize, spareSize, eccOffset, 0); TRACE_INFO("\tNandflash PMECC initialized\n\r"); DMAD_Initialize( &dmad, POLLING_MODE ); if ( NandFlashConfigureDmaChannels( &dmad )) { pMailbox->status =APPLET_DEV_UNKNOWN; goto exit; } /* Initialize current pmecc parameter header, This 32-bit word is configured below */ currentPmeccHeader.usePmecc = 1; currentPmeccHeader.nbSectorPerPage = pmeccDesc.pageSize >> 8; currentPmeccHeader.spareSize = spareSize; currentPmeccHeader.eccBitReq = pmeccDesc.errBitNbrCapability; currentPmeccHeader.sectorSize = pmeccDesc.sectorSize; currentPmeccHeader.eccOffset = pmeccDesc.eccStartAddr; currentPmeccHeader.reserved = 0; currentPmeccHeader.key = 12; memcpy(&backupPmeccHeader, ¤tPmeccHeader, sizeof(nfParamHeader_t)); memcpy(¤tPmeccHeaderValue, ¤tPmeccHeader, sizeof(nfParamHeader_t)); pMailbox->argument.outputInit.pmeccParamHeader = currentPmeccHeaderValue; /* The Boot Program reads the first page without ECC check, to determine if the NAND parameter header is present. The header is made of 52 times the same 32-bit word (for redundancy reasons) which must contain NAND and PMECC parameters used to correctly perform the read of the rest of the data in the NAND. */ for (i = 0; i< 52; i++) memcpy(&bootNfParamHeader[i], ¤tPmeccHeader, sizeof(nfParamHeader_t)); NandSwitchEcc(ECC_PMECC); pMailbox->status = APPLET_SUCCESS; }
/** * \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 Returns the number of available pages in a translated nandflash. * * \param translated Pointer to a TranslatedNandFlash instance. * \return the number of available pages in a translated nandflash. */ unsigned int TranslatedNandFlash_GetDeviceSizeInPages( const struct TranslatedNandFlash *translated) { return TranslatedNandFlash_GetDeviceSizeInBlocks(translated) * NandFlashModel_GetBlockSizeInPages(MODEL(translated)); }