From 24d75dac82f1138bc9454ecb4788b438405a608e Mon Sep 17 00:00:00 2001 From: =?utf8?q?Lothar=20Wa=C3=9Fmann?= Date: Tue, 16 Jun 2015 11:11:55 +0200 Subject: [PATCH] mtd: mxc_nand: sync with Linux mxc_nand driver --- drivers/mtd/nand/mxc_nand.c | 1872 ++++++++++++++++------------------- 1 file changed, 843 insertions(+), 1029 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 2e5b5b9bf9..566f55de6a 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -14,690 +15,526 @@ defined(CONFIG_MX51) || defined(CONFIG_MX53) #include #endif -#include "mxc_nand.h" + +static struct mxc_nand_host mxc_host; +static struct mxc_nand_host *host = &mxc_host; + +#ifdef CONFIG_MX27 +static int is_16bit_nand(void) +{ + struct system_control_regs *sc_regs = + (struct system_control_regs *)IMX_SYSTEM_CTL_BASE; + + if (readl(&sc_regs->fmcr) & NF_16BIT_SEL) + return 1; + else + return 0; +} +#elif defined(CONFIG_MX31) +static int is_16bit_nand(void) +{ + struct clock_control_regs *sc_regs = + (struct clock_control_regs *)CCM_BASE; + + if (readl(&sc_regs->rcsr) & CCM_RCSR_NF16B) + return 1; + else + return 0; +} +#elif defined(CONFIG_MX25) || defined(CONFIG_MX35) +static int is_16bit_nand(void) +{ + struct ccm_regs *ccm = + (struct ccm_regs *)IMX_CCM_BASE; + + if (readl(&ccm->rcsr) & CCM_RCSR_NF_16BIT_SEL) + return 1; + else + return 0; +} +#elif defined(CONFIG_MX51) +static int is_16bit_nand(void) +{ + struct src *src = (struct src *)SRC_BASE_ADDR; + + if (readl(&src->sbmr) & (1 << 2)) + return 1; + else + return 0; +} +#elif defined(CONFIG_MX53) +/* BOOT_CFG[1..3][0..7] */ +#define SRC_BOOT_CFG(m, n) (1 << ((m) * 8 + (n))) +static int is_16bit_nand(void) +{ + struct src *src = (struct src *)SRC_BASE_ADDR; + + if (readl(&src->sbmr) & SRC_BOOT_CFG(2, 5)) + return 1; + else + return 0; +} +#else +#warning "8/16 bit NAND autodetection not supported" +static int is_16bit_nand(void) +{ + return 0; +} +#endif + +#define MXC_NAND_TIMEOUT (1 * HZ) #define DRIVER_NAME "mxc_nand" -struct mxc_nand_host { - struct mtd_info mtd; - struct nand_chip *nand; +#ifndef CONFIG_MXC_NAND_REGS_BASE +#error CONFIG_MXC_NAND_REGS_BASE not defined +#endif - struct mxc_nand_regs __iomem *regs; -#ifdef MXC_NFC_V3_2 - struct mxc_nand_ip_regs __iomem *ip_regs; +#if defined(CONFIG_MX27) || defined(CONFIG_MX31) +#define nfc_is_v1() 1 +#define nfc_is_v21() 0 +#define nfc_is_v3_2() 0 +#define nfc_is_v3() nfc_is_v3_2() +#define NFC_VERSION "V1" +#elif defined(CONFIG_MX25) || defined(CONFIG_MX35) +#define nfc_is_v1() 0 +#define nfc_is_v21() 1 +#define nfc_is_v3_2() 0 +#define nfc_is_v3() nfc_is_v3_2() +#define NFC_VERSION "V2" +#elif defined(CONFIG_MX51) || defined(CONFIG_MX53) +#define nfc_is_v1() 0 +#define nfc_is_v21() 0 +#define nfc_is_v3_2() 1 +#define nfc_is_v3() nfc_is_v3_2() +#define NFC_VERSION "V3" +#ifndef CONFIG_MXC_NAND_IP_REGS_BASE +#error CONFIG_MXC_NAND_IP_REGS_BASE not defined +#endif +#else +#error mxc_nand driver not supported on this platform +#define NFC_VERSION "unknown" #endif - int spare_only; - int status_request; - int pagesize_2k; - int clk_act; - uint16_t col_addr; - unsigned int page_addr; -}; -static struct mxc_nand_host mxc_host; -static struct mxc_nand_host *host = &mxc_host; +/* Addresses for NFC registers */ +#define NFC_V1_V2_BUF_SIZE (host->regs + 0x00) +#define NFC_V1_V2_BUF_ADDR (host->regs + 0x04) +#define NFC_V1_V2_FLASH_ADDR (host->regs + 0x06) +#define NFC_V1_V2_FLASH_CMD (host->regs + 0x08) +#define NFC_V1_V2_CONFIG (host->regs + 0x0a) +#define NFC_V1_V2_ECC_STATUS_RESULT (host->regs + 0x0c) +#define NFC_V1_V2_RSLTMAIN_AREA (host->regs + 0x0e) +#define NFC_V1_V2_RSLTSPARE_AREA (host->regs + 0x10) +#define NFC_V1_V2_WRPROT (host->regs + 0x12) +#define NFC_V1_UNLOCKSTART_BLKADDR (host->regs + 0x14) +#define NFC_V1_UNLOCKEND_BLKADDR (host->regs + 0x16) +#define NFC_V21_UNLOCKSTART_BLKADDR0 (host->regs + 0x20) +#define NFC_V21_UNLOCKSTART_BLKADDR1 (host->regs + 0x24) +#define NFC_V21_UNLOCKSTART_BLKADDR2 (host->regs + 0x28) +#define NFC_V21_UNLOCKSTART_BLKADDR3 (host->regs + 0x2c) +#define NFC_V21_UNLOCKEND_BLKADDR0 (host->regs + 0x22) +#define NFC_V21_UNLOCKEND_BLKADDR1 (host->regs + 0x26) +#define NFC_V21_UNLOCKEND_BLKADDR2 (host->regs + 0x2a) +#define NFC_V21_UNLOCKEND_BLKADDR3 (host->regs + 0x2e) +#define NFC_V1_V2_NF_WRPRST (host->regs + 0x18) +#define NFC_V1_V2_CONFIG1 (host->regs + 0x1a) +#define NFC_V1_V2_CONFIG2 (host->regs + 0x1c) + +#define NFC_V2_CONFIG1_ECC_MODE_4 (1 << 0) +#define NFC_V1_V2_CONFIG1_SP_EN (1 << 2) +#define NFC_V1_V2_CONFIG1_ECC_EN (1 << 3) +#define NFC_V1_V2_CONFIG1_INT_MSK (1 << 4) +#define NFC_V1_V2_CONFIG1_BIG (1 << 5) +#define NFC_V1_V2_CONFIG1_RST (1 << 6) +#define NFC_V1_V2_CONFIG1_CE (1 << 7) +#define NFC_V2_CONFIG1_ONE_CYCLE (1 << 8) +#define NFC_V2_CONFIG1_PPB(x) (((x) & 0x3) << 9) +#define NFC_V2_CONFIG1_FP_INT (1 << 11) + +#define NFC_V1_V2_CONFIG2_INT (1 << 15) -/* 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) >> 3) -#define BITPOS(x) ((x) & 0xf) +/* + * Operation modes for the NFC. Valid for v1, v2 and v3 + * type controllers. + */ +#define NFC_CMD (1 << 0) +#define NFC_ADDR (1 << 1) +#define NFC_INPUT (1 << 2) +#define NFC_OUTPUT (1 << 3) +#define NFC_ID (1 << 4) +#define NFC_STATUS (1 << 5) + +#define NFC_V3_FLASH_CMD (host->regs_axi + 0x00) +#define NFC_V3_FLASH_ADDR0 (host->regs_axi + 0x04) + +#define NFC_V3_CONFIG1 (host->regs_axi + 0x34) +#define NFC_V3_CONFIG1_SP_EN (1 << 0) +#define NFC_V3_CONFIG1_RBA(x) (((x) & 0x7 ) << 4) + +#define NFC_V3_ECC_STATUS_RESULT (host->regs_axi + 0x38) + +#define NFC_V3_LAUNCH (host->regs_axi + 0x40) + +#define NFC_V3_WRPROT (host->regs_ip + 0x0) +#define NFC_V3_WRPROT_LOCK_TIGHT (1 << 0) +#define NFC_V3_WRPROT_LOCK (1 << 1) +#define NFC_V3_WRPROT_UNLOCK (1 << 2) +#define NFC_V3_WRPROT_BLS_UNLOCK (2 << 6) + +#define NFC_V3_WRPROT_UNLOCK_BLK_ADD0 (host->regs_ip + 0x04) + +#define NFC_V3_CONFIG2 (host->regs_ip + 0x24) +#define NFC_V3_CONFIG2_PS_512 (0 << 0) +#define NFC_V3_CONFIG2_PS_2048 (1 << 0) +#define NFC_V3_CONFIG2_PS_4096 (2 << 0) +#define NFC_V3_CONFIG2_ONE_CYCLE (1 << 2) +#define NFC_V3_CONFIG2_ECC_EN (1 << 3) +#define NFC_V3_CONFIG2_2CMD_PHASES (1 << 4) +#define NFC_V3_CONFIG2_NUM_ADDR_PHASE0 (1 << 5) +#define NFC_V3_CONFIG2_ECC_MODE_8 (1 << 6) +#define NFC_V3_CONFIG2_PPB(x) (((x) & 0x3) << 7) +#define MX53_CONFIG2_PPB(x) (((x) & 0x3) << 8) +#define NFC_V3_CONFIG2_NUM_ADDR_PHASE1(x) (((x) & 0x3) << 12) +#define NFC_V3_CONFIG2_INT_MSK (1 << 15) +#define NFC_V3_CONFIG2_ST_CMD(x) (((x) & 0xff) << 24) +#define NFC_V3_CONFIG2_SPAS(x) (((x) & 0xff) << 16) + +#define NFC_V3_CONFIG3 (host->regs_ip + 0x28) +#define NFC_V3_CONFIG3_ADD_OP(x) (((x) & 0x3) << 0) +#define NFC_V3_CONFIG3_FW8 (1 << 3) +#define NFC_V3_CONFIG3_SBB(x) (((x) & 0x7) << 8) +#define NFC_V3_CONFIG3_NUM_OF_DEVICES(x) (((x) & 0x7) << 12) +#define NFC_V3_CONFIG3_RBB_MODE (1 << 15) +#define NFC_V3_CONFIG3_NO_SDMA (1 << 20) + +#define NFC_V3_IPC (host->regs_ip + 0x2C) +#define NFC_V3_IPC_CREQ (1 << 0) +#define NFC_V3_IPC_CACK (1 << 1) +#define NFC_V3_IPC_INT (1 << 31) + +#define NFC_V3_DELAY_LINE (host->regs_ip + 0x34) -/* Define single bit Error positions in Main & Spare area */ -#define MAIN_SINGLEBIT_ERROR 0x4 -#define SPARE_SINGLEBIT_ERROR 0x1 +struct mxc_nand_host { + struct mtd_info mtd; + struct nand_chip nand; + + void *spare0; + void *main_area0; + + void __iomem *base; + void __iomem *regs; + void __iomem *regs_axi; + void __iomem *regs_ip; + int status_request; + int eccsize; + int active_cs; + + uint8_t *data_buf; + unsigned int buf_start; + int spare_len; + + void (*preset)(struct mtd_info *); + void (*send_cmd)(struct mxc_nand_host *, uint16_t, int); + void (*send_addr)(struct mxc_nand_host *, uint16_t, int); + void (*send_page)(struct mtd_info *, unsigned int); + void (*send_read_id)(struct mxc_nand_host *); + uint16_t (*get_dev_status)(struct mxc_nand_host *); + int (*check_int)(struct mxc_nand_host *); +}; /* OOB placement block for use with hardware ecc generation */ -#if defined(MXC_NFC_V1) -#ifndef CONFIG_SYS_NAND_LARGEPAGE -static struct nand_ecclayout nand_hw_eccoob = { +static struct nand_ecclayout nandv1_hw_eccoob_smallpage = { .eccbytes = 5, .eccpos = {6, 7, 8, 9, 10}, - .oobfree = { {0, 5}, {11, 5}, } + .oobfree = {{0, 5}, {12, 4}, } }; -#else -static struct nand_ecclayout nand_hw_eccoob2k = { + +static struct nand_ecclayout nandv1_hw_eccoob_largepage = { .eccbytes = 20, + .eccpos = {6, 7, 8, 9, 10, 22, 23, 24, 25, 26, + 38, 39, 40, 41, 42, 54, 55, 56, 57, 58}, + .oobfree = {{2, 4}, {11, 10}, {27, 10}, {43, 10}, {59, 5}, } +}; + +/* OOB description for 512 byte pages with 16 byte OOB */ +static struct nand_ecclayout nandv2_hw_eccoob_smallpage = { + .eccbytes = 1 * 9, .eccpos = { - 6, 7, 8, 9, 10, - 22, 23, 24, 25, 26, - 38, 39, 40, 41, 42, - 54, 55, 56, 57, 58, + 7, 8, 9, 10, 11, 12, 13, 14, 15 }, - .oobfree = { {2, 4}, {11, 11}, {27, 11}, {43, 11}, {59, 5} }, + .oobfree = { + {.offset = 0, .length = 5} + } }; -#endif -#elif defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2) -#ifndef CONFIG_SYS_NAND_LARGEPAGE -static struct nand_ecclayout nand_hw_eccoob = { - .eccbytes = 9, - .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, - .oobfree = { {2, 5} } + +/* OOB description for 2048 byte pages with 64 byte OOB */ +static struct nand_ecclayout nandv2_hw_eccoob_largepage = { + .eccbytes = 4 * 9, + .eccpos = { + 7, 8, 9, 10, 11, 12, 13, 14, 15, + 23, 24, 25, 26, 27, 28, 29, 30, 31, + 39, 40, 41, 42, 43, 44, 45, 46, 47, + 55, 56, 57, 58, 59, 60, 61, 62, 63 + }, + .oobfree = { + {.offset = 2, .length = 4}, + {.offset = 16, .length = 7}, + {.offset = 32, .length = 7}, + {.offset = 48, .length = 7} + } }; -#else -static struct nand_ecclayout nand_hw_eccoob2k = { - .eccbytes = 36, + +/* OOB description for 4096 byte pages with 128 byte OOB */ +static struct nand_ecclayout nandv2_hw_eccoob_4k = { + .eccbytes = 8 * 9, .eccpos = { - 7, 8, 9, 10, 11, 12, 13, 14, 15, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 23, 24, 25, 26, 27, 28, 29, 30, 31, 39, 40, 41, 42, 43, 44, 45, 46, 47, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 71, 72, 73, 74, 75, 76, 77, 78, 79, + 87, 88, 89, 90, 91, 92, 93, 94, 95, + 103, 104, 105, 106, 107, 108, 109, 110, 111, + 119, 120, 121, 122, 123, 124, 125, 126, 127, }, - .oobfree = { {2, 5}, {16, 7}, {32, 7}, {48, 7} }, + .oobfree = { + {.offset = 2, .length = 4}, + {.offset = 16, .length = 7}, + {.offset = 32, .length = 7}, + {.offset = 48, .length = 7}, + {.offset = 64, .length = 7}, + {.offset = 80, .length = 7}, + {.offset = 96, .length = 7}, + {.offset = 112, .length = 7}, + } }; -#endif -#endif -static int is_16bit_nand(void) +static int check_int_v3(struct mxc_nand_host *host) { -#if defined(CONFIG_SYS_NAND_BUSWIDTH_16BIT) + uint32_t tmp; + + tmp = readl(NFC_V3_IPC); + if (!(tmp & NFC_V3_IPC_INT)) + return 0; + + tmp &= ~NFC_V3_IPC_INT; + writel(tmp, NFC_V3_IPC); + return 1; -#else - return 0; -#endif } -static uint32_t *mxc_nand_memcpy32(uint32_t *dest, uint32_t *source, size_t size) +static int check_int_v1_v2(struct mxc_nand_host *host) { - uint32_t *d = dest; + uint32_t tmp; - size >>= 2; - while (size--) - __raw_writel(__raw_readl(source++), d++); - return dest; + tmp = readw(NFC_V1_V2_CONFIG2); + if (!(tmp & NFC_V1_V2_CONFIG2_INT)) + return 0; + + writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2); + + return 1; } -/* - * This function polls the NANDFC to wait for the basic operation to - * complete by checking the INT bit. +/* This function polls the NANDFC to wait for the basic operation to + * complete by checking the INT bit of config2 register. */ -static void wait_op_done(struct mxc_nand_host *host, int max_retries, - uint16_t param) +static void wait_op_done(struct mxc_nand_host *host, bool useirq) { - uint32_t tmp; + int max_retries = 8000; while (max_retries-- > 0) { -#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) - tmp = readnfc(&host->regs->config2); - if (tmp & NFC_V1_V2_CONFIG2_INT) { - tmp &= ~NFC_V1_V2_CONFIG2_INT; - writenfc(tmp, &host->regs->config2); -#elif defined(MXC_NFC_V3_2) - tmp = readnfc(&host->ip_regs->ipc); - if (tmp & NFC_V3_IPC_INT) { - tmp &= ~NFC_V3_IPC_INT; - writenfc(tmp, &host->ip_regs->ipc); -#endif + if (host->check_int(host)) break; - } + udelay(1); } - if (max_retries < 0) { - MTDDEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n", - __func__, param); - } + if (max_retries < 0) + pr_debug("%s: INT not set\n", __func__); } -/* - * This function issues the specified command to the NAND device and - * waits for completion. - */ -static void send_cmd(struct mxc_nand_host *host, uint16_t cmd) +static void send_cmd_v3(struct mxc_nand_host *host, uint16_t cmd, int useirq) { - MTDDEBUG(MTD_DEBUG_LEVEL3, "send_cmd(host, 0x%x)\n", cmd); + /* fill command */ + writel(cmd, NFC_V3_FLASH_CMD); - writenfc(cmd, &host->regs->flash_cmd); - writenfc(NFC_CMD, &host->regs->operation); + /* send out command */ + writel(NFC_CMD, NFC_V3_LAUNCH); /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, cmd); + wait_op_done(host, 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. - */ -static void send_addr(struct mxc_nand_host *host, uint16_t addr) +/* This function issues the specified command to the NAND device and + * waits for completion. */ +static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq) { - MTDDEBUG(MTD_DEBUG_LEVEL3, "send_addr(host, 0x%x)\n", addr); + pr_debug("send_cmd(host, 0x%x, %d)\n", cmd, useirq); - writenfc(addr, &host->regs->flash_addr); - writenfc(NFC_ADDR, &host->regs->operation); + writew(cmd, NFC_V1_V2_FLASH_CMD); + writew(NFC_CMD, NFC_V1_V2_CONFIG2); /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, addr); + wait_op_done(host, useirq); } -/* - * This function requests the NANDFC to initiate the transfer - * of data currently in the NANDFC RAM buffer to the NAND device. - */ -static void send_prog_page(struct mxc_nand_host *host, uint8_t buf_id, - int spare_only) +static void send_addr_v3(struct mxc_nand_host *host, uint16_t addr, int islast) { - if (spare_only) - MTDDEBUG(MTD_DEBUG_LEVEL1, "send_prog_page (%d)\n", spare_only); - - if (is_mxc_nfc_21() || is_mxc_nfc_32()) { - int i; - /* - * The controller copies the 64 bytes of spare data from - * the first 16 bytes of each of the 4 64 byte spare buffers. - * Copy the contiguous data starting in spare_area[0] to - * the four spare area buffers. - */ - for (i = 1; i < 4; i++) { - void __iomem *src = &host->regs->spare_area[0][i * 16]; - void __iomem *dst = &host->regs->spare_area[i][0]; - - mxc_nand_memcpy32(dst, src, 16); - } - } + /* fill address */ + writel(addr, NFC_V3_FLASH_ADDR0); -#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) - writenfc(buf_id, &host->regs->buf_addr); -#elif defined(MXC_NFC_V3_2) - uint32_t tmp = readnfc(&host->regs->config1); - tmp &= ~NFC_V3_CONFIG1_RBA_MASK; - tmp |= NFC_V3_CONFIG1_RBA(buf_id); - writenfc(tmp, &host->regs->config1); -#endif - - /* Configure spare or page+spare access */ - if (!host->pagesize_2k) { - uint32_t config1 = readnfc(&host->regs->config1); - if (spare_only) - config1 |= NFC_CONFIG1_SP_EN; - else - config1 &= ~NFC_CONFIG1_SP_EN; - writenfc(config1, &host->regs->config1); - } + /* send out address */ + writel(NFC_ADDR, NFC_V3_LAUNCH); - writenfc(NFC_INPUT, &host->regs->operation); - - /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, spare_only); + wait_op_done(host, islast); } -/* - * Requests NANDFC to initiate the transfer of data from the - * NAND device into in the NANDFC ram buffer. - */ -static void send_read_page(struct mxc_nand_host *host, uint8_t buf_id, - int spare_only) +/* 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. */ +static void send_addr_v1_v2(struct mxc_nand_host *host, uint16_t addr, int islast) { - MTDDEBUG(MTD_DEBUG_LEVEL3, "send_read_page (%d)\n", spare_only); - -#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) - writenfc(buf_id, &host->regs->buf_addr); -#elif defined(MXC_NFC_V3_2) - uint32_t tmp = readnfc(&host->regs->config1); - tmp &= ~NFC_V3_CONFIG1_RBA_MASK; - tmp |= NFC_V3_CONFIG1_RBA(buf_id); - writenfc(tmp, &host->regs->config1); -#endif - - /* Configure spare or page+spare access */ - if (!host->pagesize_2k) { - uint32_t config1 = readnfc(&host->regs->config1); - if (spare_only) - config1 |= NFC_CONFIG1_SP_EN; - else - config1 &= ~NFC_CONFIG1_SP_EN; - writenfc(config1, &host->regs->config1); - } + pr_debug("send_addr(host, 0x%x %d)\n", addr, islast); - writenfc(NFC_OUTPUT, &host->regs->operation); + writew(addr, NFC_V1_V2_FLASH_ADDR); + writew(NFC_ADDR, NFC_V1_V2_CONFIG2); /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, spare_only); - - if (is_mxc_nfc_21() || is_mxc_nfc_32()) { - int i; - - /* - * The controller copies the 64 bytes of spare data to - * the first 16 bytes of each of the 4 spare buffers. - * Make the data contiguous starting in spare_area[0]. - */ - for (i = 1; i < 4; i++) { - void __iomem *src = &host->regs->spare_area[i][0]; - void __iomem *dst = &host->regs->spare_area[0][i * 16]; - - mxc_nand_memcpy32(dst, src, 16); - } - } + wait_op_done(host, islast); } -/* Request the NANDFC to perform a read of the NAND device ID. */ -static void send_read_id(struct mxc_nand_host *host) +static void send_page_v3(struct mtd_info *mtd, unsigned int ops) { + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; uint32_t tmp; -#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) - /* NANDFC buffer 0 is used for device ID output */ - writenfc(0x0, &host->regs->buf_addr); -#elif defined(MXC_NFC_V3_2) - tmp = readnfc(&host->regs->config1); - tmp &= ~NFC_V3_CONFIG1_RBA_MASK; - writenfc(tmp, &host->regs->config1); -#endif - - /* Read ID into main buffer */ - tmp = readnfc(&host->regs->config1); - tmp &= ~NFC_CONFIG1_SP_EN; - writenfc(tmp, &host->regs->config1); + tmp = readl(NFC_V3_CONFIG1); + tmp &= ~(7 << 4); + writel(tmp, NFC_V3_CONFIG1); - writenfc(NFC_ID, &host->regs->operation); + /* transfer data from NFC ram to nand */ + writel(ops, NFC_V3_LAUNCH); - /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, 0); + wait_op_done(host, false); } -/* - * This function requests the NANDFC to perform a read of the - * NAND device status and returns the current status. - */ -static uint16_t get_dev_status(struct mxc_nand_host *host) -{ -#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) - void __iomem *main_buf = host->regs->main_area[1]; - uint32_t store; -#endif - uint32_t ret, tmp; - /* Issue status request to NAND device */ - -#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) - /* store the main area1 first word, later do recovery */ - store = readl(main_buf); - /* NANDFC buffer 1 is used for device status */ - writenfc(1, &host->regs->buf_addr); -#endif - - /* Read status into main buffer */ - tmp = readnfc(&host->regs->config1); - tmp &= ~NFC_CONFIG1_SP_EN; - writenfc(tmp, &host->regs->config1); - - writenfc(NFC_STATUS, &host->regs->operation); - - /* Wait for operation to complete */ - wait_op_done(host, TROP_US_DELAY, 0); - -#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) - /* - * Status is placed in first word of main buffer - * get status, then recovery area 1 data - */ - ret = readw(main_buf); - writel(store, main_buf); -#elif defined(MXC_NFC_V3_2) - ret = readnfc(&host->regs->config1) >> 16; -#endif - - return ret; -} - -/* This function is used by upper layer to checks if device is ready */ -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 on) +static void send_page_v1_v2(struct mtd_info *mtd, unsigned int ops) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; -#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) - uint16_t tmp = readnfc(&host->regs->config1); + int bufs, i; - if (on) - tmp |= NFC_V1_V2_CONFIG1_ECC_EN; + if (nfc_is_v1() && mtd->writesize > 512) + bufs = 4; else - tmp &= ~NFC_V1_V2_CONFIG1_ECC_EN; - writenfc(tmp, &host->regs->config1); -#elif defined(MXC_NFC_V3_2) - uint32_t tmp = readnfc(&host->ip_regs->config2); + bufs = 1; - if (on) - tmp |= NFC_V3_CONFIG2_ECC_EN; - else - tmp &= ~NFC_V3_CONFIG2_ECC_EN; - writenfc(tmp, &host->ip_regs->config2); -#endif -} + for (i = 0; i < bufs; i++) { -#ifdef CONFIG_MXC_NAND_HWECC -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. - */ -} + /* NANDFC buffer 0 is used for page read/write */ + writew((host->active_cs << 4) | i, NFC_V1_V2_BUF_ADDR); -#if defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2) -static int mxc_nand_read_oob_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, - int page) -{ - struct mxc_nand_host *host = chip->priv; - uint8_t *buf = chip->oob_poi; - int length = mtd->oobsize; - int eccpitch = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; - uint8_t *bufpoi = buf; - int i, toread; - - MTDDEBUG(MTD_DEBUG_LEVEL0, - "%s: Reading OOB area of page %u to oob %p\n", - __func__, page, buf); - - chip->cmdfunc(mtd, NAND_CMD_READOOB, mtd->writesize, page); - for (i = 0; i < chip->ecc.steps; i++) { - toread = min_t(int, length, chip->ecc.prepad); - if (toread) { - chip->read_buf(mtd, bufpoi, toread); - bufpoi += toread; - length -= toread; - } - bufpoi += chip->ecc.bytes; - host->col_addr += chip->ecc.bytes; - length -= chip->ecc.bytes; - - toread = min_t(int, length, chip->ecc.postpad); - if (toread) { - chip->read_buf(mtd, bufpoi, toread); - bufpoi += toread; - length -= toread; - } - } - if (length > 0) - chip->read_buf(mtd, bufpoi, length); - - _mxc_nand_enable_hwecc(mtd, 0); - chip->cmdfunc(mtd, NAND_CMD_READOOB, - mtd->writesize + chip->ecc.prepad, page); - bufpoi = buf + chip->ecc.prepad; - length = mtd->oobsize - chip->ecc.prepad; - for (i = 0; i < chip->ecc.steps; i++) { - toread = min_t(int, length, chip->ecc.bytes); - chip->read_buf(mtd, bufpoi, toread); - bufpoi += eccpitch; - length -= eccpitch; - host->col_addr += chip->ecc.postpad + chip->ecc.prepad; + writew(ops, NFC_V1_V2_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, true); } - _mxc_nand_enable_hwecc(mtd, 1); - return 1; } -static int mxc_nand_read_page_raw_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, - uint8_t *buf, - int oob_required, - int page) +static void send_read_id_v3(struct mxc_nand_host *host) { - struct mxc_nand_host *host = chip->priv; - int eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccpitch = eccbytes + chip->ecc.prepad + chip->ecc.postpad; - uint8_t *oob = chip->oob_poi; - int steps, size; - int n; - - _mxc_nand_enable_hwecc(mtd, 0); - chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); - - for (n = 0, steps = chip->ecc.steps; steps > 0; n++, steps--) { - host->col_addr = n * eccsize; - chip->read_buf(mtd, buf, eccsize); - buf += eccsize; - - host->col_addr = mtd->writesize + n * eccpitch; - if (chip->ecc.prepad) { - chip->read_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; - } - - chip->read_buf(mtd, oob, eccbytes); - oob += eccbytes; + /* Read ID into main buffer */ + writel(NFC_ID, NFC_V3_LAUNCH); - if (chip->ecc.postpad) { - chip->read_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; - } - } + wait_op_done(host, true); - size = mtd->oobsize - (oob - chip->oob_poi); - if (size) - chip->read_buf(mtd, oob, size); - _mxc_nand_enable_hwecc(mtd, 1); + memcpy(host->data_buf, host->main_area0, 16); - return 0; + pr_debug("read ID %02x %02x %02x %02x\n", + host->data_buf[0], host->data_buf[1], + host->data_buf[2], host->data_buf[3]); } -static int mxc_nand_read_page_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, - uint8_t *buf, - int oob_required, - int page) +/* Request the NANDFC to perform a read of the NAND device ID. */ +static void send_read_id_v1_v2(struct mxc_nand_host *host) { - struct mxc_nand_host *host = chip->priv; - int n, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccpitch = eccbytes + chip->ecc.prepad + chip->ecc.postpad; - int eccsteps = chip->ecc.steps; - uint8_t *p = buf; - uint8_t *oob = chip->oob_poi; - - MTDDEBUG(MTD_DEBUG_LEVEL1, "Reading page %u to buf %p oob %p\n", - page, buf, oob); + struct nand_chip *this = &host->nand; - /* first read the data area and the available portion of OOB */ - for (n = 0; eccsteps; n++, eccsteps--, p += eccsize) { - int stat; - - host->col_addr = n * eccsize; - - chip->read_buf(mtd, p, eccsize); - - host->col_addr = mtd->writesize + n * eccpitch; - - if (chip->ecc.prepad) { - chip->read_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; - } - - stat = chip->ecc.correct(mtd, p, oob, NULL); + /* NANDFC buffer 0 is used for device ID output */ + writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); - if (stat < 0) - mtd->ecc_stats.failed++; - else - mtd->ecc_stats.corrected += stat; - oob += eccbytes; + writew(NFC_ID, NFC_V1_V2_CONFIG2); - if (chip->ecc.postpad) { - chip->read_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; - } - } + /* Wait for operation to complete */ + wait_op_done(host, true); - /* Calculate remaining oob bytes */ - n = mtd->oobsize - (oob - chip->oob_poi); - if (n) - chip->read_buf(mtd, oob, n); - - /* Then switch ECC off and read the OOB area to get the ECC code */ - _mxc_nand_enable_hwecc(mtd, 0); - chip->cmdfunc(mtd, NAND_CMD_READOOB, mtd->writesize, page); - eccsteps = chip->ecc.steps; - oob = chip->oob_poi + chip->ecc.prepad; - for (n = 0; eccsteps; n++, eccsteps--, p += eccsize) { - host->col_addr = mtd->writesize + - n * eccpitch + - chip->ecc.prepad; - chip->read_buf(mtd, oob, eccbytes); - oob += eccbytes + chip->ecc.postpad; - } - _mxc_nand_enable_hwecc(mtd, 1); - return 0; -} + memcpy(host->data_buf, host->main_area0, 16); -static int mxc_nand_write_oob_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, int page) -{ - struct mxc_nand_host *host = chip->priv; - int eccpitch = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; - int length = mtd->oobsize; - int i, len, status, steps = chip->ecc.steps; - const uint8_t *bufpoi = chip->oob_poi; - - chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); - for (i = 0; i < steps; i++) { - len = min_t(int, length, eccpitch); - - chip->write_buf(mtd, bufpoi, len); - bufpoi += len; - length -= len; - host->col_addr += chip->ecc.prepad + chip->ecc.postpad; + if (this->options & NAND_BUSWIDTH_16) { + /* compress the ID info */ + host->data_buf[1] = host->data_buf[2]; + host->data_buf[2] = host->data_buf[4]; + host->data_buf[3] = host->data_buf[6]; + host->data_buf[4] = host->data_buf[8]; + host->data_buf[5] = host->data_buf[10]; } - if (length > 0) - chip->write_buf(mtd, bufpoi, length); - - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - status = chip->waitfunc(mtd, chip); - return status & NAND_STATUS_FAIL ? -EIO : 0; } -static int mxc_nand_write_page_raw_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf, - int oob_required) +static uint16_t get_dev_status_v3(struct mxc_nand_host *host) { - struct mxc_nand_host *host = chip->priv; - int eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccpitch = eccbytes + chip->ecc.prepad + chip->ecc.postpad; - uint8_t *oob = chip->oob_poi; - int steps, size; - int n; - - for (n = 0, steps = chip->ecc.steps; steps > 0; n++, steps--) { - host->col_addr = n * eccsize; - chip->write_buf(mtd, buf, eccsize); - buf += eccsize; - - host->col_addr = mtd->writesize + n * eccpitch; - - if (chip->ecc.prepad) { - chip->write_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; - } - - host->col_addr += eccbytes; - oob += eccbytes; - - if (chip->ecc.postpad) { - chip->write_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; - } - } + writel(NFC_STATUS, NFC_V3_LAUNCH); + wait_op_done(host, true); - size = mtd->oobsize - (oob - chip->oob_poi); - if (size) - chip->write_buf(mtd, oob, size); - return 0; + return readl(NFC_V3_CONFIG1) >> 16; } -static int mxc_nand_write_page_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf, - int oob_required) +/* This function requests the NANDFC to perform a read of the + * NAND device status and returns the current status. */ +static uint16_t get_dev_status_v1_v2(struct mxc_nand_host *host) { - struct mxc_nand_host *host = chip->priv; - int i, n, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccpitch = eccbytes + chip->ecc.prepad + chip->ecc.postpad; - int eccsteps = chip->ecc.steps; - const uint8_t *p = buf; - uint8_t *oob = chip->oob_poi; - - chip->ecc.hwctl(mtd, NAND_ECC_WRITE); - - for (i = n = 0; - eccsteps; - n++, eccsteps--, i += eccbytes, p += eccsize) { - host->col_addr = n * eccsize; + void __iomem *main_buf = host->main_area0; + uint32_t store; + uint16_t ret; - chip->write_buf(mtd, p, eccsize); + writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); - host->col_addr = mtd->writesize + n * eccpitch; + /* + * The device status is stored in main_area0. To + * prevent corruption of the buffer save the value + * and restore it afterwards. + */ + store = readl(main_buf); - if (chip->ecc.prepad) { - chip->write_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; - } + writew(NFC_STATUS, NFC_V1_V2_CONFIG2); + wait_op_done(host, true); - chip->write_buf(mtd, oob, eccbytes); - oob += eccbytes; + ret = readw(main_buf); - if (chip->ecc.postpad) { - chip->write_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; - } - } + writel(store, main_buf); - /* Calculate remaining oob bytes */ - i = mtd->oobsize - (oob - chip->oob_poi); - if (i) - chip->write_buf(mtd, oob, i); - return 0; + return ret; } -static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat, - u_char *read_ecc, u_char *calc_ecc) +/* This functions is used by upper layer to checks if device is ready */ +static int mxc_nand_dev_ready(struct mtd_info *mtd) { - struct nand_chip *nand_chip = mtd->priv; - struct mxc_nand_host *host = nand_chip->priv; - uint32_t ecc_status = readl(&host->regs->ecc_status_result); - int subpages = mtd->writesize / nand_chip->subpagesize; - int pg2blk_shift = nand_chip->phys_erase_shift - - nand_chip->page_shift; - - do { - if ((ecc_status & 0xf) > 4) { - static int last_bad = -1; - - if (last_bad != host->page_addr >> pg2blk_shift) { - last_bad = host->page_addr >> pg2blk_shift; - printk(KERN_DEBUG - "MXC_NAND: HWECC uncorrectable ECC error" - " in block %u page %u subpage %d\n", - last_bad, host->page_addr, - mtd->writesize / nand_chip->subpagesize - - subpages); - } - return -1; - } - ecc_status >>= 4; - subpages--; - } while (subpages > 0); + /* + * NFC handles R/B internally. Therefore, this function + * always returns status as ready. + */ + return 1; +} - return 0; +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. + */ } -#else -#define mxc_nand_read_page_syndrome NULL -#define mxc_nand_read_page_raw_syndrome NULL -#define mxc_nand_read_oob_syndrome NULL -#define mxc_nand_write_page_syndrome NULL -#define mxc_nand_write_page_raw_syndrome NULL -#define mxc_nand_write_oob_syndrome NULL - -static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat, + +static int mxc_nand_correct_data_v1(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) { struct nand_chip *nand_chip = mtd->priv; @@ -708,61 +545,72 @@ static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat, * additional correction. 2-Bit errors cannot be corrected by * HW ECC, so we need to return failure */ - uint16_t ecc_status = readnfc(&host->regs->ecc_status_result); + uint16_t ecc_status = readw(NFC_V1_V2_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"); + printk("MXC_NAND: HWECC uncorrectable 2-bit ECC error\n"); return -1; } return 0; } -#endif + +static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + u32 ecc_stat, err; + int no_subpages = 1; + int ret = 0; + u8 ecc_bit_mask, err_limit; + + ecc_bit_mask = (host->eccsize == 4) ? 0x7 : 0xf; + err_limit = (host->eccsize == 4) ? 0x4 : 0x8; + + no_subpages = mtd->writesize >> 9; + + if (nfc_is_v21()) + ecc_stat = readl(NFC_V1_V2_ECC_STATUS_RESULT); + else + ecc_stat = readl(NFC_V3_ECC_STATUS_RESULT); + + 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); + + mtd->ecc_stats.corrected += ret; + if (ret) + pr_debug("%d Symbol Correctable RS-ECC Errors\n", ret); + + return ret; +} static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) { return 0; } -#endif static u_char mxc_nand_read_byte(struct mtd_info *mtd) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; - uint8_t ret = 0; - uint16_t col; - uint16_t __iomem *main_buf = - (uint16_t __iomem *)host->regs->main_area[0]; - uint16_t __iomem *spare_buf = - (uint16_t __iomem *)host->regs->spare_area[0]; - union { - uint16_t word; - uint8_t bytes[2]; - } nfc_word; + uint8_t ret; /* Check for status request */ if (host->status_request) - return get_dev_status(host) & 0xFF; - - /* Get column for 16-bit access */ - col = host->col_addr >> 1; - - /* If we are accessing the spare region */ - if (host->spare_only) - nfc_word.word = readw(&spare_buf[col]); - else - nfc_word.word = readw(&main_buf[col]); + return host->get_dev_status(host) & 0xFF; - /* Pick upper/lower byte of word from RAM buffer */ - ret = nfc_word.bytes[host->col_addr & 0x1]; - - /* Update saved column address */ - if (nand_chip->options & NAND_BUSWIDTH_16) - host->col_addr += 2; - else - host->col_addr++; + ret = *(uint8_t *)(host->data_buf + host->buf_start); + host->buf_start++; return ret; } @@ -771,250 +619,294 @@ static uint16_t mxc_nand_read_word(struct mtd_info *mtd) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; - uint16_t col, ret; - uint16_t __iomem *p; - - MTDDEBUG(MTD_DEBUG_LEVEL3, - "mxc_nand_read_word(col = %d)\n", host->col_addr); - - col = host->col_addr; - /* Adjust saved column address */ - if (col < mtd->writesize && host->spare_only) - col += mtd->writesize; - - if (col < mtd->writesize) { - p = (uint16_t __iomem *)(host->regs->main_area[0] + - (col >> 1)); - } else { - p = (uint16_t __iomem *)(host->regs->spare_area[0] + - ((col - mtd->writesize) >> 1)); - } + uint16_t ret; - if (col & 1) { - union { - uint16_t word; - uint8_t bytes[2]; - } nfc_word[3]; - - nfc_word[0].word = readw(p); - nfc_word[1].word = readw(p + 1); - - nfc_word[2].bytes[0] = nfc_word[0].bytes[1]; - nfc_word[2].bytes[1] = nfc_word[1].bytes[0]; - - ret = nfc_word[2].word; - } else { - ret = readw(p); - } - - /* Update saved column address */ - host->col_addr = col + 2; + ret = *(uint16_t *)(host->data_buf + host->buf_start); + host->buf_start += 2; return ret; } -/* - * Write data of length len to buffer buf. The data to be +/* Write data of length len to buffer 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 - */ + * Operation by the NFC, the data is written to NAND Flash */ static void mxc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; - int n, col, i = 0; + u16 col = host->buf_start; + int n = mtd->oobsize + mtd->writesize - col; - MTDDEBUG(MTD_DEBUG_LEVEL3, - "mxc_nand_write_buf(col = %d, len = %d)\n", host->col_addr, - len); + n = min(n, len); - col = host->col_addr; + memcpy(host->data_buf + col, buf, n); - /* Adjust saved column address */ - if (col < mtd->writesize && host->spare_only) - col += mtd->writesize; + host->buf_start += n; +} - n = mtd->writesize + mtd->oobsize - col; - n = min(len, n); +/* 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 len is then copied to buffer buf. + */ +static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + u16 col = host->buf_start; + int n = mtd->oobsize + mtd->writesize - col; - MTDDEBUG(MTD_DEBUG_LEVEL3, - "%s:%d: col = %d, n = %d\n", __func__, __LINE__, col, n); + n = min(n, len); - while (n > 0) { - void __iomem *p; + memcpy(buf, host->data_buf + col, n); - if (col < mtd->writesize) { - p = host->regs->main_area[0] + (col & ~3); - } else { - p = host->regs->spare_area[0] - - mtd->writesize + (col & ~3); - } + host->buf_start += n; +} - MTDDEBUG(MTD_DEBUG_LEVEL3, "%s:%d: p = %p\n", __func__, - __LINE__, p); +#if defined(__UBOOT__) && defined(CONFIG_MTD_NAND_VERIFY_WRITE) +/* Used by the upper layer to verify the data in NAND Flash + * with the data in the buf. */ +static int mxc_nand_verify_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + return -EFAULT; +} +#endif - if (((col | (unsigned long)&buf[i]) & 3) || n < 4) { - union { - uint32_t word; - uint8_t bytes[4]; - } nfc_word; +/* This function is used by upper layer for select and + * deselect of the NAND chip */ +static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; - nfc_word.word = readl(p); - nfc_word.bytes[col & 3] = buf[i++]; - n--; - col++; + if (nfc_is_v21()) { + host->active_cs = chip; + writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); + } +} - writel(nfc_word.word, p); - } else { - int m = mtd->writesize - col; +/* + * Function to transfer data to/from spare area. + */ +static void copy_spare(struct mtd_info *mtd, bool bfrom) +{ + struct nand_chip *this = mtd->priv; + struct mxc_nand_host *host = this->priv; + u16 i, j; + u16 n = mtd->writesize >> 9; + u8 *d = host->data_buf + mtd->writesize; + u8 *s = host->spare0; + u16 t = host->spare_len; + + j = (mtd->oobsize / n >> 1) << 1; + + if (bfrom) { + for (i = 0; i < n - 1; i++) + memcpy(d + i * j, s + i * t, j); + + /* the last section */ + memcpy(d + i * j, s + i * t, mtd->oobsize - i * j); + } else { + for (i = 0; i < n - 1; i++) + memcpy(&s[i * t], &d[i * j], j); - if (col >= mtd->writesize) - m += mtd->oobsize; + /* the last section */ + memcpy(&s[i * t], &d[i * j], mtd->oobsize - i * j); + } +} - m = min(n, m) & ~3; +static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; - MTDDEBUG(MTD_DEBUG_LEVEL3, - "%s:%d: n = %d, m = %d, i = %d, col = %d\n", - __func__, __LINE__, n, m, i, col); + /* 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 + * perform a read/write buf operation, the saved column + * address is used to index into the full page. + */ + host->send_addr(host, 0, page_addr == -1); + if (mtd->writesize > 512) + /* another col addr cycle for 2k page */ + host->send_addr(host, 0, false); + } - mxc_nand_memcpy32(p, (uint32_t *)&buf[i], m); - col += m; - i += m; - n -= m; + /* Write out page address, if necessary */ + if (page_addr != -1) { + /* paddr_0 - p_addr_7 */ + host->send_addr(host, (page_addr & 0xff), false); + + if (mtd->writesize > 512) { + if (mtd->size >= 0x10000000) { + /* paddr_8 - paddr_15 */ + host->send_addr(host, (page_addr >> 8) & 0xff, false); + host->send_addr(host, (page_addr >> 16) & 0xff, true); + } else + /* paddr_8 - paddr_15 */ + host->send_addr(host, (page_addr >> 8) & 0xff, true); + } else { + /* One more address cycle for higher density devices */ + if (mtd->size >= 0x4000000) { + /* paddr_8 - paddr_15 */ + host->send_addr(host, (page_addr >> 8) & 0xff, false); + host->send_addr(host, (page_addr >> 16) & 0xff, true); + } else + /* paddr_8 - paddr_15 */ + host->send_addr(host, (page_addr >> 8) & 0xff, true); } } - /* Update saved column address */ - host->col_addr = col; } /* - * 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 len is then copied to buffer buf. + * v2 and v3 type controllers can do 4bit or 8bit ecc depending + * on how much oob the nand chip has. For 8bit ecc we need at least + * 26 bytes of oob data per 512 byte block. */ -static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +static int get_eccsize(struct mtd_info *mtd) { - struct nand_chip *nand_chip = mtd->priv; - struct mxc_nand_host *host = nand_chip->priv; - int n, col, i = 0; + int oobbytes_per_512 = 0; - MTDDEBUG(MTD_DEBUG_LEVEL3, - "mxc_nand_read_buf(col = %d, len = %d)\n", host->col_addr, len); + oobbytes_per_512 = mtd->oobsize * 512 / mtd->writesize; - col = host->col_addr; + if (oobbytes_per_512 < 26) + return 4; + else + return 8; +} + +static void preset_v1_v2(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + uint16_t config1 = 0; - /* Adjust saved column address */ - if (col < mtd->writesize && host->spare_only) - col += mtd->writesize; + if (nand_chip->ecc.mode == NAND_ECC_HW) + config1 |= NFC_V1_V2_CONFIG1_ECC_EN; - n = mtd->writesize + mtd->oobsize - col; - n = min(len, n); + if (nfc_is_v21()) + config1 |= NFC_V2_CONFIG1_FP_INT; - while (n > 0) { - void __iomem *p; + if (nfc_is_v21() && mtd->writesize) { + uint16_t pages_per_block = mtd->erasesize / mtd->writesize; - if (col < mtd->writesize) { - p = host->regs->main_area[0] + (col & ~3); - } else { - p = host->regs->spare_area[0] - - mtd->writesize + (col & ~3); - } + host->eccsize = get_eccsize(mtd); + if (host->eccsize == 4) + config1 |= NFC_V2_CONFIG1_ECC_MODE_4; - if (((col | (int)&buf[i]) & 3) || n < 4) { - union { - uint32_t word; - uint8_t bytes[4]; - } nfc_word; + config1 |= NFC_V2_CONFIG1_PPB(ffs(pages_per_block) - 6); + } else { + host->eccsize = 1; + } - nfc_word.word = readl(p); - buf[i++] = nfc_word.bytes[col & 3]; - n--; - col++; - } else { - int m = mtd->writesize - col; + writew(config1, NFC_V1_V2_CONFIG1); + /* preset operation */ - if (col >= mtd->writesize) - m += mtd->oobsize; + /* Unlock the internal RAM Buffer */ + writew(0x2, NFC_V1_V2_CONFIG); - m = min(n, m) & ~3; - mxc_nand_memcpy32((uint32_t *)&buf[i], p, m); + /* Blocks to be unlocked */ + if (nfc_is_v21()) { + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR0); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR1); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR2); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR3); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR0); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR1); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR2); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR3); + } else if (nfc_is_v1()) { + writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR); + writew(0x4000, NFC_V1_UNLOCKEND_BLKADDR); + } else + BUG(); - col += m; - i += m; - n -= m; - } - } - /* Update saved column address */ - host->col_addr = col; + /* Unlock Block Command for given address range */ + writew(0x4, NFC_V1_V2_WRPROT); } -#ifdef __UBOOT__ -#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) -/* - * Used by the upper layer to verify the data in NAND Flash - * with the data in the buf. - */ -static int mxc_nand_verify_buf(struct mtd_info *mtd, - const u_char *buf, int len) +static void preset_v3(struct mtd_info *mtd) { - u_char tmp[256]; - uint bsize; + struct nand_chip *chip = mtd->priv; + struct mxc_nand_host *host = chip->priv; + uint32_t config2, config3; + int i, addr_phases; - while (len) { - bsize = min(len, 256); - mxc_nand_read_buf(mtd, tmp, bsize); + writel(NFC_V3_CONFIG1_RBA(0), NFC_V3_CONFIG1); + writel(NFC_V3_IPC_CREQ, NFC_V3_IPC); + WARN_ON(!(readl(NFC_V3_IPC) & NFC_V3_IPC_CACK)); - if (memcmp(buf, tmp, bsize)) - return 1; + /* Unlock the internal RAM Buffer */ + writel(NFC_V3_WRPROT_BLS_UNLOCK | NFC_V3_WRPROT_UNLOCK, + NFC_V3_WRPROT); - buf += bsize; - len -= bsize; + /* Blocks to be unlocked */ + for (i = 0; i < CONFIG_SYS_NAND_MAX_CHIPS; i++) + writel(0x0 | (0xffff << 16), + NFC_V3_WRPROT_UNLOCK_BLK_ADD0 + (i << 2)); + + config2 = NFC_V3_CONFIG2_ONE_CYCLE | + NFC_V3_CONFIG2_2CMD_PHASES | + NFC_V3_CONFIG2_SPAS(mtd->oobsize >> 1) | + NFC_V3_CONFIG2_ST_CMD(0x70) | + NFC_V3_CONFIG2_NUM_ADDR_PHASE0; + + if (chip->ecc.mode == NAND_ECC_HW) + config2 |= NFC_V3_CONFIG2_ECC_EN; + + addr_phases = fls(chip->pagemask) >> 3; + + if (mtd->writesize == 2048) { + config2 |= NFC_V3_CONFIG2_PS_2048; + config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases); + } else if (mtd->writesize == 4096) { + config2 |= NFC_V3_CONFIG2_PS_4096; + config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases); + } else { + config2 |= NFC_V3_CONFIG2_PS_512; + config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases - 1); } - return 0; -} -#endif + if (mtd->writesize) { +#if defined CONFIG_MX53 + config2 |= MX53_CONFIG2_PPB(ffs(mtd->erasesize / mtd->writesize) - 6); +#else + config2 |= NFC_V3_CONFIG2_PPB(ffs(mtd->erasesize / mtd->writesize) - 6); #endif + host->eccsize = get_eccsize(mtd); + if (host->eccsize == 8) + config2 |= NFC_V3_CONFIG2_ECC_MODE_8; + } -/* - * This function is used by upper layer for select and - * deselect of the NAND chip - */ -static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) -{ - struct nand_chip *nand_chip = mtd->priv; - struct mxc_nand_host *host = nand_chip->priv; + writel(config2, NFC_V3_CONFIG2); - switch (chip) { - case -1: - /* TODO: Disable the NFC clock */ - if (host->clk_act) - host->clk_act = 0; - break; - case 0: - /* TODO: Enable the NFC clock */ - if (!host->clk_act) - host->clk_act = 1; - break; + config3 = NFC_V3_CONFIG3_NUM_OF_DEVICES(0) | + NFC_V3_CONFIG3_NO_SDMA | + NFC_V3_CONFIG3_RBB_MODE | + NFC_V3_CONFIG3_SBB(6) | /* Reset default */ + NFC_V3_CONFIG3_ADD_OP(0); - default: - break; - } + if (!(chip->options & NAND_BUSWIDTH_16)) + config3 |= NFC_V3_CONFIG3_FW8; + + writel(config3, NFC_V3_CONFIG3); + + writel(0, NFC_V3_DELAY_LINE); + writel(0, NFC_V3_IPC); } -/* - * Used by the upper layer to write command to NAND Flash for - * different operations to be carried out on NAND Flash - */ -void mxc_nand_command(struct mtd_info *mtd, unsigned command, +/* Used by the upper layer to write command to NAND Flash for + * different operations to be carried out on NAND Flash */ +static void mxc_nand_command(struct mtd_info *mtd, unsigned command, int column, int page_addr) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; - MTDDEBUG(MTD_DEBUG_LEVEL3, - "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", + pr_debug("mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", command, column, page_addr); /* Reset command state information */ @@ -1022,144 +914,86 @@ void mxc_nand_command(struct mtd_info *mtd, unsigned command, /* Command pre-processing step */ switch (command) { + case NAND_CMD_RESET: + host->preset(mtd); + host->send_cmd(host, command, false); + break; case NAND_CMD_STATUS: - host->col_addr = 0; + host->buf_start = 0; host->status_request = true; - break; - case NAND_CMD_READ0: - host->page_addr = page_addr; - host->col_addr = column; - host->spare_only = false; + host->send_cmd(host, command, true); + mxc_do_addr_cycle(mtd, column, page_addr); break; + case NAND_CMD_READ0: case NAND_CMD_READOOB: - host->col_addr = column; - host->spare_only = true; - if (host->pagesize_2k) - command = NAND_CMD_READ0; /* only READ0 is valid */ - break; + if (command == NAND_CMD_READ0) + host->buf_start = column; + else + host->buf_start = column + mtd->writesize; - case NAND_CMD_SEQIN: - if (column >= mtd->writesize) { - /* - * before sending SEQIN command for partial write, - * we need read one page out. FSL NFC does not support - * partial write. It always sends out 512+ecc+512+ecc - * for large page nand flash. But for small page nand - * flash, it does support SPARE ONLY operation. - */ - if (host->pagesize_2k) { - /* call ourself to read a page */ - mxc_nand_command(mtd, NAND_CMD_READ0, 0, - page_addr); - } - - host->col_addr = column - mtd->writesize; - host->spare_only = true; - - /* Set program pointer to spare region */ - if (!host->pagesize_2k) - send_cmd(host, NAND_CMD_READOOB); - } else { - host->spare_only = false; - host->col_addr = column; + command = NAND_CMD_READ0; /* only READ0 is valid */ - /* Set program pointer to page start */ - if (!host->pagesize_2k) - send_cmd(host, NAND_CMD_READ0); - } - break; + host->send_cmd(host, command, false); + mxc_do_addr_cycle(mtd, column, page_addr); - case NAND_CMD_PAGEPROG: - send_prog_page(host, 0, host->spare_only); + if (mtd->writesize > 512) + host->send_cmd(host, NAND_CMD_READSTART, true); - if (host->pagesize_2k && is_mxc_nfc_1()) { - /* data in 4 areas */ - send_prog_page(host, 1, host->spare_only); - send_prog_page(host, 2, host->spare_only); - send_prog_page(host, 3, host->spare_only); - } + host->send_page(mtd, NFC_OUTPUT); + memcpy(host->data_buf, host->main_area0, mtd->writesize); + copy_spare(mtd, true); break; - } - - /* Write out the command to the device. */ - send_cmd(host, 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 perform - * a read/write buffer operation, we will use the saved - * column address to index into the full page. - */ - send_addr(host, 0); - if (host->pagesize_2k) - /* another col addr cycle for 2k page */ - send_addr(host, 0); - } - /* Write out page address, if necessary */ - if (page_addr != -1) { - u32 page_mask = nand_chip->pagemask; - do { - send_addr(host, page_addr & 0xFF); - page_addr >>= 8; - page_mask >>= 8; - } while (page_mask); - } - - /* Command post-processing step */ - switch (command) { - - case NAND_CMD_RESET: - break; + case NAND_CMD_SEQIN: + if (column >= mtd->writesize) + /* call ourself to read a page */ + mxc_nand_command(mtd, NAND_CMD_READ0, 0, page_addr); - case NAND_CMD_READOOB: - case NAND_CMD_READ0: - if (host->pagesize_2k) { - /* send read confirm command */ - send_cmd(host, NAND_CMD_READSTART); - /* read for each AREA */ - send_read_page(host, 0, host->spare_only); - if (is_mxc_nfc_1()) { - send_read_page(host, 1, host->spare_only); - send_read_page(host, 2, host->spare_only); - send_read_page(host, 3, host->spare_only); - } - } else { - send_read_page(host, 0, host->spare_only); - } - break; + host->buf_start = column; - case NAND_CMD_READID: - host->col_addr = 0; - send_read_id(host); + host->send_cmd(host, command, false); + mxc_do_addr_cycle(mtd, column, page_addr); break; case NAND_CMD_PAGEPROG: + memcpy(host->main_area0, host->data_buf, mtd->writesize); + copy_spare(mtd, false); + host->send_page(mtd, NFC_INPUT); + host->send_cmd(host, command, true); + mxc_do_addr_cycle(mtd, column, page_addr); break; - case NAND_CMD_STATUS: + case NAND_CMD_READID: + host->send_cmd(host, command, true); + mxc_do_addr_cycle(mtd, column, page_addr); + host->send_read_id(host); + host->buf_start = column; break; + case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: + host->send_cmd(host, command, false); + mxc_do_addr_cycle(mtd, column, page_addr); + break; } } -#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT - -static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; -static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; +/* + * The generic flash bbt decriptors overlap with our ecc + * hardware, so define some i.MX specific ones. + */ +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, + .options = NAND_BBT_LASTBLOCK | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 0, .len = 4, .veroffs = 4, .maxblocks = 4, @@ -1167,36 +1001,36 @@ static struct nand_bbt_descr bbt_main_descr = { }; 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, + .options = NAND_BBT_LASTBLOCK | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 0, .len = 4, .veroffs = 4, .maxblocks = 4, .pattern = mirror_pattern, }; -#endif - -int board_nand_init(struct nand_chip *this) +static void mxc_nand_chip_init(int devno) { - struct mtd_info *mtd; -#if defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2) - uint32_t tmp; -#endif - -#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT - this->bbt_options |= NAND_BBT_USE_FLASH; - this->bbt_td = &bbt_main_descr; - this->bbt_md = &bbt_mirror_descr; -#endif + int err; + struct nand_chip *this; + struct mtd_info *mtd = &nand_info[devno]; + struct nand_ecclayout *oob_smallpage, *oob_largepage; + + /* allocate a minimal buffer for the read_id command */ + host->data_buf = malloc(16); + if (!host->data_buf) { + printk("Failed to allocate ID buffer\n"); + return; + } /* structures must be linked */ - mtd = &host->mtd; + this = &host->nand; +// host->mtd = mtd; mtd->priv = this; - host->nand = this; + mtd->name = DRIVER_NAME; - /* 5 us command delay time */ + /* 50 us command delay time */ this->chip_delay = 5; this->priv = host; @@ -1207,144 +1041,124 @@ int board_nand_init(struct nand_chip *this) this->read_word = mxc_nand_read_word; this->write_buf = mxc_nand_write_buf; this->read_buf = mxc_nand_read_buf; -#ifdef __UBOOT__ -#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) +#if defined(__UBOOT__) && defined(CONFIG_MTD_NAND_VERIFY_WRITE) this->verify_buf = mxc_nand_verify_buf; #endif -#endif + host->base = (void __iomem *)CONFIG_MXC_NAND_REGS_BASE; + if (!host->base) { + return; + } - host->regs = (struct mxc_nand_regs __iomem *)CONFIG_MXC_NAND_REGS_BASE; -#ifdef MXC_NFC_V3_2 - host->ip_regs = - (struct mxc_nand_ip_regs __iomem *)CONFIG_MXC_NAND_IP_REGS_BASE; -#endif - host->clk_act = 1; + host->main_area0 = host->base; -#ifdef CONFIG_MXC_NAND_HWECC - this->ecc.calculate = mxc_nand_calculate_ecc; - this->ecc.hwctl = mxc_nand_enable_hwecc; - this->ecc.correct = mxc_nand_correct_data; - if (is_mxc_nfc_21() || is_mxc_nfc_32()) { - this->ecc.mode = NAND_ECC_HW_SYNDROME; - this->ecc.read_page = mxc_nand_read_page_syndrome; - this->ecc.read_page_raw = mxc_nand_read_page_raw_syndrome; - this->ecc.read_oob = mxc_nand_read_oob_syndrome; - this->ecc.write_page = mxc_nand_write_page_syndrome; - this->ecc.write_page_raw = mxc_nand_write_page_raw_syndrome; - this->ecc.write_oob = mxc_nand_write_oob_syndrome; - this->ecc.bytes = 9; - this->ecc.prepad = 7; - } else { - this->ecc.mode = NAND_ECC_HW; + if (nfc_is_v1() || nfc_is_v21()) { + host->preset = preset_v1_v2; + host->send_cmd = send_cmd_v1_v2; + host->send_addr = send_addr_v1_v2; + host->send_page = send_page_v1_v2; + host->send_read_id = send_read_id_v1_v2; + host->get_dev_status = get_dev_status_v1_v2; + host->check_int = check_int_v1_v2; } - if (is_mxc_nfc_1()) - this->ecc.strength = 1; - else + if (nfc_is_v21()) { + host->regs = host->base + 0x1e00; + host->spare0 = host->base + 0x1000; + host->spare_len = 64; + oob_smallpage = &nandv2_hw_eccoob_smallpage; + oob_largepage = &nandv2_hw_eccoob_largepage; + this->ecc.bytes = 9; + } else if (nfc_is_v1()) { + host->regs = host->base + 0xe00; + host->spare0 = host->base + 0x800; + host->spare_len = 16; + oob_smallpage = &nandv1_hw_eccoob_smallpage; + oob_largepage = &nandv1_hw_eccoob_largepage; + this->ecc.bytes = 3; + host->eccsize = 1; + } else if (nfc_is_v3_2()) { + host->regs_ip = (void __iomem *)CONFIG_MXC_NAND_IP_REGS_BASE; + host->regs_axi = host->base + 0x1e00; + host->spare0 = host->base + 0x1000; + host->spare_len = 64; + host->preset = preset_v3; + host->send_cmd = send_cmd_v3; + host->send_addr = send_addr_v3; + host->send_page = send_page_v3; + host->send_read_id = send_read_id_v3; + host->check_int = check_int_v3; + host->get_dev_status = get_dev_status_v3; + oob_smallpage = &nandv2_hw_eccoob_smallpage; + oob_largepage = &nandv2_hw_eccoob_largepage; this->ecc.strength = 4; - - host->pagesize_2k = 0; + } else + hang(); this->ecc.size = 512; - _mxc_nand_enable_hwecc(mtd, 1); + this->ecc.layout = oob_smallpage; + +#ifdef CONFIG_MXC_NAND_HWECC + this->ecc.calculate = mxc_nand_calculate_ecc; + this->ecc.hwctl = mxc_nand_enable_hwecc; + if (nfc_is_v1()) + this->ecc.correct = mxc_nand_correct_data_v1; + else + this->ecc.correct = mxc_nand_correct_data_v2_v3; + this->ecc.mode = NAND_ECC_HW; #else - this->ecc.layout = &nand_soft_eccoob; this->ecc.mode = NAND_ECC_SOFT; - _mxc_nand_enable_hwecc(mtd, 0); #endif - /* Reset NAND */ - this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); - - /* NAND bus width determines access functions used by upper layer */ + /* NAND bus width determines access funtions used by upper layer */ if (is_16bit_nand()) this->options |= NAND_BUSWIDTH_16; -#ifdef CONFIG_SYS_NAND_LARGEPAGE - host->pagesize_2k = 1; - this->ecc.layout = &nand_hw_eccoob2k; -#else - host->pagesize_2k = 0; - this->ecc.layout = &nand_hw_eccoob; -#endif - -#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1) -#ifdef MXC_NFC_V2_1 - tmp = readnfc(&host->regs->config1); - tmp |= NFC_V2_CONFIG1_ONE_CYCLE; - tmp |= NFC_V2_CONFIG1_ECC_MODE_4; - writenfc(tmp, &host->regs->config1); - if (host->pagesize_2k) - writenfc(64/2, &host->regs->spare_area_size); - else - writenfc(16/2, &host->regs->spare_area_size); +#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT + this->bbt_options |= NAND_BBT_USE_FLASH; + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; + this->bbt_td->options |= NAND_BBT_CREATE; + this->bbt_md->options |= NAND_BBT_CREATE; #endif - /* - * preset operation - * Unlock the internal RAM Buffer - */ - writenfc(0x2, &host->regs->config); - - /* Blocks to be unlocked */ - writenfc(0x0, &host->regs->unlockstart_blkaddr); - /* Originally (Freescale LTIB 2.6.21) 0x4000 was written to the - * unlockend_blkaddr, but the magic 0x4000 does not always work - * when writing more than some 32 megabytes (on 2k page nands) - * However 0xFFFF doesn't seem to have this kind - * of limitation (tried it back and forth several times). - * The linux kernel driver sets this to 0xFFFF for the v2 controller - * only, but probably this was not tested there for v1. - * The very same limitation seems to apply to this kernel driver. - * This might be NAND chip specific and the i.MX31 datasheet is - * extremely vague about the semantics of this register. - */ - writenfc(0xFFFF, &host->regs->unlockend_blkaddr); - - /* Unlock Block Command for given address range */ - writenfc(0x4, &host->regs->wrprot); -#elif defined(MXC_NFC_V3_2) - writenfc(NFC_V3_CONFIG1_RBA(0), &host->regs->config1); - writenfc(NFC_V3_IPC_CREQ, &host->ip_regs->ipc); - - /* Unlock the internal RAM Buffer */ - writenfc(NFC_V3_WRPROT_BLS_UNLOCK | NFC_V3_WRPROT_UNLOCK, - &host->ip_regs->wrprot); - - /* Blocks to be unlocked */ - for (tmp = 0; tmp < CONFIG_SYS_NAND_MAX_CHIPS; tmp++) - writenfc(0x0 | 0xFFFF << 16, - &host->ip_regs->wrprot_unlock_blkaddr[tmp]); - - writenfc(0, &host->ip_regs->ipc); - - tmp = readnfc(&host->ip_regs->config2); - tmp &= ~(NFC_V3_CONFIG2_SPAS_MASK | NFC_V3_CONFIG2_EDC_MASK | - NFC_V3_CONFIG2_ECC_MODE_8 | NFC_V3_CONFIG2_PS_MASK); - tmp |= NFC_V3_CONFIG2_ONE_CYCLE; - - if (host->pagesize_2k) { - tmp |= NFC_V3_CONFIG2_SPAS(64/2); - tmp |= NFC_V3_CONFIG2_PS_2048; - } else { - tmp |= NFC_V3_CONFIG2_SPAS(16/2); - tmp |= NFC_V3_CONFIG2_PS_512; + /* first scan to find the device and get the page size */ + if (nand_scan_ident(mtd, nfc_is_v21() ? 4 : 1, NULL)) { + return; } - writenfc(tmp, &host->ip_regs->config2); - - tmp = NFC_V3_CONFIG3_NUM_OF_DEVS(0) | - NFC_V3_CONFIG3_NO_SDMA | - NFC_V3_CONFIG3_RBB_MODE | - NFC_V3_CONFIG3_SBB(6) | /* Reset default */ - NFC_V3_CONFIG3_ADD_OP(0); - - if (!(this->options & NAND_BUSWIDTH_16)) - tmp |= NFC_V3_CONFIG3_FW8; + host->data_buf = realloc(host->data_buf, + mtd->writesize + mtd->oobsize); + if (!host->data_buf) { + printk("Failed to allocate data buffer of %u byte\n", + mtd->writesize + mtd->oobsize); + return; + } + pr_debug("Allocated %u byte data buffer\n", + mtd->writesize + mtd->oobsize); + + /* Call preset again, with correct writesize this time */ + host->preset(mtd); + + if (mtd->writesize == 2048) + this->ecc.layout = oob_largepage; + if (nfc_is_v21() && mtd->writesize == 4096) + this->ecc.layout = &nandv2_hw_eccoob_4k; + + /* second phase scan */ + err = nand_scan_tail(mtd); + if (err) { + printk("Nand scan failed: %d\n", err); + return; + } - writenfc(tmp, &host->ip_regs->config3); + err = nand_register(devno); + if (err) + return; +} - writenfc(0, &host->ip_regs->delay_line); -#endif +void board_nand_init(void) +{ + int i; - return 0; + for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) + mxc_nand_chip_init(i); } -- 2.39.2