From df467899da0b71465760b4e35127bce837244eee Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 8 Nov 2017 17:00:27 +0100 Subject: mtd: nand: fix interpretation of NAND_CMD_NONE in nand_command[_lp]() Some drivers (like nand_hynix.c) call ->cmdfunc() with NAND_CMD_NONE and a column address and expect the controller to only send address cycles. Right now, the default ->cmdfunc() implementations provided by the core do not filter out the command cycle in this case and forwards the request to the controller driver through the ->cmd_ctrl() method. The thing is, NAND controller drivers can get this wrong and send a command cycle with a NAND_CMD_NONE opcode and since NAND_CMD_NONE is -1, and the command field is usually casted to an u8, we end up sending the 0xFF command which is actually a RESET operation. Add conditions in nand_command[_lp]() functions to sending the initial command cycle when command == NAND_CMD_NONE. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/mtd/nand/nand_base.c') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 6135d007a068..630048f5abdc 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -710,7 +710,8 @@ static void nand_command(struct mtd_info *mtd, unsigned int command, chip->cmd_ctrl(mtd, readcmd, ctrl); ctrl &= ~NAND_CTRL_CHANGE; } - chip->cmd_ctrl(mtd, command, ctrl); + if (command != NAND_CMD_NONE) + chip->cmd_ctrl(mtd, command, ctrl); /* Address cycle, when necessary */ ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE; @@ -738,6 +739,7 @@ static void nand_command(struct mtd_info *mtd, unsigned int command, */ switch (command) { + case NAND_CMD_NONE: case NAND_CMD_PAGEPROG: case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: @@ -831,7 +833,9 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, } /* Command latch cycle */ - chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); + if (command != NAND_CMD_NONE) + chip->cmd_ctrl(mtd, command, + NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); if (column != -1 || page_addr != -1) { int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE; @@ -866,6 +870,7 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, */ switch (command) { + case NAND_CMD_NONE: case NAND_CMD_CACHEDPROG: case NAND_CMD_PAGEPROG: case NAND_CMD_ERASE1: -- cgit v1.2.3 From eb94555e9e97c9983461214046b4d72c4ab4ba70 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 30 Nov 2017 18:01:28 +0100 Subject: mtd: nand: use usual return values for the ->erase() hook Avoid using specific defined values for checking returned status of the ->erase() hook. Instead, use usual negative error values on failure, zero otherwise. Signed-off-by: Miquel Raynal Acked-by: Masahiro Yamada Signed-off-by: Boris Brezillon --- drivers/mtd/nand/denali.c | 2 +- drivers/mtd/nand/docg4.c | 7 ++++++- drivers/mtd/nand/nand_base.c | 10 ++++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) (limited to 'drivers/mtd/nand/nand_base.c') diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 34008a02ddb0..3e19861a46c6 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -951,7 +951,7 @@ static int denali_erase(struct mtd_info *mtd, int page) irq_status = denali_wait_for_irq(denali, INTR__ERASE_COMP | INTR__ERASE_FAIL); - return irq_status & INTR__ERASE_COMP ? 0 : NAND_STATUS_FAIL; + return irq_status & INTR__ERASE_COMP ? 0 : -EIO; } static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr, diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c index 2436cbc71662..45c01b4b34c7 100644 --- a/drivers/mtd/nand/docg4.c +++ b/drivers/mtd/nand/docg4.c @@ -900,6 +900,7 @@ static int docg4_erase_block(struct mtd_info *mtd, int page) struct docg4_priv *doc = nand_get_controller_data(nand); void __iomem *docptr = doc->virtadr; uint16_t g4_page; + int status; dev_dbg(doc->dev, "%s: page %04x\n", __func__, page); @@ -939,7 +940,11 @@ static int docg4_erase_block(struct mtd_info *mtd, int page) poll_status(doc); write_nop(docptr); - return nand->waitfunc(mtd, nand); + status = nand->waitfunc(mtd, nand); + if (status < 0) + return status; + + return status & NAND_STATUS_FAIL ? -EIO : 0; } static int write_page(struct mtd_info *mtd, struct nand_chip *nand, diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 630048f5abdc..eacc3f39cafd 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2989,11 +2989,17 @@ out: static int single_erase(struct mtd_info *mtd, int page) { struct nand_chip *chip = mtd_to_nand(mtd); + int status; + /* Send commands to erase a block */ chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); - return chip->waitfunc(mtd, chip); + status = chip->waitfunc(mtd, chip); + if (status < 0) + return status; + + return status & NAND_STATUS_FAIL ? -EIO : 0; } /** @@ -3077,7 +3083,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, status = chip->erase(mtd, page & chip->pagemask); /* See if block erase succeeded */ - if (status & NAND_STATUS_FAIL) { + if (status) { pr_debug("%s: failed erase, page 0x%08x\n", __func__, page); instr->state = MTD_ERASE_FAILED; -- cgit v1.2.3 From 97d90da8a886949f09bb4754843fb0b504956ad2 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 30 Nov 2017 18:01:29 +0100 Subject: mtd: nand: provide several helpers to do common NAND operations This is part of the process of removing direct calls to ->cmdfunc() outside of the core in order to introduce a better interface to execute NAND operations. Here we provide several helpers and make use of them to remove all direct calls to ->cmdfunc(). This way, we can easily modify those helpers to make use of the new ->exec_op() interface when available. Signed-off-by: Boris Brezillon [miquel.raynal@free-electrons.com: rebased and fixed some conflicts] Signed-off-by: Miquel Raynal Acked-by: Masahiro Yamada --- drivers/mtd/nand/atmel/nand-controller.c | 2 +- drivers/mtd/nand/brcmnand/brcmnand.c | 9 +- drivers/mtd/nand/cafe_nand.c | 14 +- drivers/mtd/nand/denali.c | 37 +- drivers/mtd/nand/diskonchip.c | 4 +- drivers/mtd/nand/docg4.c | 2 +- drivers/mtd/nand/fsmc_nand.c | 5 +- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 58 +- drivers/mtd/nand/hisi504_nand.c | 3 +- drivers/mtd/nand/jz4740_nand.c | 16 +- drivers/mtd/nand/lpc32xx_mlc.c | 2 +- drivers/mtd/nand/lpc32xx_slc.c | 22 +- drivers/mtd/nand/mtk_nand.c | 11 +- drivers/mtd/nand/nand_base.c | 1011 +++++++++++++++++++++++++----- drivers/mtd/nand/nand_hynix.c | 115 ++-- drivers/mtd/nand/nand_micron.c | 77 ++- drivers/mtd/nand/omap2.c | 8 +- drivers/mtd/nand/pxa3xx_nand.c | 8 +- drivers/mtd/nand/qcom_nandc.c | 16 +- drivers/mtd/nand/r852.c | 11 +- drivers/mtd/nand/sunxi_nand.c | 71 +-- drivers/mtd/nand/tango_nand.c | 26 +- drivers/mtd/nand/tmio_nand.c | 5 +- include/linux/mtd/rawnand.h | 29 + 24 files changed, 1131 insertions(+), 431 deletions(-) (limited to 'drivers/mtd/nand/nand_base.c') diff --git a/drivers/mtd/nand/atmel/nand-controller.c b/drivers/mtd/nand/atmel/nand-controller.c index 90a71a56bc23..e81fdd2d47b1 100644 --- a/drivers/mtd/nand/atmel/nand-controller.c +++ b/drivers/mtd/nand/atmel/nand-controller.c @@ -1000,7 +1000,7 @@ static int atmel_hsmc_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf, * to the non-optimized one. */ if (nand->activecs->rb.type != ATMEL_NAND_NATIVE_RB) { - chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); + nand_read_page_op(chip, page, 0, NULL, 0); return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, raw); diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c index e0eb51d8c012..3f441096a14c 100644 --- a/drivers/mtd/nand/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/brcmnand/brcmnand.c @@ -1071,7 +1071,7 @@ static void brcmnand_wp(struct mtd_info *mtd, int wp) return; brcmnand_set_wp(ctrl, wp); - chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + nand_status_op(chip, NULL); /* NAND_STATUS_WP 0x00 = protected, 0x80 = not protected */ ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY | @@ -1453,7 +1453,7 @@ static uint8_t brcmnand_read_byte(struct mtd_info *mtd) /* At FC_BYTES boundary, switch to next column */ if (host->last_byte > 0 && offs == 0) - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, addr, -1); + nand_change_read_column_op(chip, addr, NULL, 0, false); ret = ctrl->flash_cache[offs]; break; @@ -1689,7 +1689,7 @@ static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd, sas = mtd->oobsize / chip->ecc.steps; /* read without ecc for verification */ - chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); + nand_read_page_op(chip, page, 0, NULL, 0); ret = chip->ecc.read_page_raw(mtd, chip, buf, true, page); if (ret) return ret; @@ -2369,12 +2369,11 @@ static int brcmnand_resume(struct device *dev) list_for_each_entry(host, &ctrl->host_list, node) { struct nand_chip *chip = &host->chip; - struct mtd_info *mtd = nand_to_mtd(chip); brcmnand_save_restore_cs_config(host, 1); /* Reset the chip, required by some chips after power-up */ - chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + nand_reset_op(chip); } return 0; diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index bc558c438a57..95c2cfa68b66 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -353,23 +353,15 @@ static void cafe_nand_bug(struct mtd_info *mtd) static int cafe_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) { - int status = 0; - - chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - status = chip->waitfunc(mtd, chip); - - return status & NAND_STATUS_FAIL ? -EIO : 0; + return nand_prog_page_op(chip, page, mtd->writesize, chip->oob_poi, + mtd->oobsize); } /* Don't use -- use nand_read_oob_std for now */ static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) { - chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - return 0; + return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); } /** * cafe_nand_read_page_syndrome - [REPLACEABLE] hardware ecc syndrome based page read diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 3e19861a46c6..d5c80d617854 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -645,8 +645,6 @@ static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip, int page, int write) { struct denali_nand_info *denali = mtd_to_denali(mtd); - unsigned int start_cmd = write ? NAND_CMD_SEQIN : NAND_CMD_READ0; - unsigned int rnd_cmd = write ? NAND_CMD_RNDIN : NAND_CMD_RNDOUT; int writesize = mtd->writesize; int oobsize = mtd->oobsize; uint8_t *bufpoi = chip->oob_poi; @@ -658,11 +656,11 @@ static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip, int i, pos, len; /* BBM at the beginning of the OOB area */ - chip->cmdfunc(mtd, start_cmd, writesize, page); if (write) - chip->write_buf(mtd, bufpoi, oob_skip); + nand_prog_page_begin_op(chip, page, writesize, bufpoi, + oob_skip); else - chip->read_buf(mtd, bufpoi, oob_skip); + nand_read_page_op(chip, page, writesize, bufpoi, oob_skip); bufpoi += oob_skip; /* OOB ECC */ @@ -675,30 +673,35 @@ static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip, else if (pos + len > writesize) len = writesize - pos; - chip->cmdfunc(mtd, rnd_cmd, pos, -1); if (write) - chip->write_buf(mtd, bufpoi, len); + nand_change_write_column_op(chip, pos, bufpoi, len, + false); else - chip->read_buf(mtd, bufpoi, len); + nand_change_read_column_op(chip, pos, bufpoi, len, + false); bufpoi += len; if (len < ecc_bytes) { len = ecc_bytes - len; - chip->cmdfunc(mtd, rnd_cmd, writesize + oob_skip, -1); if (write) - chip->write_buf(mtd, bufpoi, len); + nand_change_write_column_op(chip, writesize + + oob_skip, bufpoi, + len, false); else - chip->read_buf(mtd, bufpoi, len); + nand_change_read_column_op(chip, writesize + + oob_skip, bufpoi, + len, false); bufpoi += len; } } /* OOB free */ len = oobsize - (bufpoi - chip->oob_poi); - chip->cmdfunc(mtd, rnd_cmd, size - len, -1); if (write) - chip->write_buf(mtd, bufpoi, len); + nand_change_write_column_op(chip, size - len, bufpoi, len, + false); else - chip->read_buf(mtd, bufpoi, len); + nand_change_read_column_op(chip, size - len, bufpoi, len, + false); } static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, @@ -788,16 +791,12 @@ static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) { struct denali_nand_info *denali = mtd_to_denali(mtd); - int status; denali_reset_irq(denali); denali_oob_xfer(mtd, chip, page, 1); - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - status = chip->waitfunc(mtd, chip); - - return status & NAND_STATUS_FAIL ? -EIO : 0; + return nand_prog_page_end_op(chip); } static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index 72671dc52e2e..6bc93ea66f50 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -448,7 +448,7 @@ static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this) int status; DoC_WaitReady(doc); - this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + nand_status_op(this, NULL); DoC_WaitReady(doc); status = (int)this->read_byte(mtd); @@ -595,7 +595,7 @@ static void doc2001plus_select_chip(struct mtd_info *mtd, int chip) /* Assert ChipEnable and deassert WriteProtect */ WriteDOC((DOC_FLASH_CE), docptr, Mplus_FlashSelect); - this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + nand_reset_op(this); doc->curchip = chip; doc->curfloor = floor; diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c index 45c01b4b34c7..5a27f56dafdc 100644 --- a/drivers/mtd/nand/docg4.c +++ b/drivers/mtd/nand/docg4.c @@ -864,7 +864,7 @@ static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand, dev_dbg(doc->dev, "%s: page %x\n", __func__, page); - docg4_command(mtd, NAND_CMD_READ0, nand->ecc.size, page); + nand_read_page_op(nand, page, nand->ecc.size, NULL, 0); writew(DOC_ECCCONF0_READ_MODE | DOCG4_OOB_SIZE, docptr + DOC_ECCCONF0); write_nop(docptr); diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index eac15d9bf49e..b44e5c6545e0 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -697,7 +697,7 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, unsigned int max_bitflips = 0; for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) { - chip->cmdfunc(mtd, NAND_CMD_READ0, s * eccsize, page); + nand_read_page_op(chip, page, s * eccsize, NULL, 0); chip->ecc.hwctl(mtd, NAND_ECC_READ); chip->read_buf(mtd, p, eccsize); @@ -720,8 +720,7 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, if (chip->options & NAND_BUSWIDTH_16) len = roundup(len, 2); - chip->cmdfunc(mtd, NAND_CMD_READOOB, off, page); - chip->read_buf(mtd, oob + j, len); + nand_read_oob_op(chip, page, off, oob + j, len); j += len; } diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 9e365d488b6c..63a425ced4cd 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -1097,8 +1097,8 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, eccbytes = DIV_ROUND_UP(offset + eccbits, 8); offset /= 8; eccbytes -= offset; - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); - chip->read_buf(mtd, eccbuf, eccbytes); + nand_change_read_column_op(chip, offset, eccbuf, + eccbytes, false); /* * ECC data are not byte aligned and we may have @@ -1220,7 +1220,7 @@ static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, meta = geo->metadata_size; if (first) { col = meta + (size + ecc_parity_size) * first; - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, col, -1); + nand_change_read_column_op(chip, col, NULL, 0, false); meta = 0; buf = buf + first * size; @@ -1411,7 +1411,7 @@ static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, memset(chip->oob_poi, ~0, mtd->oobsize); /* Read out the conventional OOB. */ - chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page); + nand_read_page_op(chip, page, mtd->writesize, NULL, 0); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); /* @@ -1421,7 +1421,7 @@ static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, */ if (GPMI_IS_MX23(this)) { /* Read the block mark into the first byte of the OOB buffer. */ - chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + nand_read_page_op(chip, page, 0, NULL, 0); chip->oob_poi[0] = chip->read_byte(mtd); } @@ -1432,7 +1432,6 @@ static int gpmi_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) { struct mtd_oob_region of = { }; - int status = 0; /* Do we have available oob area? */ mtd_ooblayout_free(mtd, 0, &of); @@ -1442,12 +1441,8 @@ gpmi_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) if (!nand_is_slc(chip)) return -EPERM; - chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize + of.offset, page); - chip->write_buf(mtd, chip->oob_poi + of.offset, of.length); - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - - status = chip->waitfunc(mtd, chip); - return status & NAND_STATUS_FAIL ? -EIO : 0; + return nand_prog_page_op(chip, page, mtd->writesize + of.offset, + chip->oob_poi + of.offset, of.length); } /* @@ -1622,7 +1617,7 @@ static int gpmi_ecc_write_page_raw(struct mtd_info *mtd, static int gpmi_ecc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, int page) { - chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + nand_read_page_op(chip, page, 0, NULL, 0); return gpmi_ecc_read_page_raw(mtd, chip, NULL, 1, page); } @@ -1630,7 +1625,7 @@ static int gpmi_ecc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, static int gpmi_ecc_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, int page) { - chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0, page); + nand_prog_page_begin_op(chip, page, 0, NULL, 0); return gpmi_ecc_write_page_raw(mtd, chip, NULL, 1, page); } @@ -1641,7 +1636,7 @@ static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs) struct gpmi_nand_data *this = nand_get_controller_data(chip); int ret = 0; uint8_t *block_mark; - int column, page, status, chipnr; + int column, page, chipnr; chipnr = (int)(ofs >> chip->chip_shift); chip->select_chip(mtd, chipnr); @@ -1655,13 +1650,7 @@ static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs) /* Shift to get page */ page = (int)(ofs >> chip->page_shift); - chip->cmdfunc(mtd, NAND_CMD_SEQIN, column, page); - chip->write_buf(mtd, block_mark, 1); - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - - status = chip->waitfunc(mtd, chip); - if (status & NAND_STATUS_FAIL) - ret = -EIO; + ret = nand_prog_page_op(chip, page, column, block_mark, 1); chip->select_chip(mtd, -1); @@ -1729,7 +1718,7 @@ static int mx23_check_transcription_stamp(struct gpmi_nand_data *this) * Read the NCB fingerprint. The fingerprint is four bytes long * and starts in the 12th byte of the page. */ - chip->cmdfunc(mtd, NAND_CMD_READ0, 12, page); + nand_read_page_op(chip, page, 12, NULL, 0); chip->read_buf(mtd, buffer, strlen(fingerprint)); /* Look for the fingerprint. */ @@ -1789,17 +1778,10 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this) dev_dbg(dev, "Erasing the search area...\n"); for (block = 0; block < search_area_size_in_blocks; block++) { - /* Compute the page address. */ - page = block * block_size_in_pages; - /* Erase this block. */ dev_dbg(dev, "\tErasing block 0x%x\n", block); - chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); - chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); - - /* Wait for the erase to finish. */ - status = chip->waitfunc(mtd, chip); - if (status & NAND_STATUS_FAIL) + status = nand_erase_op(chip, block); + if (status) dev_err(dev, "[%s] Erase failed.\n", __func__); } @@ -1815,13 +1797,11 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this) /* Write the first page of the current stride. */ dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page); - chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); - chip->ecc.write_page_raw(mtd, chip, buffer, 0, page); - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - /* Wait for the write to finish. */ - status = chip->waitfunc(mtd, chip); - if (status & NAND_STATUS_FAIL) + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + chip->ecc.write_page_raw(mtd, chip, buffer, 0, page); + status = nand_prog_page_end_op(chip); + if (status) dev_err(dev, "[%s] Write failed.\n", __func__); } @@ -1876,7 +1856,7 @@ static int mx23_boot_init(struct gpmi_nand_data *this) /* Send the command to read the conventional block mark. */ chip->select_chip(mtd, chipnr); - chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page); + nand_read_page_op(chip, page, mtd->writesize, NULL, 0); block_mark = chip->read_byte(mtd); chip->select_chip(mtd, -1); diff --git a/drivers/mtd/nand/hisi504_nand.c b/drivers/mtd/nand/hisi504_nand.c index 0897261c3e17..184d765c8bbe 100644 --- a/drivers/mtd/nand/hisi504_nand.c +++ b/drivers/mtd/nand/hisi504_nand.c @@ -574,8 +574,7 @@ static int hisi_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, { struct hinfc_host *host = nand_get_controller_data(chip); - chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); if (host->irq_status & HINFC504_INTS_UE) { host->irq_status = 0; diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index ad827d4af3e9..613b00a9604b 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c @@ -313,6 +313,7 @@ static int jz_nand_detect_bank(struct platform_device *pdev, uint32_t ctrl; struct nand_chip *chip = &nand->chip; struct mtd_info *mtd = nand_to_mtd(chip); + u8 id[2]; /* Request I/O resource. */ sprintf(res_name, "bank%d", bank); @@ -335,17 +336,16 @@ static int jz_nand_detect_bank(struct platform_device *pdev, /* Retrieve the IDs from the first chip. */ chip->select_chip(mtd, 0); - chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); - chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); - *nand_maf_id = chip->read_byte(mtd); - *nand_dev_id = chip->read_byte(mtd); + nand_reset_op(chip); + nand_readid_op(chip, 0, id, sizeof(id)); + *nand_maf_id = id[0]; + *nand_dev_id = id[1]; } else { /* Detect additional chip. */ chip->select_chip(mtd, chipnr); - chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); - chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); - if (*nand_maf_id != chip->read_byte(mtd) - || *nand_dev_id != chip->read_byte(mtd)) { + nand_reset_op(chip); + nand_readid_op(chip, 0, id, sizeof(id)); + if (*nand_maf_id != id[0] || *nand_dev_id != id[1]) { ret = -ENODEV; goto notfound_id; } diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/lpc32xx_mlc.c index 5796468db653..31cb3b2967b9 100644 --- a/drivers/mtd/nand/lpc32xx_mlc.c +++ b/drivers/mtd/nand/lpc32xx_mlc.c @@ -461,7 +461,7 @@ static int lpc32xx_read_page(struct mtd_info *mtd, struct nand_chip *chip, } /* Writing Command and Address */ - chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + nand_read_page_op(chip, page, 0, NULL, 0); /* For all sub-pages */ for (i = 0; i < host->mlcsubpages; i++) { diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c index b61f28a1554d..2b96c281b1a2 100644 --- a/drivers/mtd/nand/lpc32xx_slc.c +++ b/drivers/mtd/nand/lpc32xx_slc.c @@ -399,10 +399,7 @@ static void lpc32xx_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int static int lpc32xx_nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, int page) { - chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - - return 0; + return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); } /* @@ -411,17 +408,8 @@ static int lpc32xx_nand_read_oob_syndrome(struct mtd_info *mtd, static int lpc32xx_nand_write_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, int page) { - int status; - - chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - - /* Send command to program the OOB data */ - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - - status = chip->waitfunc(mtd, chip); - - return status & NAND_STATUS_FAIL ? -EIO : 0; + return nand_prog_page_op(chip, page, mtd->writesize, chip->oob_poi, + mtd->oobsize); } /* @@ -632,7 +620,7 @@ static int lpc32xx_nand_read_page_syndrome(struct mtd_info *mtd, uint8_t *oobecc, tmpecc[LPC32XX_ECC_SAVE_SIZE]; /* Issue read command */ - chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + nand_read_page_op(chip, page, 0, NULL, 0); /* Read data and oob, calculate ECC */ status = lpc32xx_xfer(mtd, buf, chip->ecc.steps, 1); @@ -675,7 +663,7 @@ static int lpc32xx_nand_read_page_raw_syndrome(struct mtd_info *mtd, int page) { /* Issue read command */ - chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + nand_read_page_op(chip, page, 0, NULL, 0); /* Raw reads can just use the FIFO interface */ chip->read_buf(mtd, buf, chip->ecc.size * chip->ecc.steps); diff --git a/drivers/mtd/nand/mtk_nand.c b/drivers/mtd/nand/mtk_nand.c index 6d0101e13ef6..9c4adaf9331b 100644 --- a/drivers/mtd/nand/mtk_nand.c +++ b/drivers/mtd/nand/mtk_nand.c @@ -834,16 +834,13 @@ static int mtk_nfc_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, { int ret; - chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + nand_prog_page_begin_op(chip, page, 0, NULL, 0); ret = mtk_nfc_write_page_raw(mtd, chip, NULL, 1, page); if (ret < 0) return -EIO; - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - ret = chip->waitfunc(mtd, chip); - - return ret & NAND_STATUS_FAIL ? -EIO : 0; + return nand_prog_page_end_op(chip); } static int mtk_nfc_update_ecc_stats(struct mtd_info *mtd, u8 *buf, u32 sectors) @@ -893,7 +890,7 @@ static int mtk_nfc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, buf = bufpoi + start * chip->ecc.size; if (column != 0) - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, column, -1); + nand_change_read_column_op(chip, column, NULL, 0, false); addr = dma_map_single(nfc->dev, buf, len, DMA_FROM_DEVICE); rc = dma_mapping_error(nfc->dev, addr); @@ -1016,7 +1013,7 @@ static int mtk_nfc_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, static int mtk_nfc_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page) { - chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + nand_read_page_op(chip, page, 0, NULL, 0); return mtk_nfc_read_page_raw(mtd, chip, NULL, 1, page); } diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index eacc3f39cafd..539132ef0095 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -561,14 +561,19 @@ static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs) static int nand_check_wp(struct mtd_info *mtd) { struct nand_chip *chip = mtd_to_nand(mtd); + u8 status; + int ret; /* Broken xD cards report WP despite being writable */ if (chip->options & NAND_BROKEN_XD) return 0; /* Check the WP bit */ - chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); - return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; + ret = nand_status_op(chip, &status); + if (ret) + return ret; + + return status & NAND_STATUS_WP ? 0 : 1; } /** @@ -667,10 +672,17 @@ EXPORT_SYMBOL_GPL(nand_wait_ready); static void nand_wait_status_ready(struct mtd_info *mtd, unsigned long timeo) { register struct nand_chip *chip = mtd_to_nand(mtd); + int ret; timeo = jiffies + msecs_to_jiffies(timeo); do { - if ((chip->read_byte(mtd) & NAND_STATUS_READY)) + u8 status; + + ret = nand_read_data_op(chip, &status, sizeof(status), true); + if (ret) + return; + + if (status & NAND_STATUS_READY) break; touch_softlockup_watchdog(); } while (time_before(jiffies, timeo)); @@ -1019,7 +1031,15 @@ static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip, if (chip->dev_ready(mtd)) break; } else { - if (chip->read_byte(mtd) & NAND_STATUS_READY) + int ret; + u8 status; + + ret = nand_read_data_op(chip, &status, sizeof(status), + true); + if (ret) + return; + + if (status & NAND_STATUS_READY) break; } mdelay(1); @@ -1036,8 +1056,9 @@ static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip, static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) { - int status; unsigned long timeo = 400; + u8 status; + int ret; /* * Apply this short delay always to ensure that we do wait tWB in any @@ -1045,7 +1066,9 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) */ ndelay(100); - chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + ret = nand_status_op(chip, NULL); + if (ret) + return ret; if (in_interrupt() || oops_in_progress) panic_nand_wait(mtd, chip, timeo); @@ -1056,14 +1079,22 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) if (chip->dev_ready(mtd)) break; } else { - if (chip->read_byte(mtd) & NAND_STATUS_READY) + ret = nand_read_data_op(chip, &status, + sizeof(status), true); + if (ret) + return ret; + + if (status & NAND_STATUS_READY) break; } cond_resched(); } while (time_before(jiffies, timeo)); } - status = (int)chip->read_byte(mtd); + ret = nand_read_data_op(chip, &status, sizeof(status), true); + if (ret) + return ret; + /* This can happen if in case of timeout or buggy dev_ready */ WARN_ON(!(status & NAND_STATUS_READY)); return status; @@ -1217,6 +1248,516 @@ static void nand_release_data_interface(struct nand_chip *chip) kfree(chip->data_interface); } +/** + * nand_read_page_op - Do a READ PAGE operation + * @chip: The NAND chip + * @page: page to read + * @offset_in_page: offset within the page + * @buf: buffer used to store the data + * @len: length of the buffer + * + * This function issues a READ PAGE operation. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_read_page_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, void *buf, unsigned int len) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (len && !buf) + return -EINVAL; + + if (offset_in_page + len > mtd->writesize + mtd->oobsize) + return -EINVAL; + + chip->cmdfunc(mtd, NAND_CMD_READ0, offset_in_page, page); + if (len) + chip->read_buf(mtd, buf, len); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_read_page_op); + +/** + * nand_read_param_page_op - Do a READ PARAMETER PAGE operation + * @chip: The NAND chip + * @page: parameter page to read + * @buf: buffer used to store the data + * @len: length of the buffer + * + * This function issues a READ PARAMETER PAGE operation. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +static int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf, + unsigned int len) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + unsigned int i; + u8 *p = buf; + + if (len && !buf) + return -EINVAL; + + chip->cmdfunc(mtd, NAND_CMD_PARAM, page, -1); + for (i = 0; i < len; i++) + p[i] = chip->read_byte(mtd); + + return 0; +} + +/** + * nand_change_read_column_op - Do a CHANGE READ COLUMN operation + * @chip: The NAND chip + * @offset_in_page: offset within the page + * @buf: buffer used to store the data + * @len: length of the buffer + * @force_8bit: force 8-bit bus access + * + * This function issues a CHANGE READ COLUMN operation. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_change_read_column_op(struct nand_chip *chip, + unsigned int offset_in_page, void *buf, + unsigned int len, bool force_8bit) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (len && !buf) + return -EINVAL; + + if (offset_in_page + len > mtd->writesize + mtd->oobsize) + return -EINVAL; + + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset_in_page, -1); + if (len) + chip->read_buf(mtd, buf, len); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_change_read_column_op); + +/** + * nand_read_oob_op - Do a READ OOB operation + * @chip: The NAND chip + * @page: page to read + * @offset_in_oob: offset within the OOB area + * @buf: buffer used to store the data + * @len: length of the buffer + * + * This function issues a READ OOB operation. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_read_oob_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_oob, void *buf, unsigned int len) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (len && !buf) + return -EINVAL; + + if (offset_in_oob + len > mtd->oobsize) + return -EINVAL; + + chip->cmdfunc(mtd, NAND_CMD_READOOB, offset_in_oob, page); + if (len) + chip->read_buf(mtd, buf, len); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_read_oob_op); + +/** + * nand_prog_page_begin_op - starts a PROG PAGE operation + * @chip: The NAND chip + * @page: page to write + * @offset_in_page: offset within the page + * @buf: buffer containing the data to write to the page + * @len: length of the buffer + * + * This function issues the first half of a PROG PAGE operation. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_prog_page_begin_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, const void *buf, + unsigned int len) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (len && !buf) + return -EINVAL; + + if (offset_in_page + len > mtd->writesize + mtd->oobsize) + return -EINVAL; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, offset_in_page, page); + + if (buf) + chip->write_buf(mtd, buf, len); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_prog_page_begin_op); + +/** + * nand_prog_page_end_op - ends a PROG PAGE operation + * @chip: The NAND chip + * + * This function issues the second half of a PROG PAGE operation. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_prog_page_end_op(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int status; + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + + status = chip->waitfunc(mtd, chip); + if (status & NAND_STATUS_FAIL) + return -EIO; + + return 0; +} +EXPORT_SYMBOL_GPL(nand_prog_page_end_op); + +/** + * nand_prog_page_op - Do a full PROG PAGE operation + * @chip: The NAND chip + * @page: page to write + * @offset_in_page: offset within the page + * @buf: buffer containing the data to write to the page + * @len: length of the buffer + * + * This function issues a full PROG PAGE operation. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_prog_page_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, const void *buf, + unsigned int len) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int status; + + if (!len || !buf) + return -EINVAL; + + if (offset_in_page + len > mtd->writesize + mtd->oobsize) + return -EINVAL; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, offset_in_page, page); + chip->write_buf(mtd, buf, len); + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + + status = chip->waitfunc(mtd, chip); + if (status & NAND_STATUS_FAIL) + return -EIO; + + return 0; +} +EXPORT_SYMBOL_GPL(nand_prog_page_op); + +/** + * nand_change_write_column_op - Do a CHANGE WRITE COLUMN operation + * @chip: The NAND chip + * @offset_in_page: offset within the page + * @buf: buffer containing the data to send to the NAND + * @len: length of the buffer + * @force_8bit: force 8-bit bus access + * + * This function issues a CHANGE WRITE COLUMN operation. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_change_write_column_op(struct nand_chip *chip, + unsigned int offset_in_page, + const void *buf, unsigned int len, + bool force_8bit) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (len && !buf) + return -EINVAL; + + if (offset_in_page + len > mtd->writesize + mtd->oobsize) + return -EINVAL; + + chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset_in_page, -1); + if (len) + chip->write_buf(mtd, buf, len); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_change_write_column_op); + +/** + * nand_readid_op - Do a READID operation + * @chip: The NAND chip + * @addr: address cycle to pass after the READID command + * @buf: buffer used to store the ID + * @len: length of the buffer + * + * This function sends a READID command and reads back the ID returned by the + * NAND. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf, + unsigned int len) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + unsigned int i; + u8 *id = buf; + + if (len && !buf) + return -EINVAL; + + chip->cmdfunc(mtd, NAND_CMD_READID, addr, -1); + + for (i = 0; i < len; i++) + id[i] = chip->read_byte(mtd); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_readid_op); + +/** + * nand_status_op - Do a STATUS operation + * @chip: The NAND chip + * @status: out variable to store the NAND status + * + * This function sends a STATUS command and reads back the status returned by + * the NAND. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_status_op(struct nand_chip *chip, u8 *status) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + if (status) + *status = chip->read_byte(mtd); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_status_op); + +/** + * nand_exit_status_op - Exit a STATUS operation + * @chip: The NAND chip + * + * This function sends a READ0 command to cancel the effect of the STATUS + * command to avoid reading only the status until a new read command is sent. + * + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_exit_status_op(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + chip->cmdfunc(mtd, NAND_CMD_READ0, -1, -1); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_exit_status_op); + +/** + * nand_erase_op - Do an erase operation + * @chip: The NAND chip + * @eraseblock: block to erase + * + * This function sends an ERASE command and waits for the NAND to be ready + * before returning. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + unsigned int page = eraseblock << + (chip->phys_erase_shift - chip->page_shift); + int status; + + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); + chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); + + status = chip->waitfunc(mtd, chip); + if (status < 0) + return status; + + if (status & NAND_STATUS_FAIL) + return -EIO; + + return 0; +} +EXPORT_SYMBOL_GPL(nand_erase_op); + +/** + * nand_set_features_op - Do a SET FEATURES operation + * @chip: The NAND chip + * @feature: feature id + * @data: 4 bytes of data + * + * This function sends a SET FEATURES command and waits for the NAND to be + * ready before returning. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +static int nand_set_features_op(struct nand_chip *chip, u8 feature, + const void *data) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + const u8 *params = data; + int i, status; + + chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, feature, -1); + for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) + chip->write_byte(mtd, params[i]); + + status = chip->waitfunc(mtd, chip); + if (status & NAND_STATUS_FAIL) + return -EIO; + + return 0; +} + +/** + * nand_get_features_op - Do a GET FEATURES operation + * @chip: The NAND chip + * @feature: feature id + * @data: 4 bytes of data + * + * This function sends a GET FEATURES command and waits for the NAND to be + * ready before returning. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +static int nand_get_features_op(struct nand_chip *chip, u8 feature, + void *data) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + u8 *params = data; + int i; + + chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, feature, -1); + for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) + params[i] = chip->read_byte(mtd); + + return 0; +} + +/** + * nand_reset_op - Do a reset operation + * @chip: The NAND chip + * + * This function sends a RESET command and waits for the NAND to be ready + * before returning. + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_reset_op(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + return 0; +} +EXPORT_SYMBOL_GPL(nand_reset_op); + +/** + * nand_read_data_op - Read data from the NAND + * @chip: The NAND chip + * @buf: buffer used to store the data + * @len: length of the buffer + * @force_8bit: force 8-bit bus access + * + * This function does a raw data read on the bus. Usually used after launching + * another NAND operation like nand_read_page_op(). + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len, + bool force_8bit) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (!len || !buf) + return -EINVAL; + + if (force_8bit) { + u8 *p = buf; + unsigned int i; + + for (i = 0; i < len; i++) + p[i] = chip->read_byte(mtd); + } else { + chip->read_buf(mtd, buf, len); + } + + return 0; +} +EXPORT_SYMBOL_GPL(nand_read_data_op); + +/** + * nand_write_data_op - Write data from the NAND + * @chip: The NAND chip + * @buf: buffer containing the data to send on the bus + * @len: length of the buffer + * @force_8bit: force 8-bit bus access + * + * This function does a raw data write on the bus. Usually used after launching + * another NAND operation like nand_write_page_begin_op(). + * This function does not select/unselect the CS line. + * + * Returns 0 on success, a negative error code otherwise. + */ +int nand_write_data_op(struct nand_chip *chip, const void *buf, + unsigned int len, bool force_8bit) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (!len || !buf) + return -EINVAL; + + if (force_8bit) { + const u8 *p = buf; + unsigned int i; + + for (i = 0; i < len; i++) + chip->write_byte(mtd, p[i]); + } else { + chip->write_buf(mtd, buf, len); + } + + return 0; +} +EXPORT_SYMBOL_GPL(nand_write_data_op); + /** * nand_reset - Reset and initialize a NAND device * @chip: The NAND chip @@ -1238,8 +1779,10 @@ int nand_reset(struct nand_chip *chip, int chipnr) * interface settings, hence this weird ->select_chip() dance. */ chip->select_chip(mtd, chipnr); - chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + ret = nand_reset_op(chip); chip->select_chip(mtd, -1); + if (ret) + return ret; chip->select_chip(mtd, chipnr); ret = nand_setup_data_interface(chip, chipnr); @@ -1395,9 +1938,19 @@ EXPORT_SYMBOL(nand_check_erased_ecc_chunk); int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - chip->read_buf(mtd, buf, mtd->writesize); - if (oob_required) - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + int ret; + + ret = nand_read_data_op(chip, buf, mtd->writesize, false); + if (ret) + return ret; + + if (oob_required) { + ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, + false); + if (ret) + return ret; + } + return 0; } EXPORT_SYMBOL(nand_read_page_raw); @@ -1419,29 +1972,46 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; uint8_t *oob = chip->oob_poi; - int steps, size; + int steps, size, ret; for (steps = chip->ecc.steps; steps > 0; steps--) { - chip->read_buf(mtd, buf, eccsize); + ret = nand_read_data_op(chip, buf, eccsize, false); + if (ret) + return ret; + buf += eccsize; if (chip->ecc.prepad) { - chip->read_buf(mtd, oob, chip->ecc.prepad); + ret = nand_read_data_op(chip, oob, chip->ecc.prepad, + false); + if (ret) + return ret; + oob += chip->ecc.prepad; } - chip->read_buf(mtd, oob, eccbytes); + ret = nand_read_data_op(chip, oob, eccbytes, false); + if (ret) + return ret; + oob += eccbytes; if (chip->ecc.postpad) { - chip->read_buf(mtd, oob, chip->ecc.postpad); + ret = nand_read_data_op(chip, oob, chip->ecc.postpad, + false); + if (ret) + return ret; + oob += chip->ecc.postpad; } } size = mtd->oobsize - (oob - chip->oob_poi); - if (size) - chip->read_buf(mtd, oob, size); + if (size) { + ret = nand_read_data_op(chip, oob, size, false); + if (ret) + return ret; + } return 0; } @@ -1530,7 +2100,9 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1); p = bufpoi + data_col_addr; - chip->read_buf(mtd, p, datafrag_len); + ret = nand_read_data_op(chip, p, datafrag_len, false); + if (ret) + return ret; /* Calculate ECC */ for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) @@ -1548,8 +2120,11 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, gaps = 1; if (gaps) { - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + ret = nand_change_read_column_op(chip, mtd->writesize, + chip->oob_poi, mtd->oobsize, + false); + if (ret) + return ret; } else { /* * Send the command to read the particular ECC bytes take care @@ -1563,9 +2138,12 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, (busw - 1)) aligned_len++; - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, - mtd->writesize + aligned_pos, -1); - chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len); + ret = nand_change_read_column_op(chip, + mtd->writesize + aligned_pos, + &chip->oob_poi[aligned_pos], + aligned_len, false); + if (ret) + return ret; } ret = mtd_ooblayout_get_eccbytes(mtd, chip->buffers->ecccode, @@ -1622,10 +2200,17 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { chip->ecc.hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, p, eccsize); + + ret = nand_read_data_op(chip, p, eccsize, false); + if (ret) + return ret; + chip->ecc.calculate(mtd, p, &ecc_calc[i]); } - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, false); + if (ret) + return ret; ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, chip->ecc.total); @@ -1684,9 +2269,13 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, unsigned int max_bitflips = 0; /* Read the OOB area first */ - chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + ret = nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); + if (ret) + return ret; + + ret = nand_read_page_op(chip, page, 0, NULL, 0); + if (ret) + return ret; ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, chip->ecc.total); @@ -1697,7 +2286,11 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, int stat; chip->ecc.hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, p, eccsize); + + ret = nand_read_data_op(chip, p, eccsize, false); + if (ret) + return ret; + chip->ecc.calculate(mtd, p, &ecc_calc[i]); stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); @@ -1734,7 +2327,7 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - int i, eccsize = chip->ecc.size; + int ret, i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; int eccpadbytes = eccbytes + chip->ecc.prepad + chip->ecc.postpad; @@ -1746,21 +2339,36 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, int stat; chip->ecc.hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, p, eccsize); + + ret = nand_read_data_op(chip, p, eccsize, false); + if (ret) + return ret; if (chip->ecc.prepad) { - chip->read_buf(mtd, oob, chip->ecc.prepad); + ret = nand_read_data_op(chip, oob, chip->ecc.prepad, + false); + if (ret) + return ret; + oob += chip->ecc.prepad; } chip->ecc.hwctl(mtd, NAND_ECC_READSYN); - chip->read_buf(mtd, oob, eccbytes); + + ret = nand_read_data_op(chip, oob, eccbytes, false); + if (ret) + return ret; + stat = chip->ecc.correct(mtd, p, oob, NULL); oob += eccbytes; if (chip->ecc.postpad) { - chip->read_buf(mtd, oob, chip->ecc.postpad); + ret = nand_read_data_op(chip, oob, chip->ecc.postpad, + false); + if (ret) + return ret; + oob += chip->ecc.postpad; } @@ -1784,8 +2392,11 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, /* Calculate remaining oob bytes */ i = mtd->oobsize - (oob - chip->oob_poi); - if (i) - chip->read_buf(mtd, oob, i); + if (i) { + ret = nand_read_data_op(chip, oob, i, false); + if (ret) + return ret; + } return max_bitflips; } @@ -1906,8 +2517,11 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, __func__, buf); read_retry: - if (nand_standard_page_accessors(&chip->ecc)) - chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); + if (nand_standard_page_accessors(&chip->ecc)) { + ret = nand_read_page_op(chip, page, 0, NULL, 0); + if (ret) + break; + } /* * Now read the page into the buffer. Absent an error, @@ -2066,9 +2680,7 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, */ int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page) { - chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - return 0; + return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); } EXPORT_SYMBOL(nand_read_oob_std); @@ -2086,25 +2698,43 @@ int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; int eccsize = chip->ecc.size; uint8_t *bufpoi = chip->oob_poi; - int i, toread, sndrnd = 0, pos; + int i, toread, sndrnd = 0, pos, ret; + + ret = nand_read_page_op(chip, page, chip->ecc.size, NULL, 0); + if (ret) + return ret; - chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page); for (i = 0; i < chip->ecc.steps; i++) { if (sndrnd) { + int ret; + pos = eccsize + i * (eccsize + chunk); if (mtd->writesize > 512) - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, pos, -1); + ret = nand_change_read_column_op(chip, pos, + NULL, 0, + false); else - chip->cmdfunc(mtd, NAND_CMD_READ0, pos, page); + ret = nand_read_page_op(chip, page, pos, NULL, + 0); + + if (ret) + return ret; } else sndrnd = 1; toread = min_t(int, length, chunk); - chip->read_buf(mtd, bufpoi, toread); + + ret = nand_read_data_op(chip, bufpoi, toread, false); + if (ret) + return ret; + bufpoi += toread; length -= toread; } - if (length > 0) - chip->read_buf(mtd, bufpoi, length); + if (length > 0) { + ret = nand_read_data_op(chip, bufpoi, length, false); + if (ret) + return ret; + } return 0; } @@ -2118,18 +2748,8 @@ EXPORT_SYMBOL(nand_read_oob_syndrome); */ int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page) { - int status = 0; - const uint8_t *buf = chip->oob_poi; - int length = mtd->oobsize; - - chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); - chip->write_buf(mtd, buf, length); - /* Send command to program the OOB data */ - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - - status = chip->waitfunc(mtd, chip); - - return status & NAND_STATUS_FAIL ? -EIO : 0; + return nand_prog_page_op(chip, page, mtd->writesize, chip->oob_poi, + mtd->oobsize); } EXPORT_SYMBOL(nand_write_oob_std); @@ -2145,7 +2765,7 @@ int nand_write_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, { int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; int eccsize = chip->ecc.size, length = mtd->oobsize; - int i, len, pos, status = 0, sndcmd = 0, steps = chip->ecc.steps; + int ret, i, len, pos, sndcmd = 0, steps = chip->ecc.steps; const uint8_t *bufpoi = chip->oob_poi; /* @@ -2159,7 +2779,10 @@ int nand_write_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, } else pos = eccsize; - chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page); + ret = nand_prog_page_begin_op(chip, page, pos, NULL, 0); + if (ret) + return ret; + for (i = 0; i < steps; i++) { if (sndcmd) { if (mtd->writesize <= 512) { @@ -2168,28 +2791,40 @@ int nand_write_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, len = eccsize; while (len > 0) { int num = min_t(int, len, 4); - chip->write_buf(mtd, (uint8_t *)&fill, - num); + + ret = nand_write_data_op(chip, &fill, + num, false); + if (ret) + return ret; + len -= num; } } else { pos = eccsize + i * (eccsize + chunk); - chip->cmdfunc(mtd, NAND_CMD_RNDIN, pos, -1); + ret = nand_change_write_column_op(chip, pos, + NULL, 0, + false); + if (ret) + return ret; } } else sndcmd = 1; len = min_t(int, length, chunk); - chip->write_buf(mtd, bufpoi, len); + + ret = nand_write_data_op(chip, bufpoi, len, false); + if (ret) + return ret; + bufpoi += len; length -= len; } - if (length > 0) - chip->write_buf(mtd, bufpoi, length); - - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - status = chip->waitfunc(mtd, chip); + if (length > 0) { + ret = nand_write_data_op(chip, bufpoi, length, false); + if (ret) + return ret; + } - return status & NAND_STATUS_FAIL ? -EIO : 0; + return nand_prog_page_end_op(chip); } EXPORT_SYMBOL(nand_write_oob_syndrome); @@ -2341,9 +2976,18 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required, int page) { - chip->write_buf(mtd, buf, mtd->writesize); - if (oob_required) - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + int ret; + + ret = nand_write_data_op(chip, buf, mtd->writesize, false); + if (ret) + return ret; + + if (oob_required) { + ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, + false); + if (ret) + return ret; + } return 0; } @@ -2367,29 +3011,46 @@ static int nand_write_page_raw_syndrome(struct mtd_info *mtd, int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; uint8_t *oob = chip->oob_poi; - int steps, size; + int steps, size, ret; for (steps = chip->ecc.steps; steps > 0; steps--) { - chip->write_buf(mtd, buf, eccsize); + ret = nand_write_data_op(chip, buf, eccsize, false); + if (ret) + return ret; + buf += eccsize; if (chip->ecc.prepad) { - chip->write_buf(mtd, oob, chip->ecc.prepad); + ret = nand_write_data_op(chip, oob, chip->ecc.prepad, + false); + if (ret) + return ret; + oob += chip->ecc.prepad; } - chip->write_buf(mtd, oob, eccbytes); + ret = nand_write_data_op(chip, oob, eccbytes, false); + if (ret) + return ret; + oob += eccbytes; if (chip->ecc.postpad) { - chip->write_buf(mtd, oob, chip->ecc.postpad); + ret = nand_write_data_op(chip, oob, chip->ecc.postpad, + false); + if (ret) + return ret; + oob += chip->ecc.postpad; } } size = mtd->oobsize - (oob - chip->oob_poi); - if (size) - chip->write_buf(mtd, oob, size); + if (size) { + ret = nand_write_data_op(chip, oob, size, false); + if (ret) + return ret; + } return 0; } @@ -2443,7 +3104,11 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { chip->ecc.hwctl(mtd, NAND_ECC_WRITE); - chip->write_buf(mtd, p, eccsize); + + ret = nand_write_data_op(chip, p, eccsize, false); + if (ret) + return ret; + chip->ecc.calculate(mtd, p, &ecc_calc[i]); } @@ -2452,7 +3117,9 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, if (ret) return ret; - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false); + if (ret) + return ret; return 0; } @@ -2488,7 +3155,9 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, chip->ecc.hwctl(mtd, NAND_ECC_WRITE); /* write data (untouched subpages already masked by 0xFF) */ - chip->write_buf(mtd, buf, ecc_size); + ret = nand_write_data_op(chip, buf, ecc_size, false); + if (ret) + return ret; /* mask ECC of un-touched subpages by padding 0xFF */ if ((step < start_step) || (step > end_step)) @@ -2515,7 +3184,9 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, return ret; /* write OOB buffer to NAND device */ - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + ret = nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false); + if (ret) + return ret; return 0; } @@ -2542,31 +3213,49 @@ static int nand_write_page_syndrome(struct mtd_info *mtd, int eccsteps = chip->ecc.steps; const uint8_t *p = buf; uint8_t *oob = chip->oob_poi; + int ret; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - chip->ecc.hwctl(mtd, NAND_ECC_WRITE); - chip->write_buf(mtd, p, eccsize); + + ret = nand_write_data_op(chip, p, eccsize, false); + if (ret) + return ret; if (chip->ecc.prepad) { - chip->write_buf(mtd, oob, chip->ecc.prepad); + ret = nand_write_data_op(chip, oob, chip->ecc.prepad, + false); + if (ret) + return ret; + oob += chip->ecc.prepad; } chip->ecc.calculate(mtd, p, oob); - chip->write_buf(mtd, oob, eccbytes); + + ret = nand_write_data_op(chip, oob, eccbytes, false); + if (ret) + return ret; + oob += eccbytes; if (chip->ecc.postpad) { - chip->write_buf(mtd, oob, chip->ecc.postpad); + ret = nand_write_data_op(chip, oob, chip->ecc.postpad, + false); + if (ret) + return ret; + oob += chip->ecc.postpad; } } /* Calculate remaining oob bytes */ i = mtd->oobsize - (oob - chip->oob_poi); - if (i) - chip->write_buf(mtd, oob, i); + if (i) { + ret = nand_write_data_op(chip, oob, i, false); + if (ret) + return ret; + } return 0; } @@ -2594,8 +3283,11 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, else subpage = 0; - if (nand_standard_page_accessors(&chip->ecc)) - chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + if (nand_standard_page_accessors(&chip->ecc)) { + status = nand_prog_page_begin_op(chip, page, 0, NULL, 0); + if (status) + return status; + } if (unlikely(raw)) status = chip->ecc.write_page_raw(mtd, chip, buf, @@ -2610,13 +3302,8 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, if (status < 0) return status; - if (nand_standard_page_accessors(&chip->ecc)) { - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - - status = chip->waitfunc(mtd, chip); - if (status & NAND_STATUS_FAIL) - return -EIO; - } + if (nand_standard_page_accessors(&chip->ecc)) + return nand_prog_page_end_op(chip); return 0; } @@ -2989,17 +3676,12 @@ out: static int single_erase(struct mtd_info *mtd, int page) { struct nand_chip *chip = mtd_to_nand(mtd); - int status; + unsigned int eraseblock; /* Send commands to erase a block */ - chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); - chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); + eraseblock = page >> (chip->phys_erase_shift - chip->page_shift); - status = chip->waitfunc(mtd, chip); - if (status < 0) - return status; - - return status & NAND_STATUS_FAIL ? -EIO : 0; + return nand_erase_op(chip, eraseblock); } /** @@ -3226,22 +3908,12 @@ static int nand_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len) static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip, int addr, uint8_t *subfeature_param) { - int status; - int i; - if (!chip->onfi_version || !(le16_to_cpu(chip->onfi_params.opt_cmd) & ONFI_OPT_CMD_SET_GET_FEATURES)) return -EINVAL; - chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1); - for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) - chip->write_byte(mtd, subfeature_param[i]); - - status = chip->waitfunc(mtd, chip); - if (status & NAND_STATUS_FAIL) - return -EIO; - return 0; + return nand_set_features_op(chip, addr, subfeature_param); } /** @@ -3254,17 +3926,12 @@ static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip, static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip, int addr, uint8_t *subfeature_param) { - int i; - if (!chip->onfi_version || !(le16_to_cpu(chip->onfi_params.opt_cmd) & ONFI_OPT_CMD_SET_GET_FEATURES)) return -EINVAL; - chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1); - for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) - *subfeature_param++ = chip->read_byte(mtd); - return 0; + return nand_get_features_op(chip, addr, subfeature_param); } /** @@ -3407,12 +4074,11 @@ static u16 onfi_crc16(u16 crc, u8 const *p, size_t len) static int nand_flash_detect_ext_param_page(struct nand_chip *chip, struct nand_onfi_params *p) { - struct mtd_info *mtd = nand_to_mtd(chip); struct onfi_ext_param_page *ep; struct onfi_ext_section *s; struct onfi_ext_ecc_info *ecc; uint8_t *cursor; - int ret = -EINVAL; + int ret; int len; int i; @@ -3422,14 +4088,18 @@ static int nand_flash_detect_ext_param_page(struct nand_chip *chip, return -ENOMEM; /* Send our own NAND_CMD_PARAM. */ - chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1); + ret = nand_read_param_page_op(chip, 0, NULL, 0); + if (ret) + goto ext_out; /* Use the Change Read Column command to skip the ONFI param pages. */ - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, - sizeof(*p) * p->num_of_param_pages , -1); + ret = nand_change_read_column_op(chip, + sizeof(*p) * p->num_of_param_pages, + ep, len, true); + if (ret) + goto ext_out; - /* Read out the Extended Parameter Page. */ - chip->read_buf(mtd, (uint8_t *)ep, len); + ret = -EINVAL; if ((onfi_crc16(ONFI_CRC_BASE, ((uint8_t *)ep) + 2, len - 2) != le16_to_cpu(ep->crc))) { pr_debug("fail in the CRC.\n"); @@ -3482,19 +4152,23 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) { struct mtd_info *mtd = nand_to_mtd(chip); struct nand_onfi_params *p = &chip->onfi_params; - int i, j; - int val; + char id[4]; + int i, ret, val; /* Try ONFI for unknown chip or LP */ - chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1); - if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' || - chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I') + ret = nand_readid_op(chip, 0x20, id, sizeof(id)); + if (ret || strncmp(id, "ONFI", 4)) + return 0; + + ret = nand_read_param_page_op(chip, 0, NULL, 0); + if (ret) return 0; - chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1); for (i = 0; i < 3; i++) { - for (j = 0; j < sizeof(*p); j++) - ((uint8_t *)p)[j] = chip->read_byte(mtd); + ret = nand_read_data_op(chip, p, sizeof(*p), true); + if (ret) + return 0; + if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) == le16_to_cpu(p->crc)) { break; @@ -3585,20 +4259,22 @@ static int nand_flash_detect_jedec(struct nand_chip *chip) struct mtd_info *mtd = nand_to_mtd(chip); struct nand_jedec_params *p = &chip->jedec_params; struct jedec_ecc_info *ecc; - int val; - int i, j; + char id[5]; + int i, val, ret; /* Try JEDEC for unknown chip or LP */ - chip->cmdfunc(mtd, NAND_CMD_READID, 0x40, -1); - if (chip->read_byte(mtd) != 'J' || chip->read_byte(mtd) != 'E' || - chip->read_byte(mtd) != 'D' || chip->read_byte(mtd) != 'E' || - chip->read_byte(mtd) != 'C') + ret = nand_readid_op(chip, 0x40, id, sizeof(id)); + if (ret || strncmp(id, "JEDEC", sizeof(id))) + return 0; + + ret = nand_read_param_page_op(chip, 0x40, NULL, 0); + if (ret) return 0; - chip->cmdfunc(mtd, NAND_CMD_PARAM, 0x40, -1); for (i = 0; i < 3; i++) { - for (j = 0; j < sizeof(*p); j++) - ((uint8_t *)p)[j] = chip->read_byte(mtd); + ret = nand_read_data_op(chip, p, sizeof(*p), true); + if (ret) + return 0; if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 510) == le16_to_cpu(p->crc)) @@ -3877,8 +4553,7 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) { const struct nand_manufacturer *manufacturer; struct mtd_info *mtd = nand_to_mtd(chip); - int busw; - int i; + int busw, ret; u8 *id_data = chip->id.data; u8 maf_id, dev_id; @@ -3886,17 +4561,21 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx) * after power-up. */ - nand_reset(chip, 0); + ret = nand_reset(chip, 0); + if (ret) + return ret; /* Select the device */ chip->select_chip(mtd, 0); /* Send the command for reading device ID */ - chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + ret = nand_readid_op(chip, 0, id_data, 2); + if (ret) + return ret; /* Read manufacturer and device IDs */ - maf_id = chip->read_byte(mtd); - dev_id = chip->read_byte(mtd); + maf_id = id_data[0]; + dev_id = id_data[1]; /* * Try again to make sure, as some systems the bus-hold or other @@ -3905,11 +4584,10 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) * not match, ignore the device completely. */ - chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); - /* Read entire ID string */ - for (i = 0; i < ARRAY_SIZE(chip->id.data); i++) - id_data[i] = chip->read_byte(mtd); + ret = nand_readid_op(chip, 0, id_data, sizeof(chip->id.data)); + if (ret) + return ret; if (id_data[0] != maf_id || id_data[1] != dev_id) { pr_info("second ID read did not match %02x,%02x against %02x,%02x\n", @@ -4236,15 +4914,16 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, /* Check for a chip array */ for (i = 1; i < maxchips; i++) { + u8 id[2]; + /* See comment in nand_get_flash_type for reset */ nand_reset(chip, i); chip->select_chip(mtd, i); /* Send the command for reading device ID */ - chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + nand_readid_op(chip, 0, id, sizeof(id)); /* Read manufacturer and device IDs */ - if (nand_maf_id != chip->read_byte(mtd) || - nand_dev_id != chip->read_byte(mtd)) { + if (nand_maf_id != id[0] || nand_dev_id != id[1]) { chip->select_chip(mtd, -1); break; } diff --git a/drivers/mtd/nand/nand_hynix.c b/drivers/mtd/nand/nand_hynix.c index 72d98cbff4ca..bae0da2aa2a8 100644 --- a/drivers/mtd/nand/nand_hynix.c +++ b/drivers/mtd/nand/nand_hynix.c @@ -66,16 +66,35 @@ struct hynix_read_retry_otp { }; static bool hynix_nand_has_valid_jedecid(struct nand_chip *chip) +{ + u8 jedecid[5] = { }; + int ret; + + ret = nand_readid_op(chip, 0x40, jedecid, sizeof(jedecid)); + if (ret) + return false; + + return !strncmp("JEDEC", jedecid, sizeof(jedecid)); +} + +static int hynix_nand_cmd_op(struct nand_chip *chip, u8 cmd) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + chip->cmdfunc(mtd, cmd, -1, -1); + + return 0; +} + +static int hynix_nand_reg_write_op(struct nand_chip *chip, u8 addr, u8 val) { struct mtd_info *mtd = nand_to_mtd(chip); - u8 jedecid[6] = { }; - int i = 0; + u16 column = ((u16)addr << 8) | addr; - chip->cmdfunc(mtd, NAND_CMD_READID, 0x40, -1); - for (i = 0; i < 5; i++) - jedecid[i] = chip->read_byte(mtd); + chip->cmdfunc(mtd, NAND_CMD_NONE, column, -1); + chip->write_byte(mtd, val); - return !strcmp("JEDEC", jedecid); + return 0; } static int hynix_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) @@ -83,13 +102,15 @@ static int hynix_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) struct nand_chip *chip = mtd_to_nand(mtd); struct hynix_nand *hynix = nand_get_manufacturer_data(chip); const u8 *values; - int i; + int i, ret; values = hynix->read_retry->values + (retry_mode * hynix->read_retry->nregs); /* Enter 'Set Hynix Parameters' mode */ - chip->cmdfunc(mtd, NAND_HYNIX_CMD_SET_PARAMS, -1, -1); + ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_SET_PARAMS); + if (ret) + return ret; /* * Configure the NAND in the requested read-retry mode. @@ -101,17 +122,14 @@ static int hynix_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) * probably tweaked at production in this case). */ for (i = 0; i < hynix->read_retry->nregs; i++) { - int column = hynix->read_retry->regs[i]; - - column |= column << 8; - chip->cmdfunc(mtd, NAND_CMD_NONE, column, -1); - chip->write_byte(mtd, values[i]); + ret = hynix_nand_reg_write_op(chip, hynix->read_retry->regs[i], + values[i]); + if (ret) + return ret; } /* Apply the new settings. */ - chip->cmdfunc(mtd, NAND_HYNIX_CMD_APPLY_PARAMS, -1, -1); - - return 0; + return hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_APPLY_PARAMS); } /** @@ -167,40 +185,63 @@ static int hynix_read_rr_otp(struct nand_chip *chip, const struct hynix_read_retry_otp *info, void *buf) { - struct mtd_info *mtd = nand_to_mtd(chip); - int i; + int i, ret; - chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + ret = nand_reset_op(chip); + if (ret) + return ret; - chip->cmdfunc(mtd, NAND_HYNIX_CMD_SET_PARAMS, -1, -1); + ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_SET_PARAMS); + if (ret) + return ret; for (i = 0; i < info->nregs; i++) { - int column = info->regs[i]; - - column |= column << 8; - chip->cmdfunc(mtd, NAND_CMD_NONE, column, -1); - chip->write_byte(mtd, info->values[i]); + ret = hynix_nand_reg_write_op(chip, info->regs[i], + info->values[i]); + if (ret) + return ret; } - chip->cmdfunc(mtd, NAND_HYNIX_CMD_APPLY_PARAMS, -1, -1); + ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_APPLY_PARAMS); + if (ret) + return ret; /* Sequence to enter OTP mode? */ - chip->cmdfunc(mtd, 0x17, -1, -1); - chip->cmdfunc(mtd, 0x04, -1, -1); - chip->cmdfunc(mtd, 0x19, -1, -1); + ret = hynix_nand_cmd_op(chip, 0x17); + if (ret) + return ret; + + ret = hynix_nand_cmd_op(chip, 0x4); + if (ret) + return ret; + + ret = hynix_nand_cmd_op(chip, 0x19); + if (ret) + return ret; /* Now read the page */ - chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, info->page); - chip->read_buf(mtd, buf, info->size); + ret = nand_read_page_op(chip, info->page, 0, buf, info->size); + if (ret) + return ret; /* Put everything back to normal */ - chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); - chip->cmdfunc(mtd, NAND_HYNIX_CMD_SET_PARAMS, 0x38, -1); - chip->write_byte(mtd, 0x0); - chip->cmdfunc(mtd, NAND_HYNIX_CMD_APPLY_PARAMS, -1, -1); - chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, -1); + ret = nand_reset_op(chip); + if (ret) + return ret; - return 0; + ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_SET_PARAMS); + if (ret) + return ret; + + ret = hynix_nand_reg_write_op(chip, 0x38, 0); + if (ret) + return ret; + + ret = hynix_nand_cmd_op(chip, NAND_HYNIX_CMD_APPLY_PARAMS); + if (ret) + return ret; + + return nand_read_page_op(chip, 0, 0, NULL, 0); } #define NAND_HYNIX_1XNM_RR_COUNT_OFFS 0 diff --git a/drivers/mtd/nand/nand_micron.c b/drivers/mtd/nand/nand_micron.c index abf6a3c376e8..bf2dc23e1c32 100644 --- a/drivers/mtd/nand/nand_micron.c +++ b/drivers/mtd/nand/nand_micron.c @@ -117,16 +117,28 @@ micron_nand_read_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - int status; - int max_bitflips = 0; + u8 status; + int ret, max_bitflips = 0; - micron_nand_on_die_ecc_setup(chip, true); + ret = micron_nand_on_die_ecc_setup(chip, true); + if (ret) + return ret; + + ret = nand_read_page_op(chip, page, 0, NULL, 0); + if (ret) + goto out; + + ret = nand_status_op(chip, &status); + if (ret) + goto out; + + ret = nand_exit_status_op(chip); + if (ret) + goto out; - chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); - chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); - status = chip->read_byte(mtd); if (status & NAND_STATUS_FAIL) mtd->ecc_stats.failed++; + /* * The internal ECC doesn't tell us the number of bitflips * that have been corrected, but tells us if it recommends to @@ -137,13 +149,12 @@ micron_nand_read_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip, else if (status & NAND_STATUS_WRITE_RECOMMENDED) max_bitflips = chip->ecc.strength; - chip->cmdfunc(mtd, NAND_CMD_READ0, -1, -1); - - nand_read_page_raw(mtd, chip, buf, oob_required, page); + ret = nand_read_page_raw(mtd, chip, buf, oob_required, page); +out: micron_nand_on_die_ecc_setup(chip, false); - return max_bitflips; + return ret ? ret : max_bitflips; } static int @@ -151,18 +162,26 @@ micron_nand_write_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required, int page) { - int status; + int ret; - micron_nand_on_die_ecc_setup(chip, true); + ret = micron_nand_on_die_ecc_setup(chip, true); + if (ret) + return ret; - chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); - nand_write_page_raw(mtd, chip, buf, oob_required, page); - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - status = chip->waitfunc(mtd, chip); + ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); + if (ret) + goto out; + ret = nand_write_page_raw(mtd, chip, buf, oob_required, page); + if (ret) + return ret; + + ret = nand_prog_page_end_op(chip); + +out: micron_nand_on_die_ecc_setup(chip, false); - return status & NAND_STATUS_FAIL ? -EIO : 0; + return ret; } static int @@ -171,10 +190,13 @@ micron_nand_read_page_raw_on_die_ecc(struct mtd_info *mtd, uint8_t *buf, int oob_required, int page) { - chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); - nand_read_page_raw(mtd, chip, buf, oob_required, page); + int ret; - return 0; + ret = nand_read_page_op(chip, page, 0, NULL, 0); + if (ret) + return ret; + + return nand_read_page_raw(mtd, chip, buf, oob_required, page); } static int @@ -183,14 +205,17 @@ micron_nand_write_page_raw_on_die_ecc(struct mtd_info *mtd, const uint8_t *buf, int oob_required, int page) { - int status; + int ret; - chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); - nand_write_page_raw(mtd, chip, buf, oob_required, page); - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - status = chip->waitfunc(mtd, chip); + ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); + if (ret) + return ret; + + ret = nand_write_page_raw(mtd, chip, buf, oob_required, page); + if (ret) + return ret; - return status & NAND_STATUS_FAIL ? -EIO : 0; + return nand_prog_page_end_op(chip); } enum { diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index dad438c4906a..6e1b209cd5a7 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1647,10 +1647,10 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, chip->read_buf(mtd, buf, mtd->writesize); /* Read oob bytes */ - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, - mtd->writesize + BADBLOCK_MARKER_LENGTH, -1); - chip->read_buf(mtd, chip->oob_poi + BADBLOCK_MARKER_LENGTH, - chip->ecc.total); + nand_change_read_column_op(chip, + mtd->writesize + BADBLOCK_MARKER_LENGTH, + chip->oob_poi + BADBLOCK_MARKER_LENGTH, + chip->ecc.total, false); /* Calculate ecc bytes */ omap_calculate_ecc_bch_multi(mtd, buf, ecc_calc); diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 90b9a9ccbe60..28bcdf64c1fc 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -520,15 +520,13 @@ static int pxa3xx_nand_init_timings_compat(struct pxa3xx_nand_host *host, struct nand_chip *chip = &host->chip; struct pxa3xx_nand_info *info = host->info_data; const struct pxa3xx_nand_flash *f = NULL; - struct mtd_info *mtd = nand_to_mtd(&host->chip); int i, id, ntypes; + u8 idbuf[2]; ntypes = ARRAY_SIZE(builtin_flash_types); - chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); - - id = chip->read_byte(mtd); - id |= chip->read_byte(mtd) << 0x8; + nand_readid_op(chip, 0, idbuf, sizeof(idbuf)); + id = idbuf[0] | (idbuf[1] << 8); for (i = 0; i < ntypes; i++) { f = &builtin_flash_types[i]; diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c index 2656c1ac5646..e34313ecd903 100644 --- a/drivers/mtd/nand/qcom_nandc.c +++ b/drivers/mtd/nand/qcom_nandc.c @@ -1990,7 +1990,7 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, struct nand_ecc_ctrl *ecc = &chip->ecc; u8 *oob = chip->oob_poi; int data_size, oob_size; - int ret, status = 0; + int ret; host->use_ecc = true; @@ -2027,11 +2027,7 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, return -EIO; } - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - - status = chip->waitfunc(mtd, chip); - - return status & NAND_STATUS_FAIL ? -EIO : 0; + return nand_prog_page_end_op(chip); } static int qcom_nandc_block_bad(struct mtd_info *mtd, loff_t ofs) @@ -2081,7 +2077,7 @@ static int qcom_nandc_block_markbad(struct mtd_info *mtd, loff_t ofs) struct qcom_nand_host *host = to_qcom_nand_host(chip); struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); struct nand_ecc_ctrl *ecc = &chip->ecc; - int page, ret, status = 0; + int page, ret; clear_read_regs(nandc); clear_bam_transaction(nandc); @@ -2114,11 +2110,7 @@ static int qcom_nandc_block_markbad(struct mtd_info *mtd, loff_t ofs) return -EIO; } - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - - status = chip->waitfunc(mtd, chip); - - return status & NAND_STATUS_FAIL ? -EIO : 0; + return nand_prog_page_end_op(chip); } /* diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c index fc9287af4614..595635b9e9de 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/r852.c @@ -364,7 +364,7 @@ static int r852_wait(struct mtd_info *mtd, struct nand_chip *chip) struct r852_device *dev = nand_get_controller_data(chip); unsigned long timeout; - int status; + u8 status; timeout = jiffies + (chip->state == FL_ERASING ? msecs_to_jiffies(400) : msecs_to_jiffies(20)); @@ -373,8 +373,7 @@ static int r852_wait(struct mtd_info *mtd, struct nand_chip *chip) if (chip->dev_ready(mtd)) break; - chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); - status = (int)chip->read_byte(mtd); + nand_status_op(chip, &status); /* Unfortunelly, no way to send detailed error status... */ if (dev->dma_error) { @@ -522,9 +521,7 @@ exit: static int r852_read_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) { - chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - return 0; + return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); } /* @@ -1046,7 +1043,7 @@ static int r852_resume(struct device *device) if (dev->card_registred) { r852_engine_enable(dev); dev->chip->select_chip(mtd, 0); - dev->chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + nand_reset_op(dev->chip); dev->chip->select_chip(mtd, -1); } diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 82244be3e766..da5cc36f4c30 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -958,12 +958,12 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, int ret; if (*cur_off != data_off) - nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1); + nand_change_read_column_op(nand, data_off, NULL, 0, false); sunxi_nfc_randomizer_read_buf(mtd, NULL, ecc->size, false, page); if (data_off + ecc->size != oob_off) - nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); + nand_change_read_column_op(nand, oob_off, NULL, 0, false); ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); if (ret) @@ -991,16 +991,15 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, * Re-read the data with the randomizer disabled to identify * bitflips in erased pages. */ - if (nand->options & NAND_NEED_SCRAMBLING) { - nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1); - nand->read_buf(mtd, data, ecc->size); - } else { + if (nand->options & NAND_NEED_SCRAMBLING) + nand_change_read_column_op(nand, data_off, data, + ecc->size, false); + else memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); - } - nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); - nand->read_buf(mtd, oob, ecc->bytes + 4); + nand_change_read_column_op(nand, oob_off, oob, ecc->bytes + 4, + false); ret = nand_check_erased_ecc_chunk(data, ecc->size, oob, ecc->bytes + 4, @@ -1011,7 +1010,8 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); if (oob_required) { - nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); + nand_change_read_column_op(nand, oob_off, NULL, 0, + false); sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4, true, page); @@ -1038,8 +1038,8 @@ static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd, return; if (!cur_off || *cur_off != offset) - nand->cmdfunc(mtd, NAND_CMD_RNDOUT, - offset + mtd->writesize, -1); + nand_change_read_column_op(nand, mtd->writesize, NULL, 0, + false); if (!randomize) sunxi_nfc_read_buf(mtd, oob + offset, len); @@ -1116,9 +1116,9 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct mtd_info *mtd, uint8_t *buf, if (oob_required && !erased) { /* TODO: use DMA to retrieve OOB */ - nand->cmdfunc(mtd, NAND_CMD_RNDOUT, - mtd->writesize + oob_off, -1); - nand->read_buf(mtd, oob, ecc->bytes + 4); + nand_change_read_column_op(nand, + mtd->writesize + oob_off, + oob, ecc->bytes + 4, false); sunxi_nfc_hw_ecc_get_prot_oob_bytes(mtd, oob, i, !i, page); @@ -1143,18 +1143,17 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct mtd_info *mtd, uint8_t *buf, /* * Re-read the data with the randomizer disabled to * identify bitflips in erased pages. + * TODO: use DMA to read page in raw mode */ - if (randomized) { - /* TODO: use DMA to read page in raw mode */ - nand->cmdfunc(mtd, NAND_CMD_RNDOUT, - data_off, -1); - nand->read_buf(mtd, data, ecc->size); - } + if (randomized) + nand_change_read_column_op(nand, data_off, + data, ecc->size, + false); /* TODO: use DMA to retrieve OOB */ - nand->cmdfunc(mtd, NAND_CMD_RNDOUT, - mtd->writesize + oob_off, -1); - nand->read_buf(mtd, oob, ecc->bytes + 4); + nand_change_read_column_op(nand, + mtd->writesize + oob_off, + oob, ecc->bytes + 4, false); ret = nand_check_erased_ecc_chunk(data, ecc->size, oob, ecc->bytes + 4, @@ -1187,12 +1186,12 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, int ret; if (data_off != *cur_off) - nand->cmdfunc(mtd, NAND_CMD_RNDIN, data_off, -1); + nand_change_write_column_op(nand, data_off, NULL, 0, false); sunxi_nfc_randomizer_write_buf(mtd, data, ecc->size, false, page); if (data_off + ecc->size != oob_off) - nand->cmdfunc(mtd, NAND_CMD_RNDIN, oob_off, -1); + nand_change_write_column_op(nand, oob_off, NULL, 0, false); ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); if (ret) @@ -1228,8 +1227,8 @@ static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd, return; if (!cur_off || *cur_off != offset) - nand->cmdfunc(mtd, NAND_CMD_RNDIN, - offset + mtd->writesize, -1); + nand_change_write_column_op(nand, offset + mtd->writesize, + NULL, 0, false); sunxi_nfc_randomizer_write_buf(mtd, oob + offset, len, false, page); @@ -1285,7 +1284,7 @@ static int sunxi_nfc_hw_ecc_read_page_dma(struct mtd_info *mtd, return ret; /* Fallback to PIO mode */ - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1); + nand_change_read_column_op(chip, 0, NULL, 0, false); return sunxi_nfc_hw_ecc_read_page(mtd, chip, buf, oob_required, page); } @@ -1335,7 +1334,7 @@ static int sunxi_nfc_hw_ecc_read_subpage_dma(struct mtd_info *mtd, return ret; /* Fallback to PIO mode */ - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1); + nand_change_read_column_op(chip, 0, NULL, 0, false); return sunxi_nfc_hw_ecc_read_subpage(mtd, chip, data_offs, readlen, buf, page); @@ -1540,7 +1539,7 @@ static int sunxi_nfc_hw_common_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) { - chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + nand_read_page_op(chip, page, 0, NULL, 0); chip->pagebuf = -1; @@ -1551,9 +1550,9 @@ static int sunxi_nfc_hw_common_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) { - int ret, status; + int ret; - chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0, page); + nand_prog_page_begin_op(chip, page, 0, NULL, 0); chip->pagebuf = -1; @@ -1563,11 +1562,7 @@ static int sunxi_nfc_hw_common_ecc_write_oob(struct mtd_info *mtd, return ret; /* Send command to program the OOB data */ - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - - status = chip->waitfunc(mtd, chip); - - return status & NAND_STATUS_FAIL ? -EIO : 0; + return nand_prog_page_end_op(chip); } static const s32 tWB_lut[] = {6, 12, 16, 20}; diff --git a/drivers/mtd/nand/tango_nand.c b/drivers/mtd/nand/tango_nand.c index 766906f03943..97a300b46b1d 100644 --- a/drivers/mtd/nand/tango_nand.c +++ b/drivers/mtd/nand/tango_nand.c @@ -329,7 +329,7 @@ static void aux_read(struct nand_chip *chip, u8 **buf, int len, int *pos) if (!*buf) { /* skip over "len" bytes */ - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, *pos, -1); + nand_change_read_column_op(chip, *pos, NULL, 0, false); } else { tango_read_buf(mtd, *buf, len); *buf += len; @@ -344,7 +344,7 @@ static void aux_write(struct nand_chip *chip, const u8 **buf, int len, int *pos) if (!*buf) { /* skip over "len" bytes */ - chip->cmdfunc(mtd, NAND_CMD_RNDIN, *pos, -1); + nand_change_write_column_op(chip, *pos, NULL, 0, false); } else { tango_write_buf(mtd, *buf, len); *buf += len; @@ -427,7 +427,7 @@ static void raw_write(struct nand_chip *chip, const u8 *buf, const u8 *oob) static int tango_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, u8 *buf, int oob_required, int page) { - chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + nand_read_page_op(chip, page, 0, NULL, 0); raw_read(chip, buf, chip->oob_poi); return 0; } @@ -435,23 +435,15 @@ static int tango_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, static int tango_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, const u8 *buf, int oob_required, int page) { - int status; - - chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0, page); + nand_prog_page_begin_op(chip, page, 0, NULL, 0); raw_write(chip, buf, chip->oob_poi); - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - - status = chip->waitfunc(mtd, chip); - if (status & NAND_STATUS_FAIL) - return -EIO; - - return 0; + return nand_prog_page_end_op(chip); } static int tango_read_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) { - chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + nand_read_page_op(chip, page, 0, NULL, 0); raw_read(chip, NULL, chip->oob_poi); return 0; } @@ -459,11 +451,9 @@ static int tango_read_oob(struct mtd_info *mtd, struct nand_chip *chip, static int tango_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) { - chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0, page); + nand_prog_page_begin_op(chip, page, 0, NULL, 0); raw_write(chip, NULL, chip->oob_poi); - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - chip->waitfunc(mtd, chip); - return 0; + return nand_prog_page_end_op(chip); } static int oob_ecc(struct mtd_info *mtd, int idx, struct mtd_oob_region *res) diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c index 84dbf32332e1..dcaa924502de 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/tmio_nand.c @@ -192,6 +192,7 @@ tmio_nand_wait(struct mtd_info *mtd, struct nand_chip *nand_chip) { struct tmio_nand *tmio = mtd_to_tmio(mtd); long timeout; + u8 status; /* enable RDYREQ interrupt */ tmio_iowrite8(0x0f, tmio->fcr + FCR_ISR); @@ -212,8 +213,8 @@ tmio_nand_wait(struct mtd_info *mtd, struct nand_chip *nand_chip) dev_warn(&tmio->dev->dev, "timeout waiting for interrupt\n"); } - nand_chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); - return nand_chip->read_byte(mtd); + nand_status_op(nand_chip, &status); + return status; } /* diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 749bb08c4772..fd99d5137d71 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1316,6 +1316,35 @@ int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, /* Reset and initialize a NAND device */ int nand_reset(struct nand_chip *chip, int chipnr); +/* NAND operation helpers */ +int nand_reset_op(struct nand_chip *chip); +int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf, + unsigned int len); +int nand_status_op(struct nand_chip *chip, u8 *status); +int nand_exit_status_op(struct nand_chip *chip); +int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock); +int nand_read_page_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, void *buf, unsigned int len); +int nand_change_read_column_op(struct nand_chip *chip, + unsigned int offset_in_page, void *buf, + unsigned int len, bool force_8bit); +int nand_read_oob_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, void *buf, unsigned int len); +int nand_prog_page_begin_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, const void *buf, + unsigned int len); +int nand_prog_page_end_op(struct nand_chip *chip); +int nand_prog_page_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, const void *buf, + unsigned int len); +int nand_change_write_column_op(struct nand_chip *chip, + unsigned int offset_in_page, const void *buf, + unsigned int len, bool force_8bit); +int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len, + bool force_8bit); +int nand_write_data_op(struct nand_chip *chip, const void *buf, + unsigned int len, bool force_8bit); + /* Free resources held by the NAND device */ void nand_cleanup(struct nand_chip *chip); -- cgit v1.2.3 From 25f815f66a141436df8a4c45e5d2765272aea2ac Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 30 Nov 2017 18:01:30 +0100 Subject: mtd: nand: force drivers to explicitly send READ/PROG commands The core currently send the READ0 and SEQIN+PAGEPROG commands in nand_do_read/write_ops(). This is inconsistent with ->read/write_oob[_raw]() hooks behavior which are expected to send these commands. There's already a flag (NAND_ECC_CUSTOM_PAGE_ACCESS) to inform the core that a specific controller wants to send the READ/SEQIN+PAGEPROG commands on its own, but it's an opt-in flag, and existing drivers are unlikely to be updated to pass it. Moreover, some controllers cannot dissociate the READ/PAGEPROG commands from the associated data transfer and ECC engine activation, and developers have to hack things in their ->cmdfunc() implementation to handle such complex cases, or have to accept the perf penalty of sending twice the same command. To address this problem we are planning on adding a new interface which is passed all information about a NAND operation (including the amount of data to transfer) and replacing all calls to ->cmdfunc() to calls to this new ->exec_op() hook. But, in order to do that, we need to have all ->cmdfunc() calls placed near their associated ->read/write_buf/byte() calls. Modify the core and relevant drivers to make NAND_ECC_CUSTOM_PAGE_ACCESS the default case, and remove this flag. Signed-off-by: Boris Brezillon [miquel.raynal@free-electrons.com: tested, fixed and rebased on nand/next] Signed-off-by: Miquel Raynal Acked-by: Masahiro Yamada --- drivers/mtd/nand/atmel/nand-controller.c | 7 ++- drivers/mtd/nand/bf5xx_nand.c | 6 +- drivers/mtd/nand/brcmnand/brcmnand.c | 13 +++- drivers/mtd/nand/cafe_nand.c | 6 +- drivers/mtd/nand/denali.c | 1 - drivers/mtd/nand/docg4.c | 12 ++-- drivers/mtd/nand/fsl_elbc_nand.c | 10 +-- drivers/mtd/nand/fsl_ifc_nand.c | 6 +- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 31 +++++----- drivers/mtd/nand/hisi504_nand.c | 6 +- drivers/mtd/nand/lpc32xx_mlc.c | 5 +- drivers/mtd/nand/lpc32xx_slc.c | 11 +++- drivers/mtd/nand/mtk_nand.c | 22 +++---- drivers/mtd/nand/nand_base.c | 87 +++++++++++---------------- drivers/mtd/nand/nand_micron.c | 56 ++--------------- drivers/mtd/nand/omap2.c | 10 ++- drivers/mtd/nand/pxa3xx_nand.c | 6 +- drivers/mtd/nand/qcom_nandc.c | 11 ++++ drivers/mtd/nand/sh_flctl.c | 6 +- drivers/mtd/nand/sunxi_nand.c | 34 +++++++---- drivers/mtd/nand/tango_nand.c | 1 - drivers/mtd/nand/vf610_nfc.c | 6 +- drivers/staging/mt29f_spinand/mt29f_spinand.c | 5 +- include/linux/mtd/rawnand.h | 11 ---- 24 files changed, 171 insertions(+), 198 deletions(-) (limited to 'drivers/mtd/nand/nand_base.c') diff --git a/drivers/mtd/nand/atmel/nand-controller.c b/drivers/mtd/nand/atmel/nand-controller.c index e81fdd2d47b1..b2f00b398490 100644 --- a/drivers/mtd/nand/atmel/nand-controller.c +++ b/drivers/mtd/nand/atmel/nand-controller.c @@ -841,6 +841,8 @@ static int atmel_nand_pmecc_write_pg(struct nand_chip *chip, const u8 *buf, struct atmel_nand *nand = to_atmel_nand(chip); int ret; + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw); if (ret) return ret; @@ -857,7 +859,7 @@ static int atmel_nand_pmecc_write_pg(struct nand_chip *chip, const u8 *buf, atmel_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); - return 0; + return nand_prog_page_end_op(chip); } static int atmel_nand_pmecc_write_page(struct mtd_info *mtd, @@ -881,6 +883,8 @@ static int atmel_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf, struct mtd_info *mtd = nand_to_mtd(chip); int ret; + nand_read_page_op(chip, page, 0, NULL, 0); + ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw); if (ret) return ret; @@ -1178,7 +1182,6 @@ static int atmel_hsmc_nand_ecc_init(struct atmel_nand *nand) chip->ecc.write_page = atmel_hsmc_nand_pmecc_write_page; chip->ecc.read_page_raw = atmel_hsmc_nand_pmecc_read_page_raw; chip->ecc.write_page_raw = atmel_hsmc_nand_pmecc_write_page_raw; - chip->ecc.options |= NAND_ECC_CUSTOM_PAGE_ACCESS; return 0; } diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 5655dca6ce43..87bbd177b3e5 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -572,6 +572,8 @@ static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd, static int bf5xx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { + nand_read_page_op(chip, page, 0, NULL, 0); + bf5xx_nand_read_buf(mtd, buf, mtd->writesize); bf5xx_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -582,10 +584,10 @@ static int bf5xx_nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required, int page) { - bf5xx_nand_write_buf(mtd, buf, mtd->writesize); + nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); bf5xx_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); - return 0; + return nand_prog_page_end_op(chip); } /* diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c index 3f441096a14c..e6879d4d53ca 100644 --- a/drivers/mtd/nand/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/brcmnand/brcmnand.c @@ -1689,7 +1689,6 @@ static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd, sas = mtd->oobsize / chip->ecc.steps; /* read without ecc for verification */ - nand_read_page_op(chip, page, 0, NULL, 0); ret = chip->ecc.read_page_raw(mtd, chip, buf, true, page); if (ret) return ret; @@ -1793,6 +1792,8 @@ static int brcmnand_read_page(struct mtd_info *mtd, struct nand_chip *chip, struct brcmnand_host *host = nand_get_controller_data(chip); u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL; + nand_read_page_op(chip, page, 0, NULL, 0); + return brcmnand_read(mtd, chip, host->last_addr, mtd->writesize >> FC_SHIFT, (u32 *)buf, oob); } @@ -1804,6 +1805,8 @@ static int brcmnand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL; int ret; + nand_read_page_op(chip, page, 0, NULL, 0); + brcmnand_set_ecc_enabled(host, 0); ret = brcmnand_read(mtd, chip, host->last_addr, mtd->writesize >> FC_SHIFT, (u32 *)buf, oob); @@ -1909,8 +1912,10 @@ static int brcmnand_write_page(struct mtd_info *mtd, struct nand_chip *chip, struct brcmnand_host *host = nand_get_controller_data(chip); void *oob = oob_required ? chip->oob_poi : NULL; + nand_prog_page_begin_op(chip, page, 0, NULL, 0); brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob); - return 0; + + return nand_prog_page_end_op(chip); } static int brcmnand_write_page_raw(struct mtd_info *mtd, @@ -1920,10 +1925,12 @@ static int brcmnand_write_page_raw(struct mtd_info *mtd, struct brcmnand_host *host = nand_get_controller_data(chip); void *oob = oob_required ? chip->oob_poi : NULL; + nand_prog_page_begin_op(chip, page, 0, NULL, 0); brcmnand_set_ecc_enabled(host, 0); brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob); brcmnand_set_ecc_enabled(host, 1); - return 0; + + return nand_prog_page_end_op(chip); } static int brcmnand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 95c2cfa68b66..de36762e3058 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -383,7 +383,7 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, cafe_readl(cafe, NAND_ECC_RESULT), cafe_readl(cafe, NAND_ECC_SYN01)); - chip->read_buf(mtd, buf, mtd->writesize); + nand_read_page_op(chip, page, 0, buf, mtd->writesize); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); if (checkecc && cafe_readl(cafe, NAND_ECC_RESULT) & (1<<18)) { @@ -541,13 +541,13 @@ static int cafe_nand_write_page_lowlevel(struct mtd_info *mtd, { struct cafe_priv *cafe = nand_get_controller_data(chip); - chip->write_buf(mtd, buf, mtd->writesize); + nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); /* Set up ECC autogeneration */ cafe->ctl2 |= (1<<30); - return 0; + return nand_prog_page_end_op(chip); } static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs) diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index d5c80d617854..47a253737bb2 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -1358,7 +1358,6 @@ int denali_init(struct denali_nand_info *denali) chip->read_buf = denali_read_buf; chip->write_buf = denali_write_buf; } - chip->ecc.options |= NAND_ECC_CUSTOM_PAGE_ACCESS; chip->ecc.read_page = denali_read_page; chip->ecc.read_page_raw = denali_read_page_raw; chip->ecc.write_page = denali_write_page; diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c index 5a27f56dafdc..72f1327c4430 100644 --- a/drivers/mtd/nand/docg4.c +++ b/drivers/mtd/nand/docg4.c @@ -785,6 +785,8 @@ static int read_page(struct mtd_info *mtd, struct nand_chip *nand, dev_dbg(doc->dev, "%s: page %08x\n", __func__, page); + nand_read_page_op(nand, page, 0, NULL, 0); + writew(DOC_ECCCONF0_READ_MODE | DOC_ECCCONF0_ECC_ENABLE | DOC_ECCCONF0_UNKNOWN | @@ -948,7 +950,7 @@ static int docg4_erase_block(struct mtd_info *mtd, int page) } static int write_page(struct mtd_info *mtd, struct nand_chip *nand, - const uint8_t *buf, bool use_ecc) + const uint8_t *buf, int page, bool use_ecc) { struct docg4_priv *doc = nand_get_controller_data(nand); void __iomem *docptr = doc->virtadr; @@ -956,6 +958,8 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *nand, dev_dbg(doc->dev, "%s...\n", __func__); + nand_prog_page_begin_op(nand, page, 0, NULL, 0); + writew(DOC_ECCCONF0_ECC_ENABLE | DOC_ECCCONF0_UNKNOWN | DOCG4_BCH_SIZE, @@ -1000,19 +1004,19 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *nand, writew(0, docptr + DOC_DATAEND); write_nop(docptr); - return 0; + return nand_prog_page_end_op(nand); } static int docg4_write_page_raw(struct mtd_info *mtd, struct nand_chip *nand, const uint8_t *buf, int oob_required, int page) { - return write_page(mtd, nand, buf, false); + return write_page(mtd, nand, buf, page, false); } static int docg4_write_page(struct mtd_info *mtd, struct nand_chip *nand, const uint8_t *buf, int oob_required, int page) { - return write_page(mtd, nand, buf, true); + return write_page(mtd, nand, buf, page, true); } static int docg4_write_oob(struct mtd_info *mtd, struct nand_chip *nand, diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 17db2f90aa2c..8b6dcd739ecb 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -713,7 +713,7 @@ static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip, struct fsl_lbc_ctrl *ctrl = priv->ctrl; struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; - fsl_elbc_read_buf(mtd, buf, mtd->writesize); + nand_read_page_op(chip, page, 0, buf, mtd->writesize); if (oob_required) fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -729,10 +729,10 @@ static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip, static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required, int page) { - fsl_elbc_write_buf(mtd, buf, mtd->writesize); + nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); - return 0; + return nand_prog_page_end_op(chip); } /* ECC will be calculated automatically, and errors will be detected in @@ -742,10 +742,10 @@ static int fsl_elbc_write_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t offset, uint32_t data_len, const uint8_t *buf, int oob_required, int page) { + nand_prog_page_begin_op(chip, page, 0, NULL, 0); fsl_elbc_write_buf(mtd, buf, mtd->writesize); fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); - - return 0; + return nand_prog_page_end_op(chip); } static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index bbdd68a54d68..4872a7ba6503 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -688,7 +688,7 @@ static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip, struct fsl_ifc_ctrl *ctrl = priv->ctrl; struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; - fsl_ifc_read_buf(mtd, buf, mtd->writesize); + nand_read_page_op(chip, page, 0, buf, mtd->writesize); if (oob_required) fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -711,10 +711,10 @@ static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip, static int fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required, int page) { - fsl_ifc_write_buf(mtd, buf, mtd->writesize); + nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize); - return 0; + return nand_prog_page_end_op(chip); } static int fsl_ifc_chip_init_tail(struct mtd_info *mtd) diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 63a425ced4cd..3c3f3f58fdcb 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -1043,6 +1043,8 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, unsigned int max_bitflips = 0; int ret; + nand_read_page_op(chip, page, 0, NULL, 0); + dev_dbg(this->dev, "page number is : %d\n", page); ret = read_page_prepare(this, buf, nfc_geo->payload_size, this->payload_virt, this->payload_phys, @@ -1220,12 +1222,12 @@ static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, meta = geo->metadata_size; if (first) { col = meta + (size + ecc_parity_size) * first; - nand_change_read_column_op(chip, col, NULL, 0, false); - meta = 0; buf = buf + first * size; } + nand_read_page_op(chip, page, col, NULL, 0); + /* Save the old environment */ r1_old = r1_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT0); r2_old = r2_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT1); @@ -1277,6 +1279,9 @@ static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, int ret; dev_dbg(this->dev, "ecc write page.\n"); + + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + if (this->swap_block_mark) { /* * If control arrives here, we're doing block mark swapping. @@ -1338,7 +1343,10 @@ exit_auxiliary: payload_virt, payload_phys); } - return 0; + if (ret) + return ret; + + return nand_prog_page_end_op(chip); } /* @@ -1472,8 +1480,8 @@ static int gpmi_ecc_read_page_raw(struct mtd_info *mtd, uint8_t *oob = chip->oob_poi; int step; - chip->read_buf(mtd, tmp_buf, - mtd->writesize + mtd->oobsize); + nand_read_page_op(chip, page, 0, tmp_buf, + mtd->writesize + mtd->oobsize); /* * If required, swap the bad block marker and the data stored in the @@ -1609,24 +1617,19 @@ static int gpmi_ecc_write_page_raw(struct mtd_info *mtd, if (this->swap_block_mark) swap(tmp_buf[0], tmp_buf[mtd->writesize]); - chip->write_buf(mtd, tmp_buf, mtd->writesize + mtd->oobsize); - - return 0; + return nand_prog_page_op(chip, page, 0, tmp_buf, + mtd->writesize + mtd->oobsize); } static int gpmi_ecc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, int page) { - nand_read_page_op(chip, page, 0, NULL, 0); - return gpmi_ecc_read_page_raw(mtd, chip, NULL, 1, page); } static int gpmi_ecc_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, int page) { - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - return gpmi_ecc_write_page_raw(mtd, chip, NULL, 1, page); } @@ -1798,9 +1801,7 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this) /* Write the first page of the current stride. */ dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page); - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - chip->ecc.write_page_raw(mtd, chip, buffer, 0, page); - status = nand_prog_page_end_op(chip); + status = chip->ecc.write_page_raw(mtd, chip, buffer, 0, page); if (status) dev_err(dev, "[%s] Write failed.\n", __func__); } diff --git a/drivers/mtd/nand/hisi504_nand.c b/drivers/mtd/nand/hisi504_nand.c index 184d765c8bbe..cb862793ab6d 100644 --- a/drivers/mtd/nand/hisi504_nand.c +++ b/drivers/mtd/nand/hisi504_nand.c @@ -544,7 +544,7 @@ static int hisi_nand_read_page_hwecc(struct mtd_info *mtd, int max_bitflips = 0, stat = 0, stat_max = 0, status_ecc; int stat_1, stat_2; - chip->read_buf(mtd, buf, mtd->writesize); + nand_read_page_op(chip, page, 0, buf, mtd->writesize); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); /* errors which can not be corrected by ECC */ @@ -589,11 +589,11 @@ static int hisi_nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required, int page) { - chip->write_buf(mtd, buf, mtd->writesize); + nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); if (oob_required) chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - return 0; + return nand_prog_page_end_op(chip); } static void hisi_nfc_host_init(struct hinfc_host *host) diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/lpc32xx_mlc.c index 31cb3b2967b9..e357948a7505 100644 --- a/drivers/mtd/nand/lpc32xx_mlc.c +++ b/drivers/mtd/nand/lpc32xx_mlc.c @@ -522,6 +522,8 @@ static int lpc32xx_write_page_lowlevel(struct mtd_info *mtd, memcpy(dma_buf, buf, mtd->writesize); } + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + for (i = 0; i < host->mlcsubpages; i++) { /* Start Encode */ writeb(0x00, MLC_ECC_ENC_REG(host->io_base)); @@ -550,7 +552,8 @@ static int lpc32xx_write_page_lowlevel(struct mtd_info *mtd, /* Wait for Controller Ready */ lpc32xx_waitfunc_controller(mtd, chip); } - return 0; + + return nand_prog_page_end_op(chip); } static int lpc32xx_read_oob(struct mtd_info *mtd, struct nand_chip *chip, diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c index 2b96c281b1a2..5f7cc6da0a7f 100644 --- a/drivers/mtd/nand/lpc32xx_slc.c +++ b/drivers/mtd/nand/lpc32xx_slc.c @@ -686,6 +686,8 @@ static int lpc32xx_nand_write_page_syndrome(struct mtd_info *mtd, uint8_t *pb; int error; + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + /* Write data, calculate ECC on outbound data */ error = lpc32xx_xfer(mtd, (uint8_t *)buf, chip->ecc.steps, 0); if (error) @@ -704,7 +706,8 @@ static int lpc32xx_nand_write_page_syndrome(struct mtd_info *mtd, /* Write ECC data to device */ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - return 0; + + return nand_prog_page_end_op(chip); } /* @@ -717,9 +720,11 @@ static int lpc32xx_nand_write_page_raw_syndrome(struct mtd_info *mtd, int oob_required, int page) { /* Raw writes can just use the FIFO interface */ - chip->write_buf(mtd, buf, chip->ecc.size * chip->ecc.steps); + nand_prog_page_begin_op(chip, page, 0, buf, + chip->ecc.size * chip->ecc.steps); chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - return 0; + + return nand_prog_page_end_op(chip); } static int lpc32xx_nand_dma_setup(struct lpc32xx_nand_host *host) diff --git a/drivers/mtd/nand/mtk_nand.c b/drivers/mtd/nand/mtk_nand.c index 9c4adaf9331b..5d76be451596 100644 --- a/drivers/mtd/nand/mtk_nand.c +++ b/drivers/mtd/nand/mtk_nand.c @@ -761,6 +761,8 @@ static int mtk_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, u32 reg; int ret; + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + if (!raw) { /* OOB => FDM: from register, ECC: from HW */ reg = nfi_readw(nfc, NFI_CNFG) | CNFG_AUTO_FMT_EN; @@ -794,7 +796,10 @@ static int mtk_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, if (!raw) mtk_ecc_disable(nfc->ecc); - return ret; + if (ret) + return ret; + + return nand_prog_page_end_op(chip); } static int mtk_nfc_write_page_hwecc(struct mtd_info *mtd, @@ -832,15 +837,7 @@ static int mtk_nfc_write_subpage_hwecc(struct mtd_info *mtd, static int mtk_nfc_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page) { - int ret; - - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - - ret = mtk_nfc_write_page_raw(mtd, chip, NULL, 1, page); - if (ret < 0) - return -EIO; - - return nand_prog_page_end_op(chip); + return mtk_nfc_write_page_raw(mtd, chip, NULL, 1, page); } static int mtk_nfc_update_ecc_stats(struct mtd_info *mtd, u8 *buf, u32 sectors) @@ -889,8 +886,7 @@ static int mtk_nfc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, len = sectors * chip->ecc.size + (raw ? sectors * spare : 0); buf = bufpoi + start * chip->ecc.size; - if (column != 0) - nand_change_read_column_op(chip, column, NULL, 0, false); + nand_read_page_op(chip, page, column, NULL, 0); addr = dma_map_single(nfc->dev, buf, len, DMA_FROM_DEVICE); rc = dma_mapping_error(nfc->dev, addr); @@ -1013,8 +1009,6 @@ static int mtk_nfc_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, static int mtk_nfc_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page) { - nand_read_page_op(chip, page, 0, NULL, 0); - return mtk_nfc_read_page_raw(mtd, chip, NULL, 1, page); } diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 539132ef0095..e3bf33bc1fb6 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1940,7 +1940,7 @@ int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, { int ret; - ret = nand_read_data_op(chip, buf, mtd->writesize, false); + ret = nand_read_page_op(chip, page, 0, buf, mtd->writesize); if (ret) return ret; @@ -1974,6 +1974,10 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, uint8_t *oob = chip->oob_poi; int steps, size, ret; + ret = nand_read_page_op(chip, page, 0, NULL, 0); + if (ret) + return ret; + for (steps = chip->ecc.steps; steps > 0; steps--) { ret = nand_read_data_op(chip, buf, eccsize, false); if (ret) @@ -2096,11 +2100,8 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, data_col_addr = start_step * chip->ecc.size; /* If we read not a page aligned data */ - if (data_col_addr != 0) - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1); - p = bufpoi + data_col_addr; - ret = nand_read_data_op(chip, p, datafrag_len, false); + ret = nand_read_page_op(chip, page, data_col_addr, p, datafrag_len); if (ret) return ret; @@ -2198,6 +2199,10 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *ecc_code = chip->buffers->ecccode; unsigned int max_bitflips = 0; + ret = nand_read_page_op(chip, page, 0, NULL, 0); + if (ret) + return ret; + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { chip->ecc.hwctl(mtd, NAND_ECC_READ); @@ -2335,6 +2340,10 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *oob = chip->oob_poi; unsigned int max_bitflips = 0; + ret = nand_read_page_op(chip, page, 0, NULL, 0); + if (ret) + return ret; + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; @@ -2517,12 +2526,6 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, __func__, buf); read_retry: - if (nand_standard_page_accessors(&chip->ecc)) { - ret = nand_read_page_op(chip, page, 0, NULL, 0); - if (ret) - break; - } - /* * Now read the page into the buffer. Absent an error, * the read methods return max bitflips per ecc step. @@ -2978,7 +2981,7 @@ int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, { int ret; - ret = nand_write_data_op(chip, buf, mtd->writesize, false); + ret = nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); if (ret) return ret; @@ -2989,7 +2992,7 @@ int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, return ret; } - return 0; + return nand_prog_page_end_op(chip); } EXPORT_SYMBOL(nand_write_page_raw); @@ -3013,6 +3016,10 @@ static int nand_write_page_raw_syndrome(struct mtd_info *mtd, uint8_t *oob = chip->oob_poi; int steps, size, ret; + ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); + if (ret) + return ret; + for (steps = chip->ecc.steps; steps > 0; steps--) { ret = nand_write_data_op(chip, buf, eccsize, false); if (ret) @@ -3052,7 +3059,7 @@ static int nand_write_page_raw_syndrome(struct mtd_info *mtd, return ret; } - return 0; + return nand_prog_page_end_op(chip); } /** * nand_write_page_swecc - [REPLACEABLE] software ECC based page write function @@ -3102,6 +3109,10 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *ecc_calc = chip->buffers->ecccalc; const uint8_t *p = buf; + ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); + if (ret) + return ret; + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { chip->ecc.hwctl(mtd, NAND_ECC_WRITE); @@ -3121,7 +3132,7 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, if (ret) return ret; - return 0; + return nand_prog_page_end_op(chip); } @@ -3150,6 +3161,10 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, int oob_bytes = mtd->oobsize / ecc_steps; int step, ret; + ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); + if (ret) + return ret; + for (step = 0; step < ecc_steps; step++) { /* configure controller for WRITE access */ chip->ecc.hwctl(mtd, NAND_ECC_WRITE); @@ -3188,7 +3203,7 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, if (ret) return ret; - return 0; + return nand_prog_page_end_op(chip); } @@ -3215,6 +3230,10 @@ static int nand_write_page_syndrome(struct mtd_info *mtd, uint8_t *oob = chip->oob_poi; int ret; + ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); + if (ret) + return ret; + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { chip->ecc.hwctl(mtd, NAND_ECC_WRITE); @@ -3257,7 +3276,7 @@ static int nand_write_page_syndrome(struct mtd_info *mtd, return ret; } - return 0; + return nand_prog_page_end_op(chip); } /** @@ -3283,12 +3302,6 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, else subpage = 0; - if (nand_standard_page_accessors(&chip->ecc)) { - status = nand_prog_page_begin_op(chip, page, 0, NULL, 0); - if (status) - return status; - } - if (unlikely(raw)) status = chip->ecc.write_page_raw(mtd, chip, buf, oob_required, page); @@ -3302,9 +3315,6 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, if (status < 0) return status; - if (nand_standard_page_accessors(&chip->ecc)) - return nand_prog_page_end_op(chip); - return 0; } @@ -5290,26 +5300,6 @@ static bool nand_ecc_strength_good(struct mtd_info *mtd) return corr >= ds_corr && ecc->strength >= chip->ecc_strength_ds; } -static bool invalid_ecc_page_accessors(struct nand_chip *chip) -{ - struct nand_ecc_ctrl *ecc = &chip->ecc; - - if (nand_standard_page_accessors(ecc)) - return false; - - /* - * NAND_ECC_CUSTOM_PAGE_ACCESS flag is set, make sure the NAND - * controller driver implements all the page accessors because - * default helpers are not suitable when the core does not - * send the READ0/PAGEPROG commands. - */ - return (!ecc->read_page || !ecc->write_page || - !ecc->read_page_raw || !ecc->write_page_raw || - (NAND_HAS_SUBPAGE_READ(chip) && !ecc->read_subpage) || - (NAND_HAS_SUBPAGE_WRITE(chip) && !ecc->write_subpage && - ecc->hwctl && ecc->calculate)); -} - /** * nand_scan_tail - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure @@ -5331,11 +5321,6 @@ int nand_scan_tail(struct mtd_info *mtd) return -EINVAL; } - if (invalid_ecc_page_accessors(chip)) { - pr_err("Invalid ECC page accessors setup\n"); - return -EINVAL; - } - if (!(chip->options & NAND_OWN_BUFFERS)) { nbuf = kzalloc(sizeof(*nbuf), GFP_KERNEL); if (!nbuf) diff --git a/drivers/mtd/nand/nand_micron.c b/drivers/mtd/nand/nand_micron.c index bf2dc23e1c32..02e109ae73f1 100644 --- a/drivers/mtd/nand/nand_micron.c +++ b/drivers/mtd/nand/nand_micron.c @@ -149,7 +149,10 @@ micron_nand_read_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip, else if (status & NAND_STATUS_WRITE_RECOMMENDED) max_bitflips = chip->ecc.strength; - ret = nand_read_page_raw(mtd, chip, buf, oob_required, page); + ret = nand_read_data_op(chip, buf, mtd->writesize, false); + if (!ret && oob_required) + ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, + false); out: micron_nand_on_die_ecc_setup(chip, false); @@ -168,56 +171,12 @@ micron_nand_write_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip, if (ret) return ret; - ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); - if (ret) - goto out; - ret = nand_write_page_raw(mtd, chip, buf, oob_required, page); - if (ret) - return ret; - - ret = nand_prog_page_end_op(chip); - -out: micron_nand_on_die_ecc_setup(chip, false); return ret; } -static int -micron_nand_read_page_raw_on_die_ecc(struct mtd_info *mtd, - struct nand_chip *chip, - uint8_t *buf, int oob_required, - int page) -{ - int ret; - - ret = nand_read_page_op(chip, page, 0, NULL, 0); - if (ret) - return ret; - - return nand_read_page_raw(mtd, chip, buf, oob_required, page); -} - -static int -micron_nand_write_page_raw_on_die_ecc(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf, int oob_required, - int page) -{ - int ret; - - ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); - if (ret) - return ret; - - ret = nand_write_page_raw(mtd, chip, buf, oob_required, page); - if (ret) - return ret; - - return nand_prog_page_end_op(chip); -} - enum { /* The NAND flash doesn't support on-die ECC */ MICRON_ON_DIE_UNSUPPORTED, @@ -310,17 +269,14 @@ static int micron_nand_init(struct nand_chip *chip) return -EINVAL; } - chip->ecc.options = NAND_ECC_CUSTOM_PAGE_ACCESS; chip->ecc.bytes = 8; chip->ecc.size = 512; chip->ecc.strength = 4; chip->ecc.algo = NAND_ECC_BCH; chip->ecc.read_page = micron_nand_read_page_on_die_ecc; chip->ecc.write_page = micron_nand_write_page_on_die_ecc; - chip->ecc.read_page_raw = - micron_nand_read_page_raw_on_die_ecc; - chip->ecc.write_page_raw = - micron_nand_write_page_raw_on_die_ecc; + chip->ecc.read_page_raw = nand_read_page_raw; + chip->ecc.write_page_raw = nand_write_page_raw; mtd_set_ooblayout(mtd, µn_nand_on_die_ooblayout_ops); } diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 6e1b209cd5a7..5cb4db6f88e3 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1532,6 +1532,8 @@ static int omap_write_page_bch(struct mtd_info *mtd, struct nand_chip *chip, int ret; uint8_t *ecc_calc = chip->buffers->ecccalc; + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + /* Enable GPMC ecc engine */ chip->ecc.hwctl(mtd, NAND_ECC_WRITE); @@ -1548,7 +1550,8 @@ static int omap_write_page_bch(struct mtd_info *mtd, struct nand_chip *chip, /* Write ecc vector to OOB area */ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - return 0; + + return nand_prog_page_end_op(chip); } /** @@ -1582,6 +1585,7 @@ static int omap_write_subpage_bch(struct mtd_info *mtd, * ECC is calculated for all subpages but we choose * only what we want. */ + nand_prog_page_begin_op(chip, page, 0, NULL, 0); /* Enable GPMC ECC engine */ chip->ecc.hwctl(mtd, NAND_ECC_WRITE); @@ -1614,7 +1618,7 @@ static int omap_write_subpage_bch(struct mtd_info *mtd, /* write OOB buffer to NAND device */ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - return 0; + return nand_prog_page_end_op(chip); } /** @@ -1640,6 +1644,8 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, int stat, ret; unsigned int max_bitflips = 0; + nand_read_page_op(chip, page, 0, NULL, 0); + /* Enable GPMC ecc engine */ chip->ecc.hwctl(mtd, NAND_ECC_READ); diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 28bcdf64c1fc..021374fe59dc 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1348,10 +1348,10 @@ static int pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required, int page) { - chip->write_buf(mtd, buf, mtd->writesize); + nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - return 0; + return nand_prog_page_end_op(chip); } static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd, @@ -1361,7 +1361,7 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd, struct pxa3xx_nand_host *host = nand_get_controller_data(chip); struct pxa3xx_nand_info *info = host->info_data; - chip->read_buf(mtd, buf, mtd->writesize); + nand_read_page_op(chip, page, 0, buf, mtd->writesize); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); if (info->retcode == ERR_CORERR && info->use_ecc) { diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c index e34313ecd903..245d0f39e0aa 100644 --- a/drivers/mtd/nand/qcom_nandc.c +++ b/drivers/mtd/nand/qcom_nandc.c @@ -1725,6 +1725,7 @@ static int qcom_nandc_read_page(struct mtd_info *mtd, struct nand_chip *chip, u8 *data_buf, *oob_buf = NULL; int ret; + nand_read_page_op(chip, page, 0, NULL, 0); data_buf = buf; oob_buf = oob_required ? chip->oob_poi : NULL; @@ -1750,6 +1751,7 @@ static int qcom_nandc_read_page_raw(struct mtd_info *mtd, int i, ret; int read_loc; + nand_read_page_op(chip, page, 0, NULL, 0); data_buf = buf; oob_buf = chip->oob_poi; @@ -1850,6 +1852,8 @@ static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip, u8 *data_buf, *oob_buf; int i, ret; + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + clear_read_regs(nandc); clear_bam_transaction(nandc); @@ -1902,6 +1906,9 @@ static int qcom_nandc_write_page(struct mtd_info *mtd, struct nand_chip *chip, free_descs(nandc); + if (!ret) + ret = nand_prog_page_end_op(chip); + return ret; } @@ -1916,6 +1923,7 @@ static int qcom_nandc_write_page_raw(struct mtd_info *mtd, u8 *data_buf, *oob_buf; int i, ret; + nand_prog_page_begin_op(chip, page, 0, NULL, 0); clear_read_regs(nandc); clear_bam_transaction(nandc); @@ -1970,6 +1978,9 @@ static int qcom_nandc_write_page_raw(struct mtd_info *mtd, free_descs(nandc); + if (!ret) + ret = nand_prog_page_end_op(chip); + return ret; } diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 3c5008a4f5f3..c4e7755448e6 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -614,7 +614,7 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - chip->read_buf(mtd, buf, mtd->writesize); + nand_read_page_op(chip, page, 0, buf, mtd->writesize); if (oob_required) chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); return 0; @@ -624,9 +624,9 @@ static int flctl_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required, int page) { - chip->write_buf(mtd, buf, mtd->writesize); + nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); - return 0; + return nand_prog_page_end_op(chip); } static void execmd_read_page_sector(struct mtd_info *mtd, int page_addr) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index da5cc36f4c30..5c176dee821e 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -1245,6 +1245,8 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, int ret, i, cur_off = 0; bool raw_mode = false; + nand_read_page_op(chip, page, 0, NULL, 0); + sunxi_nfc_hw_ecc_enable(mtd); for (i = 0; i < ecc->steps; i++) { @@ -1278,14 +1280,14 @@ static int sunxi_nfc_hw_ecc_read_page_dma(struct mtd_info *mtd, { int ret; + nand_read_page_op(chip, page, 0, NULL, 0); + ret = sunxi_nfc_hw_ecc_read_chunks_dma(mtd, buf, oob_required, page, chip->ecc.steps); if (ret >= 0) return ret; /* Fallback to PIO mode */ - nand_change_read_column_op(chip, 0, NULL, 0, false); - return sunxi_nfc_hw_ecc_read_page(mtd, chip, buf, oob_required, page); } @@ -1298,6 +1300,8 @@ static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd, int ret, i, cur_off = 0; unsigned int max_bitflips = 0; + nand_read_page_op(chip, page, 0, NULL, 0); + sunxi_nfc_hw_ecc_enable(mtd); for (i = data_offs / ecc->size; @@ -1329,13 +1333,13 @@ static int sunxi_nfc_hw_ecc_read_subpage_dma(struct mtd_info *mtd, int nchunks = DIV_ROUND_UP(data_offs + readlen, chip->ecc.size); int ret; + nand_read_page_op(chip, page, 0, NULL, 0); + ret = sunxi_nfc_hw_ecc_read_chunks_dma(mtd, buf, false, page, nchunks); if (ret >= 0) return ret; /* Fallback to PIO mode */ - nand_change_read_column_op(chip, 0, NULL, 0, false); - return sunxi_nfc_hw_ecc_read_subpage(mtd, chip, data_offs, readlen, buf, page); } @@ -1348,6 +1352,8 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc = &chip->ecc; int ret, i, cur_off = 0; + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + sunxi_nfc_hw_ecc_enable(mtd); for (i = 0; i < ecc->steps; i++) { @@ -1369,7 +1375,7 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, sunxi_nfc_hw_ecc_disable(mtd); - return 0; + return nand_prog_page_end_op(chip); } static int sunxi_nfc_hw_ecc_write_subpage(struct mtd_info *mtd, @@ -1381,6 +1387,8 @@ static int sunxi_nfc_hw_ecc_write_subpage(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc = &chip->ecc; int ret, i, cur_off = 0; + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + sunxi_nfc_hw_ecc_enable(mtd); for (i = data_offs / ecc->size; @@ -1399,7 +1407,7 @@ static int sunxi_nfc_hw_ecc_write_subpage(struct mtd_info *mtd, sunxi_nfc_hw_ecc_disable(mtd); - return 0; + return nand_prog_page_end_op(chip); } static int sunxi_nfc_hw_ecc_write_page_dma(struct mtd_info *mtd, @@ -1429,6 +1437,8 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct mtd_info *mtd, sunxi_nfc_hw_ecc_set_prot_oob_bytes(mtd, oob, i, !i, page); } + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + sunxi_nfc_hw_ecc_enable(mtd); sunxi_nfc_randomizer_config(mtd, page, false); sunxi_nfc_randomizer_enable(mtd); @@ -1459,7 +1469,7 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct mtd_info *mtd, sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, NULL, page); - return 0; + return nand_prog_page_end_op(chip); pio_fallback: return sunxi_nfc_hw_ecc_write_page(mtd, chip, buf, oob_required, page); @@ -1475,6 +1485,8 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, int ret, i, cur_off = 0; bool raw_mode = false; + nand_read_page_op(chip, page, 0, NULL, 0); + sunxi_nfc_hw_ecc_enable(mtd); for (i = 0; i < ecc->steps; i++) { @@ -1511,6 +1523,8 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc = &chip->ecc; int ret, i, cur_off = 0; + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + sunxi_nfc_hw_ecc_enable(mtd); for (i = 0; i < ecc->steps; i++) { @@ -1532,15 +1546,13 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, sunxi_nfc_hw_ecc_disable(mtd); - return 0; + return nand_prog_page_end_op(chip); } static int sunxi_nfc_hw_common_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) { - nand_read_page_op(chip, page, 0, NULL, 0); - chip->pagebuf = -1; return chip->ecc.read_page(mtd, chip, chip->buffers->databuf, 1, page); @@ -1552,8 +1564,6 @@ static int sunxi_nfc_hw_common_ecc_write_oob(struct mtd_info *mtd, { int ret; - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - chip->pagebuf = -1; memset(chip->buffers->databuf, 0xff, mtd->writesize); diff --git a/drivers/mtd/nand/tango_nand.c b/drivers/mtd/nand/tango_nand.c index 97a300b46b1d..c5bee00b7f5e 100644 --- a/drivers/mtd/nand/tango_nand.c +++ b/drivers/mtd/nand/tango_nand.c @@ -580,7 +580,6 @@ static int chip_init(struct device *dev, struct device_node *np) ecc->write_page = tango_write_page; ecc->read_oob = tango_read_oob; ecc->write_oob = tango_write_oob; - ecc->options = NAND_ECC_CUSTOM_PAGE_ACCESS; err = nand_scan_tail(mtd); if (err) diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/vf610_nfc.c index 8037d4b48a05..80d31a58e558 100644 --- a/drivers/mtd/nand/vf610_nfc.c +++ b/drivers/mtd/nand/vf610_nfc.c @@ -560,7 +560,7 @@ static int vf610_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip, int eccsize = chip->ecc.size; int stat; - vf610_nfc_read_buf(mtd, buf, eccsize); + nand_read_page_op(chip, page, 0, buf, eccsize); if (oob_required) vf610_nfc_read_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -580,7 +580,7 @@ static int vf610_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, { struct vf610_nfc *nfc = mtd_to_nfc(mtd); - vf610_nfc_write_buf(mtd, buf, mtd->writesize); + nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); if (oob_required) vf610_nfc_write_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -588,7 +588,7 @@ static int vf610_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, nfc->use_hw_ecc = true; nfc->write_sz = mtd->writesize + mtd->oobsize; - return 0; + return nand_prog_page_end_op(chip); } static const struct of_device_id vf610_nfc_dt_ids[] = { diff --git a/drivers/staging/mt29f_spinand/mt29f_spinand.c b/drivers/staging/mt29f_spinand/mt29f_spinand.c index 87595c594b12..264ad362d858 100644 --- a/drivers/staging/mt29f_spinand/mt29f_spinand.c +++ b/drivers/staging/mt29f_spinand/mt29f_spinand.c @@ -637,8 +637,7 @@ static int spinand_write_page_hwecc(struct mtd_info *mtd, int eccsteps = chip->ecc.steps; enable_hw_ecc = 1; - chip->write_buf(mtd, p, eccsize * eccsteps); - return 0; + return nand_prog_page_op(chip, page, 0, p, eccsize * eccsteps); } static int spinand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, @@ -653,7 +652,7 @@ static int spinand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, enable_read_hw_ecc = 1; - chip->read_buf(mtd, p, eccsize * eccsteps); + nand_read_page_op(chip, page, 0, p, eccsize * eccsteps); if (oob_required) chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index fd99d5137d71..e6810f0b8f9e 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -133,12 +133,6 @@ enum nand_ecc_algo { */ #define NAND_ECC_GENERIC_ERASED_CHECK BIT(0) #define NAND_ECC_MAXIMIZE BIT(1) -/* - * If your controller already sends the required NAND commands when - * reading or writing a page, then the framework is not supposed to - * send READ0 and SEQIN/PAGEPROG respectively. - */ -#define NAND_ECC_CUSTOM_PAGE_ACCESS BIT(2) /* Bit mask for flags passed to do_nand_read_ecc */ #define NAND_GET_DEVICE 0x80 @@ -602,11 +596,6 @@ struct nand_ecc_ctrl { int page); }; -static inline int nand_standard_page_accessors(struct nand_ecc_ctrl *ecc) -{ - return !(ecc->options & NAND_ECC_CUSTOM_PAGE_ACCESS); -} - /** * struct nand_buffers - buffer structure for read/write * @ecccalc: buffer pointer for calculated ECC, size is oobsize. -- cgit v1.2.3 From 17fa8044188c152e8a3b9493f8b8054cacbfb9ba Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 30 Nov 2017 18:01:31 +0100 Subject: mtd: nand: provide valid ->data_interface during NAND detection Right now, the chip->data_interface field is populated in nand_scan_tail(), so after the whole NAND detection has taken place. This is fine because these timings are not yet used by the core so early in the probe process, but the situation is about to change with the introduction of ->exec_op(). Also, by convention, nand_scan_ident() is not supposed to allocate resources, only nand_scan_tail() can, so this prevent us from allocating and initializing the data_interface object in nand_scan_ident(). In order to solve this problem, directly embed a data_interface object in nand_chip so that we don't have to allocate it, and initialize it to ONFI SDR mode 0 at the very beginning of nand_scan_ident(). Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 46 ++++++++++++++++++----------------------- drivers/mtd/nand/nand_timings.c | 21 +++++-------------- include/linux/mtd/rawnand.h | 7 ++----- 3 files changed, 27 insertions(+), 47 deletions(-) (limited to 'drivers/mtd/nand/nand_base.c') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index e3bf33bc1fb6..215c52a3b9ad 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -816,8 +816,8 @@ static void nand_ccs_delay(struct nand_chip *chip) * Wait tCCS_min if it is correctly defined, otherwise wait 500ns * (which should be safe for all NANDs). */ - if (chip->data_interface && chip->data_interface->timings.sdr.tCCS_min) - ndelay(chip->data_interface->timings.sdr.tCCS_min / 1000); + if (chip->setup_data_interface) + ndelay(chip->data_interface.timings.sdr.tCCS_min / 1000); else ndelay(500); } @@ -1112,7 +1112,6 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) static int nand_reset_data_interface(struct nand_chip *chip, int chipnr) { struct mtd_info *mtd = nand_to_mtd(chip); - const struct nand_data_interface *conf; int ret; if (!chip->setup_data_interface) @@ -1132,8 +1131,8 @@ static int nand_reset_data_interface(struct nand_chip *chip, int chipnr) * timings to timing mode 0. */ - conf = nand_get_default_data_interface(); - ret = chip->setup_data_interface(mtd, chipnr, conf); + onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0); + ret = chip->setup_data_interface(mtd, chipnr, &chip->data_interface); if (ret) pr_err("Failed to configure data interface to SDR timing mode 0\n"); @@ -1158,7 +1157,7 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) struct mtd_info *mtd = nand_to_mtd(chip); int ret; - if (!chip->setup_data_interface || !chip->data_interface) + if (!chip->setup_data_interface) return 0; /* @@ -1179,7 +1178,7 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) goto err; } - ret = chip->setup_data_interface(mtd, chipnr, chip->data_interface); + ret = chip->setup_data_interface(mtd, chipnr, &chip->data_interface); err: return ret; } @@ -1219,21 +1218,16 @@ static int nand_init_data_interface(struct nand_chip *chip) modes = GENMASK(chip->onfi_timing_mode_default, 0); } - chip->data_interface = kzalloc(sizeof(*chip->data_interface), - GFP_KERNEL); - if (!chip->data_interface) - return -ENOMEM; for (mode = fls(modes) - 1; mode >= 0; mode--) { - ret = onfi_init_data_interface(chip, chip->data_interface, - NAND_SDR_IFACE, mode); + ret = onfi_fill_data_interface(chip, NAND_SDR_IFACE, mode); if (ret) continue; /* Pass -1 to only */ ret = chip->setup_data_interface(mtd, NAND_DATA_IFACE_CHECK_ONLY, - chip->data_interface); + &chip->data_interface); if (!ret) { chip->onfi_timing_mode_default = mode; break; @@ -1243,11 +1237,6 @@ static int nand_init_data_interface(struct nand_chip *chip) return 0; } -static void nand_release_data_interface(struct nand_chip *chip) -{ - kfree(chip->data_interface); -} - /** * nand_read_page_op - Do a READ PAGE operation * @chip: The NAND chip @@ -1763,11 +1752,16 @@ EXPORT_SYMBOL_GPL(nand_write_data_op); * @chip: The NAND chip * @chipnr: Internal die id * - * Returns 0 for success or negative error code otherwise + * Save the timings data structure, then apply SDR timings mode 0 (see + * nand_reset_data_interface for details), do the reset operation, and + * apply back the previous timings. + * + * Returns 0 on success, a negative error code otherwise. */ int nand_reset(struct nand_chip *chip, int chipnr) { struct mtd_info *mtd = nand_to_mtd(chip); + struct nand_data_interface saved_data_intf = chip->data_interface; int ret; ret = nand_reset_data_interface(chip, chipnr); @@ -1785,6 +1779,7 @@ int nand_reset(struct nand_chip *chip, int chipnr) return ret; chip->select_chip(mtd, chipnr); + chip->data_interface = saved_data_intf; ret = nand_setup_data_interface(chip, chipnr); chip->select_chip(mtd, -1); if (ret) @@ -4889,6 +4884,9 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, struct nand_chip *chip = mtd_to_nand(mtd); int ret; + /* Enforce the right timings for reset/detection */ + onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0); + ret = nand_dt_init(chip); if (ret) return ret; @@ -5629,7 +5627,7 @@ int nand_scan_tail(struct mtd_info *mtd) chip->select_chip(mtd, -1); if (ret) - goto err_nand_data_iface_cleanup; + goto err_nand_manuf_cleanup; } /* Check, if we should skip the bad block table scan */ @@ -5639,12 +5637,10 @@ int nand_scan_tail(struct mtd_info *mtd) /* Build bad block table */ ret = chip->scan_bbt(mtd); if (ret) - goto err_nand_data_iface_cleanup; + goto err_nand_manuf_cleanup; return 0; -err_nand_data_iface_cleanup: - nand_release_data_interface(chip); err_nand_manuf_cleanup: nand_manufacturer_cleanup(chip); @@ -5703,8 +5699,6 @@ void nand_cleanup(struct nand_chip *chip) chip->ecc.algo == NAND_ECC_BCH) nand_bch_free((struct nand_bch_control *)chip->ecc.priv); - nand_release_data_interface(chip); - /* Free bad block table memory */ kfree(chip->bbt); if (!(chip->options & NAND_OWN_BUFFERS) && chip->buffers) { diff --git a/drivers/mtd/nand/nand_timings.c b/drivers/mtd/nand/nand_timings.c index 5d1533bcc5bd..9400d039ddbd 100644 --- a/drivers/mtd/nand/nand_timings.c +++ b/drivers/mtd/nand/nand_timings.c @@ -283,16 +283,16 @@ const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode) EXPORT_SYMBOL(onfi_async_timing_mode_to_sdr_timings); /** - * onfi_init_data_interface - [NAND Interface] Initialize a data interface from + * onfi_fill_data_interface - [NAND Interface] Initialize a data interface from * given ONFI mode - * @iface: The data interface to be initialized * @mode: The ONFI timing mode */ -int onfi_init_data_interface(struct nand_chip *chip, - struct nand_data_interface *iface, +int onfi_fill_data_interface(struct nand_chip *chip, enum nand_data_interface_type type, int timing_mode) { + struct nand_data_interface *iface = &chip->data_interface; + if (type != NAND_SDR_IFACE) return -EINVAL; @@ -321,15 +321,4 @@ int onfi_init_data_interface(struct nand_chip *chip, return 0; } -EXPORT_SYMBOL(onfi_init_data_interface); - -/** - * nand_get_default_data_interface - [NAND Interface] Retrieve NAND - * data interface for mode 0. This is used as default timing after - * reset. - */ -const struct nand_data_interface *nand_get_default_data_interface(void) -{ - return &onfi_sdr_timings[0]; -} -EXPORT_SYMBOL(nand_get_default_data_interface); +EXPORT_SYMBOL(onfi_fill_data_interface); diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index e6810f0b8f9e..2a72eab286ef 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -917,7 +917,7 @@ struct nand_chip { u16 max_bb_per_die; u32 blocks_per_die; - struct nand_data_interface *data_interface; + struct nand_data_interface data_interface; int read_retries; @@ -1214,8 +1214,7 @@ static inline int onfi_get_sync_timing_mode(struct nand_chip *chip) return le16_to_cpu(chip->onfi_params.src_sync_timing_mode); } -int onfi_init_data_interface(struct nand_chip *chip, - struct nand_data_interface *iface, +int onfi_fill_data_interface(struct nand_chip *chip, enum nand_data_interface_type type, int timing_mode); @@ -1258,8 +1257,6 @@ static inline int jedec_feature(struct nand_chip *chip) /* get timing characteristics from ONFI timing mode. */ const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode); -/* get data interface from ONFI timing mode 0, used after reset. */ -const struct nand_data_interface *nand_get_default_data_interface(void); int nand_check_erased_ecc_chunk(void *data, int datalen, void *ecc, int ecclen, -- cgit v1.2.3 From 8b311ead8bff9b56e512e3e544c488042ad0e7e7 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 5 Dec 2017 17:47:15 +0900 Subject: mtd: nand: remove unused NAND_OWN_BUFFERS flag The last/only user of NAND_OWN_BUFFERS (cafe_nand.c) has been reworked. This flag is no longer needed. Suggested-by: Boris Brezillon Signed-off-by: Masahiro Yamada Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 45 ++++++++++++++++++++------------------------ include/linux/mtd/rawnand.h | 5 ----- 2 files changed, 20 insertions(+), 30 deletions(-) (limited to 'drivers/mtd/nand/nand_base.c') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 215c52a3b9ad..b63cc95e9179 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -5319,35 +5319,30 @@ int nand_scan_tail(struct mtd_info *mtd) return -EINVAL; } - if (!(chip->options & NAND_OWN_BUFFERS)) { - nbuf = kzalloc(sizeof(*nbuf), GFP_KERNEL); - if (!nbuf) - return -ENOMEM; - - nbuf->ecccalc = kmalloc(mtd->oobsize, GFP_KERNEL); - if (!nbuf->ecccalc) { - ret = -ENOMEM; - goto err_free_nbuf; - } + nbuf = kzalloc(sizeof(*nbuf), GFP_KERNEL); + if (!nbuf) + return -ENOMEM; - nbuf->ecccode = kmalloc(mtd->oobsize, GFP_KERNEL); - if (!nbuf->ecccode) { - ret = -ENOMEM; - goto err_free_nbuf; - } + nbuf->ecccalc = kmalloc(mtd->oobsize, GFP_KERNEL); + if (!nbuf->ecccalc) { + ret = -ENOMEM; + goto err_free_nbuf; + } - nbuf->databuf = kmalloc(mtd->writesize + mtd->oobsize, - GFP_KERNEL); - if (!nbuf->databuf) { - ret = -ENOMEM; - goto err_free_nbuf; - } + nbuf->ecccode = kmalloc(mtd->oobsize, GFP_KERNEL); + if (!nbuf->ecccode) { + ret = -ENOMEM; + goto err_free_nbuf; + } - chip->buffers = nbuf; - } else if (!chip->buffers) { - return -ENOMEM; + nbuf->databuf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); + if (!nbuf->databuf) { + ret = -ENOMEM; + goto err_free_nbuf; } + chip->buffers = nbuf; + /* * FIXME: some NAND manufacturer drivers expect the first die to be * selected when manufacturer->init() is called. They should be fixed @@ -5701,7 +5696,7 @@ void nand_cleanup(struct nand_chip *chip) /* Free bad block table memory */ kfree(chip->bbt); - if (!(chip->options & NAND_OWN_BUFFERS) && chip->buffers) { + if (chip->buffers) { kfree(chip->buffers->databuf); kfree(chip->buffers->ecccode); kfree(chip->buffers->ecccalc); diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 2a72eab286ef..fca802ef9af3 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -185,11 +185,6 @@ enum nand_ecc_algo { /* Non chip related options */ /* This option skips the bbt scan during initialization. */ #define NAND_SKIP_BBTSCAN 0x00010000 -/* - * This option is defined if the board driver allocates its own buffers - * (e.g. because it needs them DMA-coherent). - */ -#define NAND_OWN_BUFFERS 0x00020000 /* Chip may not exist, so silence any errors in scan */ #define NAND_SCAN_SILENT_NODEV 0x00040000 /* -- cgit v1.2.3 From c0313b966a0942fba934d34c7a76f444641d0b6e Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 5 Dec 2017 17:47:16 +0900 Subject: mtd: nand: squash struct nand_buffers into struct nand_chip struct nand_buffers is malloc'ed in nand_scan_tail() just for containing three pointers. Squash this struct into nand_chip. Move and rename as follows: chip->buffers->ecccalc -> chip->ecc.calc_buf chip->buffers->ecccode -> chip->ecc.code_buf chip->buffers->databuf -> chip->data_buf Signed-off-by: Masahiro Yamada Signed-off-by: Boris Brezillon --- drivers/mtd/nand/brcmnand/brcmnand.c | 2 +- drivers/mtd/nand/denali.c | 2 +- drivers/mtd/nand/fsmc_nand.c | 4 +- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 4 +- drivers/mtd/nand/nand_base.c | 91 ++++++++++++++-------------------- drivers/mtd/nand/nand_bbt.c | 2 +- drivers/mtd/nand/omap2.c | 10 ++-- drivers/mtd/nand/sunxi_nand.c | 6 +-- include/linux/mtd/rawnand.h | 23 +++------ 9 files changed, 59 insertions(+), 85 deletions(-) (limited to 'drivers/mtd/nand/nand_base.c') diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c index e6879d4d53ca..54842512edb1 100644 --- a/drivers/mtd/nand/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/brcmnand/brcmnand.c @@ -1681,7 +1681,7 @@ static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd, int ret; if (!buf) { - buf = chip->buffers->databuf; + buf = chip->data_buf; /* Invalidate page cache */ chip->pagebuf = -1; } diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 47a253737bb2..00698b33cb22 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -330,7 +330,7 @@ static int denali_check_erased_page(struct mtd_info *mtd, unsigned long uncor_ecc_flags, unsigned int max_bitflips) { - uint8_t *ecc_code = chip->buffers->ecccode; + uint8_t *ecc_code = chip->ecc.code_buf; int ecc_steps = chip->ecc.steps; int ecc_size = chip->ecc.size; int ecc_bytes = chip->ecc.bytes; diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index b44e5c6545e0..f49ed46fa770 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -684,8 +684,8 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; uint8_t *p = buf; - uint8_t *ecc_calc = chip->buffers->ecccalc; - uint8_t *ecc_code = chip->buffers->ecccode; + uint8_t *ecc_calc = chip->ecc.calc_buf; + uint8_t *ecc_code = chip->ecc.code_buf; int off, len, group = 0; /* * ecc_oob is intentionally taken as uint16_t. In 16bit devices, we diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 3c3f3f58fdcb..b51db8c85405 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -1696,7 +1696,7 @@ static int mx23_check_transcription_stamp(struct gpmi_nand_data *this) unsigned int search_area_size_in_strides; unsigned int stride; unsigned int page; - uint8_t *buffer = chip->buffers->databuf; + uint8_t *buffer = chip->data_buf; int saved_chip_number; int found_an_ncb_fingerprint = false; @@ -1755,7 +1755,7 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this) unsigned int block; unsigned int stride; unsigned int page; - uint8_t *buffer = chip->buffers->databuf; + uint8_t *buffer = chip->data_buf; int saved_chip_number; int status; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index b63cc95e9179..32c0239b380a 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2030,8 +2030,8 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; uint8_t *p = buf; - uint8_t *ecc_calc = chip->buffers->ecccalc; - uint8_t *ecc_code = chip->buffers->ecccode; + uint8_t *ecc_calc = chip->ecc.calc_buf; + uint8_t *ecc_code = chip->ecc.code_buf; unsigned int max_bitflips = 0; chip->ecc.read_page_raw(mtd, chip, buf, 1, page); @@ -2102,7 +2102,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, /* Calculate ECC */ for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) - chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]); + chip->ecc.calculate(mtd, p, &chip->ecc.calc_buf[i]); /* * The performance is faster if we position offsets according to @@ -2142,7 +2142,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, return ret; } - ret = mtd_ooblayout_get_eccbytes(mtd, chip->buffers->ecccode, + ret = mtd_ooblayout_get_eccbytes(mtd, chip->ecc.code_buf, chip->oob_poi, index, eccfrag_len); if (ret) return ret; @@ -2151,13 +2151,13 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) { int stat; - stat = chip->ecc.correct(mtd, p, - &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]); + stat = chip->ecc.correct(mtd, p, &chip->ecc.code_buf[i], + &chip->ecc.calc_buf[i]); if (stat == -EBADMSG && (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { /* check for empty pages with bitflips */ stat = nand_check_erased_ecc_chunk(p, chip->ecc.size, - &chip->buffers->ecccode[i], + &chip->ecc.code_buf[i], chip->ecc.bytes, NULL, 0, chip->ecc.strength); @@ -2190,8 +2190,8 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; uint8_t *p = buf; - uint8_t *ecc_calc = chip->buffers->ecccalc; - uint8_t *ecc_code = chip->buffers->ecccode; + uint8_t *ecc_calc = chip->ecc.calc_buf; + uint8_t *ecc_code = chip->ecc.code_buf; unsigned int max_bitflips = 0; ret = nand_read_page_op(chip, page, 0, NULL, 0); @@ -2264,8 +2264,8 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; uint8_t *p = buf; - uint8_t *ecc_code = chip->buffers->ecccode; - uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->ecc.code_buf; + uint8_t *ecc_calc = chip->ecc.calc_buf; unsigned int max_bitflips = 0; /* Read the OOB area first */ @@ -2514,7 +2514,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, /* Is the current page in the buffer? */ if (realpage != chip->pagebuf || oob) { - bufpoi = use_bufpoi ? chip->buffers->databuf : buf; + bufpoi = use_bufpoi ? chip->data_buf : buf; if (use_bufpoi && aligned) pr_debug("%s: using read bounce buffer for buf@%p\n", @@ -2555,7 +2555,7 @@ read_retry: /* Invalidate page cache */ chip->pagebuf = -1; } - memcpy(buf, chip->buffers->databuf + col, bytes); + memcpy(buf, chip->data_buf + col, bytes); } if (unlikely(oob)) { @@ -2596,7 +2596,7 @@ read_retry: buf += bytes; max_bitflips = max_t(unsigned int, max_bitflips, ret); } else { - memcpy(buf, chip->buffers->databuf + col, bytes); + memcpy(buf, chip->data_buf + col, bytes); buf += bytes; max_bitflips = max_t(unsigned int, max_bitflips, chip->pagebuf_bitflips); @@ -3071,7 +3071,7 @@ static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, int i, eccsize = chip->ecc.size, ret; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; - uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_calc = chip->ecc.calc_buf; const uint8_t *p = buf; /* Software ECC calculation */ @@ -3101,7 +3101,7 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, int i, eccsize = chip->ecc.size, ret; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; - uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_calc = chip->ecc.calc_buf; const uint8_t *p = buf; ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); @@ -3147,7 +3147,7 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, int oob_required, int page) { uint8_t *oob_buf = chip->oob_poi; - uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_calc = chip->ecc.calc_buf; int ecc_size = chip->ecc.size; int ecc_bytes = chip->ecc.bytes; int ecc_steps = chip->ecc.steps; @@ -3187,7 +3187,7 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, /* copy calculated ECC for whole page to chip->buffer->oob */ /* this include masked-value(0xFF) for unwritten subpages */ - ecc_calc = chip->buffers->ecccalc; + ecc_calc = chip->ecc.calc_buf; ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, chip->ecc.total); if (ret) @@ -3434,9 +3434,9 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, if (part_pagewr) bytes = min_t(int, bytes - column, writelen); chip->pagebuf = -1; - memset(chip->buffers->databuf, 0xff, mtd->writesize); - memcpy(&chip->buffers->databuf[column], buf, bytes); - wbuf = chip->buffers->databuf; + memset(chip->data_buf, 0xff, mtd->writesize); + memcpy(&chip->data_buf[column], buf, bytes); + wbuf = chip->data_buf; } if (unlikely(oob)) { @@ -5310,7 +5310,6 @@ int nand_scan_tail(struct mtd_info *mtd) { struct nand_chip *chip = mtd_to_nand(mtd); struct nand_ecc_ctrl *ecc = &chip->ecc; - struct nand_buffers *nbuf = NULL; int ret, i; /* New bad blocks should be marked in OOB, flash-based BBT, or both */ @@ -5319,30 +5318,22 @@ int nand_scan_tail(struct mtd_info *mtd) return -EINVAL; } - nbuf = kzalloc(sizeof(*nbuf), GFP_KERNEL); - if (!nbuf) + ecc->calc_buf = kmalloc(mtd->oobsize, GFP_KERNEL); + if (!ecc->calc_buf) return -ENOMEM; - nbuf->ecccalc = kmalloc(mtd->oobsize, GFP_KERNEL); - if (!nbuf->ecccalc) { + ecc->code_buf = kmalloc(mtd->oobsize, GFP_KERNEL); + if (!ecc->code_buf) { ret = -ENOMEM; - goto err_free_nbuf; + goto err_free_buf; } - nbuf->ecccode = kmalloc(mtd->oobsize, GFP_KERNEL); - if (!nbuf->ecccode) { + chip->data_buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); + if (!chip->data_buf) { ret = -ENOMEM; - goto err_free_nbuf; + goto err_free_buf; } - nbuf->databuf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); - if (!nbuf->databuf) { - ret = -ENOMEM; - goto err_free_nbuf; - } - - chip->buffers = nbuf; - /* * FIXME: some NAND manufacturer drivers expect the first die to be * selected when manufacturer->init() is called. They should be fixed @@ -5353,10 +5344,10 @@ int nand_scan_tail(struct mtd_info *mtd) ret = nand_manufacturer_init(chip); chip->select_chip(mtd, -1); if (ret) - goto err_free_nbuf; + goto err_free_buf; /* Set the internal oob buffer location, just after the page data */ - chip->oob_poi = chip->buffers->databuf + mtd->writesize; + chip->oob_poi = chip->data_buf + mtd->writesize; /* * If no default placement scheme is given, select an appropriate one. @@ -5640,13 +5631,10 @@ int nand_scan_tail(struct mtd_info *mtd) err_nand_manuf_cleanup: nand_manufacturer_cleanup(chip); -err_free_nbuf: - if (nbuf) { - kfree(nbuf->databuf); - kfree(nbuf->ecccode); - kfree(nbuf->ecccalc); - kfree(nbuf); - } +err_free_buf: + kfree(chip->data_buf); + kfree(ecc->code_buf); + kfree(ecc->calc_buf); return ret; } @@ -5696,12 +5684,9 @@ void nand_cleanup(struct nand_chip *chip) /* Free bad block table memory */ kfree(chip->bbt); - if (chip->buffers) { - kfree(chip->buffers->databuf); - kfree(chip->buffers->ecccode); - kfree(chip->buffers->ecccalc); - kfree(chip->buffers); - } + kfree(chip->data_buf); + kfree(chip->ecc.code_buf); + kfree(chip->ecc.calc_buf); /* Free bad block descriptor memory */ if (chip->badblock_pattern && chip->badblock_pattern->options diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 2915b6739bf8..36092850be2c 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -898,7 +898,7 @@ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *b { struct nand_chip *this = mtd_to_nand(mtd); - return create_bbt(mtd, this->buffers->databuf, bd, -1); + return create_bbt(mtd, this->data_buf, bd, -1); } /** diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 5cb4db6f88e3..8cdf7d3d8fa7 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1530,7 +1530,7 @@ static int omap_write_page_bch(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required, int page) { int ret; - uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_calc = chip->ecc.calc_buf; nand_prog_page_begin_op(chip, page, 0, NULL, 0); @@ -1571,7 +1571,7 @@ static int omap_write_subpage_bch(struct mtd_info *mtd, u32 data_len, const u8 *buf, int oob_required, int page) { - u8 *ecc_calc = chip->buffers->ecccalc; + u8 *ecc_calc = chip->ecc.calc_buf; int ecc_size = chip->ecc.size; int ecc_bytes = chip->ecc.bytes; int ecc_steps = chip->ecc.steps; @@ -1609,7 +1609,7 @@ static int omap_write_subpage_bch(struct mtd_info *mtd, /* copy calculated ECC for whole page to chip->buffer->oob */ /* this include masked-value(0xFF) for unwritten subpages */ - ecc_calc = chip->buffers->ecccalc; + ecc_calc = chip->ecc.calc_buf; ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, chip->ecc.total); if (ret) @@ -1639,8 +1639,8 @@ static int omap_write_subpage_bch(struct mtd_info *mtd, static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - uint8_t *ecc_calc = chip->buffers->ecccalc; - uint8_t *ecc_code = chip->buffers->ecccode; + uint8_t *ecc_calc = chip->ecc.calc_buf; + uint8_t *ecc_code = chip->ecc.code_buf; int stat, ret; unsigned int max_bitflips = 0; diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 5c176dee821e..2275fbedfb2a 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -1555,7 +1555,7 @@ static int sunxi_nfc_hw_common_ecc_read_oob(struct mtd_info *mtd, { chip->pagebuf = -1; - return chip->ecc.read_page(mtd, chip, chip->buffers->databuf, 1, page); + return chip->ecc.read_page(mtd, chip, chip->data_buf, 1, page); } static int sunxi_nfc_hw_common_ecc_write_oob(struct mtd_info *mtd, @@ -1566,8 +1566,8 @@ static int sunxi_nfc_hw_common_ecc_write_oob(struct mtd_info *mtd, chip->pagebuf = -1; - memset(chip->buffers->databuf, 0xff, mtd->writesize); - ret = chip->ecc.write_page(mtd, chip, chip->buffers->databuf, 1, page); + memset(chip->data_buf, 0xff, mtd->writesize); + ret = chip->ecc.write_page(mtd, chip, chip->data_buf, 1, page); if (ret) return ret; diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index fca802ef9af3..f8f27c6801a6 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -514,6 +514,8 @@ static const struct nand_ecc_caps __name = { \ * @postpad: padding information for syndrome based ECC generators * @options: ECC specific options (see NAND_ECC_XXX flags defined above) * @priv: pointer to private ECC control data + * @calc_buf: buffer for calculated ECC, size is oobsize. + * @code_buf: buffer for ECC read from flash, size is oobsize. * @hwctl: function to control hardware ECC generator. Must only * be provided if an hardware ECC is available * @calculate: function for ECC calculation or readback from ECC hardware @@ -564,6 +566,8 @@ struct nand_ecc_ctrl { int postpad; unsigned int options; void *priv; + u8 *calc_buf; + u8 *code_buf; void (*hwctl)(struct mtd_info *mtd, int mode); int (*calculate)(struct mtd_info *mtd, const uint8_t *dat, uint8_t *ecc_code); @@ -591,21 +595,6 @@ struct nand_ecc_ctrl { int page); }; -/** - * struct nand_buffers - buffer structure for read/write - * @ecccalc: buffer pointer for calculated ECC, size is oobsize. - * @ecccode: buffer pointer for ECC read from flash, size is oobsize. - * @databuf: buffer pointer for data, size is (page size + oobsize). - * - * Do not change the order of buffers. databuf and oobrbuf must be in - * consecutive order. - */ -struct nand_buffers { - uint8_t *ecccalc; - uint8_t *ecccode; - uint8_t *databuf; -}; - /** * struct nand_sdr_timings - SDR NAND chip timings * @@ -774,7 +763,6 @@ struct nand_manufacturer_ops { * @setup_read_retry: [FLASHSPECIFIC] flash (vendor) specific function for * setting the read-retry mode. Mostly needed for MLC NAND. * @ecc: [BOARDSPECIFIC] ECC control structure - * @buffers: buffer structure for read/write * @buf_align: minimum buffer alignment required by a platform * @hwcontrol: platform-specific hardware control structure * @erase: [REPLACEABLE] erase function @@ -814,6 +802,7 @@ struct nand_manufacturer_ops { * @numchips: [INTERN] number of physical chips * @chipsize: [INTERN] the size of one chip for multichip arrays * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 + * @data_buf: [INTERN] buffer for data, size is (page size + oobsize). * @pagebuf: [INTERN] holds the pagenumber which is currently in * data_buf. * @pagebuf_bitflips: [INTERN] holds the bitflip count for the page which is @@ -892,6 +881,7 @@ struct nand_chip { int numchips; uint64_t chipsize; int pagemask; + u8 *data_buf; int pagebuf; unsigned int pagebuf_bitflips; int subpagesize; @@ -922,7 +912,6 @@ struct nand_chip { struct nand_hw_control *controller; struct nand_ecc_ctrl ecc; - struct nand_buffers *buffers; unsigned long buf_align; struct nand_hw_control hwcontrol; -- cgit v1.2.3 From aeb93af96d0b0f0916aa0f65fe400a1808ea9cb9 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 5 Dec 2017 12:09:29 +0100 Subject: mtd: nand: Only allocate ecc->{calc, code}_buf when actually needed The only users of the ecc->{calc,code}_buf buffers are NAND controller drivers implementing ecc->calculate() and/or ecc->correct(). Since the ->oobsize can be non-negligle, especially on modern NAND devices, we'd better allocate it only when it is actually required. Make ecc->{calc,code}_buf allocation dependent on the presence of ecc->calculate() or ecc->correct(). Signed-off-by: Boris Brezillon Reviewed-by: Masahiro Yamada --- drivers/mtd/nand/nand_base.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) (limited to 'drivers/mtd/nand/nand_base.c') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 32c0239b380a..84d0a5d67e33 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -5318,21 +5318,9 @@ int nand_scan_tail(struct mtd_info *mtd) return -EINVAL; } - ecc->calc_buf = kmalloc(mtd->oobsize, GFP_KERNEL); - if (!ecc->calc_buf) - return -ENOMEM; - - ecc->code_buf = kmalloc(mtd->oobsize, GFP_KERNEL); - if (!ecc->code_buf) { - ret = -ENOMEM; - goto err_free_buf; - } - chip->data_buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); - if (!chip->data_buf) { - ret = -ENOMEM; - goto err_free_buf; - } + if (!chip->data_buf) + return -ENOMEM; /* * FIXME: some NAND manufacturer drivers expect the first die to be @@ -5495,6 +5483,15 @@ int nand_scan_tail(struct mtd_info *mtd) goto err_nand_manuf_cleanup; } + if (ecc->correct || ecc->calculate) { + ecc->calc_buf = kmalloc(mtd->oobsize, GFP_KERNEL); + ecc->code_buf = kmalloc(mtd->oobsize, GFP_KERNEL); + if (!ecc->calc_buf || !ecc->code_buf) { + ret = -ENOMEM; + goto err_nand_manuf_cleanup; + } + } + /* For many systems, the standard OOB write also works for raw */ if (!ecc->read_oob_raw) ecc->read_oob_raw = ecc->read_oob; -- cgit v1.2.3 From 8878b126df769831cb2fa4088c3806538e8305f5 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 9 Nov 2017 14:16:45 +0100 Subject: mtd: nand: add ->exec_op() implementation Introduce a new interface to instruct NAND controllers to send specific NAND operations. The new interface takes the form of a single method called ->exec_op(). This method is designed to replace ->cmd_ctrl(), ->cmdfunc() and ->read/write_byte/word/buf() hooks. ->exec_op() is passed a set of instructions describing the operation to execute. Each instruction has a type (ADDR, CMD, DATA, WAITRDY) and delay. The delay is here to help simple controllers wait enough time between each instruction, advanced controllers with integrated timings control can ignore these delays. Controllers that natively support complex operations (operations formed of several instructions) can use the NAND op parser infrastructure. This infrastructure allows controller drivers to describe the sequence of instructions they support (called nand_op_pattern) and a hook for each of these supported sequences. The core then tries to find the best match for a given NAND operation, and calls the associated hook. Various other helpers are also added to ease NAND controller drivers writing. This new interface should ease support of vendor specific operations in that NAND manufacturer drivers now have a way to check if the controller they are connected to supports a specific operation, and complain or refuse to probe the NAND chip when that's not the case. Suggested-by: Boris Brezillon Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 1013 ++++++++++++++++++++++++++++++++++++++++- drivers/mtd/nand/nand_hynix.c | 9 + include/linux/mtd/rawnand.h | 368 ++++++++++++++- 3 files changed, 1364 insertions(+), 26 deletions(-) (limited to 'drivers/mtd/nand/nand_base.c') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 84d0a5d67e33..ab8ad9e8a8d8 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -688,6 +688,66 @@ static void nand_wait_status_ready(struct mtd_info *mtd, unsigned long timeo) } while (time_before(jiffies, timeo)); }; +/** + * nand_soft_waitrdy - Poll STATUS reg until RDY bit is set to 1 + * @chip: NAND chip structure + * @timeout_ms: Timeout in ms + * + * Poll the STATUS register using ->exec_op() until the RDY bit becomes 1. + * If that does not happen whitin the specified timeout, -ETIMEDOUT is + * returned. + * + * This helper is intended to be used when the controller does not have access + * to the NAND R/B pin. + * + * Be aware that calling this helper from an ->exec_op() implementation means + * ->exec_op() must be re-entrant. + * + * Return 0 if the NAND chip is ready, a negative error otherwise. + */ +int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms) +{ + u8 status = 0; + int ret; + + if (!chip->exec_op) + return -ENOTSUPP; + + ret = nand_status_op(chip, NULL); + if (ret) + return ret; + + timeout_ms = jiffies + msecs_to_jiffies(timeout_ms); + do { + ret = nand_read_data_op(chip, &status, sizeof(status), true); + if (ret) + break; + + if (status & NAND_STATUS_READY) + break; + + /* + * Typical lowest execution time for a tR on most NANDs is 10us, + * use this as polling delay before doing something smarter (ie. + * deriving a delay from the timeout value, timeout_ms/ratio). + */ + udelay(10); + } while (time_before(jiffies, timeout_ms)); + + /* + * We have to exit READ_STATUS mode in order to read real data on the + * bus in case the WAITRDY instruction is preceding a DATA_IN + * instruction. + */ + nand_exit_status_op(chip); + + if (ret) + return ret; + + return status & NAND_STATUS_READY ? 0 : -ETIMEDOUT; +}; +EXPORT_SYMBOL_GPL(nand_soft_waitrdy); + /** * nand_command - [DEFAULT] Send command to NAND device * @mtd: MTD device structure @@ -1237,6 +1297,140 @@ static int nand_init_data_interface(struct nand_chip *chip) return 0; } +/** + * nand_fill_column_cycles - fill the column cycles of an address + * @chip: The NAND chip + * @addrs: Array of address cycles to fill + * @offset_in_page: The offset in the page + * + * Fills the first or the first two bytes of the @addrs field depending + * on the NAND bus width and the page size. + * + * Returns the number of cycles needed to encode the column, or a negative + * error code in case one of the arguments is invalid. + */ +static int nand_fill_column_cycles(struct nand_chip *chip, u8 *addrs, + unsigned int offset_in_page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + /* Make sure the offset is less than the actual page size. */ + if (offset_in_page > mtd->writesize + mtd->oobsize) + return -EINVAL; + + /* + * On small page NANDs, there's a dedicated command to access the OOB + * area, and the column address is relative to the start of the OOB + * area, not the start of the page. Asjust the address accordingly. + */ + if (mtd->writesize <= 512 && offset_in_page >= mtd->writesize) + offset_in_page -= mtd->writesize; + + /* + * The offset in page is expressed in bytes, if the NAND bus is 16-bit + * wide, then it must be divided by 2. + */ + if (chip->options & NAND_BUSWIDTH_16) { + if (WARN_ON(offset_in_page % 2)) + return -EINVAL; + + offset_in_page /= 2; + } + + addrs[0] = offset_in_page; + + /* + * Small page NANDs use 1 cycle for the columns, while large page NANDs + * need 2 + */ + if (mtd->writesize <= 512) + return 1; + + addrs[1] = offset_in_page >> 8; + + return 2; +} + +static int nand_sp_exec_read_page_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, void *buf, + unsigned int len) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + u8 addrs[4]; + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_READ0, 0), + NAND_OP_ADDR(3, addrs, PSEC_TO_NSEC(sdr->tWB_max)), + NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max), + PSEC_TO_NSEC(sdr->tRR_min)), + NAND_OP_DATA_IN(len, buf, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + int ret; + + /* Drop the DATA_IN instruction if len is set to 0. */ + if (!len) + op.ninstrs--; + + if (offset_in_page >= mtd->writesize) + instrs[0].ctx.cmd.opcode = NAND_CMD_READOOB; + else if (offset_in_page >= 256 && + !(chip->options & NAND_BUSWIDTH_16)) + instrs[0].ctx.cmd.opcode = NAND_CMD_READ1; + + ret = nand_fill_column_cycles(chip, addrs, offset_in_page); + if (ret < 0) + return ret; + + addrs[1] = page; + addrs[2] = page >> 8; + + if (chip->options & NAND_ROW_ADDR_3) { + addrs[3] = page >> 16; + instrs[1].ctx.addr.naddrs++; + } + + return nand_exec_op(chip, &op); +} + +static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, void *buf, + unsigned int len) +{ + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + u8 addrs[5]; + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_READ0, 0), + NAND_OP_ADDR(4, addrs, 0), + NAND_OP_CMD(NAND_CMD_READSTART, PSEC_TO_NSEC(sdr->tWB_max)), + NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max), + PSEC_TO_NSEC(sdr->tRR_min)), + NAND_OP_DATA_IN(len, buf, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + int ret; + + /* Drop the DATA_IN instruction if len is set to 0. */ + if (!len) + op.ninstrs--; + + ret = nand_fill_column_cycles(chip, addrs, offset_in_page); + if (ret < 0) + return ret; + + addrs[2] = page; + addrs[3] = page >> 8; + + if (chip->options & NAND_ROW_ADDR_3) { + addrs[4] = page >> 16; + instrs[1].ctx.addr.naddrs++; + } + + return nand_exec_op(chip, &op); +} + /** * nand_read_page_op - Do a READ PAGE operation * @chip: The NAND chip @@ -1261,6 +1455,16 @@ int nand_read_page_op(struct nand_chip *chip, unsigned int page, if (offset_in_page + len > mtd->writesize + mtd->oobsize) return -EINVAL; + if (chip->exec_op) { + if (mtd->writesize > 512) + return nand_lp_exec_read_page_op(chip, page, + offset_in_page, buf, + len); + + return nand_sp_exec_read_page_op(chip, page, offset_in_page, + buf, len); + } + chip->cmdfunc(mtd, NAND_CMD_READ0, offset_in_page, page); if (len) chip->read_buf(mtd, buf, len); @@ -1291,6 +1495,25 @@ static int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf, if (len && !buf) return -EINVAL; + if (chip->exec_op) { + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_PARAM, 0), + NAND_OP_ADDR(1, &page, PSEC_TO_NSEC(sdr->tWB_max)), + NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max), + PSEC_TO_NSEC(sdr->tRR_min)), + NAND_OP_8BIT_DATA_IN(len, buf, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + + /* Drop the DATA_IN instruction if len is set to 0. */ + if (!len) + op.ninstrs--; + + return nand_exec_op(chip, &op); + } + chip->cmdfunc(mtd, NAND_CMD_PARAM, page, -1); for (i = 0; i < len; i++) p[i] = chip->read_byte(mtd); @@ -1323,6 +1546,37 @@ int nand_change_read_column_op(struct nand_chip *chip, if (offset_in_page + len > mtd->writesize + mtd->oobsize) return -EINVAL; + /* Small page NANDs do not support column change. */ + if (mtd->writesize <= 512) + return -ENOTSUPP; + + if (chip->exec_op) { + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + u8 addrs[2] = {}; + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_RNDOUT, 0), + NAND_OP_ADDR(2, addrs, 0), + NAND_OP_CMD(NAND_CMD_RNDOUTSTART, + PSEC_TO_NSEC(sdr->tCCS_min)), + NAND_OP_DATA_IN(len, buf, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + int ret; + + ret = nand_fill_column_cycles(chip, addrs, offset_in_page); + if (ret < 0) + return ret; + + /* Drop the DATA_IN instruction if len is set to 0. */ + if (!len) + op.ninstrs--; + + instrs[3].ctx.data.force_8bit = force_8bit; + + return nand_exec_op(chip, &op); + } + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset_in_page, -1); if (len) chip->read_buf(mtd, buf, len); @@ -1355,6 +1609,11 @@ int nand_read_oob_op(struct nand_chip *chip, unsigned int page, if (offset_in_oob + len > mtd->oobsize) return -EINVAL; + if (chip->exec_op) + return nand_read_page_op(chip, page, + mtd->writesize + offset_in_oob, + buf, len); + chip->cmdfunc(mtd, NAND_CMD_READOOB, offset_in_oob, page); if (len) chip->read_buf(mtd, buf, len); @@ -1363,6 +1622,81 @@ int nand_read_oob_op(struct nand_chip *chip, unsigned int page, } EXPORT_SYMBOL_GPL(nand_read_oob_op); +static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, const void *buf, + unsigned int len, bool prog) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + u8 addrs[5] = {}; + struct nand_op_instr instrs[] = { + /* + * The first instruction will be dropped if we're dealing + * with a large page NAND and adjusted if we're dealing + * with a small page NAND and the page offset is > 255. + */ + NAND_OP_CMD(NAND_CMD_READ0, 0), + NAND_OP_CMD(NAND_CMD_SEQIN, 0), + NAND_OP_ADDR(0, addrs, PSEC_TO_NSEC(sdr->tADL_min)), + NAND_OP_DATA_OUT(len, buf, 0), + NAND_OP_CMD(NAND_CMD_PAGEPROG, PSEC_TO_NSEC(sdr->tWB_max)), + NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + int naddrs = nand_fill_column_cycles(chip, addrs, offset_in_page); + int ret; + u8 status; + + if (naddrs < 0) + return naddrs; + + addrs[naddrs++] = page; + addrs[naddrs++] = page >> 8; + if (chip->options & NAND_ROW_ADDR_3) + addrs[naddrs++] = page >> 16; + + instrs[2].ctx.addr.naddrs = naddrs; + + /* Drop the last two instructions if we're not programming the page. */ + if (!prog) { + op.ninstrs -= 2; + /* Also drop the DATA_OUT instruction if empty. */ + if (!len) + op.ninstrs--; + } + + if (mtd->writesize <= 512) { + /* + * Small pages need some more tweaking: we have to adjust the + * first instruction depending on the page offset we're trying + * to access. + */ + if (offset_in_page >= mtd->writesize) + instrs[0].ctx.cmd.opcode = NAND_CMD_READOOB; + else if (offset_in_page >= 256 && + !(chip->options & NAND_BUSWIDTH_16)) + instrs[0].ctx.cmd.opcode = NAND_CMD_READ1; + } else { + /* + * Drop the first command if we're dealing with a large page + * NAND. + */ + op.instrs++; + op.ninstrs--; + } + + ret = nand_exec_op(chip, &op); + if (!prog || ret) + return ret; + + ret = nand_status_op(chip, &status); + if (ret) + return ret; + + return status; +} + /** * nand_prog_page_begin_op - starts a PROG PAGE operation * @chip: The NAND chip @@ -1388,6 +1722,10 @@ int nand_prog_page_begin_op(struct nand_chip *chip, unsigned int page, if (offset_in_page + len > mtd->writesize + mtd->oobsize) return -EINVAL; + if (chip->exec_op) + return nand_exec_prog_page_op(chip, page, offset_in_page, buf, + len, false); + chip->cmdfunc(mtd, NAND_CMD_SEQIN, offset_in_page, page); if (buf) @@ -1409,11 +1747,35 @@ EXPORT_SYMBOL_GPL(nand_prog_page_begin_op); int nand_prog_page_end_op(struct nand_chip *chip) { struct mtd_info *mtd = nand_to_mtd(chip); - int status; + int ret; + u8 status; - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + if (chip->exec_op) { + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_PAGEPROG, + PSEC_TO_NSEC(sdr->tWB_max)), + NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + + ret = nand_exec_op(chip, &op); + if (ret) + return ret; + + ret = nand_status_op(chip, &status); + if (ret) + return ret; + } else { + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + ret = chip->waitfunc(mtd, chip); + if (ret < 0) + return ret; + + status = ret; + } - status = chip->waitfunc(mtd, chip); if (status & NAND_STATUS_FAIL) return -EIO; @@ -1447,11 +1809,16 @@ int nand_prog_page_op(struct nand_chip *chip, unsigned int page, if (offset_in_page + len > mtd->writesize + mtd->oobsize) return -EINVAL; - chip->cmdfunc(mtd, NAND_CMD_SEQIN, offset_in_page, page); - chip->write_buf(mtd, buf, len); - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + if (chip->exec_op) { + status = nand_exec_prog_page_op(chip, page, offset_in_page, buf, + len, true); + } else { + chip->cmdfunc(mtd, NAND_CMD_SEQIN, offset_in_page, page); + chip->write_buf(mtd, buf, len); + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + } - status = chip->waitfunc(mtd, chip); if (status & NAND_STATUS_FAIL) return -EIO; @@ -1485,6 +1852,35 @@ int nand_change_write_column_op(struct nand_chip *chip, if (offset_in_page + len > mtd->writesize + mtd->oobsize) return -EINVAL; + /* Small page NANDs do not support column change. */ + if (mtd->writesize <= 512) + return -ENOTSUPP; + + if (chip->exec_op) { + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + u8 addrs[2]; + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_RNDIN, 0), + NAND_OP_ADDR(2, addrs, PSEC_TO_NSEC(sdr->tCCS_min)), + NAND_OP_DATA_OUT(len, buf, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + int ret; + + ret = nand_fill_column_cycles(chip, addrs, offset_in_page); + if (ret < 0) + return ret; + + instrs[2].ctx.data.force_8bit = force_8bit; + + /* Drop the DATA_OUT instruction if len is set to 0. */ + if (!len) + op.ninstrs--; + + return nand_exec_op(chip, &op); + } + chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset_in_page, -1); if (len) chip->write_buf(mtd, buf, len); @@ -1516,6 +1912,23 @@ int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf, if (len && !buf) return -EINVAL; + if (chip->exec_op) { + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_READID, 0), + NAND_OP_ADDR(1, &addr, PSEC_TO_NSEC(sdr->tADL_min)), + NAND_OP_8BIT_DATA_IN(len, buf, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + + /* Drop the DATA_IN instruction if len is set to 0. */ + if (!len) + op.ninstrs--; + + return nand_exec_op(chip, &op); + } + chip->cmdfunc(mtd, NAND_CMD_READID, addr, -1); for (i = 0; i < len; i++) @@ -1540,6 +1953,22 @@ int nand_status_op(struct nand_chip *chip, u8 *status) { struct mtd_info *mtd = nand_to_mtd(chip); + if (chip->exec_op) { + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_STATUS, + PSEC_TO_NSEC(sdr->tADL_min)), + NAND_OP_8BIT_DATA_IN(1, status, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + + if (!status) + op.ninstrs--; + + return nand_exec_op(chip, &op); + } + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); if (status) *status = chip->read_byte(mtd); @@ -1563,6 +1992,15 @@ int nand_exit_status_op(struct nand_chip *chip) { struct mtd_info *mtd = nand_to_mtd(chip); + if (chip->exec_op) { + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_READ0, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + + return nand_exec_op(chip, &op); + } + chip->cmdfunc(mtd, NAND_CMD_READ0, -1, -1); return 0; @@ -1585,14 +2023,42 @@ int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock) struct mtd_info *mtd = nand_to_mtd(chip); unsigned int page = eraseblock << (chip->phys_erase_shift - chip->page_shift); - int status; + int ret; + u8 status; - chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); - chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); + if (chip->exec_op) { + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + u8 addrs[3] = { page, page >> 8, page >> 16 }; + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_ERASE1, 0), + NAND_OP_ADDR(2, addrs, 0), + NAND_OP_CMD(NAND_CMD_ERASE2, + PSEC_TO_MSEC(sdr->tWB_max)), + NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tBERS_max), 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); - status = chip->waitfunc(mtd, chip); - if (status < 0) - return status; + if (chip->options & NAND_ROW_ADDR_3) + instrs[1].ctx.addr.naddrs++; + + ret = nand_exec_op(chip, &op); + if (ret) + return ret; + + ret = nand_status_op(chip, &status); + if (ret) + return ret; + } else { + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); + chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); + + ret = chip->waitfunc(mtd, chip); + if (ret < 0) + return ret; + + status = ret; + } if (status & NAND_STATUS_FAIL) return -EIO; @@ -1618,13 +2084,40 @@ static int nand_set_features_op(struct nand_chip *chip, u8 feature, { struct mtd_info *mtd = nand_to_mtd(chip); const u8 *params = data; - int i, status; + int i, ret; + u8 status; - chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, feature, -1); - for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) - chip->write_byte(mtd, params[i]); + if (chip->exec_op) { + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_SET_FEATURES, 0), + NAND_OP_ADDR(1, &feature, PSEC_TO_NSEC(sdr->tADL_min)), + NAND_OP_8BIT_DATA_OUT(ONFI_SUBFEATURE_PARAM_LEN, data, + PSEC_TO_NSEC(sdr->tWB_max)), + NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tFEAT_max), 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + + ret = nand_exec_op(chip, &op); + if (ret) + return ret; + + ret = nand_status_op(chip, &status); + if (ret) + return ret; + } else { + chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, feature, -1); + for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) + chip->write_byte(mtd, params[i]); + + ret = chip->waitfunc(mtd, chip); + if (ret < 0) + return ret; + + status = ret; + } - status = chip->waitfunc(mtd, chip); if (status & NAND_STATUS_FAIL) return -EIO; @@ -1650,6 +2143,22 @@ static int nand_get_features_op(struct nand_chip *chip, u8 feature, u8 *params = data; int i; + if (chip->exec_op) { + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_GET_FEATURES, 0), + NAND_OP_ADDR(1, &feature, PSEC_TO_NSEC(sdr->tWB_max)), + NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tFEAT_max), + PSEC_TO_NSEC(sdr->tRR_min)), + NAND_OP_8BIT_DATA_IN(ONFI_SUBFEATURE_PARAM_LEN, + data, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + + return nand_exec_op(chip, &op); + } + chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, feature, -1); for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) params[i] = chip->read_byte(mtd); @@ -1671,6 +2180,18 @@ int nand_reset_op(struct nand_chip *chip) { struct mtd_info *mtd = nand_to_mtd(chip); + if (chip->exec_op) { + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + struct nand_op_instr instrs[] = { + NAND_OP_CMD(NAND_CMD_RESET, PSEC_TO_NSEC(sdr->tWB_max)), + NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tRST_max), 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + + return nand_exec_op(chip, &op); + } + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); return 0; @@ -1698,6 +2219,17 @@ int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len, if (!len || !buf) return -EINVAL; + if (chip->exec_op) { + struct nand_op_instr instrs[] = { + NAND_OP_DATA_IN(len, buf, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + + instrs[0].ctx.data.force_8bit = force_8bit; + + return nand_exec_op(chip, &op); + } + if (force_8bit) { u8 *p = buf; unsigned int i; @@ -1733,6 +2265,17 @@ int nand_write_data_op(struct nand_chip *chip, const void *buf, if (!len || !buf) return -EINVAL; + if (chip->exec_op) { + struct nand_op_instr instrs[] = { + NAND_OP_DATA_OUT(len, buf, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + + instrs[0].ctx.data.force_8bit = force_8bit; + + return nand_exec_op(chip, &op); + } + if (force_8bit) { const u8 *p = buf; unsigned int i; @@ -1747,6 +2290,420 @@ int nand_write_data_op(struct nand_chip *chip, const void *buf, } EXPORT_SYMBOL_GPL(nand_write_data_op); +/** + * struct nand_op_parser_ctx - Context used by the parser + * @instrs: array of all the instructions that must be addressed + * @ninstrs: length of the @instrs array + * @subop: Sub-operation to be passed to the NAND controller + * + * This structure is used by the core to split NAND operations into + * sub-operations that can be handled by the NAND controller. + */ +struct nand_op_parser_ctx { + const struct nand_op_instr *instrs; + unsigned int ninstrs; + struct nand_subop subop; +}; + +/** + * nand_op_parser_must_split_instr - Checks if an instruction must be split + * @pat: the parser pattern element that matches @instr + * @instr: pointer to the instruction to check + * @start_offset: this is an in/out parameter. If @instr has already been + * split, then @start_offset is the offset from which to start + * (either an address cycle or an offset in the data buffer). + * Conversely, if the function returns true (ie. instr must be + * split), this parameter is updated to point to the first + * data/address cycle that has not been taken care of. + * + * Some NAND controllers are limited and cannot send X address cycles with a + * unique operation, or cannot read/write more than Y bytes at the same time. + * In this case, split the instruction that does not fit in a single + * controller-operation into two or more chunks. + * + * Returns true if the instruction must be split, false otherwise. + * The @start_offset parameter is also updated to the offset at which the next + * bundle of instruction must start (if an address or a data instruction). + */ +static bool +nand_op_parser_must_split_instr(const struct nand_op_parser_pattern_elem *pat, + const struct nand_op_instr *instr, + unsigned int *start_offset) +{ + switch (pat->type) { + case NAND_OP_ADDR_INSTR: + if (!pat->addr.maxcycles) + break; + + if (instr->ctx.addr.naddrs - *start_offset > + pat->addr.maxcycles) { + *start_offset += pat->addr.maxcycles; + return true; + } + break; + + case NAND_OP_DATA_IN_INSTR: + case NAND_OP_DATA_OUT_INSTR: + if (!pat->data.maxlen) + break; + + if (instr->ctx.data.len - *start_offset > pat->data.maxlen) { + *start_offset += pat->data.maxlen; + return true; + } + break; + + default: + break; + } + + return false; +} + +/** + * nand_op_parser_match_pat - Checks if a pattern matches the instructions + * remaining in the parser context + * @pat: the pattern to test + * @ctx: the parser context structure to match with the pattern @pat + * + * Check if @pat matches the set or a sub-set of instructions remaining in @ctx. + * Returns true if this is the case, false ortherwise. When true is returned, + * @ctx->subop is updated with the set of instructions to be passed to the + * controller driver. + */ +static bool +nand_op_parser_match_pat(const struct nand_op_parser_pattern *pat, + struct nand_op_parser_ctx *ctx) +{ + unsigned int instr_offset = ctx->subop.first_instr_start_off; + const struct nand_op_instr *end = ctx->instrs + ctx->ninstrs; + const struct nand_op_instr *instr = ctx->subop.instrs; + unsigned int i, ninstrs; + + for (i = 0, ninstrs = 0; i < pat->nelems && instr < end; i++) { + /* + * The pattern instruction does not match the operation + * instruction. If the instruction is marked optional in the + * pattern definition, we skip the pattern element and continue + * to the next one. If the element is mandatory, there's no + * match and we can return false directly. + */ + if (instr->type != pat->elems[i].type) { + if (!pat->elems[i].optional) + return false; + + continue; + } + + /* + * Now check the pattern element constraints. If the pattern is + * not able to handle the whole instruction in a single step, + * we have to split it. + * The last_instr_end_off value comes back updated to point to + * the position where we have to split the instruction (the + * start of the next subop chunk). + */ + if (nand_op_parser_must_split_instr(&pat->elems[i], instr, + &instr_offset)) { + ninstrs++; + i++; + break; + } + + instr++; + ninstrs++; + instr_offset = 0; + } + + /* + * This can happen if all instructions of a pattern are optional. + * Still, if there's not at least one instruction handled by this + * pattern, this is not a match, and we should try the next one (if + * any). + */ + if (!ninstrs) + return false; + + /* + * We had a match on the pattern head, but the pattern may be longer + * than the instructions we're asked to execute. We need to make sure + * there's no mandatory elements in the pattern tail. + */ + for (; i < pat->nelems; i++) { + if (!pat->elems[i].optional) + return false; + } + + /* + * We have a match: update the subop structure accordingly and return + * true. + */ + ctx->subop.ninstrs = ninstrs; + ctx->subop.last_instr_end_off = instr_offset; + + return true; +} + +#if IS_ENABLED(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) +static void nand_op_parser_trace(const struct nand_op_parser_ctx *ctx) +{ + const struct nand_op_instr *instr; + char *prefix = " "; + unsigned int i; + + pr_debug("executing subop:\n"); + + for (i = 0; i < ctx->ninstrs; i++) { + instr = &ctx->instrs[i]; + + if (instr == &ctx->subop.instrs[0]) + prefix = " ->"; + + switch (instr->type) { + case NAND_OP_CMD_INSTR: + pr_debug("%sCMD [0x%02x]\n", prefix, + instr->ctx.cmd.opcode); + break; + case NAND_OP_ADDR_INSTR: + pr_debug("%sADDR [%d cyc: %*ph]\n", prefix, + instr->ctx.addr.naddrs, + instr->ctx.addr.naddrs < 64 ? + instr->ctx.addr.naddrs : 64, + instr->ctx.addr.addrs); + break; + case NAND_OP_DATA_IN_INSTR: + pr_debug("%sDATA_IN [%d B%s]\n", prefix, + instr->ctx.data.len, + instr->ctx.data.force_8bit ? + ", force 8-bit" : ""); + break; + case NAND_OP_DATA_OUT_INSTR: + pr_debug("%sDATA_OUT [%d B%s]\n", prefix, + instr->ctx.data.len, + instr->ctx.data.force_8bit ? + ", force 8-bit" : ""); + break; + case NAND_OP_WAITRDY_INSTR: + pr_debug("%sWAITRDY [max %d ms]\n", prefix, + instr->ctx.waitrdy.timeout_ms); + break; + } + + if (instr == &ctx->subop.instrs[ctx->subop.ninstrs - 1]) + prefix = " "; + } +} +#else +static void nand_op_parser_trace(const struct nand_op_parser_ctx *ctx) +{ + /* NOP */ +} +#endif + +/** + * nand_op_parser_exec_op - exec_op parser + * @chip: the NAND chip + * @parser: patterns description provided by the controller driver + * @op: the NAND operation to address + * @check_only: when true, the function only checks if @op can be handled but + * does not execute the operation + * + * Helper function designed to ease integration of NAND controller drivers that + * only support a limited set of instruction sequences. The supported sequences + * are described in @parser, and the framework takes care of splitting @op into + * multiple sub-operations (if required) and pass them back to the ->exec() + * callback of the matching pattern if @check_only is set to false. + * + * NAND controller drivers should call this function from their own ->exec_op() + * implementation. + * + * Returns 0 on success, a negative error code otherwise. A failure can be + * caused by an unsupported operation (none of the supported patterns is able + * to handle the requested operation), or an error returned by one of the + * matching pattern->exec() hook. + */ +int nand_op_parser_exec_op(struct nand_chip *chip, + const struct nand_op_parser *parser, + const struct nand_operation *op, bool check_only) +{ + struct nand_op_parser_ctx ctx = { + .subop.instrs = op->instrs, + .instrs = op->instrs, + .ninstrs = op->ninstrs, + }; + unsigned int i; + + while (ctx.subop.instrs < op->instrs + op->ninstrs) { + int ret; + + for (i = 0; i < parser->npatterns; i++) { + const struct nand_op_parser_pattern *pattern; + + pattern = &parser->patterns[i]; + if (!nand_op_parser_match_pat(pattern, &ctx)) + continue; + + nand_op_parser_trace(&ctx); + + if (check_only) + break; + + ret = pattern->exec(chip, &ctx.subop); + if (ret) + return ret; + + break; + } + + if (i == parser->npatterns) { + pr_debug("->exec_op() parser: pattern not found!\n"); + return -ENOTSUPP; + } + + /* + * Update the context structure by pointing to the start of the + * next subop. + */ + ctx.subop.instrs = ctx.subop.instrs + ctx.subop.ninstrs; + if (ctx.subop.last_instr_end_off) + ctx.subop.instrs -= 1; + + ctx.subop.first_instr_start_off = ctx.subop.last_instr_end_off; + } + + return 0; +} +EXPORT_SYMBOL_GPL(nand_op_parser_exec_op); + +static bool nand_instr_is_data(const struct nand_op_instr *instr) +{ + return instr && (instr->type == NAND_OP_DATA_IN_INSTR || + instr->type == NAND_OP_DATA_OUT_INSTR); +} + +static bool nand_subop_instr_is_valid(const struct nand_subop *subop, + unsigned int instr_idx) +{ + return subop && instr_idx < subop->ninstrs; +} + +static int nand_subop_get_start_off(const struct nand_subop *subop, + unsigned int instr_idx) +{ + if (instr_idx) + return 0; + + return subop->first_instr_start_off; +} + +/** + * nand_subop_get_addr_start_off - Get the start offset in an address array + * @subop: The entire sub-operation + * @instr_idx: Index of the instruction inside the sub-operation + * + * During driver development, one could be tempted to directly use the + * ->addr.addrs field of address instructions. This is wrong as address + * instructions might be split. + * + * Given an address instruction, returns the offset of the first cycle to issue. + */ +int nand_subop_get_addr_start_off(const struct nand_subop *subop, + unsigned int instr_idx) +{ + if (!nand_subop_instr_is_valid(subop, instr_idx) || + subop->instrs[instr_idx].type != NAND_OP_ADDR_INSTR) + return -EINVAL; + + return nand_subop_get_start_off(subop, instr_idx); +} +EXPORT_SYMBOL_GPL(nand_subop_get_addr_start_off); + +/** + * nand_subop_get_num_addr_cyc - Get the remaining address cycles to assert + * @subop: The entire sub-operation + * @instr_idx: Index of the instruction inside the sub-operation + * + * During driver development, one could be tempted to directly use the + * ->addr->naddrs field of a data instruction. This is wrong as instructions + * might be split. + * + * Given an address instruction, returns the number of address cycle to issue. + */ +int nand_subop_get_num_addr_cyc(const struct nand_subop *subop, + unsigned int instr_idx) +{ + int start_off, end_off; + + if (!nand_subop_instr_is_valid(subop, instr_idx) || + subop->instrs[instr_idx].type != NAND_OP_ADDR_INSTR) + return -EINVAL; + + start_off = nand_subop_get_addr_start_off(subop, instr_idx); + + if (instr_idx == subop->ninstrs - 1 && + subop->last_instr_end_off) + end_off = subop->last_instr_end_off; + else + end_off = subop->instrs[instr_idx].ctx.addr.naddrs; + + return end_off - start_off; +} +EXPORT_SYMBOL_GPL(nand_subop_get_num_addr_cyc); + +/** + * nand_subop_get_data_start_off - Get the start offset in a data array + * @subop: The entire sub-operation + * @instr_idx: Index of the instruction inside the sub-operation + * + * During driver development, one could be tempted to directly use the + * ->data->buf.{in,out} field of data instructions. This is wrong as data + * instructions might be split. + * + * Given a data instruction, returns the offset to start from. + */ +int nand_subop_get_data_start_off(const struct nand_subop *subop, + unsigned int instr_idx) +{ + if (!nand_subop_instr_is_valid(subop, instr_idx) || + !nand_instr_is_data(&subop->instrs[instr_idx])) + return -EINVAL; + + return nand_subop_get_start_off(subop, instr_idx); +} +EXPORT_SYMBOL_GPL(nand_subop_get_data_start_off); + +/** + * nand_subop_get_data_len - Get the number of bytes to retrieve + * @subop: The entire sub-operation + * @instr_idx: Index of the instruction inside the sub-operation + * + * During driver development, one could be tempted to directly use the + * ->data->len field of a data instruction. This is wrong as data instructions + * might be split. + * + * Returns the length of the chunk of data to send/receive. + */ +int nand_subop_get_data_len(const struct nand_subop *subop, + unsigned int instr_idx) +{ + int start_off = 0, end_off; + + if (!nand_subop_instr_is_valid(subop, instr_idx) || + !nand_instr_is_data(&subop->instrs[instr_idx])) + return -EINVAL; + + start_off = nand_subop_get_data_start_off(subop, instr_idx); + + if (instr_idx == subop->ninstrs - 1 && + subop->last_instr_end_off) + end_off = subop->last_instr_end_off; + else + end_off = subop->instrs[instr_idx].ctx.data.len; + + return end_off - start_off; +} +EXPORT_SYMBOL_GPL(nand_subop_get_data_len); + /** * nand_reset - Reset and initialize a NAND device * @chip: The NAND chip @@ -4002,7 +4959,7 @@ static void nand_set_defaults(struct nand_chip *chip) chip->chip_delay = 20; /* check, if a user supplied command function given */ - if (chip->cmdfunc == NULL) + if (!chip->cmdfunc && !chip->exec_op) chip->cmdfunc = nand_command; /* check, if a user supplied wait function given */ @@ -4894,15 +5851,21 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, if (!mtd->name && mtd->dev.parent) mtd->name = dev_name(mtd->dev.parent); - if ((!chip->cmdfunc || !chip->select_chip) && !chip->cmd_ctrl) { + /* + * ->cmdfunc() is legacy and will only be used if ->exec_op() is not + * populated. + */ + if (!chip->exec_op) { /* - * Default functions assigned for chip_select() and - * cmdfunc() both expect cmd_ctrl() to be populated, - * so we need to check that that's the case + * Default functions assigned for ->cmdfunc() and + * ->select_chip() both expect ->cmd_ctrl() to be populated. */ - pr_err("chip.cmd_ctrl() callback is not provided"); - return -EINVAL; + if ((!chip->cmdfunc || !chip->select_chip) && !chip->cmd_ctrl) { + pr_err("->cmd_ctrl() should be provided\n"); + return -EINVAL; + } } + /* Set the default functions */ nand_set_defaults(chip); diff --git a/drivers/mtd/nand/nand_hynix.c b/drivers/mtd/nand/nand_hynix.c index bae0da2aa2a8..d542908a0ebb 100644 --- a/drivers/mtd/nand/nand_hynix.c +++ b/drivers/mtd/nand/nand_hynix.c @@ -81,6 +81,15 @@ static int hynix_nand_cmd_op(struct nand_chip *chip, u8 cmd) { struct mtd_info *mtd = nand_to_mtd(chip); + if (chip->exec_op) { + struct nand_op_instr instrs[] = { + NAND_OP_CMD(cmd, 0), + }; + struct nand_operation op = NAND_OPERATION(instrs); + + return nand_exec_op(chip, &op); + } + chip->cmdfunc(mtd, cmd, -1, -1); return 0; diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index f8f27c6801a6..469dc724f5df 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -734,6 +734,350 @@ struct nand_manufacturer_ops { void (*cleanup)(struct nand_chip *chip); }; +/** + * struct nand_op_cmd_instr - Definition of a command instruction + * @opcode: the command to issue in one cycle + */ +struct nand_op_cmd_instr { + u8 opcode; +}; + +/** + * struct nand_op_addr_instr - Definition of an address instruction + * @naddrs: length of the @addrs array + * @addrs: array containing the address cycles to issue + */ +struct nand_op_addr_instr { + unsigned int naddrs; + const u8 *addrs; +}; + +/** + * struct nand_op_data_instr - Definition of a data instruction + * @len: number of data bytes to move + * @in: buffer to fill when reading from the NAND chip + * @out: buffer to read from when writing to the NAND chip + * @force_8bit: force 8-bit access + * + * Please note that "in" and "out" are inverted from the ONFI specification + * and are from the controller perspective, so a "in" is a read from the NAND + * chip while a "out" is a write to the NAND chip. + */ +struct nand_op_data_instr { + unsigned int len; + union { + void *in; + const void *out; + } buf; + bool force_8bit; +}; + +/** + * struct nand_op_waitrdy_instr - Definition of a wait ready instruction + * @timeout_ms: maximum delay while waiting for the ready/busy pin in ms + */ +struct nand_op_waitrdy_instr { + unsigned int timeout_ms; +}; + +/** + * enum nand_op_instr_type - Definition of all instruction types + * @NAND_OP_CMD_INSTR: command instruction + * @NAND_OP_ADDR_INSTR: address instruction + * @NAND_OP_DATA_IN_INSTR: data in instruction + * @NAND_OP_DATA_OUT_INSTR: data out instruction + * @NAND_OP_WAITRDY_INSTR: wait ready instruction + */ +enum nand_op_instr_type { + NAND_OP_CMD_INSTR, + NAND_OP_ADDR_INSTR, + NAND_OP_DATA_IN_INSTR, + NAND_OP_DATA_OUT_INSTR, + NAND_OP_WAITRDY_INSTR, +}; + +/** + * struct nand_op_instr - Instruction object + * @type: the instruction type + * @cmd/@addr/@data/@waitrdy: extra data associated to the instruction. + * You'll have to use the appropriate element + * depending on @type + * @delay_ns: delay the controller should apply after the instruction has been + * issued on the bus. Most modern controllers have internal timings + * control logic, and in this case, the controller driver can ignore + * this field. + */ +struct nand_op_instr { + enum nand_op_instr_type type; + union { + struct nand_op_cmd_instr cmd; + struct nand_op_addr_instr addr; + struct nand_op_data_instr data; + struct nand_op_waitrdy_instr waitrdy; + } ctx; + unsigned int delay_ns; +}; + +/* + * Special handling must be done for the WAITRDY timeout parameter as it usually + * is either tPROG (after a prog), tR (before a read), tRST (during a reset) or + * tBERS (during an erase) which all of them are u64 values that cannot be + * divided by usual kernel macros and must be handled with the special + * DIV_ROUND_UP_ULL() macro. + */ +#define __DIVIDE(dividend, divisor) ({ \ + sizeof(dividend) == sizeof(u32) ? \ + DIV_ROUND_UP(dividend, divisor) : \ + DIV_ROUND_UP_ULL(dividend, divisor); \ + }) +#define PSEC_TO_NSEC(x) __DIVIDE(x, 1000) +#define PSEC_TO_MSEC(x) __DIVIDE(x, 1000000000) + +#define NAND_OP_CMD(id, ns) \ + { \ + .type = NAND_OP_CMD_INSTR, \ + .ctx.cmd.opcode = id, \ + .delay_ns = ns, \ + } + +#define NAND_OP_ADDR(ncycles, cycles, ns) \ + { \ + .type = NAND_OP_ADDR_INSTR, \ + .ctx.addr = { \ + .naddrs = ncycles, \ + .addrs = cycles, \ + }, \ + .delay_ns = ns, \ + } + +#define NAND_OP_DATA_IN(l, b, ns) \ + { \ + .type = NAND_OP_DATA_IN_INSTR, \ + .ctx.data = { \ + .len = l, \ + .buf.in = b, \ + .force_8bit = false, \ + }, \ + .delay_ns = ns, \ + } + +#define NAND_OP_DATA_OUT(l, b, ns) \ + { \ + .type = NAND_OP_DATA_OUT_INSTR, \ + .ctx.data = { \ + .len = l, \ + .buf.out = b, \ + .force_8bit = false, \ + }, \ + .delay_ns = ns, \ + } + +#define NAND_OP_8BIT_DATA_IN(l, b, ns) \ + { \ + .type = NAND_OP_DATA_IN_INSTR, \ + .ctx.data = { \ + .len = l, \ + .buf.in = b, \ + .force_8bit = true, \ + }, \ + .delay_ns = ns, \ + } + +#define NAND_OP_8BIT_DATA_OUT(l, b, ns) \ + { \ + .type = NAND_OP_DATA_OUT_INSTR, \ + .ctx.data = { \ + .len = l, \ + .buf.out = b, \ + .force_8bit = true, \ + }, \ + .delay_ns = ns, \ + } + +#define NAND_OP_WAIT_RDY(tout_ms, ns) \ + { \ + .type = NAND_OP_WAITRDY_INSTR, \ + .ctx.waitrdy.timeout_ms = tout_ms, \ + .delay_ns = ns, \ + } + +/** + * struct nand_subop - a sub operation + * @instrs: array of instructions + * @ninstrs: length of the @instrs array + * @first_instr_start_off: offset to start from for the first instruction + * of the sub-operation + * @last_instr_end_off: offset to end at (excluded) for the last instruction + * of the sub-operation + * + * Both @first_instr_start_off and @last_instr_end_off only apply to data or + * address instructions. + * + * When an operation cannot be handled as is by the NAND controller, it will + * be split by the parser into sub-operations which will be passed to the + * controller driver. + */ +struct nand_subop { + const struct nand_op_instr *instrs; + unsigned int ninstrs; + unsigned int first_instr_start_off; + unsigned int last_instr_end_off; +}; + +int nand_subop_get_addr_start_off(const struct nand_subop *subop, + unsigned int op_id); +int nand_subop_get_num_addr_cyc(const struct nand_subop *subop, + unsigned int op_id); +int nand_subop_get_data_start_off(const struct nand_subop *subop, + unsigned int op_id); +int nand_subop_get_data_len(const struct nand_subop *subop, + unsigned int op_id); + +/** + * struct nand_op_parser_addr_constraints - Constraints for address instructions + * @maxcycles: maximum number of address cycles the controller can issue in a + * single step + */ +struct nand_op_parser_addr_constraints { + unsigned int maxcycles; +}; + +/** + * struct nand_op_parser_data_constraints - Constraints for data instructions + * @maxlen: maximum data length that the controller can handle in a single step + */ +struct nand_op_parser_data_constraints { + unsigned int maxlen; +}; + +/** + * struct nand_op_parser_pattern_elem - One element of a pattern + * @type: the instructuction type + * @optional: whether this element of the pattern is optional or mandatory + * @addr/@data: address or data constraint (number of cycles or data length) + */ +struct nand_op_parser_pattern_elem { + enum nand_op_instr_type type; + bool optional; + union { + struct nand_op_parser_addr_constraints addr; + struct nand_op_parser_data_constraints data; + }; +}; + +#define NAND_OP_PARSER_PAT_CMD_ELEM(_opt) \ + { \ + .type = NAND_OP_CMD_INSTR, \ + .optional = _opt, \ + } + +#define NAND_OP_PARSER_PAT_ADDR_ELEM(_opt, _maxcycles) \ + { \ + .type = NAND_OP_ADDR_INSTR, \ + .optional = _opt, \ + .addr.maxcycles = _maxcycles, \ + } + +#define NAND_OP_PARSER_PAT_DATA_IN_ELEM(_opt, _maxlen) \ + { \ + .type = NAND_OP_DATA_IN_INSTR, \ + .optional = _opt, \ + .data.maxlen = _maxlen, \ + } + +#define NAND_OP_PARSER_PAT_DATA_OUT_ELEM(_opt, _maxlen) \ + { \ + .type = NAND_OP_DATA_OUT_INSTR, \ + .optional = _opt, \ + .data.maxlen = _maxlen, \ + } + +#define NAND_OP_PARSER_PAT_WAITRDY_ELEM(_opt) \ + { \ + .type = NAND_OP_WAITRDY_INSTR, \ + .optional = _opt, \ + } + +/** + * struct nand_op_parser_pattern - NAND sub-operation pattern descriptor + * @elems: array of pattern elements + * @nelems: number of pattern elements in @elems array + * @exec: the function that will issue a sub-operation + * + * A pattern is a list of elements, each element reprensenting one instruction + * with its constraints. The pattern itself is used by the core to match NAND + * chip operation with NAND controller operations. + * Once a match between a NAND controller operation pattern and a NAND chip + * operation (or a sub-set of a NAND operation) is found, the pattern ->exec() + * hook is called so that the controller driver can issue the operation on the + * bus. + * + * Controller drivers should declare as many patterns as they support and pass + * this list of patterns (created with the help of the following macro) to + * the nand_op_parser_exec_op() helper. + */ +struct nand_op_parser_pattern { + const struct nand_op_parser_pattern_elem *elems; + unsigned int nelems; + int (*exec)(struct nand_chip *chip, const struct nand_subop *subop); +}; + +#define NAND_OP_PARSER_PATTERN(_exec, ...) \ + { \ + .exec = _exec, \ + .elems = (struct nand_op_parser_pattern_elem[]) { __VA_ARGS__ }, \ + .nelems = sizeof((struct nand_op_parser_pattern_elem[]) { __VA_ARGS__ }) / \ + sizeof(struct nand_op_parser_pattern_elem), \ + } + +/** + * struct nand_op_parser - NAND controller operation parser descriptor + * @patterns: array of supported patterns + * @npatterns: length of the @patterns array + * + * The parser descriptor is just an array of supported patterns which will be + * iterated by nand_op_parser_exec_op() everytime it tries to execute an + * NAND operation (or tries to determine if a specific operation is supported). + * + * It is worth mentioning that patterns will be tested in their declaration + * order, and the first match will be taken, so it's important to order patterns + * appropriately so that simple/inefficient patterns are placed at the end of + * the list. Usually, this is where you put single instruction patterns. + */ +struct nand_op_parser { + const struct nand_op_parser_pattern *patterns; + unsigned int npatterns; +}; + +#define NAND_OP_PARSER(...) \ + { \ + .patterns = (struct nand_op_parser_pattern[]) { __VA_ARGS__ }, \ + .npatterns = sizeof((struct nand_op_parser_pattern[]) { __VA_ARGS__ }) / \ + sizeof(struct nand_op_parser_pattern), \ + } + +/** + * struct nand_operation - NAND operation descriptor + * @instrs: array of instructions to execute + * @ninstrs: length of the @instrs array + * + * The actual operation structure that will be passed to chip->exec_op(). + */ +struct nand_operation { + const struct nand_op_instr *instrs; + unsigned int ninstrs; +}; + +#define NAND_OPERATION(_instrs) \ + { \ + .instrs = _instrs, \ + .ninstrs = ARRAY_SIZE(_instrs), \ + } + +int nand_op_parser_exec_op(struct nand_chip *chip, + const struct nand_op_parser *parser, + const struct nand_operation *op, bool check_only); + /** * struct nand_chip - NAND Private Flash Chip Data * @mtd: MTD device registered to the MTD framework @@ -760,6 +1104,10 @@ struct nand_manufacturer_ops { * commands to the chip. * @waitfunc: [REPLACEABLE] hardwarespecific function for wait on * ready. + * @exec_op: controller specific method to execute NAND operations. + * This method replaces ->cmdfunc(), + * ->{read,write}_{buf,byte,word}(), ->dev_ready() and + * ->waifunc(). * @setup_read_retry: [FLASHSPECIFIC] flash (vendor) specific function for * setting the read-retry mode. Mostly needed for MLC NAND. * @ecc: [BOARDSPECIFIC] ECC control structure @@ -859,6 +1207,9 @@ struct nand_chip { void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr); int(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this); + int (*exec_op)(struct nand_chip *chip, + const struct nand_operation *op, + bool check_only); int (*erase)(struct mtd_info *mtd, int page); int (*scan_bbt)(struct mtd_info *mtd); int (*onfi_set_features)(struct mtd_info *mtd, struct nand_chip *chip, @@ -869,7 +1220,6 @@ struct nand_chip { int (*setup_data_interface)(struct mtd_info *mtd, int chipnr, const struct nand_data_interface *conf); - int chip_delay; unsigned int options; unsigned int bbt_options; @@ -929,6 +1279,15 @@ struct nand_chip { } manufacturer; }; +static inline int nand_exec_op(struct nand_chip *chip, + const struct nand_operation *op) +{ + if (!chip->exec_op) + return -ENOTSUPP; + + return chip->exec_op(chip, op, false); +} + extern const struct mtd_ooblayout_ops nand_ooblayout_sp_ops; extern const struct mtd_ooblayout_ops nand_ooblayout_lp_ops; @@ -1320,4 +1679,11 @@ void nand_cleanup(struct nand_chip *chip); /* Default extended ID decoding function */ void nand_decode_ext_id(struct nand_chip *chip); + +/* + * External helper for controller drivers that have to implement the WAITRDY + * instruction and have no physical pin to check it. + */ +int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms); + #endif /* __LINUX_MTD_RAWNAND_H */ -- cgit v1.2.3 From d787b8b3509a78b1bf922cc3a1061711074847a8 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 22 Dec 2017 18:12:41 +0100 Subject: mtd: nand: Fix unfinished comment in nand_init_data_interface() Give an unfinished comment a meaning. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/mtd/nand/nand_base.c') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index ab8ad9e8a8d8..96c97588e1ba 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1284,7 +1284,10 @@ static int nand_init_data_interface(struct nand_chip *chip) if (ret) continue; - /* Pass -1 to only */ + /* + * Pass NAND_DATA_IFACE_CHECK_ONLY to only check if the + * controller supports the requested timings. + */ ret = chip->setup_data_interface(mtd, NAND_DATA_IFACE_CHECK_ONLY, &chip->data_interface); -- cgit v1.2.3 From 87e89ce8d0d14f573c068c61bec2117751fb5103 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 12 Jan 2018 10:13:36 +0100 Subject: mtd: nand: Fix nand_do_read_oob() return value Starting from commit 041e4575f034 ("mtd: nand: handle ECC errors in OOB"), nand_do_read_oob() (from the NAND core) did return 0 or a negative error, and the MTD layer expected it. However, the trend for the NAND layer is now to return an error or a positive number of bitflips. Deciding which status to return to the user belongs to the MTD layer. Commit e47f68587b82 ("mtd: check for max_bitflips in mtd_read_oob()") brought this logic to the mtd_read_oob() function while the return value coming from nand_do_read_oob() (called by the ->_read_oob() hook) was left unchanged. Fixes: e47f68587b82 ("mtd: check for max_bitflips in mtd_read_oob()") Cc: stable@vger.kernel.org Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/mtd/nand/nand_base.c') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 96c97588e1ba..3ff77bef9739 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3797,6 +3797,7 @@ EXPORT_SYMBOL(nand_write_oob_syndrome); static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { + unsigned int max_bitflips = 0; int page, realpage, chipnr; struct nand_chip *chip = mtd_to_nand(mtd); struct mtd_ecc_stats stats; @@ -3854,6 +3855,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, nand_wait_ready(mtd); } + max_bitflips = max_t(unsigned int, max_bitflips, ret); + readlen -= len; if (!readlen) break; @@ -3879,7 +3882,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; - return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; + return max_bitflips; } /** -- cgit v1.2.3 From c1a72e2dbb4abb90bd408480d7c48ba40cb799ce Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 19 Jan 2018 19:11:27 +0100 Subject: mtd: nand: Fix build issues due to an anonymous union GCC-4.4.4 raises errors when assigning a parameter in an anonymous union, leading to this kind of failure: drivers/mtd/nand/marvell_nand.c:1936: warning: missing braces around initializer warning: (near initialization for '(anonymous)[1].') error: unknown field 'data' specified in initializer error: unknown field 'addr' specified in initializer Work around the situation by naming these unions. Fixes: 8878b126df76 ("mtd: nand: add ->exec_op() implementation") Reported-by: Andrew Morton Signed-off-by: Miquel Raynal Tested-by: Andrew Morton Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 13 +++++++------ include/linux/mtd/rawnand.h | 8 ++++---- 2 files changed, 11 insertions(+), 10 deletions(-) (limited to 'drivers/mtd/nand/nand_base.c') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 3ff77bef9739..66b67014508f 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2335,23 +2335,24 @@ nand_op_parser_must_split_instr(const struct nand_op_parser_pattern_elem *pat, { switch (pat->type) { case NAND_OP_ADDR_INSTR: - if (!pat->addr.maxcycles) + if (!pat->ctx.addr.maxcycles) break; if (instr->ctx.addr.naddrs - *start_offset > - pat->addr.maxcycles) { - *start_offset += pat->addr.maxcycles; + pat->ctx.addr.maxcycles) { + *start_offset += pat->ctx.addr.maxcycles; return true; } break; case NAND_OP_DATA_IN_INSTR: case NAND_OP_DATA_OUT_INSTR: - if (!pat->data.maxlen) + if (!pat->ctx.data.maxlen) break; - if (instr->ctx.data.len - *start_offset > pat->data.maxlen) { - *start_offset += pat->data.maxlen; + if (instr->ctx.data.len - *start_offset > + pat->ctx.data.maxlen) { + *start_offset += pat->ctx.data.maxlen; return true; } break; diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 469dc724f5df..56c5570aadbe 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -962,7 +962,7 @@ struct nand_op_parser_pattern_elem { union { struct nand_op_parser_addr_constraints addr; struct nand_op_parser_data_constraints data; - }; + } ctx; }; #define NAND_OP_PARSER_PAT_CMD_ELEM(_opt) \ @@ -975,21 +975,21 @@ struct nand_op_parser_pattern_elem { { \ .type = NAND_OP_ADDR_INSTR, \ .optional = _opt, \ - .addr.maxcycles = _maxcycles, \ + .ctx.addr.maxcycles = _maxcycles, \ } #define NAND_OP_PARSER_PAT_DATA_IN_ELEM(_opt, _maxlen) \ { \ .type = NAND_OP_DATA_IN_INSTR, \ .optional = _opt, \ - .data.maxlen = _maxlen, \ + .ctx.data.maxlen = _maxlen, \ } #define NAND_OP_PARSER_PAT_DATA_OUT_ELEM(_opt, _maxlen) \ { \ .type = NAND_OP_DATA_OUT_INSTR, \ .optional = _opt, \ - .data.maxlen = _maxlen, \ + .ctx.data.maxlen = _maxlen, \ } #define NAND_OP_PARSER_PAT_WAITRDY_ELEM(_opt) \ -- cgit v1.2.3