[RFC 0/4] mtd: spinand: Add continuous read mode support

This series add support continuous read mode for SPI NAND.
Zhengxun (4): mtd: spinand: Add support continuous read mode mtd: spinand: Add continuous read state mtd: spinand: Add support continuous read operation mtd: spinand: macronix: Add support for MX35LF2GE4AD
drivers/mtd/nand/spi/core.c | 115 +++++++++++++++++++++++++++++++- drivers/mtd/nand/spi/macronix.c | 8 +++ include/linux/mtd/spinand.h | 4 ++ 3 files changed, 126 insertions(+), 1 deletion(-)

The patch supports setting the "CONT" bit of the configuration register and adding a continuous read mode flag for identification.
Signed-off-by: Zhengxun zhengxunli.mxic@gmail.com --- drivers/mtd/nand/spi/core.c | 11 +++++++++++ include/linux/mtd/spinand.h | 2 ++ 2 files changed, 13 insertions(+)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index e5330958c7..88d07b625d 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -216,6 +216,17 @@ static int spinand_init_quad_enable(struct spinand_device *spinand) enable ? CFG_QUAD_ENABLE : 0); }
+static int spinand_continuous_read_enable(struct spinand_device *spinand) +{ + return spinand_upd_cfg(spinand, CFG_CONT_READ_ENABLE, + CFG_CONT_READ_ENABLE); +} + +static int spinand_continuous_read_disable(struct spinand_device *spinand) +{ + return spinand_upd_cfg(spinand, CFG_CONT_READ_ENABLE, 0); +} + static int spinand_ecc_enable(struct spinand_device *spinand, bool enable) { diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 15bcd59f34..e6a5478b0a 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -131,6 +131,7 @@ #define REG_CFG 0xb0 #define CFG_OTP_ENABLE BIT(6) #define CFG_ECC_ENABLE BIT(4) +#define CFG_CONT_READ_ENABLE BIT(2) #define CFG_QUAD_ENABLE BIT(0)
/* status register */ @@ -247,6 +248,7 @@ struct spinand_ecc_info {
#define SPINAND_HAS_QE_BIT BIT(0) #define SPINAND_HAS_CR_FEAT_BIT BIT(1) +#define SPINAND_HAS_CONT_READ_BIT BIT(2)
/** * struct spinand_info - Structure used to describe SPI NAND chips

Add continuous read state and initialize state to default false.
Signed-off-by: Zhengxun zhengxunli.mxic@gmail.com --- drivers/mtd/nand/spi/core.c | 8 ++++++++ include/linux/mtd/spinand.h | 2 ++ 2 files changed, 10 insertions(+)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 88d07b625d..6a497e67bb 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -216,6 +216,11 @@ static int spinand_init_quad_enable(struct spinand_device *spinand) enable ? CFG_QUAD_ENABLE : 0); }
+static void spinand_init_continuous_read(struct spinand_device *spinand) +{ + spinand->use_continuous_read = false; +} + static int spinand_continuous_read_enable(struct spinand_device *spinand) { return spinand_upd_cfg(spinand, CFG_CONT_READ_ENABLE, @@ -1125,6 +1130,9 @@ static int spinand_init(struct spinand_device *spinand)
mtd->oobavail = ret;
+ /* Init continuous read */ + spinand_init_continuous_read(spinand); + return 0;
err_cleanup_nanddev: diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index e6a5478b0a..6bb93cd912 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -335,6 +335,7 @@ struct spinand_info { * because the spi-mem interface explicitly requests that buffers * passed in spi_mem_op be DMA-able, so we can't based the bufs on * the stack + * @use_continuous_read: record the continuous read status * @manufacturer: SPI NAND manufacturer information * @priv: manufacturer private data */ @@ -365,6 +366,7 @@ struct spinand_device { u8 *databuf; u8 *oobbuf; u8 *scratchbuf; + bool use_continuous_read; const struct spinand_manufacturer *manufacturer; void *priv; };

The continuous read operation including: firstly, starting with the page read command and the 1st page data will be read into the cache after the read latency tRD. Secondly, Issuing the Read From Cache commands (03h/0Bh/3Bh/6Bh/BBh/EBh) to read out the data from cache continuously. After all the data is read out, the host should pull CS# high to terminate this continuous read operation and wait tRST for the NAND device resets read operation.
The continuous read usuage is enable by read mutiple page (at least larger than 1 page size) and the column address is don't care in this operation, since the data output for each page will always start from byte 0 and a full page data should be read out for each page.
On the other hand, since the continuous read mode can only read the entire page of data and cannot read the oob data, the dynamic change mode is added to enable continuous read mode and disable continuous read mode in spinand_mtd_continuous_read to avoid writing and erasing operation is abnormal.
The performance of continuous read mode is as follows. Set the flash to QUAD mode and run 25MZ direct mapping mode on the SPI bus and use the MTD test module to show the performance of continuous reads.
Signed-off-by: Zhengxun zhengxunli.mxic@gmail.com --- drivers/mtd/nand/spi/core.c | 96 ++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 6a497e67bb..314506bc7f 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -268,7 +268,10 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand, u16 column = 0; int ret;
- if (req->datalen) { + if (spinand->use_continuous_read) { + buf = req->databuf.in; + nbytes = req->datalen; + } else if (req->datalen) { adjreq.datalen = nanddev_page_size(nand); adjreq.dataoffs = 0; adjreq.databuf.in = spinand->databuf; @@ -311,6 +314,9 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand, op.addr.val += op.data.nbytes; }
+ if (spinand->use_continuous_read) + return 0; + if (req->datalen) memcpy(req->databuf.in, spinand->databuf + req->dataoffs, req->datalen); @@ -576,6 +582,80 @@ static int spinand_write_page(struct spinand_device *spinand, return ret; }
+static int spinand_mtd_continuous_read(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops, + struct nand_io_iter *iter) +{ + struct spinand_device *spinand = mtd_to_spinand(mtd); + struct nand_device *nand = mtd_to_nanddev(mtd); + int ret = 0; + + /* + * Since the continuous read mode can only read the entire page of data + * and cannot read the oob data, therefore, only ECC-Free SPI-NAND support + * continuous read mode now. + */ + iter->req.mode = MTD_OPS_RAW; + iter->req.dataoffs = nanddev_offs_to_pos(nand, from, &iter->req.pos); + iter->req.databuf.in = ops->datbuf; + iter->req.datalen = ops->len; + + if (from & (nanddev_page_size(nand) - 1)) { + pr_debug("%s: unaligned address\n", __func__); + return -EINVAL; + } + + ret = spinand_continuous_read_enable(spinand); + if (ret) + return ret; + + spinand->use_continuous_read = true; + + ret = spinand_select_target(spinand, iter->req.pos.target); + if (ret) + return ret; + + /* + * The continuous read operation including: firstly, starting with the + * page read command and the 1 st page data will be read into the cache + * after the read latency tRD. Secondly, Issuing the Read From Cache + * commands (03h/0Bh/3Bh/6Bh/BBh/EBh) to read out the data from cache + * continuously. + * + * The cache is divided into two halves, while one half of the cache is + * outputting the data, the other half will be loaded for the new data; + * therefore, the host can read out the data continuously from page to + * page. Multiple of Read From Cache commands can be issued in one + * continuous read operation, each Read From Cache command is required + * to read multiple 4-byte data exactly; otherwise, the data output will + * be out of sequence from one Read From Cache command to another Read + * From Cache command. + * + * After all the data is read out, the host should pull CS# high to + * terminate this continuous read operation and wait a 6us of tRST for + * the NAND device resets read operation. The data output for each page + * will always start from byte 0 and a full page data should be read out + * for each page. + */ + ret = spinand_read_page(spinand, &iter->req, false); + if (ret) + return ret; + + ret = spinand_reset_op(spinand); + if (ret) + return ret; + + ret = spinand_continuous_read_disable(spinand); + if (ret) + return ret; + + spinand->use_continuous_read = false; + + ops->retlen = iter->req.datalen; + + return ret; +} + static int spinand_mtd_read(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { @@ -594,6 +674,19 @@ static int spinand_mtd_read(struct mtd_info *mtd, loff_t from, mutex_lock(&spinand->lock); #endif
+ /* + * If the device support continuous read mode and read length larger + * than one page size will enter the continuous read mode. This mode + * helps avoid issuing a page read command and read from cache command + * again, and improves read performance for continuous addresses. + */ + if ((spinand->flags & SPINAND_HAS_CONT_READ_BIT) && + (ops->len > nanddev_page_size(nand))) { + ret = spinand_mtd_continuous_read(mtd, from, ops, &iter); + + goto continuous_read_finish; + } + nanddev_io_for_each_page(nand, from, ops, &iter) { WATCHDOG_RESET(); ret = spinand_select_target(spinand, iter.req.pos.target); @@ -621,6 +714,7 @@ static int spinand_mtd_read(struct mtd_info *mtd, loff_t from, ops->oobretlen += iter.req.ooblen; }
+continuous_read_finish: #ifndef __UBOOT__ mutex_unlock(&spinand->lock); #endif

Macronix NAND Flash devices are available in different configurations and densities.
MX"35" means SPI NAND MX35"LF"/"UF" , LF means 3V and UF meands 1.8V MX35LF"2G" , 2G means 2Gbits MX35LF2G"E4"/"24"/"14", E4 means internal ECC and Quad I/O(x4) 24 means 8-bit ecc requirement and Quad I/O(x4) 14 means 4-bit ecc requirement and Quad I/O(x4
Validated by read, erase, write, on Xilinx Zynq PicoZed FPGA board which included Macronix SPI Host (drivers/spi/spi-mxic.c).
Signed-off-by: Zhengxun zhengxunli.mxic@gmail.com --- drivers/mtd/nand/spi/macronix.c | 8 ++++++++ 1 file changed, 8 insertions(+)
diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c index 6d643a8000..e0e0f9c8b7 100644 --- a/drivers/mtd/nand/spi/macronix.c +++ b/drivers/mtd/nand/spi/macronix.c @@ -122,6 +122,14 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), + SPINAND_INFO("MX35LF2GE4AD", 0x26, + NAND_MEMORG(1, 2048, 64, 64, 2048, 2, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT | SPINAND_HAS_CONT_READ_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), SPINAND_INFO("MX35UF4GE4AD", 0xb7, NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1), NAND_ECCREQ(8, 512),
participants (1)
-
Zhengxun