All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2] mtd: nand: automate NAND timings selection
@ 2016-09-06 10:39 ` Sascha Hauer
  0 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 10:39 UTC (permalink / raw)
  To: linux-mtd; +Cc: Boris Brezillon, kernel, linux-arm-kernel

This series aims at automating the NAND timings selection which is
currently supposed to be done in each NAND controller driver, thus
simplifying drivers implementation.

As suggested by Boris this version of the series introduces a nand_reset()
function which replaces the several open coded NAND_CMD_RESET commands
in the code. This makes sure we can apply the timing each time after
after reset.

Also I have brought back the conversion patch for teh sunxi driver whic
was part of Boris initial posting. It's untested due to the lack of hardware,
so please test before applying.

Sascha

Changes since v1:
- create a nand_reset() function to create a single place to reset NAND
  chips and to apply timings
- Add patch to convert sunxi driver for automated timing setup
- split into more patches

Changes since the initial posting from Boris:

- Integrate Feedback from Ezequiel Garcia
- When iterating over the chips calling onfi_set_features() for each
  bail out when any of the calls fail, not only the last one.
- When one of the onfi_set_features() calls fail then reset the chipi
  afterwards.
- Drop Sunxi example, add patch for the mxc_nand controller instead.

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

* [PATCH v2] mtd: nand: automate NAND timings selection
@ 2016-09-06 10:39 ` Sascha Hauer
  0 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 10:39 UTC (permalink / raw)
  To: linux-arm-kernel

This series aims at automating the NAND timings selection which is
currently supposed to be done in each NAND controller driver, thus
simplifying drivers implementation.

As suggested by Boris this version of the series introduces a nand_reset()
function which replaces the several open coded NAND_CMD_RESET commands
in the code. This makes sure we can apply the timing each time after
after reset.

Also I have brought back the conversion patch for teh sunxi driver whic
was part of Boris initial posting. It's untested due to the lack of hardware,
so please test before applying.

Sascha

Changes since v1:
- create a nand_reset() function to create a single place to reset NAND
  chips and to apply timings
- Add patch to convert sunxi driver for automated timing setup
- split into more patches

Changes since the initial posting from Boris:

- Integrate Feedback from Ezequiel Garcia
- When iterating over the chips calling onfi_set_features() for each
  bail out when any of the calls fail, not only the last one.
- When one of the onfi_set_features() calls fail then reset the chipi
  afterwards.
- Drop Sunxi example, add patch for the mxc_nand controller instead.

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

* [PATCH 1/7] mtd: nand: Create a NAND reset function
  2016-09-06 10:39 ` Sascha Hauer
@ 2016-09-06 10:39   ` Sascha Hauer
  -1 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 10:39 UTC (permalink / raw)
  To: linux-mtd; +Cc: Boris Brezillon, kernel, linux-arm-kernel, Sascha Hauer

When NAND devices are resetted some initialization may have to be done,
like for example they have to be configured for the timing mode that
shall be used. To get a common place where this initialization can be
implemented create a nand_reset() function. This currently only issues
a NAND_CMD_RESET to the NAND device. The places issuing this command
manually are replaced with a call to nand_reset().

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/mtd/nand/nand_base.c | 23 ++++++++++++++++++-----
 include/linux/mtd/nand.h     |  3 +++
 2 files changed, 21 insertions(+), 5 deletions(-)

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 77533f7..20151fc 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -948,6 +948,19 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
 }
 
 /**
+ * nand_reset - Reset and initialize a NAND device
+ * @mtd: mtd info
+ *
+ * Returns 0 for success or negative error code otherwise
+ */
+int nand_reset(struct mtd_info *mtd)
+{
+	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+	return 0;
+}
+
+/**
  * __nand_unlock - [REPLACEABLE] unlocks specified locked blocks
  * @mtd: mtd info
  * @ofs: offset to start unlock from
@@ -1025,7 +1038,7 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 	 * some operation can also clear the bit 7 of status register
 	 * eg. erase/program a locked block
 	 */
-	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+	nand_reset(mtd);
 
 	/* Check, if it is write protected */
 	if (nand_check_wp(mtd)) {
@@ -1084,7 +1097,7 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 	 * some operation can also clear the bit 7 of status register
 	 * eg. erase/program a locked block
 	 */
-	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+	nand_reset(mtd);
 
 	/* Check, if it is write protected */
 	if (nand_check_wp(mtd)) {
@@ -2788,7 +2801,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
 	 * if we don't do this. I have no clue why, but I seem to have 'fixed'
 	 * it in the doc2000 driver in August 1999.  dwmw2.
 	 */
-	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+	nand_reset(mtd);
 
 	/* Check, if it is write protected */
 	if (nand_check_wp(mtd)) {
@@ -3829,7 +3842,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 	 * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
 	 * after power-up.
 	 */
-	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+	nand_reset(mtd);
 
 	/* Send the command for reading device ID */
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
@@ -4161,7 +4174,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
 	for (i = 1; i < maxchips; i++) {
 		chip->select_chip(mtd, i);
 		/* See comment in nand_get_flash_type for reset */
-		chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+		nand_reset(mtd);
 		/* Send the command for reading device ID */
 		chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
 		/* Read manufacturer and device IDs */
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 8dd6e01..9af9575 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -50,6 +50,9 @@ extern int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
 /* unlocks specified locked blocks */
 extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
 
+/* Reset and initialize a NAND device */
+extern int nand_reset(struct mtd_info *mtd);
+
 /* The maximum number of NAND chips in an array */
 #define NAND_MAX_CHIPS		8
 
-- 
2.8.1

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

* [PATCH 1/7] mtd: nand: Create a NAND reset function
@ 2016-09-06 10:39   ` Sascha Hauer
  0 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 10:39 UTC (permalink / raw)
  To: linux-arm-kernel

When NAND devices are resetted some initialization may have to be done,
like for example they have to be configured for the timing mode that
shall be used. To get a common place where this initialization can be
implemented create a nand_reset() function. This currently only issues
a NAND_CMD_RESET to the NAND device. The places issuing this command
manually are replaced with a call to nand_reset().

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/mtd/nand/nand_base.c | 23 ++++++++++++++++++-----
 include/linux/mtd/nand.h     |  3 +++
 2 files changed, 21 insertions(+), 5 deletions(-)

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 77533f7..20151fc 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -948,6 +948,19 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
 }
 
 /**
+ * nand_reset - Reset and initialize a NAND device
+ * @mtd: mtd info
+ *
+ * Returns 0 for success or negative error code otherwise
+ */
+int nand_reset(struct mtd_info *mtd)
+{
+	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+	return 0;
+}
+
+/**
  * __nand_unlock - [REPLACEABLE] unlocks specified locked blocks
  * @mtd: mtd info
  * @ofs: offset to start unlock from
@@ -1025,7 +1038,7 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 	 * some operation can also clear the bit 7 of status register
 	 * eg. erase/program a locked block
 	 */
-	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+	nand_reset(mtd);
 
 	/* Check, if it is write protected */
 	if (nand_check_wp(mtd)) {
@@ -1084,7 +1097,7 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 	 * some operation can also clear the bit 7 of status register
 	 * eg. erase/program a locked block
 	 */
-	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+	nand_reset(mtd);
 
 	/* Check, if it is write protected */
 	if (nand_check_wp(mtd)) {
@@ -2788,7 +2801,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
 	 * if we don't do this. I have no clue why, but I seem to have 'fixed'
 	 * it in the doc2000 driver in August 1999.  dwmw2.
 	 */
-	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+	nand_reset(mtd);
 
 	/* Check, if it is write protected */
 	if (nand_check_wp(mtd)) {
@@ -3829,7 +3842,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 	 * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
 	 * after power-up.
 	 */
-	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+	nand_reset(mtd);
 
 	/* Send the command for reading device ID */
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
@@ -4161,7 +4174,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
 	for (i = 1; i < maxchips; i++) {
 		chip->select_chip(mtd, i);
 		/* See comment in nand_get_flash_type for reset */
-		chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+		nand_reset(mtd);
 		/* Send the command for reading device ID */
 		chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
 		/* Read manufacturer and device IDs */
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 8dd6e01..9af9575 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -50,6 +50,9 @@ extern int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
 /* unlocks specified locked blocks */
 extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
 
+/* Reset and initialize a NAND device */
+extern int nand_reset(struct mtd_info *mtd);
+
 /* The maximum number of NAND chips in an array */
 #define NAND_MAX_CHIPS		8
 
-- 
2.8.1

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

* [PATCH 2/7] mtd: nand: Introduce nand_data_interface
  2016-09-06 10:39 ` Sascha Hauer
@ 2016-09-06 10:39   ` Sascha Hauer
  -1 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 10:39 UTC (permalink / raw)
  To: linux-mtd; +Cc: Boris Brezillon, kernel, linux-arm-kernel, Sascha Hauer

Currently we have no data structure to fully describe a NAND timing.
We only have struct nand_sdr_timings for NAND timings in SDR mode,
but nothing for DDR mode and also no container to store both types
of timing.
This patch adds struct nand_data_interface which stores the timing
type and a union of different timings. This can be used to pass to
drivers in order to configure the timing.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 include/linux/mtd/nand.h | 109 ++++++++++++++++++++++++++---------------------
 1 file changed, 60 insertions(+), 49 deletions(-)

diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 9af9575..19c73ef 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -568,6 +568,66 @@ struct nand_buffers {
 	uint8_t *databuf;
 };
 
+/*
+ * struct nand_sdr_timings - SDR NAND chip timings
+ *
+ * This struct defines the timing requirements of a SDR NAND chip.
+ * These information can be found in every NAND datasheets and the timings
+ * meaning are described in the ONFI specifications:
+ * www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf (chapter 4.15 Timing
+ * Parameters)
+ *
+ * All these timings are expressed in picoseconds.
+ */
+
+struct nand_sdr_timings {
+	u32 tALH_min;
+	u32 tADL_min;
+	u32 tALS_min;
+	u32 tAR_min;
+	u32 tCEA_max;
+	u32 tCEH_min;
+	u32 tCH_min;
+	u32 tCHZ_max;
+	u32 tCLH_min;
+	u32 tCLR_min;
+	u32 tCLS_min;
+	u32 tCOH_min;
+	u32 tCS_min;
+	u32 tDH_min;
+	u32 tDS_min;
+	u32 tFEAT_max;
+	u32 tIR_min;
+	u32 tITC_max;
+	u32 tRC_min;
+	u32 tREA_max;
+	u32 tREH_min;
+	u32 tRHOH_min;
+	u32 tRHW_min;
+	u32 tRHZ_max;
+	u32 tRLOH_min;
+	u32 tRP_min;
+	u32 tRR_min;
+	u64 tRST_max;
+	u32 tWB_max;
+	u32 tWC_min;
+	u32 tWH_min;
+	u32 tWHR_min;
+	u32 tWP_min;
+	u32 tWW_min;
+};
+
+enum nand_data_interface_type {
+	NAND_SDR_IFACE,
+};
+
+struct nand_data_interface {
+	enum nand_data_interface_type type;
+	union {
+		struct nand_sdr_timings sdr;
+	} timings;
+};
+
 /**
  * struct nand_chip - NAND Private Flash Chip Data
  * @mtd:		MTD device registered to the MTD framework
@@ -1026,55 +1086,6 @@ static inline int jedec_feature(struct nand_chip *chip)
 		: 0;
 }
 
-/*
- * struct nand_sdr_timings - SDR NAND chip timings
- *
- * This struct defines the timing requirements of a SDR NAND chip.
- * These informations can be found in every NAND datasheets and the timings
- * meaning are described in the ONFI specifications:
- * www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf (chapter 4.15 Timing
- * Parameters)
- *
- * All these timings are expressed in picoseconds.
- */
-
-struct nand_sdr_timings {
-	u32 tALH_min;
-	u32 tADL_min;
-	u32 tALS_min;
-	u32 tAR_min;
-	u32 tCEA_max;
-	u32 tCEH_min;
-	u32 tCH_min;
-	u32 tCHZ_max;
-	u32 tCLH_min;
-	u32 tCLR_min;
-	u32 tCLS_min;
-	u32 tCOH_min;
-	u32 tCS_min;
-	u32 tDH_min;
-	u32 tDS_min;
-	u32 tFEAT_max;
-	u32 tIR_min;
-	u32 tITC_max;
-	u32 tRC_min;
-	u32 tREA_max;
-	u32 tREH_min;
-	u32 tRHOH_min;
-	u32 tRHW_min;
-	u32 tRHZ_max;
-	u32 tRLOH_min;
-	u32 tRP_min;
-	u32 tRR_min;
-	u64 tRST_max;
-	u32 tWB_max;
-	u32 tWC_min;
-	u32 tWH_min;
-	u32 tWHR_min;
-	u32 tWP_min;
-	u32 tWW_min;
-};
-
 /* get timing characteristics from ONFI timing mode. */
 const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode);
 
-- 
2.8.1

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

* [PATCH 2/7] mtd: nand: Introduce nand_data_interface
@ 2016-09-06 10:39   ` Sascha Hauer
  0 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 10:39 UTC (permalink / raw)
  To: linux-arm-kernel

Currently we have no data structure to fully describe a NAND timing.
We only have struct nand_sdr_timings for NAND timings in SDR mode,
but nothing for DDR mode and also no container to store both types
of timing.
This patch adds struct nand_data_interface which stores the timing
type and a union of different timings. This can be used to pass to
drivers in order to configure the timing.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 include/linux/mtd/nand.h | 109 ++++++++++++++++++++++++++---------------------
 1 file changed, 60 insertions(+), 49 deletions(-)

diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 9af9575..19c73ef 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -568,6 +568,66 @@ struct nand_buffers {
 	uint8_t *databuf;
 };
 
+/*
+ * struct nand_sdr_timings - SDR NAND chip timings
+ *
+ * This struct defines the timing requirements of a SDR NAND chip.
+ * These information can be found in every NAND datasheets and the timings
+ * meaning are described in the ONFI specifications:
+ * www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf (chapter 4.15 Timing
+ * Parameters)
+ *
+ * All these timings are expressed in picoseconds.
+ */
+
+struct nand_sdr_timings {
+	u32 tALH_min;
+	u32 tADL_min;
+	u32 tALS_min;
+	u32 tAR_min;
+	u32 tCEA_max;
+	u32 tCEH_min;
+	u32 tCH_min;
+	u32 tCHZ_max;
+	u32 tCLH_min;
+	u32 tCLR_min;
+	u32 tCLS_min;
+	u32 tCOH_min;
+	u32 tCS_min;
+	u32 tDH_min;
+	u32 tDS_min;
+	u32 tFEAT_max;
+	u32 tIR_min;
+	u32 tITC_max;
+	u32 tRC_min;
+	u32 tREA_max;
+	u32 tREH_min;
+	u32 tRHOH_min;
+	u32 tRHW_min;
+	u32 tRHZ_max;
+	u32 tRLOH_min;
+	u32 tRP_min;
+	u32 tRR_min;
+	u64 tRST_max;
+	u32 tWB_max;
+	u32 tWC_min;
+	u32 tWH_min;
+	u32 tWHR_min;
+	u32 tWP_min;
+	u32 tWW_min;
+};
+
+enum nand_data_interface_type {
+	NAND_SDR_IFACE,
+};
+
+struct nand_data_interface {
+	enum nand_data_interface_type type;
+	union {
+		struct nand_sdr_timings sdr;
+	} timings;
+};
+
 /**
  * struct nand_chip - NAND Private Flash Chip Data
  * @mtd:		MTD device registered to the MTD framework
@@ -1026,55 +1086,6 @@ static inline int jedec_feature(struct nand_chip *chip)
 		: 0;
 }
 
-/*
- * struct nand_sdr_timings - SDR NAND chip timings
- *
- * This struct defines the timing requirements of a SDR NAND chip.
- * These informations can be found in every NAND datasheets and the timings
- * meaning are described in the ONFI specifications:
- * www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf (chapter 4.15 Timing
- * Parameters)
- *
- * All these timings are expressed in picoseconds.
- */
-
-struct nand_sdr_timings {
-	u32 tALH_min;
-	u32 tADL_min;
-	u32 tALS_min;
-	u32 tAR_min;
-	u32 tCEA_max;
-	u32 tCEH_min;
-	u32 tCH_min;
-	u32 tCHZ_max;
-	u32 tCLH_min;
-	u32 tCLR_min;
-	u32 tCLS_min;
-	u32 tCOH_min;
-	u32 tCS_min;
-	u32 tDH_min;
-	u32 tDS_min;
-	u32 tFEAT_max;
-	u32 tIR_min;
-	u32 tITC_max;
-	u32 tRC_min;
-	u32 tREA_max;
-	u32 tREH_min;
-	u32 tRHOH_min;
-	u32 tRHW_min;
-	u32 tRHZ_max;
-	u32 tRLOH_min;
-	u32 tRP_min;
-	u32 tRR_min;
-	u64 tRST_max;
-	u32 tWB_max;
-	u32 tWC_min;
-	u32 tWH_min;
-	u32 tWHR_min;
-	u32 tWP_min;
-	u32 tWW_min;
-};
-
 /* get timing characteristics from ONFI timing mode. */
 const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode);
 
-- 
2.8.1

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

* [PATCH 3/7] mtd: nand: convert ONFI mode into data interface
  2016-09-06 10:39 ` Sascha Hauer
@ 2016-09-06 10:39   ` Sascha Hauer
  -1 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 10:39 UTC (permalink / raw)
  To: linux-mtd; +Cc: Boris Brezillon, kernel, linux-arm-kernel, Sascha Hauer

struct nand_data_interface is the designated type to pass to
the NAND drivers to configure the timing. To simplify this introduce
onfi_async_timing_mode_to_data_interface() to convert a ONFI
mode into a nand_data_interface.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/mtd/nand/nand_timings.c | 444 +++++++++++++++++++++-------------------
 include/linux/mtd/nand.h        |   2 +
 2 files changed, 240 insertions(+), 206 deletions(-)

diff --git a/drivers/mtd/nand/nand_timings.c b/drivers/mtd/nand/nand_timings.c
index e81470a..260b074 100644
--- a/drivers/mtd/nand/nand_timings.c
+++ b/drivers/mtd/nand/nand_timings.c
@@ -13,228 +13,246 @@
 #include <linux/export.h>
 #include <linux/mtd/nand.h>
 
-static const struct nand_sdr_timings onfi_sdr_timings[] = {
+static const struct nand_data_interface onfi_sdr_timings[] = {
 	/* Mode 0 */
 	{
-		.tADL_min = 200000,
-		.tALH_min = 20000,
-		.tALS_min = 50000,
-		.tAR_min = 25000,
-		.tCEA_max = 100000,
-		.tCEH_min = 20000,
-		.tCH_min = 20000,
-		.tCHZ_max = 100000,
-		.tCLH_min = 20000,
-		.tCLR_min = 20000,
-		.tCLS_min = 50000,
-		.tCOH_min = 0,
-		.tCS_min = 70000,
-		.tDH_min = 20000,
-		.tDS_min = 40000,
-		.tFEAT_max = 1000000,
-		.tIR_min = 10000,
-		.tITC_max = 1000000,
-		.tRC_min = 100000,
-		.tREA_max = 40000,
-		.tREH_min = 30000,
-		.tRHOH_min = 0,
-		.tRHW_min = 200000,
-		.tRHZ_max = 200000,
-		.tRLOH_min = 0,
-		.tRP_min = 50000,
-		.tRST_max = 250000000000ULL,
-		.tWB_max = 200000,
-		.tRR_min = 40000,
-		.tWC_min = 100000,
-		.tWH_min = 30000,
-		.tWHR_min = 120000,
-		.tWP_min = 50000,
-		.tWW_min = 100000,
+		.type = NAND_SDR_IFACE,
+		.timings.sdr = {
+			.tADL_min = 200000,
+			.tALH_min = 20000,
+			.tALS_min = 50000,
+			.tAR_min = 25000,
+			.tCEA_max = 100000,
+			.tCEH_min = 20000,
+			.tCH_min = 20000,
+			.tCHZ_max = 100000,
+			.tCLH_min = 20000,
+			.tCLR_min = 20000,
+			.tCLS_min = 50000,
+			.tCOH_min = 0,
+			.tCS_min = 70000,
+			.tDH_min = 20000,
+			.tDS_min = 40000,
+			.tFEAT_max = 1000000,
+			.tIR_min = 10000,
+			.tITC_max = 1000000,
+			.tRC_min = 100000,
+			.tREA_max = 40000,
+			.tREH_min = 30000,
+			.tRHOH_min = 0,
+			.tRHW_min = 200000,
+			.tRHZ_max = 200000,
+			.tRLOH_min = 0,
+			.tRP_min = 50000,
+			.tRST_max = 250000000000ULL,
+			.tWB_max = 200000,
+			.tRR_min = 40000,
+			.tWC_min = 100000,
+			.tWH_min = 30000,
+			.tWHR_min = 120000,
+			.tWP_min = 50000,
+			.tWW_min = 100000,
+		},
 	},
 	/* Mode 1 */
 	{
-		.tADL_min = 100000,
-		.tALH_min = 10000,
-		.tALS_min = 25000,
-		.tAR_min = 10000,
-		.tCEA_max = 45000,
-		.tCEH_min = 20000,
-		.tCH_min = 10000,
-		.tCHZ_max = 50000,
-		.tCLH_min = 10000,
-		.tCLR_min = 10000,
-		.tCLS_min = 25000,
-		.tCOH_min = 15000,
-		.tCS_min = 35000,
-		.tDH_min = 10000,
-		.tDS_min = 20000,
-		.tFEAT_max = 1000000,
-		.tIR_min = 0,
-		.tITC_max = 1000000,
-		.tRC_min = 50000,
-		.tREA_max = 30000,
-		.tREH_min = 15000,
-		.tRHOH_min = 15000,
-		.tRHW_min = 100000,
-		.tRHZ_max = 100000,
-		.tRLOH_min = 0,
-		.tRP_min = 25000,
-		.tRR_min = 20000,
-		.tRST_max = 500000000,
-		.tWB_max = 100000,
-		.tWC_min = 45000,
-		.tWH_min = 15000,
-		.tWHR_min = 80000,
-		.tWP_min = 25000,
-		.tWW_min = 100000,
+		.type = NAND_SDR_IFACE,
+		.timings.sdr = {
+			.tADL_min = 100000,
+			.tALH_min = 10000,
+			.tALS_min = 25000,
+			.tAR_min = 10000,
+			.tCEA_max = 45000,
+			.tCEH_min = 20000,
+			.tCH_min = 10000,
+			.tCHZ_max = 50000,
+			.tCLH_min = 10000,
+			.tCLR_min = 10000,
+			.tCLS_min = 25000,
+			.tCOH_min = 15000,
+			.tCS_min = 35000,
+			.tDH_min = 10000,
+			.tDS_min = 20000,
+			.tFEAT_max = 1000000,
+			.tIR_min = 0,
+			.tITC_max = 1000000,
+			.tRC_min = 50000,
+			.tREA_max = 30000,
+			.tREH_min = 15000,
+			.tRHOH_min = 15000,
+			.tRHW_min = 100000,
+			.tRHZ_max = 100000,
+			.tRLOH_min = 0,
+			.tRP_min = 25000,
+			.tRR_min = 20000,
+			.tRST_max = 500000000,
+			.tWB_max = 100000,
+			.tWC_min = 45000,
+			.tWH_min = 15000,
+			.tWHR_min = 80000,
+			.tWP_min = 25000,
+			.tWW_min = 100000,
+		},
 	},
 	/* Mode 2 */
 	{
-		.tADL_min = 100000,
-		.tALH_min = 10000,
-		.tALS_min = 15000,
-		.tAR_min = 10000,
-		.tCEA_max = 30000,
-		.tCEH_min = 20000,
-		.tCH_min = 10000,
-		.tCHZ_max = 50000,
-		.tCLH_min = 10000,
-		.tCLR_min = 10000,
-		.tCLS_min = 15000,
-		.tCOH_min = 15000,
-		.tCS_min = 25000,
-		.tDH_min = 5000,
-		.tDS_min = 15000,
-		.tFEAT_max = 1000000,
-		.tIR_min = 0,
-		.tITC_max = 1000000,
-		.tRC_min = 35000,
-		.tREA_max = 25000,
-		.tREH_min = 15000,
-		.tRHOH_min = 15000,
-		.tRHW_min = 100000,
-		.tRHZ_max = 100000,
-		.tRLOH_min = 0,
-		.tRR_min = 20000,
-		.tRST_max = 500000000,
-		.tWB_max = 100000,
-		.tRP_min = 17000,
-		.tWC_min = 35000,
-		.tWH_min = 15000,
-		.tWHR_min = 80000,
-		.tWP_min = 17000,
-		.tWW_min = 100000,
+		.type = NAND_SDR_IFACE,
+		.timings.sdr = {
+			.tADL_min = 100000,
+			.tALH_min = 10000,
+			.tALS_min = 15000,
+			.tAR_min = 10000,
+			.tCEA_max = 30000,
+			.tCEH_min = 20000,
+			.tCH_min = 10000,
+			.tCHZ_max = 50000,
+			.tCLH_min = 10000,
+			.tCLR_min = 10000,
+			.tCLS_min = 15000,
+			.tCOH_min = 15000,
+			.tCS_min = 25000,
+			.tDH_min = 5000,
+			.tDS_min = 15000,
+			.tFEAT_max = 1000000,
+			.tIR_min = 0,
+			.tITC_max = 1000000,
+			.tRC_min = 35000,
+			.tREA_max = 25000,
+			.tREH_min = 15000,
+			.tRHOH_min = 15000,
+			.tRHW_min = 100000,
+			.tRHZ_max = 100000,
+			.tRLOH_min = 0,
+			.tRR_min = 20000,
+			.tRST_max = 500000000,
+			.tWB_max = 100000,
+			.tRP_min = 17000,
+			.tWC_min = 35000,
+			.tWH_min = 15000,
+			.tWHR_min = 80000,
+			.tWP_min = 17000,
+			.tWW_min = 100000,
+		},
 	},
 	/* Mode 3 */
 	{
-		.tADL_min = 100000,
-		.tALH_min = 5000,
-		.tALS_min = 10000,
-		.tAR_min = 10000,
-		.tCEA_max = 25000,
-		.tCEH_min = 20000,
-		.tCH_min = 5000,
-		.tCHZ_max = 50000,
-		.tCLH_min = 5000,
-		.tCLR_min = 10000,
-		.tCLS_min = 10000,
-		.tCOH_min = 15000,
-		.tCS_min = 25000,
-		.tDH_min = 5000,
-		.tDS_min = 10000,
-		.tFEAT_max = 1000000,
-		.tIR_min = 0,
-		.tITC_max = 1000000,
-		.tRC_min = 30000,
-		.tREA_max = 20000,
-		.tREH_min = 10000,
-		.tRHOH_min = 15000,
-		.tRHW_min = 100000,
-		.tRHZ_max = 100000,
-		.tRLOH_min = 0,
-		.tRP_min = 15000,
-		.tRR_min = 20000,
-		.tRST_max = 500000000,
-		.tWB_max = 100000,
-		.tWC_min = 30000,
-		.tWH_min = 10000,
-		.tWHR_min = 80000,
-		.tWP_min = 15000,
-		.tWW_min = 100000,
+		.type = NAND_SDR_IFACE,
+		.timings.sdr = {
+			.tADL_min = 100000,
+			.tALH_min = 5000,
+			.tALS_min = 10000,
+			.tAR_min = 10000,
+			.tCEA_max = 25000,
+			.tCEH_min = 20000,
+			.tCH_min = 5000,
+			.tCHZ_max = 50000,
+			.tCLH_min = 5000,
+			.tCLR_min = 10000,
+			.tCLS_min = 10000,
+			.tCOH_min = 15000,
+			.tCS_min = 25000,
+			.tDH_min = 5000,
+			.tDS_min = 10000,
+			.tFEAT_max = 1000000,
+			.tIR_min = 0,
+			.tITC_max = 1000000,
+			.tRC_min = 30000,
+			.tREA_max = 20000,
+			.tREH_min = 10000,
+			.tRHOH_min = 15000,
+			.tRHW_min = 100000,
+			.tRHZ_max = 100000,
+			.tRLOH_min = 0,
+			.tRP_min = 15000,
+			.tRR_min = 20000,
+			.tRST_max = 500000000,
+			.tWB_max = 100000,
+			.tWC_min = 30000,
+			.tWH_min = 10000,
+			.tWHR_min = 80000,
+			.tWP_min = 15000,
+			.tWW_min = 100000,
+		},
 	},
 	/* Mode 4 */
 	{
-		.tADL_min = 70000,
-		.tALH_min = 5000,
-		.tALS_min = 10000,
-		.tAR_min = 10000,
-		.tCEA_max = 25000,
-		.tCEH_min = 20000,
-		.tCH_min = 5000,
-		.tCHZ_max = 30000,
-		.tCLH_min = 5000,
-		.tCLR_min = 10000,
-		.tCLS_min = 10000,
-		.tCOH_min = 15000,
-		.tCS_min = 20000,
-		.tDH_min = 5000,
-		.tDS_min = 10000,
-		.tFEAT_max = 1000000,
-		.tIR_min = 0,
-		.tITC_max = 1000000,
-		.tRC_min = 25000,
-		.tREA_max = 20000,
-		.tREH_min = 10000,
-		.tRHOH_min = 15000,
-		.tRHW_min = 100000,
-		.tRHZ_max = 100000,
-		.tRLOH_min = 5000,
-		.tRP_min = 12000,
-		.tRR_min = 20000,
-		.tRST_max = 500000000,
-		.tWB_max = 100000,
-		.tWC_min = 25000,
-		.tWH_min = 10000,
-		.tWHR_min = 80000,
-		.tWP_min = 12000,
-		.tWW_min = 100000,
+		.type = NAND_SDR_IFACE,
+		.timings.sdr = {
+			.tADL_min = 70000,
+			.tALH_min = 5000,
+			.tALS_min = 10000,
+			.tAR_min = 10000,
+			.tCEA_max = 25000,
+			.tCEH_min = 20000,
+			.tCH_min = 5000,
+			.tCHZ_max = 30000,
+			.tCLH_min = 5000,
+			.tCLR_min = 10000,
+			.tCLS_min = 10000,
+			.tCOH_min = 15000,
+			.tCS_min = 20000,
+			.tDH_min = 5000,
+			.tDS_min = 10000,
+			.tFEAT_max = 1000000,
+			.tIR_min = 0,
+			.tITC_max = 1000000,
+			.tRC_min = 25000,
+			.tREA_max = 20000,
+			.tREH_min = 10000,
+			.tRHOH_min = 15000,
+			.tRHW_min = 100000,
+			.tRHZ_max = 100000,
+			.tRLOH_min = 5000,
+			.tRP_min = 12000,
+			.tRR_min = 20000,
+			.tRST_max = 500000000,
+			.tWB_max = 100000,
+			.tWC_min = 25000,
+			.tWH_min = 10000,
+			.tWHR_min = 80000,
+			.tWP_min = 12000,
+			.tWW_min = 100000,
+		},
 	},
 	/* Mode 5 */
 	{
-		.tADL_min = 70000,
-		.tALH_min = 5000,
-		.tALS_min = 10000,
-		.tAR_min = 10000,
-		.tCEA_max = 25000,
-		.tCEH_min = 20000,
-		.tCH_min = 5000,
-		.tCHZ_max = 30000,
-		.tCLH_min = 5000,
-		.tCLR_min = 10000,
-		.tCLS_min = 10000,
-		.tCOH_min = 15000,
-		.tCS_min = 15000,
-		.tDH_min = 5000,
-		.tDS_min = 7000,
-		.tFEAT_max = 1000000,
-		.tIR_min = 0,
-		.tITC_max = 1000000,
-		.tRC_min = 20000,
-		.tREA_max = 16000,
-		.tREH_min = 7000,
-		.tRHOH_min = 15000,
-		.tRHW_min = 100000,
-		.tRHZ_max = 100000,
-		.tRLOH_min = 5000,
-		.tRP_min = 10000,
-		.tRR_min = 20000,
-		.tRST_max = 500000000,
-		.tWB_max = 100000,
-		.tWC_min = 20000,
-		.tWH_min = 7000,
-		.tWHR_min = 80000,
-		.tWP_min = 10000,
-		.tWW_min = 100000,
+		.type = NAND_SDR_IFACE,
+		.timings.sdr = {
+			.tADL_min = 70000,
+			.tALH_min = 5000,
+			.tALS_min = 10000,
+			.tAR_min = 10000,
+			.tCEA_max = 25000,
+			.tCEH_min = 20000,
+			.tCH_min = 5000,
+			.tCHZ_max = 30000,
+			.tCLH_min = 5000,
+			.tCLR_min = 10000,
+			.tCLS_min = 10000,
+			.tCOH_min = 15000,
+			.tCS_min = 15000,
+			.tDH_min = 5000,
+			.tDS_min = 7000,
+			.tFEAT_max = 1000000,
+			.tIR_min = 0,
+			.tITC_max = 1000000,
+			.tRC_min = 20000,
+			.tREA_max = 16000,
+			.tREH_min = 7000,
+			.tRHOH_min = 15000,
+			.tRHW_min = 100000,
+			.tRHZ_max = 100000,
+			.tRLOH_min = 5000,
+			.tRP_min = 10000,
+			.tRR_min = 20000,
+			.tRST_max = 500000000,
+			.tWB_max = 100000,
+			.tWC_min = 20000,
+			.tWH_min = 7000,
+			.tWHR_min = 80000,
+			.tWP_min = 10000,
+			.tWW_min = 100000,
+		},
 	},
 };
 
@@ -248,6 +266,20 @@ const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode)
 	if (mode < 0 || mode >= ARRAY_SIZE(onfi_sdr_timings))
 		return ERR_PTR(-EINVAL);
 
-	return &onfi_sdr_timings[mode];
+	return &onfi_sdr_timings[mode].timings.sdr;
 }
 EXPORT_SYMBOL(onfi_async_timing_mode_to_sdr_timings);
+
+/**
+ * onfi_async_timing_mode_to_data_interface - [NAND Interface] Retrieve NAND
+ * data interface according to the given ONFI timing mode
+ * @mode: ONFI timing mode
+ */
+const struct nand_data_interface *onfi_async_timing_mode_to_data_interface(int mode)
+{
+	if (mode < 0 || mode >= ARRAY_SIZE(onfi_sdr_timings))
+		return ERR_PTR(-EINVAL);
+
+	return &onfi_sdr_timings[mode];
+}
+EXPORT_SYMBOL(onfi_async_timing_mode_to_data_interface);
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 19c73ef..a8bdf6c 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -1088,6 +1088,8 @@ static inline int jedec_feature(struct nand_chip *chip)
 
 /* get timing characteristics from ONFI timing mode. */
 const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode);
+/* get data interface from ONFI timing mode. */
+const struct nand_data_interface *onfi_async_timing_mode_to_data_interface(int mode);
 
 int nand_check_erased_ecc_chunk(void *data, int datalen,
 				void *ecc, int ecclen,
-- 
2.8.1

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

* [PATCH 3/7] mtd: nand: convert ONFI mode into data interface
@ 2016-09-06 10:39   ` Sascha Hauer
  0 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 10:39 UTC (permalink / raw)
  To: linux-arm-kernel

struct nand_data_interface is the designated type to pass to
the NAND drivers to configure the timing. To simplify this introduce
onfi_async_timing_mode_to_data_interface() to convert a ONFI
mode into a nand_data_interface.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/mtd/nand/nand_timings.c | 444 +++++++++++++++++++++-------------------
 include/linux/mtd/nand.h        |   2 +
 2 files changed, 240 insertions(+), 206 deletions(-)

diff --git a/drivers/mtd/nand/nand_timings.c b/drivers/mtd/nand/nand_timings.c
index e81470a..260b074 100644
--- a/drivers/mtd/nand/nand_timings.c
+++ b/drivers/mtd/nand/nand_timings.c
@@ -13,228 +13,246 @@
 #include <linux/export.h>
 #include <linux/mtd/nand.h>
 
-static const struct nand_sdr_timings onfi_sdr_timings[] = {
+static const struct nand_data_interface onfi_sdr_timings[] = {
 	/* Mode 0 */
 	{
-		.tADL_min = 200000,
-		.tALH_min = 20000,
-		.tALS_min = 50000,
-		.tAR_min = 25000,
-		.tCEA_max = 100000,
-		.tCEH_min = 20000,
-		.tCH_min = 20000,
-		.tCHZ_max = 100000,
-		.tCLH_min = 20000,
-		.tCLR_min = 20000,
-		.tCLS_min = 50000,
-		.tCOH_min = 0,
-		.tCS_min = 70000,
-		.tDH_min = 20000,
-		.tDS_min = 40000,
-		.tFEAT_max = 1000000,
-		.tIR_min = 10000,
-		.tITC_max = 1000000,
-		.tRC_min = 100000,
-		.tREA_max = 40000,
-		.tREH_min = 30000,
-		.tRHOH_min = 0,
-		.tRHW_min = 200000,
-		.tRHZ_max = 200000,
-		.tRLOH_min = 0,
-		.tRP_min = 50000,
-		.tRST_max = 250000000000ULL,
-		.tWB_max = 200000,
-		.tRR_min = 40000,
-		.tWC_min = 100000,
-		.tWH_min = 30000,
-		.tWHR_min = 120000,
-		.tWP_min = 50000,
-		.tWW_min = 100000,
+		.type = NAND_SDR_IFACE,
+		.timings.sdr = {
+			.tADL_min = 200000,
+			.tALH_min = 20000,
+			.tALS_min = 50000,
+			.tAR_min = 25000,
+			.tCEA_max = 100000,
+			.tCEH_min = 20000,
+			.tCH_min = 20000,
+			.tCHZ_max = 100000,
+			.tCLH_min = 20000,
+			.tCLR_min = 20000,
+			.tCLS_min = 50000,
+			.tCOH_min = 0,
+			.tCS_min = 70000,
+			.tDH_min = 20000,
+			.tDS_min = 40000,
+			.tFEAT_max = 1000000,
+			.tIR_min = 10000,
+			.tITC_max = 1000000,
+			.tRC_min = 100000,
+			.tREA_max = 40000,
+			.tREH_min = 30000,
+			.tRHOH_min = 0,
+			.tRHW_min = 200000,
+			.tRHZ_max = 200000,
+			.tRLOH_min = 0,
+			.tRP_min = 50000,
+			.tRST_max = 250000000000ULL,
+			.tWB_max = 200000,
+			.tRR_min = 40000,
+			.tWC_min = 100000,
+			.tWH_min = 30000,
+			.tWHR_min = 120000,
+			.tWP_min = 50000,
+			.tWW_min = 100000,
+		},
 	},
 	/* Mode 1 */
 	{
-		.tADL_min = 100000,
-		.tALH_min = 10000,
-		.tALS_min = 25000,
-		.tAR_min = 10000,
-		.tCEA_max = 45000,
-		.tCEH_min = 20000,
-		.tCH_min = 10000,
-		.tCHZ_max = 50000,
-		.tCLH_min = 10000,
-		.tCLR_min = 10000,
-		.tCLS_min = 25000,
-		.tCOH_min = 15000,
-		.tCS_min = 35000,
-		.tDH_min = 10000,
-		.tDS_min = 20000,
-		.tFEAT_max = 1000000,
-		.tIR_min = 0,
-		.tITC_max = 1000000,
-		.tRC_min = 50000,
-		.tREA_max = 30000,
-		.tREH_min = 15000,
-		.tRHOH_min = 15000,
-		.tRHW_min = 100000,
-		.tRHZ_max = 100000,
-		.tRLOH_min = 0,
-		.tRP_min = 25000,
-		.tRR_min = 20000,
-		.tRST_max = 500000000,
-		.tWB_max = 100000,
-		.tWC_min = 45000,
-		.tWH_min = 15000,
-		.tWHR_min = 80000,
-		.tWP_min = 25000,
-		.tWW_min = 100000,
+		.type = NAND_SDR_IFACE,
+		.timings.sdr = {
+			.tADL_min = 100000,
+			.tALH_min = 10000,
+			.tALS_min = 25000,
+			.tAR_min = 10000,
+			.tCEA_max = 45000,
+			.tCEH_min = 20000,
+			.tCH_min = 10000,
+			.tCHZ_max = 50000,
+			.tCLH_min = 10000,
+			.tCLR_min = 10000,
+			.tCLS_min = 25000,
+			.tCOH_min = 15000,
+			.tCS_min = 35000,
+			.tDH_min = 10000,
+			.tDS_min = 20000,
+			.tFEAT_max = 1000000,
+			.tIR_min = 0,
+			.tITC_max = 1000000,
+			.tRC_min = 50000,
+			.tREA_max = 30000,
+			.tREH_min = 15000,
+			.tRHOH_min = 15000,
+			.tRHW_min = 100000,
+			.tRHZ_max = 100000,
+			.tRLOH_min = 0,
+			.tRP_min = 25000,
+			.tRR_min = 20000,
+			.tRST_max = 500000000,
+			.tWB_max = 100000,
+			.tWC_min = 45000,
+			.tWH_min = 15000,
+			.tWHR_min = 80000,
+			.tWP_min = 25000,
+			.tWW_min = 100000,
+		},
 	},
 	/* Mode 2 */
 	{
-		.tADL_min = 100000,
-		.tALH_min = 10000,
-		.tALS_min = 15000,
-		.tAR_min = 10000,
-		.tCEA_max = 30000,
-		.tCEH_min = 20000,
-		.tCH_min = 10000,
-		.tCHZ_max = 50000,
-		.tCLH_min = 10000,
-		.tCLR_min = 10000,
-		.tCLS_min = 15000,
-		.tCOH_min = 15000,
-		.tCS_min = 25000,
-		.tDH_min = 5000,
-		.tDS_min = 15000,
-		.tFEAT_max = 1000000,
-		.tIR_min = 0,
-		.tITC_max = 1000000,
-		.tRC_min = 35000,
-		.tREA_max = 25000,
-		.tREH_min = 15000,
-		.tRHOH_min = 15000,
-		.tRHW_min = 100000,
-		.tRHZ_max = 100000,
-		.tRLOH_min = 0,
-		.tRR_min = 20000,
-		.tRST_max = 500000000,
-		.tWB_max = 100000,
-		.tRP_min = 17000,
-		.tWC_min = 35000,
-		.tWH_min = 15000,
-		.tWHR_min = 80000,
-		.tWP_min = 17000,
-		.tWW_min = 100000,
+		.type = NAND_SDR_IFACE,
+		.timings.sdr = {
+			.tADL_min = 100000,
+			.tALH_min = 10000,
+			.tALS_min = 15000,
+			.tAR_min = 10000,
+			.tCEA_max = 30000,
+			.tCEH_min = 20000,
+			.tCH_min = 10000,
+			.tCHZ_max = 50000,
+			.tCLH_min = 10000,
+			.tCLR_min = 10000,
+			.tCLS_min = 15000,
+			.tCOH_min = 15000,
+			.tCS_min = 25000,
+			.tDH_min = 5000,
+			.tDS_min = 15000,
+			.tFEAT_max = 1000000,
+			.tIR_min = 0,
+			.tITC_max = 1000000,
+			.tRC_min = 35000,
+			.tREA_max = 25000,
+			.tREH_min = 15000,
+			.tRHOH_min = 15000,
+			.tRHW_min = 100000,
+			.tRHZ_max = 100000,
+			.tRLOH_min = 0,
+			.tRR_min = 20000,
+			.tRST_max = 500000000,
+			.tWB_max = 100000,
+			.tRP_min = 17000,
+			.tWC_min = 35000,
+			.tWH_min = 15000,
+			.tWHR_min = 80000,
+			.tWP_min = 17000,
+			.tWW_min = 100000,
+		},
 	},
 	/* Mode 3 */
 	{
-		.tADL_min = 100000,
-		.tALH_min = 5000,
-		.tALS_min = 10000,
-		.tAR_min = 10000,
-		.tCEA_max = 25000,
-		.tCEH_min = 20000,
-		.tCH_min = 5000,
-		.tCHZ_max = 50000,
-		.tCLH_min = 5000,
-		.tCLR_min = 10000,
-		.tCLS_min = 10000,
-		.tCOH_min = 15000,
-		.tCS_min = 25000,
-		.tDH_min = 5000,
-		.tDS_min = 10000,
-		.tFEAT_max = 1000000,
-		.tIR_min = 0,
-		.tITC_max = 1000000,
-		.tRC_min = 30000,
-		.tREA_max = 20000,
-		.tREH_min = 10000,
-		.tRHOH_min = 15000,
-		.tRHW_min = 100000,
-		.tRHZ_max = 100000,
-		.tRLOH_min = 0,
-		.tRP_min = 15000,
-		.tRR_min = 20000,
-		.tRST_max = 500000000,
-		.tWB_max = 100000,
-		.tWC_min = 30000,
-		.tWH_min = 10000,
-		.tWHR_min = 80000,
-		.tWP_min = 15000,
-		.tWW_min = 100000,
+		.type = NAND_SDR_IFACE,
+		.timings.sdr = {
+			.tADL_min = 100000,
+			.tALH_min = 5000,
+			.tALS_min = 10000,
+			.tAR_min = 10000,
+			.tCEA_max = 25000,
+			.tCEH_min = 20000,
+			.tCH_min = 5000,
+			.tCHZ_max = 50000,
+			.tCLH_min = 5000,
+			.tCLR_min = 10000,
+			.tCLS_min = 10000,
+			.tCOH_min = 15000,
+			.tCS_min = 25000,
+			.tDH_min = 5000,
+			.tDS_min = 10000,
+			.tFEAT_max = 1000000,
+			.tIR_min = 0,
+			.tITC_max = 1000000,
+			.tRC_min = 30000,
+			.tREA_max = 20000,
+			.tREH_min = 10000,
+			.tRHOH_min = 15000,
+			.tRHW_min = 100000,
+			.tRHZ_max = 100000,
+			.tRLOH_min = 0,
+			.tRP_min = 15000,
+			.tRR_min = 20000,
+			.tRST_max = 500000000,
+			.tWB_max = 100000,
+			.tWC_min = 30000,
+			.tWH_min = 10000,
+			.tWHR_min = 80000,
+			.tWP_min = 15000,
+			.tWW_min = 100000,
+		},
 	},
 	/* Mode 4 */
 	{
-		.tADL_min = 70000,
-		.tALH_min = 5000,
-		.tALS_min = 10000,
-		.tAR_min = 10000,
-		.tCEA_max = 25000,
-		.tCEH_min = 20000,
-		.tCH_min = 5000,
-		.tCHZ_max = 30000,
-		.tCLH_min = 5000,
-		.tCLR_min = 10000,
-		.tCLS_min = 10000,
-		.tCOH_min = 15000,
-		.tCS_min = 20000,
-		.tDH_min = 5000,
-		.tDS_min = 10000,
-		.tFEAT_max = 1000000,
-		.tIR_min = 0,
-		.tITC_max = 1000000,
-		.tRC_min = 25000,
-		.tREA_max = 20000,
-		.tREH_min = 10000,
-		.tRHOH_min = 15000,
-		.tRHW_min = 100000,
-		.tRHZ_max = 100000,
-		.tRLOH_min = 5000,
-		.tRP_min = 12000,
-		.tRR_min = 20000,
-		.tRST_max = 500000000,
-		.tWB_max = 100000,
-		.tWC_min = 25000,
-		.tWH_min = 10000,
-		.tWHR_min = 80000,
-		.tWP_min = 12000,
-		.tWW_min = 100000,
+		.type = NAND_SDR_IFACE,
+		.timings.sdr = {
+			.tADL_min = 70000,
+			.tALH_min = 5000,
+			.tALS_min = 10000,
+			.tAR_min = 10000,
+			.tCEA_max = 25000,
+			.tCEH_min = 20000,
+			.tCH_min = 5000,
+			.tCHZ_max = 30000,
+			.tCLH_min = 5000,
+			.tCLR_min = 10000,
+			.tCLS_min = 10000,
+			.tCOH_min = 15000,
+			.tCS_min = 20000,
+			.tDH_min = 5000,
+			.tDS_min = 10000,
+			.tFEAT_max = 1000000,
+			.tIR_min = 0,
+			.tITC_max = 1000000,
+			.tRC_min = 25000,
+			.tREA_max = 20000,
+			.tREH_min = 10000,
+			.tRHOH_min = 15000,
+			.tRHW_min = 100000,
+			.tRHZ_max = 100000,
+			.tRLOH_min = 5000,
+			.tRP_min = 12000,
+			.tRR_min = 20000,
+			.tRST_max = 500000000,
+			.tWB_max = 100000,
+			.tWC_min = 25000,
+			.tWH_min = 10000,
+			.tWHR_min = 80000,
+			.tWP_min = 12000,
+			.tWW_min = 100000,
+		},
 	},
 	/* Mode 5 */
 	{
-		.tADL_min = 70000,
-		.tALH_min = 5000,
-		.tALS_min = 10000,
-		.tAR_min = 10000,
-		.tCEA_max = 25000,
-		.tCEH_min = 20000,
-		.tCH_min = 5000,
-		.tCHZ_max = 30000,
-		.tCLH_min = 5000,
-		.tCLR_min = 10000,
-		.tCLS_min = 10000,
-		.tCOH_min = 15000,
-		.tCS_min = 15000,
-		.tDH_min = 5000,
-		.tDS_min = 7000,
-		.tFEAT_max = 1000000,
-		.tIR_min = 0,
-		.tITC_max = 1000000,
-		.tRC_min = 20000,
-		.tREA_max = 16000,
-		.tREH_min = 7000,
-		.tRHOH_min = 15000,
-		.tRHW_min = 100000,
-		.tRHZ_max = 100000,
-		.tRLOH_min = 5000,
-		.tRP_min = 10000,
-		.tRR_min = 20000,
-		.tRST_max = 500000000,
-		.tWB_max = 100000,
-		.tWC_min = 20000,
-		.tWH_min = 7000,
-		.tWHR_min = 80000,
-		.tWP_min = 10000,
-		.tWW_min = 100000,
+		.type = NAND_SDR_IFACE,
+		.timings.sdr = {
+			.tADL_min = 70000,
+			.tALH_min = 5000,
+			.tALS_min = 10000,
+			.tAR_min = 10000,
+			.tCEA_max = 25000,
+			.tCEH_min = 20000,
+			.tCH_min = 5000,
+			.tCHZ_max = 30000,
+			.tCLH_min = 5000,
+			.tCLR_min = 10000,
+			.tCLS_min = 10000,
+			.tCOH_min = 15000,
+			.tCS_min = 15000,
+			.tDH_min = 5000,
+			.tDS_min = 7000,
+			.tFEAT_max = 1000000,
+			.tIR_min = 0,
+			.tITC_max = 1000000,
+			.tRC_min = 20000,
+			.tREA_max = 16000,
+			.tREH_min = 7000,
+			.tRHOH_min = 15000,
+			.tRHW_min = 100000,
+			.tRHZ_max = 100000,
+			.tRLOH_min = 5000,
+			.tRP_min = 10000,
+			.tRR_min = 20000,
+			.tRST_max = 500000000,
+			.tWB_max = 100000,
+			.tWC_min = 20000,
+			.tWH_min = 7000,
+			.tWHR_min = 80000,
+			.tWP_min = 10000,
+			.tWW_min = 100000,
+		},
 	},
 };
 
@@ -248,6 +266,20 @@ const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode)
 	if (mode < 0 || mode >= ARRAY_SIZE(onfi_sdr_timings))
 		return ERR_PTR(-EINVAL);
 
-	return &onfi_sdr_timings[mode];
+	return &onfi_sdr_timings[mode].timings.sdr;
 }
 EXPORT_SYMBOL(onfi_async_timing_mode_to_sdr_timings);
+
+/**
+ * onfi_async_timing_mode_to_data_interface - [NAND Interface] Retrieve NAND
+ * data interface according to the given ONFI timing mode
+ * @mode: ONFI timing mode
+ */
+const struct nand_data_interface *onfi_async_timing_mode_to_data_interface(int mode)
+{
+	if (mode < 0 || mode >= ARRAY_SIZE(onfi_sdr_timings))
+		return ERR_PTR(-EINVAL);
+
+	return &onfi_sdr_timings[mode];
+}
+EXPORT_SYMBOL(onfi_async_timing_mode_to_data_interface);
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 19c73ef..a8bdf6c 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -1088,6 +1088,8 @@ static inline int jedec_feature(struct nand_chip *chip)
 
 /* get timing characteristics from ONFI timing mode. */
 const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode);
+/* get data interface from ONFI timing mode. */
+const struct nand_data_interface *onfi_async_timing_mode_to_data_interface(int mode);
 
 int nand_check_erased_ecc_chunk(void *data, int datalen,
 				void *ecc, int ecclen,
-- 
2.8.1

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

* [PATCH 4/7] mtd: nand: automate NAND timings selection
  2016-09-06 10:39 ` Sascha Hauer
@ 2016-09-06 10:39   ` Sascha Hauer
  -1 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 10:39 UTC (permalink / raw)
  To: linux-mtd; +Cc: Boris Brezillon, kernel, linux-arm-kernel, Sascha Hauer

From: Boris Brezillon <boris.brezillon@free-electrons.com>

The NAND framework provides several helpers to query timing modes supported
by a NAND chip, but this implies that all NAND controller drivers have
to implement the same timings selection dance. Also currently NAND
devices can be resetted at arbitrary places which also resets the timing
for ONFI chips to timing mode 0.

Provide a common logic to select the best timings based on ONFI or
->onfi_timing_mode_default information. Hook this into nand_reset()
to make sure the new timing is applied each time during a reset.

NAND controller willing to support timings adjustment should just
implement the ->setup_data_interface() method.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/mtd/nand/nand_base.c | 112 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/mtd/nand.h     |  14 ++++--
 2 files changed, 122 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 20151fc..37852e9 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -955,8 +955,63 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
  */
 int nand_reset(struct mtd_info *mtd)
 {
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	int ret;
+
+	if (chip->setup_data_interface) {
+		struct nand_data_interface conf = {
+			.type = NAND_SDR_IFACE,
+			.timings.sdr = *onfi_async_timing_mode_to_sdr_timings(0),
+		};
+
+		/*
+		 * The ONFI specification says:
+		 * "
+		 * To transition from NV-DDR or NV-DDR2 to the SDR data
+		 * interface, the host shall use the Reset (FFh) command
+		 * using SDR timing mode 0. A device in any timing mode is
+		 * required to recognize Reset (FFh) command issued in SDR
+		 * timing mode 0.
+		 * "
+		 *
+		 * Configure the data interface in SDR mode and set the
+		 * timings to timing mode 0.
+		 */
+
+		ret = chip->setup_data_interface(mtd, &conf, false);
+		if (ret) {
+			pr_err("Failed to configure data interface to SDR timing mode 0\n");
+			return ret;
+		}
+	}
+
 	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
 
+	/*
+	 * Setup the NAND interface (interface type + timings).
+	 */
+	if (chip->data_iface) {
+		uint8_t tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = {
+			chip->onfi_timing_mode_default,
+		};
+
+		/*
+		 * Ensure the timing mode has be changed on the chip side
+		 * before changing timings on the controller side.
+		 */
+		if (chip->onfi_version) {
+			ret = chip->onfi_set_features(mtd, chip,
+					ONFI_FEATURE_ADDR_TIMING_MODE,
+					tmode_param);
+			if (ret)
+				return ret;
+		}
+
+		ret = chip->setup_data_interface(mtd, chip->data_iface, false);
+		if (ret)
+			return ret;
+	}
+
 	return 0;
 }
 
@@ -3335,6 +3390,54 @@ static void nand_onfi_detect_micron(struct nand_chip *chip,
 	chip->setup_read_retry = nand_setup_read_retry_micron;
 }
 
+/**
+ * nand_find_data_interface - Find the best data interface and timings
+ * @mtd: MTD device structure
+ *
+ * Try to find the best data interface and NAND timings supported by the
+ * chip and the driver.
+ * First tries to retrieve supported timing modes from ONFI information,
+ * and if the NAND chip does not support ONFI, relies on the
+ * ->onfi_timing_mode_default specified in the nand_ids table.
+ *
+ * Returns 0 for success or negative error code otherwise.
+ */
+static int nand_find_data_interface(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	int modes, mode, ret;
+	const struct nand_data_interface *conf;
+
+	/*
+	 * First try to identify the best timings from ONFI parameters and
+	 * if the NAND does not support ONFI, fallback to the default ONFI
+	 * timing mode.
+	 */
+	modes = onfi_get_async_timing_mode(chip);
+	if (modes == ONFI_TIMING_MODE_UNKNOWN)
+		modes = GENMASK(chip->onfi_timing_mode_default, 0);
+
+	ret = -EINVAL;
+	for (mode = fls(modes) - 1; mode >= 0; mode--) {
+		conf = onfi_async_timing_mode_to_data_interface(mode);
+
+		ret = chip->setup_data_interface(mtd, conf, true);
+		if (!ret) {
+			chip->onfi_timing_mode_default = mode;
+			break;
+		}
+	}
+
+	if (ret)
+		return ret;
+
+	chip->data_iface = kmemdup(conf, sizeof(*conf), GFP_KERNEL);
+	if (!chip->data_iface)
+		return -ENOMEM;
+
+	return 0;
+}
+
 /*
  * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
  */
@@ -4168,6 +4271,12 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
 		return PTR_ERR(type);
 	}
 
+	if (chip->setup_data_interface) {
+		ret = nand_find_data_interface(mtd);
+		if (ret)
+			return ret;
+	}
+
 	chip->select_chip(mtd, -1);
 
 	/* Check for a chip array */
@@ -4627,6 +4736,9 @@ void nand_release(struct mtd_info *mtd)
 
 	mtd_device_unregister(mtd);
 
+	/* Free interface config struct */
+	kfree(chip->data_iface);
+
 	/* Free bad block table memory */
 	kfree(chip->bbt);
 	if (!(chip->options & NAND_OWN_BUFFERS))
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index a8bdf6c..5269357 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -690,10 +690,9 @@ struct nand_data_interface {
  *                      also from the datasheet. It is the recommended ECC step
  *			size, if known; if unknown, set to zero.
  * @onfi_timing_mode_default: [INTERN] default ONFI timing mode. This field is
- *			      either deduced from the datasheet if the NAND
- *			      chip is not ONFI compliant or set to 0 if it is
- *			      (an ONFI chip is always configured in mode 0
- *			      after a NAND reset)
+ * 			      set to the actually used ONFI mode if the chip is
+ * 			      ONFI compliant or deduced from the datasheet if
+ * 			      the NAND chip is not ONFI compliant.
  * @numchips:		[INTERN] number of physical chips
  * @chipsize:		[INTERN] the size of one chip for multichip arrays
  * @pagemask:		[INTERN] page number mask = number of (pages / chip) - 1
@@ -713,6 +712,7 @@ struct nand_data_interface {
  * @read_retries:	[INTERN] the number of read retry modes supported
  * @onfi_set_features:	[REPLACEABLE] set the features for ONFI nand
  * @onfi_get_features:	[REPLACEABLE] get the features for ONFI nand
+ * @setup_data_interface: [OPTIONAL] setup the data interface and timing
  * @bbt:		[INTERN] bad block table pointer
  * @bbt_td:		[REPLACEABLE] bad block table descriptor for flash
  *			lookup.
@@ -759,6 +759,10 @@ struct nand_chip {
 	int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
 			int feature_addr, uint8_t *subfeature_para);
 	int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode);
+	int (*setup_data_interface)(struct mtd_info *mtd,
+				    const struct nand_data_interface *conf,
+				    bool check_only);
+
 
 	int chip_delay;
 	unsigned int options;
@@ -788,6 +792,8 @@ struct nand_chip {
 		struct nand_jedec_params jedec_params;
 	};
 
+	const struct nand_data_interface *data_iface;
+
 	int read_retries;
 
 	flstate_t state;
-- 
2.8.1

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

* [PATCH 4/7] mtd: nand: automate NAND timings selection
@ 2016-09-06 10:39   ` Sascha Hauer
  0 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 10:39 UTC (permalink / raw)
  To: linux-arm-kernel

From: Boris Brezillon <boris.brezillon@free-electrons.com>

The NAND framework provides several helpers to query timing modes supported
by a NAND chip, but this implies that all NAND controller drivers have
to implement the same timings selection dance. Also currently NAND
devices can be resetted at arbitrary places which also resets the timing
for ONFI chips to timing mode 0.

Provide a common logic to select the best timings based on ONFI or
->onfi_timing_mode_default information. Hook this into nand_reset()
to make sure the new timing is applied each time during a reset.

NAND controller willing to support timings adjustment should just
implement the ->setup_data_interface() method.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/mtd/nand/nand_base.c | 112 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/mtd/nand.h     |  14 ++++--
 2 files changed, 122 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 20151fc..37852e9 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -955,8 +955,63 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
  */
 int nand_reset(struct mtd_info *mtd)
 {
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	int ret;
+
+	if (chip->setup_data_interface) {
+		struct nand_data_interface conf = {
+			.type = NAND_SDR_IFACE,
+			.timings.sdr = *onfi_async_timing_mode_to_sdr_timings(0),
+		};
+
+		/*
+		 * The ONFI specification says:
+		 * "
+		 * To transition from NV-DDR or NV-DDR2 to the SDR data
+		 * interface, the host shall use the Reset (FFh) command
+		 * using SDR timing mode 0. A device in any timing mode is
+		 * required to recognize Reset (FFh) command issued in SDR
+		 * timing mode 0.
+		 * "
+		 *
+		 * Configure the data interface in SDR mode and set the
+		 * timings to timing mode 0.
+		 */
+
+		ret = chip->setup_data_interface(mtd, &conf, false);
+		if (ret) {
+			pr_err("Failed to configure data interface to SDR timing mode 0\n");
+			return ret;
+		}
+	}
+
 	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
 
+	/*
+	 * Setup the NAND interface (interface type + timings).
+	 */
+	if (chip->data_iface) {
+		uint8_t tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = {
+			chip->onfi_timing_mode_default,
+		};
+
+		/*
+		 * Ensure the timing mode has be changed on the chip side
+		 * before changing timings on the controller side.
+		 */
+		if (chip->onfi_version) {
+			ret = chip->onfi_set_features(mtd, chip,
+					ONFI_FEATURE_ADDR_TIMING_MODE,
+					tmode_param);
+			if (ret)
+				return ret;
+		}
+
+		ret = chip->setup_data_interface(mtd, chip->data_iface, false);
+		if (ret)
+			return ret;
+	}
+
 	return 0;
 }
 
@@ -3335,6 +3390,54 @@ static void nand_onfi_detect_micron(struct nand_chip *chip,
 	chip->setup_read_retry = nand_setup_read_retry_micron;
 }
 
+/**
+ * nand_find_data_interface - Find the best data interface and timings
+ * @mtd: MTD device structure
+ *
+ * Try to find the best data interface and NAND timings supported by the
+ * chip and the driver.
+ * First tries to retrieve supported timing modes from ONFI information,
+ * and if the NAND chip does not support ONFI, relies on the
+ * ->onfi_timing_mode_default specified in the nand_ids table.
+ *
+ * Returns 0 for success or negative error code otherwise.
+ */
+static int nand_find_data_interface(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	int modes, mode, ret;
+	const struct nand_data_interface *conf;
+
+	/*
+	 * First try to identify the best timings from ONFI parameters and
+	 * if the NAND does not support ONFI, fallback to the default ONFI
+	 * timing mode.
+	 */
+	modes = onfi_get_async_timing_mode(chip);
+	if (modes == ONFI_TIMING_MODE_UNKNOWN)
+		modes = GENMASK(chip->onfi_timing_mode_default, 0);
+
+	ret = -EINVAL;
+	for (mode = fls(modes) - 1; mode >= 0; mode--) {
+		conf = onfi_async_timing_mode_to_data_interface(mode);
+
+		ret = chip->setup_data_interface(mtd, conf, true);
+		if (!ret) {
+			chip->onfi_timing_mode_default = mode;
+			break;
+		}
+	}
+
+	if (ret)
+		return ret;
+
+	chip->data_iface = kmemdup(conf, sizeof(*conf), GFP_KERNEL);
+	if (!chip->data_iface)
+		return -ENOMEM;
+
+	return 0;
+}
+
 /*
  * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
  */
@@ -4168,6 +4271,12 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
 		return PTR_ERR(type);
 	}
 
+	if (chip->setup_data_interface) {
+		ret = nand_find_data_interface(mtd);
+		if (ret)
+			return ret;
+	}
+
 	chip->select_chip(mtd, -1);
 
 	/* Check for a chip array */
@@ -4627,6 +4736,9 @@ void nand_release(struct mtd_info *mtd)
 
 	mtd_device_unregister(mtd);
 
+	/* Free interface config struct */
+	kfree(chip->data_iface);
+
 	/* Free bad block table memory */
 	kfree(chip->bbt);
 	if (!(chip->options & NAND_OWN_BUFFERS))
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index a8bdf6c..5269357 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -690,10 +690,9 @@ struct nand_data_interface {
  *                      also from the datasheet. It is the recommended ECC step
  *			size, if known; if unknown, set to zero.
  * @onfi_timing_mode_default: [INTERN] default ONFI timing mode. This field is
- *			      either deduced from the datasheet if the NAND
- *			      chip is not ONFI compliant or set to 0 if it is
- *			      (an ONFI chip is always configured in mode 0
- *			      after a NAND reset)
+ * 			      set to the actually used ONFI mode if the chip is
+ * 			      ONFI compliant or deduced from the datasheet if
+ * 			      the NAND chip is not ONFI compliant.
  * @numchips:		[INTERN] number of physical chips
  * @chipsize:		[INTERN] the size of one chip for multichip arrays
  * @pagemask:		[INTERN] page number mask = number of (pages / chip) - 1
@@ -713,6 +712,7 @@ struct nand_data_interface {
  * @read_retries:	[INTERN] the number of read retry modes supported
  * @onfi_set_features:	[REPLACEABLE] set the features for ONFI nand
  * @onfi_get_features:	[REPLACEABLE] get the features for ONFI nand
+ * @setup_data_interface: [OPTIONAL] setup the data interface and timing
  * @bbt:		[INTERN] bad block table pointer
  * @bbt_td:		[REPLACEABLE] bad block table descriptor for flash
  *			lookup.
@@ -759,6 +759,10 @@ struct nand_chip {
 	int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
 			int feature_addr, uint8_t *subfeature_para);
 	int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode);
+	int (*setup_data_interface)(struct mtd_info *mtd,
+				    const struct nand_data_interface *conf,
+				    bool check_only);
+
 
 	int chip_delay;
 	unsigned int options;
@@ -788,6 +792,8 @@ struct nand_chip {
 		struct nand_jedec_params jedec_params;
 	};
 
+	const struct nand_data_interface *data_iface;
+
 	int read_retries;
 
 	flstate_t state;
-- 
2.8.1

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

* [PATCH 5/7] mtd: nand: sunxi: switch from manual to automated timing config
  2016-09-06 10:39 ` Sascha Hauer
@ 2016-09-06 10:39   ` Sascha Hauer
  -1 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 10:39 UTC (permalink / raw)
  To: linux-mtd; +Cc: Boris Brezillon, kernel, linux-arm-kernel, Sascha Hauer

The NAND framework is now able to select the best NAND timings for us.
All we have to do is implement a ->setup_data_interface() function to
apply those timings and remove the timing selection code from the sunxi
driver.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/mtd/nand/sunxi_nand.c | 77 +++++++++----------------------------------
 1 file changed, 15 insertions(+), 62 deletions(-)

diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
index e414b31..ee42b8c 100644
--- a/drivers/mtd/nand/sunxi_nand.c
+++ b/drivers/mtd/nand/sunxi_nand.c
@@ -1572,14 +1572,23 @@ static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
 #define sunxi_nand_lookup_timing(l, p, c) \
 			_sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c)
 
-static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
-				       const struct nand_sdr_timings *timings)
+static int sunxi_nfc_setup_data_interface(struct mtd_info *mtd,
+					    const struct nand_data_interface *conf,
+					    bool check_only)
 {
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct sunxi_nand_chip *chip = to_sunxi_nand(nand);
 	struct sunxi_nfc *nfc = to_sunxi_nfc(chip->nand.controller);
+	const struct nand_sdr_timings *timings;
 	u32 min_clk_period = 0;
 	s32 tWB, tADL, tWHR, tRHW, tCAD;
 	long real_clk_rate;
 
+	if (conf->type != NAND_SDR_IFACE)
+		return -ENOTSUPP;
+
+	timings = &conf->timings.sdr;
+
 	/* T1 <=> tCLS */
 	if (timings->tCLS_min > min_clk_period)
 		min_clk_period = timings->tCLS_min;
@@ -1679,6 +1688,9 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
 		return tRHW;
 	}
 
