diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mtd/nand/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/mx31_nand.c | 969 |
2 files changed, 970 insertions, 0 deletions
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 89ccec2..975a1b8 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -46,6 +46,7 @@ COBJS-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o COBJS-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o COBJS-$(CONFIG_NAND_PLAT) += nand_plat.o +COBJS-$(CONFIG_MX31_NAND) += mx31_nand.o endif COBJS := $(COBJS-y) diff --git a/drivers/mtd/nand/mx31_nand.c b/drivers/mtd/nand/mx31_nand.c new file mode 100644 index 0000000..325e0f0 --- /dev/null +++ b/drivers/mtd/nand/mx31_nand.c @@ -0,0 +1,969 @@ +/* + * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <common.h> +#include <nand.h> +#include <asm-arm/arch/mx31-regs.h> + +/* + * Define delays in microsec for NAND device operations + */ +#define TROP_US_DELAY 2000 + +/* + * Macros to get byte and bit positions of ECC + */ +#define COLPOS(x) ((x) >> 4) +#define BITPOS(x) ((x) & 0xf) + +/* Define single bit Error positions in Main & Spare area */ +#define MAIN_SINGLEBIT_ERROR 0x4 +#define SPARE_SINGLEBIT_ERROR 0x1 + +struct nand_info { + int oob; + int read_status; + int largepage; + u16 col; +}; + +static struct nand_info nandinfo; +static int ecc_disabled; + +/* + * OOB placement block for use with hardware ecc generation + */ +static struct nand_oobinfo nand_hw_eccoob_8 = { + .useecc = MTD_NANDECC_AUTOPL_USR, + .eccbytes = 5, + .eccpos = {6, 7, 8, 9, 10}, + .oobfree = { + {0, 5}, + {11, 5} + } +}; + +static struct nand_oobinfo nand_hw_eccoob_2k = { + .useecc = MTD_NANDECC_AUTOPL_USR, + .eccbytes = 20, + .eccpos = {6, 7, 8, 9, 10, 22, 23, 24, 25, 26, + 38, 39, 40, 41, 42, 54, 55, 56, 57, 58}, + .oobfree = { + {0, 5}, + {11, 10}, + {27, 10}, + {43, 10}, + {59, 5} + } +}; + +/* Define some generic bad / good block scan pattern which are used + * while scanning a device for factory marked good / bad blocks. */ +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; + +static struct nand_bbt_descr smallpage_memorybased = { + .options = NAND_BBT_SCAN2NDPAGE, + .offs = 5, + .len = 1, + .pattern = scan_ff_pattern +}; + +static struct nand_bbt_descr largepage_memorybased = { + .options = 0, + .offs = 0, + .len = 2, + .pattern = scan_ff_pattern +}; + +/* Generic flash bbt decriptors */ +static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' }; +static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 0, + .len = 4, + .veroffs = 4, + .maxblocks = 4, + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 0, + .len = 4, + .veroffs = 4, + .maxblocks = 4, + .pattern = mirror_pattern +}; + +/** + * memcpy variant that copies 32 bit words. This is needed since the + * NFC only allows 32 bit accesses. Added for U-boot. + */ +static void *memcpy_32(void *dest, const void *src, size_t n) +{ + u32 *dst_32 = (u32 *) dest; + const u32 *src_32 = (u32 *) src; + + while (n > 0) { + *dst_32++ = *src_32++; + n -= 4; + } + + return dest; +} + +/** + * This function polls the NANDFC to wait for the basic operation to + * complete by checking the INT bit of config2 register. + * + * @param max_retries number of retry attempts (separated by 1 us) + */ +static void wait_op_done(int max_retries) +{ + while (max_retries-- > 0) { + if (NFC_CONFIG2 & NFC_INT) { + NFC_CONFIG2 &= ~NFC_INT; + break; + } + udelay(1); + } + if (max_retries <= 0) + DEBUG(MTD_DEBUG_LEVEL0, "wait: INT not set\n"); +} + +/** + * This function issues the specified command to the NAND device and + * waits for completion. + * + * @param cmd command for NAND Flash + */ +static void send_cmd(u16 cmd) +{ + DEBUG(MTD_DEBUG_LEVEL3, "send_cmd(0x%x)\n", cmd); + + NFC_FLASH_CMD = (u16) cmd; + NFC_CONFIG2 = NFC_CMD; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY); +} + +/** + * This function sends an address (or partial address) to the + * NAND device. The address is used to select the source/destination for + * a NAND command. + * + * @param addr address to be written to NFC. + * @param islast 1 if this is the last address cycle for command + */ +static void send_addr(u16 addr) +{ + DEBUG(MTD_DEBUG_LEVEL3, "send_addr(0x%x %d)\n", addr); + + NFC_FLASH_ADDR = addr; + NFC_CONFIG2 = NFC_ADDR; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY); +} + +/** + * This function requests the NANDFC to initate the transfer + * of data currently in the NANDFC RAM buffer to the NAND device. + * + * @param buf_id Specify Internal RAM Buffer number (0-3) + * @param oob set to 1 if only the spare area is transferred + */ +static void send_prog_page(u8 buf_id) +{ + DEBUG(MTD_DEBUG_LEVEL3, "send_prog_page (%d)\n", nandinfo.oob); + + /* NANDFC buffer 0 is used for page read/write */ + + NFC_BUF_ADDR = buf_id; + + /* Configure spare or page+spare access */ + if (!nandinfo.largepage) { + if (nandinfo.oob) + NFC_CONFIG1 |= NFC_SP_EN; + else + NFC_CONFIG1 &= ~NFC_SP_EN; + } + NFC_CONFIG2 = NFC_INPUT; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY); +} + +/** + * This function will correct the single bit ECC error + * + * @param buf_id Specify Internal RAM Buffer number (0-3) + * @param eccpos Ecc byte and bit position + * @param oob set to 1 if only spare area needs correction + */ +static void mxc_nd_correct_error(u8 buf_id, u16 eccpos, int oob) +{ + u16 col; + u8 pos; + u16 *buf; + + /* Get col & bit position of error + these macros works for both 8 & 16 bits */ + col = COLPOS(eccpos); /* Get half-word position */ + pos = BITPOS(eccpos); /* Get bit position */ + + DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nd_correct_error (col=%d pos=%d)\n", col, pos); + + /* Set the pointer for main / spare area */ + if (!oob) + buf = (u16 *)(MAIN_AREA0 + col + (256 * buf_id)); + else + buf = (u16 *)(SPARE_AREA0 + col + (8 * buf_id)); + + /* Fix the data */ + *buf ^= 1 << pos; +} + +/** + * This function will maintains state of single bit Error + * in Main & spare area + * + * @param buf_id Specify Internal RAM Buffer number (0-3) + * @param spare set to 1 if only spare area needs correction + */ +static void mxc_nd_correct_ecc(u8 buf_id, int spare) +{ + u16 value, ecc_status; + + /* Read the ECC result */ + ecc_status = NFC_ECC_STATUS_RESULT; + DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nd_correct_ecc (Ecc status=%x)\n", ecc_status); + + if (((ecc_status & 0xC) == MAIN_SINGLEBIT_ERROR) + || ((ecc_status & 0x3) == SPARE_SINGLEBIT_ERROR)) { + if (ecc_disabled) { + if ((ecc_status & 0xC) == MAIN_SINGLEBIT_ERROR) { + value = NFC_RSLTMAIN_AREA; + /* Correct single bit error in Mainarea + NFC will not correct the error in + current page */ + mxc_nd_correct_error(buf_id, value, 0); + } + if ((ecc_status & 0x3) == SPARE_SINGLEBIT_ERROR) { + value = NFC_RSLTSPARE_AREA; + /* Correct single bit error in Mainarea + NFC will not correct the error in + current page */ + mxc_nd_correct_error(buf_id, value, 1); + } + + } else { + /* Disable ECC */ + NFC_CONFIG1 &= ~NFC_ECC_EN; + ecc_disabled = 1; + } + } else if (ecc_status == 0) { + if (ecc_disabled) { + /* Enable ECC */ + NFC_CONFIG1 |= NFC_ECC_EN; + ecc_disabled = 0; + } + } /* else 2-bit Error. Do nothing */ +} + +/** + * This function requests the NANDFC to initated the transfer + * of data from the NAND device into in the NANDFC ram buffer. + * + * @param buf_id Specify Internal RAM Buffer number (0-3) + * @param oob set 1 if only the spare area is + * transferred + */ +static void send_read_page(u8 buf_id) +{ + DEBUG(MTD_DEBUG_LEVEL3, "send_read_page (%d)\n", nandinfo.oob); + + /* NANDFC buffer 0 is used for page read/write */ + NFC_BUF_ADDR = buf_id; + + /* Configure spare or page+spare access */ + if (!nandinfo.largepage) { + if (nandinfo.oob) + NFC_CONFIG1 |= NFC_SP_EN; + else + NFC_CONFIG1 &= ~NFC_SP_EN; + } + + NFC_CONFIG2 = NFC_OUTPUT; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY); + + /* If there are single bit errors in + two consecutive page reads then + the error is not corrected by the + NFC for the second page. + Correct single bit error in driver */ + + mxc_nd_correct_ecc(buf_id, nandinfo.oob); +} + +/** + * This function requests the NANDFC to perform a read of the + * NAND device ID. + */ +static void send_read_id(void) +{ + /* NANDFC buffer 0 is used for device ID output */ + NFC_BUF_ADDR = 0x0; + + /* Read ID into main buffer */ + NFC_CONFIG1 &= ~NFC_SP_EN; + NFC_CONFIG2 = NFC_ID; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY); +} + +/** + * This function requests the NANDFC to perform a read of the + * NAND device status and returns the current status. + * + * @return device status + */ +static u16 get_dev_status(void) +{ + volatile u16 *mainbuf = MAIN_AREA1; + u32 store; + u16 ret; + /* Issue status request to NAND device */ + + /* store the main area1 first word, later do recovery */ + store = *((u32 *) mainbuf); + /* + * NANDFC buffer 1 is used for device status to prevent + * corruption of read/write buffer on status requests. + */ + NFC_BUF_ADDR = 1; + + /* Read status into main buffer */ + NFC_CONFIG1 &= ~NFC_SP_EN; + NFC_CONFIG2 = NFC_STATUS; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY); + + /* Status is placed in first word of main buffer */ + /* get status, then recovery area 1 data */ + ret = mainbuf[0]; + *((u32 *) mainbuf) = store; + + return ret; +} + +static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + /* + * If HW ECC is enabled, we turn it on during init. There is + * no need to enable again here. + */ +} + +static int mxc_nand_correct_data(struct mtd_info *mtd, unsigned char *dat, + unsigned char *read_ecc, unsigned char *calc_ecc) +{ + /* + * 1-Bit errors are automatically corrected in HW. No need for + * additional correction. 2-Bit errors cannot be corrected by + * HW ECC, so we need to return failure + */ + u16 ecc_status = NFC_ECC_STATUS_RESULT; + + if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) { + DEBUG(MTD_DEBUG_LEVEL0, + "MXC_NAND: HWECC uncorrectable 2-bit ECC error\n"); + return -1; + } + + return 0; +} + +static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *dat, + unsigned char *ecc_code) +{ + /* + * Just return success. HW ECC does not read/write the NFC spare + * buffer. Only the FLASH spare area contains the calcuated ECC. + */ + return 0; +} + +/** + * This function reads byte from the NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * + * @return data read from the NAND Flash + */ +static unsigned char mxc_nand_read_byte(struct mtd_info *mtd) +{ + unsigned char ret_val = 0; + u16 col, rd_word; + volatile u16 *mainbuf = MAIN_AREA0; + volatile u16 *sparebuf = SPARE_AREA0; + + /* Check for status request */ + if (nandinfo.read_status) + return get_dev_status() & 0xFF; + + /* Get column for 16-bit access */ + col = nandinfo.col >> 1; + + /* If we are accessing the spare region */ + if (nandinfo.oob) + rd_word = sparebuf[col]; + else + rd_word = mainbuf[col]; + + /* Pick upper/lower byte of word from RAM buffer */ + if (nandinfo.col & 0x1) + ret_val = (rd_word >> 8) & 0xFF; + else + ret_val = rd_word & 0xFF; + + /* Update saved column address */ + nandinfo.col++; + + return ret_val; +} + +/** + * This function reads word from the NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * + * @return data read from the NAND Flash + */ +static u16 mxc_nand_read_word(struct mtd_info *mtd) +{ + u16 col; + u16 rd_word, ret_val; + volatile u16 *p; + + DEBUG(MTD_DEBUG_LEVEL3, "mxc_nand_read_word(col = %d)\n", nandinfo.col); + + col = nandinfo.col; + /* Adjust saved column address */ + if (col < mtd->oobblock && nandinfo.oob) + col += mtd->oobblock; + + if (col < mtd->oobblock) + p = (MAIN_AREA0) + (col >> 1); + else + p = (SPARE_AREA0) + ((col - mtd->oobblock) >> 1); + + if (col & 1) { + rd_word = *p; + ret_val = (rd_word >> 8) & 0xff; + rd_word = *(p + 1); + ret_val |= (rd_word << 8) & 0xff00; + + } else + ret_val = *p; + + /* Update saved column address */ + nandinfo.col = col + 2; + + return ret_val; +} + +/** + * This function writes data of length \b len to buffer \b buf. The data + * to be written on NAND Flash is first copied to RAMbuffer. After the + * Data Input Operation by the NFC, the data is written to NAND Flash. + * + * @param mtd MTD structure for the NAND Flash + * @param buf data to be written to NAND Flash + * @param len number of bytes to be written + */ +static void mxc_nand_write_buf(struct mtd_info *mtd, + const unsigned char *buf, int len) +{ + int n; + int col; + int i = 0; + + DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_write_buf(col = %d, len = %d)\n", nandinfo.col, len); + + col = nandinfo.col; + + /* Adjust saved column address */ + if (col < mtd->oobblock && nandinfo.oob) + col += mtd->oobblock; + + n = mtd->oobblock + mtd->oobsize - col; + if (len > mtd->oobblock + mtd->oobsize - col) + DEBUG(MTD_DEBUG_LEVEL1, "Error: too much data.\n"); + + n = min(len, n); + + DEBUG(MTD_DEBUG_LEVEL3, + "%s:%d: col = %d, n = %d\n", __FUNCTION__, __LINE__, col, n); + + while (n) { + volatile u32 *p; + if (col < mtd->oobblock) + p = (volatile u32 *)((ulong) (MAIN_AREA0) + (col & ~3)); + else + p = (volatile u32 *)((ulong) (SPARE_AREA0) - + mtd->oobblock + (col & ~3)); + + DEBUG(MTD_DEBUG_LEVEL3, "%s:%d: p = %p\n", + __FUNCTION__, __LINE__, p); + + if (((col | (int)&buf[i]) & 3) || n < 16) { + u32 data = 0; + + if (col & 3 || n < 4) + data = *p; + + switch (col & 3) { + case 0: + if (n) { + data = (data & 0xffffff00) | + (buf[i++] << 0); + n--; + col++; + } + case 1: + if (n) { + data = (data & 0xffff00ff) | + (buf[i++] << 8); + n--; + col++; + } + case 2: + if (n) { + data = (data & 0xff00ffff) | + (buf[i++] << 16); + n--; + col++; + } + case 3: + if (n) { + data = (data & 0x00ffffff) | + (buf[i++] << 24); + n--; + col++; + } + } + + *p = data; + } else { + int m = mtd->oobblock - col; + + if (col >= mtd->oobblock) + m += mtd->oobsize; + + m = min(n, m) & ~3; + + DEBUG(MTD_DEBUG_LEVEL3, + "%s:%d: n = %d, m = %d, i = %d, col = %d\n", + __FUNCTION__, __LINE__, n, m, i, col); + + memcpy_32((void *)(p), &buf[i], m); + col += m; + i += m; + n -= m; + } + } + /* Update saved column address */ + nandinfo.col = col; +} + +/** + * This function id is used to read the data buffer from the NAND Flash. To + * read the data from NAND Flash first the data output cycle is initiated by + * the NFC, which copies the data to RAMbuffer. This data of length \b len is + * then copied to buffer \b buf. + * + * @param mtd MTD structure for the NAND Flash + * @param buf data to be read from NAND Flash + * @param len number of bytes to be read + */ +static void mxc_nand_read_buf(struct mtd_info *mtd, unsigned char *buf, int len) +{ + int n; + int col; + int i = 0; + + DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_read_buf(col = %d, len = %d)\n", nandinfo.col, len); + + col = nandinfo.col; + /** + * Adjust saved column address + * for nand_read_oob will pass col within oobsize + */ + if (col < mtd->oobblock && nandinfo.oob) + col += mtd->oobblock; + + n = mtd->oobblock + mtd->oobsize - col; + n = min(len, n); + + while (n) { + volatile u32 *p; + + if (col < mtd->oobblock) + p = (volatile u32 *)((ulong) (MAIN_AREA0) + (col & ~3)); + else + p = (volatile u32 *)((ulong) (SPARE_AREA0) - + mtd->oobblock + (col & ~3)); + + if (((col | (int)&buf[i]) & 3) || n < 16) { + u32 data; + + data = *p; + switch (col & 3) { + case 0: + if (n) { + buf[i++] = (u8) (data); + n--; + col++; + } + case 1: + if (n) { + buf[i++] = (u8) (data >> 8); + n--; + col++; + } + case 2: + if (n) { + buf[i++] = (u8) (data >> 16); + n--; + col++; + } + case 3: + if (n) { + buf[i++] = (u8) (data >> 24); + n--; + col++; + } + } + } else { + int m = mtd->oobblock - col; + + if (col >= mtd->oobblock) + m += mtd->oobsize; + + m = min(n, m) & ~3; + memcpy_32(&buf[i], (void *)(p), m); + col += m; + i += m; + n -= m; + } + } + /* Update saved column address */ + nandinfo.col = col; +} + +/** + * This function is used by the upper layer to verify the data in NAND Flash + * with the data in the \b buf. + * + * @param mtd MTD structure for the NAND Flash + * @param buf data to be verified + * @param len length of the data to be verified + * + * @return -EFAULT if error else 0 + */ +static int +mxc_nand_verify_buf(struct mtd_info *mtd, const unsigned char *buf, int len) +{ + return -1; /* Was -EFAULT */ +} + +/** + * This function is used by upper layer for select and deselect of the NAND + * chip. + * + * @param mtd MTD structure for the NAND Flash + * @param chip val indicating select or deselect + */ +static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) +{ +} + +/** + * This function is used by the upper layer to write command to NAND Flash + * for different operations to be carried out on NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * @param command command for NAND Flash + * @param column column offset for the page read + * @param page_addr page to be read from NAND Flash + */ +static void mxc_nand_command(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", + command, column, page_addr); + + /* Reset command state information */ + nandinfo.read_status = 0; + nandinfo.oob = 0; + + /* Command pre-processing step */ + switch (command) { + + case NAND_CMD_STATUS: + nandinfo.col = 0; + nandinfo.read_status = 1; + break; + + case NAND_CMD_READ0: + nandinfo.col = column; + break; + + case NAND_CMD_READOOB: + nandinfo.col = column; + nandinfo.oob = 1; + if (nandinfo.largepage) + command = NAND_CMD_READ0; + break; + + case NAND_CMD_SEQIN: + if (column >= mtd->oobblock) { + /* write oob routine caller */ + if (nandinfo.largepage) { + /* + * FIXME: before send SEQIN command for + * write OOB, we must read one page out. + * For 2K nand has no READ1 command to set + * current HW pointer to spare area, we must + * write the whole page including OOB together. + */ + /* call itself to read a page */ + mxc_nand_command(mtd, NAND_CMD_READ0, 0, + page_addr); + } + nandinfo.col = column - mtd->oobblock; + nandinfo.oob = 1; + /* Set program pointer to spare region */ + if (!nandinfo.largepage) + send_cmd(NAND_CMD_READOOB); + } else { + nandinfo.oob = 0; + nandinfo.col = column; + /* Set program pointer to page start */ + if (!nandinfo.largepage) + send_cmd(NAND_CMD_READ0); + } + break; + + case NAND_CMD_PAGEPROG: + if (ecc_disabled) { + /* Enable Ecc for page writes */ + NFC_CONFIG1 |= NFC_ECC_EN; + } + send_prog_page(0); + + if (nandinfo.largepage) { + /* data in 4 areas datas */ + send_prog_page(1); + send_prog_page(2); + send_prog_page(3); + } + + break; + + case NAND_CMD_ERASE1: + break; + } + + /* + * Write out the command to the device. + */ + send_cmd(command); + + /* + * Write out column address, if necessary + */ + if (column != -1) { + /* + * MXC NANDFC can only perform full page+spare or + * spare-only read/write. When the upper layers + * layers perform a read/write buf operation, + * we will used the saved column adress to index into + * the full page. + */ + send_addr(0); + if (nandinfo.largepage) + /* another col addr cycle for 2k page */ + send_addr(0); + } + + /* + * Write out page address, if necessary + */ + if (page_addr != -1) { + /* paddr_0 - p_addr_7 */ + send_addr((page_addr & 0xff)); + + if (nandinfo.largepage) { + /* One more address cycle for higher + * density devices */ + + if (mtd->size >= 0x10000000) { + /* paddr_8 - paddr_15 */ + send_addr((page_addr >> 8) & 0xff); + send_addr((page_addr >> 16) & 0xff); + } else + /* paddr_8 - paddr_15 */ + send_addr((page_addr >> 8) & 0xff); + } else { + /* One more address cycle for higher + * density devices */ + + if (mtd->size >= 0x4000000) { + /* paddr_8 - paddr_15 */ + send_addr((page_addr >> 8) & 0xff); + send_addr((page_addr >> 16) & 0xff); + } else + /* paddr_8 - paddr_15 */ + send_addr((page_addr >> 8) & 0xff); + } + } + + /* + * Command post-processing step + */ + switch (command) { + + case NAND_CMD_RESET: + break; + + case NAND_CMD_READOOB: + case NAND_CMD_READ0: + if (nandinfo.largepage) { + /* send read confirm command */ + send_cmd(NAND_CMD_READSTART); + /* read for each AREA */ + send_read_page(0); + send_read_page(1); + send_read_page(2); + send_read_page(3); + } else + send_read_page(0); + break; + + case NAND_CMD_READID: + send_read_id(); + break; + + case NAND_CMD_PAGEPROG: + if (ecc_disabled) { + /* Disable Ecc after page writes */ + NFC_CONFIG1 &= ~NFC_ECC_EN; + } + break; + + case NAND_CMD_ERASE2: + break; + } +} + +static int mxc_nand_scan_bbt(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + + /* Config before scanning */ + /* Do not rely on NFMS_BIT, set/clear NFMS bit based + * on mtd->oobblock */ + if (mtd->oobblock == 2048) + NFMS |= 1 << NFMS_BIT; + else if ((NFMS >> NFMS_BIT) & 0x1) + NFMS &= ~(1 << NFMS_BIT); + + /* use flash based bbt */ + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; + + /* update flash based bbt */ + this->options |= NAND_USE_FLASH_BBT; + + if (!this->badblock_pattern) { + if (nandinfo.largepage) + this->badblock_pattern = &smallpage_memorybased; + else + this->badblock_pattern = (mtd->oobblock > 512) ? + &largepage_memorybased : &smallpage_memorybased; + } + /* Build bad block table */ + return nand_scan_bbt(mtd, this->badblock_pattern); +} + +int board_nand_init(struct nand_chip *nand) +{ + nand->chip_delay = 0; + + nand->cmdfunc = mxc_nand_command; + nand->select_chip = mxc_nand_select_chip; + nand->read_byte = mxc_nand_read_byte; + nand->read_word = mxc_nand_read_word; + nand->write_buf = mxc_nand_write_buf; + nand->read_buf = mxc_nand_read_buf; + nand->verify_buf = mxc_nand_verify_buf; + nand->scan_bbt = mxc_nand_scan_bbt; + nand->calculate_ecc = mxc_nand_calculate_ecc; + nand->correct_data = mxc_nand_correct_data; + nand->enable_hwecc = mxc_nand_enable_hwecc; + nand->eccmode = NAND_ECC_HW3_512; + nand->eccbytes = 512; + nand->eccsize = 3; + + /* Reset NAND */ + NFC_CONFIG1 |= NFC_INT_MSK | NFC_RST | NFC_ECC_EN; + + /* Unlock the internal RAM buffer */ + NFC_CONFIG = 0x2; + + /* Block to be unlocked */ + NFC_UNLOCKSTART_BLKADDR = 0x0; + NFC_UNLOCKEND_BLKADDR = 0x4000; + + /* Unlock Block Command for given address range */ + NFC_WRPROT = 0x4; + + /* Only 8 bit bus support for now */ + nand->options |= 0; + + if ((NFMS >> NFMS_BIT) & 1) { + nandinfo.largepage = 1; + nand->autooob = &nand_hw_eccoob_2k; + } else { + nandinfo.largepage = 0; + nand->autooob = &nand_hw_eccoob_8; + } + + return 0; +} |