All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/9] Introduction to SPI NAND framework
@ 2017-03-23  9:43 Peter Pan
  2017-03-23  9:43 ` [PATCH v4 1/9] mtd: nand: add oob iterator in nand_for_each_page Peter Pan
                   ` (9 more replies)
  0 siblings, 10 replies; 36+ messages in thread
From: Peter Pan @ 2017-03-23  9:43 UTC (permalink / raw)
  To: boris.brezillon, richard, computersforpeace, arnaud.mouiche,
	thomas.petazzoni, marex, cyrille.pitchen, linux-mtd
  Cc: peterpandong, peterpansjtu, linshunquan1

v4 also changes a lot than v3, thanks for Boris's and Arnaud's
valuable comments! The biggest change is "add spinand_alloc(),
spinand_free(), spinand_register(), spinand_unregister() interfaces
and make old interface - spinand_detect(), spinand_init() static".
While this is just a beginning. more effort is needed to make clear
layer. I really hope for comments on this part.

This series introductes a SPI NAND framework.
SPI NAND is a new NAND family device with SPI protocol as
its interface. And its command set is totally different
with parallel NAND.

Our first attempt was more than 2 years ago[1]. At that
time, I didn't make BBT shareable and there were too many
duplicate code with parallel NAND, so that serie stoped.
But the discussion never stops. Now Boris has a plan to
make a generic NAND framework which can be shared with
both parallel and SPI NAND. Now the first step of the
new generic NAND framework is finished. And it is waiting
for a user. After discussion with Boris. We both think it's
time to rebuild SPI NAND framework based on the new NAND
framework and send out for reviewing.

This series is based on Boris's nand/generic branch[2], which
is on 4.11-rc1. In this serie, BBT code is totally shared.
Of course SPI NAND can share more code with parallel, this
requires to put more in new NAND core (now only BBT included).
I'd like to send this serie out first, then we can decide
which part should be in new NAND core.

This series only supports basic SPI NAND features and uses
generic spi controller for data transfer, on-die ECC for data
correction. Support advanced features and specific SPI NAND
controller with hardware ECC is the next step.

This series is tested on Xilinx Zedboard with Micron
MT29F2G01ABAGDSF SPI NAND chip.


[1]http://lists.infradead.org/pipermail/linux-mtd/2015-January/057223.html
[2]https://github.com/bbrezillon/linux-0day/tree/nand/generic

v4 changes:
- initialize struct mtd_oob_ops to 0 in bbt.c
- rename new added helper in nand.h to nand_check_xxxx()
- add struct mtd_oob_ops consistency check in nand_check_oob_ops()
- add dataleft in struct nand_page_iter instead of offs
- remove spinand_manufacturers->ops->detect() check since it is mandatory
- remove spinand_set_manufacturer_ops() and do the job in
  spinand_manufacturer_detect()
- move .priv out of struct spinand_controller
- add spinand_alloc/free/register/unregister() and make
  spinand_detect/init() static
- make BBT be configured by device tree
- chip->id.data stores raw ID directly
- refine device info print message after detect success
- add struct mtd_layout_ops pointer in struct micron_spinand_info
- remove micron_spinand_init() and do its job in micron_spinand_detect()
- fix BBT block cannot be erased bug

v3 changes:
- rebase patch on 4.11-rc1[1]
- change read ID method. read 4 bytes ID out then let ->detect() of each
  manufacutre driver to decode ID and detect the device.
- make SPI NAND id table private to each manufacutre driver
- fix coding style to make checkpatch.pl happy
- update the MAINTAINERS file for spi nand code
- add nand_size() helper in nand.h
- use nand_for_each_page() helper in spinand_do_read/write_ops()
- create helper to check boundaries in generic NAND code and use it
  in SPI NAND core
- rename spinand_base.c to core.c
- manufactures' drivers expose spinand_manufacturer struct instead of
  spinand_manufacturer_ops struct to keep Manufacture ID macro in
  manufactures' drivers and rename spinand_ids.c to manufacture.c
- rename spinand_micron.c to micron.c
- rename chips/ directory to controllers/
- rename generic_spi.c to generic-spi.c
- replace ->build_column_addr() and ->get_dummy() hooks with ->prepare_op() in
  spinand_manufacturer_ops struct
- rename __spinand_erase() to spinand_erase()
- rename spinand_erase() to spinand_erase_skip_bbt()
- rename spinand_scan_ident() to spinand_detect()
- rename spinand_scan_tail() to spinand_init()
- move non detect related code from spinand_detect() to spinand_init()
- remove spinand_fill_nandd, assign nand->ops in spinand_detect()
- merge v2 patch 3(bad block support) and patch 4(BBT support)
- drop getchip parameter, remove spinand_get/remove_device(), take the lock
  by caller directly
- fix function comment headers
- use nand_bbt_is_initialized() helper
- replace spinand_ecc_engine and spinand_controller object in spinand_device
  struct with pointer
- replace struct spinand_manufacturer_ops pointer in spinand_device struct
  with spinand_manufacturer struct


v2 changes:
- replace "spi_nand" with "spinand".
- rename spi nand related structs for better understanding.
- introduce spi nand controller, manufacturer and ecc_engine struct.
- add spi nand manufacturer initialization function refer to Boris's
  manuf-init branch.
- remove NAND_SKIP_BBTSCAN from series. Add it later when enabling HW ECC.
- reorganize series according to Boris's suggestion.


Peter Pan (9):
  mtd: nand: add oob iterator in nand_for_each_page
  mtd: nand: make sure mtd_oob_ops consistent in bbt
  mtd: nand: add more helpers in nand.h
  nand: spi: add basic blocks for infrastructure
  nand: spi: add basic operations support
  nand: spi: Add bad block support
  nand: spi: add Micron spi nand support
  nand: spi: Add generic SPI controller support
  MAINTAINERS: Add SPI NAND entry

 MAINTAINERS                                    |    9 +
 drivers/mtd/nand/Kconfig                       |    1 +
 drivers/mtd/nand/Makefile                      |    1 +
 drivers/mtd/nand/bbt.c                         |   13 +-
 drivers/mtd/nand/spi/Kconfig                   |    7 +
 drivers/mtd/nand/spi/Makefile                  |    4 +
 drivers/mtd/nand/spi/controllers/Kconfig       |    5 +
 drivers/mtd/nand/spi/controllers/Makefile      |    1 +
 drivers/mtd/nand/spi/controllers/generic-spi.c |  150 +++
 drivers/mtd/nand/spi/core.c                    | 1523 ++++++++++++++++++++++++
 drivers/mtd/nand/spi/manufactures.c            |   27 +
 drivers/mtd/nand/spi/micron.c                  |  226 ++++
 include/linux/mtd/nand.h                       |  100 +-
 include/linux/mtd/spinand.h                    |  272 +++++
 14 files changed, 2325 insertions(+), 14 deletions(-)
 create mode 100644 drivers/mtd/nand/spi/Kconfig
 create mode 100644 drivers/mtd/nand/spi/Makefile
 create mode 100644 drivers/mtd/nand/spi/controllers/Kconfig
 create mode 100644 drivers/mtd/nand/spi/controllers/Makefile
 create mode 100644 drivers/mtd/nand/spi/controllers/generic-spi.c
 create mode 100644 drivers/mtd/nand/spi/core.c
 create mode 100644 drivers/mtd/nand/spi/manufactures.c
 create mode 100644 drivers/mtd/nand/spi/micron.c
 create mode 100644 include/linux/mtd/spinand.h

-- 
1.9.1

^ permalink raw reply	[flat|nested] 36+ messages in thread

* [PATCH v4 1/9] mtd: nand: add oob iterator in nand_for_each_page
  2017-03-23  9:43 [PATCH v4 0/9] Introduction to SPI NAND framework Peter Pan
@ 2017-03-23  9:43 ` Peter Pan
  2017-03-23 11:13   ` Marek Vasut
  2017-03-29 19:34   ` Boris Brezillon
  2017-03-23  9:43 ` [PATCH v4 2/9] mtd: nand: make sure mtd_oob_ops consistent in bbt Peter Pan
                   ` (8 subsequent siblings)
  9 siblings, 2 replies; 36+ messages in thread
From: Peter Pan @ 2017-03-23  9:43 UTC (permalink / raw)
  To: boris.brezillon, richard, computersforpeace, arnaud.mouiche,
	thomas.petazzoni, marex, cyrille.pitchen, linux-mtd
  Cc: peterpandong, peterpansjtu, linshunquan1

Iterate nand pages by both page and oob operation.

Signed-off-by: Peter Pan <peterpandong@micron.com>
---
 include/linux/mtd/nand.h | 38 +++++++++++++++++++++++++++++++-------
 1 file changed, 31 insertions(+), 7 deletions(-)

diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index c2197b4..54ded4c 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -84,9 +84,12 @@ struct nand_device {
  * @pageoffset: the offset within a page
  */
 struct nand_page_iter {
-	loff_t offs;
 	int page;
 	int pageoffs;
+	size_t dataleft;
+	int ooboffs;
+	int oobsize;
+	size_t oobleft;
 };
 
 /**
@@ -193,14 +196,19 @@ static inline int nand_per_page_oobsize(struct nand_device *nand)
  * @offs: absolute offset
  * @iter: page iterator
  */
-static inline void nand_page_iter_init(struct nand_device *nand, loff_t offs,
+static inline void nand_page_iter_init(struct nand_device *nand,
+				       loff_t offs, size_t len, u32 ooboffs,
+				       size_t ooblen, u32 oobsize,
 				       struct nand_page_iter *iter)
 {
 	u64 page = offs;
 
 	iter->pageoffs = do_div(page, nand->memorg.pagesize);
 	iter->page = page;
-	iter->offs = offs;
+	iter->dataleft = len;
+	iter->ooboffs = ooboffs;
+	iter->oobsize = oobsize;
+	iter->oobleft = ooblen;
 }
 
 /**
@@ -212,13 +220,29 @@ static inline void nand_page_iter_next(struct nand_device *nand,
 				       struct nand_page_iter *iter)
 {
 	iter->page++;
-	iter->offs += nand_page_size(nand) - iter->pageoffs;
 	iter->pageoffs = 0;
+	if (iter->dataleft)
+		iter->dataleft -= min_t (int,
+					 nand_page_size(nand) - iter->pageoffs,
+					 iter->dataleft);
+	if (iter->oobleft)
+		iter->oobleft -= min_t(int, iter->oobsize - iter->ooboffs,
+				       iter->oobleft);
+}
+
+static inline bool nand_page_iter_end(struct nand_device *nand,
+				      struct nand_page_iter *iter)
+{
+	if (iter->dataleft || iter->oobleft)
+		return false;
+	return true;
 }
 
-#define nand_for_each_page(nand, start, len, iter)		\
-	for (nand_page_iter_init(nand, start, iter);		\
-	     (iter)->offs < (start) + (len);			\
+#define nand_for_each_page(nand, start, len, ooboffs, ooblen,	\
+			   oobsize, iter)	\
+	for (nand_page_iter_init(nand, start, len, ooboffs,	\
+				 ooblen, oobsize, iter);	\
+	     !nand_page_iter_end(nand, iter);		\
 	     nand_page_iter_next(nand, iter))
 
 /**
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v4 2/9] mtd: nand: make sure mtd_oob_ops consistent in bbt
  2017-03-23  9:43 [PATCH v4 0/9] Introduction to SPI NAND framework Peter Pan
  2017-03-23  9:43 ` [PATCH v4 1/9] mtd: nand: add oob iterator in nand_for_each_page Peter Pan
@ 2017-03-23  9:43 ` Peter Pan
  2017-03-29 19:48   ` Boris Brezillon
  2017-03-23  9:43 ` [PATCH v4 3/9] mtd: nand: add more helpers in nand.h Peter Pan
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 36+ messages in thread
From: Peter Pan @ 2017-03-23  9:43 UTC (permalink / raw)
  To: boris.brezillon, richard, computersforpeace, arnaud.mouiche,
	thomas.petazzoni, marex, cyrille.pitchen, linux-mtd
  Cc: peterpandong, peterpansjtu, linshunquan1

This commit makes sure struct mtd_oob_ops passed to mtd_read/write_ops()
is consistent, which means .datbuf = NULL and .len = 0 when not reading
page; .oobbuf = NULL and .ooblen = 0 when not reading oob. If no, (ie.
ops.datbuf = NULL, while ops.len != 0.), nand_for_each_page() will run
forever.

Signed-off-by: Peter Pan <peterpandong@micron.com>
---
 drivers/mtd/nand/bbt.c | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/nand/bbt.c b/drivers/mtd/nand/bbt.c
index 94cd3e5..c35a6e8 100644
--- a/drivers/mtd/nand/bbt.c
+++ b/drivers/mtd/nand/bbt.c
@@ -328,7 +328,7 @@ static int scan_read_oob(struct nand_device *this, uint8_t *buf, loff_t offs,
 			 size_t len)
 {
 	struct mtd_info *mtd = nand_to_mtd(this);
-	struct mtd_oob_ops ops;
+	struct mtd_oob_ops ops = {0};
 	int res, ret = 0;
 
 	ops.mode = MTD_OPS_PLACE_OOB;
@@ -369,11 +369,12 @@ static int scan_write_bbt(struct nand_device *this, loff_t offs, size_t len,
 			  uint8_t *buf, uint8_t *oob)
 {
 	struct mtd_info *mtd = nand_to_mtd(this);
-	struct mtd_oob_ops ops;
+	struct mtd_oob_ops ops = {0};
 
 	ops.mode = MTD_OPS_PLACE_OOB;
 	ops.ooboffs = 0;
-	ops.ooblen = nand_per_page_oobsize(this);
+	/* oob from caller may be NULL */
+	ops.ooblen = oob ? nand_per_page_oobsize(this) : 0;
 	ops.datbuf = buf;
 	ops.oobbuf = oob;
 	ops.len = len;
@@ -428,13 +429,12 @@ static int scan_block_fast(struct nand_device *this, struct nand_bbt_descr *bd,
 			   loff_t offs, uint8_t *buf, int numpages)
 {
 	struct mtd_info *mtd = nand_to_mtd(this);
-	struct mtd_oob_ops ops;
+	struct mtd_oob_ops ops = {0};
 	int j, ret;
 
 	ops.ooblen = nand_per_page_oobsize(this);
 	ops.oobbuf = buf;
 	ops.ooboffs = 0;
-	ops.datbuf = NULL;
 	ops.mode = MTD_OPS_PLACE_OOB;
 
 	for (j = 0; j < numpages; j++) {
@@ -734,11 +734,10 @@ static int write_bbt(struct nand_device *this, uint8_t *buf,
 	uint8_t rcode = td->reserved_block_code;
 	size_t retlen, len = 0;
 	loff_t to;
-	struct mtd_oob_ops ops;
+	struct mtd_oob_ops ops = {0};
 
 	ops.ooblen = nand_per_page_oobsize(this);
 	ops.ooboffs = 0;
-	ops.datbuf = NULL;
 	ops.mode = MTD_OPS_PLACE_OOB;
 
 	if (!rcode)
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v4 3/9] mtd: nand: add more helpers in nand.h
  2017-03-23  9:43 [PATCH v4 0/9] Introduction to SPI NAND framework Peter Pan
  2017-03-23  9:43 ` [PATCH v4 1/9] mtd: nand: add oob iterator in nand_for_each_page Peter Pan
  2017-03-23  9:43 ` [PATCH v4 2/9] mtd: nand: make sure mtd_oob_ops consistent in bbt Peter Pan
@ 2017-03-23  9:43 ` Peter Pan
  2017-03-23 11:19   ` Marek Vasut
  2017-03-29 19:57   ` Boris Brezillon
  2017-03-23  9:43 ` [PATCH v4 4/9] nand: spi: add basic blocks for infrastructure Peter Pan
                   ` (6 subsequent siblings)
  9 siblings, 2 replies; 36+ messages in thread
From: Peter Pan @ 2017-03-23  9:43 UTC (permalink / raw)
  To: boris.brezillon, richard, computersforpeace, arnaud.mouiche,
	thomas.petazzoni, marex, cyrille.pitchen, linux-mtd
  Cc: peterpandong, peterpansjtu, linshunquan1

This commit adds some helpers in nand.h
    nand_size()
    nand_check_address()
    nand_check_oob_ops()
    nand_oob_ops_across_page()
    nand_check_erase_ops()

Signed-off-by: Peter Pan <peterpandong@micron.com>
---
 include/linux/mtd/nand.h | 62 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 62 insertions(+)

diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 54ded4c..0c52401 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -434,6 +434,68 @@ static inline int nand_neraseblocks(struct nand_device *nand)
 }
 
 /**
+ * nand_size - Get NAND size
+ * @nand: NAND device
+ *
+ * Returns the total size exposed by @nand.
+ */
+static inline u64 nand_size(struct nand_device *nand)
+{
+	return nand->memorg.ndies * nand->memorg.diesize;
+}
+
+static inline int nand_check_address(struct nand_device *nand, loff_t addr)
+{
+	return addr < nand_size(nand) ? 0 : -EINVAL;
+}
+
+static inline int nand_check_oob_ops(struct nand_device *nand, loff_t start,
+				     struct mtd_oob_ops *ops)
+{
+	struct mtd_info *mtd = nand_to_mtd(nand);
+	int ooblen = ops->mode == MTD_OPS_AUTO_OOB ?
+		mtd->oobavail : mtd->oobsize;
+
+	if ((!!ops->datbuf != !!ops->len) ||
+	    (!!ops->oobbuf != !!ops->ooblen))
+		return -EINVAL;
+	if (ops->ooboffs >= ooblen)
+		return -EINVAL;
+	if (ops->ooboffs + ops->ooblen >
+	    (nand_len_to_pages(nand, nand_size(nand)) -
+			       nand_offs_to_page(nand, start)) * ooblen)
+		return -EINVAL;
+
+	return 0;
+}
+
+static inline bool nand_oob_ops_across_page(struct nand_device *nand,
+					    struct mtd_oob_ops *ops)
+{
+	struct mtd_info *mtd = nand_to_mtd(nand);
+	int ooblen = ops->mode == MTD_OPS_AUTO_OOB ?
+		     mtd->oobavail : mtd->oobsize;
+
+	return (ops->ooboffs + ops->ooblen) > ooblen;
+}
+
+static inline int nand_check_erase_ops(struct nand_device *nand,
+				       struct erase_info *einfo)
+{
+	/* check address align on block boundary */
+	if (einfo->addr & (nand_eraseblock_size(nand) - 1))
+		return -EINVAL;
+	/* check lendth align on block boundary */
+	if (einfo->len & (nand_eraseblock_size(nand) - 1))
+		return -EINVAL;
+	/* Do not allow erase past end of device */
+	if ((einfo->addr + einfo->len) > nand_size(nand))
+		return -EINVAL;
+
+	return 0;
+}
+
+/**
  * nand_register - Register a NAND device
  * @nand: NAND device
  *
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v4 4/9] nand: spi: add basic blocks for infrastructure
  2017-03-23  9:43 [PATCH v4 0/9] Introduction to SPI NAND framework Peter Pan
                   ` (2 preceding siblings ...)
  2017-03-23  9:43 ` [PATCH v4 3/9] mtd: nand: add more helpers in nand.h Peter Pan
@ 2017-03-23  9:43 ` Peter Pan
  2017-03-23 11:29   ` Marek Vasut
                     ` (2 more replies)
  2017-03-23  9:43 ` [PATCH v4 5/9] nand: spi: add basic operations support Peter Pan
                   ` (5 subsequent siblings)
  9 siblings, 3 replies; 36+ messages in thread
From: Peter Pan @ 2017-03-23  9:43 UTC (permalink / raw)
  To: boris.brezillon, richard, computersforpeace, arnaud.mouiche,
	thomas.petazzoni, marex, cyrille.pitchen, linux-mtd
  Cc: peterpandong, peterpansjtu, linshunquan1

This is the first commit for spi nand framkework.
This commit is to add add basic building blocks
for the SPI NAND infrastructure.

Signed-off-by: Peter Pan <peterpandong@micron.com>
---
 drivers/mtd/nand/Kconfig            |   1 +
 drivers/mtd/nand/Makefile           |   1 +
 drivers/mtd/nand/spi/Kconfig        |   5 +
 drivers/mtd/nand/spi/Makefile       |   2 +
 drivers/mtd/nand/spi/core.c         | 464 ++++++++++++++++++++++++++++++++++++
 drivers/mtd/nand/spi/manufactures.c |  24 ++
 include/linux/mtd/spinand.h         | 270 +++++++++++++++++++++
 7 files changed, 767 insertions(+)
 create mode 100644 drivers/mtd/nand/spi/Kconfig
 create mode 100644 drivers/mtd/nand/spi/Makefile
 create mode 100644 drivers/mtd/nand/spi/core.c
 create mode 100644 drivers/mtd/nand/spi/manufactures.c
 create mode 100644 include/linux/mtd/spinand.h

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 1c1a1f4..7695fd8 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -2,3 +2,4 @@ config MTD_NAND_CORE
 	tristate
 
 source "drivers/mtd/nand/raw/Kconfig"
+source "drivers/mtd/nand/spi/Kconfig"
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index fe430d9..6221958 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -3,3 +3,4 @@ obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o
 nandcore-objs :=  bbt.o
 
 obj-y	+= raw/
+obj-$(CONFIG_MTD_SPI_NAND)	+= spi/
diff --git a/drivers/mtd/nand/spi/Kconfig b/drivers/mtd/nand/spi/Kconfig
new file mode 100644
index 0000000..d77c46e
--- /dev/null
+++ b/drivers/mtd/nand/spi/Kconfig
@@ -0,0 +1,5 @@
+menuconfig MTD_SPI_NAND
+	tristate "SPI NAND device Support"
+	depends on MTD_NAND
+	help
+	  This is the framework for the SPI NAND device drivers.
diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile
new file mode 100644
index 0000000..eabdb81
--- /dev/null
+++ b/drivers/mtd/nand/spi/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_MTD_SPI_NAND) += core.o
+obj-$(CONFIG_MTD_SPI_NAND) += manufactures.o
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
new file mode 100644
index 0000000..d1ac522
--- /dev/null
+++ b/drivers/mtd/nand/spi/core.c
@@ -0,0 +1,464 @@
+/*
+ *
+ * Copyright (c) 2009-2017 Micron Technology, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "SPI NAND: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/mtd/spinand.h>
+#include <linux/slab.h>
+
+/*
+ * spinand_exec_op - execute SPI NAND operation by controller ->exec_op() hook
+ * @chip: SPI NAND device structure
+ * @op: pointer to spinand_op struct
+ */
+static inline int spinand_exec_op(struct spinand_device *chip,
+				  struct spinand_op *op)
+{
+	return chip->controller.controller->ops->exec_op(chip, op);
+}
+
+/*
+ * spinand_op_init - initialize spinand_op struct
+ * @op: pointer to spinand_op struct
+ */
+static inline void spinand_op_init(struct spinand_op *op)
+{
+	memset(op, 0, sizeof(struct spinand_op));
+	op->addr_nbits = 1;
+	op->data_nbits = 1;
+}
+
+/*
+ * spinand_read_reg - send command 0Fh to read register
+ * @chip: SPI NAND device structure
+ * @reg; register to read
+ * @buf: buffer to store value
+ */
+static int spinand_read_reg(struct spinand_device *chip, u8 reg, u8 *buf)
+{
+	struct spinand_op op;
+	int ret;
+
+	spinand_op_init(&op);
+	op.cmd = SPINAND_CMD_GET_FEATURE;
+	op.n_addr = 1;
+	op.addr[0] = reg;
+	op.n_rx = 1;
+	op.rx_buf = buf;
+
+	ret = spinand_exec_op(chip, &op);
+	if (ret < 0)
+		pr_err("err: %d read register %d\n", ret, reg);
+
+	return ret;
+}
+
+/*
+ * spinand_write_reg - send command 1Fh to write register
+ * @chip: SPI NAND device structure
+ * @reg; register to write
+ * @buf: buffer stored value
+ */
+static int spinand_write_reg(struct spinand_device *chip, u8 reg, u8 *buf)
+{
+	struct spinand_op op;
+	int ret;
+
+	spinand_op_init(&op);
+	op.cmd = SPINAND_CMD_SET_FEATURE;
+	op.n_addr = 1;
+	op.addr[0] = reg;
+	op.n_tx = 1,
+	op.tx_buf = buf,
+
+	ret = spinand_exec_op(chip, &op);
+	if (ret < 0)
+		pr_err("err: %d write register %d\n", ret, reg);
+
+	return ret;
+}
+
+/*
+ * spinand_read_status - get status register value
+ * @chip: SPI NAND device structure
+ * @status: buffer to store value
+ * Description:
+ *   After read, write, or erase, the NAND device is expected to set the
+ *   busy status.
+ *   This function is to allow reading the status of the command: read,
+ *   write, and erase.
+ */
+static int spinand_read_status(struct spinand_device *chip, uint8_t *status)
+{
+	return spinand_read_reg(chip, REG_STATUS, status);
+}
+
+/*
+ * spinand_wait - wait until the command is done
+ * @chip: SPI NAND device structure
+ * @s: buffer to store status register value (can be NULL)
+ */
+static int spinand_wait(struct spinand_device *chip, u8 *s)
+{
+	unsigned long timeo = msecs_to_jiffies(400);
+	u8 status;
+
+	do {
+		spinand_read_status(chip, &status);
+		if ((status & STATUS_OIP_MASK) == STATUS_READY)
+			goto out;
+	} while (time_before(jiffies, timeo));
+
+	/*
+	 * Extra read, just in case the STATUS_READY bit has changed
+	 * since our last check
+	 */
+	spinand_read_status(chip, &status);
+out:
+	if (s)
+		*s = status;
+
+	return (status & STATUS_OIP_MASK) == STATUS_READY ? 0 :	-ETIMEDOUT;
+}
+
+/*
+ * spinand_read_id - send 9Fh command to get ID
+ * @chip: SPI NAND device structure
+ * @buf: buffer to store id
+ * Description:
+ *   Manufacturers' read ID method is not unique. Some need a dummy before
+ *   reading, some's ID has three byte.
+ *   This function send one byte opcode (9Fh) and then read
+ *   SPINAND_MAX_ID_LEN (4 currently) bytes. Manufacturer's detect function
+ *   need to filter out real ID from the 4 bytes.
+ */
+static int spinand_read_id(struct spinand_device *chip, u8 *buf)
+{
+	struct spinand_op op;
+
+	spinand_op_init(&op);
+	op.cmd = SPINAND_CMD_READ_ID;
+	op.n_rx = SPINAND_MAX_ID_LEN;
+	op.rx_buf = buf;
+
+	return spinand_exec_op(chip, &op);
+}
+
+/*
+ * spinand_reset - reset device by FFh command.
+ * @chip: SPI NAND device structure
+ */
+static int spinand_reset(struct spinand_device *chip)
+{
+	struct spinand_op op;
+	int ret;
+
+	spinand_op_init(&op);
+	op.cmd = SPINAND_CMD_RESET;
+
+	ret = spinand_exec_op(chip, &op);
+	if (ret < 0) {
+		pr_err("spinand reset failed!\n");
+		goto out;
+	}
+	ret = spinand_wait(chip, NULL);
+
+out:
+	return ret;
+}
+
+/*
+ * spinand_lock_block - write block lock register to lock/unlock device
+ * @chip: SPI NAND device structure
+ * @lock: value to set to block lock register
+ */
+static int spinand_lock_block(struct spinand_device *chip, u8 lock)
+{
+	return spinand_write_reg(chip, REG_BLOCK_LOCK, &lock);
+}
+
+/*
+ * spinand_set_rd_wr_op - choose the best read write command
+ * @chip: SPI NAND device structure
+ * Description:
+ *   Chose the fastest r/w command according to spi controller's and
+ *   device's ability.
+ */
+static void spinand_set_rd_wr_op(struct spinand_device *chip)
+{
+	u32 controller_cap = chip->controller.controller->caps;
+	u32 rw_mode = chip->rw_mode;
+
+	if ((controller_cap & SPINAND_CAP_RD_QUAD) &&
+	    (rw_mode & SPINAND_RD_QUAD))
+		chip->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_QUAD_IO;
+	else if ((controller_cap & SPINAND_CAP_RD_X4) &&
+		 (rw_mode & SPINAND_RD_X4))
+		chip->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_X4;
+	else if ((controller_cap & SPINAND_CAP_RD_DUAL) &&
+		 (rw_mode & SPINAND_RD_DUAL))
+		chip->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_DUAL_IO;
+	else if ((controller_cap & SPINAND_CAP_RD_X2) &&
+		 (rw_mode & SPINAND_RD_X2))
+		chip->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_X2;
+	else
+		chip->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_FAST;
+
+	if ((controller_cap & SPINAND_CAP_WR_X4) &&
+	    (rw_mode & SPINAND_WR_X4))
+		chip->write_cache_op = SPINAND_CMD_PROG_LOAD_X4;
+	else
+		chip->write_cache_op = SPINAND_CMD_PROG_LOAD;
+}
+
+/*
+ * spinand_manufacturer_detect - detect SPI NAND device by each manufacturer
+ * @chip: SPI NAND device structure
+ *
+ * ->detect() should decode raw id in chip->id.data and initialize device
+ * related part in spinand_device structure if it is the right device.
+ * ->detect() can not be NULL.
+ */
+static int spinand_manufacturer_detect(struct spinand_device *chip)
+{
+	int i = 0;
+
+	for (; spinand_manufacturers[i]->id != 0x0; i++) {
+		if (!spinand_manufacturers[i]->ops ||
+		    !spinand_manufacturers[i]->ops->detect) {
+			pr_err("%s's ops or ops->detect() is be NULL!\n",
+			       spinand_manufacturers[i]->name);
+			return -EINVAL;
+		}
+		if (spinand_manufacturers[i]->ops->detect(chip)) {
+			chip->manufacturer.manu = spinand_manufacturers[i];
+			return 0;
+		}
+	}
+
+	return -ENODEV;
+}
+
+/*
+ * spinand_manufacturer_init - manufacturer initialization function.
+ * @chip: SPI NAND device structure
+ *
+ * Manufacturer drivers should put all their specific initialization code in
+ * their ->init() hook.
+ */
+static int spinand_manufacturer_init(struct spinand_device *chip)
+{
+	if (chip->manufacturer.manu->ops->init)
+		return chip->manufacturer.manu->ops->init(chip);
+
+	return 0;
+}
+
+/*
+ * spinand_manufacturer_cleanup - manufacturer cleanup function.
+ * @chip: SPI NAND device structure
+ *
+ * Manufacturer drivers should put all their specific cleanup code in their
+ * ->cleanup() hook.
+ */
+static void spinand_manufacturer_cleanup(struct spinand_device *chip)
+{
+	/* Release manufacturer private data */
+	if (chip->manufacturer.manu->ops->cleanup)
+		return chip->manufacturer.manu->ops->cleanup(chip);
+}
+
+/*
+ * spinand_dt_init - Initialize SPI NAND by device tree node
+ * @chip: SPI NAND device structure
+ *
+ * TODO: put ecc_mode, ecc_strength, ecc_step, bbt, etc in here
+ * and move it in generic NAND core.
+ */
+static void spinand_dt_init(struct spinand_device *chip)
+{
+}
+
+/*
+ * spinand_detect - detect the SPI NAND device
+ * @chip: SPI NAND device structure
+ */
+static int spinand_detect(struct spinand_device *chip)
+{
+	struct nand_device *nand = &chip->base;
+	int ret;
+
+	spinand_reset(chip);
+	spinand_read_id(chip, chip->id.data);
+	chip->id.len = SPINAND_MAX_ID_LEN;
+
+	ret = spinand_manufacturer_detect(chip);
+	if (ret) {
+		pr_err("SPI NAND: unknown raw ID %*phN\n",
+		       SPINAND_MAX_ID_LEN, chip->id.data);
+		goto out;
+	}
+
+	pr_info("%s (%s) is found.\n",
+		chip->name, chip->manufacturer.manu->name);
+	pr_info("%d MiB, block size: %d KiB, page size: %d, OOB size: %d\n",
+		(int)(nand_size(nand) >> 20), nand_eraseblock_size(nand) >> 10,
+		nand_page_size(nand), nand_per_page_oobsize(nand));
+
+out:
+	return ret;
+}
+
+/*
+ * spinand_init - initialize the SPI NAND device
+ * @chip: SPI NAND device structure
+ */
+static int spinand_init(struct spinand_device *chip)
+{
+	struct mtd_info *mtd = spinand_to_mtd(chip);
+	struct nand_device *nand = mtd_to_nand(mtd);
+	struct spinand_ecc_engine *ecc_engine;
+	int ret;
+
+	spinand_dt_init(chip);
+	spinand_set_rd_wr_op(chip);
+
+	chip->buf = kzalloc(nand_page_size(nand) + nand_per_page_oobsize(nand),
+			    GFP_KERNEL);
+	if (!chip->buf) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	chip->oobbuf = chip->buf + nand_page_size(nand);
+
+	spinand_manufacturer_init(chip);
+
+	mtd->name = chip->name;
+	mtd->size = nand_size(nand);
+	mtd->erasesize = nand_eraseblock_size(nand);
+	mtd->writesize = nand_page_size(nand);
+	mtd->writebufsize = mtd->writesize;
+	mtd->owner = THIS_MODULE;
+	mtd->type = MTD_NANDFLASH;
+	mtd->flags = MTD_CAP_NANDFLASH;
+	if (!mtd->ecc_strength)
+		mtd->ecc_strength = ecc_engine->strength ?
+				    ecc_engine->strength : 1;
+
+	mtd->oobsize = nand_per_page_oobsize(nand);
+	ret = mtd_ooblayout_count_freebytes(mtd);
+	if (ret < 0)
+		ret = 0;
+	mtd->oobavail = ret;
+
+	if (!mtd->bitflip_threshold)
+		mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3,
+						      4);
+	/* After power up, all blocks are locked, so unlock it here. */
+	spinand_lock_block(chip, BL_ALL_UNLOCKED);
+
+	return nand_register(nand);
+
+err:
+	return ret;
+}
+
+/*
+ * spinand_alloc - [SPI NAND Interface] allocate SPI NAND device instance
+ * @dev: pointer to device model structure
+ */
+struct spinand_device *spinand_alloc(struct device *dev)
+{
+	struct spinand_device *chip;
+	struct spinand_ecc_engine *ecc_engine;
+
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		goto err1;
+
+	ecc_engine = kzalloc(sizeof(*ecc_engine), GFP_KERNEL);
+	if (!ecc_engine)
+		goto err2;
+
+	ecc_engine->mode = SPINAND_ECC_ONDIE;
+	chip->ecc.engine = ecc_engine;
+	spinand_set_of_node(chip, dev->of_node);
+	mutex_init(&chip->lock);
+
+	return chip;
+
+err2:
+	kfree(chip);
+err1:
+	return ERR_PTR(-ENOMEM);
+}
+EXPORT_SYMBOL_GPL(spinand_alloc);
+
+/*
+ * spinand_free - [SPI NAND Interface] free SPI NAND device instance
+ * @chip: SPI NAND device structure
+ */
+void spinand_free(struct spinand_device *chip)
+{
+	kfree(chip->ecc.engine);
+	kfree(chip);
+}
+EXPORT_SYMBOL_GPL(spinand_free);
+
+/*
+ * spinand_register - [SPI NAND Interface] register SPI NAND device
+ * @chip: SPI NAND device structure
+ */
+int spinand_register(struct spinand_device *chip)
+{
+	int ret;
+
+	ret = spinand_detect(chip);
+	if (ret) {
+		pr_err("Detect SPI NAND failed with error %d.\n", ret);
+		return ret;
+	}
+
+	ret = spinand_init(chip);
+	if (ret)
+		pr_err("Init SPI NAND failed with error %d.\n", ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(spinand_register);
+
+/*
+ * spinand_unregister - [SPI NAND Interface] unregister SPI NAND device
+ * @chip: SPI NAND device structure
+ */
+int spinand_unregister(struct spinand_device *chip)
+{
+	struct nand_device *nand = &chip->base;
+
+	nand_unregister(nand);
+	spinand_manufacturer_cleanup(chip);
+	kfree(chip->buf);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(spinand_unregister);
+
+MODULE_DESCRIPTION("SPI NAND framework");
+MODULE_AUTHOR("Peter Pan<peterpandong@micron.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/nand/spi/manufactures.c b/drivers/mtd/nand/spi/manufactures.c
new file mode 100644
index 0000000..7e0b42d
--- /dev/null
+++ b/drivers/mtd/nand/spi/manufactures.c
@@ -0,0 +1,24 @@
+/**
+ *
+ * Copyright (c) 2009-2017 Micron Technology, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/mtd/spinand.h>
+
+struct spinand_manufacturer spinand_manufacturer_end = {0x0, "Unknown", NULL};
+
+struct spinand_manufacturer *spinand_manufacturers[] = {
+	&spinand_manufacturer_end,
+};
+EXPORT_SYMBOL(spinand_manufacturers);
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
new file mode 100644
index 0000000..44748b4
--- /dev/null
+++ b/include/linux/mtd/spinand.h
@@ -0,0 +1,270 @@
+/**
+ *
+ * Copyright (c) 2009-2017 Micron Technology, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __LINUX_MTD_SPINAND_H
+#define __LINUX_MTD_SPINAND_H
+
+#include <linux/mutex.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+
+/*
+ * Standard SPI-NAND flash commands
+ */
+#define SPINAND_CMD_RESET			0xff
+#define SPINAND_CMD_GET_FEATURE			0x0f
+#define SPINAND_CMD_SET_FEATURE			0x1f
+#define SPINAND_CMD_PAGE_READ			0x13
+#define SPINAND_CMD_READ_PAGE_CACHE_RDM		0x30
+#define SPINAND_CMD_READ_PAGE_CACHE_LAST	0x3f
+#define SPINAND_CMD_READ_FROM_CACHE		0x03
+#define SPINAND_CMD_READ_FROM_CACHE_FAST	0x0b
+#define SPINAND_CMD_READ_FROM_CACHE_X2		0x3b
+#define SPINAND_CMD_READ_FROM_CACHE_DUAL_IO	0xbb
+#define SPINAND_CMD_READ_FROM_CACHE_X4		0x6b
+#define SPINAND_CMD_READ_FROM_CACHE_QUAD_IO	0xeb
+#define SPINAND_CMD_BLK_ERASE			0xd8
+#define SPINAND_CMD_PROG_EXC			0x10
+#define SPINAND_CMD_PROG_LOAD			0x02
+#define SPINAND_CMD_PROG_LOAD_RDM_DATA		0x84
+#define SPINAND_CMD_PROG_LOAD_X4		0x32
+#define SPINAND_CMD_PROG_LOAD_RDM_DATA_X4	0x34
+#define SPINAND_CMD_READ_ID			0x9f
+#define SPINAND_CMD_WR_DISABLE			0x04
+#define SPINAND_CMD_WR_ENABLE			0x06
+
+/* feature registers */
+#define REG_BLOCK_LOCK		0xa0
+#define REG_CFG			0xb0
+#define REG_STATUS		0xc0
+#define REG_DIE_SELECT		0xd0
+
+/* status */
+#define STATUS_OIP_MASK		0x01
+#define STATUS_CRBSY_MASK	0x80
+#define STATUS_READY		(0 << 0)
+#define STATUS_BUSY		(1 << 0)
+
+#define STATUS_E_FAIL_MASK	0x04
+#define STATUS_E_FAIL		(1 << 2)
+
+#define STATUS_P_FAIL_MASK	0x08
+#define STATUS_P_FAIL		(1 << 3)
+
+/*Configuration register defines*/
+#define CFG_QE_MASK		0x01
+#define CFG_QE_ENABLE		0x01
+#define CFG_ECC_MASK		0x10
+#define CFG_ECC_ENABLE		0x10
+#define CFG_LOT_MASK		0x20
+#define CFG_LOT_ENABLE		0x20
+#define CFG_OTP_MASK		0xc2
+#define CFG_OTP_ENTER		0x40
+#define CFG_OTP_EXIT		0x00
+
+/* block lock */
+#define BL_ALL_LOCKED		0x7c
+#define BL_U_1_1024_LOCKED	0x08
+#define BL_U_1_512_LOCKED	0x10
+#define BL_U_1_256_LOCKED	0x18
+#define BL_U_1_128_LOCKED	0x20
+#define BL_U_1_64_LOCKED	0x28
+#define BL_U_1_32_LOCKED	0x30
+#define BL_U_1_16_LOCKED	0x38
+#define BL_U_1_8_LOCKED		0x40
+#define BL_U_1_4_LOCKED		0x48
+#define BL_U_1_2_LOCKED		0x50
+#define BL_L_1_1024_LOCKED	0x0c
+#define BL_L_1_512_LOCKED	0x14
+#define BL_L_1_256_LOCKED	0x1c
+#define BL_L_1_128_LOCKED	0x24
+#define BL_L_1_64_LOCKED	0x2c
+#define BL_L_1_32_LOCKED	0x34
+#define BL_L_1_16_LOCKED	0x3c
+#define BL_L_1_8_LOCKED		0x44
+#define BL_L_1_4_LOCKED		0x4c
+#define BL_L_1_2_LOCKED		0x54
+#define BL_ALL_UNLOCKED		0X00
+
+/* die select */
+#define DIE_SELECT_MASK		0x40
+#define DIE_SELECT_DS0		0x00
+#define DIE_SELECT_DS1		0x40
+
+struct spinand_op;
+struct spinand_device;
+
+#define SPINAND_MAX_ID_LEN	4
+
+/**
+ * struct nand_id - NAND id structure
+ * @data: buffer containing the id bytes. Currently 8 bytes large, but can
+ *	  be extended if required.
+ * @len: ID length.
+ */
+struct spinand_id {
+	u8 data[SPINAND_MAX_ID_LEN];
+	int len;
+};
+
+struct spinand_controller_ops {
+	int (*exec_op)(struct spinand_device *chip,
+		       struct spinand_op *op);
+};
+
+struct spinand_manufacturer_ops {
+	bool (*detect)(struct spinand_device *chip);
+	int (*init)(struct spinand_device *chip);
+	void (*cleanup)(struct spinand_device *chip);
+};
+
+struct spinand_manufacturer {
+	u8 id;
+	char *name;
+	const struct spinand_manufacturer_ops *ops;
+};
+
+extern struct spinand_manufacturer *spinand_manufacturers[];
+
+struct spinand_ecc_engine_ops {
+	void (*get_ecc_status)(struct spinand_device *chip,
+			       unsigned int status, unsigned int *corrected,
+			       unsigned int *ecc_errors);
+	void (*disable_ecc)(struct spinand_device *chip);
+	void (*enable_ecc)(struct spinand_device *chip);
+};
+
+enum spinand_ecc_mode {
+	SPINAND_ECC_ONDIE,
+	SPINAND_ECC_HW,
+};
+
+struct spinand_ecc_engine {
+	enum spinand_ecc_mode mode;
+	u32 strength;
+	u32 steps;
+	struct spinand_ecc_engine_ops *ops;
+};
+
+#define SPINAND_CAP_RD_X1 BIT(0)
+#define SPINAND_CAP_RD_X2 BIT(1)
+#define SPINAND_CAP_RD_X4 BIT(2)
+#define SPINAND_CAP_RD_DUAL BIT(3)
+#define SPINAND_CAP_RD_QUAD BIT(4)
+#define SPINAND_CAP_WR_X1 BIT(5)
+#define SPINAND_CAP_WR_X2 BIT(6)
+#define SPINAND_CAP_WR_X4 BIT(7)
+#define SPINAND_CAP_WR_DUAL BIT(8)
+#define SPINAND_CAP_WR_QUAD BIT(9)
+#define SPINAND_CAP_HW_ECC BIT(10)
+
+struct spinand_controller {
+	struct spinand_controller_ops *ops;
+	u32 caps;
+};
+
+/**
+ * struct spinand_device - SPI-NAND Private Flash Chip Data
+ * @base: NAND device instance
+ * @lock: protection lock
+ * @name: name of the chip
+ * @id: ID structure
+ * @read_cache_op: Opcode of read from cache
+ * @write_cache_op: Opcode of program load
+ * @buf: buffer for read/write data
+ * @oobbuf: buffer for read/write oob
+ * @rw_mode: read/write mode of SPI NAND chip
+ * @controller: SPI NAND controller instance
+ * @manufacturer: SPI NAND manufacturer instance, describe
+ *                manufacturer related objects
+ * @ecc_engine: SPI NAND ECC engine instance
+ */
+struct spinand_device {
+	struct nand_device base;
+	struct mutex lock;
+	char *name;
+	struct spinand_id id;
+	u8 read_cache_op;
+	u8 write_cache_op;
+	u8 *buf;
+	u8 *oobbuf;
+	u32 rw_mode;
+	struct {
+		struct spinand_controller *controller;
+		void *priv;
+	} controller;
+	struct {
+		const struct spinand_manufacturer *manu;
+		void *priv;
+	} manufacturer;
+	struct {
+		struct spinand_ecc_engine *engine;
+		void *context;
+	} ecc;
+};
+
+static inline struct spinand_device *mtd_to_spinand(struct mtd_info *mtd)
+{
+	return container_of(mtd_to_nand(mtd), struct spinand_device, base);
+}
+
+static inline struct mtd_info *spinand_to_mtd(struct spinand_device *chip)
+{
+	return nand_to_mtd(&chip->base);
+}
+
+static inline void spinand_set_of_node(struct spinand_device *chip,
+				       struct device_node *np)
+{
+	nand_set_of_node(&chip->base, np);
+}
+
+#define SPINAND_MAX_ADDR_LEN	4
+
+struct spinand_op {
+	u8 cmd;
+	u8 n_addr;
+	u8 addr_nbits;
+	u8 dummy_bytes;
+	u8 addr[SPINAND_MAX_ADDR_LEN];
+	u32 n_tx;
+	const u8 *tx_buf;
+	u32 n_rx;
+	u8 *rx_buf;
+	u8 data_nbits;
+};
+
+/* SPI NAND supported OP mode */
+#define SPINAND_RD_X1		0x00000001
+#define SPINAND_RD_X2		0x00000002
+#define SPINAND_RD_X4		0x00000004
+#define SPINAND_RD_DUAL		0x00000008
+#define SPINAND_RD_QUAD		0x00000010
+#define SPINAND_WR_X1		0x00000020
+#define SPINAND_WR_X2		0x00000040
+#define SPINAND_WR_X4		0x00000080
+#define SPINAND_WR_DUAL		0x00000100
+#define SPINAND_WR_QUAD		0x00000200
+
+#define SPINAND_RD_COMMON	(SPINAND_RD_X1 | SPINAND_RD_X2 | \
+				 SPINAND_RD_X4 | SPINAND_RD_DUAL | \
+				 SPINAND_RD_QUAD)
+#define SPINAND_WR_COMMON	(SPINAND_WR_X1 | SPINAND_WR_X4)
+#define SPINAND_OP_COMMON	(SPINAND_RD_COMMON | SPINAND_WR_COMMON)
+
+struct spinand_device *spinand_alloc(struct device *dev);
+void spinand_free(struct spinand_device *chip);
+int spinand_register(struct spinand_device *chip);
+int spinand_unregister(struct spinand_device *chip);
+#endif /* __LINUX_MTD_SPINAND_H */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v4 5/9] nand: spi: add basic operations support
  2017-03-23  9:43 [PATCH v4 0/9] Introduction to SPI NAND framework Peter Pan
                   ` (3 preceding siblings ...)
  2017-03-23  9:43 ` [PATCH v4 4/9] nand: spi: add basic blocks for infrastructure Peter Pan
@ 2017-03-23  9:43 ` Peter Pan
  2017-03-23  9:43 ` [PATCH v4 6/9] nand: spi: Add bad block support Peter Pan
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 36+ messages in thread
From: Peter Pan @ 2017-03-23  9:43 UTC (permalink / raw)
  To: boris.brezillon, richard, computersforpeace, arnaud.mouiche,
	thomas.petazzoni, marex, cyrille.pitchen, linux-mtd
  Cc: peterpandong, peterpansjtu, linshunquan1

This commit is to support read, readoob, write,
writeoob and erase operations in the new spi nand
framework.

Signed-off-by: Peter Pan <peterpandong@micron.com>
---
 drivers/mtd/nand/spi/core.c | 746 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/mtd/spinand.h |   2 +
 2 files changed, 748 insertions(+)

diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index d1ac522..bb1166e 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -109,6 +109,232 @@ static int spinand_read_status(struct spinand_device *chip, uint8_t *status)
 }
 
 /*
+ * spinand_get_cfg - get configuration register value
+ * @chip: SPI NAND device structure
+ * @cfg: buffer to store value
+ * Description:
+ *   Configuration register includes OTP config, Lock Tight enable/disable
+ *   and Internal ECC enable/disable.
+ */
+static int spinand_get_cfg(struct spinand_device *chip, u8 *cfg)
+{
+	return spinand_read_reg(chip, REG_CFG, cfg);
+}
+
+/*
+ * spinand_set_cfg - set value to configuration register
+ * @chip: SPI NAND device structure
+ * @cfg: buffer stored value
+ * Description:
+ *   Configuration register includes OTP config, Lock Tight enable/disable
+ *   and Internal ECC enable/disable.
+ */
+static int spinand_set_cfg(struct spinand_device *chip, u8 *cfg)
+{
+	return spinand_write_reg(chip, REG_CFG, cfg);
+}
+
+/*
+ * spinand_enable_ecc - enable internal ECC
+ * @chip: SPI NAND device structure
+ */
+static void spinand_enable_ecc(struct spinand_device *chip)
+{
+	u8 cfg = 0;
+
+	spinand_get_cfg(chip, &cfg);
+	if ((cfg & CFG_ECC_MASK) == CFG_ECC_ENABLE)
+		return;
+	cfg |= CFG_ECC_ENABLE;
+	spinand_set_cfg(chip, &cfg);
+}
+
+/*
+ * spinand_disable_ecc - disable internal ECC
+ * @chip: SPI NAND device structure
+ */
+static void spinand_disable_ecc(struct spinand_device *chip)
+{
+	u8 cfg = 0;
+
+	spinand_get_cfg(chip, &cfg);
+	if ((cfg & CFG_ECC_MASK) == CFG_ECC_ENABLE) {
+		cfg &= ~CFG_ECC_ENABLE;
+		spinand_set_cfg(chip, &cfg);
+	}
+}
+
+/*
+ * spinand_write_enable - send command 06h to enable write or erase the
+ * NAND cells
+ * @chip: SPI NAND device structure
+ */
+static int spinand_write_enable(struct spinand_device *chip)
+{
+	struct spinand_op op;
+
+	spinand_op_init(&op);
+	op.cmd = SPINAND_CMD_WR_ENABLE;
+
+	return spinand_exec_op(chip, &op);
+}
+
+/*
+ * spinand_read_page_to_cache - send command 13h to read data from NAND array
+ * to cache
+ * @chip: SPI NAND device structure
+ * @page_addr: page to read
+ */
+static int spinand_read_page_to_cache(struct spinand_device *chip,
+				      u32 page_addr)
+{
+	struct spinand_op op;
+
+	spinand_op_init(&op);
+	op.cmd = SPINAND_CMD_PAGE_READ;
+	op.n_addr = 3;
+	op.addr[0] = (u8)(page_addr >> 16);
+	op.addr[1] = (u8)(page_addr >> 8);
+	op.addr[2] = (u8)page_addr;
+
+	return spinand_exec_op(chip, &op);
+}
+
+/*
+ * spinand_get_address_bits - return address should be transferred
+ * by how many bits
+ * @opcode: command's operation code
+ */
+static int spinand_get_address_bits(u8 opcode)
+{
+	switch (opcode) {
+	case SPINAND_CMD_READ_FROM_CACHE_QUAD_IO:
+		return 4;
+	case SPINAND_CMD_READ_FROM_CACHE_DUAL_IO:
+		return 2;
+	default:
+		return 1;
+	}
+}
+
+/*
+ * spinand_get_data_bits - return data should be transferred by how many bits
+ * @opcode: command's operation code
+ */
+static int spinand_get_data_bits(u8 opcode)
+{
+	switch (opcode) {
+	case SPINAND_CMD_READ_FROM_CACHE_QUAD_IO:
+	case SPINAND_CMD_READ_FROM_CACHE_X4:
+	case SPINAND_CMD_PROG_LOAD_X4:
+	case SPINAND_CMD_PROG_LOAD_RDM_DATA_X4:
+		return 4;
+	case SPINAND_CMD_READ_FROM_CACHE_DUAL_IO:
+	case SPINAND_CMD_READ_FROM_CACHE_X2:
+		return 2;
+	default:
+		return 1;
+	}
+}
+
+/*
+ * spinand_read_from_cache - read data out from cache register
+ * @chip: SPI NAND device structure
+ * @page_addr: page to read
+ * @column: the location to read from the cache
+ * @len: number of bytes to read
+ * @rbuf: buffer held @len bytes
+ */
+static int spinand_read_from_cache(struct spinand_device *chip, u32 page_addr,
+				   u32 column, size_t len, u8 *rbuf)
+{
+	struct spinand_op op;
+
+	spinand_op_init(&op);
+	op.cmd = chip->read_cache_op;
+	op.n_addr = 2;
+	op.addr[0] = (u8)(column >> 8);
+	op.addr[1] = (u8)column;
+	op.addr_nbits = spinand_get_address_bits(chip->read_cache_op);
+	op.n_rx = len;
+	op.rx_buf = rbuf;
+	op.data_nbits = spinand_get_data_bits(chip->read_cache_op);
+	if (chip->manufacturer.manu->ops->prepare_op)
+		chip->manufacturer.manu->ops->prepare_op(chip, &op,
+							 page_addr, column);
+
+	return spinand_exec_op(chip, &op);
+}
+
+/*
+ * spinand_write_to_cache - write data to cache register
+ * @chip: SPI NAND device structure
+ * @page_addr: page to write
+ * @column: the location to write to the cache
+ * @len: number of bytes to write
+ * @wrbuf: buffer held @len bytes
+ */
+static int spinand_write_to_cache(struct spinand_device *chip, u32 page_addr,
+				  u32 column, size_t len, const u8 *wbuf)
+{
+	struct spinand_op op;
+
+	spinand_op_init(&op);
+	op.cmd = chip->write_cache_op;
+	op.n_addr = 2;
+	op.addr[0] = (u8)(column >> 8);
+	op.addr[1] = (u8)column;
+	op.addr_nbits = spinand_get_address_bits(chip->write_cache_op);
+	op.n_tx = len;
+	op.tx_buf = wbuf;
+	op.data_nbits = spinand_get_data_bits(chip->write_cache_op);
+	if (chip->manufacturer.manu->ops->prepare_op)
+		chip->manufacturer.manu->ops->prepare_op(chip, &op,
+							 page_addr, column);
+
+	return spinand_exec_op(chip, &op);
+}
+
+/*
+ * spinand_program_execute - send command 10h to write a page from
+ * cache to the NAND array
+ * @chip: SPI NAND device structure
+ * @page_addr: the physical page location to write the page.
+ */
+static int spinand_program_execute(struct spinand_device *chip, u32 page_addr)
+{
+	struct spinand_op op;
+
+	spinand_op_init(&op);
+	op.cmd = SPINAND_CMD_PROG_EXC;
+	op.n_addr = 3;
+	op.addr[0] = (u8)(page_addr >> 16);
+	op.addr[1] = (u8)(page_addr >> 8);
+	op.addr[2] = (u8)page_addr;
+
+	return spinand_exec_op(chip, &op);
+}
+
+/*
+ * spinand_erase_block_erase - send command D8h to erase a block
+ * @chip: SPI NAND device structure
+ * @page_addr: the start page address of block to be erased.
+ */
+static int spinand_erase_block(struct spinand_device *chip, u32 page_addr)
+{
+	struct spinand_op op;
+
+	spinand_op_init(&op);
+	op.cmd = SPINAND_CMD_BLK_ERASE;
+	op.n_addr = 3;
+	op.addr[0] = (u8)(page_addr >> 16);
+	op.addr[1] = (u8)(page_addr >> 8);
+	op.addr[2] = (u8)page_addr;
+
+	return spinand_exec_op(chip, &op);
+}
+
+/*
  * spinand_wait - wait until the command is done
  * @chip: SPI NAND device structure
  * @s: buffer to store status register value (can be NULL)
@@ -193,6 +419,521 @@ static int spinand_lock_block(struct spinand_device *chip, u8 lock)
 }
 
 /*
+ * spinand_get_ecc_status - get ecc correction information from status register
+ * @chip: SPI NAND device structure
+ * @status: status register value
+ * @corrected: corrected bit flip number
+ * @ecc_error: ecc correction error or not
+ */
+static void spinand_get_ecc_status(struct spinand_device *chip,
+				   unsigned int status,
+				   unsigned int *corrected,
+				   unsigned int *ecc_error)
+{
+	return chip->ecc.engine->ops->get_ecc_status(chip, status, corrected,
+						     ecc_error);
+}
+
+/*
+ * spinand_do_read_page - read page from device to buffer
+ * @mtd: MTD device structure
+ * @page_addr: page address/raw address
+ * @ecc_off: without ecc or not
+ * @corrected: how many bit flip corrected
+ * @oob_only: read OOB only or the whole page
+ */
+static int spinand_do_read_page(struct mtd_info *mtd, u32 page_addr,
+				bool ecc_off, int *corrected, bool oob_only)
+{
+	struct spinand_device *chip = mtd_to_spinand(mtd);
+	struct nand_device *nand = mtd_to_nand(mtd);
+	int ret, ecc_error = 0;
+	u8 status;
+
+	spinand_read_page_to_cache(chip, page_addr);
+	ret = spinand_wait(chip, &status);
+	if (ret < 0) {
+		pr_err("error %d waiting page 0x%x to cache\n",
+		       ret, page_addr);
+		return ret;
+	}
+	if (!oob_only)
+		spinand_read_from_cache(chip, page_addr, 0,
+					nand_page_size(nand) +
+					nand_per_page_oobsize(nand),
+					chip->buf);
+	else
+		spinand_read_from_cache(chip, page_addr, nand_page_size(nand),
+					nand_per_page_oobsize(nand),
+					chip->oobbuf);
+	if (!ecc_off) {
+		spinand_get_ecc_status(chip, status, corrected, &ecc_error);
+		/*
+		 * If there's an ECC error, print a message and notify MTD
+		 * about it. Then complete the read, to load actual data on
+		 * the buffer (instead of the status result).
+		 */
+		if (ecc_error) {
+			pr_err("internal ECC error reading page 0x%x\n",
+			       page_addr);
+			mtd->ecc_stats.failed++;
+		} else if (*corrected) {
+			mtd->ecc_stats.corrected += *corrected;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * spinand_do_write_page - write data from buffer to device
+ * @mtd: MTD device structure
+ * @page_addr: page address/raw address
+ * @oob_only: write OOB only or the whole page
+ */
+static int spinand_do_write_page(struct mtd_info *mtd, u32 page_addr,
+				 bool oob_only)
+{
+	struct spinand_device *chip = mtd_to_spinand(mtd);
+	struct nand_device *nand = mtd_to_nand(mtd);
+	u8 status;
+	int ret = 0;
+
+	spinand_write_enable(chip);
+	if (!oob_only)
+		spinand_write_to_cache(chip, page_addr, 0,
+				       nand_page_size(nand) +
+				       nand_per_page_oobsize(nand), chip->buf);
+	else
+		spinand_write_to_cache(chip, page_addr, nand_page_size(nand),
+				       nand_per_page_oobsize(nand),
+				       chip->oobbuf);
+	spinand_program_execute(chip, page_addr);
+	ret = spinand_wait(chip, &status);
+	if (ret < 0) {
+		pr_err("error %d reading page 0x%x from cache\n",
+		       ret, page_addr);
+		return ret;
+	}
+	if ((status & STATUS_P_FAIL_MASK) == STATUS_P_FAIL) {
+		pr_err("program page 0x%x failed\n", page_addr);
+		ret = -EIO;
+	}
+	return ret;
+}
+
+/*
+ * spinand_transfer_oob - transfer oob to client buffer
+ * @chip: SPI NAND device structure
+ * @oob: oob destination address
+ * @ops: oob ops structure
+ * @len: size of oob to transfer
+ */
+static int spinand_transfer_oob(struct spinand_device *chip, u8 *oob,
+				struct mtd_oob_ops *ops, size_t len)
+{
+	struct mtd_info *mtd = spinand_to_mtd(chip);
+	int ret = 0;
+
+	switch (ops->mode) {
+	case MTD_OPS_PLACE_OOB:
+	case MTD_OPS_RAW:
+		memcpy(oob, chip->oobbuf + ops->ooboffs, len);
+		break;
+	case MTD_OPS_AUTO_OOB:
+		ret = mtd_ooblayout_get_databytes(mtd, oob, chip->oobbuf,
+						  ops->ooboffs, len);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+/*
+ * spinand_fill_oob - transfer client buffer to oob
+ * @chip: SPI NAND device structure
+ * @oob: oob data buffer
+ * @len: oob data write length
+ * @ops: oob ops structure
+ */
+static int spinand_fill_oob(struct spinand_device *chip, uint8_t *oob,
+			    size_t len, struct mtd_oob_ops *ops)
+{
+	struct mtd_info *mtd = spinand_to_mtd(chip);
+	struct nand_device *nand = mtd_to_nand(mtd);
+	int ret = 0;
+
+	memset(chip->oobbuf, 0xff, nand_per_page_oobsize(nand));
+	switch (ops->mode) {
+	case MTD_OPS_PLACE_OOB:
+	case MTD_OPS_RAW:
+		memcpy(chip->oobbuf + ops->ooboffs, oob, len);
+		break;
+	case MTD_OPS_AUTO_OOB:
+		ret = mtd_ooblayout_set_databytes(mtd, oob, chip->oobbuf,
+						  ops->ooboffs, len);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+/*
+ * spinand_read_pages - read data from device to buffer
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @ops: oob operations description structure
+ * @max_bitflips: maximum bitflip count
+ */
+static int spinand_read_pages(struct mtd_info *mtd, loff_t from,
+			      struct mtd_oob_ops *ops,
+			      unsigned int *max_bitflips)
+{
+	struct spinand_device *chip = mtd_to_spinand(mtd);
+	struct nand_device *nand = mtd_to_nand(mtd);
+	int size, ret;
+	unsigned int corrected = 0;
+	bool ecc_off = ops->mode == MTD_OPS_RAW;
+	int ooblen = ops->mode == MTD_OPS_AUTO_OOB ?
+		     mtd->oobavail : mtd->oobsize;
+	bool oob_only = !ops->datbuf;
+	struct nand_page_iter iter;
+
+	ops->retlen = 0;
+	ops->oobretlen = 0;
+	*max_bitflips = 0;
+
+	nand_for_each_page(nand, from, ops->len, ops->ooboffs, ops->ooblen,
+			   ooblen, &iter) {
+		ret = spinand_do_read_page(mtd, iter.page, ecc_off,
+					   &corrected, oob_only);
+		if (ret)
+			break;
+		*max_bitflips = max(*max_bitflips, corrected);
+		if (ops->datbuf) {
+			size = min_t(int, iter.dataleft,
+				     nand_page_size(nand) - iter.pageoffs);
+			memcpy(ops->datbuf + ops->retlen,
+			       chip->buf + iter.pageoffs, size);
+			ops->retlen += size;
+		}
+		if (ops->oobbuf) {
+			size = min_t(int, iter.oobleft, ooblen);
+			ret = spinand_transfer_oob(chip,
+						   ops->oobbuf + ops->oobretlen,
+						   ops, size);
+			if (ret) {
+				pr_err("Transfer oob error %d\n", ret);
+				return ret;
+			}
+			ops->oobretlen += size;
+		}
+	}
+
+	return ret;
+}
+
+/*
+ * spinand_do_read_ops - read data from device to buffer
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @ops: oob operations description structure
+ */
+static int spinand_do_read_ops(struct mtd_info *mtd, loff_t from,
+			       struct mtd_oob_ops *ops)
+{
+	struct spinand_device *chip = mtd_to_spinand(mtd);
+	struct nand_device *nand = mtd_to_nand(mtd);
+	int ret;
+	struct mtd_ecc_stats stats;
+	unsigned int max_bitflips = 0;
+	bool ecc_off = ops->mode == MTD_OPS_RAW;
+
+	ret = nand_check_address(nand, from);
+	if (ret) {
+		pr_err("%s: invalid read address\n", __func__);
+		return ret;
+	}
+	ret = nand_check_oob_ops(nand, from, ops);
+	if (ret) {
+		pr_err("%s: invalid oob operation input\n", __func__);
+		return ret;
+	}
+	mutex_lock(&chip->lock);
+	stats = mtd->ecc_stats;
+	if (ecc_off)
+		spinand_disable_ecc(chip);
+	ret = spinand_read_pages(mtd, from, ops, &max_bitflips);
+	if (ecc_off)
+		spinand_enable_ecc(chip);
+	if (ret)
+		goto out;
+
+	if (mtd->ecc_stats.failed - stats.failed) {
+		ret = -EBADMSG;
+		goto out;
+	}
+	ret = max_bitflips;
+
+out:
+	mutex_unlock(&chip->lock);
+	return ret;
+}
+
+/*
+ * spinand_write_pages - write data from buffer to device
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operations description structure
+ */
+static int spinand_write_pages(struct mtd_info *mtd, loff_t to,
+			       struct mtd_oob_ops *ops)
+{
+	struct spinand_device *chip = mtd_to_spinand(mtd);
+	struct nand_device *nand = mtd_to_nand(mtd);
+	int ret = 0;
+	int size = 0;
+	int oob_size = 0;
+	int ooblen = ops->mode == MTD_OPS_AUTO_OOB ?
+		     mtd->oobavail : mtd->oobsize;
+	bool oob_only = !ops->datbuf;
+	struct nand_page_iter iter;
+
+	ops->retlen = 0;
+	ops->oobretlen = 0;
+
+	nand_for_each_page(nand, to, ops->len, ops->ooboffs, ops->ooblen,
+			   ooblen, &iter) {
+		memset(chip->buf, 0xff,
+		       nand_page_size(nand) + nand_per_page_oobsize(nand));
+		if (ops->oobbuf) {
+			oob_size = min_t(int, iter.oobleft, ooblen);
+			ret = spinand_fill_oob(chip,
+					       ops->oobbuf + ops->oobretlen,
+					       oob_size, ops);
+			if (ret) {
+				pr_err("Fill oob error %d\n", ret);
+				return ret;
+			}
+		}
+		if (ops->datbuf) {
+			size = min_t(int, iter.dataleft,
+				     nand_page_size(nand) - iter.pageoffs);
+			memcpy(chip->buf + iter.pageoffs,
+			       ops->datbuf + ops->retlen, size);
+		}
+		ret = spinand_do_write_page(mtd, iter.page, oob_only);
+		if (ret) {
+			pr_err("error %d writing page 0x%x\n",
+			       ret, iter.page);
+			return ret;
+		}
+		if (ops->datbuf)
+			ops->retlen += size;
+		if (ops->oobbuf)
+			ops->oobretlen += oob_size;
+	}
+
+	return ret;
+}
+
+/*
+ * spinand_do_write_ops - write data from buffer to device
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operations description structure
+ */
+static int spinand_do_write_ops(struct mtd_info *mtd, loff_t to,
+				struct mtd_oob_ops *ops)
+{
+	struct spinand_device *chip = mtd_to_spinand(mtd);
+	struct nand_device *nand = mtd_to_nand(mtd);
+	int ret = 0;
+	bool ecc_off = ops->mode == MTD_OPS_RAW;
+
+	ret = nand_check_address(nand, to);
+	if (ret) {
+		pr_err("%s: invalid write address\n", __func__);
+		return ret;
+	}
+	ret = nand_check_oob_ops(nand, to, ops);
+	if (ret) {
+		pr_err("%s: invalid oob operation input\n", __func__);
+		return ret;
+	}
+	if (nand_oob_ops_across_page(mtd_to_nand(mtd), ops)) {
+		pr_err("%s: try to across page when writing with OOB\n",
+		       __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&chip->lock);
+	if (ecc_off)
+		spinand_disable_ecc(chip);
+	ret = spinand_write_pages(mtd, to, ops);
+	if (ecc_off)
+		spinand_enable_ecc(chip);
+	mutex_unlock(&chip->lock);
+
+	return ret;
+}
+
+/*
+ * spinand_read - [MTD Interface] read page data
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @len: number of bytes to read
+ * @retlen: pointer to variable to store the number of read bytes
+ * @buf: the databuffer to put data
+ */
+static int spinand_read(struct mtd_info *mtd, loff_t from, size_t len,
+			size_t *retlen, u8 *buf)
+{
+	struct mtd_oob_ops ops;
+	int ret;
+
+	memset(&ops, 0, sizeof(ops));
+	ops.len = len;
+	ops.datbuf = buf;
+	ops.mode = MTD_OPS_PLACE_OOB;
+	ret = spinand_do_read_ops(mtd, from, &ops);
+	*retlen = ops.retlen;
+
+	return ret;
+}
+
+/*
+ * spinand_write - [MTD Interface] write page data
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @len: number of bytes to write
+ * @retlen: pointer to variable to store the number of written bytes
+ * @buf: the data to write
+ */
+static int spinand_write(struct mtd_info *mtd, loff_t to, size_t len,
+			 size_t *retlen, const u8 *buf)
+{
+	struct mtd_oob_ops ops;
+	int ret;
+
+	memset(&ops, 0, sizeof(ops));
+	ops.len = len;
+	ops.datbuf = (uint8_t *)buf;
+	ops.mode = MTD_OPS_PLACE_OOB;
+	ret =  spinand_do_write_ops(mtd, to, &ops);
+	*retlen = ops.retlen;
+
+	return ret;
+}
+
+/*
+ * spinand_read_oob - [MTD Interface] read page data and/or out-of-band
+ * @mtd: MTD device structure
+ * @from: offset to read from
+ * @ops: oob operation description structure
+ */
+static int spinand_read_oob(struct mtd_info *mtd, loff_t from,
+			    struct mtd_oob_ops *ops)
+{
+	int ret = -ENOTSUPP;
+
+	ops->retlen = 0;
+	switch (ops->mode) {
+	case MTD_OPS_PLACE_OOB:
+	case MTD_OPS_AUTO_OOB:
+	case MTD_OPS_RAW:
+		ret = spinand_do_read_ops(mtd, from, ops);
+		break;
+	}
+
+	return ret;
+}
+
+/*
+ * spinand_write_oob - [MTD Interface] write page data and/or out-of-band
+ * @mtd: MTD device structure
+ * @to: offset to write to
+ * @ops: oob operation description structure
+ */
+static int spinand_write_oob(struct mtd_info *mtd, loff_t to,
+			     struct mtd_oob_ops *ops)
+{
+	int ret = -ENOTSUPP;
+
+	ops->retlen = 0;
+	switch (ops->mode) {
+	case MTD_OPS_PLACE_OOB:
+	case MTD_OPS_AUTO_OOB:
+	case MTD_OPS_RAW:
+		ret = spinand_do_write_ops(mtd, to, ops);
+		break;
+	}
+
+	return ret;
+}
+
+/*
+ * spinand_erase - [MTD Interface] erase block(s)
+ * @mtd: MTD device structure
+ * @einfo: erase instruction
+ */
+static int spinand_erase(struct mtd_info *mtd, struct erase_info *einfo)
+{
+	struct spinand_device *chip = mtd_to_spinand(mtd);
+	struct nand_device *nand = mtd_to_nand(mtd);
+	loff_t offs = einfo->addr, len = einfo->len;
+	u8 status;
+	int ret;
+
+	ret = nand_check_erase_ops(nand, einfo);
+	if (ret) {
+		pr_err("%s: invalid erase operation input\n", __func__);
+		return ret;
+	}
+
+	mutex_lock(&chip->lock);
+	einfo->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
+	einfo->state = MTD_ERASING;
+
+	while (len) {
+		spinand_write_enable(chip);
+		spinand_erase_block(chip, nand_offs_to_page(nand, offs));
+		ret = spinand_wait(chip, &status);
+		if (ret < 0) {
+			pr_err("block erase command wait failed\n");
+			einfo->state = MTD_ERASE_FAILED;
+			goto erase_exit;
+		}
+		if ((status & STATUS_E_FAIL_MASK) == STATUS_E_FAIL) {
+			pr_err("erase block 0x%012llx failed\n", offs);
+			einfo->state = MTD_ERASE_FAILED;
+			einfo->fail_addr = offs;
+			goto erase_exit;
+		}
+
+		/* Increment page address and decrement length */
+		len -= nand_eraseblock_size(nand);
+		offs += nand_eraseblock_size(nand);
+	}
+
+	einfo->state = MTD_ERASE_DONE;
+
+erase_exit:
+
+	ret = einfo->state == MTD_ERASE_DONE ? 0 : -EIO;
+
+	mutex_unlock(&chip->lock);
+
+	/* Do call back function */
+	if (!ret)
+		mtd_erase_callback(einfo);
+
+	return ret;
+}
+
+/*
  * spinand_set_rd_wr_op - choose the best read write command
  * @chip: SPI NAND device structure
  * Description:
@@ -366,6 +1107,11 @@ static int spinand_init(struct spinand_device *chip)
 	if (ret < 0)
 		ret = 0;
 	mtd->oobavail = ret;
+	mtd->_erase = spinand_erase;
+	mtd->_read = spinand_read;
+	mtd->_write = spinand_write;
+	mtd->_read_oob = spinand_read_oob;
+	mtd->_write_oob = spinand_write_oob;
 
 	if (!mtd->bitflip_threshold)
 		mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3,
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index 44748b4..872bf11 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -127,6 +127,8 @@ struct spinand_manufacturer_ops {
 	bool (*detect)(struct spinand_device *chip);
 	int (*init)(struct spinand_device *chip);
 	void (*cleanup)(struct spinand_device *chip);
+	void (*prepare_op)(struct spinand_device *chip, struct spinand_op *op,
+			   u32 page, u32 column);
 };
 
 struct spinand_manufacturer {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v4 6/9] nand: spi: Add bad block support
  2017-03-23  9:43 [PATCH v4 0/9] Introduction to SPI NAND framework Peter Pan
                   ` (4 preceding siblings ...)
  2017-03-23  9:43 ` [PATCH v4 5/9] nand: spi: add basic operations support Peter Pan
@ 2017-03-23  9:43 ` Peter Pan
  2017-03-23  9:43 ` [PATCH v4 7/9] nand: spi: add Micron spi nand support Peter Pan
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 36+ messages in thread
From: Peter Pan @ 2017-03-23  9:43 UTC (permalink / raw)
  To: boris.brezillon, richard, computersforpeace, arnaud.mouiche,
	thomas.petazzoni, marex, cyrille.pitchen, linux-mtd
  Cc: peterpandong, peterpansjtu, linshunquan1

Add isbad and markbad support for SPI NAND. And do not
erase bad blocks in spi_nand_erase. BBT is also enabled
in this patch.

Signed-off-by: Peter Pan <peterpandong@micron.com>
---
 drivers/mtd/nand/spi/core.c | 327 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 320 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index bb1166e..29432a3 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -20,6 +20,10 @@
 #include <linux/jiffies.h>
 #include <linux/mtd/spinand.h>
 #include <linux/slab.h>
+#include <linux/of.h>
+
+static int spinand_erase_skip_bbt(struct mtd_info *mtd,
+				  struct erase_info *einfo);
 
 /*
  * spinand_exec_op - execute SPI NAND operation by controller ->exec_op() hook
@@ -875,11 +879,154 @@ static int spinand_write_oob(struct mtd_info *mtd, loff_t to,
 }
 
 /*
- * spinand_erase - [MTD Interface] erase block(s)
+ * spinand_block_bad - check if block at offset is bad by bad block marker
+ * @mtd: MTD device structure
+ * @offs: offset from device start
+ */
+static int spinand_block_bad(struct mtd_info *mtd, loff_t offs)
+{
+	struct nand_device *nand = mtd_to_nand(mtd);
+	struct mtd_oob_ops ops = {0};
+	u32 block_addr;
+	u8 bad[2] = {0, 0};
+	u8 ret = 0;
+	unsigned int max_bitflips;
+
+	block_addr = nand_offs_to_eraseblock(nand, offs);
+	ops.mode = MTD_OPS_PLACE_OOB;
+	ops.ooblen = 2;
+	ops.oobbuf = bad;
+	spinand_read_pages(mtd, nand_eraseblock_to_offs(nand, block_addr),
+			   &ops, &max_bitflips);
+	if (bad[0] != 0xFF || bad[1] != 0xFF)
+		ret =  1;
+
+	return ret;
+}
+
+/*
+ * spinand_block_checkbad - check if a block is marked bad
+ * @mtd: MTD device structure
+ * @offs: offset from device start
+ * @allowbbt: 1, if allowe to access the bbt area
+ * Description:
+ *   Check, if the block is bad. Either by reading the bad block table or
+ *   reading bad block marker.
+ */
+static int spinand_block_checkbad(struct mtd_info *mtd, loff_t offs,
+				  int allowbbt)
+{
+	struct nand_device *nand = mtd_to_nand(mtd);
+	int ret;
+
+	if (nand_bbt_is_initialized(nand))
+		ret = nand_isbad_bbt(nand, offs, allowbbt);
+	else
+		ret = spinand_block_bad(mtd, offs);
+
+	return ret;
+}
+
+/*
+ * spinand_block_isbad - [MTD Interface] check if block at offset is bad
+ * @mtd: MTD device structure
+ * @offs: offset from device start
+ */
+static int spinand_block_isbad(struct mtd_info *mtd, loff_t offs)
+{
+	struct spinand_device *chip = mtd_to_spinand(mtd);
+	int ret;
+
+	mutex_lock(&chip->lock);
+	ret = spinand_block_checkbad(mtd, offs, 0);
+	mutex_unlock(&chip->lock);
+
+	return ret;
+}
+
+/*
+ * spinand_block_markbad_lowlevel - mark a block bad
+ * @mtd: MTD device structure
+ * @offs: offset from device start
+ *
+ * This function performs the generic bad block marking steps (i.e., bad
+ * block table(s) and/or marker(s)).
+ *
+ * We try operations in the following order:
+ *  (1) erase the affected block, to allow OOB marker to be written cleanly
+ *  (2) write bad block marker to OOB area of affected block (unless flag
+ *      NAND_BBT_NO_OOB_BBM is present)
+ *  (3) update the BBT
+ */
+static int spinand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t offs)
+{
+	struct nand_device *nand = mtd_to_nand(mtd);
+	struct mtd_oob_ops ops = {0};
+	struct erase_info einfo = {0};
+	u32 block_addr;
+	u8 buf[2] = {0, 0};
+	int res, ret = 0;
+
+	if (!nand_bbt_is_initialized(nand) ||
+	    !(nand->bbt.options & NAND_BBT_NO_OOB_BBM)) {
+		/*erase bad block before mark bad block*/
+		einfo.mtd = mtd;
+		einfo.addr = offs;
+		einfo.len = nand_eraseblock_size(nand);
+		spinand_erase_skip_bbt(mtd, &einfo);
+
+		block_addr = nand_offs_to_eraseblock(nand, offs);
+		ops.mode = MTD_OPS_PLACE_OOB;
+		ops.ooblen = 2;
+		ops.oobbuf = buf;
+		ret = spinand_do_write_ops(mtd,
+					   nand_eraseblock_to_offs(nand,
+								   block_addr),
+					   &ops);
+	}
+
+	/* Mark block bad in BBT */
+	if (nand_bbt_is_initialized(nand)) {
+		res = nand_markbad_bbt(nand, offs);
+		if (!ret)
+			ret = res;
+	}
+
+	if (!ret)
+		mtd->ecc_stats.badblocks++;
+
+	return ret;
+}
+
+/*
+ * spinand_block_markbad - [MTD Interface] mark block at the given offset
+ * as bad
+ * @mtd: MTD device structure
+ * @offs: offset relative to mtd start
+ */
+static int spinand_block_markbad(struct mtd_info *mtd, loff_t offs)
+{
+	int ret;
+
+	ret = spinand_block_isbad(mtd, offs);
+	if (ret) {
+		/* If it was bad already, return success and do nothing */
+		if (ret > 0)
+			return 0;
+		return ret;
+	}
+
+	return spinand_block_markbad_lowlevel(mtd, offs);
+}
+
+/*
+ * spinand_erase - erase block(s)
  * @mtd: MTD device structure
  * @einfo: erase instruction
+ * @allowbbt: allow to access bbt
  */
-static int spinand_erase(struct mtd_info *mtd, struct erase_info *einfo)
+static int spinand_erase(struct mtd_info *mtd, struct erase_info *einfo,
+			 int allowbbt)
 {
 	struct spinand_device *chip = mtd_to_spinand(mtd);
 	struct nand_device *nand = mtd_to_nand(mtd);
@@ -898,6 +1045,13 @@ static int spinand_erase(struct mtd_info *mtd, struct erase_info *einfo)
 	einfo->state = MTD_ERASING;
 
 	while (len) {
+		/* Check if we have a bad block, we do not erase bad blocks! */
+		if (spinand_block_checkbad(mtd, offs, allowbbt)) {
+			pr_warn("%s: attempt to erase a bad block at 0x%012llx\n",
+				__func__, offs);
+			einfo->state = MTD_ERASE_FAILED;
+			goto erase_exit;
+		}
 		spinand_write_enable(chip);
 		spinand_erase_block(chip, nand_offs_to_page(nand, offs));
 		ret = spinand_wait(chip, &status);
@@ -934,6 +1088,33 @@ static int spinand_erase(struct mtd_info *mtd, struct erase_info *einfo)
 }
 
 /*
+ * spinand_erase_skip_bbt - [MTD Interface] erase block(s) except BBT
+ * @mtd: MTD device structure
+ * @einfo: erase instruction
+ */
+static int spinand_erase_skip_bbt(struct mtd_info *mtd,
+				  struct erase_info *einfo)
+{
+	return spinand_erase(mtd, einfo, 0);
+}
+
+/*
+ * spinand_block_isreserved - [MTD Interface] check if a block is
+ * marked reserved.
+ * @mtd: MTD device structure
+ * @offs: offset from device start
+ */
+static int spinand_block_isreserved(struct mtd_info *mtd, loff_t offs)
+{
+	struct nand_device *nand = mtd_to_nand(mtd);
+
+	if (!nand_bbt_is_initialized(nand))
+		return 0;
+	/* Return info from the table */
+	return nand_isreserved_bbt(nand, offs);
+}
+
+/*
  * spinand_set_rd_wr_op - choose the best read write command
  * @chip: SPI NAND device structure
  * Description:
@@ -968,6 +1149,105 @@ static void spinand_set_rd_wr_op(struct spinand_device *chip)
 }
 
 /*
+ * spinand_erase_bbt - erase block(s) including BBT
+ * @nand: nand device structure
+ * @einfo: erase instruction
+ */
+static int spinand_erase_bbt(struct nand_device *nand,
+			     struct erase_info *einfo)
+{
+	return spinand_erase(nand_to_mtd(nand), einfo, 1);
+}
+
+/*
+ * spinand_erase_bbt - write bad block marker to certain block
+ * @nand: nand device structure
+ * @block: block to mark bad
+ */
+static int spinand_markbad(struct nand_device *nand, int block)
+{
+	struct mtd_oob_ops ops = {0};
+	u8 buf[2] = {0, 0};
+
+	ops.mode = MTD_OPS_PLACE_OOB;
+	ops.ooboffs = 0;
+	ops.ooblen = 2;
+	ops.oobbuf = buf;
+
+	return spinand_do_write_ops(nand_to_mtd(nand),
+				    nand_eraseblock_to_offs(nand, block),
+				    &ops);
+}
+
+static const struct nand_ops spinand_ops = {
+	.erase = spinand_erase_bbt,
+	.markbad = spinand_markbad,
+};
+
+/*
+ * Define some generic bad/good block scan pattern which are used
+ * while scanning a device for factory marked good/bad blocks.
+ */
+static u8 scan_ff_pattern[] = { 0xff, 0xff };
+
+#define BADBLOCK_SCAN_MASK (~NAND_BBT_NO_OOB)
+
+/*
+ * spinand_create_badblock_pattern - creates a BBT descriptor structure
+ * @chip: SPI NAND device structure
+ *
+ * This function allocates and initializes a nand_bbt_descr for BBM detection.
+ * The new descriptor is stored in nand->bbt.bbp. Thus, nand->bbt.bbp should
+ * be NULL when passed to this function.
+ */
+static int spinand_create_badblock_pattern(struct spinand_device *chip)
+{
+	struct nand_device *nand = &chip->base;
+	struct nand_bbt_descr *bd;
+
+	if (nand->bbt.bbp) {
+		pr_warn("Bad block pattern already allocated; not replacing\n");
+		return -EINVAL;
+	}
+	bd = kzalloc(sizeof(*bd), GFP_KERNEL);
+	if (!bd)
+		return -ENOMEM;
+	bd->options = nand->bbt.options & BADBLOCK_SCAN_MASK;
+	bd->offs = 0;
+	bd->len = 2;
+	bd->pattern = scan_ff_pattern;
+	bd->options |= NAND_BBT_DYNAMICSTRUCT;
+	nand->bbt.bbp = bd;
+
+	return 0;
+}
+
+/*
+ * spinand_scan_bbt - scan BBT in SPI NAND device
+ * @chip: SPI NAND device structure
+ */
+static int spinand_scan_bbt(struct spinand_device *chip)
+{
+	struct nand_device *nand = &chip->base;
+	int ret;
+
+	/*
+	 * It's better to put BBT marker in-band, since some oob area
+	 * is not ecc protected by internal(on-die) ECC
+	 */
+	if (nand->bbt.options & NAND_BBT_USE_FLASH)
+		nand->bbt.options |= NAND_BBT_NO_OOB;
+	nand->bbt.td = NULL;
+	nand->bbt.md = NULL;
+
+	ret = spinand_create_badblock_pattern(chip);
+	if (ret)
+		return ret;
+
+	return nand_scan_bbt(nand);
+}
+
+/*
  * spinand_manufacturer_detect - detect SPI NAND device by each manufacturer
  * @chip: SPI NAND device structure
  *
@@ -1025,14 +1305,30 @@ static void spinand_manufacturer_cleanup(struct spinand_device *chip)
 }
 
 /*
+ * TODO: move of_get_nand_on_flash_bbt() to generic NAND core
+ */
+static bool of_get_nand_on_flash_bbt(struct device_node *np)
+{
+	return of_property_read_bool(np, "nand-on-flash-bbt");
+}
+
+/*
  * spinand_dt_init - Initialize SPI NAND by device tree node
  * @chip: SPI NAND device structure
  *
- * TODO: put ecc_mode, ecc_strength, ecc_step, bbt, etc in here
- * and move it in generic NAND core.
+ * TODO: put ecc_mode, ecc_strength, ecc_step, etc in here and move
+ * it in generic NAND core.
  */
 static void spinand_dt_init(struct spinand_device *chip)
 {
+	struct nand_device *nand = &chip->base;
+	struct device_node *dn = nand_get_of_node(nand);
+
+	if (!dn)
+		return;
+
+	if (of_get_nand_on_flash_bbt(dn))
+		nand->bbt.options |= NAND_BBT_USE_FLASH;
 }
 
 /*
@@ -1083,13 +1379,14 @@ static int spinand_init(struct spinand_device *chip)
 			    GFP_KERNEL);
 	if (!chip->buf) {
 		ret = -ENOMEM;
-		goto err;
+		goto err1;
 	}
 
 	chip->oobbuf = chip->buf + nand_page_size(nand);
 
 	spinand_manufacturer_init(chip);
 
+	nand->ops = &spinand_ops;
 	mtd->name = chip->name;
 	mtd->size = nand_size(nand);
 	mtd->erasesize = nand_eraseblock_size(nand);
@@ -1107,11 +1404,14 @@ static int spinand_init(struct spinand_device *chip)
 	if (ret < 0)
 		ret = 0;
 	mtd->oobavail = ret;
-	mtd->_erase = spinand_erase;
+	mtd->_erase = spinand_erase_skip_bbt;
 	mtd->_read = spinand_read;
 	mtd->_write = spinand_write;
 	mtd->_read_oob = spinand_read_oob;
 	mtd->_write_oob = spinand_write_oob;
+	mtd->_block_isbad = spinand_block_isbad;
+	mtd->_block_markbad = spinand_block_markbad;
+	mtd->_block_isreserved = spinand_block_isreserved;
 
 	if (!mtd->bitflip_threshold)
 		mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3,
@@ -1119,9 +1419,18 @@ static int spinand_init(struct spinand_device *chip)
 	/* After power up, all blocks are locked, so unlock it here. */
 	spinand_lock_block(chip, BL_ALL_UNLOCKED);
 
+	/* Build bad block table */
+	ret = spinand_scan_bbt(chip);
+	if (ret) {
+		pr_err("Scan Bad Block Table failed.\n");
+		goto err2;
+	}
+
 	return nand_register(nand);
 
-err:
+err2:
+	kfree(chip->buf);
+err1:
 	return ret;
 }
 
@@ -1196,10 +1505,14 @@ int spinand_register(struct spinand_device *chip)
 int spinand_unregister(struct spinand_device *chip)
 {
 	struct nand_device *nand = &chip->base;
+	struct nand_bbt_descr *bd = nand->bbt.bbp;
 
 	nand_unregister(nand);
 	spinand_manufacturer_cleanup(chip);
 	kfree(chip->buf);
+	kfree(nand->bbt.bbt);
+	if (bd->options & NAND_BBT_DYNAMICSTRUCT)
+		kfree(bd);
 
 	return 0;
 }
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v4 7/9] nand: spi: add Micron spi nand support
  2017-03-23  9:43 [PATCH v4 0/9] Introduction to SPI NAND framework Peter Pan
                   ` (5 preceding siblings ...)
  2017-03-23  9:43 ` [PATCH v4 6/9] nand: spi: Add bad block support Peter Pan
@ 2017-03-23  9:43 ` Peter Pan
  2017-03-30 12:31   ` Arnaud Mouiche
  2017-03-23  9:43 ` [PATCH v4 8/9] nand: spi: Add generic SPI controller support Peter Pan
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 36+ messages in thread
From: Peter Pan @ 2017-03-23  9:43 UTC (permalink / raw)
  To: boris.brezillon, richard, computersforpeace, arnaud.mouiche,
	thomas.petazzoni, marex, cyrille.pitchen, linux-mtd
  Cc: peterpandong, peterpansjtu, linshunquan1

This commit is to add support for Micron MT29F2G01ABAGD
spi nand chip.

Signed-off-by: Peter Pan <peterpandong@micron.com>
---
 drivers/mtd/nand/spi/Makefile       |   1 +
 drivers/mtd/nand/spi/manufactures.c |   3 +
 drivers/mtd/nand/spi/micron.c       | 226 ++++++++++++++++++++++++++++++++++++
 3 files changed, 230 insertions(+)
 create mode 100644 drivers/mtd/nand/spi/micron.c

diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile
index eabdb81..db0b91b 100644
--- a/drivers/mtd/nand/spi/Makefile
+++ b/drivers/mtd/nand/spi/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_MTD_SPI_NAND) += core.o
 obj-$(CONFIG_MTD_SPI_NAND) += manufactures.o
+obj-$(CONFIG_MTD_SPI_NAND) += micron.o
diff --git a/drivers/mtd/nand/spi/manufactures.c b/drivers/mtd/nand/spi/manufactures.c
index 7e0b42d..40dae11 100644
--- a/drivers/mtd/nand/spi/manufactures.c
+++ b/drivers/mtd/nand/spi/manufactures.c
@@ -16,9 +16,12 @@
 #include <linux/module.h>
 #include <linux/mtd/spinand.h>
 
+extern struct spinand_manufacturer micron_spinand_manufacture;
+
 struct spinand_manufacturer spinand_manufacturer_end = {0x0, "Unknown", NULL};
 
 struct spinand_manufacturer *spinand_manufacturers[] = {
+	&micron_spinand_manufacture,
 	&spinand_manufacturer_end,
 };
 EXPORT_SYMBOL(spinand_manufacturers);
diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c
new file mode 100644
index 0000000..0c360e0
--- /dev/null
+++ b/drivers/mtd/nand/spi/micron.c
@@ -0,0 +1,226 @@
+/*
+ *
+ * Copyright (c) 2009-2017 Micron Technology, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mtd/spinand.h>
+
+#define SPINAND_MFR_MICRON		0x2C
+
+#define SPI_NAND_MT29F_ECC_MASK		0x70
+#define SPI_NAND_MT29F_ECC_0_BIT	0x00
+#define SPI_NAND_MT29F_ECC_1_3_BIT	0x10
+#define SPI_NAND_MT29F_ECC_4_6_BIT	0x30
+#define SPI_NAND_MT29F_ECC_7_8_BIT	0x50
+#define SPI_NAND_MT29F_ECC_UNCORR	0x20
+
+struct micron_spinand_info {
+	char *name;
+	u8 dev_id;
+	u32 page_size;
+	u32 oob_size;
+	u32 pages_per_blk;
+	u32 blks_per_lun;
+	u32 luns_per_chip;
+	u32 ecc_strength;
+	u32 rw_mode;
+	const struct mtd_ooblayout_ops *ooblayout_ops;
+};
+
+#define MICRON_SPI_NAND_INFO(nm, did, pagesz, oobsz, pg_per_blk,	\
+			     blk_per_lun, lun_per_chip, ecc_stren,	\
+			     rwmode, ooblayoutops)		\
+	{	\
+		.name = (nm), .dev_id = (did),		\
+		.page_size = (pagesz), .oob_size = (oobsz),		\
+		.pages_per_blk = (pg_per_blk),		\
+		.blks_per_lun = (blk_per_lun),		\
+		.luns_per_chip = (lun_per_chip),	\
+		.ecc_strength = (ecc_stren),	\
+		.rw_mode = (rwmode),		\
+		.ooblayout_ops = (ooblayoutops)	\
+	}
+
+static int micron_ooblayout_ecc_128(struct mtd_info *mtd, int section,
+				    struct mtd_oob_region *oobregion)
+{
+	if (section)
+		return -ERANGE;
+
+	oobregion->length = 64;
+	oobregion->offset = 64;
+
+	return 0;
+}
+
+static int micron_ooblayout_free_128(struct mtd_info *mtd, int section,
+				     struct mtd_oob_region *oobregion)
+{
+	if (section)
+		return -ERANGE;
+
+	oobregion->length = 62;
+	oobregion->offset = 2;
+
+	return 0;
+}
+
+static const struct mtd_ooblayout_ops micron_ooblayout_128_ops = {
+	.ecc = micron_ooblayout_ecc_128,
+	.free = micron_ooblayout_free_128,
+};
+
+static const struct micron_spinand_info micron_spinand_table[] = {
+	MICRON_SPI_NAND_INFO("MT29F2G01ABAGD", 0x24, 2048, 128, 64, 2048, 1,
+			     8, SPINAND_OP_COMMON, &micron_ooblayout_128_ops),
+	{.name = NULL},
+};
+
+static int micron_spinand_get_dummy(struct spinand_device *chip,
+				    struct spinand_op *op)
+{
+	u8 opcode = op->cmd;
+
+	switch (opcode) {
+	case SPINAND_CMD_READ_FROM_CACHE:
+	case SPINAND_CMD_READ_FROM_CACHE_FAST:
+	case SPINAND_CMD_READ_FROM_CACHE_X2:
+	case SPINAND_CMD_READ_FROM_CACHE_DUAL_IO:
+	case SPINAND_CMD_READ_FROM_CACHE_X4:
+	case SPINAND_CMD_READ_ID:
+		return 1;
+	case SPINAND_CMD_READ_FROM_CACHE_QUAD_IO:
+		return 2;
+	default:
+		return 0;
+	}
+}
+
+/*
+ * mt29f_get_ecc_status - get ecc correction information from status register
+ * @chip: SPI NAND device structure
+ * @status: status register value
+ * @corrected: corrected bit flip number
+ * @ecc_error: ecc correction error or not
+ */
+static void mt29f_get_ecc_status(struct spinand_device *chip,
+				 unsigned int status, unsigned int *corrected,
+				 unsigned int *ecc_error)
+{
+	unsigned int ecc_status = status & SPI_NAND_MT29F_ECC_MASK;
+
+	*ecc_error = (ecc_status == SPI_NAND_MT29F_ECC_UNCORR);
+	switch (ecc_status) {
+	case SPI_NAND_MT29F_ECC_0_BIT:
+		*corrected = 0;
+		break;
+	case SPI_NAND_MT29F_ECC_1_3_BIT:
+		*corrected = 3;
+		break;
+	case SPI_NAND_MT29F_ECC_4_6_BIT:
+		*corrected = 6;
+		break;
+	case SPI_NAND_MT29F_ECC_7_8_BIT:
+		*corrected = 8;
+		break;
+	}
+}
+
+static struct spinand_ecc_engine_ops generic_spi_ecc_engine_ops = {
+	.get_ecc_status = mt29f_get_ecc_status,
+};
+
+/*
+ * micron_spinand_scan_id_table - scan chip info in id table
+ * @chip: SPI-NAND device structure
+ * @id: point to manufacture id and device id
+ * Description:
+ *   If found in id table, config chip with table information.
+ */
+static bool micron_spinand_scan_id_table(struct spinand_device *chip, u8 dev_id)
+{
+	struct mtd_info *mtd = spinand_to_mtd(chip);
+	struct nand_device *nand = mtd_to_nand(mtd);
+	struct micron_spinand_info *type = NULL;
+	struct nand_memory_organization *memorg = &nand->memorg;
+	struct spinand_ecc_engine *ecc_engine = chip->ecc.engine;
+
+	for (type = (struct micron_spinand_info *)micron_spinand_table;
+	     type->name; type++) {
+		if (dev_id != type->dev_id)
+			continue;
+		chip->name = type->name;
+		memorg->eraseblocksize = type->page_size
+				* type->pages_per_blk;
+		memorg->pagesize = type->page_size;
+		memorg->oobsize = type->oob_size;
+		memorg->diesize = memorg->eraseblocksize * type->blks_per_lun;
+		memorg->ndies = type->luns_per_chip;
+		if (ecc_engine->mode == SPINAND_ECC_ONDIE) {
+			ecc_engine->ops = &generic_spi_ecc_engine_ops;
+			ecc_engine->strength = type->ecc_strength;
+			mtd_set_ooblayout(mtd, type->ooblayout_ops);
+		}
+		chip->rw_mode = type->rw_mode;
+
+		return true;
+	}
+
+	return false;
+}
+
+/*
+ * micron_spinand_detect - initialize device related part in spinand_device
+ * struct if it is Micron device.
+ * @chip: SPI NAND device structure
+ */
+static bool micron_spinand_detect(struct spinand_device *chip)
+{
+	u8 *id = chip->id.data;
+
+	/*
+	 * Micron SPI NAND read ID need a dummy byte,
+	 * so the first byte in raw_id is dummy.
+	 */
+	if (id[1] != SPINAND_MFR_MICRON)
+		return false;
+
+	return micron_spinand_scan_id_table(chip, id[2]);
+}
+
+/*
+ * micron_spinand_prepare_op - Fix address for cache operation.
+ * @chip: SPI NAND device structure
+ * @op: pointer to spinand_op struct
+ * @page: page address
+ * @column: column address
+ */
+static void micron_spinand_prepare_op(struct spinand_device *chip,
+				      struct spinand_op *op, u32 page,
+				      u32 column)
+{
+	op->addr[0] |= (u8)((nand_page_to_eraseblock(&chip->base, page)
+			     & 0x1) << 4);
+	op->n_addr += micron_spinand_get_dummy(chip, op);
+}
+
+static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = {
+	.detect = micron_spinand_detect,
+	.prepare_op = micron_spinand_prepare_op,
+};
+
+const struct spinand_manufacturer micron_spinand_manufacture = {
+	.id = SPINAND_MFR_MICRON,
+	.name = "Micron",
+	.ops = &micron_spinand_manuf_ops,
+};
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v4 8/9] nand: spi: Add generic SPI controller support
  2017-03-23  9:43 [PATCH v4 0/9] Introduction to SPI NAND framework Peter Pan
                   ` (6 preceding siblings ...)
  2017-03-23  9:43 ` [PATCH v4 7/9] nand: spi: add Micron spi nand support Peter Pan
@ 2017-03-23  9:43 ` Peter Pan
  2017-03-23 11:33   ` Marek Vasut
  2017-03-29 21:37   ` Cyrille Pitchen
  2017-03-23  9:43 ` [PATCH v4 9/9] MAINTAINERS: Add SPI NAND entry Peter Pan
  2017-03-30 12:17 ` [PATCH v4 0/9] Introduction to SPI NAND framework Arnaud Mouiche
  9 siblings, 2 replies; 36+ messages in thread
From: Peter Pan @ 2017-03-23  9:43 UTC (permalink / raw)
  To: boris.brezillon, richard, computersforpeace, arnaud.mouiche,
	thomas.petazzoni, marex, cyrille.pitchen, linux-mtd
  Cc: peterpandong, peterpansjtu, linshunquan1

This commit supports to use generic spi controller
as spi nand controller.

Signed-off-by: Peter Pan <peterpandong@micron.com>
---
 drivers/mtd/nand/spi/Kconfig                   |   2 +
 drivers/mtd/nand/spi/Makefile                  |   1 +
 drivers/mtd/nand/spi/controllers/Kconfig       |   5 +
 drivers/mtd/nand/spi/controllers/Makefile      |   1 +
 drivers/mtd/nand/spi/controllers/generic-spi.c | 150 +++++++++++++++++++++++++
 5 files changed, 159 insertions(+)
 create mode 100644 drivers/mtd/nand/spi/controllers/Kconfig
 create mode 100644 drivers/mtd/nand/spi/controllers/Makefile
 create mode 100644 drivers/mtd/nand/spi/controllers/generic-spi.c

diff --git a/drivers/mtd/nand/spi/Kconfig b/drivers/mtd/nand/spi/Kconfig
index d77c46e..6bd1c65 100644
--- a/drivers/mtd/nand/spi/Kconfig
+++ b/drivers/mtd/nand/spi/Kconfig
@@ -3,3 +3,5 @@ menuconfig MTD_SPI_NAND
 	depends on MTD_NAND
 	help
 	  This is the framework for the SPI NAND device drivers.
+
+source "drivers/mtd/nand/spi/controllers/Kconfig"
diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile
index db0b91b..6ad5f24 100644
--- a/drivers/mtd/nand/spi/Makefile
+++ b/drivers/mtd/nand/spi/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_MTD_SPI_NAND) += core.o
 obj-$(CONFIG_MTD_SPI_NAND) += manufactures.o
 obj-$(CONFIG_MTD_SPI_NAND) += micron.o
+obj-$(CONFIG_MTD_SPI_NAND) += controllers/
diff --git a/drivers/mtd/nand/spi/controllers/Kconfig b/drivers/mtd/nand/spi/controllers/Kconfig
new file mode 100644
index 0000000..8ab7023
--- /dev/null
+++ b/drivers/mtd/nand/spi/controllers/Kconfig
@@ -0,0 +1,5 @@
+config GENERIC_SPI_NAND
+	tristate "SPI NAND with generic SPI bus Support"
+	depends on MTD_SPI_NAND && SPI
+	help
+	  This is to support SPI NAND device with generic SPI bus.
diff --git a/drivers/mtd/nand/spi/controllers/Makefile b/drivers/mtd/nand/spi/controllers/Makefile
new file mode 100644
index 0000000..46cbf29
--- /dev/null
+++ b/drivers/mtd/nand/spi/controllers/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_GENERIC_SPI_NAND) += generic-spi.o
diff --git a/drivers/mtd/nand/spi/controllers/generic-spi.c b/drivers/mtd/nand/spi/controllers/generic-spi.c
new file mode 100644
index 0000000..da1f224
--- /dev/null
+++ b/drivers/mtd/nand/spi/controllers/generic-spi.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2009-2017 Micron Technology, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/mtd/spinand.h>
+
+struct gen_spi_spinand_controller {
+	struct spinand_controller ctrl;
+	struct spi_device *spi;
+};
+
+#define to_gen_spi_spinand_controller(c) \
+	container_of(c, struct gen_spi_spinand_controller, ctrl)
+
+/*
+ * gen_spi_spinand_exec_op - to process a command to send to the
+ * SPI NAND by generic SPI bus
+ * @chip: SPI NAND device structure
+ * @op: SPI NAND operation descriptor
+ */
+static int gen_spi_spinand_exec_op(struct spinand_device *chip,
+				   struct spinand_op *op)
+{
+	struct spi_message message;
+	struct spi_transfer x[3];
+	struct spinand_controller *scontroller = chip->controller.controller;
+	struct gen_spi_spinand_controller *controller;
+
+	controller = to_gen_spi_spinand_controller(scontroller);
+	spi_message_init(&message);
+	memset(x, 0, sizeof(x));
+	x[0].len = 1;
+	x[0].tx_nbits = 1;
+	x[0].tx_buf = &op->cmd;
+	spi_message_add_tail(&x[0], &message);
+
+	if (op->n_addr + op->dummy_bytes) {
+		x[1].len = op->n_addr + op->dummy_bytes;
+		x[1].tx_nbits = op->addr_nbits;
+		x[1].tx_buf = op->addr;
+		spi_message_add_tail(&x[1], &message);
+	}
+	if (op->n_tx) {
+		x[2].len = op->n_tx;
+		x[2].tx_nbits = op->data_nbits;
+		x[2].tx_buf = op->tx_buf;
+		spi_message_add_tail(&x[2], &message);
+	} else if (op->n_rx) {
+		x[2].len = op->n_rx;
+		x[2].rx_nbits = op->data_nbits;
+		x[2].rx_buf = op->rx_buf;
+		spi_message_add_tail(&x[2], &message);
+	}
+	return spi_sync(controller->spi, &message);
+}
+
+static struct spinand_controller_ops gen_spi_spinand_ops = {
+	.exec_op = gen_spi_spinand_exec_op,
+};
+
+static int gen_spi_spinand_probe(struct spi_device *spi)
+{
+	struct spinand_device *chip;
+	struct gen_spi_spinand_controller *controller;
+	struct spinand_controller *spinand_controller;
+	int ret;
+	u32 max_speed_hz = spi->max_speed_hz;
+
+	chip = spinand_alloc(&spi->dev);
+	if (IS_ERR(chip)) {
+		ret = PTR_ERR(chip);
+		goto err1;
+	}
+	controller = kzalloc(sizeof(*controller), GFP_KERNEL);
+	if (!controller) {
+		ret = -ENOMEM;
+		goto err2;
+	}
+	controller->spi = spi;
+	spinand_controller = &controller->ctrl;
+	spinand_controller->ops = &gen_spi_spinand_ops;
+	spinand_controller->caps = SPINAND_CAP_RD_X1 | SPINAND_CAP_WR_X1;
+	if (spi->mode & SPI_RX_QUAD)
+		spinand_controller->caps |= SPINAND_CAP_RD_QUAD |
+					    SPINAND_CAP_RD_X4;
+	if (spi->mode & SPI_RX_DUAL)
+		spinand_controller->caps |= SPINAND_CAP_RD_DUAL |
+					    SPINAND_CAP_RD_X2;
+	if (spi->mode & SPI_TX_QUAD)
+		spinand_controller->caps |= SPINAND_CAP_WR_QUAD |
+					    SPINAND_CAP_WR_X4;
+	if (spi->mode & SPI_TX_DUAL)
+		spinand_controller->caps |= SPINAND_CAP_WR_DUAL |
+					    SPINAND_CAP_WR_X2;
+	chip->controller.controller = spinand_controller;
+	spi_set_drvdata(spi, chip);
+	spi->max_speed_hz = min_t(int, 25000000, max_speed_hz);
+
+	ret = spinand_register(chip);
+	if (ret)
+		goto err3;
+
+	spi->max_speed_hz = max_speed_hz;
+
+	return 0;
+
+err3:
+	kfree(controller);
+err2:
+	spinand_free(chip);
+err1:
+	return ret;
+}
+
+static int gen_spi_spinand_remove(struct spi_device *spi)
+{
+	struct spinand_device *chip = spi_get_drvdata(spi);
+
+	spinand_unregister(chip);
+	kfree(to_gen_spi_spinand_controller(chip->controller.controller));
+	spinand_free(chip);
+
+	return 0;
+}
+
+static struct spi_driver gen_spi_spinand_driver = {
+	.driver = {
+		.name	= "generic_spinand",
+		.owner	= THIS_MODULE,
+	},
+	.probe	= gen_spi_spinand_probe,
+	.remove	= gen_spi_spinand_remove,
+};
+module_spi_driver(gen_spi_spinand_driver);
+
+MODULE_DESCRIPTION("Generic SPI controller to support SPI NAND");
+MODULE_AUTHOR("Peter Pan<peterpandong@micron.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* [PATCH v4 9/9] MAINTAINERS: Add SPI NAND entry
  2017-03-23  9:43 [PATCH v4 0/9] Introduction to SPI NAND framework Peter Pan
                   ` (7 preceding siblings ...)
  2017-03-23  9:43 ` [PATCH v4 8/9] nand: spi: Add generic SPI controller support Peter Pan
@ 2017-03-23  9:43 ` Peter Pan
  2017-03-30 12:17 ` [PATCH v4 0/9] Introduction to SPI NAND framework Arnaud Mouiche
  9 siblings, 0 replies; 36+ messages in thread
From: Peter Pan @ 2017-03-23  9:43 UTC (permalink / raw)
  To: boris.brezillon, richard, computersforpeace, arnaud.mouiche,
	thomas.petazzoni, marex, cyrille.pitchen, linux-mtd
  Cc: peterpandong, peterpansjtu, linshunquan1

Add maintainer information for SPI NAND.

Signed-off-by: Peter Pan <peterpandong@micron.com>
---
 MAINTAINERS | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 09ec991..dc38bda 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8610,6 +8610,15 @@ S:	Maintained
 F:	drivers/mtd/nand/
 F:	include/linux/mtd/*nand*.h
 
+SPI NAND FLASH DRIVER
+M:	Peter Pan <peterpandong@micron.com>
+L:	linux-mtd@lists.infradead.org
+W:	http://www.linux-mtd.infradead.org/
+Q:	http://patchwork.ozlabs.org/project/linux-mtd/list/
+S:	Maintained
+F:	drivers/mtd/nand/spi/
+F:	include/linux/mtd/spinand.h
+
 NATSEMI ETHERNET DRIVER (DP8381x)
 S:	Orphan
 F:	drivers/net/ethernet/natsemi/natsemi.c
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 1/9] mtd: nand: add oob iterator in nand_for_each_page
  2017-03-23  9:43 ` [PATCH v4 1/9] mtd: nand: add oob iterator in nand_for_each_page Peter Pan
@ 2017-03-23 11:13   ` Marek Vasut
  2017-03-28  1:35     ` Peter Pan
  2017-03-29 19:34   ` Boris Brezillon
  1 sibling, 1 reply; 36+ messages in thread
From: Marek Vasut @ 2017-03-23 11:13 UTC (permalink / raw)
  To: Peter Pan, boris.brezillon, richard, computersforpeace,
	arnaud.mouiche, thomas.petazzoni, cyrille.pitchen, linux-mtd
  Cc: peterpansjtu, linshunquan1

On 03/23/2017 10:43 AM, Peter Pan wrote:
> Iterate nand pages by both page and oob operation.
> 
> Signed-off-by: Peter Pan <peterpandong@micron.com>
> ---
>  include/linux/mtd/nand.h | 38 +++++++++++++++++++++++++++++++-------
>  1 file changed, 31 insertions(+), 7 deletions(-)
> 
> diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
> index c2197b4..54ded4c 100644
> --- a/include/linux/mtd/nand.h
> +++ b/include/linux/mtd/nand.h
> @@ -84,9 +84,12 @@ struct nand_device {
>   * @pageoffset: the offset within a page
>   */
>  struct nand_page_iter {
> -	loff_t offs;
>  	int page;
>  	int pageoffs;
> +	size_t dataleft;
> +	int ooboffs;
> +	int oobsize;
> +	size_t oobleft;
>  };
>  
>  /**
> @@ -193,14 +196,19 @@ static inline int nand_per_page_oobsize(struct nand_device *nand)
>   * @offs: absolute offset
>   * @iter: page iterator
>   */
> -static inline void nand_page_iter_init(struct nand_device *nand, loff_t offs,
> +static inline void nand_page_iter_init(struct nand_device *nand,
> +				       loff_t offs, size_t len, u32 ooboffs,
> +				       size_t ooblen, u32 oobsize,
>  				       struct nand_page_iter *iter)
>  {
>  	u64 page = offs;
>  
>  	iter->pageoffs = do_div(page, nand->memorg.pagesize);
>  	iter->page = page;
> -	iter->offs = offs;
> +	iter->dataleft = len;
> +	iter->ooboffs = ooboffs;
> +	iter->oobsize = oobsize;
> +	iter->oobleft = ooblen;
>  }
>  
>  /**
> @@ -212,13 +220,29 @@ static inline void nand_page_iter_next(struct nand_device *nand,
>  				       struct nand_page_iter *iter)
>  {
>  	iter->page++;
> -	iter->offs += nand_page_size(nand) - iter->pageoffs;
>  	iter->pageoffs = 0;
> +	if (iter->dataleft)
> +		iter->dataleft -= min_t (int,
                                         ^^^
                                         shouldn't this be size_t ?

> +					 nand_page_size(nand) - iter->pageoffs,
> +					 iter->dataleft);
> +	if (iter->oobleft)
> +		iter->oobleft -= min_t(int, iter->oobsize - iter->ooboffs,

DTTO here ?

> +				       iter->oobleft);
> +}
> +
> +static inline bool nand_page_iter_end(struct nand_device *nand,
> +				      struct nand_page_iter *iter)
> +{
> +	if (iter->dataleft || iter->oobleft)
> +		return false;
> +	return true;
>  }
>  
> -#define nand_for_each_page(nand, start, len, iter)		\
> -	for (nand_page_iter_init(nand, start, iter);		\
> -	     (iter)->offs < (start) + (len);			\
> +#define nand_for_each_page(nand, start, len, ooboffs, ooblen,	\
> +			   oobsize, iter)	\

What's the difference between ooblen and oobsize ? That is totally
confusing , so add a comment explaining that ...

> +	for (nand_page_iter_init(nand, start, len, ooboffs,	\
> +				 ooblen, oobsize, iter);	\
> +	     !nand_page_iter_end(nand, iter);		\
>  	     nand_page_iter_next(nand, iter))
>  
>  /**
> 


-- 
Best regards,
Marek Vasut

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 3/9] mtd: nand: add more helpers in nand.h
  2017-03-23  9:43 ` [PATCH v4 3/9] mtd: nand: add more helpers in nand.h Peter Pan
@ 2017-03-23 11:19   ` Marek Vasut
  2017-03-29 19:57   ` Boris Brezillon
  1 sibling, 0 replies; 36+ messages in thread
From: Marek Vasut @ 2017-03-23 11:19 UTC (permalink / raw)
  To: Peter Pan, boris.brezillon, richard, computersforpeace,
	arnaud.mouiche, thomas.petazzoni, cyrille.pitchen, linux-mtd
  Cc: peterpansjtu, linshunquan1

On 03/23/2017 10:43 AM, Peter Pan wrote:
> This commit adds some helpers in nand.h
       ^^^^^^
       patch

>     nand_size()
>     nand_check_address()
>     nand_check_oob_ops()
>     nand_oob_ops_across_page()
>     nand_check_erase_ops()
> 
> Signed-off-by: Peter Pan <peterpandong@micron.com>
> ---
>  include/linux/mtd/nand.h | 62 ++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 62 insertions(+)
> 
> diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
> index 54ded4c..0c52401 100644
> --- a/include/linux/mtd/nand.h
> +++ b/include/linux/mtd/nand.h
> @@ -434,6 +434,68 @@ static inline int nand_neraseblocks(struct nand_device *nand)
>  }
>  
>  /**
> + * nand_size - Get NAND size
> + * @nand: NAND device
> + *
> + * Returns the total size exposed by @nand.
> + */
> +static inline u64 nand_size(struct nand_device *nand)
> +{
> +	return nand->memorg.ndies * nand->memorg.diesize;
> +}
> +

kerneldoc missing ... fix globally

> +static inline int nand_check_address(struct nand_device *nand, loff_t addr)
> +{
> +	return addr < nand_size(nand) ? 0 : -EINVAL;
> +}
> +
> +static inline int nand_check_oob_ops(struct nand_device *nand, loff_t start,
> +				     struct mtd_oob_ops *ops)
> +{
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +	int ooblen = ops->mode == MTD_OPS_AUTO_OOB ?
> +		mtd->oobavail : mtd->oobsize;
> +
> +	if ((!!ops->datbuf != !!ops->len) ||
> +	    (!!ops->oobbuf != !!ops->ooblen))
> +		return -EINVAL;
> +	if (ops->ooboffs >= ooblen)
> +		return -EINVAL;
> +	if (ops->ooboffs + ops->ooblen >
> +	    (nand_len_to_pages(nand, nand_size(nand)) -
> +			       nand_offs_to_page(nand, start)) * ooblen)

The indent here is wrong and misguiding . I think it might be better to
introduce some variables to avoid 3-line long conditions too.

> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static inline bool nand_oob_ops_across_page(struct nand_device *nand,
> +					    struct mtd_oob_ops *ops)
> +{
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +	int ooblen = ops->mode == MTD_OPS_AUTO_OOB ?
> +		     mtd->oobavail : mtd->oobsize;

The ooblen can probably be deduplicated by introducing some function.
Dunno if it's worth it though.

> +	return (ops->ooboffs + ops->ooblen) > ooblen;
> +}
> +
> +static inline int nand_check_erase_ops(struct nand_device *nand,
> +				       struct erase_info *einfo)
> +{
> +	/* check address align on block boundary */
> +	if (einfo->addr & (nand_eraseblock_size(nand) - 1))
> +		return -EINVAL;
> +	/* check lendth align on block boundary */
> +	if (einfo->len & (nand_eraseblock_size(nand) - 1))
> +		return -EINVAL;
> +	/* Do not allow erase past end of device */
> +	if ((einfo->addr + einfo->len) > nand_size(nand))
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +/**
>   * nand_register - Register a NAND device
>   * @nand: NAND device
>   *
> 


-- 
Best regards,
Marek Vasut

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 4/9] nand: spi: add basic blocks for infrastructure
  2017-03-23  9:43 ` [PATCH v4 4/9] nand: spi: add basic blocks for infrastructure Peter Pan
@ 2017-03-23 11:29   ` Marek Vasut
  2017-03-23 15:40     ` Boris Brezillon
  2017-03-29 22:28   ` Cyrille Pitchen
  2017-03-30 12:38   ` Arnaud Mouiche
  2 siblings, 1 reply; 36+ messages in thread
From: Marek Vasut @ 2017-03-23 11:29 UTC (permalink / raw)
  To: Peter Pan, boris.brezillon, richard, computersforpeace,
	arnaud.mouiche, thomas.petazzoni, cyrille.pitchen, linux-mtd
  Cc: peterpansjtu, linshunquan1

On 03/23/2017 10:43 AM, Peter Pan wrote:
> This is the first commit for spi nand framkework.
> This commit is to add add basic building blocks
> for the SPI NAND infrastructure.
> 
> Signed-off-by: Peter Pan <peterpandong@micron.com>
> ---

[...]

> +#define pr_fmt(fmt) "SPI NAND: " fmt

This looks awful, please drop.

> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/jiffies.h>
> +#include <linux/mtd/spinand.h>
> +#include <linux/slab.h>
> +
> +/*
> + * spinand_exec_op - execute SPI NAND operation by controller ->exec_op() hook
> + * @chip: SPI NAND device structure
> + * @op: pointer to spinand_op struct
> + */
> +static inline int spinand_exec_op(struct spinand_device *chip,
> +				  struct spinand_op *op)
> +{
> +	return chip->controller.controller->ops->exec_op(chip, op);
> +}
> +
> +/*
> + * spinand_op_init - initialize spinand_op struct
> + * @op: pointer to spinand_op struct
> + */
> +static inline void spinand_op_init(struct spinand_op *op)

Can we be consistent with op_foo or foo_op ? Pick one , but not both ...
I mean, exec_op vs op_init is inconsistent. Probably spinand_op_exec and
spinand_op_init would be better .

> +{
> +	memset(op, 0, sizeof(struct spinand_op));
> +	op->addr_nbits = 1;
> +	op->data_nbits = 1;
> +}
> +
> +/*
> + * spinand_read_reg - send command 0Fh to read register

I don't think we care which commmand is sent, just fix the comment to
say "read SPI NAND register" or something .

> + * @chip: SPI NAND device structure
> + * @reg; register to read
> + * @buf: buffer to store value
> + */
> +static int spinand_read_reg(struct spinand_device *chip, u8 reg, u8 *buf)
> +{
> +	struct spinand_op op;
> +	int ret;
> +
> +	spinand_op_init(&op);
> +	op.cmd = SPINAND_CMD_GET_FEATURE;
> +	op.n_addr = 1;
> +	op.addr[0] = reg;
> +	op.n_rx = 1;
> +	op.rx_buf = buf;
> +
> +	ret = spinand_exec_op(chip, &op);
> +	if (ret < 0)
> +		pr_err("err: %d read register %d\n", ret, reg);
> +
> +	return ret;
> +}
> +
> +/*
> + * spinand_write_reg - send command 1Fh to write register

DTTO

> + * @chip: SPI NAND device structure
> + * @reg; register to write
> + * @buf: buffer stored value
> + */
> +static int spinand_write_reg(struct spinand_device *chip, u8 reg, u8 *buf)

Buffer length is always 1 , just use u8 buf .

> +{
> +	struct spinand_op op;
> +	int ret;
> +
> +	spinand_op_init(&op);
> +	op.cmd = SPINAND_CMD_SET_FEATURE;
> +	op.n_addr = 1;
> +	op.addr[0] = reg;
> +	op.n_tx = 1,
> +	op.tx_buf = buf,
> +
> +	ret = spinand_exec_op(chip, &op);
> +	if (ret < 0)
> +		pr_err("err: %d write register %d\n", ret, reg);
> +
> +	return ret;
> +}
> +
> +/*
> + * spinand_read_status - get status register value
> + * @chip: SPI NAND device structure
> + * @status: buffer to store value
> + * Description:
> + *   After read, write, or erase, the NAND device is expected to set the
> + *   busy status.
> + *   This function is to allow reading the status of the command: read,
> + *   write, and erase.
> + */
> +static int spinand_read_status(struct spinand_device *chip, uint8_t *status)
> +{
> +	return spinand_read_reg(chip, REG_STATUS, status);
> +}
> +
> +/*
> + * spinand_wait - wait until the command is done
> + * @chip: SPI NAND device structure
> + * @s: buffer to store status register value (can be NULL)
> + */
> +static int spinand_wait(struct spinand_device *chip, u8 *s)
> +{
> +	unsigned long timeo = msecs_to_jiffies(400);
> +	u8 status;
> +
> +	do {
> +		spinand_read_status(chip, &status);
> +		if ((status & STATUS_OIP_MASK) == STATUS_READY)
> +			goto out;
> +	} while (time_before(jiffies, timeo));
> +
> +	/*
> +	 * Extra read, just in case the STATUS_READY bit has changed
> +	 * since our last check
> +	 */

Is this likely to happen ? Probably not ... so in case you reach this
place here, timeout happened.

> +	spinand_read_status(chip, &status);
> +out:
> +	if (s)
> +		*s = status;
> +
> +	return (status & STATUS_OIP_MASK) == STATUS_READY ? 0 :	-ETIMEDOUT;
> +}
> +
> +/*
> + * spinand_read_id - send 9Fh command to get ID
> + * @chip: SPI NAND device structure
> + * @buf: buffer to store id
> + * Description:
> + *   Manufacturers' read ID method is not unique. Some need a dummy before
> + *   reading, some's ID has three byte.
> + *   This function send one byte opcode (9Fh) and then read
> + *   SPINAND_MAX_ID_LEN (4 currently) bytes. Manufacturer's detect function
> + *   need to filter out real ID from the 4 bytes.
> + */
> +static int spinand_read_id(struct spinand_device *chip, u8 *buf)
> +{
> +	struct spinand_op op;
> +
> +	spinand_op_init(&op);
> +	op.cmd = SPINAND_CMD_READ_ID;
> +	op.n_rx = SPINAND_MAX_ID_LEN;
> +	op.rx_buf = buf;
> +
> +	return spinand_exec_op(chip, &op);
> +}
> +
> +/*
> + * spinand_reset - reset device by FFh command.
> + * @chip: SPI NAND device structure
> + */
> +static int spinand_reset(struct spinand_device *chip)
> +{
> +	struct spinand_op op;
> +	int ret;
> +
> +	spinand_op_init(&op);
> +	op.cmd = SPINAND_CMD_RESET;
> +
> +	ret = spinand_exec_op(chip, &op);
> +	if (ret < 0) {
> +		pr_err("spinand reset failed!\n");

Can we use dev_err() ? Also , previous patch had some pr_fmt which added
"SPI NAND:" prefix, so replace "spinand" with "SPI NAND: " to be
consistent ...

> +		goto out;
> +	}
> +	ret = spinand_wait(chip, NULL);
> +
> +out:
> +	return ret;
> +}
> +
> +/*
> + * spinand_lock_block - write block lock register to lock/unlock device
> + * @chip: SPI NAND device structure
> + * @lock: value to set to block lock register
> + */
> +static int spinand_lock_block(struct spinand_device *chip, u8 lock)
> +{
> +	return spinand_write_reg(chip, REG_BLOCK_LOCK, &lock);
> +}
> +
> +/*
> + * spinand_set_rd_wr_op - choose the best read write command
> + * @chip: SPI NAND device structure
> + * Description:
> + *   Chose the fastest r/w command according to spi controller's and
> + *   device's ability.
> + */
> +static void spinand_set_rd_wr_op(struct spinand_device *chip)
> +{
> +	u32 controller_cap = chip->controller.controller->caps;
> +	u32 rw_mode = chip->rw_mode;

What if controller can do quad, but chip can only do dual , is this
handled ?

> +	if ((controller_cap & SPINAND_CAP_RD_QUAD) &&
> +	    (rw_mode & SPINAND_RD_QUAD))
> +		chip->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_QUAD_IO;
> +	else if ((controller_cap & SPINAND_CAP_RD_X4) &&
> +		 (rw_mode & SPINAND_RD_X4))
> +		chip->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_X4;
> +	else if ((controller_cap & SPINAND_CAP_RD_DUAL) &&
> +		 (rw_mode & SPINAND_RD_DUAL))
> +		chip->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_DUAL_IO;
> +	else if ((controller_cap & SPINAND_CAP_RD_X2) &&
> +		 (rw_mode & SPINAND_RD_X2))
> +		chip->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_X2;
> +	else
> +		chip->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_FAST;
> +
> +	if ((controller_cap & SPINAND_CAP_WR_X4) &&
> +	    (rw_mode & SPINAND_WR_X4))
> +		chip->write_cache_op = SPINAND_CMD_PROG_LOAD_X4;
> +	else
> +		chip->write_cache_op = SPINAND_CMD_PROG_LOAD;
> +}
> +
> +/*
> + * spinand_manufacturer_detect - detect SPI NAND device by each manufacturer
> + * @chip: SPI NAND device structure
> + *
> + * ->detect() should decode raw id in chip->id.data and initialize device
> + * related part in spinand_device structure if it is the right device.
> + * ->detect() can not be NULL.
> + */
> +static int spinand_manufacturer_detect(struct spinand_device *chip)
> +{
> +	int i = 0;
> +
> +	for (; spinand_manufacturers[i]->id != 0x0; i++) {
> +		if (!spinand_manufacturers[i]->ops ||
> +		    !spinand_manufacturers[i]->ops->detect) {
> +			pr_err("%s's ops or ops->detect() is be NULL!\n",
> +			       spinand_manufacturers[i]->name);

Can this case happen, EVER ? I'd mandate the ->detect to be always set.

> +			return -EINVAL;
> +		}
> +		if (spinand_manufacturers[i]->ops->detect(chip)) {
> +			chip->manufacturer.manu = spinand_manufacturers[i];
> +			return 0;
> +		}
> +	}
> +
> +	return -ENODEV;
> +}
> +
> +/*
> + * spinand_manufacturer_init - manufacturer initialization function.
> + * @chip: SPI NAND device structure
> + *
> + * Manufacturer drivers should put all their specific initialization code in
> + * their ->init() hook.
> + */
> +static int spinand_manufacturer_init(struct spinand_device *chip)
> +{
> +	if (chip->manufacturer.manu->ops->init)
> +		return chip->manufacturer.manu->ops->init(chip);
> +
> +	return 0;
> +}
> +
> +/*
> + * spinand_manufacturer_cleanup - manufacturer cleanup function.
> + * @chip: SPI NAND device structure
> + *
> + * Manufacturer drivers should put all their specific cleanup code in their
> + * ->cleanup() hook.
> + */
> +static void spinand_manufacturer_cleanup(struct spinand_device *chip)
> +{
> +	/* Release manufacturer private data */
> +	if (chip->manufacturer.manu->ops->cleanup)
> +		return chip->manufacturer.manu->ops->cleanup(chip);
> +}
> +
> +/*
> + * spinand_dt_init - Initialize SPI NAND by device tree node
> + * @chip: SPI NAND device structure
> + *
> + * TODO: put ecc_mode, ecc_strength, ecc_step, bbt, etc in here
> + * and move it in generic NAND core.
> + */
> +static void spinand_dt_init(struct spinand_device *chip)
> +{
> +}
> +
> +/*
> + * spinand_detect - detect the SPI NAND device
> + * @chip: SPI NAND device structure
> + */
> +static int spinand_detect(struct spinand_device *chip)
> +{
> +	struct nand_device *nand = &chip->base;
> +	int ret;
> +
> +	spinand_reset(chip);
> +	spinand_read_id(chip, chip->id.data);
> +	chip->id.len = SPINAND_MAX_ID_LEN;
> +
> +	ret = spinand_manufacturer_detect(chip);
> +	if (ret) {
> +		pr_err("SPI NAND: unknown raw ID %*phN\n",
> +		       SPINAND_MAX_ID_LEN, chip->id.data);
> +		goto out;
> +	}
> +
> +	pr_info("%s (%s) is found.\n",
> +		chip->name, chip->manufacturer.manu->name);
> +	pr_info("%d MiB, block size: %d KiB, page size: %d, OOB size: %d\n",
> +		(int)(nand_size(nand) >> 20)

Is the case needed?

> , nand_eraseblock_size(nand) >> 10,
> +		nand_page_size(nand), nand_per_page_oobsize(nand));
> +
> +out:
> +	return ret;
> +}
> +
> +/*
> + * spinand_init - initialize the SPI NAND device
> + * @chip: SPI NAND device structure
> + */
> +static int spinand_init(struct spinand_device *chip)
> +{
> +	struct mtd_info *mtd = spinand_to_mtd(chip);
> +	struct nand_device *nand = mtd_to_nand(mtd);
> +	struct spinand_ecc_engine *ecc_engine;
> +	int ret;
> +
> +	spinand_dt_init(chip);
> +	spinand_set_rd_wr_op(chip);
> +
> +	chip->buf = kzalloc(nand_page_size(nand) + nand_per_page_oobsize(nand),
> +			    GFP_KERNEL);
> +	if (!chip->buf) {
> +		ret = -ENOMEM;
> +		goto err;
> +	}
> +
> +	chip->oobbuf = chip->buf + nand_page_size(nand);
> +
> +	spinand_manufacturer_init(chip);
> +
> +	mtd->name = chip->name;
> +	mtd->size = nand_size(nand);
> +	mtd->erasesize = nand_eraseblock_size(nand);
> +	mtd->writesize = nand_page_size(nand);
> +	mtd->writebufsize = mtd->writesize;
> +	mtd->owner = THIS_MODULE;
> +	mtd->type = MTD_NANDFLASH;
> +	mtd->flags = MTD_CAP_NANDFLASH;
> +	if (!mtd->ecc_strength)
> +		mtd->ecc_strength = ecc_engine->strength ?
> +				    ecc_engine->strength : 1;
> +
> +	mtd->oobsize = nand_per_page_oobsize(nand);
> +	ret = mtd_ooblayout_count_freebytes(mtd);
> +	if (ret < 0)
> +		ret = 0;
> +	mtd->oobavail = ret;
> +
> +	if (!mtd->bitflip_threshold)
> +		mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3,
> +						      4);
> +	/* After power up, all blocks are locked, so unlock it here. */
> +	spinand_lock_block(chip, BL_ALL_UNLOCKED);
> +
> +	return nand_register(nand);
> +
> +err:
> +	return ret;
> +}
> +
> +/*
> + * spinand_alloc - [SPI NAND Interface] allocate SPI NAND device instance
> + * @dev: pointer to device model structure
> + */
> +struct spinand_device *spinand_alloc(struct device *dev)
> +{
> +	struct spinand_device *chip;
> +	struct spinand_ecc_engine *ecc_engine;
> +
> +	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
> +	if (!chip)
> +		goto err1;
> +
> +	ecc_engine = kzalloc(sizeof(*ecc_engine), GFP_KERNEL);
> +	if (!ecc_engine)
> +		goto err2;

Just allocate a structure with both chip and ecc_engine in them, then
you don't have to kzalloc/kfree twice .

> +	ecc_engine->mode = SPINAND_ECC_ONDIE;
> +	chip->ecc.engine = ecc_engine;
> +	spinand_set_of_node(chip, dev->of_node);
> +	mutex_init(&chip->lock);
> +
> +	return chip;
> +
> +err2:
> +	kfree(chip);
> +err1:
> +	return ERR_PTR(-ENOMEM);
> +}
> +EXPORT_SYMBOL_GPL(spinand_alloc);
> +
> +/*
> + * spinand_free - [SPI NAND Interface] free SPI NAND device instance
> + * @chip: SPI NAND device structure
> + */
> +void spinand_free(struct spinand_device *chip)
> +{
> +	kfree(chip->ecc.engine);
> +	kfree(chip);
> +}
> +EXPORT_SYMBOL_GPL(spinand_free);
> +
> +/*
> + * spinand_register - [SPI NAND Interface] register SPI NAND device
> + * @chip: SPI NAND device structure
> + */
> +int spinand_register(struct spinand_device *chip)
> +{
> +	int ret;
> +
> +	ret = spinand_detect(chip);
> +	if (ret) {
> +		pr_err("Detect SPI NAND failed with error %d.\n", ret);
> +		return ret;
> +	}
> +
> +	ret = spinand_init(chip);
> +	if (ret)
> +		pr_err("Init SPI NAND failed with error %d.\n", ret);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(spinand_register);
> +
> +/*
> + * spinand_unregister - [SPI NAND Interface] unregister SPI NAND device
> + * @chip: SPI NAND device structure
> + */
> +int spinand_unregister(struct spinand_device *chip)
> +{
> +	struct nand_device *nand = &chip->base;
> +
> +	nand_unregister(nand);
> +	spinand_manufacturer_cleanup(chip);
> +	kfree(chip->buf);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(spinand_unregister);
> +
> +MODULE_DESCRIPTION("SPI NAND framework");
> +MODULE_AUTHOR("Peter Pan<peterpandong@micron.com>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/mtd/nand/spi/manufactures.c b/drivers/mtd/nand/spi/manufactures.c
> new file mode 100644
> index 0000000..7e0b42d
> --- /dev/null
> +++ b/drivers/mtd/nand/spi/manufactures.c
> @@ -0,0 +1,24 @@
> +/**
> + *
> + * Copyright (c) 2009-2017 Micron Technology, Inc.

2009-2017 ? Wow :)

> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/mtd/spinand.h>
> +
> +struct spinand_manufacturer spinand_manufacturer_end = {0x0, "Unknown", NULL};
> +
> +struct spinand_manufacturer *spinand_manufacturers[] = {
> +	&spinand_manufacturer_end,

Just populate the array directly .

> +};
> +EXPORT_SYMBOL(spinand_manufacturers);
> diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
> new file mode 100644
> index 0000000..44748b4
> --- /dev/null
> +++ b/include/linux/mtd/spinand.h
> @@ -0,0 +1,270 @@
> +/**
> + *
> + * Copyright (c) 2009-2017 Micron Technology, Inc.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +#ifndef __LINUX_MTD_SPINAND_H
> +#define __LINUX_MTD_SPINAND_H
> +
> +#include <linux/mutex.h>
> +#include <linux/mtd/mtd.h>
> +#include <linux/mtd/nand.h>
> +
> +/*
> + * Standard SPI-NAND flash commands
> + */
> +#define SPINAND_CMD_RESET			0xff
> +#define SPINAND_CMD_GET_FEATURE			0x0f
> +#define SPINAND_CMD_SET_FEATURE			0x1f
> +#define SPINAND_CMD_PAGE_READ			0x13
> +#define SPINAND_CMD_READ_PAGE_CACHE_RDM		0x30
> +#define SPINAND_CMD_READ_PAGE_CACHE_LAST	0x3f
> +#define SPINAND_CMD_READ_FROM_CACHE		0x03
> +#define SPINAND_CMD_READ_FROM_CACHE_FAST	0x0b
> +#define SPINAND_CMD_READ_FROM_CACHE_X2		0x3b
> +#define SPINAND_CMD_READ_FROM_CACHE_DUAL_IO	0xbb
> +#define SPINAND_CMD_READ_FROM_CACHE_X4		0x6b
> +#define SPINAND_CMD_READ_FROM_CACHE_QUAD_IO	0xeb
> +#define SPINAND_CMD_BLK_ERASE			0xd8
> +#define SPINAND_CMD_PROG_EXC			0x10
> +#define SPINAND_CMD_PROG_LOAD			0x02
> +#define SPINAND_CMD_PROG_LOAD_RDM_DATA		0x84
> +#define SPINAND_CMD_PROG_LOAD_X4		0x32
> +#define SPINAND_CMD_PROG_LOAD_RDM_DATA_X4	0x34
> +#define SPINAND_CMD_READ_ID			0x9f
> +#define SPINAND_CMD_WR_DISABLE			0x04
> +#define SPINAND_CMD_WR_ENABLE			0x06
> +
> +/* feature registers */
> +#define REG_BLOCK_LOCK		0xa0
> +#define REG_CFG			0xb0
> +#define REG_STATUS		0xc0
> +#define REG_DIE_SELECT		0xd0
> +
> +/* status */
> +#define STATUS_OIP_MASK		0x01
> +#define STATUS_CRBSY_MASK	0x80
> +#define STATUS_READY		(0 << 0)
> +#define STATUS_BUSY		(1 << 0)
> +
> +#define STATUS_E_FAIL_MASK	0x04
> +#define STATUS_E_FAIL		(1 << 2)

BIT() , fix globally

> +
> +#define STATUS_P_FAIL_MASK	0x08
> +#define STATUS_P_FAIL		(1 << 3)
> +
> +/*Configuration register defines*/
> +#define CFG_QE_MASK		0x01
> +#define CFG_QE_ENABLE		0x01
> +#define CFG_ECC_MASK		0x10
> +#define CFG_ECC_ENABLE		0x10
> +#define CFG_LOT_MASK		0x20
> +#define CFG_LOT_ENABLE		0x20
> +#define CFG_OTP_MASK		0xc2
> +#define CFG_OTP_ENTER		0x40
> +#define CFG_OTP_EXIT		0x00
> +
> +/* block lock */
> +#define BL_ALL_LOCKED		0x7c
> +#define BL_U_1_1024_LOCKED	0x08
> +#define BL_U_1_512_LOCKED	0x10
> +#define BL_U_1_256_LOCKED	0x18
> +#define BL_U_1_128_LOCKED	0x20
> +#define BL_U_1_64_LOCKED	0x28
> +#define BL_U_1_32_LOCKED	0x30
> +#define BL_U_1_16_LOCKED	0x38
> +#define BL_U_1_8_LOCKED		0x40
> +#define BL_U_1_4_LOCKED		0x48
> +#define BL_U_1_2_LOCKED		0x50
> +#define BL_L_1_1024_LOCKED	0x0c
> +#define BL_L_1_512_LOCKED	0x14
> +#define BL_L_1_256_LOCKED	0x1c
> +#define BL_L_1_128_LOCKED	0x24
> +#define BL_L_1_64_LOCKED	0x2c
> +#define BL_L_1_32_LOCKED	0x34
> +#define BL_L_1_16_LOCKED	0x3c
> +#define BL_L_1_8_LOCKED		0x44
> +#define BL_L_1_4_LOCKED		0x4c
> +#define BL_L_1_2_LOCKED		0x54
> +#define BL_ALL_UNLOCKED		0X00
> +
> +/* die select */
> +#define DIE_SELECT_MASK		0x40
> +#define DIE_SELECT_DS0		0x00
> +#define DIE_SELECT_DS1		0x40
> +
> +struct spinand_op;
> +struct spinand_device;
> +
> +#define SPINAND_MAX_ID_LEN	4
> +
> +/**
> + * struct nand_id - NAND id structure
> + * @data: buffer containing the id bytes. Currently 8 bytes large, but can
> + *	  be extended if required.
> + * @len: ID length.
> + */
> +struct spinand_id {
> +	u8 data[SPINAND_MAX_ID_LEN];
> +	int len;
> +};
> +
> +struct spinand_controller_ops {
> +	int (*exec_op)(struct spinand_device *chip,
> +		       struct spinand_op *op);
> +};
> +
> +struct spinand_manufacturer_ops {
> +	bool (*detect)(struct spinand_device *chip);
> +	int (*init)(struct spinand_device *chip);
> +	void (*cleanup)(struct spinand_device *chip);
> +};
> +
> +struct spinand_manufacturer {
> +	u8 id;
> +	char *name;
> +	const struct spinand_manufacturer_ops *ops;
> +};
> +
> +extern struct spinand_manufacturer *spinand_manufacturers[];
> +
> +struct spinand_ecc_engine_ops {
> +	void (*get_ecc_status)(struct spinand_device *chip,
> +			       unsigned int status, unsigned int *corrected,
> +			       unsigned int *ecc_errors);
> +	void (*disable_ecc)(struct spinand_device *chip);
> +	void (*enable_ecc)(struct spinand_device *chip);
> +};
> +
> +enum spinand_ecc_mode {
> +	SPINAND_ECC_ONDIE,
> +	SPINAND_ECC_HW,
> +};
> +
> +struct spinand_ecc_engine {
> +	enum spinand_ecc_mode mode;
> +	u32 strength;
> +	u32 steps;
> +	struct spinand_ecc_engine_ops *ops;
> +};
> +
> +#define SPINAND_CAP_RD_X1 BIT(0)
> +#define SPINAND_CAP_RD_X2 BIT(1)
> +#define SPINAND_CAP_RD_X4 BIT(2)
> +#define SPINAND_CAP_RD_DUAL BIT(3)
> +#define SPINAND_CAP_RD_QUAD BIT(4)
> +#define SPINAND_CAP_WR_X1 BIT(5)
> +#define SPINAND_CAP_WR_X2 BIT(6)
> +#define SPINAND_CAP_WR_X4 BIT(7)
> +#define SPINAND_CAP_WR_DUAL BIT(8)
> +#define SPINAND_CAP_WR_QUAD BIT(9)
> +#define SPINAND_CAP_HW_ECC BIT(10)
> +
> +struct spinand_controller {
> +	struct spinand_controller_ops *ops;
> +	u32 caps;
> +};
> +
> +/**
> + * struct spinand_device - SPI-NAND Private Flash Chip Data
> + * @base: NAND device instance
> + * @lock: protection lock
> + * @name: name of the chip
> + * @id: ID structure
> + * @read_cache_op: Opcode of read from cache
> + * @write_cache_op: Opcode of program load
> + * @buf: buffer for read/write data
> + * @oobbuf: buffer for read/write oob
> + * @rw_mode: read/write mode of SPI NAND chip
> + * @controller: SPI NAND controller instance
> + * @manufacturer: SPI NAND manufacturer instance, describe
> + *                manufacturer related objects
> + * @ecc_engine: SPI NAND ECC engine instance
> + */
> +struct spinand_device {
> +	struct nand_device base;
> +	struct mutex lock;
> +	char *name;
> +	struct spinand_id id;
> +	u8 read_cache_op;
> +	u8 write_cache_op;
> +	u8 *buf;
> +	u8 *oobbuf;
> +	u32 rw_mode;
> +	struct {
> +		struct spinand_controller *controller;
> +		void *priv;
> +	} controller;
> +	struct {
> +		const struct spinand_manufacturer *manu;
> +		void *priv;
> +	} manufacturer;
> +	struct {
> +		struct spinand_ecc_engine *engine;
> +		void *context;
> +	} ecc;
> +};
> +
> +static inline struct spinand_device *mtd_to_spinand(struct mtd_info *mtd)
> +{
> +	return container_of(mtd_to_nand(mtd), struct spinand_device, base);
> +}
> +
> +static inline struct mtd_info *spinand_to_mtd(struct spinand_device *chip)
> +{
> +	return nand_to_mtd(&chip->base);
> +}
> +
> +static inline void spinand_set_of_node(struct spinand_device *chip,
> +				       struct device_node *np)
> +{
> +	nand_set_of_node(&chip->base, np);
> +}
> +
> +#define SPINAND_MAX_ADDR_LEN	4
> +
> +struct spinand_op {
> +	u8 cmd;
> +	u8 n_addr;
> +	u8 addr_nbits;
> +	u8 dummy_bytes;
> +	u8 addr[SPINAND_MAX_ADDR_LEN];
> +	u32 n_tx;
> +	const u8 *tx_buf;
> +	u32 n_rx;
> +	u8 *rx_buf;
> +	u8 data_nbits;
> +};
> +
> +/* SPI NAND supported OP mode */
> +#define SPINAND_RD_X1		0x00000001
> +#define SPINAND_RD_X2		0x00000002
> +#define SPINAND_RD_X4		0x00000004
> +#define SPINAND_RD_DUAL		0x00000008
> +#define SPINAND_RD_QUAD		0x00000010
> +#define SPINAND_WR_X1		0x00000020
> +#define SPINAND_WR_X2		0x00000040
> +#define SPINAND_WR_X4		0x00000080
> +#define SPINAND_WR_DUAL		0x00000100
> +#define SPINAND_WR_QUAD		0x00000200
> +
> +#define SPINAND_RD_COMMON	(SPINAND_RD_X1 | SPINAND_RD_X2 | \
> +				 SPINAND_RD_X4 | SPINAND_RD_DUAL | \
> +				 SPINAND_RD_QUAD)
> +#define SPINAND_WR_COMMON	(SPINAND_WR_X1 | SPINAND_WR_X4)
> +#define SPINAND_OP_COMMON	(SPINAND_RD_COMMON | SPINAND_WR_COMMON)
> +
> +struct spinand_device *spinand_alloc(struct device *dev);
> +void spinand_free(struct spinand_device *chip);
> +int spinand_register(struct spinand_device *chip);
> +int spinand_unregister(struct spinand_device *chip);
> +#endif /* __LINUX_MTD_SPINAND_H */
> 


-- 
Best regards,
Marek Vasut

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 8/9] nand: spi: Add generic SPI controller support
  2017-03-23  9:43 ` [PATCH v4 8/9] nand: spi: Add generic SPI controller support Peter Pan
@ 2017-03-23 11:33   ` Marek Vasut
  2017-03-28  1:38     ` Peter Pan
  2017-03-29 21:37   ` Cyrille Pitchen
  1 sibling, 1 reply; 36+ messages in thread
From: Marek Vasut @ 2017-03-23 11:33 UTC (permalink / raw)
  To: Peter Pan, boris.brezillon, richard, computersforpeace,
	arnaud.mouiche, thomas.petazzoni, cyrille.pitchen, linux-mtd
  Cc: peterpansjtu, linshunquan1

On 03/23/2017 10:43 AM, Peter Pan wrote:
> This commit supports to use generic spi controller
> as spi nand controller.
> 
> Signed-off-by: Peter Pan <peterpandong@micron.com>
[...]
> +static int gen_spi_spinand_probe(struct spi_device *spi)
> +{
> +	struct spinand_device *chip;
> +	struct gen_spi_spinand_controller *controller;
> +	struct spinand_controller *spinand_controller;
> +	int ret;
> +	u32 max_speed_hz = spi->max_speed_hz;
> +
> +	chip = spinand_alloc(&spi->dev);
> +	if (IS_ERR(chip)) {
> +		ret = PTR_ERR(chip);
> +		goto err1;
> +	}
> +	controller = kzalloc(sizeof(*controller), GFP_KERNEL);
> +	if (!controller) {
> +		ret = -ENOMEM;
> +		goto err2;
> +	}
> +	controller->spi = spi;
> +	spinand_controller = &controller->ctrl;
> +	spinand_controller->ops = &gen_spi_spinand_ops;
> +	spinand_controller->caps = SPINAND_CAP_RD_X1 | SPINAND_CAP_WR_X1;
> +	if (spi->mode & SPI_RX_QUAD)
> +		spinand_controller->caps |= SPINAND_CAP_RD_QUAD |
> +					    SPINAND_CAP_RD_X4;
> +	if (spi->mode & SPI_RX_DUAL)
> +		spinand_controller->caps |= SPINAND_CAP_RD_DUAL |
> +					    SPINAND_CAP_RD_X2;
> +	if (spi->mode & SPI_TX_QUAD)
> +		spinand_controller->caps |= SPINAND_CAP_WR_QUAD |
> +					    SPINAND_CAP_WR_X4;
> +	if (spi->mode & SPI_TX_DUAL)
> +		spinand_controller->caps |= SPINAND_CAP_WR_DUAL |
> +					    SPINAND_CAP_WR_X2;
> +	chip->controller.controller = spinand_controller;
> +	spi_set_drvdata(spi, chip);
> +	spi->max_speed_hz = min_t(int, 25000000, max_speed_hz);

Please avoid hard-coding random numbers, make the 25 MHz into a macro
and add a comment explaining why you use 25 MHz

> +
> +	ret = spinand_register(chip);
> +	if (ret)
> +		goto err3;
> +
> +	spi->max_speed_hz = max_speed_hz;
> +
> +	return 0;
> +
> +err3:
> +	kfree(controller);
> +err2:
> +	spinand_free(chip);
> +err1:
> +	return ret;
> +}
> +
> +static int gen_spi_spinand_remove(struct spi_device *spi)
> +{
> +	struct spinand_device *chip = spi_get_drvdata(spi);
> +
> +	spinand_unregister(chip);
> +	kfree(to_gen_spi_spinand_controller(chip->controller.controller));

This looks pretty awful, introduce a variable maybe ?

> +	spinand_free(chip);
> +
> +	return 0;
> +}
> +
> +static struct spi_driver gen_spi_spinand_driver = {
> +	.driver = {
> +		.name	= "generic_spinand",
> +		.owner	= THIS_MODULE,
> +	},
> +	.probe	= gen_spi_spinand_probe,
> +	.remove	= gen_spi_spinand_remove,
> +};
> +module_spi_driver(gen_spi_spinand_driver);
> +
> +MODULE_DESCRIPTION("Generic SPI controller to support SPI NAND");
> +MODULE_AUTHOR("Peter Pan<peterpandong@micron.com>");
> +MODULE_LICENSE("GPL v2");
> 


-- 
Best regards,
Marek Vasut

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 4/9] nand: spi: add basic blocks for infrastructure
  2017-03-23 11:29   ` Marek Vasut
@ 2017-03-23 15:40     ` Boris Brezillon
  2017-03-23 16:33       ` Marek Vasut
  0 siblings, 1 reply; 36+ messages in thread
From: Boris Brezillon @ 2017-03-23 15:40 UTC (permalink / raw)
  To: Marek Vasut
  Cc: Peter Pan, richard, computersforpeace, arnaud.mouiche,
	thomas.petazzoni, cyrille.pitchen, linux-mtd, peterpansjtu,
	linshunquan1

On Thu, 23 Mar 2017 12:29:58 +0100
Marek Vasut <marex@denx.de> wrote:


> > +
> > +/*
> > + * spinand_read_status - get status register value
> > + * @chip: SPI NAND device structure
> > + * @status: buffer to store value
> > + * Description:
> > + *   After read, write, or erase, the NAND device is expected to set the
> > + *   busy status.
> > + *   This function is to allow reading the status of the command: read,
> > + *   write, and erase.
> > + */
> > +static int spinand_read_status(struct spinand_device *chip, uint8_t *status)
> > +{
> > +	return spinand_read_reg(chip, REG_STATUS, status);
> > +}
> > +
> > +/*
> > + * spinand_wait - wait until the command is done
> > + * @chip: SPI NAND device structure
> > + * @s: buffer to store status register value (can be NULL)
> > + */
> > +static int spinand_wait(struct spinand_device *chip, u8 *s)
> > +{
> > +	unsigned long timeo = msecs_to_jiffies(400);
> > +	u8 status;
> > +
> > +	do {
> > +		spinand_read_status(chip, &status);
> > +		if ((status & STATUS_OIP_MASK) == STATUS_READY)
> > +			goto out;
> > +	} while (time_before(jiffies, timeo));
> > +
> > +	/*
> > +	 * Extra read, just in case the STATUS_READY bit has changed
> > +	 * since our last check
> > +	 */  
> 
> Is this likely to happen ? Probably not ... so in case you reach this
> place here, timeout happened.

If the timeout is big enough, no. But it does not hurt to do one last
check.

> 
> > +	spinand_read_status(chip, &status);
> > +out:
> > +	if (s)
> > +		*s = status;
> > +
> > +	return (status & STATUS_OIP_MASK) == STATUS_READY ? 0 :	-ETIMEDOUT;
> > +}
> > +
> > +/*
> > + * spinand_read_id - send 9Fh command to get ID
> > + * @chip: SPI NAND device structure
> > + * @buf: buffer to store id
> > + * Description:
> > + *   Manufacturers' read ID method is not unique. Some need a dummy before
> > + *   reading, some's ID has three byte.
> > + *   This function send one byte opcode (9Fh) and then read
> > + *   SPINAND_MAX_ID_LEN (4 currently) bytes. Manufacturer's detect function
> > + *   need to filter out real ID from the 4 bytes.
> > + */
> > +static int spinand_read_id(struct spinand_device *chip, u8 *buf)
> > +{
> > +	struct spinand_op op;
> > +
> > +	spinand_op_init(&op);
> > +	op.cmd = SPINAND_CMD_READ_ID;
> > +	op.n_rx = SPINAND_MAX_ID_LEN;
> > +	op.rx_buf = buf;
> > +
> > +	return spinand_exec_op(chip, &op);
> > +}
> > +
> > +/*
> > + * spinand_reset - reset device by FFh command.
> > + * @chip: SPI NAND device structure
> > + */
> > +static int spinand_reset(struct spinand_device *chip)
> > +{
> > +	struct spinand_op op;
> > +	int ret;
> > +
> > +	spinand_op_init(&op);
> > +	op.cmd = SPINAND_CMD_RESET;
> > +
> > +	ret = spinand_exec_op(chip, &op);
> > +	if (ret < 0) {
> > +		pr_err("spinand reset failed!\n");  
> 
> Can we use dev_err() ? Also , previous patch had some pr_fmt which added
> "SPI NAND:" prefix, so replace "spinand" with "SPI NAND: " to be
> consistent ...

The underlying dev is not ready/initialized the first time
spinand_reset() is called.

> 
> > +		goto out;
> > +	}
> > +	ret = spinand_wait(chip, NULL);
> > +
> > +out:
> > +	return ret;
> > +}

> > +/*
> > + * spinand_manufacturer_detect - detect SPI NAND device by each manufacturer
> > + * @chip: SPI NAND device structure
> > + *
> > + * ->detect() should decode raw id in chip->id.data and initialize device
> > + * related part in spinand_device structure if it is the right device.
> > + * ->detect() can not be NULL.
> > + */
> > +static int spinand_manufacturer_detect(struct spinand_device *chip)
> > +{
> > +	int i = 0;
> > +
> > +	for (; spinand_manufacturers[i]->id != 0x0; i++) {
> > +		if (!spinand_manufacturers[i]->ops ||
> > +		    !spinand_manufacturers[i]->ops->detect) {
> > +			pr_err("%s's ops or ops->detect() is be NULL!\n",
> > +			       spinand_manufacturers[i]->name);  
> 
> Can this case happen, EVER ? I'd mandate the ->detect to be always set.

I agree. Let's assume ->detect is always != NULL. Same goes for ->ops
(assuming you use the ARRAY_SIZE() approach I suggested below).

> 
> > +			return -EINVAL;
> > +		}
> > +		if (spinand_manufacturers[i]->ops->detect(chip)) {
> > +			chip->manufacturer.manu = spinand_manufacturers[i];
> > +			return 0;
> > +		}
> > +	}
> > +
> > +	return -ENODEV;
> > +}

[...]

> > +/*
> > + * spinand_alloc - [SPI NAND Interface] allocate SPI NAND device instance
> > + * @dev: pointer to device model structure
> > + */
> > +struct spinand_device *spinand_alloc(struct device *dev)
> > +{
> > +	struct spinand_device *chip;
> > +	struct spinand_ecc_engine *ecc_engine;
> > +
> > +	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
> > +	if (!chip)
> > +		goto err1;
> > +
> > +	ecc_engine = kzalloc(sizeof(*ecc_engine), GFP_KERNEL);
> > +	if (!ecc_engine)
> > +		goto err2;  
> 
> Just allocate a structure with both chip and ecc_engine in them, then
> you don't have to kzalloc/kfree twice .

Actually, the ECC engine allocation/initialization should not be done
here. At this point, you don't know yet which ECC engine will be used
(on-die, external engine, software ECC, ...).

Let the real ECC implementation allocate the ecc.engine struct
(and overload it if needed).

> 
> > +	ecc_engine->mode = SPINAND_ECC_ONDIE;

The engine mode (which is more a type than a mode IMO) should be
stored in chip->ecc.type (or chip->ecc.mode) and be used by the
different components (core, vendor or controller code) to determine who
is supposed to provide the ECC engine.

> > +	chip->ecc.engine = ecc_engine;
> > +	spinand_set_of_node(chip, dev->of_node);
> > +	mutex_init(&chip->lock);
> > +
> > +	return chip;
> > +
> > +err2:
> > +	kfree(chip);
> > +err1:
> > +	return ERR_PTR(-ENOMEM);
> > +}
> > +EXPORT_SYMBOL_GPL(spinand_alloc);
> > +
> > +/*
> > + * spinand_free - [SPI NAND Interface] free SPI NAND device instance
> > + * @chip: SPI NAND device structure
> > + */
> > +void spinand_free(struct spinand_device *chip)
> > +{
> > +	kfree(chip->ecc.engine);
> > +	kfree(chip);
> > +}
> > +EXPORT_SYMBOL_GPL(spinand_free);
> > +
> > +/*
> > + * spinand_register - [SPI NAND Interface] register SPI NAND device
> > + * @chip: SPI NAND device structure
> > + */
> > +int spinand_register(struct spinand_device *chip)
> > +{
> > +	int ret;
> > +
> > +	ret = spinand_detect(chip);
> > +	if (ret) {
> > +		pr_err("Detect SPI NAND failed with error %d.\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = spinand_init(chip);
> > +	if (ret)
> > +		pr_err("Init SPI NAND failed with error %d.\n", ret);
> > +
> > +	return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(spinand_register);
> > +
> > +/*
> > + * spinand_unregister - [SPI NAND Interface] unregister SPI NAND device
> > + * @chip: SPI NAND device structure
> > + */
> > +int spinand_unregister(struct spinand_device *chip)
> > +{
> > +	struct nand_device *nand = &chip->base;
> > +
> > +	nand_unregister(nand);
> > +	spinand_manufacturer_cleanup(chip);
> > +	kfree(chip->buf);
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(spinand_unregister);
> > +
> > +MODULE_DESCRIPTION("SPI NAND framework");
> > +MODULE_AUTHOR("Peter Pan<peterpandong@micron.com>");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/mtd/nand/spi/manufactures.c b/drivers/mtd/nand/spi/manufactures.c
> > new file mode 100644
> > index 0000000..7e0b42d
> > --- /dev/null
> > +++ b/drivers/mtd/nand/spi/manufactures.c

manufacturers.c, but I don't think I want to keep this file anyway.

> > @@ -0,0 +1,24 @@
> > +/**
> > + *
> > + * Copyright (c) 2009-2017 Micron Technology, Inc.  
> 
> 2009-2017 ? Wow :)
> 
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License
> > + * as published by the Free Software Foundation; either version 2
> > + * of the License, or (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/mtd/spinand.h>
> > +
> > +struct spinand_manufacturer spinand_manufacturer_end = {0x0, "Unknown", NULL};
> > +
> > +struct spinand_manufacturer *spinand_manufacturers[] = {
> > +	&spinand_manufacturer_end,  
> 
> Just populate the array directly .

BTW, you don't need this sentinel. Just use ARRAY_SIZE() and put this
table directly in the core.

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 4/9] nand: spi: add basic blocks for infrastructure
  2017-03-23 15:40     ` Boris Brezillon
@ 2017-03-23 16:33       ` Marek Vasut
  2017-03-30 12:25         ` Arnaud Mouiche
  0 siblings, 1 reply; 36+ messages in thread
From: Marek Vasut @ 2017-03-23 16:33 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Peter Pan, richard, computersforpeace, arnaud.mouiche,
	thomas.petazzoni, cyrille.pitchen, linux-mtd, peterpansjtu,
	linshunquan1

On 03/23/2017 04:40 PM, Boris Brezillon wrote:
> On Thu, 23 Mar 2017 12:29:58 +0100
> Marek Vasut <marex@denx.de> wrote:
> 
> 
>>> +
>>> +/*
>>> + * spinand_read_status - get status register value
>>> + * @chip: SPI NAND device structure
>>> + * @status: buffer to store value
>>> + * Description:
>>> + *   After read, write, or erase, the NAND device is expected to set the
>>> + *   busy status.
>>> + *   This function is to allow reading the status of the command: read,
>>> + *   write, and erase.
>>> + */
>>> +static int spinand_read_status(struct spinand_device *chip, uint8_t *status)
>>> +{
>>> +	return spinand_read_reg(chip, REG_STATUS, status);
>>> +}
>>> +
>>> +/*
>>> + * spinand_wait - wait until the command is done
>>> + * @chip: SPI NAND device structure
>>> + * @s: buffer to store status register value (can be NULL)
>>> + */
>>> +static int spinand_wait(struct spinand_device *chip, u8 *s)
>>> +{
>>> +	unsigned long timeo = msecs_to_jiffies(400);
>>> +	u8 status;
>>> +
>>> +	do {
>>> +		spinand_read_status(chip, &status);
>>> +		if ((status & STATUS_OIP_MASK) == STATUS_READY)
>>> +			goto out;
>>> +	} while (time_before(jiffies, timeo));
>>> +
>>> +	/*
>>> +	 * Extra read, just in case the STATUS_READY bit has changed
>>> +	 * since our last check
>>> +	 */  
>>
>> Is this likely to happen ? Probably not ... so in case you reach this
>> place here, timeout happened.
> 
> If the timeout is big enough, no. But it does not hurt to do one last
> check.

400 mSec is not big enough ? It's huge ... this seems like a duplication
to me. btw the ad-hoc 400 mSec delay value should be replaced with a macro.

>>> +	spinand_read_status(chip, &status);
>>> +out:
>>> +	if (s)
>>> +		*s = status;
>>> +
>>> +	return (status & STATUS_OIP_MASK) == STATUS_READY ? 0 :	-ETIMEDOUT;
>>> +}
>>> +
>>> +/*
>>> + * spinand_read_id - send 9Fh command to get ID
>>> + * @chip: SPI NAND device structure
>>> + * @buf: buffer to store id
>>> + * Description:
>>> + *   Manufacturers' read ID method is not unique. Some need a dummy before
>>> + *   reading, some's ID has three byte.
>>> + *   This function send one byte opcode (9Fh) and then read
>>> + *   SPINAND_MAX_ID_LEN (4 currently) bytes. Manufacturer's detect function
>>> + *   need to filter out real ID from the 4 bytes.
>>> + */
>>> +static int spinand_read_id(struct spinand_device *chip, u8 *buf)
>>> +{
>>> +	struct spinand_op op;
>>> +
>>> +	spinand_op_init(&op);
>>> +	op.cmd = SPINAND_CMD_READ_ID;
>>> +	op.n_rx = SPINAND_MAX_ID_LEN;
>>> +	op.rx_buf = buf;
>>> +
>>> +	return spinand_exec_op(chip, &op);
>>> +}
>>> +
>>> +/*
>>> + * spinand_reset - reset device by FFh command.
>>> + * @chip: SPI NAND device structure
>>> + */
>>> +static int spinand_reset(struct spinand_device *chip)
>>> +{
>>> +	struct spinand_op op;
>>> +	int ret;
>>> +
>>> +	spinand_op_init(&op);
>>> +	op.cmd = SPINAND_CMD_RESET;
>>> +
>>> +	ret = spinand_exec_op(chip, &op);
>>> +	if (ret < 0) {
>>> +		pr_err("spinand reset failed!\n");  
>>
>> Can we use dev_err() ? Also , previous patch had some pr_fmt which added
>> "SPI NAND:" prefix, so replace "spinand" with "SPI NAND: " to be
>> consistent ...
> 
> The underlying dev is not ready/initialized the first time
> spinand_reset() is called.

OK, then at least be consistent with the prefix.

>>> +		goto out;
>>> +	}
>>> +	ret = spinand_wait(chip, NULL);
>>> +
>>> +out:
>>> +	return ret;
>>> +}
> 
>>> +/*
>>> + * spinand_manufacturer_detect - detect SPI NAND device by each manufacturer
>>> + * @chip: SPI NAND device structure
>>> + *
>>> + * ->detect() should decode raw id in chip->id.data and initialize device
>>> + * related part in spinand_device structure if it is the right device.
>>> + * ->detect() can not be NULL.
>>> + */
>>> +static int spinand_manufacturer_detect(struct spinand_device *chip)
>>> +{
>>> +	int i = 0;
>>> +
>>> +	for (; spinand_manufacturers[i]->id != 0x0; i++) {
>>> +		if (!spinand_manufacturers[i]->ops ||
>>> +		    !spinand_manufacturers[i]->ops->detect) {
>>> +			pr_err("%s's ops or ops->detect() is be NULL!\n",
>>> +			       spinand_manufacturers[i]->name);  
>>
>> Can this case happen, EVER ? I'd mandate the ->detect to be always set.
> 
> I agree. Let's assume ->detect is always != NULL. Same goes for ->ops
> (assuming you use the ARRAY_SIZE() approach I suggested below).
> 
>>
>>> +			return -EINVAL;
>>> +		}
>>> +		if (spinand_manufacturers[i]->ops->detect(chip)) {
>>> +			chip->manufacturer.manu = spinand_manufacturers[i];
>>> +			return 0;
>>> +		}
>>> +	}
>>> +
>>> +	return -ENODEV;
>>> +}
> 
> [...]
> 
>>> +/*
>>> + * spinand_alloc - [SPI NAND Interface] allocate SPI NAND device instance
>>> + * @dev: pointer to device model structure
>>> + */
>>> +struct spinand_device *spinand_alloc(struct device *dev)
>>> +{
>>> +	struct spinand_device *chip;
>>> +	struct spinand_ecc_engine *ecc_engine;
>>> +
>>> +	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
>>> +	if (!chip)
>>> +		goto err1;
>>> +
>>> +	ecc_engine = kzalloc(sizeof(*ecc_engine), GFP_KERNEL);
>>> +	if (!ecc_engine)
>>> +		goto err2;  
>>
>> Just allocate a structure with both chip and ecc_engine in them, then
>> you don't have to kzalloc/kfree twice .
> 
> Actually, the ECC engine allocation/initialization should not be done
> here. At this point, you don't know yet which ECC engine will be used
> (on-die, external engine, software ECC, ...).
> 
> Let the real ECC implementation allocate the ecc.engine struct
> (and overload it if needed).

OK

>>> +	ecc_engine->mode = SPINAND_ECC_ONDIE;
> 
> The engine mode (which is more a type than a mode IMO) should be
> stored in chip->ecc.type (or chip->ecc.mode) and be used by the
> different components (core, vendor or controller code) to determine who
> is supposed to provide the ECC engine.
> 
>>> +	chip->ecc.engine = ecc_engine;
>>> +	spinand_set_of_node(chip, dev->of_node);
>>> +	mutex_init(&chip->lock);
>>> +
>>> +	return chip;
>>> +
>>> +err2:
>>> +	kfree(chip);
>>> +err1:
>>> +	return ERR_PTR(-ENOMEM);
>>> +}
>>> +EXPORT_SYMBOL_GPL(spinand_alloc);
>>> +
>>> +/*
>>> + * spinand_free - [SPI NAND Interface] free SPI NAND device instance
>>> + * @chip: SPI NAND device structure
>>> + */
>>> +void spinand_free(struct spinand_device *chip)
>>> +{
>>> +	kfree(chip->ecc.engine);
>>> +	kfree(chip);
>>> +}
>>> +EXPORT_SYMBOL_GPL(spinand_free);
>>> +
>>> +/*
>>> + * spinand_register - [SPI NAND Interface] register SPI NAND device
>>> + * @chip: SPI NAND device structure
>>> + */
>>> +int spinand_register(struct spinand_device *chip)
>>> +{
>>> +	int ret;
>>> +
>>> +	ret = spinand_detect(chip);
>>> +	if (ret) {
>>> +		pr_err("Detect SPI NAND failed with error %d.\n", ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	ret = spinand_init(chip);
>>> +	if (ret)
>>> +		pr_err("Init SPI NAND failed with error %d.\n", ret);
>>> +
>>> +	return ret;
>>> +}
>>> +EXPORT_SYMBOL_GPL(spinand_register);
>>> +
>>> +/*
>>> + * spinand_unregister - [SPI NAND Interface] unregister SPI NAND device
>>> + * @chip: SPI NAND device structure
>>> + */
>>> +int spinand_unregister(struct spinand_device *chip)
>>> +{
>>> +	struct nand_device *nand = &chip->base;
>>> +
>>> +	nand_unregister(nand);
>>> +	spinand_manufacturer_cleanup(chip);
>>> +	kfree(chip->buf);
>>> +
>>> +	return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(spinand_unregister);
>>> +
>>> +MODULE_DESCRIPTION("SPI NAND framework");
>>> +MODULE_AUTHOR("Peter Pan<peterpandong@micron.com>");
>>> +MODULE_LICENSE("GPL v2");
>>> diff --git a/drivers/mtd/nand/spi/manufactures.c b/drivers/mtd/nand/spi/manufactures.c
>>> new file mode 100644
>>> index 0000000..7e0b42d
>>> --- /dev/null
>>> +++ b/drivers/mtd/nand/spi/manufactures.c
> 
> manufacturers.c, but I don't think I want to keep this file anyway.
> 
>>> @@ -0,0 +1,24 @@
>>> +/**
>>> + *
>>> + * Copyright (c) 2009-2017 Micron Technology, Inc.  
>>
>> 2009-2017 ? Wow :)
>>
>>> + * This program is free software; you can redistribute it and/or
>>> + * modify it under the terms of the GNU General Public License
>>> + * as published by the Free Software Foundation; either version 2
>>> + * of the License, or (at your option) any later version.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>> + * GNU General Public License for more details.
>>> + */
>>> +
>>> +#include <linux/module.h>
>>> +#include <linux/mtd/spinand.h>
>>> +
>>> +struct spinand_manufacturer spinand_manufacturer_end = {0x0, "Unknown", NULL};
>>> +
>>> +struct spinand_manufacturer *spinand_manufacturers[] = {
>>> +	&spinand_manufacturer_end,  
>>
>> Just populate the array directly .
> 
> BTW, you don't need this sentinel. Just use ARRAY_SIZE() and put this
> table directly in the core.
> 


-- 
Best regards,
Marek Vasut

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 1/9] mtd: nand: add oob iterator in nand_for_each_page
  2017-03-23 11:13   ` Marek Vasut
@ 2017-03-28  1:35     ` Peter Pan
  0 siblings, 0 replies; 36+ messages in thread
From: Peter Pan @ 2017-03-28  1:35 UTC (permalink / raw)
  To: Marek Vasut
  Cc: Peter Pan, Boris Brezillon, Richard Weinberger, Brian Norris,
	Arnaud Mouiche, Thomas Petazzoni, Cyrille Pitchen, linux-mtd,
	linshunquan (A)

Hi Marek,


On Thu, Mar 23, 2017 at 7:13 PM, Marek Vasut <marex@denx.de> wrote:
> On 03/23/2017 10:43 AM, Peter Pan wrote:
>> Iterate nand pages by both page and oob operation.
>>
>> Signed-off-by: Peter Pan <peterpandong@micron.com>
>> ---
>>  include/linux/mtd/nand.h | 38 +++++++++++++++++++++++++++++++-------
>>  1 file changed, 31 insertions(+), 7 deletions(-)
>>
>> diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
>> index c2197b4..54ded4c 100644
>> --- a/include/linux/mtd/nand.h
>> +++ b/include/linux/mtd/nand.h
>> @@ -84,9 +84,12 @@ struct nand_device {
>>   * @pageoffset: the offset within a page
>>   */
>>  struct nand_page_iter {
>> -     loff_t offs;
>>       int page;
>>       int pageoffs;
>> +     size_t dataleft;
>> +     int ooboffs;
>> +     int oobsize;
>> +     size_t oobleft;
>>  };
>>
>>  /**
>> @@ -193,14 +196,19 @@ static inline int nand_per_page_oobsize(struct nand_device *nand)
>>   * @offs: absolute offset
>>   * @iter: page iterator
>>   */
>> -static inline void nand_page_iter_init(struct nand_device *nand, loff_t offs,
>> +static inline void nand_page_iter_init(struct nand_device *nand,
>> +                                    loff_t offs, size_t len, u32 ooboffs,
>> +                                    size_t ooblen, u32 oobsize,
>>                                      struct nand_page_iter *iter)
>>  {
>>       u64 page = offs;
>>
>>       iter->pageoffs = do_div(page, nand->memorg.pagesize);
>>       iter->page = page;
>> -     iter->offs = offs;
>> +     iter->dataleft = len;
>> +     iter->ooboffs = ooboffs;
>> +     iter->oobsize = oobsize;
>> +     iter->oobleft = ooblen;
>>  }
>>
>>  /**
>> @@ -212,13 +220,29 @@ static inline void nand_page_iter_next(struct nand_device *nand,
>>                                      struct nand_page_iter *iter)
>>  {
>>       iter->page++;
>> -     iter->offs += nand_page_size(nand) - iter->pageoffs;
>>       iter->pageoffs = 0;
>> +     if (iter->dataleft)
>> +             iter->dataleft -= min_t (int,
>                                          ^^^
>                                          shouldn't this be size_t ?

Yes, you are right, Fix this in v5

>
>> +                                      nand_page_size(nand) - iter->pageoffs,
>> +                                      iter->dataleft);
>> +     if (iter->oobleft)
>> +             iter->oobleft -= min_t(int, iter->oobsize - iter->ooboffs,
>
> DTTO here ?

Fix this in v5

>
>> +                                    iter->oobleft);
>> +}
>> +
>> +static inline bool nand_page_iter_end(struct nand_device *nand,
>> +                                   struct nand_page_iter *iter)
>> +{
>> +     if (iter->dataleft || iter->oobleft)
>> +             return false;
>> +     return true;
>>  }
>>
>> -#define nand_for_each_page(nand, start, len, iter)           \
>> -     for (nand_page_iter_init(nand, start, iter);            \
>> -          (iter)->offs < (start) + (len);                    \
>> +#define nand_for_each_page(nand, start, len, ooboffs, ooblen,        \
>> +                        oobsize, iter)       \
>
> What's the difference between ooblen and oobsize ? That is totally
> confusing , so add a comment explaining that ...

Yes, A comment is needed. Fix this in v5

Thanks
Peter Pan

>
>> +     for (nand_page_iter_init(nand, start, len, ooboffs,     \
>> +                              ooblen, oobsize, iter);        \
>> +          !nand_page_iter_end(nand, iter);           \
>>            nand_page_iter_next(nand, iter))
>>
>>  /**
>>
>
>
> --
> Best regards,
> Marek Vasut

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 8/9] nand: spi: Add generic SPI controller support
  2017-03-23 11:33   ` Marek Vasut
@ 2017-03-28  1:38     ` Peter Pan
  0 siblings, 0 replies; 36+ messages in thread
From: Peter Pan @ 2017-03-28  1:38 UTC (permalink / raw)
  To: Marek Vasut
  Cc: Peter Pan, Boris Brezillon, Richard Weinberger, Brian Norris,
	Arnaud Mouiche, Thomas Petazzoni, Cyrille Pitchen, linux-mtd,
	linshunquan (A)

Hi Marek,

On Thu, Mar 23, 2017 at 7:33 PM, Marek Vasut <marex@denx.de> wrote:
> On 03/23/2017 10:43 AM, Peter Pan wrote:
>> This commit supports to use generic spi controller
>> as spi nand controller.
>>
>> Signed-off-by: Peter Pan <peterpandong@micron.com>
> [...]
>> +static int gen_spi_spinand_probe(struct spi_device *spi)
>> +{
>> +     struct spinand_device *chip;
>> +     struct gen_spi_spinand_controller *controller;
>> +     struct spinand_controller *spinand_controller;
>> +     int ret;
>> +     u32 max_speed_hz = spi->max_speed_hz;
>> +
>> +     chip = spinand_alloc(&spi->dev);
>> +     if (IS_ERR(chip)) {
>> +             ret = PTR_ERR(chip);
>> +             goto err1;
>> +     }
>> +     controller = kzalloc(sizeof(*controller), GFP_KERNEL);
>> +     if (!controller) {
>> +             ret = -ENOMEM;
>> +             goto err2;
>> +     }
>> +     controller->spi = spi;
>> +     spinand_controller = &controller->ctrl;
>> +     spinand_controller->ops = &gen_spi_spinand_ops;
>> +     spinand_controller->caps = SPINAND_CAP_RD_X1 | SPINAND_CAP_WR_X1;
>> +     if (spi->mode & SPI_RX_QUAD)
>> +             spinand_controller->caps |= SPINAND_CAP_RD_QUAD |
>> +                                         SPINAND_CAP_RD_X4;
>> +     if (spi->mode & SPI_RX_DUAL)
>> +             spinand_controller->caps |= SPINAND_CAP_RD_DUAL |
>> +                                         SPINAND_CAP_RD_X2;
>> +     if (spi->mode & SPI_TX_QUAD)
>> +             spinand_controller->caps |= SPINAND_CAP_WR_QUAD |
>> +                                         SPINAND_CAP_WR_X4;
>> +     if (spi->mode & SPI_TX_DUAL)
>> +             spinand_controller->caps |= SPINAND_CAP_WR_DUAL |
>> +                                         SPINAND_CAP_WR_X2;
>> +     chip->controller.controller = spinand_controller;
>> +     spi_set_drvdata(spi, chip);
>> +     spi->max_speed_hz = min_t(int, 25000000, max_speed_hz);
>
> Please avoid hard-coding random numbers, make the 25 MHz into a macro
> and add a comment explaining why you use 25 MHz

Yes, I forgot the comment. Fix this in v5

>
>> +
>> +     ret = spinand_register(chip);
>> +     if (ret)
>> +             goto err3;
>> +
>> +     spi->max_speed_hz = max_speed_hz;
>> +
>> +     return 0;
>> +
>> +err3:
>> +     kfree(controller);
>> +err2:
>> +     spinand_free(chip);
>> +err1:
>> +     return ret;
>> +}
>> +
>> +static int gen_spi_spinand_remove(struct spi_device *spi)
>> +{
>> +     struct spinand_device *chip = spi_get_drvdata(spi);
>> +
>> +     spinand_unregister(chip);
>> +     kfree(to_gen_spi_spinand_controller(chip->controller.controller));
>
> This looks pretty awful, introduce a variable maybe ?

Fix this in v5

Thanks,
Peter Pan

>
>> +     spinand_free(chip);
>> +
>> +     return 0;
>> +}
>> +
>> +static struct spi_driver gen_spi_spinand_driver = {
>> +     .driver = {
>> +             .name   = "generic_spinand",
>> +             .owner  = THIS_MODULE,
>> +     },
>> +     .probe  = gen_spi_spinand_probe,
>> +     .remove = gen_spi_spinand_remove,
>> +};
>> +module_spi_driver(gen_spi_spinand_driver);
>> +
>> +MODULE_DESCRIPTION("Generic SPI controller to support SPI NAND");
>> +MODULE_AUTHOR("Peter Pan<peterpandong@micron.com>");
>> +MODULE_LICENSE("GPL v2");
>>
>
>
> --
> Best regards,
> Marek Vasut

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 1/9] mtd: nand: add oob iterator in nand_for_each_page
  2017-03-23  9:43 ` [PATCH v4 1/9] mtd: nand: add oob iterator in nand_for_each_page Peter Pan
  2017-03-23 11:13   ` Marek Vasut
@ 2017-03-29 19:34   ` Boris Brezillon
  2017-03-30  8:01     ` Peter Pan
  1 sibling, 1 reply; 36+ messages in thread
From: Boris Brezillon @ 2017-03-29 19:34 UTC (permalink / raw)
  To: Peter Pan
  Cc: richard, computersforpeace, arnaud.mouiche, thomas.petazzoni,
	marex, cyrille.pitchen, linux-mtd, peterpansjtu, linshunquan1

On Thu, 23 Mar 2017 17:43:38 +0800
Peter Pan <peterpandong@micron.com> wrote:

> Iterate nand pages by both page and oob operation.

For the next version, can you merge my initial commit [1] into this one
(you can keep the authorship)?

> 
> Signed-off-by: Peter Pan <peterpandong@micron.com>
> ---
>  include/linux/mtd/nand.h | 38 +++++++++++++++++++++++++++++++-------
>  1 file changed, 31 insertions(+), 7 deletions(-)
> 
> diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
> index c2197b4..54ded4c 100644
> --- a/include/linux/mtd/nand.h
> +++ b/include/linux/mtd/nand.h
> @@ -84,9 +84,12 @@ struct nand_device {
>   * @pageoffset: the offset within a page

Update the documentation.

>   */
>  struct nand_page_iter {
> -	loff_t offs;
>  	int page;
>  	int pageoffs;
> +	size_t dataleft;
> +	int ooboffs;
> +	int oobsize;

oobsize can be extracted from nand_device, and since a nand_device
object it passed to all nand_page_iter_xxx() functions you don't need it
here.

> +	size_t oobleft;
>  };
>  
>  /**
> @@ -193,14 +196,19 @@ static inline int nand_per_page_oobsize(struct nand_device *nand)
>   * @offs: absolute offset
>   * @iter: page iterator

Update the documentation.

>   */
> -static inline void nand_page_iter_init(struct nand_device *nand, loff_t offs,
> +static inline void nand_page_iter_init(struct nand_device *nand,
> +				       loff_t offs, size_t len, u32 ooboffs,
> +				       size_t ooblen, u32 oobsize,
>  				       struct nand_page_iter *iter)
>  {
>  	u64 page = offs;
>  
>  	iter->pageoffs = do_div(page, nand->memorg.pagesize);
>  	iter->page = page;
> -	iter->offs = offs;
> +	iter->dataleft = len;
> +	iter->ooboffs = ooboffs;
> +	iter->oobsize = oobsize;
> +	iter->oobleft = ooblen;
>  }
>  
>  /**
> @@ -212,13 +220,29 @@ static inline void nand_page_iter_next(struct nand_device *nand,
>  				       struct nand_page_iter *iter)
>  {
>  	iter->page++;
> -	iter->offs += nand_page_size(nand) - iter->pageoffs;
>  	iter->pageoffs = 0;
> +	if (iter->dataleft)
> +		iter->dataleft -= min_t (int,
> +					 nand_page_size(nand) - iter->pageoffs,
> +					 iter->dataleft);
> +	if (iter->oobleft)
> +		iter->oobleft -= min_t(int, iter->oobsize - iter->ooboffs,
> +				       iter->oobleft);
> +}
> +

Document the function.

> +static inline bool nand_page_iter_end(struct nand_device *nand,
> +				      struct nand_page_iter *iter)
> +{
> +	if (iter->dataleft || iter->oobleft)
> +		return false;
> +	return true;
>  }
>  
> -#define nand_for_each_page(nand, start, len, iter)		\
> -	for (nand_page_iter_init(nand, start, iter);		\
> -	     (iter)->offs < (start) + (len);			\
> +#define nand_for_each_page(nand, start, len, ooboffs, ooblen,	\
> +			   oobsize, iter)	\
> +	for (nand_page_iter_init(nand, start, len, ooboffs,	\
> +				 ooblen, oobsize, iter);	\
> +	     !nand_page_iter_end(nand, iter);		\
>  	     nand_page_iter_next(nand, iter))
>  
>  /**

[1]https://github.com/bbrezillon/linux-0day/commit/f02767a662ec02a0206a0b6c5a410a38d7b7b313

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 2/9] mtd: nand: make sure mtd_oob_ops consistent in bbt
  2017-03-23  9:43 ` [PATCH v4 2/9] mtd: nand: make sure mtd_oob_ops consistent in bbt Peter Pan
@ 2017-03-29 19:48   ` Boris Brezillon
  0 siblings, 0 replies; 36+ messages in thread
From: Boris Brezillon @ 2017-03-29 19:48 UTC (permalink / raw)
  To: Peter Pan
  Cc: richard, computersforpeace, arnaud.mouiche, thomas.petazzoni,
	marex, cyrille.pitchen, linux-mtd, peterpansjtu, linshunquan1

On Thu, 23 Mar 2017 17:43:39 +0800
Peter Pan <peterpandong@micron.com> wrote:

> This commit makes sure struct mtd_oob_ops passed to mtd_read/write_ops()
> is consistent, which means .datbuf = NULL and .len = 0 when not reading
> page; .oobbuf = NULL and .ooblen = 0 when not reading oob. If no, (ie.
> ops.datbuf = NULL, while ops.len != 0.), nand_for_each_page() will run
> forever.
> 
> Signed-off-by: Peter Pan <peterpandong@micron.com>
> ---
>  drivers/mtd/nand/bbt.c | 13 ++++++-------
>  1 file changed, 6 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/mtd/nand/bbt.c b/drivers/mtd/nand/bbt.c
> index 94cd3e5..c35a6e8 100644
> --- a/drivers/mtd/nand/bbt.c
> +++ b/drivers/mtd/nand/bbt.c
> @@ -328,7 +328,7 @@ static int scan_read_oob(struct nand_device *this, uint8_t *buf, loff_t offs,
>  			 size_t len)
>  {
>  	struct mtd_info *mtd = nand_to_mtd(this);
> -	struct mtd_oob_ops ops;
> +	struct mtd_oob_ops ops = {0};

	struct mtd_oob_ops ops = { };

>  	int res, ret = 0;
>  
>  	ops.mode = MTD_OPS_PLACE_OOB;
> @@ -369,11 +369,12 @@ static int scan_write_bbt(struct nand_device *this, loff_t offs, size_t len,
>  			  uint8_t *buf, uint8_t *oob)
>  {
>  	struct mtd_info *mtd = nand_to_mtd(this);
> -	struct mtd_oob_ops ops;
> +	struct mtd_oob_ops ops = {0};
>  
>  	ops.mode = MTD_OPS_PLACE_OOB;
>  	ops.ooboffs = 0;
> -	ops.ooblen = nand_per_page_oobsize(this);
> +	/* oob from caller may be NULL */
> +	ops.ooblen = oob ? nand_per_page_oobsize(this) : 0;
>  	ops.datbuf = buf;
>  	ops.oobbuf = oob;
>  	ops.len = len;
> @@ -428,13 +429,12 @@ static int scan_block_fast(struct nand_device *this, struct nand_bbt_descr *bd,
>  			   loff_t offs, uint8_t *buf, int numpages)
>  {
>  	struct mtd_info *mtd = nand_to_mtd(this);
> -	struct mtd_oob_ops ops;
> +	struct mtd_oob_ops ops = {0};
>  	int j, ret;
>  
>  	ops.ooblen = nand_per_page_oobsize(this);
>  	ops.oobbuf = buf;
>  	ops.ooboffs = 0;
> -	ops.datbuf = NULL;
>  	ops.mode = MTD_OPS_PLACE_OOB;
>  
>  	for (j = 0; j < numpages; j++) {
> @@ -734,11 +734,10 @@ static int write_bbt(struct nand_device *this, uint8_t *buf,
>  	uint8_t rcode = td->reserved_block_code;
>  	size_t retlen, len = 0;
>  	loff_t to;
> -	struct mtd_oob_ops ops;
> +	struct mtd_oob_ops ops = {0};
>  
>  	ops.ooblen = nand_per_page_oobsize(this);
>  	ops.ooboffs = 0;
> -	ops.datbuf = NULL;
>  	ops.mode = MTD_OPS_PLACE_OOB;
>  
>  	if (!rcode)

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 3/9] mtd: nand: add more helpers in nand.h
  2017-03-23  9:43 ` [PATCH v4 3/9] mtd: nand: add more helpers in nand.h Peter Pan
  2017-03-23 11:19   ` Marek Vasut
@ 2017-03-29 19:57   ` Boris Brezillon
  2017-03-30  8:04     ` Peter Pan
  1 sibling, 1 reply; 36+ messages in thread
From: Boris Brezillon @ 2017-03-29 19:57 UTC (permalink / raw)
  To: Peter Pan
  Cc: richard, computersforpeace, arnaud.mouiche, thomas.petazzoni,
	marex, cyrille.pitchen, linux-mtd, peterpansjtu, linshunquan1

On Thu, 23 Mar 2017 17:43:40 +0800
Peter Pan <peterpandong@micron.com> wrote:

> This commit adds some helpers in nand.h
>     nand_size()
>     nand_check_address()
>     nand_check_oob_ops()
>     nand_oob_ops_across_page()
>     nand_check_erase_ops()
> 
> Signed-off-by: Peter Pan <peterpandong@micron.com>
> ---
>  include/linux/mtd/nand.h | 62 ++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 62 insertions(+)
> 
> diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
> index 54ded4c..0c52401 100644
> --- a/include/linux/mtd/nand.h
> +++ b/include/linux/mtd/nand.h
> @@ -434,6 +434,68 @@ static inline int nand_neraseblocks(struct nand_device *nand)
>  }
>  
>  /**
> + * nand_size - Get NAND size
> + * @nand: NAND device
> + *
> + * Returns the total size exposed by @nand.
> + */
> +static inline u64 nand_size(struct nand_device *nand)
> +{
> +	return nand->memorg.ndies * nand->memorg.diesize;
> +}
> +

The nand_size() function should probably go in the commit introducing
the interface-agnostic NAND layer (in my own series).

Since you'll be the first one to use the generic NAND layer, I propose
that you start maintaining my patch-set along with your SPI NAND
patches.

> +static inline int nand_check_address(struct nand_device *nand, loff_t addr)
> +{
> +	return addr < nand_size(nand) ? 0 : -EINVAL;
> +}
> +
> +static inline int nand_check_oob_ops(struct nand_device *nand, loff_t start,
> +				     struct mtd_oob_ops *ops)
> +{
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +	int ooblen = ops->mode == MTD_OPS_AUTO_OOB ?
> +		mtd->oobavail : mtd->oobsize;
> +
> +	if ((!!ops->datbuf != !!ops->len) ||
> +	    (!!ops->oobbuf != !!ops->ooblen))
> +		return -EINVAL;
> +	if (ops->ooboffs >= ooblen)
> +		return -EINVAL;
> +	if (ops->ooboffs + ops->ooblen >
> +	    (nand_len_to_pages(nand, nand_size(nand)) -
> +			       nand_offs_to_page(nand, start)) * ooblen)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static inline bool nand_oob_ops_across_page(struct nand_device *nand,
> +					    struct mtd_oob_ops *ops)
> +{
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +	int ooblen = ops->mode == MTD_OPS_AUTO_OOB ?
> +		     mtd->oobavail : mtd->oobsize;
> +
> +	return (ops->ooboffs + ops->ooblen) > ooblen;
> +}
> +
> +static inline int nand_check_erase_ops(struct nand_device *nand,
> +				       struct erase_info *einfo)
> +{
> +	/* check address align on block boundary */
> +	if (einfo->addr & (nand_eraseblock_size(nand) - 1))
> +		return -EINVAL;
> +	/* check lendth align on block boundary */
> +	if (einfo->len & (nand_eraseblock_size(nand) - 1))
> +		return -EINVAL;
> +	/* Do not allow erase past end of device */
> +	if ((einfo->addr + einfo->len) > nand_size(nand))
> +		return -EINVAL;
> +
> +	return 0;
> +}

Hm, looking at these functions and the tests already done in mtdcore.c
I'm not sure this is the best location.
AFAICS, all these tests can be done (or are already done) at the MTD
level, and that's where they are the most useful. The only exception I
see where it could be useful is when the BBT code directly calls nand
functions instead of mtd ones, but this should not happen that much.

> +
> +/**
>   * nand_register - Register a NAND device
>   * @nand: NAND device
>   *

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 8/9] nand: spi: Add generic SPI controller support
  2017-03-23  9:43 ` [PATCH v4 8/9] nand: spi: Add generic SPI controller support Peter Pan
  2017-03-23 11:33   ` Marek Vasut
@ 2017-03-29 21:37   ` Cyrille Pitchen
  2017-03-30  8:28     ` Peter Pan
  1 sibling, 1 reply; 36+ messages in thread
From: Cyrille Pitchen @ 2017-03-29 21:37 UTC (permalink / raw)
  To: Peter Pan, boris.brezillon, richard, computersforpeace,
	arnaud.mouiche, thomas.petazzoni, marex, cyrille.pitchen,
	linux-mtd
  Cc: peterpansjtu, linshunquan1

Hi Peter,

Le 23/03/2017 à 10:43, Peter Pan a écrit :
> This commit supports to use generic spi controller
> as spi nand controller.
> 
> Signed-off-by: Peter Pan <peterpandong@micron.com>
> ---
>  drivers/mtd/nand/spi/Kconfig                   |   2 +
>  drivers/mtd/nand/spi/Makefile                  |   1 +
>  drivers/mtd/nand/spi/controllers/Kconfig       |   5 +
>  drivers/mtd/nand/spi/controllers/Makefile      |   1 +
>  drivers/mtd/nand/spi/controllers/generic-spi.c | 150 +++++++++++++++++++++++++
>  5 files changed, 159 insertions(+)
>  create mode 100644 drivers/mtd/nand/spi/controllers/Kconfig
>  create mode 100644 drivers/mtd/nand/spi/controllers/Makefile
>  create mode 100644 drivers/mtd/nand/spi/controllers/generic-spi.c
> 
> diff --git a/drivers/mtd/nand/spi/Kconfig b/drivers/mtd/nand/spi/Kconfig
> index d77c46e..6bd1c65 100644
> --- a/drivers/mtd/nand/spi/Kconfig
> +++ b/drivers/mtd/nand/spi/Kconfig
> @@ -3,3 +3,5 @@ menuconfig MTD_SPI_NAND
>  	depends on MTD_NAND
>  	help
>  	  This is the framework for the SPI NAND device drivers.
> +
> +source "drivers/mtd/nand/spi/controllers/Kconfig"
> diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile
> index db0b91b..6ad5f24 100644
> --- a/drivers/mtd/nand/spi/Makefile
> +++ b/drivers/mtd/nand/spi/Makefile
> @@ -1,3 +1,4 @@
>  obj-$(CONFIG_MTD_SPI_NAND) += core.o
>  obj-$(CONFIG_MTD_SPI_NAND) += manufactures.o
>  obj-$(CONFIG_MTD_SPI_NAND) += micron.o
> +obj-$(CONFIG_MTD_SPI_NAND) += controllers/
> diff --git a/drivers/mtd/nand/spi/controllers/Kconfig b/drivers/mtd/nand/spi/controllers/Kconfig
> new file mode 100644
> index 0000000..8ab7023
> --- /dev/null
> +++ b/drivers/mtd/nand/spi/controllers/Kconfig
> @@ -0,0 +1,5 @@
> +config GENERIC_SPI_NAND
> +	tristate "SPI NAND with generic SPI bus Support"
> +	depends on MTD_SPI_NAND && SPI
> +	help
> +	  This is to support SPI NAND device with generic SPI bus.
> diff --git a/drivers/mtd/nand/spi/controllers/Makefile b/drivers/mtd/nand/spi/controllers/Makefile
> new file mode 100644
> index 0000000..46cbf29
> --- /dev/null
> +++ b/drivers/mtd/nand/spi/controllers/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_GENERIC_SPI_NAND) += generic-spi.o
> diff --git a/drivers/mtd/nand/spi/controllers/generic-spi.c b/drivers/mtd/nand/spi/controllers/generic-spi.c
> new file mode 100644
> index 0000000..da1f224
> --- /dev/null
> +++ b/drivers/mtd/nand/spi/controllers/generic-spi.c
> @@ -0,0 +1,150 @@
> +/*
> + * Copyright (c) 2009-2017 Micron Technology, Inc.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/spi/spi.h>
> +#include <linux/mtd/spinand.h>
> +
> +struct gen_spi_spinand_controller {
> +	struct spinand_controller ctrl;
> +	struct spi_device *spi;
> +};
> +
> +#define to_gen_spi_spinand_controller(c) \
> +	container_of(c, struct gen_spi_spinand_controller, ctrl)
> +
> +/*
> + * gen_spi_spinand_exec_op - to process a command to send to the
> + * SPI NAND by generic SPI bus
> + * @chip: SPI NAND device structure
> + * @op: SPI NAND operation descriptor
> + */
> +static int gen_spi_spinand_exec_op(struct spinand_device *chip,
> +				   struct spinand_op *op)
> +{
> +	struct spi_message message;
> +	struct spi_transfer x[3];
> +	struct spinand_controller *scontroller = chip->controller.controller;
> +	struct gen_spi_spinand_controller *controller;
> +
> +	controller = to_gen_spi_spinand_controller(scontroller);
> +	spi_message_init(&message);
> +	memset(x, 0, sizeof(x));
> +	x[0].len = 1;
> +	x[0].tx_nbits = 1;
> +	x[0].tx_buf = &op->cmd;
> +	spi_message_add_tail(&x[0], &message);
> +
> +	if (op->n_addr + op->dummy_bytes) {
> +		x[1].len = op->n_addr + op->dummy_bytes;
> +		x[1].tx_nbits = op->addr_nbits;
> +		x[1].tx_buf = op->addr;
> +		spi_message_add_tail(&x[1], &message);
> +	}
> +	if (op->n_tx) {
> +		x[2].len = op->n_tx;
> +		x[2].tx_nbits = op->data_nbits;
> +		x[2].tx_buf = op->tx_buf;
> +		spi_message_add_tail(&x[2], &message);
> +	} else if (op->n_rx) {
> +		x[2].len = op->n_rx;
> +		x[2].rx_nbits = op->data_nbits;
> +		x[2].rx_buf = op->rx_buf;
> +		spi_message_add_tail(&x[2], &message);
> +	}
> +	return spi_sync(controller->spi, &message);
> +}

2 comments:

1/
maybe you can merge 'struct spi_transfer' in some cases:

for instance with SPI x-x-z protocols, you can carry both the
instruction and address/dummy bytes in a single spi_transfer structure.

Also for Read operations (not applicable for Write/Page Program), with
SPI x-z-z, you can merge both the address/dummy and data bytes in a
single spi_transfer structure.

This could speed-up the operation with some SPI controller drivers.
However this is just an optimization otherwise it should work as is.

2/
Some spi controller drivers, like the TI QSPI controller driver, may
want to use something similar to the spi_flash_read() function used from
drivers/mtd/spi-nor/m25p80.c.

Indeed, some QSPI controllers can speed up read operations by mapping
the flash data array into the system memory. In such a case, the regular
SPI API (spi_message and spi_transfer structures) is no longer used.
That's why spi_flash_read() was introduced.

However unlike SPI-NOR memories, SPI-NAND memories require to read data
in 2 steps: internal data array -> internal cache then internal cache ->
SPI bus. Hence using a mapping in the system memory only for "read from
cache" operations is likely less interesting than reading the whole data
array like regular memory as we can do with SPI NOR.

This is another optimization, so not mandatory at all, but I just wanted
to warn you about that so be prepared to be queried about that ;)



> +
> +static struct spinand_controller_ops gen_spi_spinand_ops = {
> +	.exec_op = gen_spi_spinand_exec_op,
> +};
> +
> +static int gen_spi_spinand_probe(struct spi_device *spi)
> +{
> +	struct spinand_device *chip;
> +	struct gen_spi_spinand_controller *controller;
> +	struct spinand_controller *spinand_controller;
> +	int ret;
> +	u32 max_speed_hz = spi->max_speed_hz;
> +
> +	chip = spinand_alloc(&spi->dev);
> +	if (IS_ERR(chip)) {
> +		ret = PTR_ERR(chip);
> +		goto err1;
> +	}
> +	controller = kzalloc(sizeof(*controller), GFP_KERNEL);
> +	if (!controller) {
> +		ret = -ENOMEM;
> +		goto err2;
> +	}
> +	controller->spi = spi;
> +	spinand_controller = &controller->ctrl;
> +	spinand_controller->ops = &gen_spi_spinand_ops;
> +	spinand_controller->caps = SPINAND_CAP_RD_X1 | SPINAND_CAP_WR_X1;
> +	if (spi->mode & SPI_RX_QUAD)
> +		spinand_controller->caps |= SPINAND_CAP_RD_QUAD |
> +					    SPINAND_CAP_RD_X4;
> +	if (spi->mode & SPI_RX_DUAL)
> +		spinand_controller->caps |= SPINAND_CAP_RD_DUAL |
> +					    SPINAND_CAP_RD_X2;
> +	if (spi->mode & SPI_TX_QUAD)
> +		spinand_controller->caps |= SPINAND_CAP_WR_QUAD |
> +					    SPINAND_CAP_WR_X4;
> +	if (spi->mode & SPI_TX_DUAL)
> +		spinand_controller->caps |= SPINAND_CAP_WR_DUAL |
> +					    SPINAND_CAP_WR_X2;

I think this is wrong:
the SPI_RX_QUAD means the controller can receive data on 4 I/O line
hence it allows SPINAND_CAP_RD_X4 (1-1-4) but not necessarily
SPINAND_CAP_QUAD (1-4-4).

To add SPINAND_CAP_QUAD (1-4-4), SPINAND_CAP_WR_X4 (1-1-4) and
SPINAND_CAP_WR_QUAD (1-4-4) you need both SPI_RX_QUAD and SPI_TX_QUAD.

So with Dual SPI protocols:

at least SPI_RX_DUAL: caps = RD_X2
both SPI_RX_DUAL and SPI_TX_DUAL: caps |= (RD_DUAL | WR_X2 | WR_DUAL);

SPI masters (controllers) are not aware of SPI x-y-z flash protocols,
they just claim they can use more than 1 I/O line to read data and/or
more than 1 I/0 line to write data.

> +	chip->controller.controller = spinand_controller;
> +	spi_set_drvdata(spi, chip);
> +	spi->max_speed_hz = min_t(int, 25000000, max_speed_hz);
> +
> +	ret = spinand_register(chip);
> +	if (ret)
> +		goto err3;
> +
> +	spi->max_speed_hz = max_speed_hz;
> +
> +	return 0;
> +
> +err3:
> +	kfree(controller);
> +err2:
> +	spinand_free(chip);
> +err1:
> +	return ret;
> +}
> +
> +static int gen_spi_spinand_remove(struct spi_device *spi)
> +{
> +	struct spinand_device *chip = spi_get_drvdata(spi);
> +
> +	spinand_unregister(chip);
> +	kfree(to_gen_spi_spinand_controller(chip->controller.controller));
> +	spinand_free(chip);
> +
> +	return 0;
> +}
> +
> +static struct spi_driver gen_spi_spinand_driver = {
> +	.driver = {
> +		.name	= "generic_spinand",
> +		.owner	= THIS_MODULE,
> +	},
> +	.probe	= gen_spi_spinand_probe,
> +	.remove	= gen_spi_spinand_remove,
> +};
> +module_spi_driver(gen_spi_spinand_driver);
> +
> +MODULE_DESCRIPTION("Generic SPI controller to support SPI NAND");
> +MODULE_AUTHOR("Peter Pan<peterpandong@micron.com>");
> +MODULE_LICENSE("GPL v2");
> 

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 4/9] nand: spi: add basic blocks for infrastructure
  2017-03-23  9:43 ` [PATCH v4 4/9] nand: spi: add basic blocks for infrastructure Peter Pan
  2017-03-23 11:29   ` Marek Vasut
@ 2017-03-29 22:28   ` Cyrille Pitchen
  2017-03-30 12:38   ` Arnaud Mouiche
  2 siblings, 0 replies; 36+ messages in thread
From: Cyrille Pitchen @ 2017-03-29 22:28 UTC (permalink / raw)
  To: Peter Pan, boris.brezillon, richard, computersforpeace,
	arnaud.mouiche, thomas.petazzoni, marex, cyrille.pitchen,
	linux-mtd
  Cc: peterpansjtu, linshunquan1

Le 23/03/2017 à 10:43, Peter Pan a écrit :
> This is the first commit for spi nand framkework.
> This commit is to add add basic building blocks
> for the SPI NAND infrastructure.
> 
> Signed-off-by: Peter Pan <peterpandong@micron.com>
> ---
>  drivers/mtd/nand/Kconfig            |   1 +
>  drivers/mtd/nand/Makefile           |   1 +
>  drivers/mtd/nand/spi/Kconfig        |   5 +
>  drivers/mtd/nand/spi/Makefile       |   2 +
>  drivers/mtd/nand/spi/core.c         | 464 ++++++++++++++++++++++++++++++++++++
>  drivers/mtd/nand/spi/manufactures.c |  24 ++
>  include/linux/mtd/spinand.h         | 270 +++++++++++++++++++++
>  7 files changed, 767 insertions(+)
>  create mode 100644 drivers/mtd/nand/spi/Kconfig
>  create mode 100644 drivers/mtd/nand/spi/Makefile
>  create mode 100644 drivers/mtd/nand/spi/core.c
>  create mode 100644 drivers/mtd/nand/spi/manufactures.c
>  create mode 100644 include/linux/mtd/spinand.h
> 
> diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
> index 1c1a1f4..7695fd8 100644
> --- a/drivers/mtd/nand/Kconfig
> +++ b/drivers/mtd/nand/Kconfig
> @@ -2,3 +2,4 @@ config MTD_NAND_CORE
>  	tristate
>  
>  source "drivers/mtd/nand/raw/Kconfig"
> +source "drivers/mtd/nand/spi/Kconfig"
> diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
> index fe430d9..6221958 100644
> --- a/drivers/mtd/nand/Makefile
> +++ b/drivers/mtd/nand/Makefile
> @@ -3,3 +3,4 @@ obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o
>  nandcore-objs :=  bbt.o
>  
>  obj-y	+= raw/
> +obj-$(CONFIG_MTD_SPI_NAND)	+= spi/
> diff --git a/drivers/mtd/nand/spi/Kconfig b/drivers/mtd/nand/spi/Kconfig
> new file mode 100644
> index 0000000..d77c46e
> --- /dev/null
> +++ b/drivers/mtd/nand/spi/Kconfig
> @@ -0,0 +1,5 @@
> +menuconfig MTD_SPI_NAND
> +	tristate "SPI NAND device Support"
> +	depends on MTD_NAND
> +	help
> +	  This is the framework for the SPI NAND device drivers.
> diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile
> new file mode 100644
> index 0000000..eabdb81
> --- /dev/null
> +++ b/drivers/mtd/nand/spi/Makefile
> @@ -0,0 +1,2 @@
> +obj-$(CONFIG_MTD_SPI_NAND) += core.o
> +obj-$(CONFIG_MTD_SPI_NAND) += manufactures.o
> diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
> new file mode 100644
> index 0000000..d1ac522
> --- /dev/null
> +++ b/drivers/mtd/nand/spi/core.c
> @@ -0,0 +1,464 @@
> +/*
> + *
> + * Copyright (c) 2009-2017 Micron Technology, Inc.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#define pr_fmt(fmt) "SPI NAND: " fmt
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/jiffies.h>
> +#include <linux/mtd/spinand.h>
> +#include <linux/slab.h>
> +
> +/*
> + * spinand_exec_op - execute SPI NAND operation by controller ->exec_op() hook
> + * @chip: SPI NAND device structure
> + * @op: pointer to spinand_op struct
> + */
> +static inline int spinand_exec_op(struct spinand_device *chip,
> +				  struct spinand_op *op)
> +{
> +	return chip->controller.controller->ops->exec_op(chip, op);
> +}
> +
> +/*
> + * spinand_op_init - initialize spinand_op struct
> + * @op: pointer to spinand_op struct
> + */
> +static inline void spinand_op_init(struct spinand_op *op)
> +{
> +	memset(op, 0, sizeof(struct spinand_op));
> +	op->addr_nbits = 1;
> +	op->data_nbits = 1;
> +}
> +
> +/*
> + * spinand_read_reg - send command 0Fh to read register
> + * @chip: SPI NAND device structure
> + * @reg; register to read
> + * @buf: buffer to store value
> + */
> +static int spinand_read_reg(struct spinand_device *chip, u8 reg, u8 *buf)
> +{
> +	struct spinand_op op;
> +	int ret;
> +
> +	spinand_op_init(&op);
> +	op.cmd = SPINAND_CMD_GET_FEATURE;
> +	op.n_addr = 1;
> +	op.addr[0] = reg;
> +	op.n_rx = 1;
> +	op.rx_buf = buf;
> +
> +	ret = spinand_exec_op(chip, &op);
> +	if (ret < 0)
> +		pr_err("err: %d read register %d\n", ret, reg);
> +
> +	return ret;
> +}
> +
> +/*
> + * spinand_write_reg - send command 1Fh to write register
> + * @chip: SPI NAND device structure
> + * @reg; register to write
> + * @buf: buffer stored value
> + */
> +static int spinand_write_reg(struct spinand_device *chip, u8 reg, u8 *buf)
> +{
> +	struct spinand_op op;
> +	int ret;
> +
> +	spinand_op_init(&op);
> +	op.cmd = SPINAND_CMD_SET_FEATURE;
> +	op.n_addr = 1;
> +	op.addr[0] = reg;
> +	op.n_tx = 1,
> +	op.tx_buf = buf,
> +
> +	ret = spinand_exec_op(chip, &op);
> +	if (ret < 0)
> +		pr_err("err: %d write register %d\n", ret, reg);
> +
> +	return ret;
> +}
> +
> +/*
> + * spinand_read_status - get status register value
> + * @chip: SPI NAND device structure
> + * @status: buffer to store value
> + * Description:
> + *   After read, write, or erase, the NAND device is expected to set the
> + *   busy status.
> + *   This function is to allow reading the status of the command: read,
> + *   write, and erase.
> + */
> +static int spinand_read_status(struct spinand_device *chip, uint8_t *status)
> +{
> +	return spinand_read_reg(chip, REG_STATUS, status);
> +}
> +
> +/*
> + * spinand_wait - wait until the command is done
> + * @chip: SPI NAND device structure
> + * @s: buffer to store status register value (can be NULL)
> + */
> +static int spinand_wait(struct spinand_device *chip, u8 *s)
> +{
> +	unsigned long timeo = msecs_to_jiffies(400);
> +	u8 status;
> +
> +	do {
> +		spinand_read_status(chip, &status);
> +		if ((status & STATUS_OIP_MASK) == STATUS_READY)
> +			goto out;
> +	} while (time_before(jiffies, timeo));
> +
> +	/*
> +	 * Extra read, just in case the STATUS_READY bit has changed
> +	 * since our last check
> +	 */
> +	spinand_read_status(chip, &status);
> +out:
> +	if (s)
> +		*s = status;
> +
> +	return (status & STATUS_OIP_MASK) == STATUS_READY ? 0 :	-ETIMEDOUT;
> +}
> +
> +/*
> + * spinand_read_id - send 9Fh command to get ID
> + * @chip: SPI NAND device structure
> + * @buf: buffer to store id
> + * Description:
> + *   Manufacturers' read ID method is not unique. Some need a dummy before
> + *   reading, some's ID has three byte.
> + *   This function send one byte opcode (9Fh) and then read
> + *   SPINAND_MAX_ID_LEN (4 currently) bytes. Manufacturer's detect function
> + *   need to filter out real ID from the 4 bytes.
> + */
> +static int spinand_read_id(struct spinand_device *chip, u8 *buf)
> +{
> +	struct spinand_op op;
> +
> +	spinand_op_init(&op);
> +	op.cmd = SPINAND_CMD_READ_ID;
> +	op.n_rx = SPINAND_MAX_ID_LEN;
> +	op.rx_buf = buf;
> +
> +	return spinand_exec_op(chip, &op);
> +}
> +
> +/*
> + * spinand_reset - reset device by FFh command.
> + * @chip: SPI NAND device structure
> + */
> +static int spinand_reset(struct spinand_device *chip)
> +{
> +	struct spinand_op op;
> +	int ret;
> +
> +	spinand_op_init(&op);
> +	op.cmd = SPINAND_CMD_RESET;
> +
> +	ret = spinand_exec_op(chip, &op);
> +	if (ret < 0) {
> +		pr_err("spinand reset failed!\n");
> +		goto out;
> +	}
> +	ret = spinand_wait(chip, NULL);
> +
> +out:
> +	return ret;
> +}
> +
> +/*
> + * spinand_lock_block - write block lock register to lock/unlock device
> + * @chip: SPI NAND device structure
> + * @lock: value to set to block lock register
> + */
> +static int spinand_lock_block(struct spinand_device *chip, u8 lock)
> +{
> +	return spinand_write_reg(chip, REG_BLOCK_LOCK, &lock);
> +}
> +
> +/*
> + * spinand_set_rd_wr_op - choose the best read write command
> + * @chip: SPI NAND device structure
> + * Description:
> + *   Chose the fastest r/w command according to spi controller's and
> + *   device's ability.
> + */
> +static void spinand_set_rd_wr_op(struct spinand_device *chip)
> +{
> +	u32 controller_cap = chip->controller.controller->caps;
> +	u32 rw_mode = chip->rw_mode;
> +
> +	if ((controller_cap & SPINAND_CAP_RD_QUAD) &&
> +	    (rw_mode & SPINAND_RD_QUAD))
> +		chip->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_QUAD_IO;
> +	else if ((controller_cap & SPINAND_CAP_RD_X4) &&
> +		 (rw_mode & SPINAND_RD_X4))
> +		chip->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_X4;
> +	else if ((controller_cap & SPINAND_CAP_RD_DUAL) &&
> +		 (rw_mode & SPINAND_RD_DUAL))
> +		chip->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_DUAL_IO;
> +	else if ((controller_cap & SPINAND_CAP_RD_X2) &&
> +		 (rw_mode & SPINAND_RD_X2))
> +		chip->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_X2;
> +	else
> +		chip->read_cache_op = SPINAND_CMD_READ_FROM_CACHE_FAST;
> +
> +	if ((controller_cap & SPINAND_CAP_WR_X4) &&
> +	    (rw_mode & SPINAND_WR_X4))
> +		chip->write_cache_op = SPINAND_CMD_PROG_LOAD_X4;
> +	else
> +		chip->write_cache_op = SPINAND_CMD_PROG_LOAD;
> +}
> +
> +/*
> + * spinand_manufacturer_detect - detect SPI NAND device by each manufacturer
> + * @chip: SPI NAND device structure
> + *
> + * ->detect() should decode raw id in chip->id.data and initialize device
> + * related part in spinand_device structure if it is the right device.
> + * ->detect() can not be NULL.
> + */
> +static int spinand_manufacturer_detect(struct spinand_device *chip)
> +{
> +	int i = 0;
> +
> +	for (; spinand_manufacturers[i]->id != 0x0; i++) {
> +		if (!spinand_manufacturers[i]->ops ||
> +		    !spinand_manufacturers[i]->ops->detect) {
> +			pr_err("%s's ops or ops->detect() is be NULL!\n",
> +			       spinand_manufacturers[i]->name);
> +			return -EINVAL;
> +		}
> +		if (spinand_manufacturers[i]->ops->detect(chip)) {
> +			chip->manufacturer.manu = spinand_manufacturers[i];
> +			return 0;
> +		}

As discussed with Boris on IRC, you don't check the manufacturer ID
(first byte of the JEDEC ID) before calling ops->dectect() because of
the variable number of dummy clock cycles between the instruction cycles
and the data cycles, fine.

Indeed it would have been a good thing to call ops->dectect() only for
the right manufacturer base one the JEDEC ID. Anyways, instead be sure
that the op->detect() implementation for manufacturer A never sends any
manufacturer specific SPI command to a SPI NAND from manufacturer B,
otherwise unwanted side effects may occur!

You could add a comment to enforce this requirement.

I warn you about that, because with Micron QSPI NOR memories in 4-4-4
mode, I noticed side effects when sending the regular Read JEDEC ID
command (9Fh). I know that with Micron memories, once in their 4-4-4
mode, they no longer support the regular 9Fh command and the AFh command
should be used instead. However with Winbond memories, even in 4-4-4
mode, we have to use the 9Fh command, the AFh op code being not supported...

Since you don't know which Read JEDEC ID command to send before knowing
the actual manufuctarer and since we use the Read JEDEC ID command to
dynamically guess the manufacturer... no solution to dynamically probe
the QSPI memory if already in 4-4-4 mode!

> +	}
> +
> +	return -ENODEV;
> +}
> +
> +/*
> + * spinand_manufacturer_init - manufacturer initialization function.
> + * @chip: SPI NAND device structure
> + *
> + * Manufacturer drivers should put all their specific initialization code in
> + * their ->init() hook.
> + */
> +static int spinand_manufacturer_init(struct spinand_device *chip)
> +{
> +	if (chip->manufacturer.manu->ops->init)
> +		return chip->manufacturer.manu->ops->init(chip);
> +
> +	return 0;
> +}
> +
> +/*
> + * spinand_manufacturer_cleanup - manufacturer cleanup function.
> + * @chip: SPI NAND device structure
> + *
> + * Manufacturer drivers should put all their specific cleanup code in their
> + * ->cleanup() hook.
> + */
> +static void spinand_manufacturer_cleanup(struct spinand_device *chip)
> +{
> +	/* Release manufacturer private data */
> +	if (chip->manufacturer.manu->ops->cleanup)
> +		return chip->manufacturer.manu->ops->cleanup(chip);
> +}
> +
> +/*
> + * spinand_dt_init - Initialize SPI NAND by device tree node
> + * @chip: SPI NAND device structure
> + *
> + * TODO: put ecc_mode, ecc_strength, ecc_step, bbt, etc in here
> + * and move it in generic NAND core.
> + */
> +static void spinand_dt_init(struct spinand_device *chip)
> +{
> +}
> +
> +/*
> + * spinand_detect - detect the SPI NAND device
> + * @chip: SPI NAND device structure
> + */
> +static int spinand_detect(struct spinand_device *chip)
> +{
> +	struct nand_device *nand = &chip->base;
> +	int ret;
> +
> +	spinand_reset(chip);
> +	spinand_read_id(chip, chip->id.data);
> +	chip->id.len = SPINAND_MAX_ID_LEN;
> +
> +	ret = spinand_manufacturer_detect(chip);
> +	if (ret) {
> +		pr_err("SPI NAND: unknown raw ID %*phN\n",
> +		       SPINAND_MAX_ID_LEN, chip->id.data);
> +		goto out;
> +	}
> +
> +	pr_info("%s (%s) is found.\n",
> +		chip->name, chip->manufacturer.manu->name);
> +	pr_info("%d MiB, block size: %d KiB, page size: %d, OOB size: %d\n",
> +		(int)(nand_size(nand) >> 20), nand_eraseblock_size(nand) >> 10,
> +		nand_page_size(nand), nand_per_page_oobsize(nand));
> +
> +out:
> +	return ret;
> +}
> +
> +/*
> + * spinand_init - initialize the SPI NAND device
> + * @chip: SPI NAND device structure
> + */
> +static int spinand_init(struct spinand_device *chip)
> +{
> +	struct mtd_info *mtd = spinand_to_mtd(chip);
> +	struct nand_device *nand = mtd_to_nand(mtd);
> +	struct spinand_ecc_engine *ecc_engine;
> +	int ret;
> +
> +	spinand_dt_init(chip);
> +	spinand_set_rd_wr_op(chip);
> +
> +	chip->buf = kzalloc(nand_page_size(nand) + nand_per_page_oobsize(nand),
> +			    GFP_KERNEL);
> +	if (!chip->buf) {
> +		ret = -ENOMEM;
> +		goto err;
> +	}
> +
> +	chip->oobbuf = chip->buf + nand_page_size(nand);
> +
> +	spinand_manufacturer_init(chip);
> +
> +	mtd->name = chip->name;
> +	mtd->size = nand_size(nand);
> +	mtd->erasesize = nand_eraseblock_size(nand);
> +	mtd->writesize = nand_page_size(nand);
> +	mtd->writebufsize = mtd->writesize;
> +	mtd->owner = THIS_MODULE;
> +	mtd->type = MTD_NANDFLASH;
> +	mtd->flags = MTD_CAP_NANDFLASH;
> +	if (!mtd->ecc_strength)
> +		mtd->ecc_strength = ecc_engine->strength ?
> +				    ecc_engine->strength : 1;
> +
> +	mtd->oobsize = nand_per_page_oobsize(nand);
> +	ret = mtd_ooblayout_count_freebytes(mtd);
> +	if (ret < 0)
> +		ret = 0;
> +	mtd->oobavail = ret;
> +
> +	if (!mtd->bitflip_threshold)
> +		mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3,
> +						      4);
> +	/* After power up, all blocks are locked, so unlock it here. */
> +	spinand_lock_block(chip, BL_ALL_UNLOCKED);
> +
> +	return nand_register(nand);
> +
> +err:
> +	return ret;
> +}
> +
> +/*
> + * spinand_alloc - [SPI NAND Interface] allocate SPI NAND device instance
> + * @dev: pointer to device model structure
> + */
> +struct spinand_device *spinand_alloc(struct device *dev)
> +{
> +	struct spinand_device *chip;
> +	struct spinand_ecc_engine *ecc_engine;
> +
> +	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
> +	if (!chip)
> +		goto err1;
> +
> +	ecc_engine = kzalloc(sizeof(*ecc_engine), GFP_KERNEL);
> +	if (!ecc_engine)
> +		goto err2;
> +
> +	ecc_engine->mode = SPINAND_ECC_ONDIE;
> +	chip->ecc.engine = ecc_engine;
> +	spinand_set_of_node(chip, dev->of_node);
> +	mutex_init(&chip->lock);
> +
> +	return chip;
> +
> +err2:
> +	kfree(chip);
> +err1:
> +	return ERR_PTR(-ENOMEM);
> +}
> +EXPORT_SYMBOL_GPL(spinand_alloc);
> +
> +/*
> + * spinand_free - [SPI NAND Interface] free SPI NAND device instance
> + * @chip: SPI NAND device structure
> + */
> +void spinand_free(struct spinand_device *chip)
> +{
> +	kfree(chip->ecc.engine);
> +	kfree(chip);
> +}
> +EXPORT_SYMBOL_GPL(spinand_free);
> +
> +/*
> + * spinand_register - [SPI NAND Interface] register SPI NAND device
> + * @chip: SPI NAND device structure
> + */
> +int spinand_register(struct spinand_device *chip)
> +{
> +	int ret;
> +
> +	ret = spinand_detect(chip);
> +	if (ret) {
> +		pr_err("Detect SPI NAND failed with error %d.\n", ret);
> +		return ret;
> +	}
> +
> +	ret = spinand_init(chip);
> +	if (ret)
> +		pr_err("Init SPI NAND failed with error %d.\n", ret);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(spinand_register);
> +
> +/*
> + * spinand_unregister - [SPI NAND Interface] unregister SPI NAND device
> + * @chip: SPI NAND device structure
> + */
> +int spinand_unregister(struct spinand_device *chip)
> +{
> +	struct nand_device *nand = &chip->base;
> +
> +	nand_unregister(nand);
> +	spinand_manufacturer_cleanup(chip);
> +	kfree(chip->buf);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(spinand_unregister);
> +
> +MODULE_DESCRIPTION("SPI NAND framework");
> +MODULE_AUTHOR("Peter Pan<peterpandong@micron.com>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/mtd/nand/spi/manufactures.c b/drivers/mtd/nand/spi/manufactures.c
> new file mode 100644
> index 0000000..7e0b42d
> --- /dev/null
> +++ b/drivers/mtd/nand/spi/manufactures.c
> @@ -0,0 +1,24 @@
> +/**
> + *
> + * Copyright (c) 2009-2017 Micron Technology, Inc.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/mtd/spinand.h>
> +
> +struct spinand_manufacturer spinand_manufacturer_end = {0x0, "Unknown", NULL};
> +
> +struct spinand_manufacturer *spinand_manufacturers[] = {
> +	&spinand_manufacturer_end,
> +};
> +EXPORT_SYMBOL(spinand_manufacturers);
> diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
> new file mode 100644
> index 0000000..44748b4
> --- /dev/null
> +++ b/include/linux/mtd/spinand.h
> @@ -0,0 +1,270 @@
> +/**
> + *
> + * Copyright (c) 2009-2017 Micron Technology, Inc.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +#ifndef __LINUX_MTD_SPINAND_H
> +#define __LINUX_MTD_SPINAND_H
> +
> +#include <linux/mutex.h>
> +#include <linux/mtd/mtd.h>
> +#include <linux/mtd/nand.h>
> +
> +/*
> + * Standard SPI-NAND flash commands
> + */
> +#define SPINAND_CMD_RESET			0xff
> +#define SPINAND_CMD_GET_FEATURE			0x0f
> +#define SPINAND_CMD_SET_FEATURE			0x1f
> +#define SPINAND_CMD_PAGE_READ			0x13
> +#define SPINAND_CMD_READ_PAGE_CACHE_RDM		0x30
> +#define SPINAND_CMD_READ_PAGE_CACHE_LAST	0x3f
> +#define SPINAND_CMD_READ_FROM_CACHE		0x03
> +#define SPINAND_CMD_READ_FROM_CACHE_FAST	0x0b
> +#define SPINAND_CMD_READ_FROM_CACHE_X2		0x3b
> +#define SPINAND_CMD_READ_FROM_CACHE_DUAL_IO	0xbb
> +#define SPINAND_CMD_READ_FROM_CACHE_X4		0x6b
> +#define SPINAND_CMD_READ_FROM_CACHE_QUAD_IO	0xeb
> +#define SPINAND_CMD_BLK_ERASE			0xd8
> +#define SPINAND_CMD_PROG_EXC			0x10
> +#define SPINAND_CMD_PROG_LOAD			0x02
> +#define SPINAND_CMD_PROG_LOAD_RDM_DATA		0x84
> +#define SPINAND_CMD_PROG_LOAD_X4		0x32
> +#define SPINAND_CMD_PROG_LOAD_RDM_DATA_X4	0x34
> +#define SPINAND_CMD_READ_ID			0x9f
> +#define SPINAND_CMD_WR_DISABLE			0x04
> +#define SPINAND_CMD_WR_ENABLE			0x06
> +

Reading a Macronix datasheet, I see that they also use the "* X2" and "*
X4" for operations names. If all manufacturers have agreed on those
names and it has become the standard, ok,  why not... but personally, I
think names like READ_FROM_CACHE_1_1_4 and READ_FROM_CACHE_1_4_4 would
be more explicit about knowing the actual number of I/O lines for
instruction, address/dummy then data clock cycles.

Anyway, you'd better follow the standard, if it exists, rather than my
personal taste :)

> +/* feature registers */
> +#define REG_BLOCK_LOCK		0xa0
> +#define REG_CFG			0xb0
> +#define REG_STATUS		0xc0
> +#define REG_DIE_SELECT		0xd0
> +
> +/* status */
> +#define STATUS_OIP_MASK		0x01
> +#define STATUS_CRBSY_MASK	0x80
> +#define STATUS_READY		(0 << 0)
> +#define STATUS_BUSY		(1 << 0)
> +
> +#define STATUS_E_FAIL_MASK	0x04
> +#define STATUS_E_FAIL		(1 << 2)
> +
> +#define STATUS_P_FAIL_MASK	0x08
> +#define STATUS_P_FAIL		(1 << 3)
> +
> +/*Configuration register defines*/
> +#define CFG_QE_MASK		0x01
> +#define CFG_QE_ENABLE		0x01
> +#define CFG_ECC_MASK		0x10
> +#define CFG_ECC_ENABLE		0x10
> +#define CFG_LOT_MASK		0x20
> +#define CFG_LOT_ENABLE		0x20
> +#define CFG_OTP_MASK		0xc2
> +#define CFG_OTP_ENTER		0x40
> +#define CFG_OTP_EXIT		0x00

Marek has already told you to use the BIT() macro, so +1 :)

Anyways, I will add that I think that some of those bits are not generic
but vendor specific. For instance, I'm reading a Macronix MX35LF2GE4AB
datasheet: BIT(5) 0x20 is reserved, not "LOT".
BIT(1) 0x02 is reserved as well, hence not belonging to OTP bits.

You should separate standardized bits from those which are vendor
specific, IMHO.

Also, some SPI NAND memories, like SPI NOR, require their Quad Enable
(QE) bit to be set before using any Quad SPI command:

Write Protect and Reset/Hold pins are then reassigned to function IO2
and IO3.

I guess Micron memories don't have such a requirement but for almost all
other vendors, if I understand your series correctly, they need to set
this QE bit from the chip->manufacturer.manu->ops->init(), right?

Just after the choice of the read/write op codes with the associated SPI
1-y-z protocols, so .manu->ops->init() knows whether or not it needs to
set the QE bit.

> +
> +/* block lock */
> +#define BL_ALL_LOCKED		0x7c
> +#define BL_U_1_1024_LOCKED	0x08
> +#define BL_U_1_512_LOCKED	0x10
> +#define BL_U_1_256_LOCKED	0x18
> +#define BL_U_1_128_LOCKED	0x20
> +#define BL_U_1_64_LOCKED	0x28
> +#define BL_U_1_32_LOCKED	0x30
> +#define BL_U_1_16_LOCKED	0x38
> +#define BL_U_1_8_LOCKED		0x40
> +#define BL_U_1_4_LOCKED		0x48
> +#define BL_U_1_2_LOCKED		0x50
> +#define BL_L_1_1024_LOCKED	0x0c
> +#define BL_L_1_512_LOCKED	0x14
> +#define BL_L_1_256_LOCKED	0x1c
> +#define BL_L_1_128_LOCKED	0x24
> +#define BL_L_1_64_LOCKED	0x2c
> +#define BL_L_1_32_LOCKED	0x34
> +#define BL_L_1_16_LOCKED	0x3c
> +#define BL_L_1_8_LOCKED		0x44
> +#define BL_L_1_4_LOCKED		0x4c
> +#define BL_L_1_2_LOCKED		0x54
> +#define BL_ALL_UNLOCKED		0X00
> +
> +/* die select */
> +#define DIE_SELECT_MASK		0x40
> +#define DIE_SELECT_DS0		0x00
> +#define DIE_SELECT_DS1		0x40
> +
> +struct spinand_op;
> +struct spinand_device;
> +
> +#define SPINAND_MAX_ID_LEN	4
> +
> +/**
> + * struct nand_id - NAND id structure
> + * @data: buffer containing the id bytes. Currently 8 bytes large, but can
                                                       ^

SPINAND_MAX_ID_LEN is defined as 4, not 8. Maybe someone else has
already commented on that, if so, sorry for the noise!

> + *	  be extended if required.
> + * @len: ID length.
> + */
> +struct spinand_id {
> +	u8 data[SPINAND_MAX_ID_LEN];
> +	int len;
> +};
> +
> +struct spinand_controller_ops {
> +	int (*exec_op)(struct spinand_device *chip,
> +		       struct spinand_op *op);
> +};
> +
> +struct spinand_manufacturer_ops {
> +	bool (*detect)(struct spinand_device *chip);
> +	int (*init)(struct spinand_device *chip);
> +	void (*cleanup)(struct spinand_device *chip);
> +};
> +
> +struct spinand_manufacturer {
> +	u8 id;
> +	char *name;
> +	const struct spinand_manufacturer_ops *ops;
> +};
> +
> +extern struct spinand_manufacturer *spinand_manufacturers[];
> +
> +struct spinand_ecc_engine_ops {
> +	void (*get_ecc_status)(struct spinand_device *chip,
> +			       unsigned int status, unsigned int *corrected,
> +			       unsigned int *ecc_errors);
> +	void (*disable_ecc)(struct spinand_device *chip);
> +	void (*enable_ecc)(struct spinand_device *chip);
> +};
> +
> +enum spinand_ecc_mode {
> +	SPINAND_ECC_ONDIE,
> +	SPINAND_ECC_HW,
> +};
> +
> +struct spinand_ecc_engine {
> +	enum spinand_ecc_mode mode;
> +	u32 strength;
> +	u32 steps;
> +	struct spinand_ecc_engine_ops *ops;
> +};
> +
> +#define SPINAND_CAP_RD_X1 BIT(0)
> +#define SPINAND_CAP_RD_X2 BIT(1)
> +#define SPINAND_CAP_RD_X4 BIT(2)
> +#define SPINAND_CAP_RD_DUAL BIT(3)
> +#define SPINAND_CAP_RD_QUAD BIT(4)
> +#define SPINAND_CAP_WR_X1 BIT(5)
> +#define SPINAND_CAP_WR_X2 BIT(6)
> +#define SPINAND_CAP_WR_X4 BIT(7)
> +#define SPINAND_CAP_WR_DUAL BIT(8)
> +#define SPINAND_CAP_WR_QUAD BIT(9)
> +#define SPINAND_CAP_HW_ECC BIT(10)
> +
> +struct spinand_controller {
> +	struct spinand_controller_ops *ops;
> +	u32 caps;
> +};
> +
> +/**
> + * struct spinand_device - SPI-NAND Private Flash Chip Data
> + * @base: NAND device instance
> + * @lock: protection lock
> + * @name: name of the chip
> + * @id: ID structure
> + * @read_cache_op: Opcode of read from cache
> + * @write_cache_op: Opcode of program load
> + * @buf: buffer for read/write data
> + * @oobbuf: buffer for read/write oob
> + * @rw_mode: read/write mode of SPI NAND chip
> + * @controller: SPI NAND controller instance
> + * @manufacturer: SPI NAND manufacturer instance, describe
> + *                manufacturer related objects
> + * @ecc_engine: SPI NAND ECC engine instance
> + */
> +struct spinand_device {
> +	struct nand_device base;
> +	struct mutex lock;
> +	char *name;
> +	struct spinand_id id;
> +	u8 read_cache_op;
> +	u8 write_cache_op;
> +	u8 *buf;
> +	u8 *oobbuf;
> +	u32 rw_mode;
> +	struct {
> +		struct spinand_controller *controller;
> +		void *priv;
> +	} controller;
> +	struct {
> +		const struct spinand_manufacturer *manu;
> +		void *priv;
> +	} manufacturer;
> +	struct {
> +		struct spinand_ecc_engine *engine;
> +		void *context;
> +	} ecc;
> +};
> +
> +static inline struct spinand_device *mtd_to_spinand(struct mtd_info *mtd)
> +{
> +	return container_of(mtd_to_nand(mtd), struct spinand_device, base);
> +}
> +
> +static inline struct mtd_info *spinand_to_mtd(struct spinand_device *chip)
> +{
> +	return nand_to_mtd(&chip->base);
> +}
> +
> +static inline void spinand_set_of_node(struct spinand_device *chip,
> +				       struct device_node *np)
> +{
> +	nand_set_of_node(&chip->base, np);
> +}
> +
> +#define SPINAND_MAX_ADDR_LEN	4
> +
> +struct spinand_op {
> +	u8 cmd;
> +	u8 n_addr;
> +	u8 addr_nbits;
> +	u8 dummy_bytes;
> +	u8 addr[SPINAND_MAX_ADDR_LEN];
> +	u32 n_tx;
> +	const u8 *tx_buf;
> +	u32 n_rx;
> +	u8 *rx_buf;
> +	u8 data_nbits;
> +};

You should add a comment like you did with the 'struct spinand_device'
describing each member. I guess it can be understood for those who often
work with (Q)SPI flash memories but for others I think some comments
could help!

> +
> +/* SPI NAND supported OP mode */
> +#define SPINAND_RD_X1		0x00000001
> +#define SPINAND_RD_X2		0x00000002
> +#define SPINAND_RD_X4		0x00000004
> +#define SPINAND_RD_DUAL		0x00000008
> +#define SPINAND_RD_QUAD		0x00000010
> +#define SPINAND_WR_X1		0x00000020
> +#define SPINAND_WR_X2		0x00000040
> +#define SPINAND_WR_X4		0x00000080
> +#define SPINAND_WR_DUAL		0x00000100
> +#define SPINAND_WR_QUAD		0x00000200
> +
> +#define SPINAND_RD_COMMON	(SPINAND_RD_X1 | SPINAND_RD_X2 | \
> +				 SPINAND_RD_X4 | SPINAND_RD_DUAL | \
> +				 SPINAND_RD_QUAD)
> +#define SPINAND_WR_COMMON	(SPINAND_WR_X1 | SPINAND_WR_X4)
> +#define SPINAND_OP_COMMON	(SPINAND_RD_COMMON | SPINAND_WR_COMMON)
> +
> +struct spinand_device *spinand_alloc(struct device *dev);
> +void spinand_free(struct spinand_device *chip);
> +int spinand_register(struct spinand_device *chip);
> +int spinand_unregister(struct spinand_device *chip);
> +#endif /* __LINUX_MTD_SPINAND_H */
> 

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 1/9] mtd: nand: add oob iterator in nand_for_each_page
  2017-03-29 19:34   ` Boris Brezillon
@ 2017-03-30  8:01     ` Peter Pan
  2017-03-30  8:34       ` Boris Brezillon
  0 siblings, 1 reply; 36+ messages in thread
From: Peter Pan @ 2017-03-30  8:01 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Peter Pan, Richard Weinberger, Brian Norris, Arnaud Mouiche,
	Thomas Petazzoni, Marek Vasut, Cyrille Pitchen, linux-mtd,
	linshunquan (A)

Hi Boris,

On Thu, Mar 30, 2017 at 3:34 AM, Boris Brezillon
<boris.brezillon@free-electrons.com> wrote:
> On Thu, 23 Mar 2017 17:43:38 +0800
> Peter Pan <peterpandong@micron.com> wrote:
>
>> Iterate nand pages by both page and oob operation.
>
> For the next version, can you merge my initial commit [1] into this one
> (you can keep the authorship)?

OK. I will merge it. But I'd like to keep you as author since it's your idea:)

>
>>
>> Signed-off-by: Peter Pan <peterpandong@micron.com>
>> ---
>>  include/linux/mtd/nand.h | 38 +++++++++++++++++++++++++++++++-------
>>  1 file changed, 31 insertions(+), 7 deletions(-)
>>
>> diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
>> index c2197b4..54ded4c 100644
>> --- a/include/linux/mtd/nand.h
>> +++ b/include/linux/mtd/nand.h
>> @@ -84,9 +84,12 @@ struct nand_device {
>>   * @pageoffset: the offset within a page
>
> Update the documentation.

Fix this in v5

>
>>   */
>>  struct nand_page_iter {
>> -     loff_t offs;
>>       int page;
>>       int pageoffs;
>> +     size_t dataleft;
>> +     int ooboffs;
>> +     int oobsize;
>
> oobsize can be extracted from nand_device, and since a nand_device
> object it passed to all nand_page_iter_xxx() functions you don't need it
> here.

Er.. I think we can not get oobsize from nand_device, since oobsize is
determined
by mtd_oob_ops.mode, it can be mtd->oobavail or mtd->oobsize. What do
you think Boris?

>
>> +     size_t oobleft;
>>  };
>>
>>  /**
>> @@ -193,14 +196,19 @@ static inline int nand_per_page_oobsize(struct nand_device *nand)
>>   * @offs: absolute offset
>>   * @iter: page iterator
>
> Update the documentation.

Fix this in v5

>
>>   */
>> -static inline void nand_page_iter_init(struct nand_device *nand, loff_t offs,
>> +static inline void nand_page_iter_init(struct nand_device *nand,
>> +                                    loff_t offs, size_t len, u32 ooboffs,
>> +                                    size_t ooblen, u32 oobsize,
>>                                      struct nand_page_iter *iter)
>>  {
>>       u64 page = offs;
>>
>>       iter->pageoffs = do_div(page, nand->memorg.pagesize);
>>       iter->page = page;
>> -     iter->offs = offs;
>> +     iter->dataleft = len;
>> +     iter->ooboffs = ooboffs;
>> +     iter->oobsize = oobsize;
>> +     iter->oobleft = ooblen;
>>  }
>>
>>  /**
>> @@ -212,13 +220,29 @@ static inline void nand_page_iter_next(struct nand_device *nand,
>>                                      struct nand_page_iter *iter)
>>  {
>>       iter->page++;
>> -     iter->offs += nand_page_size(nand) - iter->pageoffs;
>>       iter->pageoffs = 0;
>> +     if (iter->dataleft)
>> +             iter->dataleft -= min_t (int,
>> +                                      nand_page_size(nand) - iter->pageoffs,
>> +                                      iter->dataleft);
>> +     if (iter->oobleft)
>> +             iter->oobleft -= min_t(int, iter->oobsize - iter->ooboffs,
>> +                                    iter->oobleft);
>> +}
>> +
>
> Document the function.

Fix this in v5


Thanks,
Peter Pan

>
>> +static inline bool nand_page_iter_end(struct nand_device *nand,
>> +                                   struct nand_page_iter *iter)
>> +{
>> +     if (iter->dataleft || iter->oobleft)
>> +             return false;
>> +     return true;
>>  }
>>
>> -#define nand_for_each_page(nand, start, len, iter)           \
>> -     for (nand_page_iter_init(nand, start, iter);            \
>> -          (iter)->offs < (start) + (len);                    \
>> +#define nand_for_each_page(nand, start, len, ooboffs, ooblen,        \
>> +                        oobsize, iter)       \
>> +     for (nand_page_iter_init(nand, start, len, ooboffs,     \
>> +                              ooblen, oobsize, iter);        \
>> +          !nand_page_iter_end(nand, iter);           \
>>            nand_page_iter_next(nand, iter))
>>
>>  /**
>
> [1]https://github.com/bbrezillon/linux-0day/commit/f02767a662ec02a0206a0b6c5a410a38d7b7b313

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 3/9] mtd: nand: add more helpers in nand.h
  2017-03-29 19:57   ` Boris Brezillon
@ 2017-03-30  8:04     ` Peter Pan
  2017-03-30  8:40       ` Boris Brezillon
  0 siblings, 1 reply; 36+ messages in thread
From: Peter Pan @ 2017-03-30  8:04 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Peter Pan, Richard Weinberger, Brian Norris, Arnaud Mouiche,
	Thomas Petazzoni, Marek Vasut, Cyrille Pitchen, linux-mtd,
	linshunquan (A)

Hi Boris,

On Thu, Mar 30, 2017 at 3:57 AM, Boris Brezillon
<boris.brezillon@free-electrons.com> wrote:
> On Thu, 23 Mar 2017 17:43:40 +0800
> Peter Pan <peterpandong@micron.com> wrote:
>
>> This commit adds some helpers in nand.h
>>     nand_size()
>>     nand_check_address()
>>     nand_check_oob_ops()
>>     nand_oob_ops_across_page()
>>     nand_check_erase_ops()
>>
>> Signed-off-by: Peter Pan <peterpandong@micron.com>
>> ---
>>  include/linux/mtd/nand.h | 62 ++++++++++++++++++++++++++++++++++++++++++++++++
>>  1 file changed, 62 insertions(+)
>>
>> diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
>> index 54ded4c..0c52401 100644
>> --- a/include/linux/mtd/nand.h
>> +++ b/include/linux/mtd/nand.h
>> @@ -434,6 +434,68 @@ static inline int nand_neraseblocks(struct nand_device *nand)
>>  }
>>
>>  /**
>> + * nand_size - Get NAND size
>> + * @nand: NAND device
>> + *
>> + * Returns the total size exposed by @nand.
>> + */
>> +static inline u64 nand_size(struct nand_device *nand)
>> +{
>> +     return nand->memorg.ndies * nand->memorg.diesize;
>> +}
>> +
>
> The nand_size() function should probably go in the commit introducing
> the interface-agnostic NAND layer (in my own series).
>
> Since you'll be the first one to use the generic NAND layer, I propose
> that you start maintaining my patch-set along with your SPI NAND
> patches.

You mean that I send your patch-set with SPI NAND patches? If so, you want
me to do this in v5 or when SPI NAND patches is ready to merge?

Thanks,
Peter Pan

>
>> +static inline int nand_check_address(struct nand_device *nand, loff_t addr)
>> +{
>> +     return addr < nand_size(nand) ? 0 : -EINVAL;
>> +}
>> +
>> +static inline int nand_check_oob_ops(struct nand_device *nand, loff_t start,
>> +                                  struct mtd_oob_ops *ops)
>> +{
>> +     struct mtd_info *mtd = nand_to_mtd(nand);
>> +     int ooblen = ops->mode == MTD_OPS_AUTO_OOB ?
>> +             mtd->oobavail : mtd->oobsize;
>> +
>> +     if ((!!ops->datbuf != !!ops->len) ||
>> +         (!!ops->oobbuf != !!ops->ooblen))
>> +             return -EINVAL;
>> +     if (ops->ooboffs >= ooblen)
>> +             return -EINVAL;
>> +     if (ops->ooboffs + ops->ooblen >
>> +         (nand_len_to_pages(nand, nand_size(nand)) -
>> +                            nand_offs_to_page(nand, start)) * ooblen)
>> +             return -EINVAL;
>> +
>> +     return 0;
>> +}
>> +
>> +static inline bool nand_oob_ops_across_page(struct nand_device *nand,
>> +                                         struct mtd_oob_ops *ops)
>> +{
>> +     struct mtd_info *mtd = nand_to_mtd(nand);
>> +     int ooblen = ops->mode == MTD_OPS_AUTO_OOB ?
>> +                  mtd->oobavail : mtd->oobsize;
>> +
>> +     return (ops->ooboffs + ops->ooblen) > ooblen;
>> +}
>> +
>> +static inline int nand_check_erase_ops(struct nand_device *nand,
>> +                                    struct erase_info *einfo)
>> +{
>> +     /* check address align on block boundary */
>> +     if (einfo->addr & (nand_eraseblock_size(nand) - 1))
>> +             return -EINVAL;
>> +     /* check lendth align on block boundary */
>> +     if (einfo->len & (nand_eraseblock_size(nand) - 1))
>> +             return -EINVAL;
>> +     /* Do not allow erase past end of device */
>> +     if ((einfo->addr + einfo->len) > nand_size(nand))
>> +             return -EINVAL;
>> +
>> +     return 0;
>> +}
>
> Hm, looking at these functions and the tests already done in mtdcore.c
> I'm not sure this is the best location.
> AFAICS, all these tests can be done (or are already done) at the MTD
> level, and that's where they are the most useful. The only exception I
> see where it could be useful is when the BBT code directly calls nand
> functions instead of mtd ones, but this should not happen that much.
>
>> +
>> +/**
>>   * nand_register - Register a NAND device
>>   * @nand: NAND device
>>   *
>

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 8/9] nand: spi: Add generic SPI controller support
  2017-03-29 21:37   ` Cyrille Pitchen
@ 2017-03-30  8:28     ` Peter Pan
  0 siblings, 0 replies; 36+ messages in thread
From: Peter Pan @ 2017-03-30  8:28 UTC (permalink / raw)
  To: Cyrille Pitchen
  Cc: Peter Pan, Boris Brezillon, Richard Weinberger, Brian Norris,
	Arnaud Mouiche, Thomas Petazzoni, Marek Vasut, Cyrille Pitchen,
	linux-mtd, linshunquan (A)

Hi Cyrille,

First of all, thank you for your valuable comments! It is really needed by me!

On Thu, Mar 30, 2017 at 5:37 AM, Cyrille Pitchen
<cyrille.pitchen@wedev4u.fr> wrote:
> Hi Peter,
>
> Le 23/03/2017 à 10:43, Peter Pan a écrit :
>> This commit supports to use generic spi controller
>> as spi nand controller.
>>
>> Signed-off-by: Peter Pan <peterpandong@micron.com>
>> ---
>>  drivers/mtd/nand/spi/Kconfig                   |   2 +
>>  drivers/mtd/nand/spi/Makefile                  |   1 +
>>  drivers/mtd/nand/spi/controllers/Kconfig       |   5 +
>>  drivers/mtd/nand/spi/controllers/Makefile      |   1 +
>>  drivers/mtd/nand/spi/controllers/generic-spi.c | 150 +++++++++++++++++++++++++
>>  5 files changed, 159 insertions(+)
>>  create mode 100644 drivers/mtd/nand/spi/controllers/Kconfig
>>  create mode 100644 drivers/mtd/nand/spi/controllers/Makefile
>>  create mode 100644 drivers/mtd/nand/spi/controllers/generic-spi.c
>>
>> diff --git a/drivers/mtd/nand/spi/Kconfig b/drivers/mtd/nand/spi/Kconfig
>> index d77c46e..6bd1c65 100644
>> --- a/drivers/mtd/nand/spi/Kconfig
>> +++ b/drivers/mtd/nand/spi/Kconfig
>> @@ -3,3 +3,5 @@ menuconfig MTD_SPI_NAND
>>       depends on MTD_NAND
>>       help
>>         This is the framework for the SPI NAND device drivers.
>> +
>> +source "drivers/mtd/nand/spi/controllers/Kconfig"
>> diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile
>> index db0b91b..6ad5f24 100644
>> --- a/drivers/mtd/nand/spi/Makefile
>> +++ b/drivers/mtd/nand/spi/Makefile
>> @@ -1,3 +1,4 @@
>>  obj-$(CONFIG_MTD_SPI_NAND) += core.o
>>  obj-$(CONFIG_MTD_SPI_NAND) += manufactures.o
>>  obj-$(CONFIG_MTD_SPI_NAND) += micron.o
>> +obj-$(CONFIG_MTD_SPI_NAND) += controllers/
>> diff --git a/drivers/mtd/nand/spi/controllers/Kconfig b/drivers/mtd/nand/spi/controllers/Kconfig
>> new file mode 100644
>> index 0000000..8ab7023
>> --- /dev/null
>> +++ b/drivers/mtd/nand/spi/controllers/Kconfig
>> @@ -0,0 +1,5 @@
>> +config GENERIC_SPI_NAND
>> +     tristate "SPI NAND with generic SPI bus Support"
>> +     depends on MTD_SPI_NAND && SPI
>> +     help
>> +       This is to support SPI NAND device with generic SPI bus.
>> diff --git a/drivers/mtd/nand/spi/controllers/Makefile b/drivers/mtd/nand/spi/controllers/Makefile
>> new file mode 100644
>> index 0000000..46cbf29
>> --- /dev/null
>> +++ b/drivers/mtd/nand/spi/controllers/Makefile
>> @@ -0,0 +1 @@
>> +obj-$(CONFIG_GENERIC_SPI_NAND) += generic-spi.o
>> diff --git a/drivers/mtd/nand/spi/controllers/generic-spi.c b/drivers/mtd/nand/spi/controllers/generic-spi.c
>> new file mode 100644
>> index 0000000..da1f224
>> --- /dev/null
>> +++ b/drivers/mtd/nand/spi/controllers/generic-spi.c
>> @@ -0,0 +1,150 @@
>> +/*
>> + * Copyright (c) 2009-2017 Micron Technology, Inc.
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * as published by the Free Software Foundation; either version 2
>> + * of the License, or (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/spi/spi.h>
>> +#include <linux/mtd/spinand.h>
>> +
>> +struct gen_spi_spinand_controller {
>> +     struct spinand_controller ctrl;
>> +     struct spi_device *spi;
>> +};
>> +
>> +#define to_gen_spi_spinand_controller(c) \
>> +     container_of(c, struct gen_spi_spinand_controller, ctrl)
>> +
>> +/*
>> + * gen_spi_spinand_exec_op - to process a command to send to the
>> + * SPI NAND by generic SPI bus
>> + * @chip: SPI NAND device structure
>> + * @op: SPI NAND operation descriptor
>> + */
>> +static int gen_spi_spinand_exec_op(struct spinand_device *chip,
>> +                                struct spinand_op *op)
>> +{
>> +     struct spi_message message;
>> +     struct spi_transfer x[3];
>> +     struct spinand_controller *scontroller = chip->controller.controller;
>> +     struct gen_spi_spinand_controller *controller;
>> +
>> +     controller = to_gen_spi_spinand_controller(scontroller);
>> +     spi_message_init(&message);
>> +     memset(x, 0, sizeof(x));
>> +     x[0].len = 1;
>> +     x[0].tx_nbits = 1;
>> +     x[0].tx_buf = &op->cmd;
>> +     spi_message_add_tail(&x[0], &message);
>> +
>> +     if (op->n_addr + op->dummy_bytes) {
>> +             x[1].len = op->n_addr + op->dummy_bytes;
>> +             x[1].tx_nbits = op->addr_nbits;
>> +             x[1].tx_buf = op->addr;
>> +             spi_message_add_tail(&x[1], &message);
>> +     }
>> +     if (op->n_tx) {
>> +             x[2].len = op->n_tx;
>> +             x[2].tx_nbits = op->data_nbits;
>> +             x[2].tx_buf = op->tx_buf;
>> +             spi_message_add_tail(&x[2], &message);
>> +     } else if (op->n_rx) {
>> +             x[2].len = op->n_rx;
>> +             x[2].rx_nbits = op->data_nbits;
>> +             x[2].rx_buf = op->rx_buf;
>> +             spi_message_add_tail(&x[2], &message);
>> +     }
>> +     return spi_sync(controller->spi, &message);
>> +}
>
> 2 comments:
>
> 1/
> maybe you can merge 'struct spi_transfer' in some cases:
>
> for instance with SPI x-x-z protocols, you can carry both the
> instruction and address/dummy bytes in a single spi_transfer structure.
>
> Also for Read operations (not applicable for Write/Page Program), with
> SPI x-z-z, you can merge both the address/dummy and data bytes in a
> single spi_transfer structure.
>
> This could speed-up the operation with some SPI controller drivers.
> However this is just an optimization otherwise it should work as is.

Yes, I agree the less spi_transfer, the better performance. The reason I didn't
to do the spi_transfer merge is this requires extra buffer to hold the
merged data.
And this patches is focus on the basic structure of SPI NAND core, I'd like to
do the optimization later.

>
> 2/
> Some spi controller drivers, like the TI QSPI controller driver, may
> want to use something similar to the spi_flash_read() function used from
> drivers/mtd/spi-nor/m25p80.c.
>
> Indeed, some QSPI controllers can speed up read operations by mapping
> the flash data array into the system memory. In such a case, the regular
> SPI API (spi_message and spi_transfer structures) is no longer used.
> That's why spi_flash_read() was introduced.
>
> However unlike SPI-NOR memories, SPI-NAND memories require to read data
> in 2 steps: internal data array -> internal cache then internal cache ->
> SPI bus. Hence using a mapping in the system memory only for "read from
> cache" operations is likely less interesting than reading the whole data
> array like regular memory as we can do with SPI NOR.
>
> This is another optimization, so not mandatory at all, but I just wanted
> to warn you about that so be prepared to be queried about that ;)

Thanks for your notice again, very useful!

>
>
>
>> +
>> +static struct spinand_controller_ops gen_spi_spinand_ops = {
>> +     .exec_op = gen_spi_spinand_exec_op,
>> +};
>> +
>> +static int gen_spi_spinand_probe(struct spi_device *spi)
>> +{
>> +     struct spinand_device *chip;
>> +     struct gen_spi_spinand_controller *controller;
>> +     struct spinand_controller *spinand_controller;
>> +     int ret;
>> +     u32 max_speed_hz = spi->max_speed_hz;
>> +
>> +     chip = spinand_alloc(&spi->dev);
>> +     if (IS_ERR(chip)) {
>> +             ret = PTR_ERR(chip);
>> +             goto err1;
>> +     }
>> +     controller = kzalloc(sizeof(*controller), GFP_KERNEL);
>> +     if (!controller) {
>> +             ret = -ENOMEM;
>> +             goto err2;
>> +     }
>> +     controller->spi = spi;
>> +     spinand_controller = &controller->ctrl;
>> +     spinand_controller->ops = &gen_spi_spinand_ops;
>> +     spinand_controller->caps = SPINAND_CAP_RD_X1 | SPINAND_CAP_WR_X1;
>> +     if (spi->mode & SPI_RX_QUAD)
>> +             spinand_controller->caps |= SPINAND_CAP_RD_QUAD |
>> +                                         SPINAND_CAP_RD_X4;
>> +     if (spi->mode & SPI_RX_DUAL)
>> +             spinand_controller->caps |= SPINAND_CAP_RD_DUAL |
>> +                                         SPINAND_CAP_RD_X2;
>> +     if (spi->mode & SPI_TX_QUAD)
>> +             spinand_controller->caps |= SPINAND_CAP_WR_QUAD |
>> +                                         SPINAND_CAP_WR_X4;
>> +     if (spi->mode & SPI_TX_DUAL)
>> +             spinand_controller->caps |= SPINAND_CAP_WR_DUAL |
>> +                                         SPINAND_CAP_WR_X2;
>
> I think this is wrong:
> the SPI_RX_QUAD means the controller can receive data on 4 I/O line
> hence it allows SPINAND_CAP_RD_X4 (1-1-4) but not necessarily
> SPINAND_CAP_QUAD (1-4-4).
>
> To add SPINAND_CAP_QUAD (1-4-4), SPINAND_CAP_WR_X4 (1-1-4) and
> SPINAND_CAP_WR_QUAD (1-4-4) you need both SPI_RX_QUAD and SPI_TX_QUAD.
>
> So with Dual SPI protocols:
>
> at least SPI_RX_DUAL: caps = RD_X2
> both SPI_RX_DUAL and SPI_TX_DUAL: caps |= (RD_DUAL | WR_X2 | WR_DUAL);
>
> SPI masters (controllers) are not aware of SPI x-y-z flash protocols,
> they just claim they can use more than 1 I/O line to read data and/or
> more than 1 I/0 line to write data.

Yes, you are right. I misunderstood the meaning of SPI_RX_QUAD. I will
fix this in v5

Thanks,
Peter Pan

>
>> +     chip->controller.controller = spinand_controller;
>> +     spi_set_drvdata(spi, chip);
>> +     spi->max_speed_hz = min_t(int, 25000000, max_speed_hz);
>> +
>> +     ret = spinand_register(chip);
>> +     if (ret)
>> +             goto err3;
>> +
>> +     spi->max_speed_hz = max_speed_hz;
>> +
>> +     return 0;
>> +
>> +err3:
>> +     kfree(controller);
>> +err2:
>> +     spinand_free(chip);
>> +err1:
>> +     return ret;
>> +}
>> +
>> +static int gen_spi_spinand_remove(struct spi_device *spi)
>> +{
>> +     struct spinand_device *chip = spi_get_drvdata(spi);
>> +
>> +     spinand_unregister(chip);
>> +     kfree(to_gen_spi_spinand_controller(chip->controller.controller));
>> +     spinand_free(chip);
>> +
>> +     return 0;
>> +}
>> +
>> +static struct spi_driver gen_spi_spinand_driver = {
>> +     .driver = {
>> +             .name   = "generic_spinand",
>> +             .owner  = THIS_MODULE,
>> +     },
>> +     .probe  = gen_spi_spinand_probe,
>> +     .remove = gen_spi_spinand_remove,
>> +};
>> +module_spi_driver(gen_spi_spinand_driver);
>> +
>> +MODULE_DESCRIPTION("Generic SPI controller to support SPI NAND");
>> +MODULE_AUTHOR("Peter Pan<peterpandong@micron.com>");
>> +MODULE_LICENSE("GPL v2");
>>
>

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 1/9] mtd: nand: add oob iterator in nand_for_each_page
  2017-03-30  8:01     ` Peter Pan
@ 2017-03-30  8:34       ` Boris Brezillon
  0 siblings, 0 replies; 36+ messages in thread
From: Boris Brezillon @ 2017-03-30  8:34 UTC (permalink / raw)
  To: Peter Pan
  Cc: Peter Pan, Richard Weinberger, Brian Norris, Arnaud Mouiche,
	Thomas Petazzoni, Marek Vasut, Cyrille Pitchen, linux-mtd,
	linshunquan (A)

On Thu, 30 Mar 2017 16:01:04 +0800
Peter Pan <peterpansjtu@gmail.com> wrote:
> 
> >  
> >>   */
> >>  struct nand_page_iter {
> >> -     loff_t offs;
> >>       int page;
> >>       int pageoffs;
> >> +     size_t dataleft;
> >> +     int ooboffs;
> >> +     int oobsize;  
> >
> > oobsize can be extracted from nand_device, and since a nand_device
> > object it passed to all nand_page_iter_xxx() functions you don't need it
> > here.  
> 
> Er.. I think we can not get oobsize from nand_device, since oobsize is
> determined
> by mtd_oob_ops.mode, it can be mtd->oobavail or mtd->oobsize. What do
> you think Boris?

Correct, I forgot about this aspect. Ignore my previous comment and try
to find a better name for this field (+ document it). How about
oobbytes_per_page?

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 3/9] mtd: nand: add more helpers in nand.h
  2017-03-30  8:04     ` Peter Pan
@ 2017-03-30  8:40       ` Boris Brezillon
  0 siblings, 0 replies; 36+ messages in thread
From: Boris Brezillon @ 2017-03-30  8:40 UTC (permalink / raw)
  To: Peter Pan
  Cc: Peter Pan, Richard Weinberger, Brian Norris, Arnaud Mouiche,
	Thomas Petazzoni, Marek Vasut, Cyrille Pitchen, linux-mtd,
	linshunquan (A)

On Thu, 30 Mar 2017 16:04:44 +0800
Peter Pan <peterpansjtu@gmail.com> wrote:

> Hi Boris,
> 
> On Thu, Mar 30, 2017 at 3:57 AM, Boris Brezillon
> <boris.brezillon@free-electrons.com> wrote:
> > On Thu, 23 Mar 2017 17:43:40 +0800
> > Peter Pan <peterpandong@micron.com> wrote:
> >  
> >> This commit adds some helpers in nand.h
> >>     nand_size()
> >>     nand_check_address()
> >>     nand_check_oob_ops()
> >>     nand_oob_ops_across_page()
> >>     nand_check_erase_ops()
> >>
> >> Signed-off-by: Peter Pan <peterpandong@micron.com>
> >> ---
> >>  include/linux/mtd/nand.h | 62 ++++++++++++++++++++++++++++++++++++++++++++++++
> >>  1 file changed, 62 insertions(+)
> >>
> >> diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
> >> index 54ded4c..0c52401 100644
> >> --- a/include/linux/mtd/nand.h
> >> +++ b/include/linux/mtd/nand.h
> >> @@ -434,6 +434,68 @@ static inline int nand_neraseblocks(struct nand_device *nand)
> >>  }
> >>
> >>  /**
> >> + * nand_size - Get NAND size
> >> + * @nand: NAND device
> >> + *
> >> + * Returns the total size exposed by @nand.
> >> + */
> >> +static inline u64 nand_size(struct nand_device *nand)
> >> +{
> >> +     return nand->memorg.ndies * nand->memorg.diesize;
> >> +}
> >> +  
> >
> > The nand_size() function should probably go in the commit introducing
> > the interface-agnostic NAND layer (in my own series).
> >
> > Since you'll be the first one to use the generic NAND layer, I propose
> > that you start maintaining my patch-set along with your SPI NAND
> > patches.  
> 
> You mean that I send your patch-set with SPI NAND patches? If so, you want
> me to do this in v5 or when SPI NAND patches is ready to merge?

No need to send the "interface-agnostic NAND layer" patches in future
versions of this series, let's wait until everyone is happy with the SPI
NAND patches.
But you can already create your own branch and maintain my patches
(rebase on newer kernel versions or nand/next if needed, squash changes
into existing commits if it makes sense, ...). It's also easier for
people who want to test your patches to have a branch containing
everything.

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 0/9] Introduction to SPI NAND framework
  2017-03-23  9:43 [PATCH v4 0/9] Introduction to SPI NAND framework Peter Pan
                   ` (8 preceding siblings ...)
  2017-03-23  9:43 ` [PATCH v4 9/9] MAINTAINERS: Add SPI NAND entry Peter Pan
@ 2017-03-30 12:17 ` Arnaud Mouiche
  2017-04-10  7:33   ` Peter Pan
  9 siblings, 1 reply; 36+ messages in thread
From: Arnaud Mouiche @ 2017-03-30 12:17 UTC (permalink / raw)
  To: Peter Pan, boris.brezillon, richard, computersforpeace,
	thomas.petazzoni, marex, cyrille.pitchen, linux-mtd
  Cc: peterpansjtu, linshunquan1

Hi all.

On 23/03/2017 10:43, Peter Pan wrote:
> v4 also changes a lot than v3, thanks for Boris's and Arnaud's
> valuable comments! The biggest change is "add spinand_alloc(),
> spinand_free(), spinand_register(), spinand_unregister() interfaces
> and make old interface - spinand_detect(), spinand_init() static".
> While this is just a beginning. more effort is needed to make clear
> layer. I really hope for comments on this part.
>
> This series introductes a SPI NAND framework.
> SPI NAND is a new NAND family device with SPI protocol as
> its interface. And its command set is totally different
> with parallel NAND.
[...]

Get a chance to test this serie today on MT29F1G01AAADD (not receiving 
the MT29F2G01ABAGD yet).

Except one bug I found that I will detail directly in the patch series:
- detection is working
- mtd operation with nanddump / nandwrite / flash_erase are ok
- mounting ubifs partition + basic write/read are ok.

Arnaud

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 4/9] nand: spi: add basic blocks for infrastructure
  2017-03-23 16:33       ` Marek Vasut
@ 2017-03-30 12:25         ` Arnaud Mouiche
  2017-03-30 12:52           ` Boris Brezillon
  0 siblings, 1 reply; 36+ messages in thread
From: Arnaud Mouiche @ 2017-03-30 12:25 UTC (permalink / raw)
  To: Marek Vasut, Boris Brezillon
  Cc: Peter Pan, richard, computersforpeace, thomas.petazzoni,
	cyrille.pitchen, linux-mtd, peterpansjtu, linshunquan1



On 23/03/2017 17:33, Marek Vasut wrote:
> On 03/23/2017 04:40 PM, Boris Brezillon wrote:
>> On Thu, 23 Mar 2017 12:29:58 +0100
>> Marek Vasut <marex@denx.de> wrote:
>>
>>
>>>> +
>>>> +/*
>>>> + * spinand_read_status - get status register value
>>>> + * @chip: SPI NAND device structure
>>>> + * @status: buffer to store value
>>>> + * Description:
>>>> + *   After read, write, or erase, the NAND device is expected to set the
>>>> + *   busy status.
>>>> + *   This function is to allow reading the status of the command: read,
>>>> + *   write, and erase.
>>>> + */
>>>> +static int spinand_read_status(struct spinand_device *chip, uint8_t *status)
>>>> +{
>>>> +	return spinand_read_reg(chip, REG_STATUS, status);
>>>> +}
>>>> +
>>>> +/*
>>>> + * spinand_wait - wait until the command is done
>>>> + * @chip: SPI NAND device structure
>>>> + * @s: buffer to store status register value (can be NULL)
>>>> + */
>>>> +static int spinand_wait(struct spinand_device *chip, u8 *s)
>>>> +{
>>>> +	unsigned long timeo = msecs_to_jiffies(400);
>>>> +	u8 status;
>>>> +
>>>> +	do {
>>>> +		spinand_read_status(chip, &status);
>>>> +		if ((status & STATUS_OIP_MASK) == STATUS_READY)
>>>> +			goto out;
>>>> +	} while (time_before(jiffies, timeo));
>>>> +
>>>> +	/*
>>>> +	 * Extra read, just in case the STATUS_READY bit has changed
>>>> +	 * since our last check
>>>> +	 */
>>> Is this likely to happen ? Probably not ... so in case you reach this
>>> place here, timeout happened.
>> If the timeout is big enough, no. But it does not hurt to do one last
>> check.
> 400 mSec is not big enough ? It's huge ... this seems like a duplication
> to me. btw the ad-hoc 400 mSec delay value should be replaced with a macro.
>
In fact, there is a bug:

> unsigned long timeo = msecs_to_jiffies(400);
> [...]
> do { } while (time_before(jiffies, timeo));

The condition is almost true except during the 5 minutes following the boot (before jiffies wrap around).
So, only one status read is done, which give a high chance of returning a timeout status.
I guess you should do : 	

	unsigned long timeo = jiffies + msecs_to_jiffies(400);

I also suspect that this is the reason why you have an additional status read.

Arnaud

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 7/9] nand: spi: add Micron spi nand support
  2017-03-23  9:43 ` [PATCH v4 7/9] nand: spi: add Micron spi nand support Peter Pan
@ 2017-03-30 12:31   ` Arnaud Mouiche
  2017-03-30 12:57     ` Boris Brezillon
  0 siblings, 1 reply; 36+ messages in thread
From: Arnaud Mouiche @ 2017-03-30 12:31 UTC (permalink / raw)
  To: Peter Pan, boris.brezillon, richard, computersforpeace,
	thomas.petazzoni, marex, cyrille.pitchen, linux-mtd
  Cc: peterpansjtu, linshunquan1



On 23/03/2017 10:43, Peter Pan wrote:
> This commit is to add support for Micron MT29F2G01ABAGD
> spi nand chip.
>
> Signed-off-by: Peter Pan <peterpandong@micron.com>
> ---
>   drivers/mtd/nand/spi/Makefile       |   1 +
>   drivers/mtd/nand/spi/manufactures.c |   3 +
>   drivers/mtd/nand/spi/micron.c       | 226 ++++++++++++++++++++++++++++++++++++
>   3 files changed, 230 insertions(+)
>   create mode 100644 drivers/mtd/nand/spi/micron.c
>
> diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile
> index eabdb81..db0b91b 100644
> --- a/drivers/mtd/nand/spi/Makefile
> +++ b/drivers/mtd/nand/spi/Makefile
> @@ -1,2 +1,3 @@
>   obj-$(CONFIG_MTD_SPI_NAND) += core.o
>   obj-$(CONFIG_MTD_SPI_NAND) += manufactures.o
> +obj-$(CONFIG_MTD_SPI_NAND) += micron.o
> diff --git a/drivers/mtd/nand/spi/manufactures.c b/drivers/mtd/nand/spi/manufactures.c
> index 7e0b42d..40dae11 100644
> --- a/drivers/mtd/nand/spi/manufactures.c
> +++ b/drivers/mtd/nand/spi/manufactures.c
> @@ -16,9 +16,12 @@
>   #include <linux/module.h>
>   #include <linux/mtd/spinand.h>
>   
> +extern struct spinand_manufacturer micron_spinand_manufacture;
> +
>   struct spinand_manufacturer spinand_manufacturer_end = {0x0, "Unknown", NULL};
>   
>   struct spinand_manufacturer *spinand_manufacturers[] = {
> +	&micron_spinand_manufacture,
>   	&spinand_manufacturer_end,
>   };
>   EXPORT_SYMBOL(spinand_manufacturers);
> diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c
> new file mode 100644
> index 0000000..0c360e0
> --- /dev/null
> +++ b/drivers/mtd/nand/spi/micron.c
> @@ -0,0 +1,226 @@
> +/*
> + *
> + * Copyright (c) 2009-2017 Micron Technology, Inc.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/mtd/spinand.h>
> +
> +#define SPINAND_MFR_MICRON		0x2C
> +
> +#define SPI_NAND_MT29F_ECC_MASK		0x70
> +#define SPI_NAND_MT29F_ECC_0_BIT	0x00
> +#define SPI_NAND_MT29F_ECC_1_3_BIT	0x10
> +#define SPI_NAND_MT29F_ECC_4_6_BIT	0x30
> +#define SPI_NAND_MT29F_ECC_7_8_BIT	0x50
> +#define SPI_NAND_MT29F_ECC_UNCORR	0x20
> +
> +struct micron_spinand_info {
> +	char *name;
> +	u8 dev_id;
> +	u32 page_size;
> +	u32 oob_size;
> +	u32 pages_per_blk;
> +	u32 blks_per_lun;
> +	u32 luns_per_chip;
> +	u32 ecc_strength;
> +	u32 rw_mode;
> +	const struct mtd_ooblayout_ops *ooblayout_ops;
> +};
> +
> +#define MICRON_SPI_NAND_INFO(nm, did, pagesz, oobsz, pg_per_blk,	\
> +			     blk_per_lun, lun_per_chip, ecc_stren,	\
> +			     rwmode, ooblayoutops)		\
> +	{	\
> +		.name = (nm), .dev_id = (did),		\
> +		.page_size = (pagesz), .oob_size = (oobsz),		\
> +		.pages_per_blk = (pg_per_blk),		\
> +		.blks_per_lun = (blk_per_lun),		\
> +		.luns_per_chip = (lun_per_chip),	\
> +		.ecc_strength = (ecc_stren),	\
> +		.rw_mode = (rwmode),		\
> +		.ooblayout_ops = (ooblayoutops)	\
> +	}
> +
> +static int micron_ooblayout_ecc_128(struct mtd_info *mtd, int section,
> +				    struct mtd_oob_region *oobregion)
> +{
> +	if (section)
> +		return -ERANGE;
> +
> +	oobregion->length = 64;
> +	oobregion->offset = 64;
> +
> +	return 0;
> +}
> +
> +static int micron_ooblayout_free_128(struct mtd_info *mtd, int section,
> +				     struct mtd_oob_region *oobregion)
> +{
> +	if (section)
> +		return -ERANGE;
> +
> +	oobregion->length = 62;
> +	oobregion->offset = 2;
> +
> +	return 0;
> +}
> +
> +static const struct mtd_ooblayout_ops micron_ooblayout_128_ops = {
> +	.ecc = micron_ooblayout_ecc_128,
> +	.free = micron_ooblayout_free_128,
> +};
> +
> +static const struct micron_spinand_info micron_spinand_table[] = {
> +	MICRON_SPI_NAND_INFO("MT29F2G01ABAGD", 0x24, 2048, 128, 64, 2048, 1,
> +			     8, SPINAND_OP_COMMON, &micron_ooblayout_128_ops),
> +	{.name = NULL},
> +};
> +
> +static int micron_spinand_get_dummy(struct spinand_device *chip,
> +				    struct spinand_op *op)
> +{
> +	u8 opcode = op->cmd;
> +
> +	switch (opcode) {
> +	case SPINAND_CMD_READ_FROM_CACHE:
> +	case SPINAND_CMD_READ_FROM_CACHE_FAST:
> +	case SPINAND_CMD_READ_FROM_CACHE_X2:
> +	case SPINAND_CMD_READ_FROM_CACHE_DUAL_IO:
> +	case SPINAND_CMD_READ_FROM_CACHE_X4:
> +	case SPINAND_CMD_READ_ID:
> +		return 1;
> +	case SPINAND_CMD_READ_FROM_CACHE_QUAD_IO:
> +		return 2;
> +	default:
> +		return 0;
> +	}
> +}
> +
> +/*
> + * mt29f_get_ecc_status - get ecc correction information from status register
> + * @chip: SPI NAND device structure
> + * @status: status register value
> + * @corrected: corrected bit flip number
> + * @ecc_error: ecc correction error or not
> + */
> +static void mt29f_get_ecc_status(struct spinand_device *chip,
> +				 unsigned int status, unsigned int *corrected,
> +				 unsigned int *ecc_error)
> +{
> +	unsigned int ecc_status = status & SPI_NAND_MT29F_ECC_MASK;
> +
> +	*ecc_error = (ecc_status == SPI_NAND_MT29F_ECC_UNCORR);
> +	switch (ecc_status) {
> +	case SPI_NAND_MT29F_ECC_0_BIT:
> +		*corrected = 0;
> +		break;
> +	case SPI_NAND_MT29F_ECC_1_3_BIT:
> +		*corrected = 3;
> +		break;
> +	case SPI_NAND_MT29F_ECC_4_6_BIT:
> +		*corrected = 6;
> +		break;
> +	case SPI_NAND_MT29F_ECC_7_8_BIT:
> +		*corrected = 8;
> +		break;
> +	}
> +}
> +
> +static struct spinand_ecc_engine_ops generic_spi_ecc_engine_ops = {
> +	.get_ecc_status = mt29f_get_ecc_status,
> +};
> +

MT29F1G01AAADD already has a different ecc status mask (only 2 bits) 
requiring a different get_ecc_status op.
Maybe you should add directly an entry in MICRON_SPI_NAND_INFO(...) 
macro to specify the ecc_engine_ops.

Also, shouldn't this generic_spi_ecc_engine_ops be a "static const struct" ?

Arnaud
> +/*
> + * micron_spinand_scan_id_table - scan chip info in id table
> + * @chip: SPI-NAND device structure
> + * @id: point to manufacture id and device id
> + * Description:
> + *   If found in id table, config chip with table information.
> + */
> +static bool micron_spinand_scan_id_table(struct spinand_device *chip, u8 dev_id)
> +{
> +	struct mtd_info *mtd = spinand_to_mtd(chip);
> +	struct nand_device *nand = mtd_to_nand(mtd);
> +	struct micron_spinand_info *type = NULL;
> +	struct nand_memory_organization *memorg = &nand->memorg;
> +	struct spinand_ecc_engine *ecc_engine = chip->ecc.engine;
> +
> +	for (type = (struct micron_spinand_info *)micron_spinand_table;
> +	     type->name; type++) {
> +		if (dev_id != type->dev_id)
> +			continue;
> +		chip->name = type->name;
> +		memorg->eraseblocksize = type->page_size
> +				* type->pages_per_blk;
> +		memorg->pagesize = type->page_size;
> +		memorg->oobsize = type->oob_size;
> +		memorg->diesize = memorg->eraseblocksize * type->blks_per_lun;
> +		memorg->ndies = type->luns_per_chip;
> +		if (ecc_engine->mode == SPINAND_ECC_ONDIE) {
> +			ecc_engine->ops = &generic_spi_ecc_engine_ops;
> +			ecc_engine->strength = type->ecc_strength;
> +			mtd_set_ooblayout(mtd, type->ooblayout_ops);
> +		}
> +		chip->rw_mode = type->rw_mode;
> +
> +		return true;
> +	}
> +
> +	return false;
> +}
> +
> +/*
> + * micron_spinand_detect - initialize device related part in spinand_device
> + * struct if it is Micron device.
> + * @chip: SPI NAND device structure
> + */
> +static bool micron_spinand_detect(struct spinand_device *chip)
> +{
> +	u8 *id = chip->id.data;
> +
> +	/*
> +	 * Micron SPI NAND read ID need a dummy byte,
> +	 * so the first byte in raw_id is dummy.
> +	 */
> +	if (id[1] != SPINAND_MFR_MICRON)
> +		return false;
> +
> +	return micron_spinand_scan_id_table(chip, id[2]);
> +}
> +
> +/*
> + * micron_spinand_prepare_op - Fix address for cache operation.
> + * @chip: SPI NAND device structure
> + * @op: pointer to spinand_op struct
> + * @page: page address
> + * @column: column address
> + */
> +static void micron_spinand_prepare_op(struct spinand_device *chip,
> +				      struct spinand_op *op, u32 page,
> +				      u32 column)
> +{
> +	op->addr[0] |= (u8)((nand_page_to_eraseblock(&chip->base, page)
> +			     & 0x1) << 4);
> +	op->n_addr += micron_spinand_get_dummy(chip, op);
> +}
> +
> +static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = {
> +	.detect = micron_spinand_detect,
> +	.prepare_op = micron_spinand_prepare_op,
> +};
> +
> +const struct spinand_manufacturer micron_spinand_manufacture = {
> +	.id = SPINAND_MFR_MICRON,
> +	.name = "Micron",
> +	.ops = &micron_spinand_manuf_ops,
> +};

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 4/9] nand: spi: add basic blocks for infrastructure
  2017-03-23  9:43 ` [PATCH v4 4/9] nand: spi: add basic blocks for infrastructure Peter Pan
  2017-03-23 11:29   ` Marek Vasut
  2017-03-29 22:28   ` Cyrille Pitchen
@ 2017-03-30 12:38   ` Arnaud Mouiche
  2017-03-30 12:51     ` Boris Brezillon
  2 siblings, 1 reply; 36+ messages in thread
From: Arnaud Mouiche @ 2017-03-30 12:38 UTC (permalink / raw)
  To: Peter Pan, boris.brezillon, richard, computersforpeace,
	thomas.petazzoni, marex, cyrille.pitchen, linux-mtd
  Cc: peterpansjtu, linshunquan1



On 23/03/2017 10:43, Peter Pan wrote:
> This is the first commit for spi nand framkework.
> This commit is to add add basic building blocks
> for the SPI NAND infrastructure.
>
> Signed-off-by: Peter Pan <peterpandong@micron.com>
> ---
>   drivers/mtd/nand/Kconfig            |   1 +
>   drivers/mtd/nand/Makefile           |   1 +
>   drivers/mtd/nand/spi/Kconfig        |   5 +
>   drivers/mtd/nand/spi/Makefile       |   2 +
>   drivers/mtd/nand/spi/core.c         | 464 ++++++++++++++++++++++++++++++++++++
>   drivers/mtd/nand/spi/manufactures.c |  24 ++
>   include/linux/mtd/spinand.h         | 270 +++++++++++++++++++++
>   7 files changed, 767 insertions(+)
>   create mode 100644 drivers/mtd/nand/spi/Kconfig
>   create mode 100644 drivers/mtd/nand/spi/Makefile
>   create mode 100644 drivers/mtd/nand/spi/core.c
>   create mode 100644 drivers/mtd/nand/spi/manufactures.c
>   create mode 100644 include/linux/mtd/spinand.h
[...]
> diff --git a/drivers/mtd/nand/spi/manufactures.c b/drivers/mtd/nand/spi/manufactures.c
> new file mode 100644
> index 0000000..7e0b42d
> --- /dev/null
> +++ b/drivers/mtd/nand/spi/manufactures.c
> @@ -0,0 +1,24 @@
> +/**
> + *
> + * Copyright (c) 2009-2017 Micron Technology, Inc.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/mtd/spinand.h>
> +
> +struct spinand_manufacturer spinand_manufacturer_end = {0x0, "Unknown", NULL};
> +
> +struct spinand_manufacturer *spinand_manufacturers[] = {
> +	&spinand_manufacturer_end,
> +};

*const* struct spinand_manufacturer *spinand_manufacturers[]  ?

> +EXPORT_SYMBOL(spinand_manufacturers);
> diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
> new file mode 100644
> index 0000000..44748b4
> --- /dev/null
> +++ b/include/linux/mtd/spinand.h
> @@ -0,0 +1,270 @@
> +/**
> + *
> + * Copyright (c) 2009-2017 Micron Technology, Inc.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +#ifndef __LINUX_MTD_SPINAND_H
> +#define __LINUX_MTD_SPINAND_H
> +
> +#include <linux/mutex.h>
> +#include <linux/mtd/mtd.h>
> +#include <linux/mtd/nand.h>
> +

[...]

> +
> +enum spinand_ecc_mode {
> +	SPINAND_ECC_ONDIE,
> +	SPINAND_ECC_HW,
> +};
> +
> +struct spinand_ecc_engine {
> +	enum spinand_ecc_mode mode;
> +	u32 strength;
> +	u32 steps;
> +	struct spinand_ecc_engine_ops *ops;

const struct spinand_ecc_engine_ops ?

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 4/9] nand: spi: add basic blocks for infrastructure
  2017-03-30 12:38   ` Arnaud Mouiche
@ 2017-03-30 12:51     ` Boris Brezillon
  0 siblings, 0 replies; 36+ messages in thread
From: Boris Brezillon @ 2017-03-30 12:51 UTC (permalink / raw)
  To: Arnaud Mouiche
  Cc: Peter Pan, richard, computersforpeace, thomas.petazzoni, marex,
	cyrille.pitchen, linux-mtd, peterpansjtu, linshunquan1

On Thu, 30 Mar 2017 14:38:00 +0200
Arnaud Mouiche <arnaud.mouiche@gmail.com> wrote:

> On 23/03/2017 10:43, Peter Pan wrote:
> > This is the first commit for spi nand framkework.
> > This commit is to add add basic building blocks
> > for the SPI NAND infrastructure.
> >
> > Signed-off-by: Peter Pan <peterpandong@micron.com>
> > ---
> >   drivers/mtd/nand/Kconfig            |   1 +
> >   drivers/mtd/nand/Makefile           |   1 +
> >   drivers/mtd/nand/spi/Kconfig        |   5 +
> >   drivers/mtd/nand/spi/Makefile       |   2 +
> >   drivers/mtd/nand/spi/core.c         | 464 ++++++++++++++++++++++++++++++++++++
> >   drivers/mtd/nand/spi/manufactures.c |  24 ++
> >   include/linux/mtd/spinand.h         | 270 +++++++++++++++++++++
> >   7 files changed, 767 insertions(+)
> >   create mode 100644 drivers/mtd/nand/spi/Kconfig
> >   create mode 100644 drivers/mtd/nand/spi/Makefile
> >   create mode 100644 drivers/mtd/nand/spi/core.c
> >   create mode 100644 drivers/mtd/nand/spi/manufactures.c
> >   create mode 100644 include/linux/mtd/spinand.h  
> [...]
> > diff --git a/drivers/mtd/nand/spi/manufactures.c b/drivers/mtd/nand/spi/manufactures.c
> > new file mode 100644
> > index 0000000..7e0b42d
> > --- /dev/null
> > +++ b/drivers/mtd/nand/spi/manufactures.c
> > @@ -0,0 +1,24 @@
> > +/**
> > + *
> > + * Copyright (c) 2009-2017 Micron Technology, Inc.
> > + *
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License
> > + * as published by the Free Software Foundation; either version 2
> > + * of the License, or (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/mtd/spinand.h>
> > +
> > +struct spinand_manufacturer spinand_manufacturer_end = {0x0, "Unknown", NULL};
> > +
> > +struct spinand_manufacturer *spinand_manufacturers[] = {
> > +	&spinand_manufacturer_end,
> > +};  
> 
> *const* struct spinand_manufacturer *spinand_manufacturers[]  ?
> 
> > +EXPORT_SYMBOL(spinand_manufacturers);
> > diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
> > new file mode 100644
> > index 0000000..44748b4
> > --- /dev/null
> > +++ b/include/linux/mtd/spinand.h
> > @@ -0,0 +1,270 @@
> > +/**
> > + *
> > + * Copyright (c) 2009-2017 Micron Technology, Inc.
> > + *
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License
> > + * as published by the Free Software Foundation; either version 2
> > + * of the License, or (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + */
> > +#ifndef __LINUX_MTD_SPINAND_H
> > +#define __LINUX_MTD_SPINAND_H
> > +
> > +#include <linux/mutex.h>
> > +#include <linux/mtd/mtd.h>
> > +#include <linux/mtd/nand.h>
> > +  
> 
> [...]
> 
> > +
> > +enum spinand_ecc_mode {
> > +	SPINAND_ECC_ONDIE,
> > +	SPINAND_ECC_HW,
> > +};
> > +
> > +struct spinand_ecc_engine {
> > +	enum spinand_ecc_mode mode;
> > +	u32 strength;
> > +	u32 steps;
> > +	struct spinand_ecc_engine_ops *ops;  
> 
> const struct spinand_ecc_engine_ops ?
> 

Yep.

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 4/9] nand: spi: add basic blocks for infrastructure
  2017-03-30 12:25         ` Arnaud Mouiche
@ 2017-03-30 12:52           ` Boris Brezillon
  0 siblings, 0 replies; 36+ messages in thread
From: Boris Brezillon @ 2017-03-30 12:52 UTC (permalink / raw)
  To: Arnaud Mouiche
  Cc: Marek Vasut, Peter Pan, richard, computersforpeace,
	thomas.petazzoni, cyrille.pitchen, linux-mtd, peterpansjtu,
	linshunquan1

On Thu, 30 Mar 2017 14:25:41 +0200
Arnaud Mouiche <arnaud.mouiche@gmail.com> wrote:

> On 23/03/2017 17:33, Marek Vasut wrote:
> > On 03/23/2017 04:40 PM, Boris Brezillon wrote:  
> >> On Thu, 23 Mar 2017 12:29:58 +0100
> >> Marek Vasut <marex@denx.de> wrote:
> >>
> >>  
> >>>> +
> >>>> +/*
> >>>> + * spinand_read_status - get status register value
> >>>> + * @chip: SPI NAND device structure
> >>>> + * @status: buffer to store value
> >>>> + * Description:
> >>>> + *   After read, write, or erase, the NAND device is expected to set the
> >>>> + *   busy status.
> >>>> + *   This function is to allow reading the status of the command: read,
> >>>> + *   write, and erase.
> >>>> + */
> >>>> +static int spinand_read_status(struct spinand_device *chip, uint8_t *status)
> >>>> +{
> >>>> +	return spinand_read_reg(chip, REG_STATUS, status);
> >>>> +}
> >>>> +
> >>>> +/*
> >>>> + * spinand_wait - wait until the command is done
> >>>> + * @chip: SPI NAND device structure
> >>>> + * @s: buffer to store status register value (can be NULL)
> >>>> + */
> >>>> +static int spinand_wait(struct spinand_device *chip, u8 *s)
> >>>> +{
> >>>> +	unsigned long timeo = msecs_to_jiffies(400);
> >>>> +	u8 status;
> >>>> +
> >>>> +	do {
> >>>> +		spinand_read_status(chip, &status);
> >>>> +		if ((status & STATUS_OIP_MASK) == STATUS_READY)
> >>>> +			goto out;
> >>>> +	} while (time_before(jiffies, timeo));
> >>>> +
> >>>> +	/*
> >>>> +	 * Extra read, just in case the STATUS_READY bit has changed
> >>>> +	 * since our last check
> >>>> +	 */  
> >>> Is this likely to happen ? Probably not ... so in case you reach this
> >>> place here, timeout happened.  
> >> If the timeout is big enough, no. But it does not hurt to do one last
> >> check.  
> > 400 mSec is not big enough ? It's huge ... this seems like a duplication
> > to me. btw the ad-hoc 400 mSec delay value should be replaced with a macro.
> >  
> In fact, there is a bug:
> 
> > unsigned long timeo = msecs_to_jiffies(400);
> > [...]
> > do { } while (time_before(jiffies, timeo));  
> 
> The condition is almost true except during the 5 minutes following the boot (before jiffies wrap around).
> So, only one status read is done, which give a high chance of returning a timeout status.
> I guess you should do : 	
> 
> 	unsigned long timeo = jiffies + msecs_to_jiffies(400);
> 
> I also suspect that this is the reason why you have an additional status read.

Oh, nice catch!

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 7/9] nand: spi: add Micron spi nand support
  2017-03-30 12:31   ` Arnaud Mouiche
@ 2017-03-30 12:57     ` Boris Brezillon
  0 siblings, 0 replies; 36+ messages in thread
From: Boris Brezillon @ 2017-03-30 12:57 UTC (permalink / raw)
  To: Arnaud Mouiche
  Cc: Peter Pan, richard, computersforpeace, thomas.petazzoni, marex,
	cyrille.pitchen, linux-mtd, peterpansjtu, linshunquan1

On Thu, 30 Mar 2017 14:31:54 +0200
Arnaud Mouiche <arnaud.mouiche@gmail.com> wrote:

> > +
> > +static struct spinand_ecc_engine_ops generic_spi_ecc_engine_ops = {
> > +	.get_ecc_status = mt29f_get_ecc_status,
> > +};
> > +  
> 
> MT29F1G01AAADD already has a different ecc status mask (only 2 bits) 
> requiring a different get_ecc_status op.

Interesting. It seems that NAND/NOR manufacturers are doing the same
mistakes over and over again :-/. And it tends to confirm that having
per-vendor drivers is actually a good idea if we want to isolate
all these vendor specific handling bits from the core.

> Maybe you should add directly an entry in MICRON_SPI_NAND_INFO(...) 
> macro to specify the ecc_engine_ops.

Yep, sounds reasonable.

> 
> Also, shouldn't this generic_spi_ecc_engine_ops be a "static const struct" ?

Definitely.

^ permalink raw reply	[flat|nested] 36+ messages in thread

* Re: [PATCH v4 0/9] Introduction to SPI NAND framework
  2017-03-30 12:17 ` [PATCH v4 0/9] Introduction to SPI NAND framework Arnaud Mouiche
@ 2017-04-10  7:33   ` Peter Pan
  0 siblings, 0 replies; 36+ messages in thread
From: Peter Pan @ 2017-04-10  7:33 UTC (permalink / raw)
  To: Arnaud Mouiche
  Cc: Peter Pan, Boris Brezillon, Richard Weinberger, Brian Norris,
	Thomas Petazzoni, Marek Vasut, Cyrille Pitchen, linux-mtd,
	linshunquan (A)

H Arnaud,

On Thu, Mar 30, 2017 at 8:17 PM, Arnaud Mouiche
<arnaud.mouiche@gmail.com> wrote:
> Hi all.
>
> On 23/03/2017 10:43, Peter Pan wrote:
>>
>> v4 also changes a lot than v3, thanks for Boris's and Arnaud's
>> valuable comments! The biggest change is "add spinand_alloc(),
>> spinand_free(), spinand_register(), spinand_unregister() interfaces
>> and make old interface - spinand_detect(), spinand_init() static".
>> While this is just a beginning. more effort is needed to make clear
>> layer. I really hope for comments on this part.
>>
>> This series introductes a SPI NAND framework.
>> SPI NAND is a new NAND family device with SPI protocol as
>> its interface. And its command set is totally different
>> with parallel NAND.
>
> [...]
>
> Get a chance to test this serie today on MT29F1G01AAADD (not receiving the
> MT29F2G01ABAGD yet).
>
> Except one bug I found that I will detail directly in the patch series:
> - detection is working
> - mtd operation with nanddump / nandwrite / flash_erase are ok
> - mounting ubifs partition + basic write/read are ok.
>
> Arnaud

This is really a big help for me. Really thank you.

Peter Pan

^ permalink raw reply	[flat|nested] 36+ messages in thread

end of thread, other threads:[~2017-04-10  7:33 UTC | newest]

Thread overview: 36+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-03-23  9:43 [PATCH v4 0/9] Introduction to SPI NAND framework Peter Pan
2017-03-23  9:43 ` [PATCH v4 1/9] mtd: nand: add oob iterator in nand_for_each_page Peter Pan
2017-03-23 11:13   ` Marek Vasut
2017-03-28  1:35     ` Peter Pan
2017-03-29 19:34   ` Boris Brezillon
2017-03-30  8:01     ` Peter Pan
2017-03-30  8:34       ` Boris Brezillon
2017-03-23  9:43 ` [PATCH v4 2/9] mtd: nand: make sure mtd_oob_ops consistent in bbt Peter Pan
2017-03-29 19:48   ` Boris Brezillon
2017-03-23  9:43 ` [PATCH v4 3/9] mtd: nand: add more helpers in nand.h Peter Pan
2017-03-23 11:19   ` Marek Vasut
2017-03-29 19:57   ` Boris Brezillon
2017-03-30  8:04     ` Peter Pan
2017-03-30  8:40       ` Boris Brezillon
2017-03-23  9:43 ` [PATCH v4 4/9] nand: spi: add basic blocks for infrastructure Peter Pan
2017-03-23 11:29   ` Marek Vasut
2017-03-23 15:40     ` Boris Brezillon
2017-03-23 16:33       ` Marek Vasut
2017-03-30 12:25         ` Arnaud Mouiche
2017-03-30 12:52           ` Boris Brezillon
2017-03-29 22:28   ` Cyrille Pitchen
2017-03-30 12:38   ` Arnaud Mouiche
2017-03-30 12:51     ` Boris Brezillon
2017-03-23  9:43 ` [PATCH v4 5/9] nand: spi: add basic operations support Peter Pan
2017-03-23  9:43 ` [PATCH v4 6/9] nand: spi: Add bad block support Peter Pan
2017-03-23  9:43 ` [PATCH v4 7/9] nand: spi: add Micron spi nand support Peter Pan
2017-03-30 12:31   ` Arnaud Mouiche
2017-03-30 12:57     ` Boris Brezillon
2017-03-23  9:43 ` [PATCH v4 8/9] nand: spi: Add generic SPI controller support Peter Pan
2017-03-23 11:33   ` Marek Vasut
2017-03-28  1:38     ` Peter Pan
2017-03-29 21:37   ` Cyrille Pitchen
2017-03-30  8:28     ` Peter Pan
2017-03-23  9:43 ` [PATCH v4 9/9] MAINTAINERS: Add SPI NAND entry Peter Pan
2017-03-30 12:17 ` [PATCH v4 0/9] Introduction to SPI NAND framework Arnaud Mouiche
2017-04-10  7:33   ` Peter Pan

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.