+	if (check_only)
+		return 0;
+
 	/*
 	 * TODO: according to ONFI specs this value only applies for DDR NAND,
 	 * but Allwinner seems to set this to 0x7. Mimic them for now.
@@ -1712,44 +1724,6 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
 	return 0;
 }
 
-static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip,
-					struct device_node *np)
-{
-	struct mtd_info *mtd = nand_to_mtd(&chip->nand);
-	const struct nand_sdr_timings *timings;
-	int ret;
-	int mode;
-
-	mode = onfi_get_async_timing_mode(&chip->nand);
-	if (mode == ONFI_TIMING_MODE_UNKNOWN) {
-		mode = chip->nand.onfi_timing_mode_default;
-	} else {
-		uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {};
-		int i;
-
-		mode = fls(mode) - 1;
-		if (mode < 0)
-			mode = 0;
-
-		feature[0] = mode;
-		for (i = 0; i < chip->nsels; i++) {
-			chip->nand.select_chip(mtd, i);
-			ret = chip->nand.onfi_set_features(mtd,	&chip->nand,
-						ONFI_FEATURE_ADDR_TIMING_MODE,
-						feature);
-			chip->nand.select_chip(mtd, -1);
-			if (ret)
-				return ret;
-		}
-	}
-
-	timings = onfi_async_timing_mode_to_sdr_timings(mode);
-	if (IS_ERR(timings))
-		return PTR_ERR(timings);
-
-	return sunxi_nand_chip_set_timings(chip, timings);
-}
-
 static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
 				    struct mtd_oob_region *oobregion)
 {
@@ -1975,7 +1949,6 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc,
 static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
 				struct device_node *np)
 {
-	const struct nand_sdr_timings *timings;
 	struct sunxi_nand_chip *chip;
 	struct mtd_info *mtd;
 	struct nand_chip *nand;
@@ -2065,25 +2038,11 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
 	nand->read_buf = sunxi_nfc_read_buf;
 	nand->write_buf = sunxi_nfc_write_buf;
 	nand->read_byte = sunxi_nfc_read_byte;
+	nand->setup_data_interface = sunxi_nfc_setup_data_interface;
 
 	mtd = nand_to_mtd(nand);
 	mtd->dev.parent = dev;
 
-	timings = onfi_async_timing_mode_to_sdr_timings(0);
-	if (IS_ERR(timings)) {
-		ret = PTR_ERR(timings);
-		dev_err(dev,
-			"could not retrieve timings for ONFI mode 0: %d\n",
-			ret);
-		return ret;
-	}
-
-	ret = sunxi_nand_chip_set_timings(chip, timings);
-	if (ret) {
-		dev_err(dev, "could not configure chip timings: %d\n", ret);
-		return ret;
-	}
-
 	ret = nand_scan_ident(mtd, nsels, NULL);
 	if (ret)
 		return ret;
@@ -2096,12 +2055,6 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
 
 	nand->options |= NAND_SUBPAGE_READ;
 
-	ret = sunxi_nand_chip_init_timings(chip, np);
-	if (ret) {
-		dev_err(dev, "could not configure chip timings: %d\n", ret);
-		return ret;
-	}
-
 	ret = sunxi_nand_ecc_init(mtd, &nand->ecc, np);
 	if (ret) {
 		dev_err(dev, "ECC init failed: %d\n", ret);
-- 
2.8.1

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

* [PATCH 5/7] mtd: nand: sunxi: switch from manual to automated timing config
@ 2016-09-06 10:39   ` Sascha Hauer
  0 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 10:39 UTC (permalink / raw)
  To: linux-arm-kernel

The NAND framework is now able to select the best NAND timings for us.
All we have to do is implement a ->setup_data_interface() function to
apply those timings and remove the timing selection code from the sunxi
driver.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/mtd/nand/sunxi_nand.c | 77 +++++++++----------------------------------
 1 file changed, 15 insertions(+), 62 deletions(-)

diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
index e414b31..ee42b8c 100644
--- a/drivers/mtd/nand/sunxi_nand.c
+++ b/drivers/mtd/nand/sunxi_nand.c
@@ -1572,14 +1572,23 @@ static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
 #define sunxi_nand_lookup_timing(l, p, c) \
 			_sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c)
 
-static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
-				       const struct nand_sdr_timings *timings)
+static int sunxi_nfc_setup_data_interface(struct mtd_info *mtd,
+					    const struct nand_data_interface *conf,
+					    bool check_only)
 {
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct sunxi_nand_chip *chip = to_sunxi_nand(nand);
 	struct sunxi_nfc *nfc = to_sunxi_nfc(chip->nand.controller);
+	const struct nand_sdr_timings *timings;
 	u32 min_clk_period = 0;
 	s32 tWB, tADL, tWHR, tRHW, tCAD;
 	long real_clk_rate;
 
+	if (conf->type != NAND_SDR_IFACE)
+		return -ENOTSUPP;
+
+	timings = &conf->timings.sdr;
+
 	/* T1 <=> tCLS */
 	if (timings->tCLS_min > min_clk_period)
 		min_clk_period = timings->tCLS_min;
@@ -1679,6 +1688,9 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
 		return tRHW;
 	}
 
