diff options
Diffstat (limited to 'drivers/mtd/nand/nand_imx.c')
-rw-r--r-- | drivers/mtd/nand/nand_imx.c | 139 |
1 files changed, 111 insertions, 28 deletions
diff --git a/drivers/mtd/nand/nand_imx.c b/drivers/mtd/nand/nand_imx.c index 590d7fe82a..f54fe21f7d 100644 --- a/drivers/mtd/nand/nand_imx.c +++ b/drivers/mtd/nand/nand_imx.c @@ -114,6 +114,8 @@ struct imx_nand_host { void (*send_read_param)(struct imx_nand_host *); uint16_t (*get_dev_status)(struct imx_nand_host *); int (*check_int)(struct imx_nand_host *); + int (*correct)(struct mtd_info *mtd); + void (*enable_hwecc)(struct nand_chip *, bool enable); }; /* @@ -485,16 +487,44 @@ static int imx_nand_dev_ready(struct mtd_info *mtd) return 1; } -static void imx_nand_enable_hwecc(struct mtd_info *mtd, int mode) +static void imx_nand_enable_hwecc_v1_v2(struct nand_chip *chip, bool enable) { - /* - * If HW ECC is enabled, we turn it on during init. There is - * no need to enable again here. - */ + struct imx_nand_host *host = chip->priv; + uint16_t config1; + + if (chip->ecc.mode != NAND_ECC_HW) + return; + + config1 = readw(host->regs + NFC_V1_V2_CONFIG1); + + if (enable) + config1 |= NFC_V1_V2_CONFIG1_ECC_EN; + else + config1 &= ~NFC_V1_V2_CONFIG1_ECC_EN; + + writew(config1, host->regs + NFC_V1_V2_CONFIG1); + } -static int imx_nand_correct_data_v1(struct mtd_info *mtd, u_char * dat, - u_char * read_ecc, u_char * calc_ecc) +static void imx_nand_enable_hwecc_v3(struct nand_chip *chip, bool enable) +{ + struct imx_nand_host *host = chip->priv; + uint32_t config2; + + if (chip->ecc.mode != NAND_ECC_HW) + return; + + config2 = readl(NFC_V3_CONFIG2); + + if (enable) + config2 |= NFC_V3_CONFIG2_ECC_EN; + else + config2 &= ~NFC_V3_CONFIG2_ECC_EN; + + writel(config2, NFC_V3_CONFIG2); +} + +static int imx_nand_correct_data_v1(struct mtd_info *mtd) { struct nand_chip *nand_chip = mtd->priv; struct imx_nand_host *host = nand_chip->priv; @@ -510,8 +540,7 @@ static int imx_nand_correct_data_v1(struct mtd_info *mtd, u_char * dat, return 0; } -static int imx_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat, - u_char *read_ecc, u_char *calc_ecc) +static int imx_nand_correct_data_v2_v3(struct mtd_info *mtd) { struct nand_chip *nand_chip = mtd->priv; struct imx_nand_host *host = nand_chip->priv; @@ -651,13 +680,13 @@ static void imx_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len) /* * Function to transfer data to/from spare area. */ -static void copy_spare(struct mtd_info *mtd, int bfrom) +static void copy_spare(struct mtd_info *mtd, int bfrom, void *buf) { struct nand_chip *this = mtd->priv; struct imx_nand_host *host = this->priv; u16 i, j; u16 n = mtd->writesize >> 9; - u8 *d = host->data_buf + mtd->writesize; + u8 *d = buf; u8 *s = host->spare0; u16 t = host->spare_len; @@ -761,9 +790,6 @@ static void preset_v1_v2(struct mtd_info *mtd) 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; @@ -828,9 +854,6 @@ static void preset_v3(struct mtd_info *mtd) 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) { @@ -872,6 +895,68 @@ static void preset_v3(struct mtd_info *mtd) writel(0, NFC_V3_DELAY_LINE); } +static int imx_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t offset, int data_len, const uint8_t *buf, + int oob_required, int page, int cached, int raw) +{ + struct imx_nand_host *host = chip->priv; + int status; + + host->enable_hwecc(chip, !raw); + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + + memcpy32(host->main_area0, buf, mtd->writesize); + if (oob_required) + copy_spare(mtd, 0, chip->oob_poi); + + host->send_page(host, NFC_INPUT); + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + + if (status & NAND_STATUS_FAIL) + return -EIO; + + return 0; +} + +static void imx_nand_do_read_page(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int oob_required) +{ + struct imx_nand_host *host = chip->priv; + + host->send_page(host, NFC_OUTPUT); + + memcpy32(buf, host->main_area0, mtd->writesize); + + if (oob_required) + copy_spare(mtd, 1, chip->oob_poi); +} + +static int imx_nand_read_page(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int oob_required, int page) +{ + struct imx_nand_host *host = chip->priv; + + host->enable_hwecc(chip, true); + + imx_nand_do_read_page(mtd, chip, buf, oob_required); + + return host->correct(mtd); +} + +static int imx_nand_read_page_raw(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, int oob_required, int page) +{ + struct imx_nand_host *host = chip->priv; + + host->enable_hwecc(chip, false); + + imx_nand_do_read_page(mtd, chip, buf, oob_required); + + return 0; +} + /* * This function is used by the upper layer to write command to NAND Flash for * different operations to be carried out on NAND Flash @@ -927,11 +1012,6 @@ static void imx_nand_command(struct mtd_info *mtd, unsigned command, if (host->pagesize_2k) /* send read confirm command */ host->send_cmd(host, NAND_CMD_READSTART); - - host->send_page(host, NFC_OUTPUT); - - memcpy32(host->data_buf, host->main_area0, mtd->writesize); - copy_spare(mtd, 1); break; case NAND_CMD_SEQIN: @@ -966,9 +1046,6 @@ static void imx_nand_command(struct mtd_info *mtd, unsigned command, break; case NAND_CMD_PAGEPROG: - memcpy32(host->main_area0, host->data_buf, mtd->writesize); - copy_spare(mtd, 0); - host->send_page(host, NFC_INPUT); host->send_cmd(host, command); mxc_do_addr_cycle(mtd, column, page_addr); break; @@ -1179,16 +1256,22 @@ static int __init imxnd_probe(struct device_d *dev) this->read_word = imx_nand_read_word; this->write_buf = imx_nand_write_buf; this->read_buf = imx_nand_read_buf; + this->write_page = imx_nand_write_page; if (host->hw_ecc) { this->ecc.calculate = imx_nand_calculate_ecc; - this->ecc.hwctl = imx_nand_enable_hwecc; + if (nfc_is_v3()) + host->enable_hwecc = imx_nand_enable_hwecc_v3; + else + host->enable_hwecc = imx_nand_enable_hwecc_v1_v2; if (nfc_is_v1()) - this->ecc.correct = imx_nand_correct_data_v1; + host->correct = imx_nand_correct_data_v1; else - this->ecc.correct = imx_nand_correct_data_v2_v3; + host->correct = imx_nand_correct_data_v2_v3; this->ecc.mode = NAND_ECC_HW; this->ecc.size = 512; + this->ecc.read_page_raw = imx_nand_read_page_raw; + this->ecc.read_page = imx_nand_read_page; } else { this->ecc.size = 512; this->ecc.mode = NAND_ECC_SOFT; |