diff options
author | Jason <r64343@freescale.com> | 2009-03-18 18:24:24 +0800 |
---|---|---|
committer | Fred Fan <r01011@freescale.com> | 2009-09-10 15:08:50 +0800 |
commit | 512c7938d979e2b0cdc9271c6745ef1d7a2cf213 (patch) | |
tree | 2cb4f9333167ab0978151f69d4fc50c13621f29a /drivers | |
parent | e4f2035ea5507b954fcf0bc0e1362b61245de68c (diff) | |
download | u-boot-imx-512c7938d979e2b0cdc9271c6745ef1d7a2cf213.zip u-boot-imx-512c7938d979e2b0cdc9271c6745ef1d7a2cf213.tar.gz u-boot-imx-512c7938d979e2b0cdc9271c6745ef1d7a2cf213.tar.bz2 |
ENGR00109851 Add nand driver for MX51 uboot
Add nand driver for MX51 uboot
Signed-off-by:Jason Liu <r64343@freescale.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mtd/nand/mxc_nand.c | 461 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 8 |
2 files changed, 257 insertions, 212 deletions
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 7e224b7..953131d 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -15,7 +15,7 @@ #include <malloc.h> #include <asm/io.h> #include <asm/errno.h> -#include <nand.h> +#include <linux/mtd/nand.h> #include <asm-arm/arch/mxc_nand.h> struct nand_info { @@ -143,6 +143,96 @@ static void wait_op_done(int max_retries) MTDDEBUG(MTD_DEBUG_LEVEL0, "wait: INT not set\n"); } +/*! + * 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 useirq True if IRQ should be used rather than polling + */ +static void send_addr(u16 addr) +{ + MTDDEBUG(MTD_DEBUG_LEVEL3, "send_addr(0x%x)\n", addr); + + /* fill address */ + raw_write((addr << NFC_FLASH_ADDR_SHIFT), REG_NFC_FLASH_ADDR); + + /* clear status */ + ACK_OPS; + + /* send out address */ + raw_write(NFC_ADDR, REG_NFC_OPS); + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY); +} + +static void mxc_do_addr_cycle_auto(struct mtd_info *mtd, int column, + int page_addr) +{ +#ifdef CONFIG_MXC_NFC_SP_AUTO + if (page_addr != -1 && column != -1) { + u32 mask = 0xFFFF; + /* the column address */ + raw_write(column & mask, NFC_FLASH_ADDR0); + raw_write((raw_read(NFC_FLASH_ADDR0) | + ((page_addr & mask) << 16)), NFC_FLASH_ADDR0); + /* the row address */ + raw_write(((raw_read(NFC_FLASH_ADDR8) & (mask << 16)) | + ((page_addr & (mask << 16)) >> 16)), + NFC_FLASH_ADDR8); + } else if (page_addr != -1) { + raw_write(page_addr, NFC_FLASH_ADDR0); + } + + MTDDEBUG(MTD_DEBUG_LEVEL3, + "AutoMode:the ADDR REGS value is (0x%x, 0x%x)\n", + raw_read(NFC_FLASH_ADDR0), raw_read(NFC_FLASH_ADDR8)); +#endif +} + +static void mxc_do_addr_cycle_atomic(struct mtd_info *mtd, int column, + int page_addr) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + + u32 page_mask = info->page_mask; + + if (column != -1) { + send_addr(column & 0xFF); + if (IS_2K_PAGE_NAND) { + /* another col addr cycle for 2k page */ + send_addr((column >> 8) & 0xF); + } else if (IS_4K_PAGE_NAND) { + /* another col addr cycle for 4k page */ + send_addr((column >> 8) & 0x1F); + } + } + if (page_addr != -1) { + do { + send_addr(page_addr & 0xff); + page_mask >>= 8; + page_addr >>= 8; + } while (page_mask != 0); + } +} + +/* + * Function to perform the address cycles. + */ +static void mxc_nand_addr_cycle(struct mtd_info *mtd, int column, int page_addr) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + + if (info->auto_mode) + mxc_do_addr_cycle_auto(mtd, column, page_addr); + else + mxc_do_addr_cycle_atomic(mtd, column, page_addr); +} + static void send_cmd_atomic(struct mtd_info *mtd, u16 cmd) { /* fill command */ @@ -158,37 +248,42 @@ static void send_cmd_atomic(struct mtd_info *mtd, u16 cmd) wait_op_done(TROP_US_DELAY); } -static void send_cmd_auto(struct mtd_info *mtd, u16 cmd) +/* + * Function to record the ECC corrected/uncorrected errors resulted + * after a page read. This NFC detects and corrects upto to 4 symbols + * of 9-bits each. + */ +static int mxc_nand_ecc_status(struct mtd_info *mtd) { -#ifdef CONFIG_MXC_NFC_SP_AUTO - switch (cmd) { - case NAND_CMD_READ0: - case NAND_CMD_READOOB: - raw_write(NAND_CMD_READ0, REG_NFC_FLASH_CMD); - break; - case NAND_CMD_SEQIN: - case NAND_CMD_ERASE1: - raw_write(cmd, REG_NFC_FLASH_CMD); - break; - case NAND_CMD_PAGEPROG: - case NAND_CMD_ERASE2: - case NAND_CMD_READSTART: - raw_write(raw_read(REG_NFC_FLASH_CMD) | cmd << NFC_CMD_1_SHIFT, - REG_NFC_FLASH_CMD); - send_cmd_interleave(mtd, cmd); - break; - case NAND_CMD_READID: - send_atomic_cmd(cmd); - send_addr(0); - break; - case NAND_CMD_RESET: - send_cmd_interleave(mtd, cmd); - case NAND_CMD_STATUS: - break; - default: - break; - } -#endif + u32 ecc_stat, err; + int no_subpages = 1; + int ret = 0; + u8 ecc_bit_mask, err_limit; + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + + ecc_bit_mask = (IS_4BIT_ECC ? 0x7 : 0xf); + err_limit = (IS_4BIT_ECC ? 0x4 : 0x8); + + no_subpages = mtd->writesize >> 9; + + no_subpages /= info->num_of_intlv; + + ecc_stat = GET_NFC_ECC_STATUS(); + do { + err = ecc_stat & ecc_bit_mask; + if (err > err_limit) { + printk(KERN_WARNING "UnCorrectable RS-ECC Error\n"); + return -1; + } else { + ret += err; + } + ecc_stat >>= 4; + } while (--no_subpages); + + MTDDEBUG(MTD_DEBUG_LEVEL3, "%d Symbol Correctable RS-ECC Error\n", ret); + + return ret; } /*! @@ -199,14 +294,15 @@ static void send_cmd_auto(struct mtd_info *mtd, u16 cmd) static void send_cmd_interleave(struct mtd_info *mtd, u16 cmd) { #ifdef CONFIG_MXC_NFC_SP_AUTO - u32 i; - u32 j = num_of_intlv; + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; u32 addr_low = raw_read(NFC_FLASH_ADDR0); u32 addr_high = raw_read(NFC_FLASH_ADDR8); u32 page_addr = addr_low >> 16 | addr_high << 16; - u8 *dbuf = mtd->info.data_buf; - u8 *obuf = mtd->info.oob_buf; + u32 i, j = info->num_of_intlv; + u8 *dbuf = info->data_buf; + u8 *obuf = info->oob_buf; u32 dlen = mtd->writesize / j; u32 olen = mtd->oobsize / j; @@ -218,9 +314,9 @@ static void send_cmd_interleave(struct mtd_info *mtd, u16 cmd) else page_addr *= this->numchips; - for (i = 0; i < j; i++) { - if (cmd == NAND_CMD_PAGEPROG) { - + switch (cmd) { + case NAND_CMD_PAGEPROG: + for (i = 0; i < j; i++) { /* reset addr cycle */ if (j > 1) mxc_nand_addr_cycle(mtd, 0, page_addr++); @@ -238,14 +334,16 @@ static void send_cmd_interleave(struct mtd_info *mtd, u16 cmd) raw_write(NFC_AUTO_PROG, REG_NFC_OPS); /* wait auto_prog_done bit set */ - if (i < j - 1) { - while (! - (raw_read(REG_NFC_OPS_STAT) & 1 << 30)) - ; - } else { - wait_op_done(TROP_US_DELAY); - } - } else if (cmd == NAND_CMD_READSTART) { + while (!(raw_read(REG_NFC_OPS_STAT) & NFC_OP_DONE)) + ; + } + + wait_op_done(TROP_US_DELAY); + while (!(raw_read(REG_NFC_OPS_STAT) & NFC_RB)); + + break; + case NAND_CMD_READSTART: + for (i = 0; i < j; i++) { /* reset addr cycle */ if (j > 1) mxc_nand_addr_cycle(mtd, 0, page_addr++); @@ -265,18 +363,62 @@ static void send_cmd_interleave(struct mtd_info *mtd, u16 cmd) /* update the value */ dbuf += dlen; obuf += olen; - } else if (cmd == NAND_CMD_ERASE2) { + } + break; + case NAND_CMD_ERASE2: + for (i = 0; i < j; i++) { if (!i) { page_addr = addr_low; page_addr *= (j > 1 ? j : this->numchips); } mxc_nand_addr_cycle(mtd, -1, page_addr++); + raw_write(0, REG_NFC_OPS_STAT); raw_write(NFC_AUTO_ERASE, REG_NFC_OPS); wait_op_done(TROP_US_DELAY); - } else if (cmd == NAND_CMD_RESET) { - NFC_SET_NFC_ACTIVE_CS(i); - send_atomic_cmd(cmd); } + break; + case NAND_CMD_RESET: + for (i = 0; i < j; i++) { + if (j > 1) + NFC_SET_NFC_ACTIVE_CS(i); + send_cmd_atomic(mtd, cmd); + } + break; + default: + break; + } +#endif +} + +static void send_cmd_auto(struct mtd_info *mtd, u16 cmd) +{ +#ifdef CONFIG_MXC_NFC_SP_AUTO + switch (cmd) { + case NAND_CMD_READ0: + case NAND_CMD_READOOB: + raw_write(NAND_CMD_READ0, REG_NFC_FLASH_CMD); + break; + case NAND_CMD_SEQIN: + case NAND_CMD_ERASE1: + raw_write(cmd, REG_NFC_FLASH_CMD); + break; + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE2: + case NAND_CMD_READSTART: + raw_write(raw_read(REG_NFC_FLASH_CMD) | cmd << NFC_CMD_1_SHIFT, + REG_NFC_FLASH_CMD); + send_cmd_interleave(mtd, cmd); + break; + case NAND_CMD_READID: + send_cmd_atomic(mtd, cmd); + send_addr(0); + break; + case NAND_CMD_RESET: + send_cmd_interleave(mtd, cmd); + case NAND_CMD_STATUS: + break; + default: + break; } #endif } @@ -299,32 +441,7 @@ static void send_cmd(struct mtd_info *mtd, u16 cmd) else send_cmd_atomic(mtd, cmd); - MTDDEBUG(MTD_DEBUG_LEVEL3, "send_cmd(0x%x, %d)\n", cmd); -} - -/*! - * 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 useirq True if IRQ should be used rather than polling - */ -static void send_addr(u16 addr) -{ - MTDDEBUG(MTD_DEBUG_LEVEL3, "send_addr(0x%x %d)\n", addr); - - /* fill address */ - raw_write((addr << NFC_FLASH_ADDR_SHIFT), REG_NFC_FLASH_ADDR); - - /* clear status */ - ACK_OPS; - - /* send out address */ - raw_write(NFC_ADDR, REG_NFC_OPS); - - /* Wait for operation to complete */ - wait_op_done(TROP_US_DELAY); + MTDDEBUG(MTD_DEBUG_LEVEL3, "send_cmd(0x%x)\n", cmd); } /*! @@ -420,7 +537,8 @@ static u16 mxc_do_status_auto(struct mtd_info *mtd) /* set ative cs */ NFC_SET_NFC_ACTIVE_CS(i); - raw_write(NFC_AUTO_STATE, REG_NFC_OPS); + /* clear status */ + ACK_OPS; /* FIXME, NFC Auto erase may have * problem, have to pollingit until @@ -428,6 +546,12 @@ static u16 mxc_do_status_auto(struct mtd_info *mtd) * it may get error */ do { + raw_write(NFC_AUTO_STATE, REG_NFC_OPS); + #ifdef CONFIG_MX51 + /* mx51to2 NFC need wait the op done */ + if (is_soc_rev(CHIP_REV_2_0) == 0) + wait_op_done(TROP_US_DELAY); + #endif status = (raw_read(NFC_CONFIG1) & mask) >> 16; } while ((status & NAND_STATUS_READY) == 0); @@ -491,44 +615,6 @@ static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode) } /* - * Function to record the ECC corrected/uncorrected errors resulted - * after a page read. This NFC detects and corrects upto to 4 symbols - * of 9-bits each. - */ -static int mxc_nand_ecc_status(struct mtd_info *mtd) -{ - u32 ecc_stat, err; - int no_subpages = 1; - int ret = 0; - u8 ecc_bit_mask, err_limit; - struct nand_chip *this = mtd->priv; - struct nand_info *info = this->priv; - - ecc_bit_mask = (IS_4BIT_ECC ? 0x7 : 0xf); - err_limit = (IS_4BIT_ECC ? 0x4 : 0x8); - - no_subpages = mtd->writesize >> 9; - - no_subpages /= info->num_of_intlv; - - ecc_stat = GET_NFC_ECC_STATUS(); - do { - err = ecc_stat & ecc_bit_mask; - if (err > err_limit) { - printk(KERN_WARNING "UnCorrectable RS-ECC Error\n"); - return -1; - } else { - ret += err; - } - ecc_stat >>= 4; - } while (--no_subpages); - - MTDDEBUG(MTD_DEBUG_LEVEL3, "%d Symbol Correctable RS-ECC Error\n", ret); - - return ret; -} - -/* * Function to correct the detected errors. This NFC corrects all the errors * detected. So this function just return 0. */ @@ -645,25 +731,6 @@ static u16 mxc_nand_read_word(struct mtd_info *mtd) } /*! - * This function reads byte from the NAND Flash - * - * @param mtd MTD structure for the NAND Flash - * - * @return data read from the NAND Flash - */ -static u_char mxc_nand_read_byte16(struct mtd_info *mtd) -{ - struct nand_chip *this = mtd->priv; - struct nand_info *info = this->priv; - - /* Check for status request */ - if (info->status_req) - return mxc_nand_get_status(mtd) & 0xFF; - - return mxc_nand_read_word(mtd) & 0xFF; -} - -/*! * This function writes data of length \b len from buffer \b buf to the NAND * internal RAM buffer's MAIN area 0. * @@ -751,72 +818,6 @@ static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) } } -static void mxc_do_addr_cycle_auto(struct mtd_info *mtd, int column, - int page_addr) -{ -#ifdef CONFIG_MXC_NFC_SP_AUTO - if (page_addr != -1 && column != -1) { - u32 mask = 0xFFFF; - /* the column address */ - raw_write(column & mask, NFC_FLASH_ADDR0); - raw_write((raw_read(NFC_FLASH_ADDR0) | - ((page_addr & mask) << 16)), NFC_FLASH_ADDR0); - /* the row address */ - raw_write(((raw_read(NFC_FLASH_ADDR8) & (mask << 16)) | - ((page_addr & (mask << 16)) >> 16)), - NFC_FLASH_ADDR8); - } else if (page_addr != -1) { - raw_write(page_addr, NFC_FLASH_ADDR0); - } - - MTDDEBUG(MTD_DEBUG_LEVEL3, - "AutoMode:the ADDR REGS value is (0x%x, 0x%x)\n", - raw_read(NFC_FLASH_ADDR0), raw_read(NFC_FLASH_ADDR8)); -#endif -} - -static void mxc_do_addr_cycle_atomic(struct mtd_info *mtd, int column, - int page_addr) -{ - struct nand_chip *this = mtd->priv; - struct nand_info *info = this->priv; - - u32 page_mask = info->page_mask; - - if (column != -1) { - send_addr(column & 0xFF); - if (IS_2K_PAGE_NAND) { - /* another col addr cycle for 2k page */ - send_addr((column >> 8) & 0xF); - } else if (IS_4K_PAGE_NAND) { - /* another col addr cycle for 4k page */ - send_addr((column >> 8) & 0x1F); - } - } - if (page_addr != -1) { - do { - send_addr(page_addr & 0xff); - page_mask >>= 8; - page_addr >>= 8; - } while (page_mask != 0); - } -} - - -/* - * Function to perform the address cycles. - */ -static void mxc_nand_addr_cycle(struct mtd_info *mtd, int column, int page_addr) -{ - struct nand_chip *this = mtd->priv; - struct nand_info *info = this->priv; - - if (info->auto_mode) - mxc_do_addr_cycle_auto(mtd, column, page_addr); - else - mxc_do_addr_cycle_atomic(mtd, column, page_addr); -} - /*! * This function is used by the upper layer to write command to NAND Flash for * different operations to be carried out on NAND Flash @@ -933,6 +934,50 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, } } +static int mxc_nand_read_oob(struct mtd_info *mtd, + struct nand_chip *chip, int page, int sndcmd) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + + if (sndcmd) { + + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); + sndcmd = 0; + } + + memcpy(chip->oob_poi, info->oob_buf, mtd->oobsize); + + return sndcmd; +} + +static int mxc_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + +#ifndef CONFIG_MXC_NFC_SP_AUTO + mxc_nand_ecc_status(mtd); +#endif + + memcpy(buf, info->data_buf, mtd->writesize); + memcpy(chip->oob_poi, info->oob_buf, mtd->oobsize); + + return 0; +} + +static void mxc_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + + memcpy(info->data_buf, buf, mtd->writesize); + memcpy(info->oob_buf, chip->oob_poi, mtd->oobsize); + +} + /* 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 }; @@ -1014,7 +1059,7 @@ static int mxc_nand_scan_bbt(struct mtd_info *mtd) this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1; this->chip_shift = ffs(this->chipsize) - 1; - /*this->oob_poi = this->buffers->databuf + mtd->writesize;*/ + this->oob_poi = this->buffers->databuf + mtd->writesize; } /* propagate ecc.layout to mtd_info */ @@ -1055,6 +1100,9 @@ static void mxc_nfc_init(void) /* Unlock Block Command for given address range */ raw_write(NFC_SET_WPC(NFC_WPC_UNLOCK), REG_NFC_WPC); + + /* Enable hw ecc */ + raw_write((raw_read(REG_NFC_ECC_EN) | NFC_ECC_EN), REG_NFC_ECC_EN); } static int mxc_alloc_buf(struct nand_info *info) @@ -1080,12 +1128,6 @@ static int mxc_alloc_buf(struct nand_info *info) return err; } -static void mxc_free_buf(struct nand_info *info) -{ - kfree(info->data_buf); - kfree(info->oob_buf); -} - /*! * This function is called during the driver binding process. * @@ -1132,13 +1174,16 @@ int board_nand_init(struct nand_chip *nand) this->read_buf = mxc_nand_read_buf; this->verify_buf = mxc_nand_verify_buf; this->scan_bbt = mxc_nand_scan_bbt; + this->ecc.read_page = mxc_nand_read_page; + this->ecc.write_page = mxc_nand_write_page; + this->ecc.read_oob = mxc_nand_read_oob; this->ecc.calculate = mxc_nand_calculate_ecc; this->ecc.correct = mxc_nand_correct_data; this->ecc.hwctl = mxc_nand_enable_hwecc; + this->ecc.layout = &nand_hw_eccoob_512; this->ecc.mode = NAND_ECC_HW; this->ecc.bytes = 9; this->ecc.size = 512; return 0; - } diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 29c0d2d..0478740 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2529,7 +2529,8 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, mtd->writesize = 1024 << (extid & 0x3); extid >>= 2; /* Calc oobsize */ - mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9); + mtd->oobsize = (*maf_id == 0x2c && dev_id == 0xd5) ? + 218 : (8 << (extid & 0x01)) * (mtd->writesize >> 9); extid >>= 2; /* Calc blocksize. Blocksize is multiples of 64KiB */ mtd->erasesize = (64 * 1024) << (extid & 0x03); @@ -2866,10 +2867,9 @@ int nand_scan_tail(struct mtd_info *mtd) mtd->ecclayout = chip->ecc.layout; /* Check, if we should skip the bad block table scan */ - if (chip->options & NAND_SKIP_BBTSCAN) - chip->options |= NAND_BBT_SCANNED; + chip->options |= NAND_BBT_SCANNED; - return 0; + return chip->scan_bbt(mtd); } /* module_text_address() isn't exported, and it's mostly a pointless |