From: aleph Date: Tue, 10 May 2011 16:25:48 +0000 (+0000) Subject: mt29f NAND driver: refactor to separate generic and hardware implementation. X-Git-Tag: 2.7.0~79 X-Git-Url: https://codewiz.org/gitweb?a=commitdiff_plain;h=22f49a0a4959e1189cc4364e16b6ff0eb692e010;p=bertos.git mt29f NAND driver: refactor to separate generic and hardware implementation. MT29F NAND is actually ONFI 1.0 compliant, so the driver can be a generic NAND driver when separating NAND logic from the hardware layer that handles the SAM3X NFC memory controller. git-svn-id: https://src.develer.com/svnoss/bertos/trunk@4887 38d2e660-2303-0410-9eaa-f027e97ec537 --- diff --git a/bertos/cfg/cfg_mt29f.h b/bertos/cfg/cfg_mt29f.h index 6a4c63e9..7fb51089 100644 --- a/bertos/cfg/cfg_mt29f.h +++ b/bertos/cfg/cfg_mt29f.h @@ -38,7 +38,62 @@ #define CFG_MT29F_H /** - * Module logging level. + * Page data size + * + * Size of the data section of a programmable page in bytes. + * + * $WIZ$ type = "int" + */ +#define CONFIG_MT29F_DATA_SIZE 2048 + +/** + * Page spare area size + * + * Size of the spare section of a programmable page in bytes. + * + * $WIZ$ type = "int" + */ +#define CONFIG_MT29F_SPARE_SIZE 64 + +/** + * Pages per block + * + * Number of pages in a erase block. + * + * $WIZ$ type = "int" + */ +#define CONFIG_MT29F_PAGES_PER_BLOCK 64 + +/** + * Number of blocks + * + * Total number of erase blocks in one NAND chip. + * + * $WIZ$ type = "int" + */ +#define CONFIG_MT29F_NUM_BLOCK 2048 + +/** + * Number of reserved blocks + * + * Blocks reserved for remapping defective NAND blocks. + * + * $WIZ$ type = "int" + */ +#define CONFIG_MT29F_NUM_REMAP_BLOCKS 128 + +/** + * NAND operations timeout + * + * How many milliseconds the cpu waits for + * completion of NAND operations. + * + * $WIZ$ type = "int" + */ +#define CONFIG_MT29F_TMOUT 100 + +/** + * Module logging level * * $WIZ$ type = "enum" * $WIZ$ value_list = "log_level" @@ -46,22 +101,11 @@ #define CONFIG_MT29F_LOG_LEVEL LOG_LVL_INFO /** - * module logging format. + * Module logging format * * $WIZ$ type = "enum" * $WIZ$ value_list = "log_format" */ #define CONFIG_MT29F_LOG_FORMAT LOG_FMT_TERSE -#if 0 -/** - * Write emb flash timeout. - * For how many milliseconds the cpu wait - * to complete write operation. - * - * $WIZ$ type = "int" - */ -#define CONFIG_FLASH_WR_TIMEOUT 100 -#endif - #endif /* CFG_MT29F_H */ diff --git a/bertos/cpu/cortex-m3/drv/mt29f_sam3.c b/bertos/cpu/cortex-m3/drv/mt29f_sam3.c index ce2faded..4d2c1c97 100644 --- a/bertos/cpu/cortex-m3/drv/mt29f_sam3.c +++ b/bertos/cpu/cortex-m3/drv/mt29f_sam3.c @@ -36,114 +36,35 @@ */ #include "mt29f_sam3.h" -#include "cfg/cfg_mt29f.h" - -// Define log settings for cfg/log.h -#define LOG_LEVEL CONFIG_MT29F_LOG_LEVEL -#define LOG_FORMAT CONFIG_MT29F_LOG_FORMAT - +#include #include #include #include #include -#include -#include #include /* cpu_relax() */ #include -#include /* memcpy() */ - -// Timeout for NAND operations in ms -#define MT29F_TMOUT 100 - -// NAND flash status codes -#define MT29F_STATUS_READY BV(6) -#define MT29F_STATUS_ERROR BV(0) - -// NAND flash commands -#define MT29F_CMD_READ_1 0x00 -#define MT29F_CMD_READ_2 0x30 -#define MT29F_CMD_COPYBACK_READ_1 0x00 -#define MT29F_CMD_COPYBACK_READ_2 0x35 -#define MT29F_CMD_COPYBACK_PROGRAM_1 0x85 -#define MT29F_CMD_COPYBACK_PROGRAM_2 0x10 -#define MT29F_CMD_RANDOM_OUT 0x05 -#define MT29F_CMD_RANDOM_OUT_2 0xE0 -#define MT29F_CMD_RANDOM_IN 0x85 -#define MT29F_CMD_READID 0x90 -#define MT29F_CMD_WRITE_1 0x80 -#define MT29F_CMD_WRITE_2 0x10 -#define MT29F_CMD_ERASE_1 0x60 -#define MT29F_CMD_ERASE_2 0xD0 -#define MT29F_CMD_STATUS 0x70 -#define MT29F_CMD_RESET 0xFF - -// Addresses for sending command, addresses and data bytes to flash -#define MT29F_CMD_ADDR 0x60400000 -#define MT29F_ADDR_ADDR 0x60200000 -#define MT29F_DATA_ADDR 0x60000000 - -// Get chip select mask for command register -#define MT29F_CSID(chip) (((chip)->chip_select << NFC_CMD_CSID_SHIFT) & NFC_CMD_CSID_MASK) - -// Get block from page -#define PAGE(blk) ((blk) * MT29F_PAGES_PER_BLOCK) - -// Page from block and page in block -#define BLOCK(page) ((uint16_t)((page) / MT29F_PAGES_PER_BLOCK)) -#define PAGE_IN_BLOCK(page) ((uint16_t)((page) % MT29F_PAGES_PER_BLOCK)) +#include /* memcpy, memset */ /* - * Remap info written in the first page of each block - * used to remap bad blocks. + * PIO definitions. */ -struct RemapInfo -{ - uint32_t tag; // Magic number to detect valid info - uint16_t mapped_blk; // Bad block the block containing this info is remapping -}; +#define MT29F_PIN_CE BV(6) +#define MT29F_PIN_RB BV(2) +#define MT29F_PINS_PORTA (MT29F_PIN_CE | MT29F_PIN_RB) +#define MT29F_PERIPH_PORTA PIO_PERIPH_B +#define MT29F_PIN_OE BV(19) +#define MT29F_PIN_WE BV(20) +#define MT29F_PIN_IO 0x0000FFFF +#define MT29F_PINS_PORTC (MT29F_PIN_OE | MT29F_PIN_WE | MT29F_PIN_IO) +#define MT29F_PERIPH_PORTC PIO_PERIPH_A -/* - * Translate flash page index plus a byte offset - * in the five address cycles format needed by NAND. - * - * Cycles in x8 mode as the MT29F2G08AAD - * 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 - * ------------------------------------------------------- - * First CA7 CA6 CA5 CA4 CA3 CA2 CA1 CA0 - * Second LOW LOW LOW LOW CA11 CA10 CA9 CA8 - * Third BA7 BA6 PA5 PA4 PA3 PA2 PA1 PA0 - * Fourth BA15 BA14 BA13 BA12 BA11 BA10 BA9 BA8 - * Fifth LOW LOW LOW LOW LOW LOW LOW BA16 - */ -static void getAddrCycles(uint32_t page, uint16_t offset, uint32_t *cycle0, uint32_t *cycle1234) -{ - ASSERT(offset < MT29F_PAGE_SIZE); - - *cycle0 = offset & 0xff; - *cycle1234 = (page << 8) | ((offset >> 8) & 0xf); - - //LOG_INFO("mt29f addr: %lx %lx\n", *cycle1234, *cycle0); -} - - -INLINE bool nfcIsBusy(void) -{ - return HWREG(NFC_CMD_BASE_ADDR + NFC_CMD_NFCCMD) & 0x8000000; -} - - -/* - * Return true if SMC/NFC controller completed the last operations. - */ -INLINE bool isCmdDone(void) -{ - return SMC_SR & SMC_SR_CMDDONE; -} +#define MT29F_PIN_CLE BV(9) +#define MT29F_PIN_ALE BV(8) +#define MT29F_PINS_PORTD (MT29F_PIN_CLE | MT29F_PIN_ALE) +#define MT29F_PERIPH_PORTD PIO_PERIPH_A /* @@ -151,14 +72,14 @@ INLINE bool isCmdDone(void) * signal. * Return true for edge detection, false in case of timeout. */ -static bool waitReadyBusy(void) +bool mt29f_waitReadyBusy(UNUSED_ARG(Mt29f *, chip), time_t timeout) { time_t start = timer_clock(); while (!(SMC_SR & SMC_SR_RB_EDGE0)) { cpu_relax(); - if (timer_clock() - start > MT29F_TMOUT) + if (timer_clock() - start > timeout) { LOG_INFO("mt29f: R/B timeout\n"); return false; @@ -172,14 +93,14 @@ static bool waitReadyBusy(void) * Wait for transfer to complete until timeout. * If transfer completes return true, false in case of timeout. */ -static bool waitTransferComplete(void) +bool mt29f_waitTransferComplete(UNUSED_ARG(Mt29f *, chip), time_t timeout) { time_t start = timer_clock(); while (!(SMC_SR & SMC_SR_XFRDONE)) { cpu_relax(); - if (timer_clock() - start > MT29F_TMOUT) + if (timer_clock() - start > timeout) { LOG_INFO("mt29f: xfer complete timeout\n"); return false; @@ -193,491 +114,98 @@ static bool waitTransferComplete(void) /* * Send command to NAND and wait for completion. */ -static void sendCommand(uint32_t cmd, +void mt29f_sendCommand(Mt29f *chip, + uint32_t cmd1, uint32_t cmd2, int num_cycles, uint32_t cycle0, uint32_t cycle1234) { reg32_t *cmd_addr; + uint32_t cmd_val; - while (nfcIsBusy()); + while (HWREG(NFC_CMD_BASE_ADDR + NFC_CMD_NFCCMD) & 0x8000000); if (num_cycles == 5) SMC_ADDR = cycle0; - cmd_addr = (reg32_t *)(NFC_CMD_BASE_ADDR + cmd); - *cmd_addr = cycle1234; - - while (!isCmdDone()); -} - - -static bool isOperationComplete(Mt29f *chip) -{ - uint8_t status; - - sendCommand(MT29F_CSID(chip) | - NFC_CMD_NFCCMD | NFC_CMD_ACYCLE_NONE | - MT29F_CMD_STATUS << 2, - 0, 0, 0); - - status = (uint8_t)HWREG(MT29F_DATA_ADDR); - return (status & MT29F_STATUS_READY) && !(status & MT29F_STATUS_ERROR); -} - - -static void chipReset(Mt29f *chip) -{ - sendCommand(MT29F_CSID(chip) | - NFC_CMD_NFCCMD | NFC_CMD_ACYCLE_NONE | - MT29F_CMD_RESET << 2, - 0, 0, 0); - - waitReadyBusy(); -} - - -/** - * Erase the whole block. - */ -int mt29f_blockErase(Mt29f *chip, uint16_t block) -{ - uint32_t cycle0; - uint32_t cycle1234; - - uint16_t remapped_block = chip->block_map[block]; - if (block != remapped_block) - { - LOG_INFO("mt29f_blockErase: remapped block: blk %d->%d\n", block, remapped_block); - block = remapped_block; - } - - getAddrCycles(PAGE(block), 0, &cycle0, &cycle1234); - - sendCommand(MT29F_CSID(chip) | - NFC_CMD_NFCCMD | NFC_CMD_ACYCLE_THREE | NFC_CMD_VCMD2 | - (MT29F_CMD_ERASE_2 << 10) | (MT29F_CMD_ERASE_1 << 2), - 3, 0, cycle1234 >> 8); - - waitReadyBusy(); - - if (!isOperationComplete(chip)) - { - LOG_ERR("mt29f: error erasing block\n"); - chip->status |= MT29F_ERR_ERASE; - return -1; - } - - return 0; -} - - -/** - * Read Device ID and configuration codes. - */ -bool mt29f_getDevId(Mt29f *chip, uint8_t dev_id[5]) -{ - sendCommand(MT29F_CSID(chip) | - NFC_CMD_NFCCMD | NFC_CMD_NFCEN | NFC_CMD_ACYCLE_ONE | - MT29F_CMD_READID << 2, - 1, 0, 0); - - waitReadyBusy(); - if (!waitTransferComplete()) - { - LOG_ERR("mt29f: getDevId timeout\n"); - chip->status |= MT29F_ERR_RD_TMOUT; - return false; - } - - memcpy(dev_id, (void *)NFC_SRAM_BASE_ADDR, 5); - return true; -} - - -static bool checkEcc(Mt29f *chip) -{ - struct RemapInfo *remap_info = (struct RemapInfo *)(NFC_SRAM_BASE_ADDR + MT29F_REMAP_TAG_OFFSET); - - /* - * Check for ECC hardware status only if a valid RemapInfo structure is found. - * That guarantees we wrote the block and a valid ECC is present. - */ - if (remap_info->tag == MT29F_REMAP_TAG) - { - uint32_t sr1 = SMC_ECC_SR1; - if (sr1) - { - LOG_INFO("ECC error, ECC_SR1=0x%lx\n", sr1); - chip->status |= MT29F_ERR_ECC; - return false; - } - } - - return true; -} - - -static bool mt29f_readPage(Mt29f *chip, uint32_t page, uint16_t offset) -{ - uint32_t cycle0; - uint32_t cycle1234; - - //LOG_INFO("mt29f_readPage: page 0x%lx off 0x%x\n", page, offset); - - getAddrCycles(page, offset, &cycle0, &cycle1234); - - sendCommand(MT29F_CSID(chip) | - NFC_CMD_NFCCMD | NFC_CMD_NFCEN | NFC_CMD_ACYCLE_FIVE | NFC_CMD_VCMD2 | - (MT29F_CMD_READ_2 << 10) | (MT29F_CMD_READ_1 << 2), - 5, cycle0, cycle1234); - - waitReadyBusy(); - if (!waitTransferComplete()) - { - LOG_ERR("mt29f: read timeout\n"); - chip->status |= MT29F_ERR_RD_TMOUT; - return false; - } - - return true; -} - - -/* - * Read page data and ECC, checking for errors. - * TODO: fix errors with ECC when possible. - */ -static bool mt29f_read(Mt29f *chip, uint32_t page, void *buf, uint16_t offset, uint16_t size) -{ - uint32_t remapped_page = PAGE(chip->block_map[BLOCK(page)]) + PAGE_IN_BLOCK(page); - - //LOG_INFO("mt29f_read: page=%ld, offset=%d, size=%d\n", page, offset, size); - - if (page != remapped_page) - { - LOG_INFO("mt29f_read: remapped block: blk %d->%d, pg %ld->%ld\n", - BLOCK(page), chip->block_map[BLOCK(page)], page, remapped_page); - page = remapped_page; - } - - if (!mt29f_readPage(chip, page, 0)) - return false; - - memcpy(buf, (void *)(NFC_SRAM_BASE_ADDR + offset), size); - - return checkEcc(chip); -} - - -/* - * Write data in NFC SRAM buffer to a NAND page, starting at a given offset. - * Usually offset will be 0 to write data or MT29F_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 mt29f_writePage(Mt29f *chip, uint32_t page, uint16_t offset) -{ - uint32_t cycle0; - uint32_t cycle1234; - - //LOG_INFO("mt29f_writePage: page 0x%lx off 0x%x\n", page, offset); + cmd_val = NFC_CMD_NFCCMD + | ((chip->chip_select << NFC_CMD_CSID_SHIFT) & NFC_CMD_CSID_MASK) + | ((num_cycles << NFC_CMD_ACYCLE_SHIFT) & NFC_CMD_ACYCLE_MASK) + | cmd1 << 2 + | cmd2 << 10; - getAddrCycles(page, offset, &cycle0, &cycle1234); + // Check for commands transferring data + if (cmd1 == MT29F_CMD_WRITE_1 || cmd1 == MT29F_CMD_READ_1 || cmd1 == MT29F_CMD_READID) + cmd_val |= NFC_CMD_NFCEN; - sendCommand(MT29F_CSID(chip) | - NFC_CMD_NFCCMD | NFC_CMD_NFCWR | NFC_CMD_NFCEN | NFC_CMD_ACYCLE_FIVE | - MT29F_CMD_WRITE_1 << 2, - 5, cycle0, cycle1234); + // Check for commands writing data + if (cmd1 == MT29F_CMD_WRITE_1) + cmd_val |= NFC_CMD_NFCWR; - if (!waitTransferComplete()) - { - LOG_ERR("mt29f: write timeout\n"); - chip->status |= MT29F_ERR_WR_TMOUT; - return false; - } - - sendCommand(MT29F_CSID(chip) | - NFC_CMD_NFCCMD | NFC_CMD_ACYCLE_NONE | - MT29F_CMD_WRITE_2 << 2, - 0, 0, 0); - - waitReadyBusy(); + // Check for two command cycles + if (cmd2) + cmd_val |= NFC_CMD_VCMD2; - if (!isOperationComplete(chip)) - { - LOG_ERR("mt29f: error writing page\n"); - chip->status |= MT29F_ERR_WRITE; - return false; - } - - return true; -} - - -/* - * Write data in a page. - */ -static bool mt29f_writePageData(Mt29f *chip, uint32_t page, const void *buf, uint16_t size) -{ - ASSERT(size <= MT29F_DATA_SIZE); - - memset((void *)NFC_SRAM_BASE_ADDR, 0xff, MT29F_PAGE_SIZE); - memcpy((void *)NFC_SRAM_BASE_ADDR, buf, size); + cmd_addr = (reg32_t *)(NFC_CMD_BASE_ADDR + cmd_val); + *cmd_addr = cycle1234; - return mt29f_writePage(chip, page, 0); + while (!(SMC_SR & SMC_SR_CMDDONE)); } /* - * Write the spare area in a page: ECC and remap block index. - * \param page the page to be written - * \parma original_page if different from page, it's the page that's being remapped + * Get NAND chip status register. * - * 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. + * NOTE: this is global between different chip selects, so returns + * the status register of the last used NAND chip. */ -static bool mt29f_writePageSpare(Mt29f *chip, uint32_t page, uint32_t original_page) +uint8_t mt29f_getChipStatus(UNUSED_ARG(Mt29f *, chip)) { - int i; - uint32_t *buf = (uint32_t *)NFC_SRAM_BASE_ADDR; - struct RemapInfo *remap_info = (struct RemapInfo *)(NFC_SRAM_BASE_ADDR + MT29F_REMAP_TAG_OFFSET); - - memset((void *)NFC_SRAM_BASE_ADDR, 0xff, MT29F_SPARE_SIZE); - - for (i = 0; i < MT29F_ECC_NWORDS; i++) - buf[i] = *((reg32_t *)(SMC_BASE + SMC_ECC_PR0_OFF) + i); - - // Write remap tag - remap_info->tag = MT29F_REMAP_TAG; - remap_info->mapped_blk = BLOCK(original_page); - - return mt29f_writePage(chip, page, MT29F_DATA_SIZE); -} - - -static bool mt29f_write(Mt29f *chip, uint32_t page, const void *buf, uint16_t size) -{ - uint32_t remapped_page = PAGE(chip->block_map[BLOCK(page)]) + PAGE_IN_BLOCK(page); - - if (page != remapped_page) - LOG_INFO("mt29f_write: remapped block: blk %d->%d, pg %ld->%ld\n", - BLOCK(page), chip->block_map[BLOCK(page)], page, remapped_page); - - return - mt29f_writePageData(chip, remapped_page, buf, size) && - mt29f_writePageSpare(chip, remapped_page, page); + return (uint8_t)HWREG(NFC_CMD_BASE_ADDR); } -/* - * Check if the given block is marked bad: ONFI standard mandates - * 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) +void *mt29f_dataBuffer(UNUSED_ARG(Mt29f *, chip)) { - uint8_t *first_byte = (uint8_t *)NFC_SRAM_BASE_ADDR; - bool good; - - // Check first byte in spare area of first page in block - mt29f_readPage(chip, PAGE(blk), MT29F_DATA_SIZE); - good = *first_byte != 0; - - if (!good) - LOG_INFO("mt29f: bad block %d\n", blk); - - return good; + return (void *)NFC_SRAM_BASE_ADDR; } -/* - * Return the main partition block remapped on given block in the remap - * partition (dest_blk). - */ -static int getBadBlockFromRemapBlock(Mt29f *chip, uint16_t dest_blk) +bool mt29f_checkEcc(Mt29f *chip) { - struct RemapInfo *remap_info = (struct RemapInfo *)NFC_SRAM_BASE_ADDR; - - if (!mt29f_readPage(chip, PAGE(dest_blk), MT29F_DATA_SIZE + MT29F_REMAP_TAG_OFFSET)) - return -1; - - if (remap_info->tag == MT29F_REMAP_TAG) - return remap_info->mapped_blk; - else - return -1; -} - - -/* - * Set a block remapping: src_blk (a block in main data partition) is remappend - * on dest_blk (block in reserved remapped blocks partition). - */ -static bool setMapping(Mt29f *chip, uint32_t src_blk, uint32_t dest_blk) -{ - struct RemapInfo *remap_info = (struct RemapInfo *)NFC_SRAM_BASE_ADDR; - - LOG_INFO("mt29f, setMapping(): src=%ld dst=%ld\n", src_blk, dest_blk); - - if (!mt29f_readPage(chip, PAGE(dest_blk), MT29F_DATA_SIZE + MT29F_REMAP_TAG_OFFSET)) - return false; - - remap_info->tag = MT29F_REMAP_TAG; - remap_info->mapped_blk = src_blk; - - return mt29f_writePage(chip, PAGE(dest_blk), MT29F_DATA_SIZE + MT29F_REMAP_TAG_OFFSET); -} - - -/* - * Get a new block from the remap partition to use as a substitute - * for a bad block. - */ -static uint16_t getFreeRemapBlock(Mt29f *chip) -{ - int blk; - - for (blk = chip->remap_start; blk < MT29F_NUM_BLOCKS; blk++) - { - if (blockIsGood(chip, blk)) - { - chip->remap_start = blk + 1; - return blk; - } - } - - LOG_ERR("mt29f: reserved blocks for bad block remapping exhausted!\n"); - return 0; -} - - -/* - * Check if NAND is initialized. - */ -static bool chipIsMarked(Mt29f *chip) -{ - return getBadBlockFromRemapBlock(chip, MT29F_NUM_USER_BLOCKS) != -1; -} - - -/* - * Initialize NAND (format). Scan NAND for factory marked bad blocks. - * All bad blocks found are remapped to the remap partition: each - * block in the remap partition used to remap bad blocks is marked. - */ -static void initBlockMap(Mt29f *chip) -{ - int b, last; - - // Default is for each block to not be remapped - for (b = 0; b < MT29F_NUM_BLOCKS; b++) - chip->block_map[b] = b; - chip->remap_start = MT29F_NUM_USER_BLOCKS; - - if (chipIsMarked(chip)) + uint32_t sr1 = SMC_ECC_SR1; + if (sr1) { - LOG_INFO("mt29f: found initialized NAND, searching for remapped blocks\n"); - - // Scan for assigned blocks in remap area - for (b = last = MT29F_NUM_USER_BLOCKS; b < MT29F_NUM_BLOCKS; b++) - { - int remapped_blk = getBadBlockFromRemapBlock(chip, b); - if (remapped_blk != -1 && remapped_blk != b) - { - LOG_INFO("mt29f: found remapped block %d->%d\n", remapped_blk, b); - chip->block_map[remapped_blk] = b; - last = b + 1; - } - } - chip->remap_start = last; + LOG_INFO("ECC error, ECC_SR1=0x%lx\n", sr1); + chip->status |= MT29F_ERR_ECC; + return false; } else - { - bool remapped_anything = false; - - LOG_INFO("mt29f: found new NAND, searching for bad blocks\n"); - - for (b = 0; b < MT29F_NUM_USER_BLOCKS; b++) - { - if (!blockIsGood(chip, b)) - { - chip->block_map[b] = getFreeRemapBlock(chip); - setMapping(chip, b, chip->block_map[b]); - remapped_anything = true; - LOG_INFO("mt29f: 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 - * remap to mark NAND and detect we already scanned it next time. - */ - if (!remapped_anything) - { - setMapping(chip, MT29F_NUM_USER_BLOCKS, MT29F_NUM_USER_BLOCKS); - LOG_INFO("mt29f: no bad block founds, marked NAND\n"); - } - } + return true; } -/** - * Reset bad blocks map and erase all blocks. +/* + * Compute ECC on data in a buffer. * - * \note DON'T USE on production chips: this function will try to erase - * factory marked bad blocks too. + * \param chip nand context + * \param buf buffer containing data + * \param size size of data buffer + * \param ecc pointer to buffer where computed ECC is stored + * \param ecc_size max size for ecc buffer */ -void mt29f_format(Mt29f *chip) +void mt29f_computeEcc(UNUSED_ARG(Mt29f *, chip), + UNUSED_ARG(const void *, buf), UNUSED_ARG(size_t, size), uint32_t *ecc, size_t ecc_size) { - int b; - - for (b = 0; b < MT29F_NUM_BLOCKS; b++) - { - LOG_INFO("mt29f: erasing block %d\n", b); - chip->block_map[b] = b; - mt29f_blockErase(chip, b); - } - chip->remap_start = MT29F_NUM_USER_BLOCKS; + size_t i; + for (i = 0; i < ecc_size; i++) + ecc[i] = *((reg32_t *)(SMC_BASE + SMC_ECC_PR0_OFF) + i); } -#ifdef _DEBUG -/* - * Create some bad blocks, erasing them and writing the bad block mark. - */ -void mt29f_ruinSomeBlocks(Mt29f *chip) +void mt29f_hwInit(UNUSED_ARG(Mt29f *, chip)) { - int bads[] = { 7, 99, 555, 1003, 1004, 1432 }; - unsigned i; - - LOG_INFO("mt29f: erasing mark\n"); - mt29f_blockErase(chip, MT29F_NUM_USER_BLOCKS); - - for (i = 0; i < countof(bads); i++) - { - LOG_INFO("mt29f: erasing block %d\n", bads[i]); - mt29f_blockErase(chip, bads[i]); - - LOG_INFO("mt29f: marking page %d as bad\n", PAGE(bads[i])); - memset((void *)NFC_SRAM_BASE_ADDR, 0, MT29F_SPARE_SIZE); - mt29f_writePage(chip, PAGE(bads[i]), MT29F_DATA_SIZE); - } -} - -#endif + // FIXME: Parameters specific for MT29F8G08AAD - -static void initPio(void) -{ - /* - * TODO: put following stuff in hw_ file dependent - * Parameters for MT29F8G08AAD - */ + // PIO init pmc_periphEnable(PIOA_ID); pmc_periphEnable(PIOC_ID); pmc_periphEnable(PIOD_ID); @@ -695,11 +223,8 @@ static void initPio(void) PIOD_PUER = MT29F_PINS_PORTD; pmc_periphEnable(SMC_SDRAMC_ID); -} - -static void initSmc(void) -{ + // SMC init SMC_SETUP0 = SMC_SETUP_NWE_SETUP(0) | SMC_SETUP_NCS_WR_SETUP(0) | SMC_SETUP_NRD_SETUP(0) @@ -740,161 +265,3 @@ static void initSmc(void) SMC_ECC_CTRL = SMC_ECC_CTRL_SWRST; SMC_ECC_MD = SMC_ECC_MD_ECC_PAGESIZE_PS2048_64 | SMC_ECC_MD_TYPCORREC_C256B; } - - -static bool commonInit(Mt29f *chip, struct Heap *heap, unsigned chip_select) -{ - memset(chip, 0, sizeof(Mt29f)); - - DB(chip->fd.priv.type = KBT_NAND); - chip->fd.blk_size = MT29F_BLOCK_SIZE; - chip->fd.blk_cnt = MT29F_NUM_USER_BLOCKS; - - chip->chip_select = chip_select; - chip->block_map = heap_allocmem(heap, MT29F_NUM_BLOCKS * sizeof(*chip->block_map)); - if (!chip->block_map) - { - LOG_ERR("mt29f: error allocating block map\n"); - return false; - } - - initPio(); - initSmc(); - chipReset(chip); - initBlockMap(chip); - - return true; -} - - -/**************** Kblock interface ****************/ - - -static size_t mt29f_writeDirect(struct KBlock *kblk, block_idx_t idx, const void *buf, size_t offset, size_t size) -{ - ASSERT(offset <= MT29F_BLOCK_SIZE); - ASSERT(offset % MT29F_DATA_SIZE == 0); - ASSERT(size <= MT29F_BLOCK_SIZE); - ASSERT(size % MT29F_DATA_SIZE == 0); - - //LOG_INFO("mt29f_writeDirect: idx=%ld offset=%d size=%d\n", idx, offset, size); - - mt29f_blockErase(MT29F_CAST(kblk), idx); - - while (offset < size) - { - uint32_t page = PAGE(idx) + (offset / MT29F_DATA_SIZE); - - if (!mt29f_write(MT29F_CAST(kblk), page, buf, MT29F_DATA_SIZE)) - break; - - offset += MT29F_DATA_SIZE; - buf = (const char *)buf + MT29F_DATA_SIZE; - } - - return offset; -} - - -static size_t mt29f_readDirect(struct KBlock *kblk, block_idx_t idx, void *buf, size_t offset, size_t size) -{ - uint32_t page; - size_t read_size; - size_t read_offset; - size_t nread = 0; - - ASSERT(offset < MT29F_BLOCK_SIZE); - ASSERT(size <= MT29F_BLOCK_SIZE); - - //LOG_INFO("mt29f_readDirect: idx=%ld offset=%d size=%d\n", idx, offset, size); - - while (nread < size) - { - page = PAGE(idx) + (offset / MT29F_DATA_SIZE); - read_offset = offset % MT29F_DATA_SIZE; - read_size = MIN(size, MT29F_DATA_SIZE - read_offset); - - if (!mt29f_read(MT29F_CAST(kblk), page, (char *)buf + nread, read_offset, read_size)) - break; - - offset += read_size; - nread += read_size; - } - - return nread; -} - - -static int mt29f_error(struct KBlock *kblk) -{ - Mt29f *chip = MT29F_CAST(kblk); - return chip->status; -} - - -static void mt29f_clearError(struct KBlock *kblk) -{ - Mt29f *chip = MT29F_CAST(kblk); - chip->status = 0; -} - - -static const KBlockVTable mt29f_buffered_vt = -{ - .readDirect = mt29f_readDirect, - .writeDirect = mt29f_writeDirect, - - .readBuf = kblock_swReadBuf, - .writeBuf = kblock_swWriteBuf, - .load = kblock_swLoad, - .store = kblock_swStore, - - .error = mt29f_error, - .clearerr = mt29f_clearError, -}; - -static const KBlockVTable mt29f_unbuffered_vt = -{ - .readDirect = mt29f_readDirect, - .writeDirect = mt29f_writeDirect, - - .error = mt29f_error, - .clearerr = mt29f_clearError, -}; - - -/** - * Initialize NAND kblock driver in buffered mode. - */ -bool mt29f_init(Mt29f *chip, struct Heap *heap, unsigned chip_select) -{ - if (!commonInit(chip, heap, chip_select)) - return false; - - chip->fd.priv.vt = &mt29f_buffered_vt; - chip->fd.priv.flags |= KB_BUFFERED; - - chip->fd.priv.buf = heap_allocmem(heap, MT29F_BLOCK_SIZE); - if (!chip->fd.priv.buf) - { - LOG_ERR("mt29f: error allocating block buffer\n"); - return false; - } - - // Load the first block in the cache - return mt29f_readDirect(&chip->fd, 0, chip->fd.priv.buf, 0, chip->fd.blk_size); -} - - -/** - * Initialize NAND kblock driver in unbuffered mode. - */ -bool mt29f_initUnbuffered(Mt29f *chip, struct Heap *heap, unsigned chip_select) -{ - if (!commonInit(chip, heap, chip_select)) - return false; - - chip->fd.priv.vt = &mt29f_unbuffered_vt; - return true; -} - diff --git a/bertos/cpu/cortex-m3/drv/mt29f_sam3.h b/bertos/cpu/cortex-m3/drv/mt29f_sam3.h index 73134498..d3c02669 100644 --- a/bertos/cpu/cortex-m3/drv/mt29f_sam3.h +++ b/bertos/cpu/cortex-m3/drv/mt29f_sam3.h @@ -38,45 +38,6 @@ #ifndef MT29F_SAM3_H #define MT29F_SAM3_H -#include - - -// MT29F2G08AAD, FIXME: configurable -#define MT29F_DATA_SIZE 0x800 // 2048 B -#define MT29F_SPARE_SIZE 0x40 // 64 B -#define MT29F_PAGE_SIZE (MT29F_DATA_SIZE + MT29F_SPARE_SIZE) -#define MT29F_PAGES_PER_BLOCK 64 -#define MT29F_BLOCK_SIZE (MT29F_DATA_SIZE * MT29F_PAGES_PER_BLOCK) -#define MT29F_NUM_BLOCKS 2048 -#define MT29F_ECC_NWORDS (MT29F_DATA_SIZE / 256) -#define MT29F_REMAP_TAG_OFFSET 0x38 -#define MT29F_REMAP_TAG 0x3e10c8ed - -// Number of reserved block for remapping -#define MT29F_NUM_REMAP_BLOCKS 128 -// Number of usable blocks, and index of first remapping block -#define MT29F_NUM_USER_BLOCKS (MT29F_NUM_BLOCKS - MT29F_NUM_REMAP_BLOCKS) - - -/* - * PIO definitions. - */ -#define MT29F_PIN_CE BV(6) -#define MT29F_PIN_RB BV(2) -#define MT29F_PINS_PORTA (MT29F_PIN_CE | MT29F_PIN_RB) -#define MT29F_PERIPH_PORTA PIO_PERIPH_B - -#define MT29F_PIN_OE BV(19) -#define MT29F_PIN_WE BV(20) -#define MT29F_PIN_IO 0x0000FFFF -#define MT29F_PINS_PORTC (MT29F_PIN_OE | MT29F_PIN_WE | MT29F_PIN_IO) -#define MT29F_PERIPH_PORTC PIO_PERIPH_A - -#define MT29F_PIN_CLE BV(9) -#define MT29F_PIN_ALE BV(8) -#define MT29F_PINS_PORTD (MT29F_PIN_CLE | MT29F_PIN_ALE) -#define MT29F_PERIPH_PORTD PIO_PERIPH_A - #endif /* MT29F_SAM3_H */ diff --git a/bertos/drv/mt29f.c b/bertos/drv/mt29f.c new file mode 100644 index 00000000..e6a10279 --- /dev/null +++ b/bertos/drv/mt29f.c @@ -0,0 +1,657 @@ +/** +* \file +* +* +* \brief Micron MT29F serial NAND driver +* +* This module allows read/write access to Micron MT29F serial +* NANDs. +* +* \author Stefano Fedrigo +*/ + +#include "mt29f.h" + +#include +#include +#include // memset + + +/* + * Remap info written in the first page of each block + * used to remap bad blocks. + */ +struct RemapInfo +{ + uint32_t tag; // Magic number to detect valid info + uint16_t mapped_blk; // Bad block the block containing this info is remapping +}; + +#define MT29F_REMAP_TAG_OFFSET (CONFIG_MT29F_SPARE_SIZE - sizeof(struct RemapInfo)) +#define MT29F_REMAP_TAG 0x3e10c8ed + +#define MT29F_ECC_NWORDS (CONFIG_MT29F_DATA_SIZE / 256) + +// NAND flash status codes +#define MT29F_STATUS_READY BV(6) +#define MT29F_STATUS_ERROR BV(0) + + +/* + * Translate flash page index plus a byte offset + * in the five address cycles format needed by NAND. + * + * Cycles in x8 mode as the MT29F2G08AAD + * 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 + * ------------------------------------------------------- + * First CA7 CA6 CA5 CA4 CA3 CA2 CA1 CA0 + * Second LOW LOW LOW LOW CA11 CA10 CA9 CA8 + * Third BA7 BA6 PA5 PA4 PA3 PA2 PA1 PA0 + * Fourth BA15 BA14 BA13 BA12 BA11 BA10 BA9 BA8 + * Fifth LOW LOW LOW LOW LOW LOW LOW BA16 + */ +static void getAddrCycles(uint32_t page, uint16_t offset, uint32_t *cycle0, uint32_t *cycle1234) +{ + ASSERT(offset < MT29F_PAGE_SIZE); + + *cycle0 = offset & 0xff; + *cycle1234 = (page << 8) | ((offset >> 8) & 0xf); + + //LOG_INFO("mt29f addr: %lx %lx\n", *cycle1234, *cycle0); +} + + +static void chipReset(Mt29f *chip) +{ + mt29f_sendCommand(chip, MT29F_CMD_RESET, 0, 0, 0, 0); + mt29f_waitReadyBusy(chip, CONFIG_MT29F_TMOUT); +} + + +static bool isOperationComplete(Mt29f *chip) +{ + uint8_t status; + + mt29f_sendCommand(chip, MT29F_CMD_STATUS, 0, 0, 0, 0); + + status = mt29f_getChipStatus(chip); + return (status & MT29F_STATUS_READY) && !(status & MT29F_STATUS_ERROR); +} + + +/** + * Erase the whole block. + */ +int mt29f_blockErase(Mt29f *chip, uint16_t block) +{ + uint32_t cycle0; + uint32_t cycle1234; + + uint16_t remapped_block = chip->block_map[block]; + if (block != remapped_block) + { + LOG_INFO("mt29f_blockErase: remapped block: blk %d->%d\n", block, remapped_block); + block = remapped_block; + } + + getAddrCycles(PAGE(block), 0, &cycle0, &cycle1234); + + mt29f_sendCommand(chip, MT29F_CMD_ERASE_1, MT29F_CMD_ERASE_2, 3, 0, cycle1234 >> 8); + + mt29f_waitReadyBusy(chip, CONFIG_MT29F_TMOUT); + + if (!isOperationComplete(chip)) + { + LOG_ERR("mt29f: error erasing block\n"); + chip->status |= MT29F_ERR_ERASE; + return -1; + } + + return 0; +} + + +/** + * Read Device ID and configuration codes. + */ +bool mt29f_getDevId(Mt29f *chip, uint8_t dev_id[5]) +{ + mt29f_sendCommand(chip, MT29F_CMD_READID, 0, 1, 0, 0); + + mt29f_waitReadyBusy(chip, CONFIG_MT29F_TMOUT); + if (!mt29f_waitTransferComplete(chip, CONFIG_MT29F_TMOUT)) + { + LOG_ERR("mt29f: getDevId timeout\n"); + chip->status |= MT29F_ERR_RD_TMOUT; + return false; + } + + memcpy(dev_id, mt29f_dataBuffer(chip), sizeof(dev_id)); + return true; +} + + +static bool mt29f_readPage(Mt29f *chip, uint32_t page, uint16_t offset) +{ + uint32_t cycle0; + uint32_t cycle1234; + + //LOG_INFO("mt29f_readPage: page 0x%lx off 0x%x\n", page, offset); + + getAddrCycles(page, offset, &cycle0, &cycle1234); + + mt29f_sendCommand(chip, MT29F_CMD_READ_1, MT29F_CMD_READ_2, 5, cycle0, cycle1234); + + mt29f_waitReadyBusy(chip, CONFIG_MT29F_TMOUT); + if (!mt29f_waitTransferComplete(chip, CONFIG_MT29F_TMOUT)) + { + LOG_ERR("mt29f: read timeout\n"); + chip->status |= MT29F_ERR_RD_TMOUT; + return false; + } + + return true; +} + + +/* + * Read page data and ECC, checking for errors. + * TODO: fix errors with ECC when possible. + */ +static bool mt29f_read(Mt29f *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); + + //LOG_INFO("mt29f_read: page=%ld, offset=%d, size=%d\n", page, offset, size); + + if (page != remapped_page) + { + LOG_INFO("mt29f_read: remapped block: blk %d->%d, pg %ld->%ld\n", + BLOCK(page), chip->block_map[BLOCK(page)], page, remapped_page); + page = remapped_page; + } + + if (!mt29f_readPage(chip, page, 0)) + return false; + + memcpy(buf, (char *)mt29f_dataBuffer(chip) + offset, size); + + /* + * Check for ECC hardware status only if a valid RemapInfo structure is found. + * That guarantees the page is written by us and a valid ECC is present. + */ + memcpy(&remap_info, (char *)buf + MT29F_REMAP_TAG_OFFSET, sizeof(remap_info)); + if (remap_info.tag == MT29F_REMAP_TAG) + return mt29f_checkEcc(chip); + else + return true; +} + + +/* + * Write data in NFC SRAM buffer to a NAND page, starting at a given offset. + * Usually offset will be 0 to write data or CONFIG_MT29F_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 mt29f_writePage(Mt29f *chip, uint32_t page, uint16_t offset) +{ + uint32_t cycle0; + uint32_t cycle1234; + + //LOG_INFO("mt29f_writePage: page 0x%lx off 0x%x\n", page, offset); + + getAddrCycles(page, offset, &cycle0, &cycle1234); + + mt29f_sendCommand(chip, MT29F_CMD_WRITE_1, 0, 5, cycle0, cycle1234); + + if (!mt29f_waitTransferComplete(chip, CONFIG_MT29F_TMOUT)) + { + LOG_ERR("mt29f: write timeout\n"); + chip->status |= MT29F_ERR_WR_TMOUT; + return false; + } + + mt29f_sendCommand(chip, MT29F_CMD_WRITE_2, 0, 0, 0, 0); + + mt29f_waitReadyBusy(chip, CONFIG_MT29F_TMOUT); + + if (!isOperationComplete(chip)) + { + LOG_ERR("mt29f: error writing page\n"); + chip->status |= MT29F_ERR_WRITE; + return false; + } + + return true; +} + + +/* + * Write data, ECC and remap block info. + * + * \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. + */ +static bool mt29f_write(Mt29f *chip, uint32_t page, const void *buf, size_t size) +{ + struct RemapInfo remap_info; + uint32_t *nand_buf = (uint32_t *)mt29f_dataBuffer(chip); + uint32_t remapped_page = PAGE(chip->block_map[BLOCK(page)]) + PAGE_IN_BLOCK(page); + + ASSERT(size <= CONFIG_MT29F_DATA_SIZE); + + if (page != remapped_page) + LOG_INFO("mt29f_write: remapped block: blk %d->%d, pg %ld->%ld\n", + BLOCK(page), chip->block_map[BLOCK(page)], page, remapped_page); + + // Data + memset(nand_buf, 0xff, MT29F_PAGE_SIZE); + memcpy(nand_buf, buf, size); + if (!mt29f_writePage(chip, remapped_page, 0)) + return false; + + // ECC + memset(nand_buf, 0xff, CONFIG_MT29F_SPARE_SIZE); + mt29f_computeEcc(chip, buf, size, nand_buf, MT29F_ECC_NWORDS); + + // Remap info + remap_info.tag = MT29F_REMAP_TAG; + remap_info.mapped_blk = BLOCK(page); + memcpy((char *)nand_buf + MT29F_REMAP_TAG_OFFSET, &remap_info, sizeof(remap_info)); + + return mt29f_writePage(chip, remapped_page, CONFIG_MT29F_DATA_SIZE); +} + + +/* + * Check if the given block is marked bad: ONFI standard mandates + * 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) +{ + uint8_t *first_byte = (uint8_t *)mt29f_dataBuffer(chip); + bool good; + + // Check first byte in spare area of first page in block + mt29f_readPage(chip, PAGE(blk), CONFIG_MT29F_DATA_SIZE); + good = *first_byte != 0; + + if (!good) + LOG_INFO("mt29f: bad block %d\n", blk); + + return good; +} + + +/* + * Return the main partition block remapped on given block in the remap + * partition (dest_blk). + */ +static int getBadBlockFromRemapBlock(Mt29f *chip, uint16_t dest_blk) +{ + struct RemapInfo *remap_info = (struct RemapInfo *)mt29f_dataBuffer(chip); + + if (!mt29f_readPage(chip, PAGE(dest_blk), CONFIG_MT29F_DATA_SIZE + MT29F_REMAP_TAG_OFFSET)) + return -1; + + if (remap_info->tag == MT29F_REMAP_TAG) + return remap_info->mapped_blk; + else + return -1; +} + + +/* + * Set a block remapping: src_blk (a block in main data partition) is remappend + * on dest_blk (block in reserved remapped blocks partition). + */ +static bool setMapping(Mt29f *chip, uint32_t src_blk, uint32_t dest_blk) +{ + struct RemapInfo *remap_info = (struct RemapInfo *)mt29f_dataBuffer(chip); + + LOG_INFO("mt29f, setMapping(): src=%ld dst=%ld\n", src_blk, dest_blk); + + if (!mt29f_readPage(chip, PAGE(dest_blk), CONFIG_MT29F_DATA_SIZE + MT29F_REMAP_TAG_OFFSET)) + return false; + + remap_info->tag = MT29F_REMAP_TAG; + remap_info->mapped_blk = src_blk; + + return mt29f_writePage(chip, PAGE(dest_blk), CONFIG_MT29F_DATA_SIZE + MT29F_REMAP_TAG_OFFSET); +} + + +/* + * Get a new block from the remap partition to use as a substitute + * for a bad block. + */ +static uint16_t getFreeRemapBlock(Mt29f *chip) +{ + int blk; + + for (blk = chip->remap_start; blk < CONFIG_MT29F_NUM_BLOCK; blk++) + { + if (blockIsGood(chip, blk)) + { + chip->remap_start = blk + 1; + return blk; + } + } + + LOG_ERR("mt29f: reserved blocks for bad block remapping exhausted!\n"); + return 0; +} + + +/* + * Check if NAND is initialized. + */ +static bool chipIsMarked(Mt29f *chip) +{ + return getBadBlockFromRemapBlock(chip, MT29F_NUM_USER_BLOCKS) != -1; +} + + +/* + * Initialize NAND (format). Scan NAND for factory marked bad blocks. + * All bad blocks found are remapped to the remap partition: each + * block in the remap partition used to remap bad blocks is marked. + */ +static void initBlockMap(Mt29f *chip) +{ + int b, last; + + // Default is for each block to not be remapped + for (b = 0; b < CONFIG_MT29F_NUM_BLOCK; b++) + chip->block_map[b] = b; + chip->remap_start = MT29F_NUM_USER_BLOCKS; + + if (chipIsMarked(chip)) + { + LOG_INFO("mt29f: found initialized NAND, searching for remapped blocks\n"); + + // Scan for assigned blocks in remap area + for (b = last = MT29F_NUM_USER_BLOCKS; b < CONFIG_MT29F_NUM_BLOCK; b++) + { + int remapped_blk = getBadBlockFromRemapBlock(chip, b); + if (remapped_blk != -1 && remapped_blk != b) + { + LOG_INFO("mt29f: found remapped block %d->%d\n", remapped_blk, b); + chip->block_map[remapped_blk] = b; + last = b + 1; + } + } + chip->remap_start = last; + } + else + { + bool remapped_anything = false; + + LOG_INFO("mt29f: found new NAND, searching for bad blocks\n"); + + for (b = 0; b < MT29F_NUM_USER_BLOCKS; b++) + { + if (!blockIsGood(chip, b)) + { + chip->block_map[b] = getFreeRemapBlock(chip); + setMapping(chip, b, chip->block_map[b]); + remapped_anything = true; + LOG_INFO("mt29f: 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 + * remap to mark NAND and detect we already scanned it next time. + */ + if (!remapped_anything) + { + setMapping(chip, MT29F_NUM_USER_BLOCKS, MT29F_NUM_USER_BLOCKS); + LOG_INFO("mt29f: no bad block founds, marked NAND\n"); + } + } +} + + +/** + * Reset bad blocks map and erase all blocks. + * + * \note DON'T USE on production chips: this function will try to erase + * factory marked bad blocks too. + */ +void mt29f_format(Mt29f *chip) +{ + int b; + + for (b = 0; b < CONFIG_MT29F_NUM_BLOCK; b++) + { + LOG_INFO("mt29f: erasing block %d\n", b); + chip->block_map[b] = b; + mt29f_blockErase(chip, b); + } + chip->remap_start = MT29F_NUM_USER_BLOCKS; +} + +#ifdef _DEBUG + +/* + * Create some bad blocks, erasing them and writing the bad block mark. + */ +void mt29f_ruinSomeBlocks(Mt29f *chip) +{ + int bads[] = { 7, 99, 555, 1003, 1004, 1432 }; + unsigned i; + + LOG_INFO("mt29f: erasing mark\n"); + mt29f_blockErase(chip, MT29F_NUM_USER_BLOCKS); + + for (i = 0; i < countof(bads); i++) + { + LOG_INFO("mt29f: erasing block %d\n", bads[i]); + mt29f_blockErase(chip, bads[i]); + + LOG_INFO("mt29f: marking page %d as bad\n", PAGE(bads[i])); + memset(mt29f_dataBuffer(chip), 0, CONFIG_MT29F_SPARE_SIZE); + mt29f_writePage(chip, PAGE(bads[i]), CONFIG_MT29F_DATA_SIZE); + } +} + +#endif + +static bool commonInit(Mt29f *chip, struct Heap *heap, unsigned chip_select) +{ + memset(chip, 0, sizeof(Mt29f)); + + DB(chip->fd.priv.type = KBT_NAND); + chip->fd.blk_size = MT29F_BLOCK_SIZE; + chip->fd.blk_cnt = MT29F_NUM_USER_BLOCKS; + + chip->chip_select = chip_select; + chip->block_map = heap_allocmem(heap, CONFIG_MT29F_NUM_BLOCK * sizeof(*chip->block_map)); + if (!chip->block_map) + { + LOG_ERR("mt29f: error allocating block map\n"); + return false; + } + + mt29f_hwInit(chip); + chipReset(chip); + initBlockMap(chip); + + return true; +} + + +/**************** Kblock interface ****************/ + + +static size_t mt29f_writeDirect(struct KBlock *kblk, block_idx_t idx, const void *buf, size_t offset, size_t size) +{ + ASSERT(offset <= MT29F_BLOCK_SIZE); + ASSERT(offset % CONFIG_MT29F_DATA_SIZE == 0); + ASSERT(size <= MT29F_BLOCK_SIZE); + ASSERT(size % CONFIG_MT29F_DATA_SIZE == 0); + + //LOG_INFO("mt29f_writeDirect: idx=%ld offset=%d size=%d\n", idx, offset, size); + + mt29f_blockErase(MT29F_CAST(kblk), idx); + + while (offset < size) + { + uint32_t page = PAGE(idx) + (offset / CONFIG_MT29F_DATA_SIZE); + + if (!mt29f_write(MT29F_CAST(kblk), page, buf, CONFIG_MT29F_DATA_SIZE)) + break; + + offset += CONFIG_MT29F_DATA_SIZE; + buf = (const char *)buf + CONFIG_MT29F_DATA_SIZE; + } + + return offset; +} + + +static size_t mt29f_readDirect(struct KBlock *kblk, block_idx_t idx, void *buf, size_t offset, size_t size) +{ + uint32_t page; + size_t read_size; + size_t read_offset; + size_t nread = 0; + + ASSERT(offset < MT29F_BLOCK_SIZE); + ASSERT(size <= MT29F_BLOCK_SIZE); + + //LOG_INFO("mt29f_readDirect: idx=%ld offset=%d size=%d\n", idx, offset, size); + + while (nread < size) + { + page = PAGE(idx) + (offset / CONFIG_MT29F_DATA_SIZE); + read_offset = offset % CONFIG_MT29F_DATA_SIZE; + read_size = MIN(size, CONFIG_MT29F_DATA_SIZE - read_offset); + + if (!mt29f_read(MT29F_CAST(kblk), page, (char *)buf + nread, read_offset, read_size)) + break; + + offset += read_size; + nread += read_size; + } + + return nread; +} + + +static int mt29f_error(struct KBlock *kblk) +{ + Mt29f *chip = MT29F_CAST(kblk); + return chip->status; +} + + +static void mt29f_clearError(struct KBlock *kblk) +{ + Mt29f *chip = MT29F_CAST(kblk); + chip->status = 0; +} + + +static const KBlockVTable mt29f_buffered_vt = +{ + .readDirect = mt29f_readDirect, + .writeDirect = mt29f_writeDirect, + + .readBuf = kblock_swReadBuf, + .writeBuf = kblock_swWriteBuf, + .load = kblock_swLoad, + .store = kblock_swStore, + + .error = mt29f_error, + .clearerr = mt29f_clearError, +}; + +static const KBlockVTable mt29f_unbuffered_vt = +{ + .readDirect = mt29f_readDirect, + .writeDirect = mt29f_writeDirect, + + .error = mt29f_error, + .clearerr = mt29f_clearError, +}; + + +/** + * Initialize NAND kblock driver in buffered mode. + */ +bool mt29f_init(Mt29f *chip, struct Heap *heap, unsigned chip_select) +{ + if (!commonInit(chip, heap, chip_select)) + return false; + + chip->fd.priv.vt = &mt29f_buffered_vt; + chip->fd.priv.flags |= KB_BUFFERED; + + chip->fd.priv.buf = heap_allocmem(heap, MT29F_BLOCK_SIZE); + if (!chip->fd.priv.buf) + { + LOG_ERR("mt29f: error allocating block buffer\n"); + return false; + } + + // Load the first block in the cache + return mt29f_readDirect(&chip->fd, 0, chip->fd.priv.buf, 0, chip->fd.blk_size); +} + + +/** + * Initialize NAND kblock driver in unbuffered mode. + */ +bool mt29f_initUnbuffered(Mt29f *chip, struct Heap *heap, unsigned chip_select) +{ + if (!commonInit(chip, heap, chip_select)) + return false; + + chip->fd.priv.vt = &mt29f_unbuffered_vt; + return true; +} + diff --git a/bertos/drv/mt29f.h b/bertos/drv/mt29f.h index e2517994..5c678ea6 100644 --- a/bertos/drv/mt29f.h +++ b/bertos/drv/mt29f.h @@ -31,9 +31,6 @@ * * \brief Micron MT29F serial NAND driver * -* This module allows read/write access to Micron MT29F serial -* NANDs. -* * \author Stefano Fedrigo * * $WIZ$ module_name = "mt29f" @@ -49,6 +46,10 @@ #include +// Define log settings for cfg/log.h +#define LOG_LEVEL CONFIG_MT29F_LOG_LEVEL +#define LOG_FORMAT CONFIG_MT29F_LOG_FORMAT + /** * \name Error codes. * \{ @@ -60,6 +61,39 @@ #define MT29F_ERR_ECC BV(5) ///< Unrecoverable ECC error /** \} */ +#define MT29F_PAGE_SIZE (CONFIG_MT29F_DATA_SIZE + CONFIG_MT29F_SPARE_SIZE) +#define MT29F_BLOCK_SIZE (CONFIG_MT29F_DATA_SIZE * CONFIG_MT29F_PAGES_PER_BLOCK) + +// Number of usable blocks, and index of first remapping block +#define MT29F_NUM_USER_BLOCKS (CONFIG_MT29F_NUM_BLOCK - CONFIG_MT29F_NUM_REMAP_BLOCKS) + + +// NAND commands +#define MT29F_CMD_READ_1 0x00 +#define MT29F_CMD_READ_2 0x30 +#define MT29F_CMD_COPYBACK_READ_1 0x00 +#define MT29F_CMD_COPYBACK_READ_2 0x35 +#define MT29F_CMD_COPYBACK_PROGRAM_1 0x85 +#define MT29F_CMD_COPYBACK_PROGRAM_2 0x10 +#define MT29F_CMD_RANDOM_OUT 0x05 +#define MT29F_CMD_RANDOM_OUT_2 0xE0 +#define MT29F_CMD_RANDOM_IN 0x85 +#define MT29F_CMD_READID 0x90 +#define MT29F_CMD_WRITE_1 0x80 +#define MT29F_CMD_WRITE_2 0x10 +#define MT29F_CMD_ERASE_1 0x60 +#define MT29F_CMD_ERASE_2 0xD0 +#define MT29F_CMD_STATUS 0x70 +#define MT29F_CMD_RESET 0xFF + + +// Get block from page +#define PAGE(blk) ((blk) * CONFIG_MT29F_PAGES_PER_BLOCK) + +// Page from block and page in block +#define BLOCK(page) ((uint16_t)((page) / CONFIG_MT29F_PAGES_PER_BLOCK)) +#define PAGE_IN_BLOCK(page) ((uint16_t)((page) % CONFIG_MT29F_PAGES_PER_BLOCK)) + /** * MT29F context. @@ -104,5 +138,15 @@ void mt29f_format(Mt29f *chip); void mt29f_ruinSomeBlocks(Mt29f *chip); #endif +// Hardware specific functions, implemented by cpu specific module +bool mt29f_waitReadyBusy(Mt29f *chip, time_t timeout); +bool mt29f_waitTransferComplete(Mt29f *chip, time_t timeout); +void mt29f_sendCommand(Mt29f *chip, uint32_t cmd1, uint32_t cmd2, + int num_cycles, uint32_t cycle0, uint32_t cycle1234); +uint8_t mt29f_getChipStatus(Mt29f *chip); +void *mt29f_dataBuffer(Mt29f *chip); +bool mt29f_checkEcc(Mt29f *chip); +void mt29f_computeEcc(Mt29f *chip, const void *buf, size_t size, uint32_t *ecc, size_t ecc_size); +void mt29f_hwInit(Mt29f *chip); #endif /* DRV_MT29F_H */