mtd: nand: provide several helpers to do common NAND operations
authorBoris Brezillon <boris.brezillon@free-electrons.com>
Fri, 15 Mar 2019 14:14:32 +0000 (15:14 +0100)
committerTom Rini <trini@konsulko.com>
Mon, 22 Apr 2019 15:55:58 +0000 (11:55 -0400)
Linux commit 97d90da8a88 ("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 <boris.brezillon@free-electrons.com>
[miquel.raynal@free-electrons.com: rebased and fixed some conflicts]
Signed-off-by: Miquel Raynal <miquel.raynal@free-electrons.com>
Acked-by: Masahiro Yamada <yamada.masahiro@socionext.com>
[Philippe Reynes: adapt code to u-boot and only keep new function]

Signed-off-by: Philippe Reynes <philippe.reynes@softathome.com>
drivers/mtd/nand/raw/nand_base.c
include/linux/mtd/rawnand.h

index 6d2ff58d86a557a1f64ad01f3385f6c033d5c5d0..e07bd6b657eeba96f2afe9338500a5290c7b4b0a 100644 (file)
@@ -486,14 +486,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;
 }
 
 /**
@@ -575,11 +580,18 @@ static void nand_wait_status_ready(struct mtd_info *mtd, unsigned long timeo)
 {
        register struct nand_chip *chip = mtd_to_nand(mtd);
        u32 time_start;
+       int ret;
 
        timeo = (CONFIG_SYS_HZ * timeo) / 1000;
        time_start = get_timer(0);
        while (get_timer(time_start) < timeo) {
-               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;
                WATCHDOG_RESET();
        }
@@ -851,7 +863,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);
@@ -867,8 +887,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;
 
        led_trigger_event(nand_led_trigger, LED_FULL);
 
@@ -878,7 +899,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;
 
        u32 timer = (CONFIG_SYS_HZ * timeo) / 1000;
        u32 time_start;
@@ -889,13 +912,21 @@ 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;
                }
        }
        led_trigger_event(nand_led_trigger, LED_OFF);
 
-       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;
@@ -1047,6 +1078,516 @@ static void __maybe_unused 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
@@ -1068,8 +1609,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);
@@ -1220,9 +1763,19 @@ EXPORT_SYMBOL(nand_check_erased_ecc_chunk);
 static 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;
 }
 
@@ -1243,29 +1796,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;
 }
@@ -1336,6 +1906,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
        int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
        int index;
        unsigned int max_bitflips = 0;
+       int ret;
 
        /* Column address within the page aligned to ECC size (256bytes) */
        start_step = data_offs / chip->ecc.size;
@@ -1353,7 +1924,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)
@@ -1370,8 +1943,11 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
                }
        }
        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
@@ -1384,9 +1960,12 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
                if (eccpos[index + (num_steps * chip->ecc.bytes)] & (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;
        }
 
        for (i = 0; i < eccfrag_len; i++)
@@ -1439,13 +2018,21 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
        uint8_t *ecc_code = chip->buffers->ecccode;
        uint32_t *eccpos = chip->ecc.layout->eccpos;
        unsigned int max_bitflips = 0;
+       int ret;
 
        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;
 
        for (i = 0; i < chip->ecc.total; i++)
                ecc_code[i] = chip->oob_poi[eccpos[i]];
@@ -1501,11 +2088,16 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
        uint32_t *eccpos = chip->ecc.layout->eccpos;
        uint8_t *ecc_calc = chip->buffers->ecccalc;
        unsigned int max_bitflips = 0;
+       int ret;
 
        /* 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;
 
        for (i = 0; i < chip->ecc.total; i++)
                ecc_code[i] = chip->oob_poi[eccpos[i]];
@@ -1514,7 +2106,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);
@@ -1551,7 +2147,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;
@@ -1563,21 +2159,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;
                }
 
@@ -1601,8 +2212,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;
 }
@@ -1739,8 +2353,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,
@@ -1874,9 +2491,7 @@ read_retry:
 static 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);
 }
 
 /**
@@ -1893,25 +2508,43 @@ static 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;
 }
@@ -1925,18 +2558,8 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
 static 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);
 }
 
 /**
@@ -1951,7 +2574,7 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd,
 {
        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;
 
        /*
@@ -1965,7 +2588,10 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd,
        } 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) {
@@ -1974,28 +2600,40 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd,
                                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);
 }
 
 /**
@@ -2154,9 +2792,18 @@ out:
 static 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;
 }
@@ -2179,29 +2826,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;
 }
@@ -2252,17 +2916,24 @@ 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;
        uint32_t *eccpos = chip->ecc.layout->eccpos;
+       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;
+
                chip->ecc.calculate(mtd, p, &ecc_calc[i]);
        }
 
        for (i = 0; i < chip->ecc.total; i++)
                chip->oob_poi[eccpos[i]] = ecc_calc[i];
 
-       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;
 }
@@ -2293,13 +2964,16 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd,
        uint32_t end_step   = (offset + data_len - 1) / ecc_size;
        int oob_bytes       = mtd->oobsize / ecc_steps;
        int step, i;
+       int ret;
 
        for (step = 0; step < ecc_steps; step++) {
                /* configure controller for WRITE access */
                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))
