mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-23 23:42:43 +00:00
54b275c8ed
Signed-off-by: Christian Lamparter <chunkeey@gmail.com> Signed-off-by: Mathias Kresin <dev@kresin.me> Signed-off-by: John Crispin <john@phrozen.org>
1587 lines
43 KiB
Diff
1587 lines
43 KiB
Diff
commit 97d90da8a886949f09bb4754843fb0b504956ad2
|
|
Author: Boris Brezillon <boris.brezillon@free-electrons.com>
|
|
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 <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>
|
|
|
|
--- 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);
|
|
|