From 54b275c8ed3ad20c447fd46deec83384822ac79d Mon Sep 17 00:00:00 2001 From: John Crispin Date: Wed, 21 Feb 2018 20:40:50 +0100 Subject: ipq40xx: add target Signed-off-by: Christian Lamparter Signed-off-by: Mathias Kresin Signed-off-by: John Crispin --- ...ovide-several-helpers-to-do-common-NAND-o.patch | 1586 ++++++++++++++++++++ 1 file changed, 1586 insertions(+) create mode 100644 target/linux/ipq40xx/patches-4.14/050-0004-mtd-nand-provide-several-helpers-to-do-common-NAND-o.patch (limited to 'target/linux/ipq40xx/patches-4.14/050-0004-mtd-nand-provide-several-helpers-to-do-common-NAND-o.patch') diff --git a/target/linux/ipq40xx/patches-4.14/050-0004-mtd-nand-provide-several-helpers-to-do-common-NAND-o.patch b/target/linux/ipq40xx/patches-4.14/050-0004-mtd-nand-provide-several-helpers-to-do-common-NAND-o.patch new file mode 100644 index 0000000000..7cbbcf588c --- /dev/null +++ b/target/linux/ipq40xx/patches-4.14/050-0004-mtd-nand-provide-several-helpers-to-do-common-NAND-o.patch @@ -0,0 +1,1586 @@ +commit 97d90da8a886949f09bb4754843fb0b504956ad2 +Author: Boris Brezillon +Date: Thu Nov 30 18:01:29 2017 +0100 + + 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 + +--- a/drivers/mtd/nand/nand_base.c ++++ b/drivers/mtd/nand/nand_base.c +@@ -561,14 +561,19 @@ static int nand_block_markbad_lowlevel(s + 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)); +@@ -1016,7 +1028,15 @@ static void panic_nand_wait(struct mtd_i + 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); +@@ -1033,8 +1053,9 @@ static void panic_nand_wait(struct mtd_i + 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 +@@ -1042,7 +1063,9 @@ static int nand_wait(struct mtd_info *mt + */ + 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); +@@ -1053,14 +1076,22 @@ static int nand_wait(struct mtd_info *mt + 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; +@@ -1215,6 +1246,516 @@ static void nand_release_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 + * @chipnr: Internal die id +@@ -1235,8 +1776,10 @@ int nand_reset(struct nand_chip *chip, i + * 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); +@@ -1392,9 +1935,19 @@ EXPORT_SYMBOL(nand_check_erased_ecc_chun + 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); +@@ -1416,29 +1969,46 @@ static int nand_read_page_raw_syndrome(s + 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; + } +@@ -1527,7 +2097,9 @@ static int nand_read_subpage(struct mtd_ + 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) +@@ -1545,8 +2117,11 @@ static int nand_read_subpage(struct mtd_ + 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 +@@ -1560,9 +2135,12 @@ static int nand_read_subpage(struct mtd_ + (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, +@@ -1619,10 +2197,17 @@ static int nand_read_page_hwecc(struct m + + 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); +@@ -1681,9 +2266,13 @@ static int nand_read_page_hwecc_oob_firs + 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); +@@ -1694,7 +2283,11 @@ static int nand_read_page_hwecc_oob_firs + 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); +@@ -1731,7 +2324,7 @@ static int nand_read_page_hwecc_oob_firs + 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; +@@ -1743,21 +2336,36 @@ static int nand_read_page_syndrome(struc + 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; + } + +@@ -1781,8 +2389,11 @@ static int nand_read_page_syndrome(struc + + /* 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; + } +@@ -1903,8 +2514,11 @@ static int nand_do_read_ops(struct mtd_i + __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, +@@ -2063,9 +2677,7 @@ static int nand_read(struct mtd_info *mt + */ + 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); + +@@ -2083,25 +2695,43 @@ int nand_read_oob_syndrome(struct mtd_in + 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; + } +@@ -2115,18 +2745,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); + +@@ -2142,7 +2762,7 @@ int nand_write_oob_syndrome(struct mtd_i + { + 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; + + /* +@@ -2156,7 +2776,10 @@ int nand_write_oob_syndrome(struct mtd_i + } 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) { +@@ -2165,28 +2788,40 @@ int nand_write_oob_syndrome(struct mtd_i + 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 + 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( + 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 + + 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 + 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(stru + 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(stru + 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(stru + 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_in + 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_in + 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); +- +- status = chip->waitfunc(mtd, chip); +- if (status < 0) +- return status; ++ eraseblock = page >> (chip->phys_erase_shift - chip->page_shift); + +- return status & NAND_STATUS_FAIL ? -EIO : 0; ++ return nand_erase_op(chip, eraseblock); + } + + /** +@@ -3226,22 +3908,12 @@ static int nand_max_bad_blocks(struct mt + 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 + 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 + 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_p + 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 + { + 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(struc + 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 + { + 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 + * 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 + * 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", +@@ -4233,15 +4911,16 @@ int nand_scan_ident(struct mtd_info *mtd + + /* 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; + } +--- 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 m + 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 m + 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(stru + 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(stru + 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); + } + + /* +--- a/include/linux/mtd/rawnand.h ++++ b/include/linux/mtd/rawnand.h +@@ -1313,6 +1313,35 @@ int nand_write_page_raw(struct mtd_info + /* 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