@@ -2324,7 +2998,9 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd,
                chip->oob_poi[eccpos[i]] = ecc_calc[i];
 
        /* 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;
 }
@@ -2351,31 +3027,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;
 }
@@ -2403,8 +3097,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,
@@ -2419,13 +3116,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;
 }
@@ -2785,11 +3477,12 @@ out:
 static int single_erase(struct mtd_info *mtd, int page)
 {
        struct nand_chip *chip = mtd_to_nand(mtd);
+       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);
 
-       return chip->waitfunc(mtd, chip);
+       return nand_erase_op(chip, eraseblock);
 }
 
 /**
@@ -2982,9 +3675,6 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
 static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
                        int addr, uint8_t *subfeature_param)
 {
-       int status;
-       int i;
-
 #ifdef CONFIG_SYS_NAND_ONFI_DETECTION
        if (!chip->onfi_version ||
            !(le16_to_cpu(chip->onfi_params.opt_cmd)
@@ -2992,14 +3682,7 @@ static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
                return -ENOTSUPP;
 #endif
 
-       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);
 }
 
 /**
@@ -3012,8 +3695,6 @@ 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;
-
 #ifdef CONFIG_SYS_NAND_ONFI_DETECTION
        if (!chip->onfi_version ||
            !(le16_to_cpu(chip->onfi_params.opt_cmd)
@@ -3021,10 +3702,7 @@ static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
                return -ENOTSUPP;
 #endif
 
-       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);
 }
 
 /* Set default functions */
@@ -3118,7 +3796,7 @@ static int nand_flash_detect_ext_param_page(struct mtd_info *mtd,
        struct onfi_ext_section *s;
        struct onfi_ext_ecc_info *ecc;
        uint8_t *cursor;
-       int ret = -EINVAL;
+       int ret;
        int len;
        int i;
 
@@ -3128,14 +3806,18 @@ static int nand_flash_detect_ext_param_page(struct mtd_info *mtd,
                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");
@@ -3212,19 +3894,23 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
                                        int *busw)
 {
        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;
@@ -3324,20 +4010,22 @@ static int nand_flash_detect_jedec(struct mtd_info *mtd, struct nand_chip *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))
@@ -3708,25 +4396,29 @@ struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
                                                  int *maf_id, int *dev_id,
                                                  struct nand_flash_dev *type)
 {
-       int busw;
-       int i, maf_idx;
+       int busw, ret;
+       int maf_idx;
        u8 id_data[8];
 
        /*
         * 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 ERR_PTR(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 ERR_PTR(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
@@ -3735,11 +4427,10 @@ struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
         * not match, ignore the device completely.
         */
 
-       chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
-
        /* Read entire ID string */
-       for (i = 0; i < 8; i++)
-               id_data[i] = chip->read_byte(mtd);
+       ret = nand_readid_op(chip, 0, id_data, 8);
+       if (ret)
+               return ERR_PTR(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",
@@ -3999,15 +4690,17 @@ 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;
                }
index 96b584704b53a393c99b5233c3f9eaefb1abeebf..bd373b96172ba24ef2a72b9c54fb2cdadf19319d 100644 (file)
@@ -1299,4 +1299,34 @@ int nand_maximize_ecc(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);
+
 #endif /* __LINUX_MTD_RAWNAND_H */