+	if (check_only)
+		return 0;
+
 	/*
 	 * TODO: according to ONFI specs this value only applies for DDR NAND,
 	 * but Allwinner seems to set this to 0x7. Mimic them for now.
@@ -1712,44 +1724,6 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
 	return 0;
 }
 
-static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip,
-					struct device_node *np)
-{
-	struct mtd_info *mtd = nand_to_mtd(&chip->nand);
-	const struct nand_sdr_timings *timings;
-	int ret;
-	int mode;
-
-	mode = onfi_get_async_timing_mode(&chip->nand);
-	if (mode == ONFI_TIMING_MODE_UNKNOWN) {
-		mode = chip->nand.onfi_timing_mode_default;
-	} else {
-		uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {};
-		int i;
-
-		mode = fls(mode) - 1;
-		if (mode < 0)
-			mode = 0;
-
-		feature[0] = mode;
-		for (i = 0; i < chip->nsels; i++) {
-			chip->nand.select_chip(mtd, i);
-			ret = chip->nand.onfi_set_features(mtd,	&chip->nand,
-						ONFI_FEATURE_ADDR_TIMING_MODE,
-						feature);
-			chip->nand.select_chip(mtd, -1);
-			if (ret)
-				return ret;
-		}
-	}
-
-	timings = onfi_async_timing_mode_to_sdr_timings(mode);
-	if (IS_ERR(timings))
-		return PTR_ERR(timings);
-
-	return sunxi_nand_chip_set_timings(chip, timings);
-}
-
 static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
 				    struct mtd_oob_region *oobregion)
 {
@@ -1975,7 +1949,6 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc,
 static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
 				struct device_node *np)
 {
-	const struct nand_sdr_timings *timings;
 	struct sunxi_nand_chip *chip;
 	struct mtd_info *mtd;
 	struct nand_chip *nand;
@@ -2065,25 +2038,11 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
 	nand->read_buf = sunxi_nfc_read_buf;
 	nand->write_buf = sunxi_nfc_write_buf;
 	nand->read_byte = sunxi_nfc_read_byte;
+	nand->setup_data_interface = sunxi_nfc_setup_data_interface;
 
 	mtd = nand_to_mtd(nand);
 	mtd->dev.parent = dev;
 
-	timings = onfi_async_timing_mode_to_sdr_timings(0);
-	if (IS_ERR(timings)) {
-		ret = PTR_ERR(timings);
-		dev_err(dev,
-			"could not retrieve timings for ONFI mode 0: %d\n",
-			ret);
-		return ret;
-	}
-
-	ret = sunxi_nand_chip_set_timings(chip, timings);
-	if (ret) {
-		dev_err(dev, "could not configure chip timings: %d\n", ret);
-		return ret;
-	}
-
 	ret = nand_scan_ident(mtd, nsels, NULL);
 	if (ret)
 		return ret;
@@ -2096,12 +2055,6 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
 
 	nand->options |= NAND_SUBPAGE_READ;
 
-	ret = sunxi_nand_chip_init_timings(chip, np);
-	if (ret) {
-		dev_err(dev, "could not configure chip timings: %d\n", ret);
-		return ret;
-	}
-
 	ret = sunxi_nand_ecc_init(mtd, &nand->ecc, np);
 	if (ret) {
 		dev_err(dev, "ECC init failed: %d\n", ret);
-- 
2.8.1

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

* [PATCH 6/7] mtd: nand: mxc: implement onfi get/set features
  2016-09-06 10:39 ` Sascha Hauer
@ 2016-09-06 10:39   ` Sascha Hauer
  -1 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 10:39 UTC (permalink / raw)
  To: linux-mtd; +Cc: Boris Brezillon, kernel, linux-arm-kernel, Sascha Hauer

To be able to support different ONFI timing modes we have to implement
the onfi_set_features and onfi_get_features. Tested on an i.MX25 SoC.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/mtd/nand/mxc_nand.c | 53 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)

diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
index 5173fad..1db8299 100644
--- a/drivers/mtd/nand/mxc_nand.c
+++ b/drivers/mtd/nand/mxc_nand.c
@@ -1239,6 +1239,57 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
 	}
 }
 
+static int mxc_nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
+			int addr, uint8_t *subfeature_param)
+{
+	struct nand_chip *nand_chip = mtd_to_nand(mtd);
+	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+	int i;
+
+	if (!chip->onfi_version ||
+	    !(le16_to_cpu(chip->onfi_params.opt_cmd)
+	      & ONFI_OPT_CMD_SET_GET_FEATURES))
+		return -EINVAL;
+
+	host->buf_start = 0;
+
+	for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
+		chip->write_byte(mtd, subfeature_param[i]);
+
+	memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize);
+	host->devtype_data->send_cmd(host, NAND_CMD_SET_FEATURES, false);
+	mxc_do_addr_cycle(mtd, addr, -1);
+	host->devtype_data->send_page(mtd, NFC_INPUT);
+
+	return 0;
+}
+
+static int mxc_nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
+			int addr, uint8_t *subfeature_param)
+{
+	struct nand_chip *nand_chip = mtd_to_nand(mtd);
+	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+	int i;
+
+	if (!chip->onfi_version ||
+	    !(le16_to_cpu(chip->onfi_params.opt_cmd)
+	      & ONFI_OPT_CMD_SET_GET_FEATURES))
+		return -EINVAL;
+
+	*(uint32_t *)host->main_area0 = 0xdeadbeef;
+
+	host->devtype_data->send_cmd(host, NAND_CMD_GET_FEATURES, false);
+	mxc_do_addr_cycle(mtd, addr, -1);
+	host->devtype_data->send_page(mtd, NFC_OUTPUT);
+	memcpy32_fromio(host->data_buf, host->main_area0, 512);
+	host->buf_start = 0;
+
+	for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
+		*subfeature_param++ = chip->read_byte(mtd);
+
+	return 0;
+}
+
 /*
  * The generic flash bbt decriptors overlap with our ecc
  * hardware, so define some i.MX specific ones.
@@ -1513,6 +1564,8 @@ static int mxcnd_probe(struct platform_device *pdev)
 	this->read_word = mxc_nand_read_word;
 	this->write_buf = mxc_nand_write_buf;
 	this->read_buf = mxc_nand_read_buf;
+	this->onfi_set_features = mxc_nand_onfi_set_features;
+	this->onfi_get_features = mxc_nand_onfi_get_features;
 
 	host->clk = devm_clk_get(&pdev->dev, NULL);
 	if (IS_ERR(host->clk))
-- 
2.8.1

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

* [PATCH 6/7] mtd: nand: mxc: implement onfi get/set features
@ 2016-09-06 10:39   ` Sascha Hauer
  0 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 10:39 UTC (permalink / raw)
  To: linux-arm-kernel

To be able to support different ONFI timing modes we have to implement
the onfi_set_features and onfi_get_features. Tested on an i.MX25 SoC.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/mtd/nand/mxc_nand.c | 53 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)

diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
index 5173fad..1db8299 100644
--- a/drivers/mtd/nand/mxc_nand.c
+++ b/drivers/mtd/nand/mxc_nand.c
@@ -1239,6 +1239,57 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
 	}
 }
 
+static int mxc_nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
+			int addr, uint8_t *subfeature_param)
+{
+	struct nand_chip *nand_chip = mtd_to_nand(mtd);
+	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+	int i;
+
+	if (!chip->onfi_version ||
+	    !(le16_to_cpu(chip->onfi_params.opt_cmd)
+	      & ONFI_OPT_CMD_SET_GET_FEATURES))
+		return -EINVAL;
+
+	host->buf_start = 0;
+
+	for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
+		chip->write_byte(mtd, subfeature_param[i]);
+
+	memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize);
+	host->devtype_data->send_cmd(host, NAND_CMD_SET_FEATURES, false);
+	mxc_do_addr_cycle(mtd, addr, -1);
+	host->devtype_data->send_page(mtd, NFC_INPUT);
+
+	return 0;
+}
+
+static int mxc_nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
+			int addr, uint8_t *subfeature_param)
+{
+	struct nand_chip *nand_chip = mtd_to_nand(mtd);
+	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+	int i;
+
+	if (!chip->onfi_version ||
+	    !(le16_to_cpu(chip->onfi_params.opt_cmd)
+	      & ONFI_OPT_CMD_SET_GET_FEATURES))
+		return -EINVAL;
+
+	*(uint32_t *)host->main_area0 = 0xdeadbeef;
+
+	host->devtype_data->send_cmd(host, NAND_CMD_GET_FEATURES, false);
+	mxc_do_addr_cycle(mtd, addr, -1);
+	host->devtype_data->send_page(mtd, NFC_OUTPUT);
+	memcpy32_fromio(host->data_buf, host->main_area0, 512);
+	host->buf_start = 0;
+
+	for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
+		*subfeature_param++ = chip->read_byte(mtd);
+
+	return 0;
+}
+
 /*
  * The generic flash bbt decriptors overlap with our ecc
  * hardware, so define some i.MX specific ones.
@@ -1513,6 +1564,8 @@ static int mxcnd_probe(struct platform_device *pdev)
 	this->read_word = mxc_nand_read_word;
 	this->write_buf = mxc_nand_write_buf;
 	this->read_buf = mxc_nand_read_buf;
+	this->onfi_set_features = mxc_nand_onfi_set_features;
+	this->onfi_get_features = mxc_nand_onfi_get_features;
 
 	host->clk = devm_clk_get(&pdev->dev, NULL);
 	if (IS_ERR(host->clk))
-- 
2.8.1

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

* [PATCH 7/7] mtd: nand: mxc: Add timing setup for v2 controllers
  2016-09-06 10:39 ` Sascha Hauer
@ 2016-09-06 10:39   ` Sascha Hauer
  -1 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 10:39 UTC (permalink / raw)
  To: linux-mtd; +Cc: Boris Brezillon, kernel, linux-arm-kernel, Sascha Hauer

So far we relied on reset default or the bootloader to configure a
suitable clk rate for the Nand controller. This works but we can
optimize the timing for better performance. This sets the clk rate for
v2 controllers (i.MX25/35) based on the timing mode read from the ONFI
parameter page. This may also enable the symmetric mode (aks EDO mode)
if necessary which reads one word per clock cycle.
Tested on an i.MX25 with a Micron MT29F4G08ABBDAHC attached.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/mtd/nand/mxc_nand.c | 83 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 83 insertions(+)

diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
index 1db8299..1fe3484 100644
--- a/drivers/mtd/nand/mxc_nand.c
+++ b/drivers/mtd/nand/mxc_nand.c
@@ -152,6 +152,9 @@ struct mxc_nand_devtype_data {
 	void (*select_chip)(struct mtd_info *mtd, int chip);
 	int (*correct_data)(struct mtd_info *mtd, u_char *dat,
 			u_char *read_ecc, u_char *calc_ecc);
+	int (*setup_data_interface)(struct mtd_info *mtd,
+			const struct nand_data_interface *conf,
+			bool check_only);
 
 	/*
 	 * On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked
@@ -1012,6 +1015,83 @@ static void preset_v1(struct mtd_info *mtd)
 	writew(0x4, NFC_V1_V2_WRPROT);
 }
 
+static int mxc_nand_v2_setup_data_interface(struct mtd_info *mtd,
+					    const struct nand_data_interface *conf,
+					    bool check_only)
+{
+	struct nand_chip *nand_chip = mtd_to_nand(mtd);
+	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+	int tRC_min_ns, tRC_ps, ret;
+	unsigned long rate, rate_round;
+	const struct nand_sdr_timings *timings;
+	uint16_t config1;
+
+	if (conf->type != NAND_SDR_IFACE)
+		return -ENOTSUPP;
+
+	config1 = readw(NFC_V1_V2_CONFIG1);
+
+	timings = &conf->timings.sdr;
+
+	tRC_min_ns = timings->tRC_min / 1000;
+	rate = 1000000000 / tRC_min_ns;
+
+	/*
+	 * For tRC < 30ns we have to use EDO mode. In this case the controller
+	 * does one access per clock cycle. Otherwise the controller does one
+	 * access in two clock cycles, thus we have to double the rate to the
+	 * controller.
+	 */
+	if (tRC_min_ns < 30) {
+		rate_round = clk_round_rate(host->clk, rate);
+		config1 |= NFC_V2_CONFIG1_ONE_CYCLE;
+		tRC_ps = 1000000000 / (rate_round / 1000);
+	} else {
+		rate *= 2;
+		rate_round = clk_round_rate(host->clk, rate);
+		config1 &= ~NFC_V2_CONFIG1_ONE_CYCLE;
+		tRC_ps = 1000000000 / (rate_round / 1000 / 2);
+	}
+
+	/*
+	 * The timing values compared against are from the i.MX25 Automotive
+	 * datasheet, Table 50. NFC Timing Parameters
+	 */
+	if (timings->tCLS_min > tRC_ps - 1000 ||
+	    timings->tCLH_min > tRC_ps - 2000 ||
+	    timings->tCS_min > tRC_ps - 1000 ||
+	    timings->tCH_min > tRC_ps - 2000 ||
+	    timings->tWP_min > tRC_ps - 1500 ||
+	    timings->tALS_min > tRC_ps ||
+	    timings->tALH_min > tRC_ps - 3000 ||
+	    timings->tDS_min > tRC_ps ||
+	    timings->tDH_min > tRC_ps - 5000 ||
+	    timings->tWC_min > 2 * tRC_ps ||
+	    timings->tWH_min > tRC_ps - 2500 ||
+	    timings->tRR_min > 6 * tRC_ps ||
+	    timings->tRP_min > 3 * tRC_ps / 2 ||
+	    timings->tRC_min > 2 * tRC_ps ||
+	    timings->tREH_min > (tRC_ps / 2) - 2500) {
+		dev_dbg(host->dev, "Timing out of bounds\n");
+		return -EINVAL;
+	}
+
+	if (check_only)
+		return 0;
+
+	ret = clk_set_rate(host->clk, rate);
+	if (ret)
+		return ret;
+
+	writew(config1, NFC_V1_V2_CONFIG1);
+
+	dev_dbg(host->dev, "Setting rate to %ldHz, %s mode\n", rate_round,
+		config1 & NFC_V2_CONFIG1_ONE_CYCLE ? "One cycle (EDO)" :
+		"normal");
+
+	return 0;
+}
+
 static void preset_v2(struct mtd_info *mtd)
 {
 	struct nand_chip *nand_chip = mtd_to_nand(mtd);
@@ -1378,6 +1458,7 @@ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = {
 	.ooblayout = &mxc_v2_ooblayout_ops,
 	.select_chip = mxc_nand_select_chip_v2,
 	.correct_data = mxc_nand_correct_data_v2_v3,
+	.setup_data_interface = mxc_nand_v2_setup_data_interface,
 	.irqpending_quirk = 0,
 	.needs_ip = 0,
 	.regs_offset = 0x1e00,
@@ -1586,6 +1667,8 @@ static int mxcnd_probe(struct platform_device *pdev)
 	if (err < 0)
 		return err;
 
+	this->setup_data_interface = host->devtype_data->setup_data_interface;
+
 	if (host->devtype_data->needs_ip) {
 		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 		host->regs_ip = devm_ioremap_resource(&pdev->dev, res);
-- 
2.8.1

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

* [PATCH 7/7] mtd: nand: mxc: Add timing setup for v2 controllers
@ 2016-09-06 10:39   ` Sascha Hauer
  0 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 10:39 UTC (permalink / raw)
  To: linux-arm-kernel

So far we relied on reset default or the bootloader to configure a
suitable clk rate for the Nand controller. This works but we can
optimize the timing for better performance. This sets the clk rate for
v2 controllers (i.MX25/35) based on the timing mode read from the ONFI
parameter page. This may also enable the symmetric mode (aks EDO mode)
if necessary which reads one word per clock cycle.
Tested on an i.MX25 with a Micron MT29F4G08ABBDAHC attached.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/mtd/nand/mxc_nand.c | 83 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 83 insertions(+)

diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
index 1db8299..1fe3484 100644
--- a/drivers/mtd/nand/mxc_nand.c
+++ b/drivers/mtd/nand/mxc_nand.c
@@ -152,6 +152,9 @@ struct mxc_nand_devtype_data {
 	void (*select_chip)(struct mtd_info *mtd, int chip);
 	int (*correct_data)(struct mtd_info *mtd, u_char *dat,
 			u_char *read_ecc, u_char *calc_ecc);
+	int (*setup_data_interface)(struct mtd_info *mtd,
+			const struct nand_data_interface *conf,
+			bool check_only);
 
 	/*
 	 * On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked
@@ -1012,6 +1015,83 @@ static void preset_v1(struct mtd_info *mtd)
 	writew(0x4, NFC_V1_V2_WRPROT);
 }
 
+static int mxc_nand_v2_setup_data_interface(struct mtd_info *mtd,
+					    const struct nand_data_interface *conf,
+					    bool check_only)
+{
+	struct nand_chip *nand_chip = mtd_to_nand(mtd);
+	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
+	int tRC_min_ns, tRC_ps, ret;
+	unsigned long rate, rate_round;
+	const struct nand_sdr_timings *timings;
+	uint16_t config1;
+
+	if (conf->type != NAND_SDR_IFACE)
+		return -ENOTSUPP;
+
+	config1 = readw(NFC_V1_V2_CONFIG1);
+
+	timings = &conf->timings.sdr;
+
+	tRC_min_ns = timings->tRC_min / 1000;
+	rate = 1000000000 / tRC_min_ns;
+
+	/*
+	 * For tRC < 30ns we have to use EDO mode. In this case the controller
+	 * does one access per clock cycle. Otherwise the controller does one
+	 * access in two clock cycles, thus we have to double the rate to the
+	 * controller.
+	 */
+	if (tRC_min_ns < 30) {
+		rate_round = clk_round_rate(host->clk, rate);
+		config1 |= NFC_V2_CONFIG1_ONE_CYCLE;
+		tRC_ps = 1000000000 / (rate_round / 1000);
+	} else {
+		rate *= 2;
+		rate_round = clk_round_rate(host->clk, rate);
+		config1 &= ~NFC_V2_CONFIG1_ONE_CYCLE;
+		tRC_ps = 1000000000 / (rate_round / 1000 / 2);
+	}
+
+	/*
+	 * The timing values compared against are from the i.MX25 Automotive
+	 * datasheet, Table 50. NFC Timing Parameters
+	 */
+	if (timings->tCLS_min > tRC_ps - 1000 ||
+	    timings->tCLH_min > tRC_ps - 2000 ||
+	    timings->tCS_min > tRC_ps - 1000 ||
+	    timings->tCH_min > tRC_ps - 2000 ||
+	    timings->tWP_min > tRC_ps - 1500 ||
+	    timings->tALS_min > tRC_ps ||
+	    timings->tALH_min > tRC_ps - 3000 ||
+	    timings->tDS_min > tRC_ps ||
+	    timings->tDH_min > tRC_ps - 5000 ||
+	    timings->tWC_min > 2 * tRC_ps ||
+	    timings->tWH_min > tRC_ps - 2500 ||
+	    timings->tRR_min > 6 * tRC_ps ||
+	    timings->tRP_min > 3 * tRC_ps / 2 ||
+	    timings->tRC_min > 2 * tRC_ps ||
+	    timings->tREH_min > (tRC_ps / 2) - 2500) {
+		dev_dbg(host->dev, "Timing out of bounds\n");
+		return -EINVAL;
+	}
+
+	if (check_only)
+		return 0;
+
+	ret = clk_set_rate(host->clk, rate);
+	if (ret)
+		return ret;
+
+	writew(config1, NFC_V1_V2_CONFIG1);
+
+	dev_dbg(host->dev, "Setting rate to %ldHz, %s mode\n", rate_round,
+		config1 & NFC_V2_CONFIG1_ONE_CYCLE ? "One cycle (EDO)" :
+		"normal");
+
+	return 0;
+}
+
 static void preset_v2(struct mtd_info *mtd)
 {
 	struct nand_chip *nand_chip = mtd_to_nand(mtd);
@@ -1378,6 +1458,7 @@ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = {
 	.ooblayout = &mxc_v2_ooblayout_ops,
 	.select_chip = mxc_nand_select_chip_v2,
 	.correct_data = mxc_nand_correct_data_v2_v3,
+	.setup_data_interface = mxc_nand_v2_setup_data_interface,
 	.irqpending_quirk = 0,
 	.needs_ip = 0,
 	.regs_offset = 0x1e00,
@@ -1586,6 +1667,8 @@ static int mxcnd_probe(struct platform_device *pdev)
 	if (err < 0)
 		return err;
 
+	this->setup_data_interface = host->devtype_data->setup_data_interface;
+
 	if (host->devtype_data->needs_ip) {
 		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 		host->regs_ip = devm_ioremap_resource(&pdev->dev, res);
-- 
2.8.1

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

* Re: [PATCH 1/7] mtd: nand: Create a NAND reset function
  2016-09-06 10:39   ` Sascha Hauer
@ 2016-09-06 11:18     ` Boris Brezillon
  -1 siblings, 0 replies; 52+ messages in thread
From: Boris Brezillon @ 2016-09-06 11:18 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-mtd, kernel, linux-arm-kernel

On Tue,  6 Sep 2016 12:39:09 +0200
Sascha Hauer <s.hauer@pengutronix.de> wrote:

> When NAND devices are resetted some initialization may have to be done,
> like for example they have to be configured for the timing mode that
> shall be used. To get a common place where this initialization can be
> implemented create a nand_reset() function. This currently only issues
> a NAND_CMD_RESET to the NAND device. The places issuing this command
> manually are replaced with a call to nand_reset().
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  drivers/mtd/nand/nand_base.c | 23 ++++++++++++++++++-----
>  include/linux/mtd/nand.h     |  3 +++
>  2 files changed, 21 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> index 77533f7..20151fc 100644
> --- a/drivers/mtd/nand/nand_base.c
> +++ b/drivers/mtd/nand/nand_base.c
> @@ -948,6 +948,19 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
>  }
>  
>  /**
> + * nand_reset - Reset and initialize a NAND device
> + * @mtd: mtd info
> + *
> + * Returns 0 for success or negative error code otherwise
> + */
> +int nand_reset(struct mtd_info *mtd)

Can we pass a struct nand_chip * here?

> +{
> +	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);

You miss the chip variable.

> +
> +	return 0;
> +}
> +
> +/**
>   * __nand_unlock - [REPLACEABLE] unlocks specified locked blocks
>   * @mtd: mtd info
>   * @ofs: offset to start unlock from
> @@ -1025,7 +1038,7 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
>  	 * some operation can also clear the bit 7 of status register
>  	 * eg. erase/program a locked block
>  	 */
> -	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
> +	nand_reset(mtd);
>  
>  	/* Check, if it is write protected */
>  	if (nand_check_wp(mtd)) {
> @@ -1084,7 +1097,7 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
>  	 * some operation can also clear the bit 7 of status register
>  	 * eg. erase/program a locked block
>  	 */
> -	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
> +	nand_reset(mtd);
>  
>  	/* Check, if it is write protected */
>  	if (nand_check_wp(mtd)) {
> @@ -2788,7 +2801,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
>  	 * if we don't do this. I have no clue why, but I seem to have 'fixed'
>  	 * it in the doc2000 driver in August 1999.  dwmw2.
>  	 */
> -	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
> +	nand_reset(mtd);
>  
>  	/* Check, if it is write protected */
>  	if (nand_check_wp(mtd)) {
> @@ -3829,7 +3842,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
>  	 * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
>  	 * after power-up.
>  	 */
> -	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
> +	nand_reset(mtd);
>  
>  	/* Send the command for reading device ID */
>  	chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
> @@ -4161,7 +4174,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
>  	for (i = 1; i < maxchips; i++) {
>  		chip->select_chip(mtd, i);
>  		/* See comment in nand_get_flash_type for reset */
> -		chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
> +		nand_reset(mtd);
>  		/* Send the command for reading device ID */
>  		chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
>  		/* Read manufacturer and device IDs */
> diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
> index 8dd6e01..9af9575 100644
> --- a/include/linux/mtd/nand.h
> +++ b/include/linux/mtd/nand.h
> @@ -50,6 +50,9 @@ extern int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
>  /* unlocks specified locked blocks */
>  extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
>  
> +/* Reset and initialize a NAND device */
> +extern int nand_reset(struct mtd_info *mtd);

The extern keyword is not needed here.

> +
>  /* The maximum number of NAND chips in an array */
>  #define NAND_MAX_CHIPS		8
>  

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

* [PATCH 1/7] mtd: nand: Create a NAND reset function
@ 2016-09-06 11:18     ` Boris Brezillon
  0 siblings, 0 replies; 52+ messages in thread
From: Boris Brezillon @ 2016-09-06 11:18 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue,  6 Sep 2016 12:39:09 +0200
Sascha Hauer <s.hauer@pengutronix.de> wrote:

> When NAND devices are resetted some initialization may have to be done,
> like for example they have to be configured for the timing mode that
> shall be used. To get a common place where this initialization can be
> implemented create a nand_reset() function. This currently only issues
> a NAND_CMD_RESET to the NAND device. The places issuing this command
> manually are replaced with a call to nand_reset().
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  drivers/mtd/nand/nand_base.c | 23 ++++++++++++++++++-----
>  include/linux/mtd/nand.h     |  3 +++
>  2 files changed, 21 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> index 77533f7..20151fc 100644
> --- a/drivers/mtd/nand/nand_base.c
> +++ b/drivers/mtd/nand/nand_base.c
> @@ -948,6 +948,19 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
>  }
>  
>  /**
> + * nand_reset - Reset and initialize a NAND device
> + * @mtd: mtd info
> + *
> + * Returns 0 for success or negative error code otherwise
> + */
> +int nand_reset(struct mtd_info *mtd)

Can we pass a struct nand_chip * here?

> +{
> +	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);

You miss the chip variable.

> +
> +	return 0;
> +}
> +
> +/**
>   * __nand_unlock - [REPLACEABLE] unlocks specified locked blocks
>   * @mtd: mtd info
>   * @ofs: offset to start unlock from
> @@ -1025,7 +1038,7 @@ int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
>  	 * some operation can also clear the bit 7 of status register
>  	 * eg. erase/program a locked block
>  	 */
> -	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
> +	nand_reset(mtd);
>  
>  	/* Check, if it is write protected */
>  	if (nand_check_wp(mtd)) {
> @@ -1084,7 +1097,7 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
>  	 * some operation can also clear the bit 7 of status register
>  	 * eg. erase/program a locked block
>  	 */
> -	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
> +	nand_reset(mtd);
>  
>  	/* Check, if it is write protected */
>  	if (nand_check_wp(mtd)) {
> @@ -2788,7 +2801,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
>  	 * if we don't do this. I have no clue why, but I seem to have 'fixed'
>  	 * it in the doc2000 driver in August 1999.  dwmw2.
>  	 */
> -	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
> +	nand_reset(mtd);
>  
>  	/* Check, if it is write protected */
>  	if (nand_check_wp(mtd)) {
> @@ -3829,7 +3842,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
>  	 * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
>  	 * after power-up.
>  	 */
> -	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
> +	nand_reset(mtd);
>  
>  	/* Send the command for reading device ID */
>  	chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
> @@ -4161,7 +4174,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
>  	for (i = 1; i < maxchips; i++) {
>  		chip->select_chip(mtd, i);
>  		/* See comment in nand_get_flash_type for reset */
> -		chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
> +		nand_reset(mtd);
>  		/* Send the command for reading device ID */
>  		chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
>  		/* Read manufacturer and device IDs */
> diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
> index 8dd6e01..9af9575 100644
> --- a/include/linux/mtd/nand.h
> +++ b/include/linux/mtd/nand.h
> @@ -50,6 +50,9 @@ extern int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
>  /* unlocks specified locked blocks */
>  extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
>  
> +/* Reset and initialize a NAND device */
> +extern int nand_reset(struct mtd_info *mtd);

The extern keyword is not needed here.

> +
>  /* The maximum number of NAND chips in an array */
>  #define NAND_MAX_CHIPS		8
>  

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

* Re: [PATCH 2/7] mtd: nand: Introduce nand_data_interface
  2016-09-06 10:39   ` Sascha Hauer
@ 2016-09-06 11:21     ` Boris Brezillon
  -1 siblings, 0 replies; 52+ messages in thread
From: Boris Brezillon @ 2016-09-06 11:21 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-mtd, kernel, linux-arm-kernel

On Tue,  6 Sep 2016 12:39:10 +0200
Sascha Hauer <s.hauer@pengutronix.de> wrote:

> Currently we have no data structure to fully describe a NAND timing.
> We only have struct nand_sdr_timings for NAND timings in SDR mode,
> but nothing for DDR mode and also no container to store both types
> of timing.
> This patch adds struct nand_data_interface which stores the timing
> type and a union of different timings. This can be used to pass to
> drivers in order to configure the timing.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  include/linux/mtd/nand.h | 109 ++++++++++++++++++++++++++---------------------
>  1 file changed, 60 insertions(+), 49 deletions(-)
> 
> diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
> index 9af9575..19c73ef 100644
> --- a/include/linux/mtd/nand.h
> +++ b/include/linux/mtd/nand.h
> @@ -568,6 +568,66 @@ struct nand_buffers {
>  	uint8_t *databuf;
>  };
>  
> +/*
> + * struct nand_sdr_timings - SDR NAND chip timings
> + *
> + * This struct defines the timing requirements of a SDR NAND chip.
> + * These information can be found in every NAND datasheets and the timings
> + * meaning are described in the ONFI specifications:
> + * www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf (chapter 4.15 Timing
> + * Parameters)
> + *
> + * All these timings are expressed in picoseconds.
> + */
> +
> +struct nand_sdr_timings {
> +	u32 tALH_min;
> +	u32 tADL_min;
> +	u32 tALS_min;
> +	u32 tAR_min;
> +	u32 tCEA_max;
> +	u32 tCEH_min;
> +	u32 tCH_min;
> +	u32 tCHZ_max;
> +	u32 tCLH_min;
> +	u32 tCLR_min;
> +	u32 tCLS_min;
> +	u32 tCOH_min;
> +	u32 tCS_min;
> +	u32 tDH_min;
> +	u32 tDS_min;
> +	u32 tFEAT_max;
> +	u32 tIR_min;
> +	u32 tITC_max;
> +	u32 tRC_min;
> +	u32 tREA_max;
> +	u32 tREH_min;
> +	u32 tRHOH_min;
> +	u32 tRHW_min;
> +	u32 tRHZ_max;
> +	u32 tRLOH_min;
> +	u32 tRP_min;
> +	u32 tRR_min;
> +	u64 tRST_max;
> +	u32 tWB_max;
> +	u32 tWC_min;
> +	u32 tWH_min;
> +	u32 tWHR_min;
> +	u32 tWP_min;
> +	u32 tWW_min;
> +};
> +
> +enum nand_data_interface_type {
> +	NAND_SDR_IFACE,
> +};
> +

I know this is my code, and I'm the one to blame here, but can you
document the nand_data_interface fields (kerneldoc format)?

> +struct nand_data_interface {
> +	enum nand_data_interface_type type;
> +	union {
> +		struct nand_sdr_timings sdr;
> +	} timings;
> +};
> +
>  /**
>   * struct nand_chip - NAND Private Flash Chip Data
>   * @mtd:		MTD device registered to the MTD framework
> @@ -1026,55 +1086,6 @@ static inline int jedec_feature(struct nand_chip *chip)
>  		: 0;
>  }
>  
> -/*
> - * struct nand_sdr_timings - SDR NAND chip timings
> - *
> - * This struct defines the timing requirements of a SDR NAND chip.
> - * These informations can be found in every NAND datasheets and the timings
> - * meaning are described in the ONFI specifications:
> - * www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf (chapter 4.15 Timing
> - * Parameters)
> - *
> - * All these timings are expressed in picoseconds.
> - */
> -
> -struct nand_sdr_timings {
> -	u32 tALH_min;
> -	u32 tADL_min;
> -	u32 tALS_min;
> -	u32 tAR_min;
> -	u32 tCEA_max;
> -	u32 tCEH_min;
> -	u32 tCH_min;
> -	u32 tCHZ_max;
> -	u32 tCLH_min;
> -	u32 tCLR_min;
> -	u32 tCLS_min;
> -	u32 tCOH_min;
> -	u32 tCS_min;
> -	u32 tDH_min;
> -	u32 tDS_min;
> -	u32 tFEAT_max;
> -	u32 tIR_min;
> -	u32 tITC_max;
> -	u32 tRC_min;
> -	u32 tREA_max;
> -	u32 tREH_min;
> -	u32 tRHOH_min;
> -	u32 tRHW_min;
> -	u32 tRHZ_max;
> -	u32 tRLOH_min;
> -	u32 tRP_min;
> -	u32 tRR_min;
> -	u64 tRST_max;
> -	u32 tWB_max;
> -	u32 tWC_min;
> -	u32 tWH_min;
> -	u32 tWHR_min;
> -	u32 tWP_min;
> -	u32 tWW_min;
> -};
> -
>  /* get timing characteristics from ONFI timing mode. */
>  const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode);
>  

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

* [PATCH 2/7] mtd: nand: Introduce nand_data_interface
@ 2016-09-06 11:21     ` Boris Brezillon
  0 siblings, 0 replies; 52+ messages in thread
From: Boris Brezillon @ 2016-09-06 11:21 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue,  6 Sep 2016 12:39:10 +0200
Sascha Hauer <s.hauer@pengutronix.de> wrote:

> Currently we have no data structure to fully describe a NAND timing.
> We only have struct nand_sdr_timings for NAND timings in SDR mode,
> but nothing for DDR mode and also no container to store both types
> of timing.
> This patch adds struct nand_data_interface which stores the timing
> type and a union of different timings. This can be used to pass to
> drivers in order to configure the timing.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  include/linux/mtd/nand.h | 109 ++++++++++++++++++++++++++---------------------
>  1 file changed, 60 insertions(+), 49 deletions(-)
> 
> diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
> index 9af9575..19c73ef 100644
> --- a/include/linux/mtd/nand.h
> +++ b/include/linux/mtd/nand.h
> @@ -568,6 +568,66 @@ struct nand_buffers {
>  	uint8_t *databuf;
>  };
>  
> +/*
> + * struct nand_sdr_timings - SDR NAND chip timings
> + *
> + * This struct defines the timing requirements of a SDR NAND chip.
> + * These information can be found in every NAND datasheets and the timings
> + * meaning are described in the ONFI specifications:
> + * www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf (chapter 4.15 Timing
> + * Parameters)
> + *
> + * All these timings are expressed in picoseconds.
> + */
> +
> +struct nand_sdr_timings {
> +	u32 tALH_min;
> +	u32 tADL_min;
> +	u32 tALS_min;
> +	u32 tAR_min;
> +	u32 tCEA_max;
> +	u32 tCEH_min;
> +	u32 tCH_min;
> +	u32 tCHZ_max;
> +	u32 tCLH_min;
> +	u32 tCLR_min;
> +	u32 tCLS_min;
> +	u32 tCOH_min;
> +	u32 tCS_min;
> +	u32 tDH_min;
> +	u32 tDS_min;
> +	u32 tFEAT_max;
> +	u32 tIR_min;
> +	u32 tITC_max;
> +	u32 tRC_min;
> +	u32 tREA_max;
> +	u32 tREH_min;
> +	u32 tRHOH_min;
> +	u32 tRHW_min;
> +	u32 tRHZ_max;
> +	u32 tRLOH_min;
> +	u32 tRP_min;
> +	u32 tRR_min;
> +	u64 tRST_max;
> +	u32 tWB_max;
> +	u32 tWC_min;
> +	u32 tWH_min;
> +	u32 tWHR_min;
> +	u32 tWP_min;
> +	u32 tWW_min;
> +};
> +
> +enum nand_data_interface_type {
> +	NAND_SDR_IFACE,
> +};
> +

I know this is my code, and I'm the one to blame here, but can you
document the nand_data_interface fields (kerneldoc format)?

> +struct nand_data_interface {
> +	enum nand_data_interface_type type;
> +	union {
> +		struct nand_sdr_timings sdr;
> +	} timings;
> +};
> +
>  /**
>   * struct nand_chip - NAND Private Flash Chip Data
>   * @mtd:		MTD device registered to the MTD framework
> @@ -1026,55 +1086,6 @@ static inline int jedec_feature(struct nand_chip *chip)
>  		: 0;
>  }
>  
> -/*
> - * struct nand_sdr_timings - SDR NAND chip timings
> - *
> - * This struct defines the timing requirements of a SDR NAND chip.
> - * These informations can be found in every NAND datasheets and the timings
> - * meaning are described in the ONFI specifications:
> - * www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf (chapter 4.15 Timing
> - * Parameters)
> - *
> - * All these timings are expressed in picoseconds.
> - */
> -
> -struct nand_sdr_timings {
> -	u32 tALH_min;
> -	u32 tADL_min;
> -	u32 tALS_min;
> -	u32 tAR_min;
> -	u32 tCEA_max;
> -	u32 tCEH_min;
> -	u32 tCH_min;
> -	u32 tCHZ_max;
> -	u32 tCLH_min;
> -	u32 tCLR_min;
> -	u32 tCLS_min;
> -	u32 tCOH_min;
> -	u32 tCS_min;
> -	u32 tDH_min;
> -	u32 tDS_min;
> -	u32 tFEAT_max;
> -	u32 tIR_min;
> -	u32 tITC_max;
> -	u32 tRC_min;
> -	u32 tREA_max;
> -	u32 tREH_min;
> -	u32 tRHOH_min;
> -	u32 tRHW_min;
> -	u32 tRHZ_max;
> -	u32 tRLOH_min;
> -	u32 tRP_min;
> -	u32 tRR_min;
> -	u64 tRST_max;
> -	u32 tWB_max;
> -	u32 tWC_min;
> -	u32 tWH_min;
> -	u32 tWHR_min;
> -	u32 tWP_min;
> -	u32 tWW_min;
> -};
> -
>  /* get timing characteristics from ONFI timing mode. */
>  const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode);
>  

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

* Re: [PATCH 3/7] mtd: nand: convert ONFI mode into data interface
  2016-09-06 10:39   ` Sascha Hauer
@ 2016-09-06 11:27     ` Boris Brezillon
  -1 siblings, 0 replies; 52+ messages in thread
From: Boris Brezillon @ 2016-09-06 11:27 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-mtd, kernel, linux-arm-kernel

On Tue,  6 Sep 2016 12:39:11 +0200
Sascha Hauer <s.hauer@pengutronix.de> wrote:

> struct nand_data_interface is the designated type to pass to
> the NAND drivers to configure the timing. To simplify this introduce
> onfi_async_timing_mode_to_data_interface() to convert a ONFI
> mode into a nand_data_interface.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  drivers/mtd/nand/nand_timings.c | 444 +++++++++++++++++++++-------------------
>  include/linux/mtd/nand.h        |   2 +
>  2 files changed, 240 insertions(+), 206 deletions(-)
> 
> diff --git a/drivers/mtd/nand/nand_timings.c b/drivers/mtd/nand/nand_timings.c
> index e81470a..260b074 100644
> --- a/drivers/mtd/nand/nand_timings.c
> +++ b/drivers/mtd/nand/nand_timings.c
> @@ -13,228 +13,246 @@
>  #include <linux/export.h>
>  #include <linux/mtd/nand.h>
>  
> -static const struct nand_sdr_timings onfi_sdr_timings[] = {
> +static const struct nand_data_interface onfi_sdr_timings[] = {

Do you have a good reason to turn onfi_sdr_timings into an array of
struct nand_data_interface?

>  	/* Mode 0 */
>  	{
> -		.tADL_min = 200000,
> -		.tALH_min = 20000,
> -		.tALS_min = 50000,
> -		.tAR_min = 25000,
> -		.tCEA_max = 100000,
> -		.tCEH_min = 20000,
> -		.tCH_min = 20000,
> -		.tCHZ_max = 100000,
> -		.tCLH_min = 20000,
> -		.tCLR_min = 20000,
> -		.tCLS_min = 50000,
> -		.tCOH_min = 0,
> -		.tCS_min = 70000,
> -		.tDH_min = 20000,
> -		.tDS_min = 40000,
> -		.tFEAT_max = 1000000,
> -		.tIR_min = 10000,
> -		.tITC_max = 1000000,
> -		.tRC_min = 100000,
> -		.tREA_max = 40000,
> -		.tREH_min = 30000,
> -		.tRHOH_min = 0,
> -		.tRHW_min = 200000,
> -		.tRHZ_max = 200000,
> -		.tRLOH_min = 0,
> -		.tRP_min = 50000,
> -		.tRST_max = 250000000000ULL,
> -		.tWB_max = 200000,
> -		.tRR_min = 40000,
> -		.tWC_min = 100000,
> -		.tWH_min = 30000,
> -		.tWHR_min = 120000,
> -		.tWP_min = 50000,
> -		.tWW_min = 100000,
> +		.type = NAND_SDR_IFACE,
> +		.timings.sdr = {
> +			.tADL_min = 200000,
> +			.tALH_min = 20000,
> +			.tALS_min = 50000,
> +			.tAR_min = 25000,
> +			.tCEA_max = 100000,
> +			.tCEH_min = 20000,
> +			.tCH_min = 20000,
> +			.tCHZ_max = 100000,
> +			.tCLH_min = 20000,
> +			.tCLR_min = 20000,
> +			.tCLS_min = 50000,
> +			.tCOH_min = 0,
> +			.tCS_min = 70000,
> +			.tDH_min = 20000,
> +			.tDS_min = 40000,
> +			.tFEAT_max = 1000000,
> +			.tIR_min = 10000,
> +			.tITC_max = 1000000,
> +			.tRC_min = 100000,
> +			.tREA_max = 40000,
> +			.tREH_min = 30000,
> +			.tRHOH_min = 0,
> +			.tRHW_min = 200000,
> +			.tRHZ_max = 200000,
> +			.tRLOH_min = 0,
> +			.tRP_min = 50000,
> +			.tRST_max = 250000000000ULL,
> +			.tWB_max = 200000,
> +			.tRR_min = 40000,
> +			.tWC_min = 100000,
> +			.tWH_min = 30000,
> +			.tWHR_min = 120000,
> +			.tWP_min = 50000,
> +			.tWW_min = 100000,
> +		},
>  	},
>  	/* Mode 1 */
>  	{
> -		.tADL_min = 100000,
> -		.tALH_min = 10000,
> -		.tALS_min = 25000,
> -		.tAR_min = 10000,
> -		.tCEA_max = 45000,
> -		.tCEH_min = 20000,
> -		.tCH_min = 10000,
> -		.tCHZ_max = 50000,
> -		.tCLH_min = 10000,
> -		.tCLR_min = 10000,
> -		.tCLS_min = 25000,
> -		.tCOH_min = 15000,
> -		.tCS_min = 35000,
> -		.tDH_min = 10000,
> -		.tDS_min = 20000,
> -		.tFEAT_max = 1000000,
> -		.tIR_min = 0,
> -		.tITC_max = 1000000,
> -		.tRC_min = 50000,
> -		.tREA_max = 30000,
> -		.tREH_min = 15000,
> -		.tRHOH_min = 15000,
> -		.tRHW_min = 100000,
> -		.tRHZ_max = 100000,
> -		.tRLOH_min = 0,
> -		.tRP_min = 25000,
> -		.tRR_min = 20000,
> -		.tRST_max = 500000000,
> -		.tWB_max = 100000,
> -		.tWC_min = 45000,
> -		.tWH_min = 15000,
> -		.tWHR_min = 80000,
> -		.tWP_min = 25000,
> -		.tWW_min = 100000,
> +		.type = NAND_SDR_IFACE,
> +		.timings.sdr = {
> +			.tADL_min = 100000,
> +			.tALH_min = 10000,
> +			.tALS_min = 25000,
> +			.tAR_min = 10000,
> +			.tCEA_max = 45000,
> +			.tCEH_min = 20000,
> +			.tCH_min = 10000,
> +			.tCHZ_max = 50000,
> +			.tCLH_min = 10000,
> +			.tCLR_min = 10000,
> +			.tCLS_min = 25000,
> +			.tCOH_min = 15000,
> +			.tCS_min = 35000,
> +			.tDH_min = 10000,
> +			.tDS_min = 20000,
> +			.tFEAT_max = 1000000,
> +			.tIR_min = 0,
> +			.tITC_max = 1000000,
> +			.tRC_min = 50000,
> +			.tREA_max = 30000,
> +			.tREH_min = 15000,
> +			.tRHOH_min = 15000,
> +			.tRHW_min = 100000,
> +			.tRHZ_max = 100000,
> +			.tRLOH_min = 0,
> +			.tRP_min = 25000,
> +			.tRR_min = 20000,
> +			.tRST_max = 500000000,
> +			.tWB_max = 100000,
> +			.tWC_min = 45000,
> +			.tWH_min = 15000,
> +			.tWHR_min = 80000,
> +			.tWP_min = 25000,
> +			.tWW_min = 100000,
> +		},
>  	},
>  	/* Mode 2 */
>  	{
> -		.tADL_min = 100000,
> -		.tALH_min = 10000,
> -		.tALS_min = 15000,
> -		.tAR_min = 10000,
> -		.tCEA_max = 30000,
> -		.tCEH_min = 20000,
> -		.tCH_min = 10000,
> -		.tCHZ_max = 50000,
> -		.tCLH_min = 10000,
> -		.tCLR_min = 10000,
> -		.tCLS_min = 15000,
> -		.tCOH_min = 15000,
> -		.tCS_min = 25000,
> -		.tDH_min = 5000,
> -		.tDS_min = 15000,
> -		.tFEAT_max = 1000000,
> -		.tIR_min = 0,
> -		.tITC_max = 1000000,
> -		.tRC_min = 35000,
> -		.tREA_max = 25000,
> -		.tREH_min = 15000,
> -		.tRHOH_min = 15000,
> -		.tRHW_min = 100000,
> -		.tRHZ_max = 100000,
> -		.tRLOH_min = 0,
> -		.tRR_min = 20000,
> -		.tRST_max = 500000000,
> -		.tWB_max = 100000,
> -		.tRP_min = 17000,
> -		.tWC_min = 35000,
> -		.tWH_min = 15000,
> -		.tWHR_min = 80000,
> -		.tWP_min = 17000,
> -		.tWW_min = 100000,
> +		.type = NAND_SDR_IFACE,
> +		.timings.sdr = {
> +			.tADL_min = 100000,
> +			.tALH_min = 10000,
> +			.tALS_min = 15000,
> +			.tAR_min = 10000,
> +			.tCEA_max = 30000,
> +			.tCEH_min = 20000,
> +			.tCH_min = 10000,
> +			.tCHZ_max = 50000,
> +			.tCLH_min = 10000,
> +			.tCLR_min = 10000,
> +			.tCLS_min = 15000,
> +			.tCOH_min = 15000,
> +			.tCS_min = 25000,
> +			.tDH_min = 5000,
> +			.tDS_min = 15000,
> +			.tFEAT_max = 1000000,
> +			.tIR_min = 0,
> +			.tITC_max = 1000000,
> +			.tRC_min = 35000,
> +			.tREA_max = 25000,
> +			.tREH_min = 15000,
> +			.tRHOH_min = 15000,
> +			.tRHW_min = 100000,
> +			.tRHZ_max = 100000,
> +			.tRLOH_min = 0,
> +			.tRR_min = 20000,
> +			.tRST_max = 500000000,
> +			.tWB_max = 100000,
> +			.tRP_min = 17000,
> +			.tWC_min = 35000,
> +			.tWH_min = 15000,
> +			.tWHR_min = 80000,
> +			.tWP_min = 17000,
> +			.tWW_min = 100000,
> +		},
>  	},
>  	/* Mode 3 */
>  	{
> -		.tADL_min = 100000,
> -		.tALH_min = 5000,
> -		.tALS_min = 10000,
> -		.tAR_min = 10000,
> -		.tCEA_max = 25000,
> -		.tCEH_min = 20000,
> -		.tCH_min = 5000,
> -		.tCHZ_max = 50000,
> -		.tCLH_min = 5000,
> -		.tCLR_min = 10000,
> -		.tCLS_min = 10000,
> -		.tCOH_min = 15000,
> -		.tCS_min = 25000,
> -		.tDH_min = 5000,
> -		.tDS_min = 10000,
> -		.tFEAT_max = 1000000,
> -		.tIR_min = 0,
> -		.tITC_max = 1000000,
> -		.tRC_min = 30000,
> -		.tREA_max = 20000,
> -		.tREH_min = 10000,
> -		.tRHOH_min = 15000,
> -		.tRHW_min = 100000,
> -		.tRHZ_max = 100000,
> -		.tRLOH_min = 0,
> -		.tRP_min = 15000,
> -		.tRR_min = 20000,
> -		.tRST_max = 500000000,
> -		.tWB_max = 100000,
> -		.tWC_min = 30000,
> -		.tWH_min = 10000,
> -		.tWHR_min = 80000,
> -		.tWP_min = 15000,
> -		.tWW_min = 100000,
> +		.type = NAND_SDR_IFACE,
> +		.timings.sdr = {
> +			.tADL_min = 100000,
> +			.tALH_min = 5000,
> +			.tALS_min = 10000,
> +			.tAR_min = 10000,
> +			.tCEA_max = 25000,
> +			.tCEH_min = 20000,
> +			.tCH_min = 5000,
> +			.tCHZ_max = 50000,
> +			.tCLH_min = 5000,
> +			.tCLR_min = 10000,
> +			.tCLS_min = 10000,
> +			.tCOH_min = 15000,
> +			.tCS_min = 25000,
> +			.tDH_min = 5000,
> +			.tDS_min = 10000,
> +			.tFEAT_max = 1000000,
> +			.tIR_min = 0,
> +			.tITC_max = 1000000,
> +			.tRC_min = 30000,
> +			.tREA_max = 20000,
> +			.tREH_min = 10000,
> +			.tRHOH_min = 15000,
> +			.tRHW_min = 100000,
> +			.tRHZ_max = 100000,
> +			.tRLOH_min = 0,
> +			.tRP_min = 15000,
> +			.tRR_min = 20000,
> +			.tRST_max = 500000000,
> +			.tWB_max = 100000,
> +			.tWC_min = 30000,
> +			.tWH_min = 10000,
> +			.tWHR_min = 80000,
> +			.tWP_min = 15000,
> +			.tWW_min = 100000,
> +		},
>  	},
>  	/* Mode 4 */
>  	{
> -		.tADL_min = 70000,
> -		.tALH_min = 5000,
> -		.tALS_min = 10000,
> -		.tAR_min = 10000,
> -		.tCEA_max = 25000,
> -		.tCEH_min = 20000,
> -		.tCH_min = 5000,
> -		.tCHZ_max = 30000,
> -		.tCLH_min = 5000,
> -		.tCLR_min = 10000,
> -		.tCLS_min = 10000,
> -		.tCOH_min = 15000,
> -		.tCS_min = 20000,
> -		.tDH_min = 5000,
> -		.tDS_min = 10000,
> -		.tFEAT_max = 1000000,
> -		.tIR_min = 0,
> -		.tITC_max = 1000000,
> -		.tRC_min = 25000,
> -		.tREA_max = 20000,
> -		.tREH_min = 10000,
> -		.tRHOH_min = 15000,
> -		.tRHW_min = 100000,
> -		.tRHZ_max = 100000,
> -		.tRLOH_min = 5000,
> -		.tRP_min = 12000,
> -		.tRR_min = 20000,
> -		.tRST_max = 500000000,
> -		.tWB_max = 100000,
> -		.tWC_min = 25000,
> -		.tWH_min = 10000,
> -		.tWHR_min = 80000,
> -		.tWP_min = 12000,
> -		.tWW_min = 100000,
> +		.type = NAND_SDR_IFACE,
> +		.timings.sdr = {
> +			.tADL_min = 70000,
> +			.tALH_min = 5000,
> +			.tALS_min = 10000,
> +			.tAR_min = 10000,
> +			.tCEA_max = 25000,
> +			.tCEH_min = 20000,
> +			.tCH_min = 5000,
> +			.tCHZ_max = 30000,
> +			.tCLH_min = 5000,
> +			.tCLR_min = 10000,
> +			.tCLS_min = 10000,
> +			.tCOH_min = 15000,
> +			.tCS_min = 20000,
> +			.tDH_min = 5000,
> +			.tDS_min = 10000,
> +			.tFEAT_max = 1000000,
> +			.tIR_min = 0,
> +			.tITC_max = 1000000,
> +			.tRC_min = 25000,
> +			.tREA_max = 20000,
> +			.tREH_min = 10000,
> +			.tRHOH_min = 15000,
> +			.tRHW_min = 100000,
> +			.tRHZ_max = 100000,
> +			.tRLOH_min = 5000,
> +			.tRP_min = 12000,
> +			.tRR_min = 20000,
> +			.tRST_max = 500000000,
> +			.tWB_max = 100000,
> +			.tWC_min = 25000,
> +			.tWH_min = 10000,
> +			.tWHR_min = 80000,
> +			.tWP_min = 12000,
> +			.tWW_min = 100000,
> +		},
>  	},
>  	/* Mode 5 */
>  	{
> -		.tADL_min = 70000,
> -		.tALH_min = 5000,
> -		.tALS_min = 10000,
> -		.tAR_min = 10000,
> -		.tCEA_max = 25000,
> -		.tCEH_min = 20000,
> -		.tCH_min = 5000,
> -		.tCHZ_max = 30000,
> -		.tCLH_min = 5000,
> -		.tCLR_min = 10000,
> -		.tCLS_min = 10000,
> -		.tCOH_min = 15000,
> -		.tCS_min = 15000,
> -		.tDH_min = 5000,
> -		.tDS_min = 7000,
> -		.tFEAT_max = 1000000,
> -		.tIR_min = 0,
> -		.tITC_max = 1000000,
> -		.tRC_min = 20000,
> -		.tREA_max = 16000,
> -		.tREH_min = 7000,
> -		.tRHOH_min = 15000,
> -		.tRHW_min = 100000,
> -		.tRHZ_max = 100000,
> -		.tRLOH_min = 5000,
> -		.tRP_min = 10000,
> -		.tRR_min = 20000,
> -		.tRST_max = 500000000,
> -		.tWB_max = 100000,
> -		.tWC_min = 20000,
> -		.tWH_min = 7000,
> -		.tWHR_min = 80000,
> -		.tWP_min = 10000,
> -		.tWW_min = 100000,
> +		.type = NAND_SDR_IFACE,
> +		.timings.sdr = {
> +			.tADL_min = 70000,
> +			.tALH_min = 5000,
> +			.tALS_min = 10000,
> +			.tAR_min = 10000,
> +			.tCEA_max = 25000,
> +			.tCEH_min = 20000,
> +			.tCH_min = 5000,
> +			.tCHZ_max = 30000,
> +			.tCLH_min = 5000,
> +			.tCLR_min = 10000,
> +			.tCLS_min = 10000,
> +			.tCOH_min = 15000,
> +			.tCS_min = 15000,
> +			.tDH_min = 5000,
> +			.tDS_min = 7000,
> +			.tFEAT_max = 1000000,
> +			.tIR_min = 0,
> +			.tITC_max = 1000000,
> +			.tRC_min = 20000,
> +			.tREA_max = 16000,
> +			.tREH_min = 7000,
> +			.tRHOH_min = 15000,
> +			.tRHW_min = 100000,
> +			.tRHZ_max = 100000,
> +			.tRLOH_min = 5000,
> +			.tRP_min = 10000,
> +			.tRR_min = 20000,
> +			.tRST_max = 500000000,
> +			.tWB_max = 100000,
> +			.tWC_min = 20000,
> +			.tWH_min = 7000,
> +			.tWHR_min = 80000,
> +			.tWP_min = 10000,
> +			.tWW_min = 100000,
> +		},
>  	},
>  };
>  
> @@ -248,6 +266,20 @@ const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode)
>  	if (mode < 0 || mode >= ARRAY_SIZE(onfi_sdr_timings))
>  		return ERR_PTR(-EINVAL);
>  
> -	return &onfi_sdr_timings[mode];
> +	return &onfi_sdr_timings[mode].timings.sdr;
>  }
>  EXPORT_SYMBOL(onfi_async_timing_mode_to_sdr_timings);
> +
> +/**
> + * onfi_async_timing_mode_to_data_interface - [NAND Interface] Retrieve NAND
> + * data interface according to the given ONFI timing mode
> + * @mode: ONFI timing mode
> + */
> +const struct nand_data_interface *onfi_async_timing_mode_to_data_interface(int mode)
> +{
> +	if (mode < 0 || mode >= ARRAY_SIZE(onfi_sdr_timings))
> +		return ERR_PTR(-EINVAL);
> +
> +	return &onfi_sdr_timings[mode];
> +}
> +EXPORT_SYMBOL(onfi_async_timing_mode_to_data_interface);
> diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
> index 19c73ef..a8bdf6c 100644
> --- a/include/linux/mtd/nand.h
> +++ b/include/linux/mtd/nand.h
> @@ -1088,6 +1088,8 @@ static inline int jedec_feature(struct nand_chip *chip)
>  
>  /* get timing characteristics from ONFI timing mode. */
>  const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode);
> +/* get data interface from ONFI timing mode. */
> +const struct nand_data_interface *onfi_async_timing_mode_to_data_interface(int mode);
>  
>  int nand_check_erased_ecc_chunk(void *data, int datalen,
>  				void *ecc, int ecclen,

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

* [PATCH 3/7] mtd: nand: convert ONFI mode into data interface
@ 2016-09-06 11:27     ` Boris Brezillon
  0 siblings, 0 replies; 52+ messages in thread
From: Boris Brezillon @ 2016-09-06 11:27 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue,  6 Sep 2016 12:39:11 +0200
Sascha Hauer <s.hauer@pengutronix.de> wrote:

> struct nand_data_interface is the designated type to pass to
> the NAND drivers to configure the timing. To simplify this introduce
> onfi_async_timing_mode_to_data_interface() to convert a ONFI
> mode into a nand_data_interface.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  drivers/mtd/nand/nand_timings.c | 444 +++++++++++++++++++++-------------------
>  include/linux/mtd/nand.h        |   2 +
>  2 files changed, 240 insertions(+), 206 deletions(-)
> 
> diff --git a/drivers/mtd/nand/nand_timings.c b/drivers/mtd/nand/nand_timings.c
> index e81470a..260b074 100644
> --- a/drivers/mtd/nand/nand_timings.c
> +++ b/drivers/mtd/nand/nand_timings.c
> @@ -13,228 +13,246 @@
>  #include <linux/export.h>
>  #include <linux/mtd/nand.h>
>  
> -static const struct nand_sdr_timings onfi_sdr_timings[] = {
> +static const struct nand_data_interface onfi_sdr_timings[] = {

Do you have a good reason to turn onfi_sdr_timings into an array of
struct nand_data_interface?

>  	/* Mode 0 */
>  	{
> -		.tADL_min = 200000,
> -		.tALH_min = 20000,
> -		.tALS_min = 50000,
> -		.tAR_min = 25000,
> -		.tCEA_max = 100000,
> -		.tCEH_min = 20000,
> -		.tCH_min = 20000,
> -		.tCHZ_max = 100000,
> -		.tCLH_min = 20000,
> -		.tCLR_min = 20000,
> -		.tCLS_min = 50000,
> -		.tCOH_min = 0,
> -		.tCS_min = 70000,
> -		.tDH_min = 20000,
> -		.tDS_min = 40000,
> -		.tFEAT_max = 1000000,
> -		.tIR_min = 10000,
> -		.tITC_max = 1000000,
> -		.tRC_min = 100000,
> -		.tREA_max = 40000,
> -		.tREH_min = 30000,
> -		.tRHOH_min = 0,
> -		.tRHW_min = 200000,
> -		.tRHZ_max = 200000,
> -		.tRLOH_min = 0,
> -		.tRP_min = 50000,
> -		.tRST_max = 250000000000ULL,
> -		.tWB_max = 200000,
> -		.tRR_min = 40000,
> -		.tWC_min = 100000,
> -		.tWH_min = 30000,
> -		.tWHR_min = 120000,
> -		.tWP_min = 50000,
> -		.tWW_min = 100000,
> +		.type = NAND_SDR_IFACE,
> +		.timings.sdr = {
> +			.tADL_min = 200000,
> +			.tALH_min = 20000,
> +			.tALS_min = 50000,
> +			.tAR_min = 25000,
> +			.tCEA_max = 100000,
> +			.tCEH_min = 20000,
> +			.tCH_min = 20000,
> +			.tCHZ_max = 100000,
> +			.tCLH_min = 20000,
> +			.tCLR_min = 20000,
> +			.tCLS_min = 50000,
> +			.tCOH_min = 0,
> +			.tCS_min = 70000,
> +			.tDH_min = 20000,
> +			.tDS_min = 40000,
> +			.tFEAT_max = 1000000,
> +			.tIR_min = 10000,
> +			.tITC_max = 1000000,
> +			.tRC_min = 100000,
> +			.tREA_max = 40000,
> +			.tREH_min = 30000,
> +			.tRHOH_min = 0,
> +			.tRHW_min = 200000,
> +			.tRHZ_max = 200000,
> +			.tRLOH_min = 0,
> +			.tRP_min = 50000,
> +			.tRST_max = 250000000000ULL,
> +			.tWB_max = 200000,
> +			.tRR_min = 40000,
> +			.tWC_min = 100000,
> +			.tWH_min = 30000,
> +			.tWHR_min = 120000,
> +			.tWP_min = 50000,
> +			.tWW_min = 100000,
> +		},
>  	},
>  	/* Mode 1 */
>  	{
> -		.tADL_min = 100000,
> -		.tALH_min = 10000,
> -		.tALS_min = 25000,
> -		.tAR_min = 10000,
> -		.tCEA_max = 45000,
> -		.tCEH_min = 20000,
> -		.tCH_min = 10000,
> -		.tCHZ_max = 50000,
> -		.tCLH_min = 10000,
> -		.tCLR_min = 10000,
> -		.tCLS_min = 25000,
> -		.tCOH_min = 15000,
> -		.tCS_min = 35000,
> -		.tDH_min = 10000,
> -		.tDS_min = 20000,
> -		.tFEAT_max = 1000000,
> -		.tIR_min = 0,
> -		.tITC_max = 1000000,
> -		.tRC_min = 50000,
> -		.tREA_max = 30000,
> -		.tREH_min = 15000,
> -		.tRHOH_min = 15000,
> -		.tRHW_min = 100000,
> -		.tRHZ_max = 100000,
> -		.tRLOH_min = 0,
> -		.tRP_min = 25000,
> -		.tRR_min = 20000,
> -		.tRST_max = 500000000,
> -		.tWB_max = 100000,
> -		.tWC_min = 45000,
> -		.tWH_min = 15000,
> -		.tWHR_min = 80000,
> -		.tWP_min = 25000,
> -		.tWW_min = 100000,
> +		.type = NAND_SDR_IFACE,
> +		.timings.sdr = {
> +			.tADL_min = 100000,
> +			.tALH_min = 10000,
> +			.tALS_min = 25000,
> +			.tAR_min = 10000,
> +			.tCEA_max = 45000,
> +			.tCEH_min = 20000,
> +			.tCH_min = 10000,
> +			.tCHZ_max = 50000,
> +			.tCLH_min = 10000,
> +			.tCLR_min = 10000,
> +			.tCLS_min = 25000,
> +			.tCOH_min = 15000,
> +			.tCS_min = 35000,
> +			.tDH_min = 10000,
> +			.tDS_min = 20000,
> +			.tFEAT_max = 1000000,
> +			.tIR_min = 0,
> +			.tITC_max = 1000000,
> +			.tRC_min = 50000,
> +			.tREA_max = 30000,
> +			.tREH_min = 15000,
> +			.tRHOH_min = 15000,
> +			.tRHW_min = 100000,
> +			.tRHZ_max = 100000,
> +			.tRLOH_min = 0,
> +			.tRP_min = 25000,
> +			.tRR_min = 20000,
> +			.tRST_max = 500000000,
> +			.tWB_max = 100000,
> +			.tWC_min = 45000,
> +			.tWH_min = 15000,
> +			.tWHR_min = 80000,
> +			.tWP_min = 25000,
> +			.tWW_min = 100000,
> +		},
>  	},
>  	/* Mode 2 */
>  	{
> -		.tADL_min = 100000,
> -		.tALH_min = 10000,
> -		.tALS_min = 15000,
> -		.tAR_min = 10000,
> -		.tCEA_max = 30000,
> -		.tCEH_min = 20000,
> -		.tCH_min = 10000,
> -		.tCHZ_max = 50000,
> -		.tCLH_min = 10000,
> -		.tCLR_min = 10000,
> -		.tCLS_min = 15000,
> -		.tCOH_min = 15000,
> -		.tCS_min = 25000,
> -		.tDH_min = 5000,
> -		.tDS_min = 15000,
> -		.tFEAT_max = 1000000,
> -		.tIR_min = 0,
> -		.tITC_max = 1000000,
> -		.tRC_min = 35000,
> -		.tREA_max = 25000,
> -		.tREH_min = 15000,
> -		.tRHOH_min = 15000,
> -		.tRHW_min = 100000,
> -		.tRHZ_max = 100000,
> -		.tRLOH_min = 0,
> -		.tRR_min = 20000,
> -		.tRST_max = 500000000,
> -		.tWB_max = 100000,
> -		.tRP_min = 17000,
> -		.tWC_min = 35000,
> -		.tWH_min = 15000,
> -		.tWHR_min = 80000,
> -		.tWP_min = 17000,
> -		.tWW_min = 100000,
> +		.type = NAND_SDR_IFACE,
> +		.timings.sdr = {
> +			.tADL_min = 100000,
> +			.tALH_min = 10000,
> +			.tALS_min = 15000,
> +			.tAR_min = 10000,
> +			.tCEA_max = 30000,
> +			.tCEH_min = 20000,
> +			.tCH_min = 10000,
> +			.tCHZ_max = 50000,
> +			.tCLH_min = 10000,
> +			.tCLR_min = 10000,
> +			.tCLS_min = 15000,
> +			.tCOH_min = 15000,
> +			.tCS_min = 25000,
> +			.tDH_min = 5000,
> +			.tDS_min = 15000,
> +			.tFEAT_max = 1000000,
> +			.tIR_min = 0,
> +			.tITC_max = 1000000,
> +			.tRC_min = 35000,
> +			.tREA_max = 25000,
> +			.tREH_min = 15000,
> +			.tRHOH_min = 15000,
> +			.tRHW_min = 100000,
> +			.tRHZ_max = 100000,
> +			.tRLOH_min = 0,
> +			.tRR_min = 20000,
> +			.tRST_max = 500000000,
> +			.tWB_max = 100000,
> +			.tRP_min = 17000,
> +			.tWC_min = 35000,
> +			.tWH_min = 15000,
> +			.tWHR_min = 80000,
> +			.tWP_min = 17000,
> +			.tWW_min = 100000,
> +		},
>  	},
>  	/* Mode 3 */
>  	{
> -		.tADL_min = 100000,
> -		.tALH_min = 5000,
> -		.tALS_min = 10000,
> -		.tAR_min = 10000,
> -		.tCEA_max = 25000,
> -		.tCEH_min = 20000,
> -		.tCH_min = 5000,
> -		.tCHZ_max = 50000,
> -		.tCLH_min = 5000,
> -		.tCLR_min = 10000,
> -		.tCLS_min = 10000,
> -		.tCOH_min = 15000,
> -		.tCS_min = 25000,
> -		.tDH_min = 5000,
> -		.tDS_min = 10000,
> -		.tFEAT_max = 1000000,
> -		.tIR_min = 0,
> -		.tITC_max = 1000000,
> -		.tRC_min = 30000,
> -		.tREA_max = 20000,
> -		.tREH_min = 10000,
> -		.tRHOH_min = 15000,
> -		.tRHW_min = 100000,
> -		.tRHZ_max = 100000,
> -		.tRLOH_min = 0,
> -		.tRP_min = 15000,
> -		.tRR_min = 20000,
> -		.tRST_max = 500000000,
> -		.tWB_max = 100000,
> -		.tWC_min = 30000,
> -		.tWH_min = 10000,
> -		.tWHR_min = 80000,
> -		.tWP_min = 15000,
> -		.tWW_min = 100000,
> +		.type = NAND_SDR_IFACE,
> +		.timings.sdr = {
> +			.tADL_min = 100000,
> +			.tALH_min = 5000,
> +			.tALS_min = 10000,
> +			.tAR_min = 10000,
> +			.tCEA_max = 25000,
> +			.tCEH_min = 20000,
> +			.tCH_min = 5000,
> +			.tCHZ_max = 50000,
> +			.tCLH_min = 5000,
> +			.tCLR_min = 10000,
> +			.tCLS_min = 10000,
> +			.tCOH_min = 15000,
> +			.tCS_min = 25000,
> +			.tDH_min = 5000,
> +			.tDS_min = 10000,
> +			.tFEAT_max = 1000000,
> +			.tIR_min = 0,
> +			.tITC_max = 1000000,
> +			.tRC_min = 30000,
> +			.tREA_max = 20000,
> +			.tREH_min = 10000,
> +			.tRHOH_min = 15000,
> +			.tRHW_min = 100000,
> +			.tRHZ_max = 100000,
> +			.tRLOH_min = 0,
> +			.tRP_min = 15000,
> +			.tRR_min = 20000,
> +			.tRST_max = 500000000,
> +			.tWB_max = 100000,
> +			.tWC_min = 30000,
> +			.tWH_min = 10000,
> +			.tWHR_min = 80000,
> +			.tWP_min = 15000,
> +			.tWW_min = 100000,
> +		},
>  	},
>  	/* Mode 4 */
>  	{
> -		.tADL_min = 70000,
> -		.tALH_min = 5000,
> -		.tALS_min = 10000,
> -		.tAR_min = 10000,
> -		.tCEA_max = 25000,
> -		.tCEH_min = 20000,
> -		.tCH_min = 5000,
> -		.tCHZ_max = 30000,
> -		.tCLH_min = 5000,
> -		.tCLR_min = 10000,
> -		.tCLS_min = 10000,
> -		.tCOH_min = 15000,
> -		.tCS_min = 20000,
> -		.tDH_min = 5000,
> -		.tDS_min = 10000,
> -		.tFEAT_max = 1000000,
> -		.tIR_min = 0,
> -		.tITC_max = 1000000,
> -		.tRC_min = 25000,
> -		.tREA_max = 20000,
> -		.tREH_min = 10000,
> -		.tRHOH_min = 15000,
> -		.tRHW_min = 100000,
> -		.tRHZ_max = 100000,
> -		.tRLOH_min = 5000,
> -		.tRP_min = 12000,
> -		.tRR_min = 20000,
> -		.tRST_max = 500000000,
> -		.tWB_max = 100000,
> -		.tWC_min = 25000,
> -		.tWH_min = 10000,
> -		.tWHR_min = 80000,
> -		.tWP_min = 12000,
> -		.tWW_min = 100000,
> +		.type = NAND_SDR_IFACE,
> +		.timings.sdr = {
> +			.tADL_min = 70000,
> +			.tALH_min = 5000,
> +			.tALS_min = 10000,
> +			.tAR_min = 10000,
> +			.tCEA_max = 25000,
> +			.tCEH_min = 20000,
> +			.tCH_min = 5000,
> +			.tCHZ_max = 30000,
> +			.tCLH_min = 5000,
> +			.tCLR_min = 10000,
> +			.tCLS_min = 10000,
> +			.tCOH_min = 15000,
> +			.tCS_min = 20000,
> +			.tDH_min = 5000,
> +			.tDS_min = 10000,
> +			.tFEAT_max = 1000000,
> +			.tIR_min = 0,
> +			.tITC_max = 1000000,
> +			.tRC_min = 25000,
> +			.tREA_max = 20000,
> +			.tREH_min = 10000,
> +			.tRHOH_min = 15000,
> +			.tRHW_min = 100000,
> +			.tRHZ_max = 100000,
> +			.tRLOH_min = 5000,
> +			.tRP_min = 12000,
> +			.tRR_min = 20000,
> +			.tRST_max = 500000000,
> +			.tWB_max = 100000,
> +			.tWC_min = 25000,
> +			.tWH_min = 10000,
> +			.tWHR_min = 80000,
> +			.tWP_min = 12000,
> +			.tWW_min = 100000,
> +		},
>  	},
>  	/* Mode 5 */
>  	{
> -		.tADL_min = 70000,
> -		.tALH_min = 5000,
> -		.tALS_min = 10000,
> -		.tAR_min = 10000,
> -		.tCEA_max = 25000,
> -		.tCEH_min = 20000,
> -		.tCH_min = 5000,
> -		.tCHZ_max = 30000,
> -		.tCLH_min = 5000,
> -		.tCLR_min = 10000,
> -		.tCLS_min = 10000,
> -		.tCOH_min = 15000,
> -		.tCS_min = 15000,
> -		.tDH_min = 5000,
> -		.tDS_min = 7000,
> -		.tFEAT_max = 1000000,
> -		.tIR_min = 0,
> -		.tITC_max = 1000000,
> -		.tRC_min = 20000,
> -		.tREA_max = 16000,
> -		.tREH_min = 7000,
> -		.tRHOH_min = 15000,
> -		.tRHW_min = 100000,
> -		.tRHZ_max = 100000,
> -		.tRLOH_min = 5000,
> -		.tRP_min = 10000,
> -		.tRR_min = 20000,
> -		.tRST_max = 500000000,
> -		.tWB_max = 100000,
> -		.tWC_min = 20000,
> -		.tWH_min = 7000,
> -		.tWHR_min = 80000,
> -		.tWP_min = 10000,
> -		.tWW_min = 100000,
> +		.type = NAND_SDR_IFACE,
> +		.timings.sdr = {
> +			.tADL_min = 70000,
> +			.tALH_min = 5000,
> +			.tALS_min = 10000,
> +			.tAR_min = 10000,
> +			.tCEA_max = 25000,
> +			.tCEH_min = 20000,
> +			.tCH_min = 5000,
> +			.tCHZ_max = 30000,
> +			.tCLH_min = 5000,
> +			.tCLR_min = 10000,
> +			.tCLS_min = 10000,
> +			.tCOH_min = 15000,
> +			.tCS_min = 15000,
> +			.tDH_min = 5000,
> +			.tDS_min = 7000,
> +			.tFEAT_max = 1000000,
> +			.tIR_min = 0,
> +			.tITC_max = 1000000,
> +			.tRC_min = 20000,
> +			.tREA_max = 16000,
> +			.tREH_min = 7000,
> +			.tRHOH_min = 15000,
> +			.tRHW_min = 100000,
> +			.tRHZ_max = 100000,
> +			.tRLOH_min = 5000,
> +			.tRP_min = 10000,
> +			.tRR_min = 20000,
> +			.tRST_max = 500000000,
> +			.tWB_max = 100000,
> +			.tWC_min = 20000,
> +			.tWH_min = 7000,
> +			.tWHR_min = 80000,
> +			.tWP_min = 10000,
> +			.tWW_min = 100000,
> +		},
>  	},
>  };
>  
> @@ -248,6 +266,20 @@ const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode)
>  	if (mode < 0 || mode >= ARRAY_SIZE(onfi_sdr_timings))
>  		return ERR_PTR(-EINVAL);
>  
> -	return &onfi_sdr_timings[mode];
> +	return &onfi_sdr_timings[mode].timings.sdr;
>  }
>  EXPORT_SYMBOL(onfi_async_timing_mode_to_sdr_timings);
> +
> +/**
> + * onfi_async_timing_mode_to_data_interface - [NAND Interface] Retrieve NAND
> + * data interface according to the given ONFI timing mode
> + * @mode: ONFI timing mode
> + */
> +const struct nand_data_interface *onfi_async_timing_mode_to_data_interface(int mode)
> +{
> +	if (mode < 0 || mode >= ARRAY_SIZE(onfi_sdr_timings))
> +		return ERR_PTR(-EINVAL);
> +
> +	return &onfi_sdr_timings[mode];
> +}
> +EXPORT_SYMBOL(onfi_async_timing_mode_to_data_interface);
> diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
> index 19c73ef..a8bdf6c 100644
> --- a/include/linux/mtd/nand.h
> +++ b/include/linux/mtd/nand.h
> @@ -1088,6 +1088,8 @@ static inline int jedec_feature(struct nand_chip *chip)
>  
>  /* get timing characteristics from ONFI timing mode. */
>  const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode);
> +/* get data interface from ONFI timing mode. */
> +const struct nand_data_interface *onfi_async_timing_mode_to_data_interface(int mode);
>  
>  int nand_check_erased_ecc_chunk(void *data, int datalen,
>  				void *ecc, int ecclen,

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

* Re: [PATCH 4/7] mtd: nand: automate NAND timings selection
  2016-09-06 10:39   ` Sascha Hauer
@ 2016-09-06 11:58     ` Boris Brezillon
  -1 siblings, 0 replies; 52+ messages in thread
From: Boris Brezillon @ 2016-09-06 11:58 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-mtd, kernel, linux-arm-kernel

On Tue,  6 Sep 2016 12:39:12 +0200
Sascha Hauer <s.hauer@pengutronix.de> wrote:

> From: Boris Brezillon <boris.brezillon@free-electrons.com>
> 
> The NAND framework provides several helpers to query timing modes supported
> by a NAND chip, but this implies that all NAND controller drivers have
> to implement the same timings selection dance. Also currently NAND
> devices can be resetted at arbitrary places which also resets the timing
> for ONFI chips to timing mode 0.
> 
> Provide a common logic to select the best timings based on ONFI or
> ->onfi_timing_mode_default information. Hook this into nand_reset()  
> to make sure the new timing is applied each time during a reset.
> 
> NAND controller willing to support timings adjustment should just
> implement the ->setup_data_interface() method.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  drivers/mtd/nand/nand_base.c | 112 +++++++++++++++++++++++++++++++++++++++++++
>  include/linux/mtd/nand.h     |  14 ++++--
>  2 files changed, 122 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> index 20151fc..37852e9 100644
> --- a/drivers/mtd/nand/nand_base.c
> +++ b/drivers/mtd/nand/nand_base.c
> @@ -955,8 +955,63 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
>   */
>  int nand_reset(struct mtd_info *mtd)
>  {
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	int ret;
> +
> +	if (chip->setup_data_interface) {
> +		struct nand_data_interface conf = {
> +			.type = NAND_SDR_IFACE,
> +			.timings.sdr = *onfi_async_timing_mode_to_sdr_timings(0),
> +		};
> +

Let's try to avoid putting such a huge structure on the stack.

> +		/*
> +		 * The ONFI specification says:
> +		 * "
> +		 * To transition from NV-DDR or NV-DDR2 to the SDR data
> +		 * interface, the host shall use the Reset (FFh) command
> +		 * using SDR timing mode 0. A device in any timing mode is
> +		 * required to recognize Reset (FFh) command issued in SDR
> +		 * timing mode 0.
> +		 * "
> +		 *
> +		 * Configure the data interface in SDR mode and set the
> +		 * timings to timing mode 0.
> +		 */
> +
> +		ret = chip->setup_data_interface(mtd, &conf, false);
> +		if (ret) {
> +			pr_err("Failed to configure data interface to SDR timing mode 0\n");
> +			return ret;
> +		}
> +	}

Can you put this code in a separate function? I'd like to keep the
nand_reset() function as small as possible.

How about nand_reset_data_interface()?

> +
>  	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
>  
> +	/*
> +	 * Setup the NAND interface (interface type + timings).
> +	 */
> +	if (chip->data_iface) {
> +		uint8_t tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = {
> +			chip->onfi_timing_mode_default,
> +		};
> +
> +		/*
> +		 * Ensure the timing mode has be changed on the chip side
					      ^ been
> +		 * before changing timings on the controller side.
> +		 */
> +		if (chip->onfi_version) {
> +			ret = chip->onfi_set_features(mtd, chip,
> +					ONFI_FEATURE_ADDR_TIMING_MODE,
> +					tmode_param);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		ret = chip->setup_data_interface(mtd, chip->data_iface, false);
> +		if (ret)
> +			return ret;
> +	}
> +

Ditto: nand_setup_data_interface()?

>  	return 0;
>  }
>  
> @@ -3335,6 +3390,54 @@ static void nand_onfi_detect_micron(struct nand_chip *chip,
>  	chip->setup_read_retry = nand_setup_read_retry_micron;
>  }
>  
> +/**
> + * nand_find_data_interface - Find the best data interface and timings
> + * @mtd: MTD device structure
> + *
> + * Try to find the best data interface and NAND timings supported by the
> + * chip and the driver.
> + * First tries to retrieve supported timing modes from ONFI information,
> + * and if the NAND chip does not support ONFI, relies on the
> + * ->onfi_timing_mode_default specified in the nand_ids table.
> + *
> + * Returns 0 for success or negative error code otherwise.
> + */
> +static int nand_find_data_interface(struct mtd_info *mtd)

How about nand_init_data_interface() or nand_init_data_iface_config()?

> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	int modes, mode, ret;
> +	const struct nand_data_interface *conf;
> +
> +	/*
> +	 * First try to identify the best timings from ONFI parameters and
> +	 * if the NAND does not support ONFI, fallback to the default ONFI
> +	 * timing mode.
> +	 */
> +	modes = onfi_get_async_timing_mode(chip);
> +	if (modes == ONFI_TIMING_MODE_UNKNOWN)
> +		modes = GENMASK(chip->onfi_timing_mode_default, 0);
> +
> +	ret = -EINVAL;
> +	for (mode = fls(modes) - 1; mode >= 0; mode--) {
> +		conf = onfi_async_timing_mode_to_data_interface(mode);

I'd still prefer to have conf allocated at the beginning of the
function and timings copied from
onfi_async_timing_mode_to_sdr_timings(mode), but maybe you can convince
me otherwise.

> +
> +		ret = chip->setup_data_interface(mtd, conf, true);
> +		if (!ret) {
> +			chip->onfi_timing_mode_default = mode;
> +			break;
> +		}
> +	}
> +
> +	if (ret)
> +		return ret;
> +
> +	chip->data_iface = kmemdup(conf, sizeof(*conf), GFP_KERNEL);
> +	if (!chip->data_iface)
> +		return -ENOMEM;
> +
> +	return 0;
> +}
> +
>  /*
>   * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
>   */
> @@ -4168,6 +4271,12 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
>  		return PTR_ERR(type);
>  	}
>  
> +	if (chip->setup_data_interface) {
> +		ret = nand_find_data_interface(mtd);
> +		if (ret)
> +			return ret;
> +	}
> +
>  	chip->select_chip(mtd, -1);
>  
>  	/* Check for a chip array */
> @@ -4627,6 +4736,9 @@ void nand_release(struct mtd_info *mtd)
>  
>  	mtd_device_unregister(mtd);
>  
> +	/* Free interface config struct */
> +	kfree(chip->data_iface);
> +

Can we hide that in an helper as well? nand_cleanup_data_interface()?

>  	/* Free bad block table memory */
>  	kfree(chip->bbt);
>  	if (!(chip->options & NAND_OWN_BUFFERS))
> diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
> index a8bdf6c..5269357 100644
> --- a/include/linux/mtd/nand.h
> +++ b/include/linux/mtd/nand.h
> @@ -690,10 +690,9 @@ struct nand_data_interface {
>   *                      also from the datasheet. It is the recommended ECC step
>   *			size, if known; if unknown, set to zero.
>   * @onfi_timing_mode_default: [INTERN] default ONFI timing mode. This field is
> - *			      either deduced from the datasheet if the NAND
> - *			      chip is not ONFI compliant or set to 0 if it is
> - *			      (an ONFI chip is always configured in mode 0
> - *			      after a NAND reset)
> + * 			      set to the actually used ONFI mode if the chip is
> + * 			      ONFI compliant or deduced from the datasheet if
> + * 			      the NAND chip is not ONFI compliant.
>   * @numchips:		[INTERN] number of physical chips
>   * @chipsize:		[INTERN] the size of one chip for multichip arrays
>   * @pagemask:		[INTERN] page number mask = number of (pages / chip) - 1
> @@ -713,6 +712,7 @@ struct nand_data_interface {
>   * @read_retries:	[INTERN] the number of read retry modes supported
>   * @onfi_set_features:	[REPLACEABLE] set the features for ONFI nand
>   * @onfi_get_features:	[REPLACEABLE] get the features for ONFI nand
> + * @setup_data_interface: [OPTIONAL] setup the data interface and timing
>   * @bbt:		[INTERN] bad block table pointer
>   * @bbt_td:		[REPLACEABLE] bad block table descriptor for flash
>   *			lookup.
> @@ -759,6 +759,10 @@ struct nand_chip {
>  	int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
>  			int feature_addr, uint8_t *subfeature_para);
>  	int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode);
> +	int (*setup_data_interface)(struct mtd_info *mtd,
> +				    const struct nand_data_interface *conf,
> +				    bool check_only);
> +
>  
>  	int chip_delay;
>  	unsigned int options;
> @@ -788,6 +792,8 @@ struct nand_chip {
>  		struct nand_jedec_params jedec_params;
>  	};
>  
> +	const struct nand_data_interface *data_iface;
> +

How about making this field non-const so that you only allocate it once
and modify it when you switch from one mode to another.

>  	int read_retries;
>  
>  	flstate_t state;

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

* [PATCH 4/7] mtd: nand: automate NAND timings selection
@ 2016-09-06 11:58     ` Boris Brezillon
  0 siblings, 0 replies; 52+ messages in thread
From: Boris Brezillon @ 2016-09-06 11:58 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue,  6 Sep 2016 12:39:12 +0200
Sascha Hauer <s.hauer@pengutronix.de> wrote:

> From: Boris Brezillon <boris.brezillon@free-electrons.com>
> 
> The NAND framework provides several helpers to query timing modes supported
> by a NAND chip, but this implies that all NAND controller drivers have
> to implement the same timings selection dance. Also currently NAND
> devices can be resetted at arbitrary places which also resets the timing
> for ONFI chips to timing mode 0.
> 
> Provide a common logic to select the best timings based on ONFI or
> ->onfi_timing_mode_default information. Hook this into nand_reset()  
> to make sure the new timing is applied each time during a reset.
> 
> NAND controller willing to support timings adjustment should just
> implement the ->setup_data_interface() method.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  drivers/mtd/nand/nand_base.c | 112 +++++++++++++++++++++++++++++++++++++++++++
>  include/linux/mtd/nand.h     |  14 ++++--
>  2 files changed, 122 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> index 20151fc..37852e9 100644
> --- a/drivers/mtd/nand/nand_base.c
> +++ b/drivers/mtd/nand/nand_base.c
> @@ -955,8 +955,63 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
>   */
>  int nand_reset(struct mtd_info *mtd)
>  {
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	int ret;
> +
> +	if (chip->setup_data_interface) {
> +		struct nand_data_interface conf = {
> +			.type = NAND_SDR_IFACE,
> +			.timings.sdr = *onfi_async_timing_mode_to_sdr_timings(0),
> +		};
> +

Let's try to avoid putting such a huge structure on the stack.

> +		/*
> +		 * The ONFI specification says:
> +		 * "
> +		 * To transition from NV-DDR or NV-DDR2 to the SDR data
> +		 * interface, the host shall use the Reset (FFh) command
> +		 * using SDR timing mode 0. A device in any timing mode is
> +		 * required to recognize Reset (FFh) command issued in SDR
> +		 * timing mode 0.
> +		 * "
> +		 *
> +		 * Configure the data interface in SDR mode and set the
> +		 * timings to timing mode 0.
> +		 */
> +
> +		ret = chip->setup_data_interface(mtd, &conf, false);
> +		if (ret) {
> +			pr_err("Failed to configure data interface to SDR timing mode 0\n");
> +			return ret;
> +		}
> +	}

Can you put this code in a separate function? I'd like to keep the
nand_reset() function as small as possible.

How about nand_reset_data_interface()?

> +
>  	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
>  
> +	/*
> +	 * Setup the NAND interface (interface type + timings).
> +	 */
> +	if (chip->data_iface) {
> +		uint8_t tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = {
> +			chip->onfi_timing_mode_default,
> +		};
> +
> +		/*
> +		 * Ensure the timing mode has be changed on the chip side
					      ^ been
> +		 * before changing timings on the controller side.
> +		 */
> +		if (chip->onfi_version) {
> +			ret = chip->onfi_set_features(mtd, chip,
> +					ONFI_FEATURE_ADDR_TIMING_MODE,
> +					tmode_param);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		ret = chip->setup_data_interface(mtd, chip->data_iface, false);
> +		if (ret)
> +			return ret;
> +	}
> +

Ditto: nand_setup_data_interface()?

>  	return 0;
>  }
>  
> @@ -3335,6 +3390,54 @@ static void nand_onfi_detect_micron(struct nand_chip *chip,
>  	chip->setup_read_retry = nand_setup_read_retry_micron;
>  }
>  
> +/**
> + * nand_find_data_interface - Find the best data interface and timings
> + * @mtd: MTD device structure
> + *
> + * Try to find the best data interface and NAND timings supported by the
> + * chip and the driver.
> + * First tries to retrieve supported timing modes from ONFI information,
> + * and if the NAND chip does not support ONFI, relies on the
> + * ->onfi_timing_mode_default specified in the nand_ids table.
> + *
> + * Returns 0 for success or negative error code otherwise.
> + */
> +static int nand_find_data_interface(struct mtd_info *mtd)

How about nand_init_data_interface() or nand_init_data_iface_config()?

> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	int modes, mode, ret;
> +	const struct nand_data_interface *conf;
> +
> +	/*
> +	 * First try to identify the best timings from ONFI parameters and
> +	 * if the NAND does not support ONFI, fallback to the default ONFI
> +	 * timing mode.
> +	 */
> +	modes = onfi_get_async_timing_mode(chip);
> +	if (modes == ONFI_TIMING_MODE_UNKNOWN)
> +		modes = GENMASK(chip->onfi_timing_mode_default, 0);
> +
> +	ret = -EINVAL;
> +	for (mode = fls(modes) - 1; mode >= 0; mode--) {
> +		conf = onfi_async_timing_mode_to_data_interface(mode);

I'd still prefer to have conf allocated at the beginning of the
function and timings copied from
onfi_async_timing_mode_to_sdr_timings(mode), but maybe you can convince
me otherwise.

> +
> +		ret = chip->setup_data_interface(mtd, conf, true);
> +		if (!ret) {
> +			chip->onfi_timing_mode_default = mode;
> +			break;
> +		}
> +	}
> +
> +	if (ret)
> +		return ret;
> +
> +	chip->data_iface = kmemdup(conf, sizeof(*conf), GFP_KERNEL);
> +	if (!chip->data_iface)
> +		return -ENOMEM;
> +
> +	return 0;
> +}
> +
>  /*
>   * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
>   */
> @@ -4168,6 +4271,12 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
>  		return PTR_ERR(type);
>  	}
>  
> +	if (chip->setup_data_interface) {
> +		ret = nand_find_data_interface(mtd);
> +		if (ret)
> +			return ret;
> +	}
> +
>  	chip->select_chip(mtd, -1);
>  
>  	/* Check for a chip array */
> @@ -4627,6 +4736,9 @@ void nand_release(struct mtd_info *mtd)
>  
>  	mtd_device_unregister(mtd);
>  
> +	/* Free interface config struct */
> +	kfree(chip->data_iface);
> +

Can we hide that in an helper as well? nand_cleanup_data_interface()?

>  	/* Free bad block table memory */
>  	kfree(chip->bbt);
>  	if (!(chip->options & NAND_OWN_BUFFERS))
> diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
> index a8bdf6c..5269357 100644
> --- a/include/linux/mtd/nand.h
> +++ b/include/linux/mtd/nand.h
> @@ -690,10 +690,9 @@ struct nand_data_interface {
>   *                      also from the datasheet. It is the recommended ECC step
>   *			size, if known; if unknown, set to zero.
>   * @onfi_timing_mode_default: [INTERN] default ONFI timing mode. This field is
> - *			      either deduced from the datasheet if the NAND
> - *			      chip is not ONFI compliant or set to 0 if it is
> - *			      (an ONFI chip is always configured in mode 0
> - *			      after a NAND reset)
> + * 			      set to the actually used ONFI mode if the chip is
> + * 			      ONFI compliant or deduced from the datasheet if
> + * 			      the NAND chip is not ONFI compliant.
>   * @numchips:		[INTERN] number of physical chips
>   * @chipsize:		[INTERN] the size of one chip for multichip arrays
>   * @pagemask:		[INTERN] page number mask = number of (pages / chip) - 1
> @@ -713,6 +712,7 @@ struct nand_data_interface {
>   * @read_retries:	[INTERN] the number of read retry modes supported
>   * @onfi_set_features:	[REPLACEABLE] set the features for ONFI nand
>   * @onfi_get_features:	[REPLACEABLE] get the features for ONFI nand
> + * @setup_data_interface: [OPTIONAL] setup the data interface and timing
>   * @bbt:		[INTERN] bad block table pointer
>   * @bbt_td:		[REPLACEABLE] bad block table descriptor for flash
>   *			lookup.
> @@ -759,6 +759,10 @@ struct nand_chip {
>  	int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
>  			int feature_addr, uint8_t *subfeature_para);
>  	int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode);
> +	int (*setup_data_interface)(struct mtd_info *mtd,
> +				    const struct nand_data_interface *conf,
> +				    bool check_only);
> +
>  
>  	int chip_delay;
>  	unsigned int options;
> @@ -788,6 +792,8 @@ struct nand_chip {
>  		struct nand_jedec_params jedec_params;
>  	};
>  
> +	const struct nand_data_interface *data_iface;
> +

How about making this field non-const so that you only allocate it once
and modify it when you switch from one mode to another.

>  	int read_retries;
>  
>  	flstate_t state;

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

* Re: [PATCH 5/7] mtd: nand: sunxi: switch from manual to automated timing config
  2016-09-06 10:39   ` Sascha Hauer
@ 2016-09-06 12:01     ` Boris Brezillon
  -1 siblings, 0 replies; 52+ messages in thread
From: Boris Brezillon @ 2016-09-06 12:01 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-mtd, kernel, linux-arm-kernel

On Tue,  6 Sep 2016 12:39:13 +0200
Sascha Hauer <s.hauer@pengutronix.de> wrote:

> The NAND framework is now able to select the best NAND timings for us.
> All we have to do is implement a ->setup_data_interface() function to
> apply those timings and remove the timing selection code from the sunxi
> driver.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  drivers/mtd/nand/sunxi_nand.c | 77 +++++++++----------------------------------
>  1 file changed, 15 insertions(+), 62 deletions(-)
> 
> diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
> index e414b31..ee42b8c 100644
> --- a/drivers/mtd/nand/sunxi_nand.c
> +++ b/drivers/mtd/nand/sunxi_nand.c
> @@ -1572,14 +1572,23 @@ static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
>  #define sunxi_nand_lookup_timing(l, p, c) \
>  			_sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c)
>  
> -static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
> -				       const struct nand_sdr_timings *timings)
> +static int sunxi_nfc_setup_data_interface(struct mtd_info *mtd,
> +					    const struct nand_data_interface *conf,
> +					    bool check_only)
>  {
> +	struct nand_chip *nand = mtd_to_nand(mtd);
> +	struct sunxi_nand_chip *chip = to_sunxi_nand(nand);
>  	struct sunxi_nfc *nfc = to_sunxi_nfc(chip->nand.controller);
> +	const struct nand_sdr_timings *timings;
>  	u32 min_clk_period = 0;
>  	s32 tWB, tADL, tWHR, tRHW, tCAD;
>  	long real_clk_rate;
>  
> +	if (conf->type != NAND_SDR_IFACE)
> +		return -ENOTSUPP;
> +
> +	timings = &conf->timings.sdr;

Can we hide this behind an accessor (nand_get_sdr_timings())? I know I'm
picky, but I've had so many drivers to patch in the past because of
internal reorganization, and I'd like to avoid that in the future.

> +
>  	/* T1 <=> tCLS */
>  	if (timings->tCLS_min > min_clk_period)
>  		min_clk_period = timings->tCLS_min;
> @@ -1679,6 +1688,9 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
>  		return tRHW;
>  	}
>  
> +	if (check_only)
> +		return 0;
> +
>  	/*
>  	 * TODO: according to ONFI specs this value only applies for DDR NAND,
>  	 * but Allwinner seems to set this to 0x7. Mimic them for now.
> @@ -1712,44 +1724,6 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
>  	return 0;
>  }
>  
> -static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip,
> -					struct device_node *np)
> -{
> -	struct mtd_info *mtd = nand_to_mtd(&chip->nand);
> -	const struct nand_sdr_timings *timings;
> -	int ret;
> -	int mode;
> -
> -	mode = onfi_get_async_timing_mode(&chip->nand);
> -	if (mode == ONFI_TIMING_MODE_UNKNOWN) {
> -		mode = chip->nand.onfi_timing_mode_default;
> -	} else {
> -		uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {};
> -		int i;
> -
> -		mode = fls(mode) - 1;
> -		if (mode < 0)
> -			mode = 0;
> -
> -		feature[0] = mode;
> -		for (i = 0; i < chip->nsels; i++) {
> -			chip->nand.select_chip(mtd, i);
> -			ret = chip->nand.onfi_set_features(mtd,	&chip->nand,
> -						ONFI_FEATURE_ADDR_TIMING_MODE,
> -						feature);
> -			chip->nand.select_chip(mtd, -1);
> -			if (ret)
> -				return ret;
> -		}
> -	}
> -
> -	timings = onfi_async_timing_mode_to_sdr_timings(mode);
> -	if (IS_ERR(timings))
> -		return PTR_ERR(timings);
> -
> -	return sunxi_nand_chip_set_timings(chip, timings);
> -}
> -
>  static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
>  				    struct mtd_oob_region *oobregion)
>  {
> @@ -1975,7 +1949,6 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc,
>  static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
>  				struct device_node *np)
>  {
> -	const struct nand_sdr_timings *timings;
>  	struct sunxi_nand_chip *chip;
>  	struct mtd_info *mtd;
>  	struct nand_chip *nand;
> @@ -2065,25 +2038,11 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
>  	nand->read_buf = sunxi_nfc_read_buf;
>  	nand->write_buf = sunxi_nfc_write_buf;
>  	nand->read_byte = sunxi_nfc_read_byte;
> +	nand->setup_data_interface = sunxi_nfc_setup_data_interface;
>  
>  	mtd = nand_to_mtd(nand);
>  	mtd->dev.parent = dev;
>  
> -	timings = onfi_async_timing_mode_to_sdr_timings(0);
> -	if (IS_ERR(timings)) {
> -		ret = PTR_ERR(timings);
> -		dev_err(dev,
> -			"could not retrieve timings for ONFI mode 0: %d\n",
> -			ret);
> -		return ret;
> -	}
> -
> -	ret = sunxi_nand_chip_set_timings(chip, timings);
> -	if (ret) {
> -		dev_err(dev, "could not configure chip timings: %d\n", ret);
> -		return ret;
> -	}
> -
>  	ret = nand_scan_ident(mtd, nsels, NULL);
>  	if (ret)
>  		return ret;
> @@ -2096,12 +2055,6 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
>  
>  	nand->options |= NAND_SUBPAGE_READ;
>  
> -	ret = sunxi_nand_chip_init_timings(chip, np);
> -	if (ret) {
> -		dev_err(dev, "could not configure chip timings: %d\n", ret);
> -		return ret;
> -	}
> -
>  	ret = sunxi_nand_ecc_init(mtd, &nand->ecc, np);
>  	if (ret) {
>  		dev_err(dev, "ECC init failed: %d\n", ret);

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

* [PATCH 5/7] mtd: nand: sunxi: switch from manual to automated timing config
@ 2016-09-06 12:01     ` Boris Brezillon
  0 siblings, 0 replies; 52+ messages in thread
From: Boris Brezillon @ 2016-09-06 12:01 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue,  6 Sep 2016 12:39:13 +0200
Sascha Hauer <s.hauer@pengutronix.de> wrote:

> The NAND framework is now able to select the best NAND timings for us.
> All we have to do is implement a ->setup_data_interface() function to
> apply those timings and remove the timing selection code from the sunxi
> driver.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  drivers/mtd/nand/sunxi_nand.c | 77 +++++++++----------------------------------
>  1 file changed, 15 insertions(+), 62 deletions(-)
> 
> diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
> index e414b31..ee42b8c 100644
> --- a/drivers/mtd/nand/sunxi_nand.c
> +++ b/drivers/mtd/nand/sunxi_nand.c
> @@ -1572,14 +1572,23 @@ static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
>  #define sunxi_nand_lookup_timing(l, p, c) \
>  			_sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c)
>  
> -static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
> -				       const struct nand_sdr_timings *timings)
> +static int sunxi_nfc_setup_data_interface(struct mtd_info *mtd,
> +					    const struct nand_data_interface *conf,
> +					    bool check_only)
>  {
> +	struct nand_chip *nand = mtd_to_nand(mtd);
> +	struct sunxi_nand_chip *chip = to_sunxi_nand(nand);
>  	struct sunxi_nfc *nfc = to_sunxi_nfc(chip->nand.controller);
> +	const struct nand_sdr_timings *timings;
>  	u32 min_clk_period = 0;
>  	s32 tWB, tADL, tWHR, tRHW, tCAD;
>  	long real_clk_rate;
>  
> +	if (conf->type != NAND_SDR_IFACE)
> +		return -ENOTSUPP;
> +
> +	timings = &conf->timings.sdr;

Can we hide this behind an accessor (nand_get_sdr_timings())? I know I'm
picky, but I've had so many drivers to patch in the past because of
internal reorganization, and I'd like to avoid that in the future.

> +
>  	/* T1 <=> tCLS */
>  	if (timings->tCLS_min > min_clk_period)
>  		min_clk_period = timings->tCLS_min;
> @@ -1679,6 +1688,9 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
>  		return tRHW;
>  	}
>  
> +	if (check_only)
> +		return 0;
> +
>  	/*
>  	 * TODO: according to ONFI specs this value only applies for DDR NAND,
>  	 * but Allwinner seems to set this to 0x7. Mimic them for now.
> @@ -1712,44 +1724,6 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
>  	return 0;
>  }
>  
> -static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip,
> -					struct device_node *np)
> -{
> -	struct mtd_info *mtd = nand_to_mtd(&chip->nand);
> -	const struct nand_sdr_timings *timings;
> -	int ret;
> -	int mode;
> -
> -	mode = onfi_get_async_timing_mode(&chip->nand);
> -	if (mode == ONFI_TIMING_MODE_UNKNOWN) {
> -		mode = chip->nand.onfi_timing_mode_default;
> -	} else {
> -		uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {};
> -		int i;
> -
> -		mode = fls(mode) - 1;
> -		if (mode < 0)
> -			mode = 0;
> -
> -		feature[0] = mode;
> -		for (i = 0; i < chip->nsels; i++) {
> -			chip->nand.select_chip(mtd, i);
> -			ret = chip->nand.onfi_set_features(mtd,	&chip->nand,
> -						ONFI_FEATURE_ADDR_TIMING_MODE,
> -						feature);
> -			chip->nand.select_chip(mtd, -1);
> -			if (ret)
> -				return ret;
> -		}
> -	}
> -
> -	timings = onfi_async_timing_mode_to_sdr_timings(mode);
> -	if (IS_ERR(timings))
> -		return PTR_ERR(timings);
> -
> -	return sunxi_nand_chip_set_timings(chip, timings);
> -}
> -
>  static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
>  				    struct mtd_oob_region *oobregion)
>  {
> @@ -1975,7 +1949,6 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc,
>  static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
>  				struct device_node *np)
>  {
> -	const struct nand_sdr_timings *timings;
>  	struct sunxi_nand_chip *chip;
>  	struct mtd_info *mtd;
>  	struct nand_chip *nand;
> @@ -2065,25 +2038,11 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
>  	nand->read_buf = sunxi_nfc_read_buf;
>  	nand->write_buf = sunxi_nfc_write_buf;
>  	nand->read_byte = sunxi_nfc_read_byte;
> +	nand->setup_data_interface = sunxi_nfc_setup_data_interface;
>  
>  	mtd = nand_to_mtd(nand);
>  	mtd->dev.parent = dev;
>  
> -	timings = onfi_async_timing_mode_to_sdr_timings(0);
> -	if (IS_ERR(timings)) {
> -		ret = PTR_ERR(timings);
> -		dev_err(dev,
> -			"could not retrieve timings for ONFI mode 0: %d\n",
> -			ret);
> -		return ret;
> -	}
> -
> -	ret = sunxi_nand_chip_set_timings(chip, timings);
> -	if (ret) {
> -		dev_err(dev, "could not configure chip timings: %d\n", ret);
> -		return ret;
> -	}
> -
>  	ret = nand_scan_ident(mtd, nsels, NULL);
>  	if (ret)
>  		return ret;
> @@ -2096,12 +2055,6 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
>  
>  	nand->options |= NAND_SUBPAGE_READ;
>  
> -	ret = sunxi_nand_chip_init_timings(chip, np);
> -	if (ret) {
> -		dev_err(dev, "could not configure chip timings: %d\n", ret);
> -		return ret;
> -	}
> -
>  	ret = sunxi_nand_ecc_init(mtd, &nand->ecc, np);
>  	if (ret) {
>  		dev_err(dev, "ECC init failed: %d\n", ret);

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

* Re: [PATCH 6/7] mtd: nand: mxc: implement onfi get/set features
  2016-09-06 10:39   ` Sascha Hauer
@ 2016-09-06 12:05     ` Boris Brezillon
  -1 siblings, 0 replies; 52+ messages in thread
From: Boris Brezillon @ 2016-09-06 12:05 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-mtd, kernel, linux-arm-kernel

On Tue,  6 Sep 2016 12:39:14 +0200
Sascha Hauer <s.hauer@pengutronix.de> wrote:

> To be able to support different ONFI timing modes we have to implement
> the onfi_set_features and onfi_get_features. Tested on an i.MX25 SoC.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  drivers/mtd/nand/mxc_nand.c | 53 +++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 53 insertions(+)
> 
> diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
> index 5173fad..1db8299 100644
> --- a/drivers/mtd/nand/mxc_nand.c
> +++ b/drivers/mtd/nand/mxc_nand.c
> @@ -1239,6 +1239,57 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
>  	}
>  }
>  
> +static int mxc_nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
> +			int addr, uint8_t *subfeature_param)
> +{
> +	struct nand_chip *nand_chip = mtd_to_nand(mtd);
> +	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
> +	int i;
> +
> +	if (!chip->onfi_version ||
> +	    !(le16_to_cpu(chip->onfi_params.opt_cmd)
> +	      & ONFI_OPT_CMD_SET_GET_FEATURES))
> +		return -EINVAL;
> +
> +	host->buf_start = 0;
> +
> +	for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
> +		chip->write_byte(mtd, subfeature_param[i]);
> +
> +	memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize);
> +	host->devtype_data->send_cmd(host, NAND_CMD_SET_FEATURES, false);
> +	mxc_do_addr_cycle(mtd, addr, -1);
> +	host->devtype_data->send_page(mtd, NFC_INPUT);
> +
> +	return 0;
> +}
> +
> +static int mxc_nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
> +			int addr, uint8_t *subfeature_param)
> +{
> +	struct nand_chip *nand_chip = mtd_to_nand(mtd);
> +	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
> +	int i;
> +
> +	if (!chip->onfi_version ||
> +	    !(le16_to_cpu(chip->onfi_params.opt_cmd)
> +	      & ONFI_OPT_CMD_SET_GET_FEATURES))
> +		return -EINVAL;
> +
> +	*(uint32_t *)host->main_area0 = 0xdeadbeef;
> +
> +	host->devtype_data->send_cmd(host, NAND_CMD_GET_FEATURES, false);
> +	mxc_do_addr_cycle(mtd, addr, -1);
> +	host->devtype_data->send_page(mtd, NFC_OUTPUT);
> +	memcpy32_fromio(host->data_buf, host->main_area0, 512);
> +	host->buf_start = 0;
> +
> +	for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
> +		*subfeature_param++ = chip->read_byte(mtd);
> +
> +	return 0;
> +}
> +
>  /*
>   * The generic flash bbt decriptors overlap with our ecc
>   * hardware, so define some i.MX specific ones.
> @@ -1513,6 +1564,8 @@ static int mxcnd_probe(struct platform_device *pdev)
>  	this->read_word = mxc_nand_read_word;
>  	this->write_buf = mxc_nand_write_buf;
>  	this->read_buf = mxc_nand_read_buf;
> +	this->onfi_set_features = mxc_nand_onfi_set_features;
> +	this->onfi_get_features = mxc_nand_onfi_get_features;

Do you really have to re-implement ->onfi_{set,get}_features()?
How about adding support for this command in your ->cmdfunc()
implementation and relying on the default ->onfi_{set,get}_features()
provided by the core?

>  
>  	host->clk = devm_clk_get(&pdev->dev, NULL);
>  	if (IS_ERR(host->clk))

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

* [PATCH 6/7] mtd: nand: mxc: implement onfi get/set features
@ 2016-09-06 12:05     ` Boris Brezillon
  0 siblings, 0 replies; 52+ messages in thread
From: Boris Brezillon @ 2016-09-06 12:05 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue,  6 Sep 2016 12:39:14 +0200
Sascha Hauer <s.hauer@pengutronix.de> wrote:

> To be able to support different ONFI timing modes we have to implement
> the onfi_set_features and onfi_get_features. Tested on an i.MX25 SoC.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  drivers/mtd/nand/mxc_nand.c | 53 +++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 53 insertions(+)
> 
> diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
> index 5173fad..1db8299 100644
> --- a/drivers/mtd/nand/mxc_nand.c
> +++ b/drivers/mtd/nand/mxc_nand.c
> @@ -1239,6 +1239,57 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
>  	}
>  }
>  
> +static int mxc_nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
> +			int addr, uint8_t *subfeature_param)
> +{
> +	struct nand_chip *nand_chip = mtd_to_nand(mtd);
> +	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
> +	int i;
> +
> +	if (!chip->onfi_version ||
> +	    !(le16_to_cpu(chip->onfi_params.opt_cmd)
> +	      & ONFI_OPT_CMD_SET_GET_FEATURES))
> +		return -EINVAL;
> +
> +	host->buf_start = 0;
> +
> +	for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
> +		chip->write_byte(mtd, subfeature_param[i]);
> +
> +	memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize);
> +	host->devtype_data->send_cmd(host, NAND_CMD_SET_FEATURES, false);
> +	mxc_do_addr_cycle(mtd, addr, -1);
> +	host->devtype_data->send_page(mtd, NFC_INPUT);
> +
> +	return 0;
> +}
> +
> +static int mxc_nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
> +			int addr, uint8_t *subfeature_param)
> +{
> +	struct nand_chip *nand_chip = mtd_to_nand(mtd);
> +	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
> +	int i;
> +
> +	if (!chip->onfi_version ||
> +	    !(le16_to_cpu(chip->onfi_params.opt_cmd)
> +	      & ONFI_OPT_CMD_SET_GET_FEATURES))
> +		return -EINVAL;
> +
> +	*(uint32_t *)host->main_area0 = 0xdeadbeef;
> +
> +	host->devtype_data->send_cmd(host, NAND_CMD_GET_FEATURES, false);
> +	mxc_do_addr_cycle(mtd, addr, -1);
> +	host->devtype_data->send_page(mtd, NFC_OUTPUT);
> +	memcpy32_fromio(host->data_buf, host->main_area0, 512);
> +	host->buf_start = 0;
> +
> +	for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
> +		*subfeature_param++ = chip->read_byte(mtd);
> +
> +	return 0;
> +}
> +
>  /*
>   * The generic flash bbt decriptors overlap with our ecc
>   * hardware, so define some i.MX specific ones.
> @@ -1513,6 +1564,8 @@ static int mxcnd_probe(struct platform_device *pdev)
>  	this->read_word = mxc_nand_read_word;
>  	this->write_buf = mxc_nand_write_buf;
>  	this->read_buf = mxc_nand_read_buf;
> +	this->onfi_set_features = mxc_nand_onfi_set_features;
> +	this->onfi_get_features = mxc_nand_onfi_get_features;

Do you really have to re-implement ->onfi_{set,get}_features()?
How about adding support for this command in your ->cmdfunc()
implementation and relying on the default ->onfi_{set,get}_features()
provided by the core?

>  
>  	host->clk = devm_clk_get(&pdev->dev, NULL);
>  	if (IS_ERR(host->clk))

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

* Re: [PATCH 3/7] mtd: nand: convert ONFI mode into data interface
  2016-09-06 10:39   ` Sascha Hauer
@ 2016-09-06 12:15     ` Boris Brezillon
  -1 siblings, 0 replies; 52+ messages in thread
From: Boris Brezillon @ 2016-09-06 12:15 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-mtd, linux-arm-kernel, kernel

On Tue,  6 Sep 2016 12:39:11 +0200
Sascha Hauer <s.hauer@pengutronix.de> wrote:

> struct nand_data_interface is the designated type to pass to
> the NAND drivers to configure the timing. To simplify this introduce
> onfi_async_timing_mode_to_data_interface() to convert a ONFI
> mode into a nand_data_interface.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  drivers/mtd/nand/nand_timings.c | 444 +++++++++++++++++++++-------------------
>  include/linux/mtd/nand.h        |   2 +
>  2 files changed, 240 insertions(+), 206 deletions(-)
> 
> diff --git a/drivers/mtd/nand/nand_timings.c b/drivers/mtd/nand/nand_timings.c
> index e81470a..260b074 100644
> --- a/drivers/mtd/nand/nand_timings.c
> +++ b/drivers/mtd/nand/nand_timings.c
> @@ -13,228 +13,246 @@
>  #include <linux/export.h>
>  #include <linux/mtd/nand.h>
>  

[...]

> +
> +/**
> + * onfi_async_timing_mode_to_data_interface - [NAND Interface] Retrieve NAND
> + * data interface according to the given ONFI timing mode
> + * @mode: ONFI timing mode
> + */
> +const struct nand_data_interface *onfi_async_timing_mode_to_data_interface(int mode)
> +{
> +	if (mode < 0 || mode >= ARRAY_SIZE(onfi_sdr_timings))
> +		return ERR_PTR(-EINVAL);
> +
> +	return &onfi_sdr_timings[mode];
> +}
> +EXPORT_SYMBOL(onfi_async_timing_mode_to_data_interface);

On second thought, I'd prefer to have the following helper:

int onfi_init_data_interface(struct nand_data_interface *iface,
			     enum nand_data_interface_type type,
			     int timing_mode)
{
	if (type != NAND_SDR_IFACE)
		return -EINVAL;

	if (timing_mode < 0 ||
	    timing_mode >= ARRAY_SIZE(onfi_sdr_timings))
		return -EINVAL;

	iface->type = NAND_SDR_IFACE;
	iface->timings.sdr = onfi_sdr_timings[timing_mode];

	return 0;
}

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

* [PATCH 3/7] mtd: nand: convert ONFI mode into data interface
@ 2016-09-06 12:15     ` Boris Brezillon
  0 siblings, 0 replies; 52+ messages in thread
From: Boris Brezillon @ 2016-09-06 12:15 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue,  6 Sep 2016 12:39:11 +0200
Sascha Hauer <s.hauer@pengutronix.de> wrote:

> struct nand_data_interface is the designated type to pass to
> the NAND drivers to configure the timing. To simplify this introduce
> onfi_async_timing_mode_to_data_interface() to convert a ONFI
> mode into a nand_data_interface.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  drivers/mtd/nand/nand_timings.c | 444 +++++++++++++++++++++-------------------
>  include/linux/mtd/nand.h        |   2 +
>  2 files changed, 240 insertions(+), 206 deletions(-)
> 
> diff --git a/drivers/mtd/nand/nand_timings.c b/drivers/mtd/nand/nand_timings.c
> index e81470a..260b074 100644
> --- a/drivers/mtd/nand/nand_timings.c
> +++ b/drivers/mtd/nand/nand_timings.c
> @@ -13,228 +13,246 @@
>  #include <linux/export.h>
>  #include <linux/mtd/nand.h>
>  

[...]

> +
> +/**
> + * onfi_async_timing_mode_to_data_interface - [NAND Interface] Retrieve NAND
> + * data interface according to the given ONFI timing mode
> + * @mode: ONFI timing mode
> + */
> +const struct nand_data_interface *onfi_async_timing_mode_to_data_interface(int mode)
> +{
> +	if (mode < 0 || mode >= ARRAY_SIZE(onfi_sdr_timings))
> +		return ERR_PTR(-EINVAL);
> +
> +	return &onfi_sdr_timings[mode];
> +}
> +EXPORT_SYMBOL(onfi_async_timing_mode_to_data_interface);

On second thought, I'd prefer to have the following helper:

int onfi_init_data_interface(struct nand_data_interface *iface,
			     enum nand_data_interface_type type,
			     int timing_mode)
{
	if (type != NAND_SDR_IFACE)
		return -EINVAL;

	if (timing_mode < 0 ||
	    timing_mode >= ARRAY_SIZE(onfi_sdr_timings))
		return -EINVAL;

	iface->type = NAND_SDR_IFACE;
	iface->timings.sdr = onfi_sdr_timings[timing_mode];

	return 0;
}

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

* Re: [PATCH 6/7] mtd: nand: mxc: implement onfi get/set features
  2016-09-06 12:05     ` Boris Brezillon
@ 2016-09-06 12:47       ` Sascha Hauer
  -1 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 12:47 UTC (permalink / raw)
  To: Boris Brezillon; +Cc: linux-mtd, kernel, linux-arm-kernel

On Tue, Sep 06, 2016 at 02:05:21PM +0200, Boris Brezillon wrote:
> On Tue,  6 Sep 2016 12:39:14 +0200
> Sascha Hauer <s.hauer@pengutronix.de> wrote:
> 
> > To be able to support different ONFI timing modes we have to implement
> > the onfi_set_features and onfi_get_features. Tested on an i.MX25 SoC.
> > 
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > ---
> >  drivers/mtd/nand/mxc_nand.c | 53 +++++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 53 insertions(+)
> > 
> > diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
> > index 5173fad..1db8299 100644
> > --- a/drivers/mtd/nand/mxc_nand.c
> > +++ b/drivers/mtd/nand/mxc_nand.c
> > @@ -1239,6 +1239,57 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
> >  	}
> >  }
> >  
> > +static int mxc_nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
> > +			int addr, uint8_t *subfeature_param)
> > +{
> > +	struct nand_chip *nand_chip = mtd_to_nand(mtd);
> > +	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
> > +	int i;
> > +
> > +	if (!chip->onfi_version ||
> > +	    !(le16_to_cpu(chip->onfi_params.opt_cmd)
> > +	      & ONFI_OPT_CMD_SET_GET_FEATURES))
> > +		return -EINVAL;
> > +
> > +	host->buf_start = 0;
> > +
> > +	for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
> > +		chip->write_byte(mtd, subfeature_param[i]);
> > +
> > +	memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize);
> > +	host->devtype_data->send_cmd(host, NAND_CMD_SET_FEATURES, false);
> > +	mxc_do_addr_cycle(mtd, addr, -1);
> > +	host->devtype_data->send_page(mtd, NFC_INPUT);
> > +
> > +	return 0;
> > +}
> > +
> > +static int mxc_nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
> > +			int addr, uint8_t *subfeature_param)
> > +{
> > +	struct nand_chip *nand_chip = mtd_to_nand(mtd);
> > +	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
> > +	int i;
> > +
> > +	if (!chip->onfi_version ||
> > +	    !(le16_to_cpu(chip->onfi_params.opt_cmd)
> > +	      & ONFI_OPT_CMD_SET_GET_FEATURES))
> > +		return -EINVAL;
> > +
> > +	*(uint32_t *)host->main_area0 = 0xdeadbeef;

This line should be removed by the way.

> > +
> > +	host->devtype_data->send_cmd(host, NAND_CMD_GET_FEATURES, false);
> > +	mxc_do_addr_cycle(mtd, addr, -1);
> > +	host->devtype_data->send_page(mtd, NFC_OUTPUT);
> > +	memcpy32_fromio(host->data_buf, host->main_area0, 512);
> > +	host->buf_start = 0;
> > +
> > +	for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
> > +		*subfeature_param++ = chip->read_byte(mtd);
> > +
> > +	return 0;
> > +}
> > +
> >  /*
> >   * The generic flash bbt decriptors overlap with our ecc
> >   * hardware, so define some i.MX specific ones.
> > @@ -1513,6 +1564,8 @@ static int mxcnd_probe(struct platform_device *pdev)
> >  	this->read_word = mxc_nand_read_word;
> >  	this->write_buf = mxc_nand_write_buf;
> >  	this->read_buf = mxc_nand_read_buf;
> > +	this->onfi_set_features = mxc_nand_onfi_set_features;
> > +	this->onfi_get_features = mxc_nand_onfi_get_features;
> 
> Do you really have to re-implement ->onfi_{set,get}_features()?
> How about adding support for this command in your ->cmdfunc()
> implementation and relying on the default ->onfi_{set,get}_features()
> provided by the core?

I probably could. The problem is that the generic
nand_onfi_set_features() first sends the command then afterwards uses
chip->write_byte to actually send the data. This is not how the mxc_nand
controller works. On mxc_nand I first need the data before I can issue
the command.
I could implement it in the ->cmdfunc() like: When NAND_CMD_SET_FEATURES
is issued, track that information. Then I have to count how many times
->write_byte() has been called and when I reach
ONFI_SUBFEATURE_PARAM_LEN I have to send the command. While that would
probably work I was glad that I could avoid that. It's one of the
places where one realizes that the nand core layer is built around the
nand controllers of the late nineties.

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* [PATCH 6/7] mtd: nand: mxc: implement onfi get/set features
@ 2016-09-06 12:47       ` Sascha Hauer
  0 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 12:47 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Sep 06, 2016 at 02:05:21PM +0200, Boris Brezillon wrote:
> On Tue,  6 Sep 2016 12:39:14 +0200
> Sascha Hauer <s.hauer@pengutronix.de> wrote:
> 
> > To be able to support different ONFI timing modes we have to implement
> > the onfi_set_features and onfi_get_features. Tested on an i.MX25 SoC.
> > 
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > ---
> >  drivers/mtd/nand/mxc_nand.c | 53 +++++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 53 insertions(+)
> > 
> > diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
> > index 5173fad..1db8299 100644
> > --- a/drivers/mtd/nand/mxc_nand.c
> > +++ b/drivers/mtd/nand/mxc_nand.c
> > @@ -1239,6 +1239,57 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
> >  	}
> >  }
> >  
> > +static int mxc_nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
> > +			int addr, uint8_t *subfeature_param)
> > +{
> > +	struct nand_chip *nand_chip = mtd_to_nand(mtd);
> > +	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
> > +	int i;
> > +
> > +	if (!chip->onfi_version ||
> > +	    !(le16_to_cpu(chip->onfi_params.opt_cmd)
> > +	      & ONFI_OPT_CMD_SET_GET_FEATURES))
> > +		return -EINVAL;
> > +
> > +	host->buf_start = 0;
> > +
> > +	for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
> > +		chip->write_byte(mtd, subfeature_param[i]);
> > +
> > +	memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize);
> > +	host->devtype_data->send_cmd(host, NAND_CMD_SET_FEATURES, false);
> > +	mxc_do_addr_cycle(mtd, addr, -1);
> > +	host->devtype_data->send_page(mtd, NFC_INPUT);
> > +
> > +	return 0;
> > +}
> > +
> > +static int mxc_nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
> > +			int addr, uint8_t *subfeature_param)
> > +{
> > +	struct nand_chip *nand_chip = mtd_to_nand(mtd);
> > +	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
> > +	int i;
> > +
> > +	if (!chip->onfi_version ||
> > +	    !(le16_to_cpu(chip->onfi_params.opt_cmd)
> > +	      & ONFI_OPT_CMD_SET_GET_FEATURES))
> > +		return -EINVAL;
> > +
> > +	*(uint32_t *)host->main_area0 = 0xdeadbeef;

This line should be removed by the way.

> > +
> > +	host->devtype_data->send_cmd(host, NAND_CMD_GET_FEATURES, false);
> > +	mxc_do_addr_cycle(mtd, addr, -1);
> > +	host->devtype_data->send_page(mtd, NFC_OUTPUT);
> > +	memcpy32_fromio(host->data_buf, host->main_area0, 512);
> > +	host->buf_start = 0;
> > +
> > +	for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
> > +		*subfeature_param++ = chip->read_byte(mtd);
> > +
> > +	return 0;
> > +}
> > +
> >  /*
> >   * The generic flash bbt decriptors overlap with our ecc
> >   * hardware, so define some i.MX specific ones.
> > @@ -1513,6 +1564,8 @@ static int mxcnd_probe(struct platform_device *pdev)
> >  	this->read_word = mxc_nand_read_word;
> >  	this->write_buf = mxc_nand_write_buf;
> >  	this->read_buf = mxc_nand_read_buf;
> > +	this->onfi_set_features = mxc_nand_onfi_set_features;
> > +	this->onfi_get_features = mxc_nand_onfi_get_features;
> 
> Do you really have to re-implement ->onfi_{set,get}_features()?
> How about adding support for this command in your ->cmdfunc()
> implementation and relying on the default ->onfi_{set,get}_features()
> provided by the core?

I probably could. The problem is that the generic
nand_onfi_set_features() first sends the command then afterwards uses
chip->write_byte to actually send the data. This is not how the mxc_nand
controller works. On mxc_nand I first need the data before I can issue
the command.
I could implement it in the ->cmdfunc() like: When NAND_CMD_SET_FEATURES
is issued, track that information. Then I have to count how many times
->write_byte() has been called and when I reach
ONFI_SUBFEATURE_PARAM_LEN I have to send the command. While that would
probably work I was glad that I could avoid that. It's one of the
places where one realizes that the nand core layer is built around the
nand controllers of the late nineties.

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH 6/7] mtd: nand: mxc: implement onfi get/set features
  2016-09-06 12:47       ` Sascha Hauer
@ 2016-09-06 12:52         ` Boris Brezillon
  -1 siblings, 0 replies; 52+ messages in thread
From: Boris Brezillon @ 2016-09-06 12:52 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-mtd, kernel, linux-arm-kernel

On Tue, 6 Sep 2016 14:47:14 +0200
Sascha Hauer <s.hauer@pengutronix.de> wrote:

> On Tue, Sep 06, 2016 at 02:05:21PM +0200, Boris Brezillon wrote:
> > On Tue,  6 Sep 2016 12:39:14 +0200
> > Sascha Hauer <s.hauer@pengutronix.de> wrote:
> >   
> > > To be able to support different ONFI timing modes we have to implement
> > > the onfi_set_features and onfi_get_features. Tested on an i.MX25 SoC.
> > > 
> > > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > > ---
> > >  drivers/mtd/nand/mxc_nand.c | 53 +++++++++++++++++++++++++++++++++++++++++++++
> > >  1 file changed, 53 insertions(+)
> > > 
> > > diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
> > > index 5173fad..1db8299 100644
> > > --- a/drivers/mtd/nand/mxc_nand.c
> > > +++ b/drivers/mtd/nand/mxc_nand.c
> > > @@ -1239,6 +1239,57 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
> > >  	}
> > >  }
> > >  
> > > +static int mxc_nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
> > > +			int addr, uint8_t *subfeature_param)
> > > +{
> > > +	struct nand_chip *nand_chip = mtd_to_nand(mtd);
> > > +	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
> > > +	int i;
> > > +
> > > +	if (!chip->onfi_version ||
> > > +	    !(le16_to_cpu(chip->onfi_params.opt_cmd)
> > > +	      & ONFI_OPT_CMD_SET_GET_FEATURES))
> > > +		return -EINVAL;
> > > +
> > > +	host->buf_start = 0;
> > > +
> > > +	for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
> > > +		chip->write_byte(mtd, subfeature_param[i]);
> > > +
> > > +	memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize);
> > > +	host->devtype_data->send_cmd(host, NAND_CMD_SET_FEATURES, false);
> > > +	mxc_do_addr_cycle(mtd, addr, -1);
> > > +	host->devtype_data->send_page(mtd, NFC_INPUT);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int mxc_nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
> > > +			int addr, uint8_t *subfeature_param)
> > > +{
> > > +	struct nand_chip *nand_chip = mtd_to_nand(mtd);
> > > +	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
> > > +	int i;
> > > +
> > > +	if (!chip->onfi_version ||
> > > +	    !(le16_to_cpu(chip->onfi_params.opt_cmd)
> > > +	      & ONFI_OPT_CMD_SET_GET_FEATURES))
> > > +		return -EINVAL;
> > > +
> > > +	*(uint32_t *)host->main_area0 = 0xdeadbeef;  
> 
> This line should be removed by the way.
> 
> > > +
> > > +	host->devtype_data->send_cmd(host, NAND_CMD_GET_FEATURES, false);
> > > +	mxc_do_addr_cycle(mtd, addr, -1);
> > > +	host->devtype_data->send_page(mtd, NFC_OUTPUT);
> > > +	memcpy32_fromio(host->data_buf, host->main_area0, 512);
> > > +	host->buf_start = 0;
> > > +
> > > +	for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
> > > +		*subfeature_param++ = chip->read_byte(mtd);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > >  /*
> > >   * The generic flash bbt decriptors overlap with our ecc
> > >   * hardware, so define some i.MX specific ones.
> > > @@ -1513,6 +1564,8 @@ static int mxcnd_probe(struct platform_device *pdev)
> > >  	this->read_word = mxc_nand_read_word;
> > >  	this->write_buf = mxc_nand_write_buf;
> > >  	this->read_buf = mxc_nand_read_buf;
> > > +	this->onfi_set_features = mxc_nand_onfi_set_features;
> > > +	this->onfi_get_features = mxc_nand_onfi_get_features;  
> > 
> > Do you really have to re-implement ->onfi_{set,get}_features()?
> > How about adding support for this command in your ->cmdfunc()
> > implementation and relying on the default ->onfi_{set,get}_features()
> > provided by the core?  
> 
> I probably could. The problem is that the generic
> nand_onfi_set_features() first sends the command then afterwards uses
> chip->write_byte to actually send the data. This is not how the mxc_nand
> controller works. On mxc_nand I first need the data before I can issue
> the command.
> I could implement it in the ->cmdfunc() like: When NAND_CMD_SET_FEATURES
> is issued, track that information. Then I have to count how many times
> ->write_byte() has been called and when I reach  
> ONFI_SUBFEATURE_PARAM_LEN I have to send the command. While that would
> probably work I was glad that I could avoid that. It's one of the
> places where one realizes that the nand core layer is built around the
> nand controllers of the late nineties.

Okay. Actually I have some plans for these problems, but let's stick to
mxc_nand_onfi_{set,get}_features() for now.

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

* [PATCH 6/7] mtd: nand: mxc: implement onfi get/set features
@ 2016-09-06 12:52         ` Boris Brezillon
  0 siblings, 0 replies; 52+ messages in thread
From: Boris Brezillon @ 2016-09-06 12:52 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 6 Sep 2016 14:47:14 +0200
Sascha Hauer <s.hauer@pengutronix.de> wrote:

> On Tue, Sep 06, 2016 at 02:05:21PM +0200, Boris Brezillon wrote:
> > On Tue,  6 Sep 2016 12:39:14 +0200
> > Sascha Hauer <s.hauer@pengutronix.de> wrote:
> >   
> > > To be able to support different ONFI timing modes we have to implement
> > > the onfi_set_features and onfi_get_features. Tested on an i.MX25 SoC.
> > > 
> > > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > > ---
> > >  drivers/mtd/nand/mxc_nand.c | 53 +++++++++++++++++++++++++++++++++++++++++++++
> > >  1 file changed, 53 insertions(+)
> > > 
> > > diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
> > > index 5173fad..1db8299 100644
> > > --- a/drivers/mtd/nand/mxc_nand.c
> > > +++ b/drivers/mtd/nand/mxc_nand.c
> > > @@ -1239,6 +1239,57 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
> > >  	}
> > >  }
> > >  
> > > +static int mxc_nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip,
> > > +			int addr, uint8_t *subfeature_param)
> > > +{
> > > +	struct nand_chip *nand_chip = mtd_to_nand(mtd);
> > > +	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
> > > +	int i;
> > > +
> > > +	if (!chip->onfi_version ||
> > > +	    !(le16_to_cpu(chip->onfi_params.opt_cmd)
> > > +	      & ONFI_OPT_CMD_SET_GET_FEATURES))
> > > +		return -EINVAL;
> > > +
> > > +	host->buf_start = 0;
> > > +
> > > +	for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
> > > +		chip->write_byte(mtd, subfeature_param[i]);
> > > +
> > > +	memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize);
> > > +	host->devtype_data->send_cmd(host, NAND_CMD_SET_FEATURES, false);
> > > +	mxc_do_addr_cycle(mtd, addr, -1);
> > > +	host->devtype_data->send_page(mtd, NFC_INPUT);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int mxc_nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
> > > +			int addr, uint8_t *subfeature_param)
> > > +{
> > > +	struct nand_chip *nand_chip = mtd_to_nand(mtd);
> > > +	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
> > > +	int i;
> > > +
> > > +	if (!chip->onfi_version ||
> > > +	    !(le16_to_cpu(chip->onfi_params.opt_cmd)
> > > +	      & ONFI_OPT_CMD_SET_GET_FEATURES))
> > > +		return -EINVAL;
> > > +
> > > +	*(uint32_t *)host->main_area0 = 0xdeadbeef;  
> 
> This line should be removed by the way.
> 
> > > +
> > > +	host->devtype_data->send_cmd(host, NAND_CMD_GET_FEATURES, false);
> > > +	mxc_do_addr_cycle(mtd, addr, -1);
> > > +	host->devtype_data->send_page(mtd, NFC_OUTPUT);
> > > +	memcpy32_fromio(host->data_buf, host->main_area0, 512);
> > > +	host->buf_start = 0;
> > > +
> > > +	for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
> > > +		*subfeature_param++ = chip->read_byte(mtd);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > >  /*
> > >   * The generic flash bbt decriptors overlap with our ecc
> > >   * hardware, so define some i.MX specific ones.
> > > @@ -1513,6 +1564,8 @@ static int mxcnd_probe(struct platform_device *pdev)
> > >  	this->read_word = mxc_nand_read_word;
> > >  	this->write_buf = mxc_nand_write_buf;
> > >  	this->read_buf = mxc_nand_read_buf;
> > > +	this->onfi_set_features = mxc_nand_onfi_set_features;
> > > +	this->onfi_get_features = mxc_nand_onfi_get_features;  
> > 
> > Do you really have to re-implement ->onfi_{set,get}_features()?
> > How about adding support for this command in your ->cmdfunc()
> > implementation and relying on the default ->onfi_{set,get}_features()
> > provided by the core?  
> 
> I probably could. The problem is that the generic
> nand_onfi_set_features() first sends the command then afterwards uses
> chip->write_byte to actually send the data. This is not how the mxc_nand
> controller works. On mxc_nand I first need the data before I can issue
> the command.
> I could implement it in the ->cmdfunc() like: When NAND_CMD_SET_FEATURES
> is issued, track that information. Then I have to count how many times
> ->write_byte() has been called and when I reach  
> ONFI_SUBFEATURE_PARAM_LEN I have to send the command. While that would
> probably work I was glad that I could avoid that. It's one of the
> places where one realizes that the nand core layer is built around the
> nand controllers of the late nineties.

Okay. Actually I have some plans for these problems, but let's stick to
mxc_nand_onfi_{set,get}_features() for now.

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

* Re: [PATCH 1/7] mtd: nand: Create a NAND reset function
  2016-09-06 11:18     ` Boris Brezillon
@ 2016-09-06 13:02       ` Sascha Hauer
  -1 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 13:02 UTC (permalink / raw)
  To: Boris Brezillon; +Cc: linux-mtd, kernel, linux-arm-kernel

On Tue, Sep 06, 2016 at 01:18:51PM +0200, Boris Brezillon wrote:
> On Tue,  6 Sep 2016 12:39:09 +0200
> Sascha Hauer <s.hauer@pengutronix.de> wrote:
> 
> > When NAND devices are resetted some initialization may have to be done,
> > like for example they have to be configured for the timing mode that
> > shall be used. To get a common place where this initialization can be
> > implemented create a nand_reset() function. This currently only issues
> > a NAND_CMD_RESET to the NAND device. The places issuing this command
> > manually are replaced with a call to nand_reset().
> > 
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > ---
> >  drivers/mtd/nand/nand_base.c | 23 ++++++++++++++++++-----
> >  include/linux/mtd/nand.h     |  3 +++
> >  2 files changed, 21 insertions(+), 5 deletions(-)
> > 
> > diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> > index 77533f7..20151fc 100644
> > --- a/drivers/mtd/nand/nand_base.c
> > +++ b/drivers/mtd/nand/nand_base.c
> > @@ -948,6 +948,19 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
> >  }
> >  
> >  /**
> > + * nand_reset - Reset and initialize a NAND device
> > + * @mtd: mtd info
> > + *
> > + * Returns 0 for success or negative error code otherwise
> > + */
> > +int nand_reset(struct mtd_info *mtd)
> 
> Can we pass a struct nand_chip * here?

