diff options
-rw-r--r-- | drivers/mtd/nand/nand_imx.c | 110 |
1 files changed, 71 insertions, 39 deletions
diff --git a/drivers/mtd/nand/nand_imx.c b/drivers/mtd/nand/nand_imx.c index 214f391c53..b3191a7516 100644 --- a/drivers/mtd/nand/nand_imx.c +++ b/drivers/mtd/nand/nand_imx.c @@ -64,6 +64,8 @@ #define NFC_V1_V2_CONFIG1_RST (1 << 6) #define NFC_V1_V2_CONFIG1_CE (1 << 7) #define NFC_V1_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) @@ -640,6 +642,67 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) } /* + * 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 int get_eccsize(struct mtd_info *mtd) +{ + int oobbytes_per_512 = 0; + + oobbytes_per_512 = mtd->oobsize * 512 / mtd->writesize; + + 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 imx_nand_host *host = nand_chip->priv; + uint16_t config1 = 0; + + if (nand_chip->ecc.mode == NAND_ECC_HW) + config1 |= NFC_V1_V2_CONFIG1_ECC_EN; + + if (nfc_is_v21()) + config1 |= NFC_V2_CONFIG1_FP_INT; + + if (nfc_is_v21() && mtd->writesize) { + uint16_t pages_per_block = mtd->erasesize / mtd->writesize; + + host->eccsize = get_eccsize(mtd); + if (host->eccsize == 4) + config1 |= NFC_V2_CONFIG1_ECC_MODE_4; + + config1 |= NFC_V2_CONFIG1_PPB(ffs(pages_per_block) - 6); + } else { + host->eccsize = 1; + } + + writew(config1, host->regs + NFC_V1_V2_CONFIG1); + /* preset operation */ + + /* Unlock the internal RAM Buffer */ + writew(0x2, host->regs + NFC_V1_V2_CONFIG); + + /* Blocks to be unlocked */ + if (nfc_is_v21()) { + writew(0x0, host->regs + NFC_V21_UNLOCKSTART_BLKADDR); + writew(0xffff, host->regs + NFC_V21_UNLOCKEND_BLKADDR); + } else if (nfc_is_v1()) { + writew(0x0, host->regs + NFC_V1_UNLOCKSTART_BLKADDR); + writew(0x4000, host->regs + NFC_V1_UNLOCKEND_BLKADDR); + } else + BUG(); + + /* Unlock Block Command for given address range */ + writew(0x4, host->regs + NFC_V1_V2_WRPROT); +} + +/* * This function is used by the upper layer to write command to NAND Flash for * different operations to be carried out on NAND Flash * @@ -667,6 +730,10 @@ static void imx_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); + break; case NAND_CMD_STATUS: host->buf_start = 0; @@ -745,7 +812,6 @@ static void imx_nand_command(struct mtd_info *mtd, unsigned command, case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: - case NAND_CMD_RESET: host->send_cmd(host, command); mxc_do_addr_cycle(mtd, column, page_addr); break; @@ -821,7 +887,6 @@ static int __init imxnd_probe(struct device_d *dev) struct imx_nand_platform_data *pdata = dev->platform_data; struct imx_nand_host *host; struct nand_ecclayout *oob_smallpage, *oob_largepage; - u16 tmp; int err = 0; #ifdef CONFIG_ARCH_IMX27 @@ -842,6 +907,7 @@ static int __init imxnd_probe(struct device_d *dev) host->main_area0 = host->base; 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; @@ -853,14 +919,12 @@ static int __init imxnd_probe(struct device_d *dev) host->regs = host->base + 0x1e00; host->spare0 = host->base + 0x1000; host->spare_len = 64; - host->eccsize = 1; oob_smallpage = &nandv2_hw_eccoob_smallpage; oob_largepage = &nandv2_hw_eccoob_largepage; } else if (nfc_is_v1()) { host->regs = host->base + 0xe00; host->spare0 = host->base + 0x800; host->spare_len = 16; - host->eccsize = 4; oob_smallpage = &nandv1_hw_eccoob_smallpage; oob_largepage = &nandv1_hw_eccoob_largepage; } @@ -891,14 +955,6 @@ static int __init imxnd_probe(struct device_d *dev) clk_enable(host->clk); #endif - tmp = readw(host->regs + NFC_V1_V2_CONFIG1); - tmp |= NFC_V1_V2_CONFIG1_INT_MSK; - tmp &= ~NFC_V1_V2_CONFIG1_SP_EN; - if (nfc_is_v21()) - /* currently no support for 218 byte OOB with stronger ECC */ - tmp |= NFC_V2_CONFIG1_ECC_MODE_4; - writew(tmp, host->regs + NFC_V1_V2_CONFIG1); - if (pdata->hw_ecc) { this->ecc.calculate = imx_nand_calculate_ecc; this->ecc.hwctl = imx_nand_enable_hwecc; @@ -908,38 +964,11 @@ static int __init imxnd_probe(struct device_d *dev) this->ecc.correct = imx_nand_correct_data_v2_v3; this->ecc.mode = NAND_ECC_HW; this->ecc.size = 512; - tmp = readw(host->regs + NFC_V1_V2_CONFIG1); - tmp |= NFC_V1_V2_CONFIG1_ECC_EN; - writew(tmp, host->regs + NFC_V1_V2_CONFIG1); } else { this->ecc.size = 512; this->ecc.mode = NAND_ECC_SOFT; - tmp = readw(host->regs + NFC_V1_V2_CONFIG1); - tmp &= ~NFC_V1_V2_CONFIG1_ECC_EN; - writew(tmp, host->regs + NFC_V1_V2_CONFIG1); } - /* Reset NAND */ - this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); - - /* preset operation */ - /* Unlock the internal RAM Buffer */ - writew(0x2, host->regs + NFC_V1_V2_CONFIG); - - /* Blocks to be unlocked */ - if (nfc_is_v21()) { - writew(0x0, host->regs + NFC_V21_UNLOCKSTART_BLKADDR); - writew(0xffff, host->regs + NFC_V21_UNLOCKEND_BLKADDR); - this->ecc.bytes = 9; - } else if (nfc_is_v1()) { - writew(0x0, host->regs + NFC_V1_UNLOCKSTART_BLKADDR); - writew(0x4000, host->regs + NFC_V1_UNLOCKEND_BLKADDR); - this->ecc.bytes = 3; - } - - /* Unlock Block Command for given address range */ - writew(0x4, host->regs + NFC_V1_V2_WRPROT); - this->ecc.layout = oob_smallpage; /* NAND bus width determines access funtions used by upper layer */ @@ -962,6 +991,9 @@ static int __init imxnd_probe(struct device_d *dev) goto escan; } + /* Call preset again, with correct writesize this time */ + host->preset(mtd); + imx_nand_set_layout(mtd->writesize, pdata->width == 2 ? 16 : 8); if (mtd->writesize == 2048) { |