X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;f=bertos%2Fdrv%2Fnand.c;h=82f5f697a30dbdec8294b41a4fb2feff0c239234;hb=2053cc3e6aa26c20a8511ad7148dd704b8e0891e;hp=81be0ff1086826d9a4e817656d6ea4657f956f5b;hpb=bd20c479c3668f2a6a109aad6059228126964c9b;p=bertos.git diff --git a/bertos/drv/nand.c b/bertos/drv/nand.c index 81be0ff1..82f5f697 100644 --- a/bertos/drv/nand.c +++ b/bertos/drv/nand.c @@ -29,23 +29,48 @@ * Copyright 2011 Develer S.r.l. (http://www.develer.com/) * --> * -* \brief NAND driver +* \brief ONFI 1.0 compliant NAND kblock driver * -* This module allows read/write access to ONFI 1.0 compliant NANDs. +* Defective blocks are remapped in a reserved area of configurable size +* at the bottom of the NAND. +* At the moment there is no wear-leveling block translation: kblock's blocks +* are mapped directly on NAND erase blocks: when a (k)block is written the +* corresponding erase block is erased and all pages within are rewritten. +* Partial write is not possible: it's recommended to use buffered mode. +* +* The driver needs to format the NAND before use. If the initialization code +* detects a fresh memory it does a bad block scan and a formatting. +* Format info isn't stored in NAND in a global structure: each block has its +* info written in the spare area of its first page. These info contais a tag +* to detect formatted blocks and an index for bad block remapping (struct +* RemapInfo). +* +* The ECC for each page is written in the spare area too. +* +* Works only in 8 bit data mode and NAND parameters are not +* detected at run-time, but hand-configured in cfg_nand.h. +* +* Heap is needed to allocate the tipically large buffer necessary +* to erase and write a block. * * \author Stefano Fedrigo +* +* notest: avr */ #include "nand.h" - #include #include #include // memset /* - * Remap info written in the first page of each block - * used to remap bad blocks. + * Remap info written in the first page of each block. + * + * This structure is used in blocks of the reserved area to store + * which block the block containing the structure is remapping. + * It's stored in all other blocks too to mark a formatted block. + * In this case the member mapped_blk has non meaning. */ struct RemapInfo { @@ -53,21 +78,47 @@ struct RemapInfo uint16_t mapped_blk; // Bad block the block containing this info is remapping }; +// Where RemapInfo is stored in the spare area #define NAND_REMAP_TAG_OFFSET (CONFIG_NAND_SPARE_SIZE - sizeof(struct RemapInfo)) + +// Fixed tag to detect RemapInfo #define NAND_REMAP_TAG 0x3e10c8ed +/* + * Number of ECC words computed for a page. + * + * For 2048 bytes pages and 1 ECC word each 256 bytes, + * 24 bytes of ECC data are stored. + */ #define NAND_ECC_NWORDS (CONFIG_NAND_DATA_SIZE / 256) -// NAND flash status codes +// Total page size (user data + spare) in bytes +#define NAND_PAGE_SIZE (CONFIG_NAND_DATA_SIZE + CONFIG_NAND_SPARE_SIZE) + +// Erase block size in bytes +#define NAND_BLOCK_SIZE (CONFIG_NAND_DATA_SIZE * CONFIG_NAND_PAGES_PER_BLOCK) + +// Number of usable blocks, and index of first remapping block +#define NAND_NUM_USER_BLOCKS (CONFIG_NAND_NUM_BLOCK - CONFIG_NAND_NUM_REMAP_BLOCKS) + +// ONFI NAND status codes #define NAND_STATUS_READY BV(6) #define NAND_STATUS_ERROR BV(0) +// Get block from page +#define PAGE(blk) ((blk) * CONFIG_NAND_PAGES_PER_BLOCK) + +// Page from block and page in block +#define BLOCK(page) ((uint16_t)((page) / CONFIG_NAND_PAGES_PER_BLOCK)) +#define PAGE_IN_BLOCK(page) ((uint16_t)((page) % CONFIG_NAND_PAGES_PER_BLOCK)) + + /* - * Translate flash page index plus a byte offset + * Translate page index plus a byte offset * in the five address cycles format needed by NAND. * - * Cycles in x8 mode as the MT29F2G08AAD + * Cycles in x8 mode. * CA = column addr, PA = page addr, BA = block addr * * Cycle I/O7 I/O6 I/O5 I/O4 I/O3 I/O2 I/O1 I/O0 @@ -84,19 +135,17 @@ static void getAddrCycles(uint32_t page, uint16_t offset, uint32_t *cycle0, uint *cycle0 = offset & 0xff; *cycle1234 = (page << 8) | ((offset >> 8) & 0xf); - - //LOG_INFO("nand addr: %lx %lx\n", *cycle1234, *cycle0); } -static void chipReset(Mt29f *chip) +static void chipReset(Nand *chip) { nand_sendCommand(chip, NAND_CMD_RESET, 0, 0, 0, 0); nand_waitReadyBusy(chip, CONFIG_NAND_TMOUT); } -static bool isOperationComplete(Mt29f *chip) +static bool isOperationComplete(Nand *chip) { uint8_t status; @@ -110,7 +159,7 @@ static bool isOperationComplete(Mt29f *chip) /** * Erase the whole block. */ -int nand_blockErase(Mt29f *chip, uint16_t block) +int nand_blockErase(Nand *chip, uint16_t block) { uint32_t cycle0; uint32_t cycle1234; @@ -142,7 +191,7 @@ int nand_blockErase(Mt29f *chip, uint16_t block) /** * Read Device ID and configuration codes. */ -bool nand_getDevId(Mt29f *chip, uint8_t dev_id[5]) +bool nand_getDevId(Nand *chip, uint8_t dev_id[5]) { nand_sendCommand(chip, NAND_CMD_READID, 0, 1, 0, 0); @@ -159,7 +208,7 @@ bool nand_getDevId(Mt29f *chip, uint8_t dev_id[5]) } -static bool nand_readPage(Mt29f *chip, uint32_t page, uint16_t offset) +static bool nand_readPage(Nand *chip, uint32_t page, uint16_t offset) { uint32_t cycle0; uint32_t cycle1234; @@ -186,7 +235,7 @@ static bool nand_readPage(Mt29f *chip, uint32_t page, uint16_t offset) * Read page data and ECC, checking for errors. * TODO: fix errors with ECC when possible. */ -static bool nand_read(Mt29f *chip, uint32_t page, void *buf, uint16_t offset, uint16_t size) +static bool nand_read(Nand *chip, uint32_t page, void *buf, uint16_t offset, uint16_t size) { struct RemapInfo remap_info; uint32_t remapped_page = PAGE(chip->block_map[BLOCK(page)]) + PAGE_IN_BLOCK(page); @@ -210,25 +259,22 @@ static bool nand_read(Mt29f *chip, uint32_t page, void *buf, uint16_t offset, ui * That guarantees the page is written by us and a valid ECC is present. */ memcpy(&remap_info, (char *)buf + NAND_REMAP_TAG_OFFSET, sizeof(remap_info)); - if (remap_info.tag == NAND_REMAP_TAG) - return nand_checkEcc(chip); + if (remap_info.tag == NAND_REMAP_TAG && !nand_checkEcc(chip)) + { + chip->status |= NAND_ERR_ECC; + return false; + } else return true; } /* - * Write data in NFC SRAM buffer to a NAND page, starting at a given offset. + * Write data stored in nand_dataBuffer() to a NAND page, starting at a given offset. * Usually offset will be 0 to write data or CONFIG_NAND_DATA_SIZE to write the spare * area. - * - * According to datasheet to get ECC computed by hardware is sufficient - * to write the main area. But it seems that in that way the last ECC_PR - * register is not generated. The workaround is to write data and dummy (ff) - * spare data in one write, at this point the last ECC_PR is correct and - * ECC data can be written in the spare area with a second program operation. */ -static bool nand_writePage(Mt29f *chip, uint32_t page, uint16_t offset) +static bool nand_writePage(Nand *chip, uint32_t page, uint16_t offset) { uint32_t cycle0; uint32_t cycle1234; @@ -267,12 +313,14 @@ static bool nand_writePage(Mt29f *chip, uint32_t page, uint16_t offset) * \param page the page to be written * \parma original_page if different from page, it's the page that's being remapped * - * ECC data are extracted from ECC_PRx registers and written - * in the page's spare area. - * For 2048 bytes pages and 1 ECC word each 256 bytes, - * 24 bytes of ECC data are stored. + * Implementation note for SAM3 NFC controller: + * according to datasheet to get ECC computed by hardware is sufficient + * to write the main area. But it seems that in that way the last ECC_PR + * register is not generated. The workaround is to write data and dummy (ff) + * spare data in one write, at this point the last ECC_PR is correct and + * ECC data can be written in the spare area with a second program operation. */ -static bool nand_write(Mt29f *chip, uint32_t page, const void *buf, size_t size) +static bool nand_write(Nand *chip, uint32_t page, const void *buf, size_t size) { struct RemapInfo remap_info; uint32_t *nand_buf = (uint32_t *)nand_dataBuffer(chip); @@ -308,7 +356,7 @@ static bool nand_write(Mt29f *chip, uint32_t page, const void *buf, size_t size) * that bad block are marked with "00" bytes on the spare area of the * first page in block. */ -static bool blockIsGood(Mt29f *chip, uint16_t blk) +static bool blockIsGood(Nand *chip, uint16_t blk) { uint8_t *first_byte = (uint8_t *)nand_dataBuffer(chip); bool good; @@ -328,7 +376,7 @@ static bool blockIsGood(Mt29f *chip, uint16_t blk) * Return the main partition block remapped on given block in the remap * partition (dest_blk). */ -static int getBadBlockFromRemapBlock(Mt29f *chip, uint16_t dest_blk) +static int getBadBlockFromRemapBlock(Nand *chip, uint16_t dest_blk) { struct RemapInfo *remap_info = (struct RemapInfo *)nand_dataBuffer(chip); @@ -343,10 +391,10 @@ static int getBadBlockFromRemapBlock(Mt29f *chip, uint16_t dest_blk) /* - * Set a block remapping: src_blk (a block in main data partition) is remappend + * Set a block remapping: src_blk (a block in main data partition) is remapped * on dest_blk (block in reserved remapped blocks partition). */ -static bool setMapping(Mt29f *chip, uint32_t src_blk, uint32_t dest_blk) +static bool setMapping(Nand *chip, uint32_t src_blk, uint32_t dest_blk) { struct RemapInfo *remap_info = (struct RemapInfo *)nand_dataBuffer(chip); @@ -366,7 +414,7 @@ static bool setMapping(Mt29f *chip, uint32_t src_blk, uint32_t dest_blk) * Get a new block from the remap partition to use as a substitute * for a bad block. */ -static uint16_t getFreeRemapBlock(Mt29f *chip) +static uint16_t getFreeRemapBlock(Nand *chip) { int blk; @@ -387,7 +435,7 @@ static uint16_t getFreeRemapBlock(Mt29f *chip) /* * Check if NAND is initialized. */ -static bool chipIsMarked(Mt29f *chip) +static bool chipIsMarked(Nand *chip) { return getBadBlockFromRemapBlock(chip, NAND_NUM_USER_BLOCKS) != -1; } @@ -395,10 +443,10 @@ static bool chipIsMarked(Mt29f *chip) /* * Initialize NAND (format). Scan NAND for factory marked bad blocks. - * All bad blocks found are remapped to the remap partition: each + * All found bad blocks are remapped to the remap partition: each * block in the remap partition used to remap bad blocks is marked. */ -static void initBlockMap(Mt29f *chip) +static void initBlockMap(Nand *chip) { int b, last; @@ -437,12 +485,12 @@ static void initBlockMap(Mt29f *chip) chip->block_map[b] = getFreeRemapBlock(chip); setMapping(chip, b, chip->block_map[b]); remapped_anything = true; - LOG_INFO("nand: found new bad block %d, remapped to %d\n", b, chip->block_map[b]); + LOG_WARN("nand: found new bad block %d, remapped to %d\n", b, chip->block_map[b]); } } /* - * If no bad blocks are found (we're lucky!) write a dummy + * If no bad blocks are found (we're lucky!) write anyway a dummy * remap to mark NAND and detect we already scanned it next time. */ if (!remapped_anything) @@ -460,7 +508,7 @@ static void initBlockMap(Mt29f *chip) * \note DON'T USE on production chips: this function will try to erase * factory marked bad blocks too. */ -void nand_format(Mt29f *chip) +void nand_format(Nand *chip) { int b; @@ -478,7 +526,7 @@ void nand_format(Mt29f *chip) /* * Create some bad blocks, erasing them and writing the bad block mark. */ -void nand_ruinSomeBlocks(Mt29f *chip) +void nand_ruinSomeBlocks(Nand *chip) { int bads[] = { 7, 99, 555, 1003, 1004, 1432 }; unsigned i; @@ -499,9 +547,9 @@ void nand_ruinSomeBlocks(Mt29f *chip) #endif -static bool commonInit(Mt29f *chip, struct Heap *heap, unsigned chip_select) +static bool commonInit(Nand *chip, struct Heap *heap, unsigned chip_select) { - memset(chip, 0, sizeof(Mt29f)); + memset(chip, 0, sizeof(Nand)); DB(chip->fd.priv.type = KBT_NAND); chip->fd.blk_size = NAND_BLOCK_SIZE; @@ -533,7 +581,7 @@ static size_t nand_writeDirect(struct KBlock *kblk, block_idx_t idx, const void ASSERT(size <= NAND_BLOCK_SIZE); ASSERT(size % CONFIG_NAND_DATA_SIZE == 0); - //LOG_INFO("nand_writeDirect: idx=%ld offset=%d size=%d\n", idx, offset, size); + LOG_INFO("nand_writeDirect: idx=%ld offset=%d size=%d\n", idx, offset, size); nand_blockErase(NAND_CAST(kblk), idx); @@ -562,7 +610,7 @@ static size_t nand_readDirect(struct KBlock *kblk, block_idx_t idx, void *buf, s ASSERT(offset < NAND_BLOCK_SIZE); ASSERT(size <= NAND_BLOCK_SIZE); - //LOG_INFO("nand_readDirect: idx=%ld offset=%d size=%d\n", idx, offset, size); + LOG_INFO("nand_readDirect: idx=%ld offset=%d size=%d\n", idx, offset, size); while (nread < size) { @@ -583,14 +631,14 @@ static size_t nand_readDirect(struct KBlock *kblk, block_idx_t idx, void *buf, s static int nand_error(struct KBlock *kblk) { - Mt29f *chip = NAND_CAST(kblk); + Nand *chip = NAND_CAST(kblk); return chip->status; } static void nand_clearError(struct KBlock *kblk) { - Mt29f *chip = NAND_CAST(kblk); + Nand *chip = NAND_CAST(kblk); chip->status = 0; } @@ -622,7 +670,7 @@ static const KBlockVTable nand_unbuffered_vt = /** * Initialize NAND kblock driver in buffered mode. */ -bool nand_init(Mt29f *chip, struct Heap *heap, unsigned chip_select) +bool nand_init(Nand *chip, struct Heap *heap, unsigned chip_select) { if (!commonInit(chip, heap, chip_select)) return false; @@ -645,7 +693,7 @@ bool nand_init(Mt29f *chip, struct Heap *heap, unsigned chip_select) /** * Initialize NAND kblock driver in unbuffered mode. */ -bool nand_initUnbuffered(Mt29f *chip, struct Heap *heap, unsigned chip_select) +bool nand_initUnbuffered(Nand *chip, struct Heap *heap, unsigned chip_select) { if (!commonInit(chip, heap, chip_select)) return false;