Instead of or additionally to truct mtd_info *? One should be enough,
although most functions take both.

> 
> > +{
> > +	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
> 
> You miss the chip variable.

Yeah, the missing hunk is probably in one of the other patches ;) Will
fix.

> >  extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
> >  
> > +/* Reset and initialize a NAND device */
> > +extern int nand_reset(struct mtd_info *mtd);
> 
> The extern keyword is not needed here.

Nope, just added it because the other functions have it aswell. Do we care
to remove the extern from the other function declarations?

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* [PATCH 1/7] mtd: nand: Create a NAND reset function
@ 2016-09-06 13:02       ` Sascha Hauer
  0 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 13:02 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Sep 06, 2016 at 01:18:51PM +0200, Boris Brezillon wrote:
> On Tue,  6 Sep 2016 12:39:09 +0200
> Sascha Hauer <s.hauer@pengutronix.de> wrote:
> 
> > When NAND devices are resetted some initialization may have to be done,
> > like for example they have to be configured for the timing mode that
> > shall be used. To get a common place where this initialization can be
> > implemented create a nand_reset() function. This currently only issues
> > a NAND_CMD_RESET to the NAND device. The places issuing this command
> > manually are replaced with a call to nand_reset().
> > 
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > ---
> >  drivers/mtd/nand/nand_base.c | 23 ++++++++++++++++++-----
> >  include/linux/mtd/nand.h     |  3 +++
> >  2 files changed, 21 insertions(+), 5 deletions(-)
> > 
> > diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> > index 77533f7..20151fc 100644
> > --- a/drivers/mtd/nand/nand_base.c
> > +++ b/drivers/mtd/nand/nand_base.c
> > @@ -948,6 +948,19 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
> >  }
> >  
> >  /**
> > + * nand_reset - Reset and initialize a NAND device
> > + * @mtd: mtd info
> > + *
> > + * Returns 0 for success or negative error code otherwise
> > + */
> > +int nand_reset(struct mtd_info *mtd)
> 
> Can we pass a struct nand_chip * here?

Instead of or additionally to truct mtd_info *? One should be enough,
although most functions take both.

> 
> > +{
> > +	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
> 
> You miss the chip variable.

Yeah, the missing hunk is probably in one of the other patches ;) Will
fix.

> >  extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
> >  
> > +/* Reset and initialize a NAND device */
> > +extern int nand_reset(struct mtd_info *mtd);
> 
> The extern keyword is not needed here.

Nope, just added it because the other functions have it aswell. Do we care
to remove the extern from the other function declarations?

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH 1/7] mtd: nand: Create a NAND reset function
  2016-09-06 13:02       ` Sascha Hauer
