Imported from Freescale's Linux NFC driver from the i.MX31 BSP release 5 (Linux 2.6.22.5) and the i.MX31 PDK BSP (Linux 2.6.24).
The code has been changed to conform (better) with the coding style in Linux/U-boot. Sections not used by U-boot have been removed. The driver has been tested on i.MX31 Litekit (small page NAND) and i.MX31 PDK (large page NAND). Both boards have 8 bit wide NAND devices. 16 bit NAND devices have not been tested and probably requires a minor code change. Signed-off-by: Magnus Lilja <[EMAIL PROTECTED]> --- Issues regarding non-standard OOB/Bad block table, verify_buf and memcpy_32() and some other of Scott Wood's comments haven't been implemented yet. And yes, the controller requires 16 bit accesses but I made a memcpy_32() instead of memcpy_16() but the plan is to implement Scott's proposal to memcpy_32() to a software buffer and then memcpy() from there. drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/mx31_nand.c | 1136 +++++++++++++++++++++++++++++++++ include/asm-arm/arch-mx31/mx31-regs.h | 96 +++ 3 files changed, 1233 insertions(+), 0 deletions(-) diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 1923310..729b8c2 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -37,6 +37,7 @@ endif COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o COBJS-$(CONFIG_NAND_FSL_UPM) += fsl_upm.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..ebcd567 --- /dev/null +++ b/drivers/mtd/nand/mx31_nand.c @@ -0,0 +1,1136 @@ +/* + * (C) Copyright 2008 Magnus Lilja <[EMAIL PROTECTED]> + * + * Based on Freescale's Linux MXC NAND driver. + * + * Copyright 2004-2007 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> + +struct mxc_mtd_s { + struct mtd_info mtd; + struct nand_chip nand; + struct device *dev; +}; + +static struct mxc_mtd_s *mxc_nand_data; + +/* + * 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 spare_only; + int status_request; + u16 col_addr; +}; + +static struct nand_info g_nandfc_info; + +#ifdef CONFIG_MTD_NAND_MXC_SWECC +static int hardware_ecc; +#else +static int hardware_ecc = 1; +#endif + +#ifndef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2 +static int ecc_disabled; +#endif + +static int is2k_pagesize; + +/* + * OOB placement block for use with hardware ecc generation + */ +static struct nand_ecclayout nand_hw_eccoob_8 = { + .eccbytes = 5, + .eccpos = {6, 7, 8, 9, 10}, + .oobfree = { + {.offset = 0, + .length = 5}, + {.offset = 11, + .length = 5}} +}; + +static struct nand_ecclayout nand_hw_eccoob_16 = { + .eccbytes = 5, + .eccpos = {6, 7, 8, 9, 10}, + .oobfree = { + {.offset = 0, + .length = 6}, + {.offset = 12, + .length = 4}} +}; + +static struct nand_ecclayout nand_hw_eccoob_2k = { + .eccbytes = 20, + .eccpos = {6, 7, 8, 9, 10, 22, 23, 24, 25, 26, + 38, 39, 40, 41, 42, 54, 55, 56, 57, 58}, + .oobfree = { + {.offset = 0, + .length = 5}, + {.offset = 11, + .length = 10}, + {.offset = 27, + .length = 10}, + {.offset = 43, + .length = 10}, + {.offset = 59, + .length = 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) + * @param param parameter for debug + * @param useirq 1 if IRQ should be used rather than polling + */ +static void wait_op_done(int max_retries, u16 param, int useirq) +{ + while (max_retries-- > 0) { + if (NFC_CONFIG2 & NFC_INT) { + NFC_CONFIG2 &= ~NFC_INT; + break; + } + udelay(1); + } + if (max_retries <= 0) + MTDDEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n", + __FUNCTION__, param); +} + +/** + * This function issues the specified command to the NAND device and + * waits for completion. + * + * @param cmd command for NAND Flash + * @param useirq 1 if IRQ should be used rather than polling + */ +static void send_cmd(u16 cmd, int useirq) +{ + MTDDEBUG(MTD_DEBUG_LEVEL3, "send_cmd(0x%x, %d)\n", cmd, useirq); + + NFC_FLASH_CMD = (u16) cmd; + NFC_CONFIG2 = NFC_CMD; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, cmd, useirq); +} + +/** + * 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, int islast) +{ + MTDDEBUG(MTD_DEBUG_LEVEL3, "send_addr(0x%x %d)\n", addr, islast); + + NFC_FLASH_ADDR = addr; + NFC_CONFIG2 = NFC_ADDR; + + /* Wait for operation to complete */ + wait_op_done(TROP_US_DELAY, addr, islast); +} + +/** + * 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 spare_only set to 1 if only the spare area is transferred + */ +static void send_prog_page(u8 buf_id, int spare_only) +{ + MTDDEBUG(MTD_DEBUG_LEVEL3, "send_prog_page (%d)\n", spare_only); + + /* NANDFC buffer 0 is used for page read/write */ + + NFC_BUF_ADDR = buf_id; + + /* Configure spare or page+spare access */ + if (!is2k_pagesize) { + if (spare_only) + 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, spare_only, 1); +} + +/** + * 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 spare_only set to 1 if only spare area needs correction + */ +static void mxc_nd_correct_error(u8 buf_id, u16 eccpos, int spare_only) +{ + u16 col; + u8 pos; + volatile 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 */ + + MTDDEBUG(MTD_DEBUG_LEVEL3, + "mxc_nd_correct_error (col=%d pos=%d)\n", col, pos); + + /* Set the pointer for main / spare area */ + if (!spare_only) + buf = (volatile u16 *)(MAIN_AREA0 + col + (256 * buf_id)); + else + buf = (volatile 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) +{ +#ifdef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2 + /* To maintain single bit error in previous page */ + static int last_err_main, last_err_spare; +#endif + u16 value, ecc_status; + + /* Read the ECC result */ + ecc_status = NFC_ECC_STATUS_RESULT; + MTDDEBUG(MTD_DEBUG_LEVEL3, + "mxc_nd_correct_ecc (Ecc status=%x)\n", ecc_status); + +#ifdef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2 + /* Check for Error in Mainarea */ + if ((ecc_status & 0xC) == MAIN_SINGLEBIT_ERROR) { + /* Check for error in previous page */ + if (last_err_main && !spare) { + 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); + } else + /* Set if single bit error in current page */ + last_err_main = 1; + } else + /* Reset if no single bit error in current page */ + last_err_main = 0; + + /* Check for Error in Sparearea */ + if ((ecc_status & 0x3) == SPARE_SINGLEBIT_ERROR) { + /* Check for error in previous page */ + if (last_err_space) { + 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 + /* Set if single bit error in current page */ + last_err_spare = 1; + } else + /* Reset if no single bit error in current page */ + last_err_spare = 0; +#else + 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 */ +#endif /* CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2 */ +} + +/** + * 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 spare_only set 1 if only the spare area is + * transferred + */ +static void send_read_page(u8 buf_id, int spare_only) +{ + MTDDEBUG(MTD_DEBUG_LEVEL3, "send_read_page (%d)\n", spare_only); + + /* NANDFC buffer 0 is used for page read/write */ + NFC_BUF_ADDR = buf_id; + + /* Configure spare or page+spare access */ + if (!is2k_pagesize) { + if (spare_only) + 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, spare_only, 1); + + /* 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, spare_only); +} + +/** + * This function requests the NANDFC to perform a read of the + * NAND device ID. + */ +static void send_read_id(void) +{ + struct nand_chip *this = &mxc_nand_data->nand; + + /* 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, 0, 1); + + if (this->options & NAND_BUSWIDTH_16) { + volatile u16 *mainbuf = MAIN_AREA0; + + /* + * Pack the every-other-byte result for 16-bit ID reads + * into every-byte as the generic code expects and various + * chips implement. + */ + + mainbuf[0] = (mainbuf[0] & 0xff) | ((mainbuf[1] & 0xff) << 8); + mainbuf[1] = (mainbuf[2] & 0xff) | ((mainbuf[3] & 0xff) << 8); + mainbuf[2] = (mainbuf[4] & 0xff) | ((mainbuf[5] & 0xff) << 8); + } +} + +/** + * 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, 0, 1); + + /* Status is placed in first word of main buffer */ + /* get status, then recovery area 1 data */ + ret = mainbuf[0]; + *((u32 *) mainbuf) = store; + + return ret; +} + +/** + * This functions is used by upper layer to checks if device is ready + * + * @param mtd MTD structure for the NAND Flash + * + * @return 0 if device is busy else 1 + */ +static int mxc_nand_dev_ready(struct mtd_info *mtd) +{ + /* + * NFC handles R/B internally.Therefore,this function + * always returns status as ready. + */ + return 1; +} + +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, u_char *dat, + u_char *read_ecc, u_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)) { + MTDDEBUG(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 u_char *dat, + u_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 u_char mxc_nand_read_byte(struct mtd_info *mtd) +{ + u_char ret_val = 0; + u16 col, rd_word; + volatile u16 *mainbuf = MAIN_AREA0; + volatile u16 *sparebuf = SPARE_AREA0; + + /* Check for status request */ + if (g_nandfc_info.status_request) + return get_dev_status() & 0xFF; + + /* Get column for 16-bit access */ + col = g_nandfc_info.col_addr >> 1; + + /* If we are accessing the spare region */ + if (g_nandfc_info.spare_only) + rd_word = sparebuf[col]; + else + rd_word = mainbuf[col]; + + /* Pick upper/lower byte of word from RAM buffer */ + if (g_nandfc_info.col_addr & 0x1) + ret_val = (rd_word >> 8) & 0xFF; + else + ret_val = rd_word & 0xFF; + + /* Update saved column address */ + g_nandfc_info.col_addr++; + + 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; + + MTDDEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_read_word(col = %d)\n", g_nandfc_info.col_addr); + + col = g_nandfc_info.col_addr; + /* Adjust saved column address */ + if (col < mtd->writesize && g_nandfc_info.spare_only) + col += mtd->writesize; + + if (col < mtd->writesize) + p = (MAIN_AREA0) + (col >> 1); + else + p = (SPARE_AREA0) + ((col - mtd->writesize) >> 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 */ + g_nandfc_info.col_addr = 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 u_char *buf, int len) +{ + int n; + int col; + int i = 0; + + MTDDEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_write_buf(col = %d, len = %d)\n", + g_nandfc_info.col_addr, len); + + col = g_nandfc_info.col_addr; + + /* Adjust saved column address */ + if (col < mtd->writesize && g_nandfc_info.spare_only) + col += mtd->writesize; + + n = mtd->writesize + mtd->oobsize - col; + if (len > mtd->writesize + mtd->oobsize - col) + MTDDEBUG(MTD_DEBUG_LEVEL1, "Error: too much data.\n"); + + n = min(len, n); + + MTDDEBUG(MTD_DEBUG_LEVEL3, + "%s:%d: col = %d, n = %d\n", __FUNCTION__, __LINE__, col, n); + + while (n) { + volatile u32 *p; + if (col < mtd->writesize) + p = (volatile u32 *)((ulong) (MAIN_AREA0) + (col & ~3)); + else + p = (volatile u32 *)((ulong) (SPARE_AREA0) - + mtd->writesize + (col & ~3)); + + MTDDEBUG(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->writesize - col; + + if (col >= mtd->writesize) + m += mtd->oobsize; + + m = min(n, m) & ~3; + + MTDDEBUG(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 */ + g_nandfc_info.col_addr = 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, u_char *buf, int len) +{ + int n; + int col; + int i = 0; + + MTDDEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_read_buf(col = %d, len = %d)\n", + g_nandfc_info.col_addr, len); + + col = g_nandfc_info.col_addr; + /* Adjust saved column address */ + if (col < mtd->writesize && g_nandfc_info.spare_only) + col += mtd->writesize; + + n = mtd->writesize + mtd->oobsize - col; + n = min(len, n); + + while (n) { + volatile u32 *p; + + if (col < mtd->writesize) + p = (volatile u32 *)((ulong) (MAIN_AREA0) + (col & ~3)); + else + p = (volatile u32 *)((ulong) (SPARE_AREA0) - + mtd->writesize + (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->writesize - col; + + if (col >= mtd->writesize) + 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 */ + g_nandfc_info.col_addr = 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 u_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) +{ +#ifdef CONFIG_MTD_NAND_MXC_FORCE_CE + if (chip > 0) { + MTDDEBUG(MTD_DEBUG_LEVEL0, + "ERROR: Illegal chip select (chip = %d)\n", chip); + return; + } + + if (chip == -1) { + NFC_CONFIG1 &= ~NFC_CE; + return; + } + + NFC_CONFIG1 |= NFC_CE; +#endif +} + +/** + * 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) +{ + MTDDEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", + command, column, page_addr); + + /* + * Reset command state information + */ + g_nandfc_info.status_request = 0; + + /* + * Command pre-processing step + */ + switch (command) { + + case NAND_CMD_STATUS: + g_nandfc_info.col_addr = 0; + g_nandfc_info.status_request = 1; + break; + + case NAND_CMD_READ0: + g_nandfc_info.col_addr = column; + g_nandfc_info.spare_only = 0; + break; + + case NAND_CMD_READOOB: + g_nandfc_info.col_addr = column; + g_nandfc_info.spare_only = 1; + if (is2k_pagesize) + command = NAND_CMD_READ0; /* only READ0 is valid */ + break; + + case NAND_CMD_SEQIN: + if (column >= mtd->writesize) { + if (is2k_pagesize) { + /* + * FIXME: before send SEQIN command for + * write OOB, we must read one page out. + * For K9F1GXX 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); + } + g_nandfc_info.col_addr = column - mtd->writesize; + g_nandfc_info.spare_only = 1; + /* Set program pointer to spare region */ + if (!is2k_pagesize) + send_cmd(NAND_CMD_READOOB, 0); + } else { + g_nandfc_info.spare_only = 0; + g_nandfc_info.col_addr = column; + /* Set program pointer to page start */ + if (!is2k_pagesize) + send_cmd(NAND_CMD_READ0, 0); + } + break; + + case NAND_CMD_PAGEPROG: +#ifndef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2 + if (ecc_disabled) { + /* Enable Ecc for page writes */ + NFC_CONFIG1 |= NFC_ECC_EN; + } +#endif + send_prog_page(0, g_nandfc_info.spare_only); + + if (is2k_pagesize) { + /* data in 4 areas datas */ + send_prog_page(1, g_nandfc_info.spare_only); + send_prog_page(2, g_nandfc_info.spare_only); + send_prog_page(3, g_nandfc_info.spare_only); + } + + break; + + case NAND_CMD_ERASE1: + break; + } + + /* + * Write out the command to the device. + */ + send_cmd(command, 0); + + /* + * 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, page_addr == -1); + if (is2k_pagesize) + /* another col addr cycle for 2k page */ + send_addr(0, 0); + } + + /* + * Write out page address, if necessary + */ + if (page_addr != -1) { + /* paddr_0 - p_addr_7 */ + send_addr((page_addr & 0xff), 0); + + if (is2k_pagesize) { + /* One more address cycle for higher + * density devices */ + + if (mtd->size >= 0x10000000) { + /* paddr_8 - paddr_15 */ + send_addr((page_addr >> 8) & 0xff, 0); + send_addr((page_addr >> 16) & 0xff, 1); + } else + /* paddr_8 - paddr_15 */ + send_addr((page_addr >> 8) & 0xff, 1); + } else { + /* One more address cycle for higher + * density devices */ + + if (mtd->size >= 0x4000000) { + /* paddr_8 - paddr_15 */ + send_addr((page_addr >> 8) & 0xff, 0); + send_addr((page_addr >> 16) & 0xff, 1); + } else + /* paddr_8 - paddr_15 */ + send_addr((page_addr >> 8) & 0xff, 1); + } + } + + /* + * Command post-processing step + */ + switch (command) { + + case NAND_CMD_RESET: + break; + + case NAND_CMD_READOOB: + case NAND_CMD_READ0: + if (is2k_pagesize) { + /* send read confirm command */ + send_cmd(NAND_CMD_READSTART, 1); + /* read for each AREA */ + send_read_page(0, g_nandfc_info.spare_only); + send_read_page(1, g_nandfc_info.spare_only); + send_read_page(2, g_nandfc_info.spare_only); + send_read_page(3, g_nandfc_info.spare_only); + } else + send_read_page(0, g_nandfc_info.spare_only); + break; + + case NAND_CMD_READID: + send_read_id(); + break; + + case NAND_CMD_PAGEPROG: +#ifndef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2 + if (ecc_disabled) { + /* Disable Ecc after page writes */ + NFC_CONFIG1 &= ~NFC_ECC_EN; + } +#endif + break; + + case NAND_CMD_STATUS: + 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->writesize */ + if (mtd->writesize == 2048) { + NFMS |= 1 << NFMS_BIT; + is2k_pagesize = 1; + } else { + if ((NFMS >> NFMS_BIT) & 0x1) { + /* This case has happened on some SoCs */ + printk(KERN_INFO + "NFMS Bit set for 512B Page, resetting it." + " [RCSR: 0x%08x]\n", + NFMS); + NFMS &= ~(1 << NFMS_BIT); + } + is2k_pagesize = 0; + } + + if (is2k_pagesize) + this->ecc.layout = &nand_hw_eccoob_2k; + + /* 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 (mtd->writesize == 2048) + this->badblock_pattern = &smallpage_memorybased; + else + this->badblock_pattern = (mtd->writesize > 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) +{ + struct mtd_info *mtd; + + mxc_nand_data = malloc(sizeof(struct mxc_mtd_s)); + if (!mxc_nand_data) { + printf("mxc_nd: No memory from malloc!\n"); + return -1; + } + memset(mxc_nand_data, 0, sizeof(struct mxc_mtd_s)); + + mtd = &mxc_nand_data->mtd; + mtd->priv = nand; + nand->priv = mxc_nand_data; + + nand->chip_delay = 0; + + nand->dev_ready = mxc_nand_dev_ready; + 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; + + NFC_CONFIG1 |= NFC_INT_MSK; + + if (hardware_ecc) { + nand->ecc.calculate = mxc_nand_calculate_ecc; + nand->ecc.hwctl = mxc_nand_enable_hwecc; + nand->ecc.correct = mxc_nand_correct_data; + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.layout = &nand_hw_eccoob_8; + nand->ecc.size = 512; + nand->ecc.bytes = 3; + NFC_CONFIG1 |= NFC_ECC_EN; + } else + nand->ecc.mode = NAND_ECC_SOFT; + + /* Reset NAND */ + nand->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* 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) { + is2k_pagesize = 1; + nand->ecc.layout = &nand_hw_eccoob_2k; + } else + is2k_pagesize = 0; + + return 0; +} diff --git a/include/asm-arm/arch-mx31/mx31-regs.h b/include/asm-arm/arch-mx31/mx31-regs.h index b04a718..78825f5 100644 --- a/include/asm-arm/arch-mx31/mx31-regs.h +++ b/include/asm-arm/arch-mx31/mx31-regs.h @@ -168,4 +168,100 @@ #define CS5_BASE 0xB6000000 #define PCMCIA_MEM_BASE 0xC0000000 +/* + * NAND controller + */ +#define NFC_BASE_ADDR 0xB8000000 + +/* + * Addresses for NFC registers + */ +#define NFC_BUF_SIZE (*((volatile u16 *)(NFC_BASE_ADDR + 0xE00))) +#define NFC_BUF_ADDR (*((volatile u16 *)(NFC_BASE_ADDR + 0xE04))) +#define NFC_FLASH_ADDR (*((volatile u16 *)(NFC_BASE_ADDR + 0xE06))) +#define NFC_FLASH_CMD (*((volatile u16 *)(NFC_BASE_ADDR + 0xE08))) +#define NFC_CONFIG (*((volatile u16 *)(NFC_BASE_ADDR + 0xE0A))) +#define NFC_ECC_STATUS_RESULT (*((volatile u16 *)(NFC_BASE_ADDR + 0xE0C))) +#define NFC_RSLTMAIN_AREA (*((volatile u16 *)(NFC_BASE_ADDR + 0xE0E))) +#define NFC_RSLTSPARE_AREA (*((volatile u16 *)(NFC_BASE_ADDR + 0xE10))) +#define NFC_WRPROT (*((volatile u16 *)(NFC_BASE_ADDR + 0xE12))) +#define NFC_UNLOCKSTART_BLKADDR (*((volatile u16 *)(NFC_BASE_ADDR + 0xE14))) +#define NFC_UNLOCKEND_BLKADDR (*((volatile u16 *)(NFC_BASE_ADDR + 0xE16))) +#define NFC_NF_WRPRST (*((volatile u16 *)(NFC_BASE_ADDR + 0xE18))) +#define NFC_CONFIG1 (*((volatile u16 *)(NFC_BASE_ADDR + 0xE1A))) +#define NFC_CONFIG2 (*((volatile u16 *)(NFC_BASE_ADDR + 0xE1C))) + +/* + * Addresses for NFC RAM BUFFER Main area 0 + */ +#define MAIN_AREA0 (volatile u16 *)(NFC_BASE_ADDR + 0x000) +#define MAIN_AREA1 (volatile u16 *)(NFC_BASE_ADDR + 0x200) +#define MAIN_AREA2 (volatile u16 *)(NFC_BASE_ADDR + 0x400) +#define MAIN_AREA3 (volatile u16 *)(NFC_BASE_ADDR + 0x600) + +/* + * Addresses for NFC SPARE BUFFER Spare area 0 + */ +#define SPARE_AREA0 (volatile u16 *)(NFC_BASE_ADDR + 0x800) +#define SPARE_AREA1 (volatile u16 *)(NFC_BASE_ADDR + 0x810) +#define SPARE_AREA2 (volatile u16 *)(NFC_BASE_ADDR + 0x820) +#define SPARE_AREA3 (volatile u16 *)(NFC_BASE_ADDR + 0x830) + +/* + * Set INT to 0, FCMD to 1, rest to 0 in NFC_CONFIG2 Register for Command + * operation + */ +#define NFC_CMD 0x1 + +/* + * Set INT to 0, FADD to 1, rest to 0 in NFC_CONFIG2 Register for Address + * operation + */ +#define NFC_ADDR 0x2 + +/* + * Set INT to 0, FDI to 1, rest to 0 in NFC_CONFIG2 Register for Input + * operation + */ +#define NFC_INPUT 0x4 + +/* + * Set INT to 0, FDO to 001, rest to 0 in NFC_CONFIG2 Register for Data + * Output operation + */ +#define NFC_OUTPUT 0x8 + +/* + * Set INT to 0, FD0 to 010, rest to 0 in NFC_CONFIG2 Register for Read ID + * operation + */ +#define NFC_ID 0x10 + +/* + * Set INT to 0, FDO to 100, rest to 0 in NFC_CONFIG2 Register for Read + * Status operation + */ +#define NFC_STATUS 0x20 + +/* + * Set INT to 1, rest to 0 in NFC_CONFIG2 Register for Read Status + * operation + */ +#define NFC_INT 0x8000 + +#define NFC_SP_EN (1 << 2) +#define NFC_ECC_EN (1 << 3) +#define NFC_INT_MSK (1 << 4) +#define NFC_BIG (1 << 5) +#define NFC_RST (1 << 6) +#define NFC_CE (1 << 7) +#define NFC_ONE_CYCLE (1 << 8) + +/* + * NFMS bit in RCSR register for pagesize of nandflash + */ +#define NFMS (*((volatile u32 *)CCM_RCSR)) +#define NFMS_BIT 30 + #endif /* __ASM_ARCH_MX31_REGS_H */ + -- 1.5.2.4 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot