diff options
author | Fred Fan <r01011@freescale.com> | 2009-02-23 13:40:12 +0800 |
---|---|---|
committer | Fred Fan <r01011@freescale.com> | 2009-09-09 17:15:27 +0800 |
commit | 4bdaaba26f5d015474dbd5a524355b5e64655ebe (patch) | |
tree | f2c70252b73a9060d6458212565ee3e0e6ab3e61 /drivers/mtd/nand | |
parent | d409f3fd05bef817c37bade4b6a803da80dc7ad0 (diff) | |
download | u-boot-imx-4bdaaba26f5d015474dbd5a524355b5e64655ebe.zip u-boot-imx-4bdaaba26f5d015474dbd5a524355b5e64655ebe.tar.gz u-boot-imx-4bdaaba26f5d015474dbd5a524355b5e64655ebe.tar.bz2 |
ENGR00099697 Add nand driver for mx35
Add nand driver for mx35
Signed-off-by:Jason Liu <r64343@freescale.com>
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r-- | drivers/mtd/nand/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/mxc_nand.c | 1148 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_ids.c | 17 |
4 files changed, 1158 insertions, 12 deletions
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 975a1b8..d2f9435 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -47,6 +47,7 @@ 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 +COBJS-y += mxc_nand.o endif COBJS := $(COBJS-y) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c new file mode 100644 index 0000000..d458d0a --- /dev/null +++ b/drivers/mtd/nand/mxc_nand.c @@ -0,0 +1,1148 @@ +/* + * 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 <malloc.h> +#include <asm/io.h> +#include <asm/errno.h> +#include <nand.h> +#include <linux/mtd/compat.h> +#include <asm-arm/arch/mxc_nand.h> + +struct nand_info { + int status_req; + int large_page; + int auto_mode; + u16 col_addr; + u8 num_of_intlv; + int page_mask; + int hw_ecc; + u8 *data_buf; + u8 *oob_buf; +}; + +/* + * Define delays in microsec for NAND device operations + */ +#define TROP_US_DELAY 2000 + +/* + * OOB placement block for use with hardware ecc generation + */ +static struct nand_oobinfo nand_hw_eccoob_512 = { + .useecc = MTD_NANDECC_AUTOPL_USR, + .eccbytes = 9, + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, + .oobfree = {{0, 4} } +}; + +static struct nand_oobinfo nand_hw_eccoob_2k = { + .useecc = MTD_NANDECC_AUTOPL_USR, + .eccbytes = 9, + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, + .oobfree = {{2, 4} } +}; + +static struct nand_oobinfo nand_hw_eccoob_4k = { + .useecc = MTD_NANDECC_AUTOPL_USR, + .eccbytes = 9, + .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, + .oobfree = {{2, 4} } +}; + +/*! + * @defgroup NAND_MTD NAND Flash MTD Driver for MXC processors + */ + +/*! + * @file mxc_nd2.c + * + * @brief This file contains the hardware specific layer for NAND Flash on + * MXC processor + * + * @ingroup NAND_MTD + */ + +/*! + * Half word access.Added for U-boot. + */ +static void *nfc_memcpy(void *dest, const void *src, size_t n) +{ + u16 *dst_16 = (u16 *) dest; + const u16 *src_16 = (u16 *) src; + + while (n > 0) { + *dst_16++ = *src_16++; + n -= 2; + } + + return dest; +} + +/* + * Functions to transfer data to/from spare erea. + */ +static void +copy_spare(struct mtd_info *mtd, void *pbuf, void *pspare, int len, int bfrom) +{ + u16 i, j; + u16 m = mtd->oobsize; + u16 n = mtd->oobblock >> 9; + u8 *d = (u8 *) pbuf; + u8 *s = (u8 *) pspare; + u16 t = SPARE_LEN; + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + + m /= info->num_of_intlv; + n /= info->num_of_intlv; + + j = (m / n >> 1) << 1; + + if (bfrom) { + for (i = 0; i < n - 1; i++) + nfc_memcpy(&d[i * j], &s[i * t], j); + + /* the last section */ + nfc_memcpy(&d[i * j], &s[i * t], len - i * j); + } else { + for (i = 0; i < n - 1; i++) + nfc_memcpy(&s[i * t], &d[i * j], j); + + /* the last section */ + nfc_memcpy(&s[i * t], &d[i * j], len - i * j); + } +} + +/*! + * This function polls the NFC to wait for the basic operation to complete by + * checking the INT bit of config2 register. + * + * @param maxRetries number of retry attempts (separated by 1 us) + * @param useirq True if IRQ should be used rather than polling + */ +static void wait_op_done(int max_retries) +{ + + while (max_retries-- > 0) { + if (raw_read(REG_NFC_OPS_STAT) & NFC_OPS_STAT) { + WRITE_NFC_IP_REG((raw_read(REG_NFC_OPS_STAT) & + ~NFC_OPS_STAT), + REG_NFC_OPS_STAT); + break; + } + udelay(1); + } + if (max_retries <= 0) + DEBUG(MTD_DEBUG_LEVEL0, "wait: INT not set\n"); +} + +static void send_cmd_atomic(struct mtd_info *mtd, u16 cmd) +{ + /* fill command */ + raw_write(cmd, REG_NFC_FLASH_CMD); + + /* clear status */ + ACK_OPS; + + /* send out command */ + raw_write(NFC_CMD, REG_NFC_OPS); + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY); +} + +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_atomic_cmd(cmd); + send_addr(0); + break; + case NAND_CMD_RESET: + send_cmd_interleave(mtd, cmd); + case NAND_CMD_STATUS: + break; + default: + break; + } +#endif +} + +/*! + * This function handle the interleave related work + * @param mtd mtd info + * @param cmd command + */ +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; + 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 dlen = mtd->oobblock / j; + u32 olen = mtd->oobsize / j; + + /* adjust the addr value + * since ADD_OP mode is 01 + */ + if (j > 1) + page_addr *= j; + else + page_addr *= this->numchips; + + for (i = 0; i < j; i++) { + if (cmd == NAND_CMD_PAGEPROG) { + + /* reset addr cycle */ + if (j > 1) + mxc_nand_addr_cycle(mtd, 0, page_addr++); + + /* data transfer */ + nfc_memcpy(MAIN_AREA0, dbuf, dlen); + copy_spare(mtd, obuf, SPARE_AREA0, olen, 0); + + /* update the value */ + dbuf += dlen; + obuf += olen; + + NFC_SET_RBA(0); + raw_write(0, REG_NFC_OPS_STAT); + 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) { + /* reset addr cycle */ + if (j > 1) + mxc_nand_addr_cycle(mtd, 0, page_addr++); + + NFC_SET_RBA(0); + raw_write(0, REG_NFC_OPS_STAT); + raw_write(NFC_AUTO_READ, REG_NFC_OPS); + wait_op_done(TROP_US_DELAY); + + /* check ecc error */ + mxc_nand_ecc_status(mtd); + + /* data transfer */ + nfc_memcpy(dbuf, MAIN_AREA0, dlen); + copy_spare(mtd, obuf, SPARE_AREA0, olen, 1); + + /* update the value */ + dbuf += dlen; + obuf += olen; + } else if (cmd == NAND_CMD_ERASE2) { + if (!i) { + page_addr = addr_low; + page_addr *= (j > 1 ? j : this->numchips); + } + mxc_nand_addr_cycle(mtd, -1, page_addr++); + 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); + } + } +#endif +} + +/*! + * This function issues the specified command to the NAND device and + * waits for completion. + * + * @param cmd command for NAND Flash + * @param useirq True if IRQ should be used rather than polling + */ +static void send_cmd(struct mtd_info *mtd, u16 cmd) +{ + + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + + if (info->auto_mode) + send_cmd_auto(mtd, cmd); + else + send_cmd_atomic(mtd, cmd); + + DEBUG(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) +{ + DEBUG(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); +} + +/*! + * This function requests the NFC to initate the transfer + * of data currently in the NFC RAM buffer to the NAND device. + * + * @param buf_id Specify Internal RAM Buffer number + */ +static void send_prog_page(struct mtd_info *mtd, u8 buf_id) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + + if (!info->auto_mode) { + /* set ram buffer id */ + NFC_SET_RBA(buf_id); + + /* clear status */ + ACK_OPS; + + /* transfer data from NFC ram to nand */ + raw_write(NFC_INPUT, REG_NFC_OPS); + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY); + + DEBUG(MTD_DEBUG_LEVEL3, "%s\n", __func__); + } +} + +/*! + * This function requests the NFC to initated the transfer + * of data from the NAND device into in the NFC ram buffer. + * + * @param buf_id Specify Internal RAM Buffer number + */ +static void send_read_page(struct mtd_info *mtd, u8 buf_id) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + + if (!info->auto_mode) { + + /* set ram buffer id */ + NFC_SET_RBA(buf_id); + + /* clear status */ + ACK_OPS; + + /* transfer data from nand to NFC ram */ + raw_write(NFC_OUTPUT, REG_NFC_OPS); + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY); + + DEBUG(MTD_DEBUG_LEVEL3, "%s(%d)\n", __func__, buf_id); + + } + +} + +/*! + * This function requests the NFC to perform a read of the + * NAND device ID. + */ +static void send_read_id(void) +{ + /* Set RBA bits for BUFFER0 */ + NFC_SET_RBA(0); + + /* clear status */ + ACK_OPS; + + /* Read ID into main buffer */ + raw_write(NFC_ID, REG_NFC_OPS); + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY); + +} + +static u16 mxc_do_status_auto(struct mtd_info *mtd) +{ + u16 status = 0; +#ifdef CONFIG_MXC_NFC_SP_AUTO + int i = 0; + u32 mask = 0xFF << 16; + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + + for (; i < info->num_of_intlv; i++) { + + /* set ative cs */ + NFC_SET_NFC_ACTIVE_CS(i); + + raw_write(NFC_AUTO_STATE, REG_NFC_OPS); + + /* FIXME, NFC Auto erase may have + * problem, have to pollingit until + * the nand get idle, otherwise + * it may get error + */ + do { + status = (raw_read(NFC_CONFIG1) & mask) >> 16; + } while ((status & NAND_STATUS_READY) == 0); + + if (status & NAND_STATUS_FAIL) + break; + } +#endif + return status; +} + +static u16 mxc_do_status_atomic(struct mtd_info *mtd) +{ + volatile u16 *mainBuf = MAIN_AREA1; + u8 val = 1; + u16 ret; + + /* Set ram buffer id */ + NFC_SET_RBA(val); + + /* clear status */ + ACK_OPS; + + /* Read status into main buffer */ + raw_write(NFC_STATUS, REG_NFC_OPS); + + /* 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; + + return ret; +} + +/*! + * This function requests the NFC to perform a read of the + * NAND device status and returns the current status. + * + * @return device status + */ +static u16 mxc_nand_get_status(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + u16 status; + + if (info->auto_mode) + status = mxc_do_status_auto(mtd); + else + status = mxc_do_status_atomic(mtd); + + return status; + +} + +static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + raw_write((raw_read(REG_NFC_ECC_EN) | NFC_ECC_EN), REG_NFC_ECC_EN); + return; +} + +/* + * 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->oobblock >> 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); + + DEBUG(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. + */ +static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + return 0; +} + +/* + * Function to calculate the ECC for the data to be stored in the Nand device. + * This NFC has a hardware RS(511,503) ECC engine together with the RS ECC + * CONTROL blocks are responsible for detection and correction of up to + * 8 symbols of 9 bits each in 528 byte page. + * So this function is just return 0. + */ + +static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + return 0; +} + +/*! + * 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, u_char * buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + u16 col = info->col_addr; + u8 *data_buf = info->data_buf; + u8 *oob_buf = info->oob_buf; + + if (mtd->oobblock) { + + int j = mtd->oobblock - col; + int n = mtd->oobsize + j; + + n = min(n, len); + + if (j > 0) { + if (n > j) { + memcpy(buf, &data_buf[col], j); + memcpy(buf + j, &oob_buf[0], n - j); + } else { + memcpy(buf, &data_buf[col], n); + } + } else { + col -= mtd->oobblock; + memcpy(buf, &oob_buf[col], len); + } + + /* update */ + info->col_addr += n; + + } else { + /* At flash identify phase, + * mtd->oobblock has not been + * set correctly, it should + * be zero.And len will less 2 + */ + memcpy(buf, &data_buf[col], len); + + /* update */ + info->col_addr += len; + } + +} + +/*! + * This function reads byte from the NAND Flash + * + * @param mtd MTD structure for the NAND Flash + * + * @return data read from the NAND Flash + */ +static uint8_t mxc_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + uint8_t ret; + + /* Check for status request */ + if (info->status_req) + return mxc_nand_get_status(mtd) & 0xFF; + + mxc_nand_read_buf(mtd, &ret, 1); + + return ret; +} + +/*! + * 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 ret; + + mxc_nand_read_buf(mtd, (uint8_t *) &ret, sizeof(u16)); + + return ret; +} + +/*! + * 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. + * + * @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 u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + u16 col = info->col_addr; + u8 *data_buf = info->data_buf; + u8 *oob_buf = info->oob_buf; + int j = mtd->oobblock - col; + int n = mtd->oobsize + j; + + n = min(n, len); + + if (j > 0) { + if (n > j) { + memcpy(&data_buf[col], buf, j); + memcpy(&oob_buf[0], buf + j, n - j); + } else { + memcpy(&data_buf[col], buf, n); + } + } else { + col -= mtd->oobblock; + memcpy(&oob_buf[col], buf, len); + } + + /* update */ + info->col_addr += n; +} + +/*! + * 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 u_char *buf, + int len) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + u_char *s = info->data_buf; + + const u_char *p = buf; + + for (; len > 0; len--) { + if (*p++ != *s++) + return -1; + } + + return 0; +} + +/*! + * 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) +{ + + switch (chip) { + case -1: + break; + + case 0 ... 7: + NFC_SET_NFC_ACTIVE_CS(chip); + break; + + default: + break; + } +} + +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); + } + + DEBUG(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 + * + * @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) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + + 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 + */ + info->status_req = 0; + + /* + * Command pre-processing step + */ + switch (command) { + case NAND_CMD_STATUS: + info->col_addr = 0; + info->status_req = 1; + break; + + case NAND_CMD_READ0: + info->col_addr = column; + break; + + case NAND_CMD_READOOB: + info->col_addr = column; + command = NAND_CMD_READ0; + break; + + case NAND_CMD_SEQIN: + if (column != 0) { + + /* FIXME: before send SEQIN command for + * partial write,We need read one page out. + * FSL NFC does not support partial write + * It alway send out 512+ecc+512+ecc ... + * for large page nand flash. But for small + * page nand flash, it did support SPARE + * ONLY operation. But to make driver + * simple. We take the same as large page,read + * whole page out and update. As for MLC nand + * NOP(num of operation) = 1. Partial written + * on one programed page is not allowed! We + * can't limit it on the driver, it need the + * upper layer applicaiton take care it + */ + + mxc_nand_command(mtd, NAND_CMD_READ0, 0, page_addr); + } + + info->col_addr = column; + break; + + case NAND_CMD_PAGEPROG: + if (!info->auto_mode) { + nfc_memcpy(MAIN_AREA0, info->data_buf, mtd->oobblock); + copy_spare(mtd, info->oob_buf, SPARE_AREA0, + mtd->oobsize, 0); + } + + send_prog_page(mtd, 0); + break; + + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + break; + } + + /* + * Write out the command to the device. + */ + send_cmd(mtd, command); + + mxc_nand_addr_cycle(mtd, column, page_addr); + + /* + * Command post-processing step + */ + switch (command) { + + case NAND_CMD_READOOB: + case NAND_CMD_READ0: + if (info->large_page) + /* send read confirm command */ + send_cmd(mtd, NAND_CMD_READSTART); + + send_read_page(mtd, 0); + + if (!info->auto_mode) { + mxc_nand_ecc_status(mtd); + nfc_memcpy(info->data_buf, MAIN_AREA0, mtd->oobblock); + copy_spare(mtd, info->oob_buf, SPARE_AREA0, + mtd->oobsize, 1); + } + break; + + case NAND_CMD_READID: + send_read_id(); + info->col_addr = column; + nfc_memcpy(info->data_buf, MAIN_AREA0, 2048); + break; + } +} + +/* 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 +}; + +static int mxc_nand_scan_bbt(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + struct nand_info *info = this->priv; + + info->page_mask = this->pagemask; + + if (IS_2K_PAGE_NAND) { + NFC_SET_NFMS(1 << NFMS_NF_PG_SZ); + this->autooob = &nand_hw_eccoob_2k; + info->large_page = 1; + } else if (IS_4K_PAGE_NAND) { + NFC_SET_NFMS(1 << NFMS_NF_PG_SZ); + this->autooob = &nand_hw_eccoob_4k; + info->large_page = 1; + } else { + this->autooob = &nand_hw_eccoob_512; + info->large_page = 0; + } + + /* reconfig for interleave mode */ + + if (this->numchips > 1 && info->auto_mode) { + info->num_of_intlv = this->numchips; + this->numchips = 1; + + /* FIXEME:need remove it + * when kernel support + * 4G larger space + */ + mtd->size = this->chipsize; + mtd->erasesize *= info->num_of_intlv; + mtd->oobblock *= info->num_of_intlv; + mtd->oobsize *= info->num_of_intlv; + this->page_shift = ffs(mtd->oobblock) - 1; + 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->oobblock;*/ + } + + /* propagate ecc.layout to mtd_info */ + memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo)); + + /* jffs2 not write oob */ + /*mtd->flags &= ~MTD_OOB_WRITEABLE;*/ + + /* 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) { + this->badblock_pattern = (mtd->oobblock > 512) ? + &largepage_memorybased : &smallpage_memorybased; + } + + /* Build bad block table */ + return nand_scan_bbt(mtd, this->badblock_pattern); +} + +static void mxc_nfc_init(void) +{ + /* Disable interrupt */ + raw_write((raw_read(REG_NFC_INTRRUPT) | NFC_INT_MSK), REG_NFC_INTRRUPT); + + /* disable spare enable */ + raw_write(raw_read(REG_NFC_SP_EN) & ~NFC_SP_EN, REG_NFC_SP_EN); + + /* Unlock the internal RAM Buffer */ + raw_write(NFC_SET_BLS(NFC_BLS_UNLCOKED), REG_NFC_BLS); + + /* Blocks to be unlocked */ + UNLOCK_ADDR(0x0, 0xFFFF); + + /* Unlock Block Command for given address range */ + raw_write(NFC_SET_WPC(NFC_WPC_UNLOCK), REG_NFC_WPC); +} + +static int mxc_alloc_buf(struct nand_info *info) +{ + int err = 0; + + info->data_buf = kmalloc(NAND_MAX_PAGESIZE, GFP_KERNEL); + if (!info->data_buf) { + printk(KERN_ERR "%s: failed to allocate data_buf\n", __func__); + err = -ENOMEM; + return err; + } + memset(info->data_buf, 0, NAND_MAX_PAGESIZE); + + info->oob_buf = kmalloc(NAND_MAX_OOBSIZE, GFP_KERNEL); + if (!info->oob_buf) { + printk(KERN_ERR "%s: failed to allocate oob_buf\n", __func__); + err = -ENOMEM; + return err; + } + memset(info->oob_buf, 0, NAND_MAX_OOBSIZE); + + 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. + * + * @param pdev the device structure used to store device specific + * information that is used by the suspend, resume and + * remove functions + * + * @return The function always returns 0. + */ +int board_nand_init(struct nand_chip *nand) +{ + struct nand_info *info; + struct nand_chip *this = nand; + int err; + + info = kmalloc(sizeof(struct nand_info), GFP_KERNEL); + if (!info) { + printk(KERN_ERR "%s: failed to allocate nand_info\n", + __func__); + err = -ENOMEM; + return err; + } + memset(info, 0, sizeof(struct nand_info)); + + if (mxc_alloc_buf(info)) { + err = -ENOMEM; + return err; + } + + info->num_of_intlv = 1; + +#ifdef CONFIG_MXC_NFC_SP_AUTO + info->auto_mode = 1; +#endif + /* init the nfc */ + mxc_nfc_init(); + + this->priv = info; + this->cmdfunc = mxc_nand_command; + this->select_chip = mxc_nand_select_chip; + this->read_byte = mxc_nand_read_byte; + this->read_word = mxc_nand_read_word; + this->write_buf = mxc_nand_write_buf; + this->read_buf = mxc_nand_read_buf; + this->verify_buf = mxc_nand_verify_buf; + this->scan_bbt = mxc_nand_scan_bbt; + this->calculate_ecc = mxc_nand_calculate_ecc; + this->correct_data = mxc_nand_correct_data; + this->enable_hwecc = mxc_nand_enable_hwecc; + this->eccmode = NAND_ECC_HW3_512; + this->eccbytes = 9; + this->eccsize = 512; + + return 0; + +} diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 360b070..29c0d2d 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -867,8 +867,8 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this) while (1) { if (get_timer(0) > timeo) { - printf("Timeout!"); - return 0x01; + /*printf("Timeout!");*/ + /*return 0x01;*/ } if (this->dev_ready) { diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 077c305..f48cccb 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -23,14 +23,13 @@ * 512 512 Byte page size */ struct nand_flash_dev nand_flash_ids[] = { - -#ifdef CONFIG_MTD_NAND_MUSEUM_IDS - {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0}, - {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0}, - {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0}, - {"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0}, - {"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0}, - {"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0}, +#if 0 + {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0}, + {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0}, + {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0}, + {"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0}, + {"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0}, + {"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0}, {"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0}, {"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0}, {"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0}, @@ -41,7 +40,6 @@ struct nand_flash_dev nand_flash_ids[] = { {"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16}, {"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16}, #endif - {"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0}, {"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0}, {"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16}, @@ -124,7 +122,6 @@ struct nand_flash_dev nand_flash_ids[] = { NAND_IS_AND | NAND_NO_AUTOINCR |NAND_NO_READRDY | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH }, - {NULL,} }; |