@ 2016-09-06 13:06         ` Boris Brezillon
  -1 siblings, 0 replies; 52+ messages in thread
From: Boris Brezillon @ 2016-09-06 13:06 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-mtd, kernel, linux-arm-kernel

On Tue, 6 Sep 2016 15:02:43 +0200
Sascha Hauer <s.hauer@pengutronix.de> wrote:

> On Tue, Sep 06, 2016 at 01:18:51PM +0200, Boris Brezillon wrote:
> > On Tue,  6 Sep 2016 12:39:09 +0200
> > Sascha Hauer <s.hauer@pengutronix.de> wrote:
> >   
> > > When NAND devices are resetted some initialization may have to be done,
> > > like for example they have to be configured for the timing mode that
> > > shall be used. To get a common place where this initialization can be
> > > implemented create a nand_reset() function. This currently only issues
> > > a NAND_CMD_RESET to the NAND device. The places issuing this command
> > > manually are replaced with a call to nand_reset().
> > > 
> > > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > > ---
> > >  drivers/mtd/nand/nand_base.c | 23 ++++++++++++++++++-----
> > >  include/linux/mtd/nand.h     |  3 +++
> > >  2 files changed, 21 insertions(+), 5 deletions(-)
> > > 
> > > diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> > > index 77533f7..20151fc 100644
> > > --- a/drivers/mtd/nand/nand_base.c
> > > +++ b/drivers/mtd/nand/nand_base.c
> > > @@ -948,6 +948,19 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
> > >  }
> > >  
> > >  /**
> > > + * nand_reset - Reset and initialize a NAND device
> > > + * @mtd: mtd info
> > > + *
> > > + * Returns 0 for success or negative error code otherwise
> > > + */
> > > +int nand_reset(struct mtd_info *mtd)  
> > 
> > Can we pass a struct nand_chip * here?  
> 
> Instead of or additionally to truct mtd_info *? One should be enough,
> although most functions take both.

'Instead of'. mtd can be extracted from chip.

> 
> >   
> > > +{
> > > +	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);  
> > 
> > You miss the chip variable.  
> 
> Yeah, the missing hunk is probably in one of the other patches ;) Will
> fix.
> 
> > >  extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
> > >  
> > > +/* Reset and initialize a NAND device */
> > > +extern int nand_reset(struct mtd_info *mtd);  
> > 
> > The extern keyword is not needed here.  
> 
> Nope, just added it because the other functions have it aswell. Do we care
> to remove the extern from the other function declarations?

You can, if you want.

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

* [PATCH 1/7] mtd: nand: Create a NAND reset function
@ 2016-09-06 13:06         ` Boris Brezillon
  0 siblings, 0 replies; 52+ messages in thread
From: Boris Brezillon @ 2016-09-06 13:06 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 6 Sep 2016 15:02:43 +0200
Sascha Hauer <s.hauer@pengutronix.de> wrote:

> On Tue, Sep 06, 2016 at 01:18:51PM +0200, Boris Brezillon wrote:
> > On Tue,  6 Sep 2016 12:39:09 +0200
> > Sascha Hauer <s.hauer@pengutronix.de> wrote:
> >   
> > > When NAND devices are resetted some initialization may have to be done,
> > > like for example they have to be configured for the timing mode that
> > > shall be used. To get a common place where this initialization can be
> > > implemented create a nand_reset() function. This currently only issues
> > > a NAND_CMD_RESET to the NAND device. The places issuing this command
> > > manually are replaced with a call to nand_reset().
> > > 
> > > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > > ---
> > >  drivers/mtd/nand/nand_base.c | 23 ++++++++++++++++++-----
> > >  include/linux/mtd/nand.h     |  3 +++
> > >  2 files changed, 21 insertions(+), 5 deletions(-)
> > > 
> > > diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> > > index 77533f7..20151fc 100644
> > > --- a/drivers/mtd/nand/nand_base.c
> > > +++ b/drivers/mtd/nand/nand_base.c
> > > @@ -948,6 +948,19 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
> > >  }
> > >  
> > >  /**
> > > + * nand_reset - Reset and initialize a NAND device
> > > + * @mtd: mtd info
> > > + *
> > > + * Returns 0 for success or negative error code otherwise
> > > + */
> > > +int nand_reset(struct mtd_info *mtd)  
> > 
> > Can we pass a struct nand_chip * here?  
> 
> Instead of or additionally to truct mtd_info *? One should be enough,
> although most functions take both.

'Instead of'. mtd can be extracted from chip.

> 
> >   
> > > +{
> > > +	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);  
> > 
> > You miss the chip variable.  
> 
> Yeah, the missing hunk is probably in one of the other patches ;) Will
> fix.
> 
> > >  extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
> > >  
> > > +/* Reset and initialize a NAND device */
> > > +extern int nand_reset(struct mtd_info *mtd);  
> > 
> > The extern keyword is not needed here.  
> 
> Nope, just added it because the other functions have it aswell. Do we care
> to remove the extern from the other function declarations?

You can, if you want.

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

* Re: [PATCH 2/7] mtd: nand: Introduce nand_data_interface
  2016-09-06 11:21     ` Boris Brezillon
@ 2016-09-06 13:34       ` Sascha Hauer
  -1 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 13:34 UTC (permalink / raw)
  To: Boris Brezillon; +Cc: linux-mtd, kernel, linux-arm-kernel

On Tue, Sep 06, 2016 at 01:21:18PM +0200, Boris Brezillon wrote:
> On Tue,  6 Sep 2016 12:39:10 +0200
> Sascha Hauer <s.hauer@pengutronix.de> wrote:
> 
> > Currently we have no data structure to fully describe a NAND timing.
> > We only have struct nand_sdr_timings for NAND timings in SDR mode,
> > but nothing for DDR mode and also no container to store both types
> > of timing.
> > This patch adds struct nand_data_interface which stores the timing
> > type and a union of different timings. This can be used to pass to
> > drivers in order to configure the timing.
> > 
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > ---
> >  include/linux/mtd/nand.h | 109 ++++++++++++++++++++++++++---------------------
> >  1 file changed, 60 insertions(+), 49 deletions(-)
> > 
> > diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
> > index 9af9575..19c73ef 100644
> > --- a/include/linux/mtd/nand.h
> > +++ b/include/linux/mtd/nand.h
> > @@ -568,6 +568,66 @@ struct nand_buffers {
> >  	uint8_t *databuf;
> >  };
> >  
> > +/*
> > + * struct nand_sdr_timings - SDR NAND chip timings
> > + *
> > + * This struct defines the timing requirements of a SDR NAND chip.
> > + * These information can be found in every NAND datasheets and the timings
> > + * meaning are described in the ONFI specifications:
> > + * www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf (chapter 4.15 Timing
> > + * Parameters)
> > + *
> > + * All these timings are expressed in picoseconds.
> > + */
> > +
> > +struct nand_sdr_timings {
> > +	u32 tALH_min;
> > +	u32 tADL_min;
> > +	u32 tALS_min;
> > +	u32 tAR_min;
> > +	u32 tCEA_max;
> > +	u32 tCEH_min;
> > +	u32 tCH_min;
> > +	u32 tCHZ_max;
> > +	u32 tCLH_min;
> > +	u32 tCLR_min;
> > +	u32 tCLS_min;
> > +	u32 tCOH_min;
> > +	u32 tCS_min;
> > +	u32 tDH_min;
> > +	u32 tDS_min;
> > +	u32 tFEAT_max;
> > +	u32 tIR_min;
> > +	u32 tITC_max;
> > +	u32 tRC_min;
> > +	u32 tREA_max;
> > +	u32 tREH_min;
> > +	u32 tRHOH_min;
> > +	u32 tRHW_min;
> > +	u32 tRHZ_max;
> > +	u32 tRLOH_min;
> > +	u32 tRP_min;
> > +	u32 tRR_min;
> > +	u64 tRST_max;
> > +	u32 tWB_max;
> > +	u32 tWC_min;
> > +	u32 tWH_min;
> > +	u32 tWHR_min;
> > +	u32 tWP_min;
> > +	u32 tWW_min;
> > +};
> > +
> > +enum nand_data_interface_type {
> > +	NAND_SDR_IFACE,
> > +};
> > +
> 
> I know this is my code, and I'm the one to blame here, but can you
> document the nand_data_interface fields (kerneldoc format)?

Sure, can do. Do you know what tCEH is? This one is not documented in
the ONFI spec.

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* [PATCH 2/7] mtd: nand: Introduce nand_data_interface
@ 2016-09-06 13:34       ` Sascha Hauer
  0 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 13:34 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Sep 06, 2016 at 01:21:18PM +0200, Boris Brezillon wrote:
> On Tue,  6 Sep 2016 12:39:10 +0200
> Sascha Hauer <s.hauer@pengutronix.de> wrote:
> 
> > Currently we have no data structure to fully describe a NAND timing.
> > We only have struct nand_sdr_timings for NAND timings in SDR mode,
> > but nothing for DDR mode and also no container to store both types
> > of timing.
> > This patch adds struct nand_data_interface which stores the timing
> > type and a union of different timings. This can be used to pass to
> > drivers in order to configure the timing.
> > 
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > ---
> >  include/linux/mtd/nand.h | 109 ++++++++++++++++++++++++++---------------------
> >  1 file changed, 60 insertions(+), 49 deletions(-)
> > 
> > diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
> > index 9af9575..19c73ef 100644
> > --- a/include/linux/mtd/nand.h
> > +++ b/include/linux/mtd/nand.h
> > @@ -568,6 +568,66 @@ struct nand_buffers {
> >  	uint8_t *databuf;
> >  };
> >  
> > +/*
> > + * struct nand_sdr_timings - SDR NAND chip timings
> > + *
> > + * This struct defines the timing requirements of a SDR NAND chip.
> > + * These information can be found in every NAND datasheets and the timings
> > + * meaning are described in the ONFI specifications:
> > + * www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf (chapter 4.15 Timing
> > + * Parameters)
> > + *
> > + * All these timings are expressed in picoseconds.
> > + */
> > +
> > +struct nand_sdr_timings {
> > +	u32 tALH_min;
> > +	u32 tADL_min;
> > +	u32 tALS_min;
> > +	u32 tAR_min;
> > +	u32 tCEA_max;
> > +	u32 tCEH_min;
> > +	u32 tCH_min;
> > +	u32 tCHZ_max;
> > +	u32 tCLH_min;
> > +	u32 tCLR_min;
> > +	u32 tCLS_min;
> > +	u32 tCOH_min;
> > +	u32 tCS_min;
> > +	u32 tDH_min;
> > +	u32 tDS_min;
> > +	u32 tFEAT_max;
> > +	u32 tIR_min;
> > +	u32 tITC_max;
> > +	u32 tRC_min;
> > +	u32 tREA_max;
> > +	u32 tREH_min;
> > +	u32 tRHOH_min;
> > +	u32 tRHW_min;
> > +	u32 tRHZ_max;
> > +	u32 tRLOH_min;
> > +	u32 tRP_min;
> > +	u32 tRR_min;
> > +	u64 tRST_max;
> > +	u32 tWB_max;
> > +	u32 tWC_min;
> > +	u32 tWH_min;
> > +	u32 tWHR_min;
> > +	u32 tWP_min;
> > +	u32 tWW_min;
> > +};
> > +
> > +enum nand_data_interface_type {
> > +	NAND_SDR_IFACE,
> > +};
> > +
> 
> I know this is my code, and I'm the one to blame here, but can you
> document the nand_data_interface fields (kerneldoc format)?

Sure, can do. Do you know what tCEH is? This one is not documented in
the ONFI spec.

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH 2/7] mtd: nand: Introduce nand_data_interface
  2016-09-06 13:34       ` Sascha Hauer
@ 2016-09-06 13:46         ` Boris Brezillon
  -1 siblings, 0 replies; 52+ messages in thread
From: Boris Brezillon @ 2016-09-06 13:46 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-mtd, kernel, linux-arm-kernel

On Tue, 6 Sep 2016 15:34:44 +0200
Sascha Hauer <s.hauer@pengutronix.de> wrote:

> On Tue, Sep 06, 2016 at 01:21:18PM +0200, Boris Brezillon wrote:
> > On Tue,  6 Sep 2016 12:39:10 +0200
> > Sascha Hauer <s.hauer@pengutronix.de> wrote:
> >   
> > > Currently we have no data structure to fully describe a NAND timing.
> > > We only have struct nand_sdr_timings for NAND timings in SDR mode,
> > > but nothing for DDR mode and also no container to store both types
> > > of timing.
> > > This patch adds struct nand_data_interface which stores the timing
> > > type and a union of different timings. This can be used to pass to
> > > drivers in order to configure the timing.
> > > 
> > > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > > ---
> > >  include/linux/mtd/nand.h | 109 ++++++++++++++++++++++++++---------------------
> > >  1 file changed, 60 insertions(+), 49 deletions(-)
> > > 
> > > diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
> > > index 9af9575..19c73ef 100644
> > > --- a/include/linux/mtd/nand.h
> > > +++ b/include/linux/mtd/nand.h
> > > @@ -568,6 +568,66 @@ struct nand_buffers {
> > >  	uint8_t *databuf;
> > >  };
> > >  
> > > +/*
> > > + * struct nand_sdr_timings - SDR NAND chip timings
> > > + *
> > > + * This struct defines the timing requirements of a SDR NAND chip.
> > > + * These information can be found in every NAND datasheets and the timings
> > > + * meaning are described in the ONFI specifications:
> > > + * www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf (chapter 4.15 Timing
> > > + * Parameters)
> > > + *
> > > + * All these timings are expressed in picoseconds.
> > > + */
> > > +
> > > +struct nand_sdr_timings {
> > > +	u32 tALH_min;
> > > +	u32 tADL_min;
> > > +	u32 tALS_min;
> > > +	u32 tAR_min;
> > > +	u32 tCEA_max;
> > > +	u32 tCEH_min;
> > > +	u32 tCH_min;
> > > +	u32 tCHZ_max;
> > > +	u32 tCLH_min;
> > > +	u32 tCLR_min;
> > > +	u32 tCLS_min;
> > > +	u32 tCOH_min;
> > > +	u32 tCS_min;
> > > +	u32 tDH_min;
> > > +	u32 tDS_min;
> > > +	u32 tFEAT_max;
> > > +	u32 tIR_min;
> > > +	u32 tITC_max;
> > > +	u32 tRC_min;
> > > +	u32 tREA_max;
> > > +	u32 tREH_min;
> > > +	u32 tRHOH_min;
> > > +	u32 tRHW_min;
> > > +	u32 tRHZ_max;
> > > +	u32 tRLOH_min;
> > > +	u32 tRP_min;
> > > +	u32 tRR_min;
> > > +	u64 tRST_max;
> > > +	u32 tWB_max;
> > > +	u32 tWC_min;
> > > +	u32 tWH_min;
> > > +	u32 tWHR_min;
> > > +	u32 tWP_min;
> > > +	u32 tWW_min;
> > > +};
> > > +
> > > +enum nand_data_interface_type {
> > > +	NAND_SDR_IFACE,
> > > +};
> > > +  
> > 
> > I know this is my code, and I'm the one to blame here, but can you
> > document the nand_data_interface fields (kerneldoc format)?  
> 
> Sure, can do. Do you know what tCEH is? This one is not documented in
> the ONFI spec.

Maybe you're not looking at the latest ONFI spec ;). Anyway, I was not
asking you to document the nand_sdr_timings fields (pointing to the
spec should be enough), just the nand_data_interface ones.

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

* [PATCH 2/7] mtd: nand: Introduce nand_data_interface
@ 2016-09-06 13:46         ` Boris Brezillon
  0 siblings, 0 replies; 52+ messages in thread
From: Boris Brezillon @ 2016-09-06 13:46 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 6 Sep 2016 15:34:44 +0200
Sascha Hauer <s.hauer@pengutronix.de> wrote:

> On Tue, Sep 06, 2016 at 01:21:18PM +0200, Boris Brezillon wrote:
> > On Tue,  6 Sep 2016 12:39:10 +0200
> > Sascha Hauer <s.hauer@pengutronix.de> wrote:
> >   
> > > Currently we have no data structure to fully describe a NAND timing.
> > > We only have struct nand_sdr_timings for NAND timings in SDR mode,
> > > but nothing for DDR mode and also no container to store both types
> > > of timing.
> > > This patch adds struct nand_data_interface which stores the timing
> > > type and a union of different timings. This can be used to pass to
> > > drivers in order to configure the timing.
> > > 
> > > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > > ---
> > >  include/linux/mtd/nand.h | 109 ++++++++++++++++++++++++++---------------------
> > >  1 file changed, 60 insertions(+), 49 deletions(-)
> > > 
> > > diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
> > > index 9af9575..19c73ef 100644
> > > --- a/include/linux/mtd/nand.h
> > > +++ b/include/linux/mtd/nand.h
> > > @@ -568,6 +568,66 @@ struct nand_buffers {
> > >  	uint8_t *databuf;
> > >  };
> > >  
> > > +/*
> > > + * struct nand_sdr_timings - SDR NAND chip timings
> > > + *
> > > + * This struct defines the timing requirements of a SDR NAND chip.
> > > + * These information can be found in every NAND datasheets and the timings
> > > + * meaning are described in the ONFI specifications:
> > > + * www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf (chapter 4.15 Timing
> > > + * Parameters)
> > > + *
> > > + * All these timings are expressed in picoseconds.
> > > + */
> > > +
> > > +struct nand_sdr_timings {
> > > +	u32 tALH_min;
> > > +	u32 tADL_min;
> > > +	u32 tALS_min;
> > > +	u32 tAR_min;
> > > +	u32 tCEA_max;
> > > +	u32 tCEH_min;
> > > +	u32 tCH_min;
> > > +	u32 tCHZ_max;
> > > +	u32 tCLH_min;
> > > +	u32 tCLR_min;
> > > +	u32 tCLS_min;
> > > +	u32 tCOH_min;
> > > +	u32 tCS_min;
> > > +	u32 tDH_min;
> > > +	u32 tDS_min;
> > > +	u32 tFEAT_max;
> > > +	u32 tIR_min;
> > > +	u32 tITC_max;
> > > +	u32 tRC_min;
> > > +	u32 tREA_max;
> > > +	u32 tREH_min;
> > > +	u32 tRHOH_min;
> > > +	u32 tRHW_min;
> > > +	u32 tRHZ_max;
> > > +	u32 tRLOH_min;
> > > +	u32 tRP_min;
> > > +	u32 tRR_min;
> > > +	u64 tRST_max;
> > > +	u32 tWB_max;
> > > +	u32 tWC_min;
> > > +	u32 tWH_min;
> > > +	u32 tWHR_min;
> > > +	u32 tWP_min;
> > > +	u32 tWW_min;
> > > +};
> > > +
> > > +enum nand_data_interface_type {
> > > +	NAND_SDR_IFACE,
> > > +};
> > > +  
> > 
> > I know this is my code, and I'm the one to blame here, but can you
> > document the nand_data_interface fields (kerneldoc format)?  
> 
> Sure, can do. Do you know what tCEH is? This one is not documented in
> the ONFI spec.

Maybe you're not looking at the latest ONFI spec ;). Anyway, I was not
asking you to document the nand_sdr_timings fields (pointing to the
spec should be enough), just the nand_data_interface ones.

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

* Re: [PATCH 4/7] mtd: nand: automate NAND timings selection
  2016-09-06 11:58     ` Boris Brezillon
@ 2016-09-06 14:08       ` Sascha Hauer
  -1 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 14:08 UTC (permalink / raw)
  To: Boris Brezillon; +Cc: linux-mtd, kernel, linux-arm-kernel

On Tue, Sep 06, 2016 at 01:58:07PM +0200, Boris Brezillon wrote:
> On Tue,  6 Sep 2016 12:39:12 +0200
> Sascha Hauer <s.hauer@pengutronix.de> wrote:
> 
> > From: Boris Brezillon <boris.brezillon@free-electrons.com>
> > 
> > The NAND framework provides several helpers to query timing modes supported
> > by a NAND chip, but this implies that all NAND controller drivers have
> > to implement the same timings selection dance. Also currently NAND
> > devices can be resetted at arbitrary places which also resets the timing
> > for ONFI chips to timing mode 0.
> > 
> > Provide a common logic to select the best timings based on ONFI or
> > ->onfi_timing_mode_default information. Hook this into nand_reset()  
> > to make sure the new timing is applied each time during a reset.
> > 
> > NAND controller willing to support timings adjustment should just
> > implement the ->setup_data_interface() method.
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > ---
> >  drivers/mtd/nand/nand_base.c | 112 +++++++++++++++++++++++++++++++++++++++++++
> >  include/linux/mtd/nand.h     |  14 ++++--
> >  2 files changed, 122 insertions(+), 4 deletions(-)
> > 
> > diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> > index 20151fc..37852e9 100644
> > --- a/drivers/mtd/nand/nand_base.c
> > +++ b/drivers/mtd/nand/nand_base.c
> > @@ -955,8 +955,63 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
> >   */
> >  int nand_reset(struct mtd_info *mtd)
> >  {
> > +	struct nand_chip *chip = mtd_to_nand(mtd);
> > +	int ret;
> > +
> > +	if (chip->setup_data_interface) {
> > +		struct nand_data_interface conf = {
> > +			.type = NAND_SDR_IFACE,
> > +			.timings.sdr = *onfi_async_timing_mode_to_sdr_timings(0),
> > +		};
> > +
> 
> Let's try to avoid putting such a huge structure on the stack.

I could do that by doing

	const struct nand_data_interface *conf = onfi_async_timing_mode_to_data_interface(0);

But you just discussed this function away ;)

I need two different timings, first the ONFI timing mode 0 and then the
real timing. I wanted to avoid re-initializing the same timing struct
instance multiple times (twice in nand_reset() and two times again for
each additional chip in an array).

> 
> > +		/*
> > +		 * The ONFI specification says:
> > +		 * "
> > +		 * To transition from NV-DDR or NV-DDR2 to the SDR data
> > +		 * interface, the host shall use the Reset (FFh) command
> > +		 * using SDR timing mode 0. A device in any timing mode is
> > +		 * required to recognize Reset (FFh) command issued in SDR
> > +		 * timing mode 0.
> > +		 * "
> > +		 *
> > +		 * Configure the data interface in SDR mode and set the
> > +		 * timings to timing mode 0.
> > +		 */
> > +
> > +		ret = chip->setup_data_interface(mtd, &conf, false);
> > +		if (ret) {
> > +			pr_err("Failed to configure data interface to SDR timing mode 0\n");
> > +			return ret;
> > +		}
> > +	}
> 
> Can you put this code in a separate function? I'd like to keep the
> nand_reset() function as small as possible.
> 
> How about nand_reset_data_interface()?

Yes, can do. In that case I would move the test if setting the data
interface is supported to that function aswell. Are you okay with that?


> 
> > +
> >  	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
> >  
> > +	/*
> > +	 * Setup the NAND interface (interface type + timings).
> > +	 */
> > +	if (chip->data_iface) {
> > +		uint8_t tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = {
> > +			chip->onfi_timing_mode_default,
> > +		};
> > +
> > +		/*
> > +		 * Ensure the timing mode has be changed on the chip side
> 					      ^ been
> > +		 * before changing timings on the controller side.
> > +		 */
> > +		if (chip->onfi_version) {
> > +			ret = chip->onfi_set_features(mtd, chip,
> > +					ONFI_FEATURE_ADDR_TIMING_MODE,
> > +					tmode_param);
> > +			if (ret)
> > +				return ret;
> > +		}
> > +
> > +		ret = chip->setup_data_interface(mtd, chip->data_iface, false);
> > +		if (ret)
> > +			return ret;
> > +	}
> > +
> 
> Ditto: nand_setup_data_interface()?
> 
> >  	return 0;
> >  }
> >  
> > @@ -3335,6 +3390,54 @@ static void nand_onfi_detect_micron(struct nand_chip *chip,
> >  	chip->setup_read_retry = nand_setup_read_retry_micron;
> >  }
> >  
> > +/**
> > + * nand_find_data_interface - Find the best data interface and timings
> > + * @mtd: MTD device structure
> > + *
> > + * Try to find the best data interface and NAND timings supported by the
> > + * chip and the driver.
> > + * First tries to retrieve supported timing modes from ONFI information,
> > + * and if the NAND chip does not support ONFI, relies on the
> > + * ->onfi_timing_mode_default specified in the nand_ids table.
> > + *
> > + * Returns 0 for success or negative error code otherwise.
> > + */
> > +static int nand_find_data_interface(struct mtd_info *mtd)
> 
> How about nand_init_data_interface() or nand_init_data_iface_config()?
> 
> > +{
> > +	struct nand_chip *chip = mtd_to_nand(mtd);
> > +	int modes, mode, ret;
> > +	const struct nand_data_interface *conf;
> > +
> > +	/*
> > +	 * First try to identify the best timings from ONFI parameters and
> > +	 * if the NAND does not support ONFI, fallback to the default ONFI
> > +	 * timing mode.
> > +	 */
> > +	modes = onfi_get_async_timing_mode(chip);
> > +	if (modes == ONFI_TIMING_MODE_UNKNOWN)
> > +		modes = GENMASK(chip->onfi_timing_mode_default, 0);
> > +
> > +	ret = -EINVAL;
> > +	for (mode = fls(modes) - 1; mode >= 0; mode--) {
> > +		conf = onfi_async_timing_mode_to_data_interface(mode);
> 
> I'd still prefer to have conf allocated at the beginning of the
> function and timings copied from
> onfi_async_timing_mode_to_sdr_timings(mode), but maybe you can convince
> me otherwise.

Let me ask the other way round: If we need struct nand_data_interface to
fully describe a timing, why don't we keep an array of these in the
kernel? Having an array of struct nand_sdr_timings() means we always
have to copy it to a bigger struct to make it usable.

> > @@ -759,6 +759,10 @@ struct nand_chip {
> >  	int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
> >  			int feature_addr, uint8_t *subfeature_para);
> >  	int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode);
> > +	int (*setup_data_interface)(struct mtd_info *mtd,
> > +				    const struct nand_data_interface *conf,
> > +				    bool check_only);
> > +
> >  
> >  	int chip_delay;
> >  	unsigned int options;
> > @@ -788,6 +792,8 @@ struct nand_chip {
> >  		struct nand_jedec_params jedec_params;
> >  	};
> >  
> > +	const struct nand_data_interface *data_iface;
> > +
> 
> How about making this field non-const so that you only allocate it once
> and modify it when you switch from one mode to another.

As said above, I need two different timings. If we modify this
nand_data_interface instance twice during reset there's not much point
in storing it in struct nand_chip at all. That was one variant I tried:
Always calculcate the timing from the supported ONFI modes when we need
it in nand_reset(). I stepped away from this variant because of the
overhead.

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* [PATCH 4/7] mtd: nand: automate NAND timings selection
@ 2016-09-06 14:08       ` Sascha Hauer
  0 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 14:08 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Sep 06, 2016 at 01:58:07PM +0200, Boris Brezillon wrote:
> On Tue,  6 Sep 2016 12:39:12 +0200
> Sascha Hauer <s.hauer@pengutronix.de> wrote:
> 
> > From: Boris Brezillon <boris.brezillon@free-electrons.com>
> > 
> > The NAND framework provides several helpers to query timing modes supported
> > by a NAND chip, but this implies that all NAND controller drivers have
> > to implement the same timings selection dance. Also currently NAND
> > devices can be resetted at arbitrary places which also resets the timing
> > for ONFI chips to timing mode 0.
> > 
> > Provide a common logic to select the best timings based on ONFI or
> > ->onfi_timing_mode_default information. Hook this into nand_reset()  
> > to make sure the new timing is applied each time during a reset.
> > 
> > NAND controller willing to support timings adjustment should just
> > implement the ->setup_data_interface() method.
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > ---
> >  drivers/mtd/nand/nand_base.c | 112 +++++++++++++++++++++++++++++++++++++++++++
> >  include/linux/mtd/nand.h     |  14 ++++--
> >  2 files changed, 122 insertions(+), 4 deletions(-)
> > 
> > diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> > index 20151fc..37852e9 100644
> > --- a/drivers/mtd/nand/nand_base.c
> > +++ b/drivers/mtd/nand/nand_base.c
> > @@ -955,8 +955,63 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
> >   */
> >  int nand_reset(struct mtd_info *mtd)
> >  {
> > +	struct nand_chip *chip = mtd_to_nand(mtd);
> > +	int ret;
> > +
> > +	if (chip->setup_data_interface) {
> > +		struct nand_data_interface conf = {
> > +			.type = NAND_SDR_IFACE,
> > +			.timings.sdr = *onfi_async_timing_mode_to_sdr_timings(0),
> > +		};
> > +
> 
> Let's try to avoid putting such a huge structure on the stack.

I could do that by doing

	const struct nand_data_interface *conf = onfi_async_timing_mode_to_data_interface(0);

But you just discussed this function away ;)

I need two different timings, first the ONFI timing mode 0 and then the
real timing. I wanted to avoid re-initializing the same timing struct
instance multiple times (twice in nand_reset() and two times again for
each additional chip in an array).

> 
> > +		/*
> > +		 * The ONFI specification says:
> > +		 * "
> > +		 * To transition from NV-DDR or NV-DDR2 to the SDR data
> > +		 * interface, the host shall use the Reset (FFh) command
> > +		 * using SDR timing mode 0. A device in any timing mode is
> > +		 * required to recognize Reset (FFh) command issued in SDR
> > +		 * timing mode 0.
> > +		 * "
> > +		 *
> > +		 * Configure the data interface in SDR mode and set the
> > +		 * timings to timing mode 0.
> > +		 */
> > +
> > +		ret = chip->setup_data_interface(mtd, &conf, false);
> > +		if (ret) {
> > +			pr_err("Failed to configure data interface to SDR timing mode 0\n");
> > +			return ret;
> > +		}
> > +	}
> 
> Can you put this code in a separate function? I'd like to keep the
> nand_reset() function as small as possible.
> 
> How about nand_reset_data_interface()?

Yes, can do. In that case I would move the test if setting the data
interface is supported to that function aswell. Are you okay with that?


> 
> > +
> >  	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
> >  
> > +	/*
> > +	 * Setup the NAND interface (interface type + timings).
> > +	 */
> > +	if (chip->data_iface) {
> > +		uint8_t tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = {
> > +			chip->onfi_timing_mode_default,
> > +		};
> > +
> > +		/*
> > +		 * Ensure the timing mode has be changed on the chip side
> 					      ^ been
> > +		 * before changing timings on the controller side.
> > +		 */
> > +		if (chip->onfi_version) {
> > +			ret = chip->onfi_set_features(mtd, chip,
> > +					ONFI_FEATURE_ADDR_TIMING_MODE,
> > +					tmode_param);
> > +			if (ret)
> > +				return ret;
> > +		}
> > +
> > +		ret = chip->setup_data_interface(mtd, chip->data_iface, false);
> > +		if (ret)
> > +			return ret;
> > +	}
> > +
> 
> Ditto: nand_setup_data_interface()?
> 
> >  	return 0;
> >  }
> >  
> > @@ -3335,6 +3390,54 @@ static void nand_onfi_detect_micron(struct nand_chip *chip,
> >  	chip->setup_read_retry = nand_setup_read_retry_micron;
> >  }
> >  
> > +/**
> > + * nand_find_data_interface - Find the best data interface and timings
> > + * @mtd: MTD device structure
> > + *
> > + * Try to find the best data interface and NAND timings supported by the
> > + * chip and the driver.
> > + * First tries to retrieve supported timing modes from ONFI information,
> > + * and if the NAND chip does not support ONFI, relies on the
> > + * ->onfi_timing_mode_default specified in the nand_ids table.
> > + *
> > + * Returns 0 for success or negative error code otherwise.
> > + */
> > +static int nand_find_data_interface(struct mtd_info *mtd)
> 
> How about nand_init_data_interface() or nand_init_data_iface_config()?
> 
> > +{
> > +	struct nand_chip *chip = mtd_to_nand(mtd);
> > +	int modes, mode, ret;
> > +	const struct nand_data_interface *conf;
> > +
> > +	/*
> > +	 * First try to identify the best timings from ONFI parameters and
> > +	 * if the NAND does not support ONFI, fallback to the default ONFI
> > +	 * timing mode.
> > +	 */
> > +	modes = onfi_get_async_timing_mode(chip);
> > +	if (modes == ONFI_TIMING_MODE_UNKNOWN)
> > +		modes = GENMASK(chip->onfi_timing_mode_default, 0);
> > +
> > +	ret = -EINVAL;
> > +	for (mode = fls(modes) - 1; mode >= 0; mode--) {
> > +		conf = onfi_async_timing_mode_to_data_interface(mode);
> 
> I'd still prefer to have conf allocated at the beginning of the
> function and timings copied from
> onfi_async_timing_mode_to_sdr_timings(mode), but maybe you can convince
> me otherwise.

Let me ask the other way round: If we need struct nand_data_interface to
fully describe a timing, why don't we keep an array of these in the
kernel? Having an array of struct nand_sdr_timings() means we always
have to copy it to a bigger struct to make it usable.

> > @@ -759,6 +759,10 @@ struct nand_chip {
> >  	int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
> >  			int feature_addr, uint8_t *subfeature_para);
> >  	int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode);
> > +	int (*setup_data_interface)(struct mtd_info *mtd,
> > +				    const struct nand_data_interface *conf,
> > +				    bool check_only);
> > +
> >  
> >  	int chip_delay;
> >  	unsigned int options;
> > @@ -788,6 +792,8 @@ struct nand_chip {
> >  		struct nand_jedec_params jedec_params;
> >  	};
> >  
> > +	const struct nand_data_interface *data_iface;
> > +
> 
> How about making this field non-const so that you only allocate it once
> and modify it when you switch from one mode to another.

As said above, I need two different timings. If we modify this
nand_data_interface instance twice during reset there's not much point
in storing it in struct nand_chip at all. That was one variant I tried:
Always calculcate the timing from the supported ONFI modes when we need
it in nand_reset(). I stepped away from this variant because of the
overhead.

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH 2/7] mtd: nand: Introduce nand_data_interface
  2016-09-06 13:46         ` Boris Brezillon
@ 2016-09-06 14:09           ` Sascha Hauer
  -1 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 14:09 UTC (permalink / raw)
  To: Boris Brezillon; +Cc: linux-mtd, kernel, linux-arm-kernel

On Tue, Sep 06, 2016 at 03:46:22PM +0200, Boris Brezillon wrote:
> On Tue, 6 Sep 2016 15:34:44 +0200
> Sascha Hauer <s.hauer@pengutronix.de> wrote:
> 
> > On Tue, Sep 06, 2016 at 01:21:18PM +0200, Boris Brezillon wrote:
> > > On Tue,  6 Sep 2016 12:39:10 +0200
> > > Sascha Hauer <s.hauer@pengutronix.de> wrote:
> > >   
> > > > Currently we have no data structure to fully describe a NAND timing.
> > > > We only have struct nand_sdr_timings for NAND timings in SDR mode,
> > > > but nothing for DDR mode and also no container to store both types
> > > > of timing.
> > > > This patch adds struct nand_data_interface which stores the timing
> > > > type and a union of different timings. This can be used to pass to
> > > > drivers in order to configure the timing.
> > > > 
> > > > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > > > ---
> > > >  include/linux/mtd/nand.h | 109 ++++++++++++++++++++++++++---------------------
> > > >  1 file changed, 60 insertions(+), 49 deletions(-)
> > > > 
> > > > diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
> > > > index 9af9575..19c73ef 100644
> > > > --- a/include/linux/mtd/nand.h
> > > > +++ b/include/linux/mtd/nand.h
> > > > @@ -568,6 +568,66 @@ struct nand_buffers {
> > > >  	uint8_t *databuf;
> > > >  };
> > > >  
> > > > +/*
> > > > + * struct nand_sdr_timings - SDR NAND chip timings
> > > > + *
> > > > + * This struct defines the timing requirements of a SDR NAND chip.
> > > > + * These information can be found in every NAND datasheets and the timings
> > > > + * meaning are described in the ONFI specifications:
> > > > + * www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf (chapter 4.15 Timing
> > > > + * Parameters)
> > > > + *
> > > > + * All these timings are expressed in picoseconds.
> > > > + */
> > > > +
> > > > +struct nand_sdr_timings {
> > > > +	u32 tALH_min;
> > > > +	u32 tADL_min;
> > > > +	u32 tALS_min;
> > > > +	u32 tAR_min;
> > > > +	u32 tCEA_max;
> > > > +	u32 tCEH_min;
> > > > +	u32 tCH_min;
> > > > +	u32 tCHZ_max;
> > > > +	u32 tCLH_min;
> > > > +	u32 tCLR_min;
> > > > +	u32 tCLS_min;
> > > > +	u32 tCOH_min;
> > > > +	u32 tCS_min;
> > > > +	u32 tDH_min;
> > > > +	u32 tDS_min;
> > > > +	u32 tFEAT_max;
> > > > +	u32 tIR_min;
> > > > +	u32 tITC_max;
> > > > +	u32 tRC_min;
> > > > +	u32 tREA_max;
> > > > +	u32 tREH_min;
> > > > +	u32 tRHOH_min;
> > > > +	u32 tRHW_min;
> > > > +	u32 tRHZ_max;
> > > > +	u32 tRLOH_min;
> > > > +	u32 tRP_min;
> > > > +	u32 tRR_min;
> > > > +	u64 tRST_max;
> > > > +	u32 tWB_max;
> > > > +	u32 tWC_min;
> > > > +	u32 tWH_min;
> > > > +	u32 tWHR_min;
> > > > +	u32 tWP_min;
> > > > +	u32 tWW_min;
> > > > +};
> > > > +
> > > > +enum nand_data_interface_type {
> > > > +	NAND_SDR_IFACE,
> > > > +};
> > > > +  
> > > 
> > > I know this is my code, and I'm the one to blame here, but can you
> > > document the nand_data_interface fields (kerneldoc format)?  
> > 
> > Sure, can do. Do you know what tCEH is? This one is not documented in
> > the ONFI spec.
> 
> Maybe you're not looking at the latest ONFI spec ;). Anyway, I was not
> asking you to document the nand_sdr_timings fields (pointing to the
> spec should be enough), just the nand_data_interface ones.

I realized that when writing the last mail, but I already added it ;)

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* [PATCH 2/7] mtd: nand: Introduce nand_data_interface
@ 2016-09-06 14:09           ` Sascha Hauer
  0 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 14:09 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Sep 06, 2016 at 03:46:22PM +0200, Boris Brezillon wrote:
> On Tue, 6 Sep 2016 15:34:44 +0200
> Sascha Hauer <s.hauer@pengutronix.de> wrote:
> 
> > On Tue, Sep 06, 2016 at 01:21:18PM +0200, Boris Brezillon wrote:
> > > On Tue,  6 Sep 2016 12:39:10 +0200
> > > Sascha Hauer <s.hauer@pengutronix.de> wrote:
> > >   
> > > > Currently we have no data structure to fully describe a NAND timing.
> > > > We only have struct nand_sdr_timings for NAND timings in SDR mode,
> > > > but nothing for DDR mode and also no container to store both types
> > > > of timing.
> > > > This patch adds struct nand_data_interface which stores the timing
> > > > type and a union of different timings. This can be used to pass to
> > > > drivers in order to configure the timing.
> > > > 
> > > > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > > > ---
> > > >  include/linux/mtd/nand.h | 109 ++++++++++++++++++++++++++---------------------
> > > >  1 file changed, 60 insertions(+), 49 deletions(-)
> > > > 
> > > > diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
> > > > index 9af9575..19c73ef 100644
> > > > --- a/include/linux/mtd/nand.h
> > > > +++ b/include/linux/mtd/nand.h
> > > > @@ -568,6 +568,66 @@ struct nand_buffers {
> > > >  	uint8_t *databuf;
> > > >  };
> > > >  
> > > > +/*
> > > > + * struct nand_sdr_timings - SDR NAND chip timings
> > > > + *
> > > > + * This struct defines the timing requirements of a SDR NAND chip.
> > > > + * These information can be found in every NAND datasheets and the timings
> > > > + * meaning are described in the ONFI specifications:
> > > > + * www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf (chapter 4.15 Timing
> > > > + * Parameters)
> > > > + *
> > > > + * All these timings are expressed in picoseconds.
> > > > + */
> > > > +
> > > > +struct nand_sdr_timings {
> > > > +	u32 tALH_min;
> > > > +	u32 tADL_min;
> > > > +	u32 tALS_min;
> > > > +	u32 tAR_min;
> > > > +	u32 tCEA_max;
> > > > +	u32 tCEH_min;
> > > > +	u32 tCH_min;
> > > > +	u32 tCHZ_max;
> > > > +	u32 tCLH_min;
> > > > +	u32 tCLR_min;
> > > > +	u32 tCLS_min;
> > > > +	u32 tCOH_min;
> > > > +	u32 tCS_min;
> > > > +	u32 tDH_min;
> > > > +	u32 tDS_min;
> > > > +	u32 tFEAT_max;
> > > > +	u32 tIR_min;
> > > > +	u32 tITC_max;
> > > > +	u32 tRC_min;
> > > > +	u32 tREA_max;
> > > > +	u32 tREH_min;
> > > > +	u32 tRHOH_min;
> > > > +	u32 tRHW_min;
> > > > +	u32 tRHZ_max;
> > > > +	u32 tRLOH_min;
> > > > +	u32 tRP_min;
> > > > +	u32 tRR_min;
> > > > +	u64 tRST_max;
> > > > +	u32 tWB_max;
> > > > +	u32 tWC_min;
> > > > +	u32 tWH_min;
> > > > +	u32 tWHR_min;
> > > > +	u32 tWP_min;
> > > > +	u32 tWW_min;
> > > > +};
> > > > +
> > > > +enum nand_data_interface_type {
> > > > +	NAND_SDR_IFACE,
> > > > +};
> > > > +  
> > > 
> > > I know this is my code, and I'm the one to blame here, but can you
> > > document the nand_data_interface fields (kerneldoc format)?  
> > 
> > Sure, can do. Do you know what tCEH is? This one is not documented in
> > the ONFI spec.
> 
> Maybe you're not looking at the latest ONFI spec ;). Anyway, I was not
> asking you to document the nand_sdr_timings fields (pointing to the
> spec should be enough), just the nand_data_interface ones.

I realized that when writing the last mail, but I already added it ;)

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH 4/7] mtd: nand: automate NAND timings selection
  2016-09-06 14:08       ` Sascha Hauer
@ 2016-09-06 14:50         ` Boris Brezillon
  -1 siblings, 0 replies; 52+ messages in thread
From: Boris Brezillon @ 2016-09-06 14:50 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-mtd, kernel, linux-arm-kernel

On Tue, 6 Sep 2016 16:08:17 +0200
Sascha Hauer <s.hauer@pengutronix.de> wrote:

> On Tue, Sep 06, 2016 at 01:58:07PM +0200, Boris Brezillon wrote:
> > On Tue,  6 Sep 2016 12:39:12 +0200
> > Sascha Hauer <s.hauer@pengutronix.de> wrote:
> >   
> > > From: Boris Brezillon <boris.brezillon@free-electrons.com>
> > > 
> > > The NAND framework provides several helpers to query timing modes supported
> > > by a NAND chip, but this implies that all NAND controller drivers have
> > > to implement the same timings selection dance. Also currently NAND
> > > devices can be resetted at arbitrary places which also resets the timing
> > > for ONFI chips to timing mode 0.
> > > 
> > > Provide a common logic to select the best timings based on ONFI or  
> > > ->onfi_timing_mode_default information. Hook this into nand_reset()    
> > > to make sure the new timing is applied each time during a reset.
> > > 
> > > NAND controller willing to support timings adjustment should just
> > > implement the ->setup_data_interface() method.
> > > 
> > > Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> > > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > > ---
> > >  drivers/mtd/nand/nand_base.c | 112 +++++++++++++++++++++++++++++++++++++++++++
> > >  include/linux/mtd/nand.h     |  14 ++++--
> > >  2 files changed, 122 insertions(+), 4 deletions(-)
> > > 
> > > diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> > > index 20151fc..37852e9 100644
> > > --- a/drivers/mtd/nand/nand_base.c
> > > +++ b/drivers/mtd/nand/nand_base.c
> > > @@ -955,8 +955,63 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
> > >   */
> > >  int nand_reset(struct mtd_info *mtd)
> > >  {
> > > +	struct nand_chip *chip = mtd_to_nand(mtd);
> > > +	int ret;
> > > +
> > > +	if (chip->setup_data_interface) {
> > > +		struct nand_data_interface conf = {
> > > +			.type = NAND_SDR_IFACE,
> > > +			.timings.sdr = *onfi_async_timing_mode_to_sdr_timings(0),
> > > +		};
> > > +  
> > 
> > Let's try to avoid putting such a huge structure on the stack.  
> 
> I could do that by doing
> 
> 	const struct nand_data_interface *conf = onfi_async_timing_mode_to_data_interface(0);
> 
> But you just discussed this function away ;)
> 
> I need two different timings, first the ONFI timing mode 0 and then the
> real timing. I wanted to avoid re-initializing the same timing struct
> instance multiple times (twice in nand_reset() and two times again for
> each additional chip in an array).
> 
> >   
> > > +		/*
> > > +		 * The ONFI specification says:
> > > +		 * "
> > > +		 * To transition from NV-DDR or NV-DDR2 to the SDR data
> > > +		 * interface, the host shall use the Reset (FFh) command
> > > +		 * using SDR timing mode 0. A device in any timing mode is
> > > +		 * required to recognize Reset (FFh) command issued in SDR
> > > +		 * timing mode 0.
> > > +		 * "
> > > +		 *
> > > +		 * Configure the data interface in SDR mode and set the
> > > +		 * timings to timing mode 0.
> > > +		 */
> > > +
> > > +		ret = chip->setup_data_interface(mtd, &conf, false);
> > > +		if (ret) {
> > > +			pr_err("Failed to configure data interface to SDR timing mode 0\n");
> > > +			return ret;
> > > +		}
> > > +	}  
> > 
> > Can you put this code in a separate function? I'd like to keep the
> > nand_reset() function as small as possible.
> > 
> > How about nand_reset_data_interface()?  
> 
> Yes, can do. In that case I would move the test if setting the data
> interface is supported to that function aswell. Are you okay with that?

Sure.

> 
> 
> >   
> > > +
> > >  	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
> > >  
> > > +	/*
> > > +	 * Setup the NAND interface (interface type + timings).
> > > +	 */
> > > +	if (chip->data_iface) {
> > > +		uint8_t tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = {
> > > +			chip->onfi_timing_mode_default,
> > > +		};
> > > +
> > > +		/*
> > > +		 * Ensure the timing mode has be changed on the chip side  
> > 					      ^ been  
> > > +		 * before changing timings on the controller side.
> > > +		 */
> > > +		if (chip->onfi_version) {
> > > +			ret = chip->onfi_set_features(mtd, chip,
> > > +					ONFI_FEATURE_ADDR_TIMING_MODE,
> > > +					tmode_param);
> > > +			if (ret)
> > > +				return ret;
> > > +		}
> > > +
> > > +		ret = chip->setup_data_interface(mtd, chip->data_iface, false);
> > > +		if (ret)
> > > +			return ret;
> > > +	}
> > > +  
> > 
> > Ditto: nand_setup_data_interface()?
> >   
> > >  	return 0;
> > >  }
> > >  
> > > @@ -3335,6 +3390,54 @@ static void nand_onfi_detect_micron(struct nand_chip *chip,
> > >  	chip->setup_read_retry = nand_setup_read_retry_micron;
> > >  }
> > >  
> > > +/**
> > > + * nand_find_data_interface - Find the best data interface and timings
> > > + * @mtd: MTD device structure
> > > + *
> > > + * Try to find the best data interface and NAND timings supported by the
> > > + * chip and the driver.
> > > + * First tries to retrieve supported timing modes from ONFI information,
> > > + * and if the NAND chip does not support ONFI, relies on the
> > > + * ->onfi_timing_mode_default specified in the nand_ids table.
> > > + *
> > > + * Returns 0 for success or negative error code otherwise.
> > > + */
> > > +static int nand_find_data_interface(struct mtd_info *mtd)  
> > 
> > How about nand_init_data_interface() or nand_init_data_iface_config()?
> >   
> > > +{
> > > +	struct nand_chip *chip = mtd_to_nand(mtd);
> > > +	int modes, mode, ret;
> > > +	const struct nand_data_interface *conf;
> > > +
> > > +	/*
> > > +	 * First try to identify the best timings from ONFI parameters and
> > > +	 * if the NAND does not support ONFI, fallback to the default ONFI
> > > +	 * timing mode.
> > > +	 */
> > > +	modes = onfi_get_async_timing_mode(chip);
> > > +	if (modes == ONFI_TIMING_MODE_UNKNOWN)
> > > +		modes = GENMASK(chip->onfi_timing_mode_default, 0);
> > > +
> > > +	ret = -EINVAL;
> > > +	for (mode = fls(modes) - 1; mode >= 0; mode--) {
> > > +		conf = onfi_async_timing_mode_to_data_interface(mode);  
> > 
> > I'd still prefer to have conf allocated at the beginning of the
> > function and timings copied from
> > onfi_async_timing_mode_to_sdr_timings(mode), but maybe you can convince
> > me otherwise.  
> 
> Let me ask the other way round: If we need struct nand_data_interface to
> fully describe a timing, why don't we keep an array of these in the
> kernel? Having an array of struct nand_sdr_timings() means we always
> have to copy it to a bigger struct to make it usable.

Actually, the plan is to let vendor specific code tweak the timings if
needed.
Some NANDs that do not support ONFI have to pick timing mode 0 because
one of their timing is not matching the ONFI spec. I'd like to let
the door to fined-grained timing tweaking open, and this is only
possible if the chip has its own nand_data_interface object (not the
const one defined in nand_timings.c).

Also note that some timings are not statically defined (like tPROG),
and are extracted from another ONFI field, and I'd like to add them to
the nand_sdr_timings struct, which again, is only possible if the
nand_chip has its own nand_data_interface instance.

> 
> > > @@ -759,6 +759,10 @@ struct nand_chip {
> > >  	int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
> > >  			int feature_addr, uint8_t *subfeature_para);
> > >  	int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode);
> > > +	int (*setup_data_interface)(struct mtd_info *mtd,
> > > +				    const struct nand_data_interface *conf,
> > > +				    bool check_only);
> > > +
> > >  
> > >  	int chip_delay;
> > >  	unsigned int options;
> > > @@ -788,6 +792,8 @@ struct nand_chip {
> > >  		struct nand_jedec_params jedec_params;
> > >  	};
> > >  
> > > +	const struct nand_data_interface *data_iface;
> > > +  
> > 
> > How about making this field non-const so that you only allocate it once
> > and modify it when you switch from one mode to another.  
> 
> As said above, I need two different timings. If we modify this
> nand_data_interface instance twice during reset there's not much point
> in storing it in struct nand_chip at all. That was one variant I tried:
> Always calculcate the timing from the supported ONFI modes when we need
> it in nand_reset(). I stepped away from this variant because of the
> overhead.

Yes, your device will be configured twice (first mode 0, then the
highest supported timing mode), but that does not mean you need to have
2 instances of nand_data_interface.

->data_iface should always be assigned to the current data interface
config. If you reset the chip and go back to timing 0, then
chip->data_iface should be set to sdr mode timing zero, and once a
new timing mode is applied, it should be updated.

And yes, there's a small overhead (copying the nand_sdr_timings data
twice), but I'm pretty sure it's negligible compared to the whole NAND
chip init overhead.
And it's not like nand_reset() is called so regularly that it's useful
to optimize this kind of things.

> 
> Sascha
> 

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

* [PATCH 4/7] mtd: nand: automate NAND timings selection
@ 2016-09-06 14:50         ` Boris Brezillon
  0 siblings, 0 replies; 52+ messages in thread
From: Boris Brezillon @ 2016-09-06 14:50 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 6 Sep 2016 16:08:17 +0200
Sascha Hauer <s.hauer@pengutronix.de> wrote:

> On Tue, Sep 06, 2016 at 01:58:07PM +0200, Boris Brezillon wrote:
> > On Tue,  6 Sep 2016 12:39:12 +0200
> > Sascha Hauer <s.hauer@pengutronix.de> wrote:
> >   
> > > From: Boris Brezillon <boris.brezillon@free-electrons.com>
> > > 
> > > The NAND framework provides several helpers to query timing modes supported
> > > by a NAND chip, but this implies that all NAND controller drivers have
> > > to implement the same timings selection dance. Also currently NAND
> > > devices can be resetted at arbitrary places which also resets the timing
> > > for ONFI chips to timing mode 0.
> > > 
> > > Provide a common logic to select the best timings based on ONFI or  
> > > ->onfi_timing_mode_default information. Hook this into nand_reset()    
> > > to make sure the new timing is applied each time during a reset.
> > > 
> > > NAND controller willing to support timings adjustment should just
> > > implement the ->setup_data_interface() method.
> > > 
> > > Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> > > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > > ---
> > >  drivers/mtd/nand/nand_base.c | 112 +++++++++++++++++++++++++++++++++++++++++++
> > >  include/linux/mtd/nand.h     |  14 ++++--
> > >  2 files changed, 122 insertions(+), 4 deletions(-)
> > > 
> > > diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> > > index 20151fc..37852e9 100644
> > > --- a/drivers/mtd/nand/nand_base.c
> > > +++ b/drivers/mtd/nand/nand_base.c
> > > @@ -955,8 +955,63 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
> > >   */
> > >  int nand_reset(struct mtd_info *mtd)
> > >  {
> > > +	struct nand_chip *chip = mtd_to_nand(mtd);
> > > +	int ret;
> > > +
> > > +	if (chip->setup_data_interface) {
> > > +		struct nand_data_interface conf = {
> > > +			.type = NAND_SDR_IFACE,
> > > +			.timings.sdr = *onfi_async_timing_mode_to_sdr_timings(0),
> > > +		};
> > > +  
> > 
> > Let's try to avoid putting such a huge structure on the stack.  
> 
> I could do that by doing
> 
> 	const struct nand_data_interface *conf = onfi_async_timing_mode_to_data_interface(0);
> 
> But you just discussed this function away ;)
> 
> I need two different timings, first the ONFI timing mode 0 and then the
> real timing. I wanted to avoid re-initializing the same timing struct
> instance multiple times (twice in nand_reset() and two times again for
> each additional chip in an array).
> 
> >   
> > > +		/*
> > > +		 * The ONFI specification says:
> > > +		 * "
> > > +		 * To transition from NV-DDR or NV-DDR2 to the SDR data
> > > +		 * interface, the host shall use the Reset (FFh) command
> > > +		 * using SDR timing mode 0. A device in any timing mode is
> > > +		 * required to recognize Reset (FFh) command issued in SDR
> > > +		 * timing mode 0.
> > > +		 * "
> > > +		 *
> > > +		 * Configure the data interface in SDR mode and set the
> > > +		 * timings to timing mode 0.
> > > +		 */
> > > +
> > > +		ret = chip->setup_data_interface(mtd, &conf, false);
> > > +		if (ret) {
> > > +			pr_err("Failed to configure data interface to SDR timing mode 0\n");
> > > +			return ret;
> > > +		}
> > > +	}  
> > 
> > Can you put this code in a separate function? I'd like to keep the
> > nand_reset() function as small as possible.
> > 
> > How about nand_reset_data_interface()?  
> 
> Yes, can do. In that case I would move the test if setting the data
> interface is supported to that function aswell. Are you okay with that?

Sure.

> 
> 
> >   
> > > +
> > >  	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
> > >  
> > > +	/*
> > > +	 * Setup the NAND interface (interface type + timings).
> > > +	 */
> > > +	if (chip->data_iface) {
> > > +		uint8_t tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = {
> > > +			chip->onfi_timing_mode_default,
> > > +		};
> > > +
> > > +		/*
> > > +		 * Ensure the timing mode has be changed on the chip side  
> > 					      ^ been  
> > > +		 * before changing timings on the controller side.
> > > +		 */
> > > +		if (chip->onfi_version) {
> > > +			ret = chip->onfi_set_features(mtd, chip,
> > > +					ONFI_FEATURE_ADDR_TIMING_MODE,
> > > +					tmode_param);
> > > +			if (ret)
> > > +				return ret;
> > > +		}
> > > +
> > > +		ret = chip->setup_data_interface(mtd, chip->data_iface, false);
> > > +		if (ret)
> > > +			return ret;
> > > +	}
> > > +  
> > 
> > Ditto: nand_setup_data_interface()?
> >   
> > >  	return 0;
> > >  }
> > >  
> > > @@ -3335,6 +3390,54 @@ static void nand_onfi_detect_micron(struct nand_chip *chip,
> > >  	chip->setup_read_retry = nand_setup_read_retry_micron;
> > >  }
> > >  
> > > +/**
> > > + * nand_find_data_interface - Find the best data interface and timings
> > > + * @mtd: MTD device structure
> > > + *
> > > + * Try to find the best data interface and NAND timings supported by the
> > > + * chip and the driver.
> > > + * First tries to retrieve supported timing modes from ONFI information,
> > > + * and if the NAND chip does not support ONFI, relies on the
> > > + * ->onfi_timing_mode_default specified in the nand_ids table.
> > > + *
> > > + * Returns 0 for success or negative error code otherwise.
> > > + */
> > > +static int nand_find_data_interface(struct mtd_info *mtd)  
> > 
> > How about nand_init_data_interface() or nand_init_data_iface_config()?
> >   
> > > +{
> > > +	struct nand_chip *chip = mtd_to_nand(mtd);
> > > +	int modes, mode, ret;
> > > +	const struct nand_data_interface *conf;
> > > +
> > > +	/*
> > > +	 * First try to identify the best timings from ONFI parameters and
> > > +	 * if the NAND does not support ONFI, fallback to the default ONFI
> > > +	 * timing mode.
> > > +	 */
> > > +	modes = onfi_get_async_timing_mode(chip);
> > > +	if (modes == ONFI_TIMING_MODE_UNKNOWN)
> > > +		modes = GENMASK(chip->onfi_timing_mode_default, 0);
> > > +
> > > +	ret = -EINVAL;
> > > +	for (mode = fls(modes) - 1; mode >= 0; mode--) {
> > > +		conf = onfi_async_timing_mode_to_data_interface(mode);  
> > 
> > I'd still prefer to have conf allocated at the beginning of the
> > function and timings copied from
> > onfi_async_timing_mode_to_sdr_timings(mode), but maybe you can convince
> > me otherwise.  
> 
> Let me ask the other way round: If we need struct nand_data_interface to
> fully describe a timing, why don't we keep an array of these in the
> kernel? Having an array of struct nand_sdr_timings() means we always
> have to copy it to a bigger struct to make it usable.

Actually, the plan is to let vendor specific code tweak the timings if
needed.
Some NANDs that do not support ONFI have to pick timing mode 0 because
one of their timing is not matching the ONFI spec. I'd like to let
the door to fined-grained timing tweaking open, and this is only
possible if the chip has its own nand_data_interface object (not the
const one defined in nand_timings.c).

Also note that some timings are not statically defined (like tPROG),
and are extracted from another ONFI field, and I'd like to add them to
the nand_sdr_timings struct, which again, is only possible if the
nand_chip has its own nand_data_interface instance.

> 
> > > @@ -759,6 +759,10 @@ struct nand_chip {
> > >  	int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
> > >  			int feature_addr, uint8_t *subfeature_para);
> > >  	int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode);
> > > +	int (*setup_data_interface)(struct mtd_info *mtd,
> > > +				    const struct nand_data_interface *conf,
> > > +				    bool check_only);
> > > +
> > >  
> > >  	int chip_delay;
> > >  	unsigned int options;
> > > @@ -788,6 +792,8 @@ struct nand_chip {
> > >  		struct nand_jedec_params jedec_params;
> > >  	};
> > >  
> > > +	const struct nand_data_interface *data_iface;
> > > +  
> > 
> > How about making this field non-const so that you only allocate it once
> > and modify it when you switch from one mode to another.  
> 
> As said above, I need two different timings. If we modify this
> nand_data_interface instance twice during reset there's not much point
> in storing it in struct nand_chip at all. That was one variant I tried:
> Always calculcate the timing from the supported ONFI modes when we need
> it in nand_reset(). I stepped away from this variant because of the
> overhead.

Yes, your device will be configured twice (first mode 0, then the
highest supported timing mode), but that does not mean you need to have
2 instances of nand_data_interface.

->data_iface should always be assigned to the current data interface
config. If you reset the chip and go back to timing 0, then
chip->data_iface should be set to sdr mode timing zero, and once a
new timing mode is applied, it should be updated.

And yes, there's a small overhead (copying the nand_sdr_timings data
twice), but I'm pretty sure it's negligible compared to the whole NAND
chip init overhead.
And it's not like nand_reset() is called so regularly that it's useful
to optimize this kind of things.

> 
> Sascha
> 

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

* Re: [PATCH 4/7] mtd: nand: automate NAND timings selection
  2016-09-06 14:50         ` Boris Brezillon
@ 2016-09-06 15:04           ` Sascha Hauer
  -1 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 15:04 UTC (permalink / raw)
  To: Boris Brezillon; +Cc: linux-mtd, kernel, linux-arm-kernel

On Tue, Sep 06, 2016 at 04:50:04PM +0200, Boris Brezillon wrote:
> On Tue, 6 Sep 2016 16:08:17 +0200
> Sascha Hauer <s.hauer@pengutronix.de> wrote:
> 
> > On Tue, Sep 06, 2016 at 01:58:07PM +0200, Boris Brezillon wrote:
> > > On Tue,  6 Sep 2016 12:39:12 +0200
> > > Sascha Hauer <s.hauer@pengutronix.de> wrote:
> > >   
> > > > From: Boris Brezillon <boris.brezillon@free-electrons.com>
> > > > 
> > > > The NAND framework provides several helpers to query timing modes supported
> > > > by a NAND chip, but this implies that all NAND controller drivers have
> > > > to implement the same timings selection dance. Also currently NAND
> > > > devices can be resetted at arbitrary places which also resets the timing
> > > > for ONFI chips to timing mode 0.
> > > > 
> > > > Provide a common logic to select the best timings based on ONFI or  
> > > > ->onfi_timing_mode_default information. Hook this into nand_reset()    
> > > > to make sure the new timing is applied each time during a reset.
> > > > 
> > > > NAND controller willing to support timings adjustment should just
> > > > implement the ->setup_data_interface() method.
> > > > 
> > > > Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> > > > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > > > ---
> > > >  drivers/mtd/nand/nand_base.c | 112 +++++++++++++++++++++++++++++++++++++++++++
> > > >  include/linux/mtd/nand.h     |  14 ++++--
> > > >  2 files changed, 122 insertions(+), 4 deletions(-)
> > > > 
> > > > diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> > > > index 20151fc..37852e9 100644
> > > > --- a/drivers/mtd/nand/nand_base.c
> > > > +++ b/drivers/mtd/nand/nand_base.c
> > > > @@ -955,8 +955,63 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
> > > >   */
> > > >  int nand_reset(struct mtd_info *mtd)
> > > >  {
> > > > +	struct nand_chip *chip = mtd_to_nand(mtd);
> > > > +	int ret;
> > > > +
> > > > +	if (chip->setup_data_interface) {
> > > > +		struct nand_data_interface conf = {
> > > > +			.type = NAND_SDR_IFACE,
> > > > +			.timings.sdr = *onfi_async_timing_mode_to_sdr_timings(0),
> > > > +		};
> > > > +  
> > > 
> > > Let's try to avoid putting such a huge structure on the stack.  
> > 
> > I could do that by doing
> > 
> > 	const struct nand_data_interface *conf = onfi_async_timing_mode_to_data_interface(0);
> > 
> > But you just discussed this function away ;)
> > 
> > I need two different timings, first the ONFI timing mode 0 and then the
> > real timing. I wanted to avoid re-initializing the same timing struct
> > instance multiple times (twice in nand_reset() and two times again for
> > each additional chip in an array).
> > 
> > >   
> > > > +		/*
> > > > +		 * The ONFI specification says:
> > > > +		 * "
> > > > +		 * To transition from NV-DDR or NV-DDR2 to the SDR data
> > > > +		 * interface, the host shall use the Reset (FFh) command
> > > > +		 * using SDR timing mode 0. A device in any timing mode is
> > > > +		 * required to recognize Reset (FFh) command issued in SDR
> > > > +		 * timing mode 0.
> > > > +		 * "
> > > > +		 *
> > > > +		 * Configure the data interface in SDR mode and set the
> > > > +		 * timings to timing mode 0.
> > > > +		 */
> > > > +
> > > > +		ret = chip->setup_data_interface(mtd, &conf, false);
> > > > +		if (ret) {
> > > > +			pr_err("Failed to configure data interface to SDR timing mode 0\n");
> > > > +			return ret;
> > > > +		}
> > > > +	}  
> > > 
> > > Can you put this code in a separate function? I'd like to keep the
> > > nand_reset() function as small as possible.
> > > 
> > > How about nand_reset_data_interface()?  
> > 
> > Yes, can do. In that case I would move the test if setting the data
> > interface is supported to that function aswell. Are you okay with that?
> 
> Sure.
> 
> > 
> > 
> > >   
> > > > +
> > > >  	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
> > > >  
> > > > +	/*
> > > > +	 * Setup the NAND interface (interface type + timings).
> > > > +	 */
> > > > +	if (chip->data_iface) {
> > > > +		uint8_t tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = {
> > > > +			chip->onfi_timing_mode_default,
> > > > +		};
> > > > +
> > > > +		/*
> > > > +		 * Ensure the timing mode has be changed on the chip side  
> > > 					      ^ been  
> > > > +		 * before changing timings on the controller side.
> > > > +		 */
> > > > +		if (chip->onfi_version) {
> > > > +			ret = chip->onfi_set_features(mtd, chip,
> > > > +					ONFI_FEATURE_ADDR_TIMING_MODE,
> > > > +					tmode_param);
> > > > +			if (ret)
> > > > +				return ret;
> > > > +		}
> > > > +
> > > > +		ret = chip->setup_data_interface(mtd, chip->data_iface, false);
> > > > +		if (ret)
> > > > +			return ret;
> > > > +	}
> > > > +  
> > > 
> > > Ditto: nand_setup_data_interface()?
> > >   
> > > >  	return 0;
> > > >  }
> > > >  
> > > > @@ -3335,6 +3390,54 @@ static void nand_onfi_detect_micron(struct nand_chip *chip,
> > > >  	chip->setup_read_retry = nand_setup_read_retry_micron;
> > > >  }
> > > >  
> > > > +/**
> > > > + * nand_find_data_interface - Find the best data interface and timings
> > > > + * @mtd: MTD device structure
> > > > + *
> > > > + * Try to find the best data interface and NAND timings supported by the
> > > > + * chip and the driver.
> > > > + * First tries to retrieve supported timing modes from ONFI information,
> > > > + * and if the NAND chip does not support ONFI, relies on the
> > > > + * ->onfi_timing_mode_default specified in the nand_ids table.
> > > > + *
> > > > + * Returns 0 for success or negative error code otherwise.
> > > > + */
> > > > +static int nand_find_data_interface(struct mtd_info *mtd)  
> > > 
> > > How about nand_init_data_interface() or nand_init_data_iface_config()?
> > >   
> > > > +{
> > > > +	struct nand_chip *chip = mtd_to_nand(mtd);
> > > > +	int modes, mode, ret;
> > > > +	const struct nand_data_interface *conf;
> > > > +
> > > > +	/*
> > > > +	 * First try to identify the best timings from ONFI parameters and
> > > > +	 * if the NAND does not support ONFI, fallback to the default ONFI
> > > > +	 * timing mode.
> > > > +	 */
> > > > +	modes = onfi_get_async_timing_mode(chip);
> > > > +	if (modes == ONFI_TIMING_MODE_UNKNOWN)
> > > > +		modes = GENMASK(chip->onfi_timing_mode_default, 0);
> > > > +
> > > > +	ret = -EINVAL;
> > > > +	for (mode = fls(modes) - 1; mode >= 0; mode--) {
> > > > +		conf = onfi_async_timing_mode_to_data_interface(mode);  
> > > 
> > > I'd still prefer to have conf allocated at the beginning of the
> > > function and timings copied from
> > > onfi_async_timing_mode_to_sdr_timings(mode), but maybe you can convince
> > > me otherwise.  
> > 
> > Let me ask the other way round: If we need struct nand_data_interface to
> > fully describe a timing, why don't we keep an array of these in the
> > kernel? Having an array of struct nand_sdr_timings() means we always
> > have to copy it to a bigger struct to make it usable.
> 
> Actually, the plan is to let vendor specific code tweak the timings if
> needed.
> Some NANDs that do not support ONFI have to pick timing mode 0 because
> one of their timing is not matching the ONFI spec. I'd like to let
> the door to fined-grained timing tweaking open, and this is only
> possible if the chip has its own nand_data_interface object (not the
> const one defined in nand_timings.c).
> 
> Also note that some timings are not statically defined (like tPROG),
> and are extracted from another ONFI field, and I'd like to add them to
> the nand_sdr_timings struct, which again, is only possible if the
> nand_chip has its own nand_data_interface instance.

Hm, in the current series the nand_chip has it's own nand_data_interface
instance, it's allocated in nand_find_data_interface().

> 
> > 
> > > > @@ -759,6 +759,10 @@ struct nand_chip {
> > > >  	int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
> > > >  			int feature_addr, uint8_t *subfeature_para);
> > > >  	int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode);
> > > > +	int (*setup_data_interface)(struct mtd_info *mtd,
> > > > +				    const struct nand_data_interface *conf,
> > > > +				    bool check_only);
> > > > +
> > > >  
> > > >  	int chip_delay;
> > > >  	unsigned int options;
> > > > @@ -788,6 +792,8 @@ struct nand_chip {
> > > >  		struct nand_jedec_params jedec_params;
> > > >  	};
> > > >  
> > > > +	const struct nand_data_interface *data_iface;
> > > > +  
> > > 
> > > How about making this field non-const so that you only allocate it once
> > > and modify it when you switch from one mode to another.  
> > 
> > As said above, I need two different timings. If we modify this
> > nand_data_interface instance twice during reset there's not much point
> > in storing it in struct nand_chip at all. That was one variant I tried:
> > Always calculcate the timing from the supported ONFI modes when we need
> > it in nand_reset(). I stepped away from this variant because of the
> > overhead.
> 
> Yes, your device will be configured twice (first mode 0, then the
> highest supported timing mode), but that does not mean you need to have
> 2 instances of nand_data_interface.
> 
> ->data_iface should always be assigned to the current data interface
> config. If you reset the chip and go back to timing 0, then
> chip->data_iface should be set to sdr mode timing zero, and once a
> new timing mode is applied, it should be updated.
> 
> And yes, there's a small overhead (copying the nand_sdr_timings data
> twice), but I'm pretty sure it's negligible compared to the whole NAND
> chip init overhead.
> And it's not like nand_reset() is called so regularly that it's useful
> to optimize this kind of things.

I haven't really thought about overhead in terms of burnt CPU cycles but
more about how easy it is to follow the code.
Anyway, I do as you wish, expect a new series tomorrow ;)

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* [PATCH 4/7] mtd: nand: automate NAND timings selection
@ 2016-09-06 15:04           ` Sascha Hauer
  0 siblings, 0 replies; 52+ messages in thread
From: Sascha Hauer @ 2016-09-06 15:04 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Sep 06, 2016 at 04:50:04PM +0200, Boris Brezillon wrote:
> On Tue, 6 Sep 2016 16:08:17 +0200
> Sascha Hauer <s.hauer@pengutronix.de> wrote:
> 
> > On Tue, Sep 06, 2016 at 01:58:07PM +0200, Boris Brezillon wrote:
> > > On Tue,  6 Sep 2016 12:39:12 +0200
> > > Sascha Hauer <s.hauer@pengutronix.de> wrote:
> > >   
> > > > From: Boris Brezillon <boris.brezillon@free-electrons.com>
> > > > 
> > > > The NAND framework provides several helpers to query timing modes supported
> > > > by a NAND chip, but this implies that all NAND controller drivers have
> > > > to implement the same timings selection dance. Also currently NAND
> > > > devices can be resetted at arbitrary places which also resets the timing
> > > > for ONFI chips to timing mode 0.
> > > > 
> > > > Provide a common logic to select the best timings based on ONFI or  
> > > > ->onfi_timing_mode_default information. Hook this into nand_reset()    
> > > > to make sure the new timing is applied each time during a reset.
> > > > 
> > > > NAND controller willing to support timings adjustment should just
> > > > implement the ->setup_data_interface() method.
> > > > 
> > > > Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> > > > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> > > > ---
> > > >  drivers/mtd/nand/nand_base.c | 112 +++++++++++++++++++++++++++++++++++++++++++
> > > >  include/linux/mtd/nand.h     |  14 ++++--
> > > >  2 files changed, 122 insertions(+), 4 deletions(-)
> > > > 
> > > > diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> > > > index 20151fc..37852e9 100644
> > > > --- a/drivers/mtd/nand/nand_base.c
> > > > +++ b/drivers/mtd/nand/nand_base.c
> > > > @@ -955,8 +955,63 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
> > > >   */
> > > >  int nand_reset(struct mtd_info *mtd)
> > > >  {
> > > > +	struct nand_chip *chip = mtd_to_nand(mtd);
> > > > +	int ret;
> > > > +
> > > > +	if (chip->setup_data_interface) {
> > > > +		struct nand_data_interface conf = {
> > > > +			.type = NAND_SDR_IFACE,
> > > > +			.timings.sdr = *onfi_async_timing_mode_to_sdr_timings(0),
> > > > +		};
> > > > +  
> > > 
> > > Let's try to avoid putting such a huge structure on the stack.  
> > 
> > I could do that by doing
> > 
> > 	const struct nand_data_interface *conf = onfi_async_timing_mode_to_data_interface(0);
> > 
> > But you just discussed this function away ;)
> > 
> > I need two different timings, first the ONFI timing mode 0 and then the
> > real timing. I wanted to avoid re-initializing the same timing struct
> > instance multiple times (twice in nand_reset() and two times again for
> > each additional chip in an array).
> > 
> > >   
> > > > +		/*
> > > > +		 * The ONFI specification says:
> > > > +		 * "
> > > > +		 * To transition from NV-DDR or NV-DDR2 to the SDR data
> > > > +		 * interface, the host shall use the Reset (FFh) command
> > > > +		 * using SDR timing mode 0. A device in any timing mode is
> > > > +		 * required to recognize Reset (FFh) command issued in SDR
> > > > +		 * timing mode 0.
> > > > +		 * "
> > > > +		 *
> > > > +		 * Configure the data interface in SDR mode and set the
> > > > +		 * timings to timing mode 0.
> > > > +		 */
> > > > +
> > > > +		ret = chip->setup_data_interface(mtd, &conf, false);
> > > > +		if (ret) {
> > > > +			pr_err("Failed to configure data interface to SDR timing mode 0\n");
> > > > +			return ret;
> > > > +		}
> > > > +	}  
> > > 
> > > Can you put this code in a separate function? I'd like to keep the
> > > nand_reset() function as small as possible.
> > > 
> > > How about nand_reset_data_interface()?  
> > 
> > Yes, can do. In that case I would move the test if setting the data
> > interface is supported to that function aswell. Are you okay with that?
> 
> Sure.
> 
> > 
> > 
> > >   
> > > > +
> > > >  	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
> > > >  
> > > > +	/*
> > > > +	 * Setup the NAND interface (interface type + timings).
> > > > +	 */
> > > > +	if (chip->data_iface) {
> > > > +		uint8_t tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = {
> > > > +			chip->onfi_timing_mode_default,
> > > > +		};
> > > > +
> > > > +		/*
> > > > +		 * Ensure the timing mode has be changed on the chip side  
> > > 					      ^ been  
> > > > +		 * before changing timings on the controller side.
> > > > +		 */
> > > > +		if (chip->onfi_version) {
> > > > +			ret = chip->onfi_set_features(mtd, chip,
> > > > +					ONFI_FEATURE_ADDR_TIMING_MODE,
> > > > +					tmode_param);
> > > > +			if (ret)
> > > > +				return ret;
> > > > +		}
> > > > +
> > > > +		ret = chip->setup_data_interface(mtd, chip->data_iface, false);
> > > > +		if (ret)
> > > > +			return ret;
> > > > +	}
> > > > +  
> > > 
> > > Ditto: nand_setup_data_interface()?
> > >   
> > > >  	return 0;
> > > >  }
> > > >  
> > > > @@ -3335,6 +3390,54 @@ static void nand_onfi_detect_micron(struct nand_chip *chip,
> > > >  	chip->setup_read_retry = nand_setup_read_retry_micron;
> > > >  }
> > > >  
> > > > +/**
> > > > + * nand_find_data_interface - Find the best data interface and timings
> > > > + * @mtd: MTD device structure
> > > > + *
> > > > + * Try to find the best data interface and NAND timings supported by the
> > > > + * chip and the driver.
> > > > + * First tries to retrieve supported timing modes from ONFI information,
> > > > + * and if the NAND chip does not support ONFI, relies on the
> > > > + * ->onfi_timing_mode_default specified in the nand_ids table.
> > > > + *
> > > > + * Returns 0 for success or negative error code otherwise.
> > > > + */
> > > > +static int nand_find_data_interface(struct mtd_info *mtd)  
> > > 
> > > How about nand_init_data_interface() or nand_init_data_iface_config()?
> > >   
> > > > +{
> > > > +	struct nand_chip *chip = mtd_to_nand(mtd);
> > > > +	int modes, mode, ret;
> > > > +	const struct nand_data_interface *conf;
> > > > +
> > > > +	/*
> > > > +	 * First try to identify the best timings from ONFI parameters and
> > > > +	 * if the NAND does not support ONFI, fallback to the default ONFI
> > > > +	 * timing mode.
> > > > +	 */
> > > > +	modes = onfi_get_async_timing_mode(chip);
> > > > +	if (modes == ONFI_TIMING_MODE_UNKNOWN)
> > > > +		modes = GENMASK(chip->onfi_timing_mode_default, 0);
> > > > +
> > > > +	ret = -EINVAL;
> > > > +	for (mode = fls(modes) - 1; mode >= 0; mode--) {
> > > > +		conf = onfi_async_timing_mode_to_data_interface(mode);  
> > > 
> > > I'd still prefer to have conf allocated at the beginning of the
> > > function and timings copied from
> > > onfi_async_timing_mode_to_sdr_timings(mode), but maybe you can convince
> > > me otherwise.  
> > 
> > Let me ask the other way round: If we need struct nand_data_interface to
> > fully describe a timing, why don't we keep an array of these in the
> > kernel? Having an array of struct nand_sdr_timings() means we always
> > have to copy it to a bigger struct to make it usable.
> 
> Actually, the plan is to let vendor specific code tweak the timings if
> needed.
> Some NANDs that do not support ONFI have to pick timing mode 0 because
> one of their timing is not matching the ONFI spec. I'd like to let
> the door to fined-grained timing tweaking open, and this is only
> possible if the chip has its own nand_data_interface object (not the
> const one defined in nand_timings.c).
> 
> Also note that some timings are not statically defined (like tPROG),
> and are extracted from another ONFI field, and I'd like to add them to
> the nand_sdr_timings struct, which again, is only possible if the
> nand_chip has its own nand_data_interface instance.

Hm, in the current series the nand_chip has it's own nand_data_interface
instance, it's allocated in nand_find_data_interface().

> 
> > 
> > > > @@ -759,6 +759,10 @@ struct nand_chip {
> > > >  	int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
> > > >  			int feature_addr, uint8_t *subfeature_para);
> > > >  	int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode);
> > > > +	int (*setup_data_interface)(struct mtd_info *mtd,
> > > > +				    const struct nand_data_interface *conf,
> > > > +				    bool check_only);
> > > > +
> > > >  
> > > >  	int chip_delay;
> > > >  	unsigned int options;
> > > > @@ -788,6 +792,8 @@ struct nand_chip {
> > > >  		struct nand_jedec_params jedec_params;
> > > >  	};
> > > >  
> > > > +	const struct nand_data_interface *data_iface;
> > > > +  
> > > 
> > > How about making this field non-const so that you only allocate it once
> > > and modify it when you switch from one mode to another.  
> > 
> > As said above, I need two different timings. If we modify this
> > nand_data_interface instance twice during reset there's not much point
> > in storing it in struct nand_chip at all. That was one variant I tried:
> > Always calculcate the timing from the supported ONFI modes when we need
> > it in nand_reset(). I stepped away from this variant because of the
> > overhead.
> 
> Yes, your device will be configured twice (first mode 0, then the
> highest supported timing mode), but that does not mean you need to have
> 2 instances of nand_data_interface.
> 
> ->data_iface should always be assigned to the current data interface
> config. If you reset the chip and go back to timing 0, then
> chip->data_iface should be set to sdr mode timing zero, and once a
> new timing mode is applied, it should be updated.
> 
> And yes, there's a small overhead (copying the nand_sdr_timings data
> twice), but I'm pretty sure it's negligible compared to the whole NAND
> chip init overhead.
> And it's not like nand_reset() is called so regularly that it's useful
> to optimize this kind of things.

I haven't really thought about overhead in terms of burnt CPU cycles but
more about how easy it is to follow the code.
Anyway, I do as you wish, expect a new series tomorrow ;)

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH 4/7] mtd: nand: automate NAND timings selection
  2016-09-06 15:04           ` Sascha Hauer
@ 2016-09-06 15:15             ` Boris Brezillon
  -1 siblings, 0 replies; 52+ messages in thread
From: Boris Brezillon @ 2016-09-06 15:15 UTC (permalink / raw)
  To: Sascha Hauer; +Cc: linux-mtd, kernel, linux-arm-kernel

On Tue, 6 Sep 2016 17:04:58 +0200
Sascha Hauer <s.hauer@pengutronix.de> wrote:


> > > >     
> > > > > +{
> > > > > +	struct nand_chip *chip = mtd_to_nand(mtd);
> > > > > +	int modes, mode, ret;
> > > > > +	const struct nand_data_interface *conf;
> > > > > +
> > > > > +	/*
> > > > > +	 * First try to identify the best timings from ONFI parameters and
> > > > > +	 * if the NAND does not support ONFI, fallback to the default ONFI
> > > > > +	 * timing mode.
> > > > > +	 */
> > > > > +	modes = onfi_get_async_timing_mode(chip);
> > > > > +	if (modes == ONFI_TIMING_MODE_UNKNOWN)
> > > > > +		modes = GENMASK(chip->onfi_timing_mode_default, 0);
> > > > > +
> > > > > +	ret = -EINVAL;
> > > > > +	for (mode = fls(modes) - 1; mode >= 0; mode--) {
> > > > > +		conf = onfi_async_timing_mode_to_data_interface(mode);    
> > > > 
> > > > I'd still prefer to have conf allocated at the beginning of the
> > > > function and timings copied from
> > > > onfi_async_timing_mode_to_sdr_timings(mode), but maybe you can convince
> > > > me otherwise.    
> > > 
> > > Let me ask the other way round: If we need struct nand_data_interface to
> > > fully describe a timing, why don't we keep an array of these in the
> > > kernel? Having an array of struct nand_sdr_timings() means we always
> > > have to copy it to a bigger struct to make it usable.  
> > 
> > Actually, the plan is to let vendor specific code tweak the timings if
> > needed.
> > Some NANDs that do not support ONFI have to pick timing mode 0 because
> > one of their timing is not matching the ONFI spec. I'd like to let
> > the door to fined-grained timing tweaking open, and this is only
> > possible if the chip has its own nand_data_interface object (not the
> > const one defined in nand_timings.c).
> > 
> > Also note that some timings are not statically defined (like tPROG),
> > and are extracted from another ONFI field, and I'd like to add them to
> > the nand_sdr_timings struct, which again, is only possible if the
> > nand_chip has its own nand_data_interface instance.  
> 
> Hm, in the current series the nand_chip has it's own nand_data_interface
> instance, it's allocated in nand_find_data_interface().

Yes, but I thought you were suggesting to drop the allocation and
directly point to the static declaration returned by
onfi_async_timing_mode_to_data_interface().

> 
> >   
> > >   
> > > > > @@ -759,6 +759,10 @@ struct nand_chip {
> > > > >  	int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
> > > > >  			int feature_addr, uint8_t *subfeature_para);
> > > > >  	int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode);
> > > > > +	int (*setup_data_interface)(struct mtd_info *mtd,
> > > > > +				    const struct nand_data_interface *conf,
> > > > > +				    bool check_only);
> > > > > +
> > > > >  
> > > > >  	int chip_delay;
> > > > >  	unsigned int options;
> > > > > @@ -788,6 +792,8 @@ struct nand_chip {
> > > > >  		struct nand_jedec_params jedec_params;
> > > > >  	};
> > > > >  
> > > > > +	const struct nand_data_interface *data_iface;
> > > > > +    
> > > > 
> > > > How about making this field non-const so that you only allocate it once
> > > > and modify it when you switch from one mode to another.    
> > > 
> > > As said above, I need two different timings. If we modify this
> > > nand_data_interface instance twice during reset there's not much point
> > > in storing it in struct nand_chip at all. That was one variant I tried:
> > > Always calculcate the timing from the supported ONFI modes when we need
> > > it in nand_reset(). I stepped away from this variant because of the
> > > overhead.  
> > 
> > Yes, your device will be configured twice (first mode 0, then the
> > highest supported timing mode), but that does not mean you need to have
> > 2 instances of nand_data_interface.
> >   
> > ->data_iface should always be assigned to the current data interface  
> > config. If you reset the chip and go back to timing 0, then
> > chip->data_iface should be set to sdr mode timing zero, and once a
> > new timing mode is applied, it should be updated.
> > 
> > And yes, there's a small overhead (copying the nand_sdr_timings data
> > twice), but I'm pretty sure it's negligible compared to the whole NAND
> > chip init overhead.
> > And it's not like nand_reset() is called so regularly that it's useful
> > to optimize this kind of things.  
> 
> I haven't really thought about overhead in terms of burnt CPU cycles but
> more about how easy it is to follow the code.

Ok.

> Anyway, I do as you wish, expect a new series tomorrow ;)

Let's see how it looks like.

Thanks,

Boris

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

* [PATCH 4/7] mtd: nand: automate NAND timings selection
@ 2016-09-06 15:15             ` Boris Brezillon
  0 siblings, 0 replies; 52+ messages in thread
From: Boris Brezillon @ 2016-09-06 15:15 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 6 Sep 2016 17:04:58 +0200
Sascha Hauer <s.hauer@pengutronix.de> wrote:


> > > >     
> > > > > +{
> > > > > +	struct nand_chip *chip = mtd_to_nand(mtd);
> > > > > +	int modes, mode, ret;
> > > > > +	const struct nand_data_interface *conf;
> > > > > +
> > > > > +	/*
> > > > > +	 * First try to identify the best timings from ONFI parameters and
> > > > > +	 * if the NAND does not support ONFI, fallback to the default ONFI
> > > > > +	 * timing mode.
> > > > > +	 */
> > > > > +	modes = onfi_get_async_timing_mode(chip);
> > > > > +	if (modes == ONFI_TIMING_MODE_UNKNOWN)
> > > > > +		modes = GENMASK(chip->onfi_timing_mode_default, 0);
> > > > > +
> > > > > +	ret = -EINVAL;
> > > > > +	for (mode = fls(modes) - 1; mode >= 0; mode--) {
> > > > > +		conf = onfi_async_timing_mode_to_data_interface(mode);    
> > > > 
> > > > I'd still prefer to have conf allocated at the beginning of the
> > > > function and timings copied from
> > > > onfi_async_timing_mode_to_sdr_timings(mode), but maybe you can convince
> > > > me otherwise.    
> > > 
> > > Let me ask the other way round: If we need struct nand_data_interface to
> > > fully describe a timing, why don't we keep an array of these in the
> > > kernel? Having an array of struct nand_sdr_timings() means we always
> > > have to copy it to a bigger struct to make it usable.  
> > 
> > Actually, the plan is to let vendor specific code tweak the timings if
> > needed.
> > Some NANDs that do not support ONFI have to pick timing mode 0 because
> > one of their timing is not matching the ONFI spec. I'd like to let
> > the door to fined-grained timing tweaking open, and this is only
> > possible if the chip has its own nand_data_interface object (not the
> > const one defined in nand_timings.c).
> > 
> > Also note that some timings are not statically defined (like tPROG),
> > and are extracted from another ONFI field, and I'd like to add them to
> > the nand_sdr_timings struct, which again, is only possible if the
> > nand_chip has its own nand_data_interface instance.  
> 
> Hm, in the current series the nand_chip has it's own nand_data_interface
> instance, it's allocated in nand_find_data_interface().

Yes, but I thought you were suggesting to drop the allocation and
directly point to the static declaration returned by
onfi_async_timing_mode_to_data_interface().

> 
> >   
> > >   
> > > > > @@ -759,6 +759,10 @@ struct nand_chip {
> > > > >  	int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
> > > > >  			int feature_addr, uint8_t *subfeature_para);
> > > > >  	int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode);
> > > > > +	int (*setup_data_interface)(struct mtd_info *mtd,
> > > > > +				    const struct nand_data_interface *conf,
> > > > > +				    bool check_only);
> > > > > +
> > > > >  
> > > > >  	int chip_delay;
> > > > >  	unsigned int options;
> > > > > @@ -788,6 +792,8 @@ struct nand_chip {
> > > > >  		struct nand_jedec_params jedec_params;
> > > > >  	};
> > > > >  
> > > > > +	const struct nand_data_interface *data_iface;
> > > > > +    
> > > > 
> > > > How about making this field non-const so that you only allocate it once
> > > > and modify it when you switch from one mode to another.    
> > > 
> > > As said above, I need two different timings. If we modify this
> > > nand_data_interface instance twice during reset there's not much point
> > > in storing it in struct nand_chip at all. That was one variant I tried:
> > > Always calculcate the timing from the supported ONFI modes when we need
> > > it in nand_reset(). I stepped away from this variant because of the
> > > overhead.  
> > 
> > Yes, your device will be configured twice (first mode 0, then the
> > highest supported timing mode), but that does not mean you need to have
> > 2 instances of nand_data_interface.
> >   
> > ->data_iface should always be assigned to the current data interface  
> > config. If you reset the chip and go back to timing 0, then
> > chip->data_iface should be set to sdr mode timing zero, and once a
> > new timing mode is applied, it should be updated.
> > 
> > And yes, there's a small overhead (copying the nand_sdr_timings data
> > twice), but I'm pretty sure it's negligible compared to the whole NAND
> > chip init overhead.
> > And it's not like nand_reset() is called so regularly that it's useful
> > to optimize this kind of things.  
> 
> I haven't really thought about overhead in terms of burnt CPU cycles but
> more about how easy it is to follow the code.

Ok.

> Anyway, I do as you wish, expect a new series tomorrow ;)

Let's see how it looks like.

Thanks,

Boris

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

end of thread, other threads:[~2016-09-06 15:15 UTC | newest]

Thread overview: 52+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-09-06 10:39 [PATCH v2] mtd: nand: automate NAND timings selection Sascha Hauer
2016-09-06 10:39 ` Sascha Hauer
2016-09-06 10:39 ` [PATCH 1/7] mtd: nand: Create a NAND reset function Sascha Hauer
2016-09-06 10:39   ` Sascha Hauer
2016-09-06 11:18   ` Boris Brezillon
2016-09-06 11:18     ` Boris Brezillon
2016-09-06 13:02     ` Sascha Hauer
2016-09-06 13:02       ` Sascha Hauer
2016-09-06 13:06       ` Boris Brezillon
2016-09-06 13:06         ` Boris Brezillon
2016-09-06 10:39 ` [PATCH 2/7] mtd: nand: Introduce nand_data_interface Sascha Hauer
2016-09-06 10:39   ` Sascha Hauer
2016-09-06 11:21   ` Boris Brezillon
2016-09-06 11:21     ` Boris Brezillon
2016-09-06 13:34     ` Sascha Hauer
2016-09-06 13:34       ` Sascha Hauer
2016-09-06 13:46       ` Boris Brezillon
2016-09-06 13:46         ` Boris Brezillon
2016-09-06 14:09         ` Sascha Hauer
2016-09-06 14:09           ` Sascha Hauer
2016-09-06 10:39 ` [PATCH 3/7] mtd: nand: convert ONFI mode into data interface Sascha Hauer
2016-09-06 10:39   ` Sascha Hauer
2016-09-06 11:27   ` Boris Brezillon
2016-09-06 11:27     ` Boris Brezillon
2016-09-06 12:15   ` Boris Brezillon
2016-09-06 12:15     ` Boris Brezillon
2016-09-06 10:39 ` [PATCH 4/7] mtd: nand: automate NAND timings selection Sascha Hauer
2016-09-06 10:39   ` Sascha Hauer
2016-09-06 11:58   ` Boris Brezillon
2016-09-06 11:58     ` Boris Brezillon
2016-09-06 14:08     ` Sascha Hauer
2016-09-06 14:08       ` Sascha Hauer
2016-09-06 14:50       ` Boris Brezillon
2016-09-06 14:50         ` Boris Brezillon
2016-09-06 15:04         ` Sascha Hauer
2016-09-06 15:04           ` Sascha Hauer
2016-09-06 15:15           ` Boris Brezillon
2016-09-06 15:15             ` Boris Brezillon
2016-09-06 10:39 ` [PATCH 5/7] mtd: nand: sunxi: switch from manual to automated timing config Sascha Hauer
2016-09-06 10:39   ` Sascha Hauer
2016-09-06 12:01   ` Boris Brezillon
2016-09-06 12:01     ` Boris Brezillon
2016-09-06 10:39 ` [PATCH 6/7] mtd: nand: mxc: implement onfi get/set features Sascha Hauer
2016-09-06 10:39   ` Sascha Hauer
2016-09-06 12:05   ` Boris Brezillon
2016-09-06 12:05     ` Boris Brezillon
2016-09-06 12:47     ` Sascha Hauer
2016-09-06 12:47       ` Sascha Hauer
2016-09-06 12:52       ` Boris Brezillon
2016-09-06 12:52         ` Boris Brezillon
2016-09-06 10:39 ` [PATCH 7/7] mtd: nand: mxc: Add timing setup for v2 controllers Sascha Hauer
2016-09-06 10:39   ` Sascha Hauer

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.