All of lore.kernel.org
 help / color / mirror / Atom feed
* [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes
@ 2017-03-23  0:17 Masahiro Yamada
  2017-03-23  0:17 ` [RESEND PATCH v2 28/53] mtd: nand: denali: support "nand-ecc-strength" DT property Masahiro Yamada
                   ` (25 more replies)
  0 siblings, 26 replies; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

Another problem of this driver is hard-coded ecc.strength and
ecc.bytes.  Currently ecc.bytes is defined as follows:

  #define ECC_8BITS	14
  #define ECC_15BITS	26

The parameters were hard-coded because only the following two cases
are possible on Intel platforms:

 - ecc.size = 512, ecc.strength = 8    --> ecc.bytes = 14
 - ecc.size = 512, ecc.strength = 15   --> ecc.bytes = 26

However, they are actually customizable parameters, for example,
UniPhier platform supports the following:

 - ecc.size = 1024, ecc.strength = 8   --> ecc.bytes = 14
 - ecc.size = 1024, ecc.strength = 16  --> ecc.bytes = 28
 - ecc.size = 1024, ecc.strength = 24  --> ecc.bytes = 42

So, we need to handle these parameters in a more generic manner.
Fortunately, the Denali User's Guide explains how to calculate the
ecc.bytes.  The formula is:

  ecc.bytes = 2 * CEIL(13 * ecc.strength / 16)  (for ecc.size = 512)
  ecc.bytes = 2 * CEIL(14 * ecc.strength / 16)  (for ecc.size = 1024)

This commit allows platforms to specify denali->ecc_strength_avail,
each bit of which represents supported ECC strength.  I am not using
the bitmap here because "unsigned long" should be enough to cover
the ECC strength of this IP.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2: None

 drivers/mtd/nand/denali.c     | 73 +++++++++++++++++++++++++++----------------
 drivers/mtd/nand/denali.h     |  1 +
 drivers/mtd/nand/denali_dt.c  |  3 ++
 drivers/mtd/nand/denali_pci.c |  1 +
 4 files changed, 51 insertions(+), 27 deletions(-)

diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index cf8daba..96074b8 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -1340,13 +1340,45 @@ static void denali_hw_init(struct denali_nand_info *denali)
 	denali_irq_init(denali);
 }
 
-/*
- * Althogh controller spec said SLC ECC is forceb to be 4bit,
- * but denali controller in MRST only support 15bit and 8bit ECC
- * correction
- */
-#define ECC_8BITS	14
-#define ECC_15BITS	26
+static int denali_calc_ecc_bytes(int ecc_size, int ecc_strength)
+{
+	WARN_ON(ecc_size != 512 && ecc_size != 1024);
+
+	return DIV_ROUND_UP(ecc_strength * (ecc_size == 512 ? 13 : 14), 16) * 2;
+}
+
+static int denali_set_max_ecc_strength(struct denali_nand_info *denali)
+{
+	struct nand_chip *chip = &denali->nand;
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	int oobsize = mtd->oobsize;
+	int ecc_size = chip->ecc.size;
+	int ecc_steps = mtd->writesize / chip->ecc.size;
+	int ecc_strength, ecc_bytes;
+	int max_strength = 0;
+
+	/* carve out the BBM area */
+	oobsize -= denali->bbtskipbytes;
+
+	for_each_set_bit(ecc_strength, &denali->ecc_strength_avail,
+			 sizeof(denali->ecc_strength_avail) * BITS_PER_BYTE) {
+		ecc_bytes = denali_calc_ecc_bytes(ecc_size, ecc_strength);
+		if (ecc_bytes * ecc_steps > oobsize)
+			break;
+
+		max_strength = ecc_strength;
+	}
+
+	if (!max_strength) {
+		dev_err(denali->dev,
+			"Your NAND chip OOB is too small. No available ECC strength.\n");
+		return -EINVAL;
+	}
+
+	chip->ecc.strength = max_strength;
+
+	return 0;
+}
 
 static int denali_ooblayout_ecc(struct mtd_info *mtd, int section,
 				struct mtd_oob_region *oobregion)
@@ -1595,27 +1627,14 @@ int denali_init(struct denali_nand_info *denali)
 		goto failed_req_irq;
 	}
 
-	/*
-	 * Denali Controller only support 15bit and 8bit ECC in MRST,
-	 * so just let controller do 15bit ECC for MLC and 8bit ECC for
-	 * SLC if possible.
-	 * */
-	if (!nand_is_slc(chip) &&
-			mtd->oobsize > denali->bbtskipbytes +
-			ECC_15BITS * (mtd->writesize / chip->ecc.size)) {
-		/* if MLC OOB size is large enough, use 15bit ECC*/
-		chip->ecc.strength = 15;
-		chip->ecc.bytes = ECC_15BITS;
-		iowrite32(15, denali->flash_reg + ECC_CORRECTION);
-	} else if (mtd->oobsize <
-		   denali->bbtskipbytes + ECC_8BITS * (mtd->writesize / chip->ecc.size)) {
-		pr_err("Your NAND chip OOB is not large enough to contain 8bit ECC correction codes");
+	ret = denali_set_max_ecc_strength(denali);
+	if (ret)
 		goto failed_req_irq;
-	} else {
-		chip->ecc.strength = 8;
-		chip->ecc.bytes = ECC_8BITS;
-		iowrite32(8, denali->flash_reg + ECC_CORRECTION);
-	}
+
+	chip->ecc.bytes = denali_calc_ecc_bytes(chip->ecc.size,
+						chip->ecc.strength);
+
+	iowrite32(chip->ecc.strength, denali->flash_reg + ECC_CORRECTION);
 
 	iowrite32(chip->ecc.size, denali->flash_reg + CFG_DATA_BLOCK_SIZE);
 	iowrite32(chip->ecc.size, denali->flash_reg + CFG_LAST_DATA_BLOCK_SIZE);
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index aa6548a..003d234a 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -353,6 +353,7 @@ struct denali_nand_info {
 	int bbtskipbytes;
 	int max_banks;
 	unsigned int revision;
+	unsigned long ecc_strength_avail;
 	unsigned int caps;
 #define DENALI_CAP_HW_ECC_FIXUP			BIT(0)
 #define DENALI_CAP_DMA_64BIT			BIT(1)
diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c
index 1681a30..c3bc333 100644
--- a/drivers/mtd/nand/denali_dt.c
+++ b/drivers/mtd/nand/denali_dt.c
@@ -31,10 +31,12 @@ struct denali_dt {
 
 struct denali_dt_data {
 	unsigned int revision;
+	unsigned long ecc_strength_avail;
 	unsigned int caps;
 };
 
 static const struct denali_dt_data denali_socfpga_data = {
+	.ecc_strength_avail = BIT(15) | BIT(8),
 	.caps = DENALI_CAP_HW_ECC_FIXUP |
 		DENALI_CAP_ECC_SIZE_512,
 };
@@ -64,6 +66,7 @@ static int denali_dt_probe(struct platform_device *pdev)
 	data = of_device_get_match_data(&pdev->dev);
 	if (data) {
 		denali->revision = data->revision;
+		denali->ecc_strength_avail = data->ecc_strength_avail;
 		denali->caps = data->caps;
 	}
 
diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/denali_pci.c
index 5202a11..a1ee9f8 100644
--- a/drivers/mtd/nand/denali_pci.c
+++ b/drivers/mtd/nand/denali_pci.c
@@ -85,6 +85,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 		goto failed_remap_reg;
 	}
 
+	denali->ecc_strength_avail = BIT(15) | BIT(8);
 	denali->caps |= DENALI_CAP_ECC_SIZE_512;
 
 	ret = denali_init(denali);
-- 
2.7.4

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

* [RESEND PATCH v2 28/53] mtd: nand: denali: support "nand-ecc-strength" DT property
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
@ 2017-03-23  0:17 ` Masahiro Yamada
  2017-03-23  8:43   ` Boris Brezillon
  2017-03-23  0:17 ` [RESEND PATCH v2 29/53] mtd: nand: denali: remove Toshiba and Hynix specific fixup code Masahiro Yamada
                   ` (24 subsequent siblings)
  25 siblings, 1 reply; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

Historically, this driver tried to choose as big ECC strength as
possible, but it would be reasonable to allow DT to set a particular
ECC strength with "nand-ecc-strength" property.

Going forward, DT platforms should specify "nand-ecc-strength" or
"nand-ecc-maximize" to show the ECC strength strategy explicitly.

If nothing is specified in DT, "nand-ecc-maximize" is implied since
this was the original behavior.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
Acked-by: Rob Herring <robh@kernel.org>
---

Changes in v2:
  - Add available values in the binding document

 Documentation/devicetree/bindings/mtd/denali-nand.txt |  6 ++++++
 drivers/mtd/nand/denali.c                             | 18 ++++++++++++++++--
 drivers/mtd/nand/denali_pci.c                         |  1 +
 3 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/mtd/denali-nand.txt b/Documentation/devicetree/bindings/mtd/denali-nand.txt
index 25313c7..647618e 100644
--- a/Documentation/devicetree/bindings/mtd/denali-nand.txt
+++ b/Documentation/devicetree/bindings/mtd/denali-nand.txt
@@ -11,6 +11,12 @@ Optional properties:
   - nand-ecc-step-size: must be 512 or 1024.  If not specified, default to:
       512   for "altr,socfpga-denali-nand"
     see nand.txt for details.
+  - nand-ecc-strength: see nand.txt for details.  Available values are:
+      8, 15      for "altr,socfpga-denali-nand"
+  - nand-ecc-maximize: see nand.txt for details
+
+Note:
+Either nand-ecc-strength or nand-ecc-maximize should be specified.
 
 The device tree may optionally contain sub-nodes describing partitions of the
 address space. See partition.txt for more detail.
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 96074b8..70e9f06 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -1627,9 +1627,23 @@ int denali_init(struct denali_nand_info *denali)
 		goto failed_req_irq;
 	}
 
-	ret = denali_set_max_ecc_strength(denali);
-	if (ret)
+	if (!chip->ecc.strength && !(chip->ecc.options & NAND_ECC_MAXIMIZE)) {
+		dev_info(denali->dev,
+			 "No ECC strength strategy is specified. Maximizing ECC strength\n");
+		chip->ecc.options |= NAND_ECC_MAXIMIZE;
+	}
+
+	if (chip->ecc.options & NAND_ECC_MAXIMIZE) {
+		ret = denali_set_max_ecc_strength(denali);
+		if (ret)
+			goto failed_req_irq;
+	} else if (!(denali->ecc_strength_avail & BIT(chip->ecc.strength))) {
+		dev_err(denali->dev,
+			"Specified ECC strength (%d) is not supported for this controller.\n",
+			chip->ecc.strength);
+		ret = -EINVAL;
 		goto failed_req_irq;
+	}
 
 	chip->ecc.bytes = denali_calc_ecc_bytes(chip->ecc.size,
 						chip->ecc.strength);
diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/denali_pci.c
index a1ee9f8..a39682a5 100644
--- a/drivers/mtd/nand/denali_pci.c
+++ b/drivers/mtd/nand/denali_pci.c
@@ -87,6 +87,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 
 	denali->ecc_strength_avail = BIT(15) | BIT(8);
 	denali->caps |= DENALI_CAP_ECC_SIZE_512;
+	denali->nand.ecc.options |= NAND_ECC_MAXIMIZE;
 
 	ret = denali_init(denali);
 	if (ret)
-- 
2.7.4

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

* [RESEND PATCH v2 29/53] mtd: nand: denali: remove Toshiba and Hynix specific fixup code
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
  2017-03-23  0:17 ` [RESEND PATCH v2 28/53] mtd: nand: denali: support "nand-ecc-strength" DT property Masahiro Yamada
@ 2017-03-23  0:17 ` Masahiro Yamada
  2017-03-23  0:17 ` [RESEND PATCH v2 30/53] mtd: nand: denali_dt: add compatible strings for UniPhier SoC variants Masahiro Yamada
                   ` (23 subsequent siblings)
  25 siblings, 0 replies; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

The Denali IP can automatically detect device parameters such as
page size, oob size, device width, etc. and this driver currently
relies on it.  However, this hardware function is problematic.

[1] Due to a hardware bug, various misdetected cases are known.
    That is why get_toshiba_nand_para() and get_hynix_nand_para()
    exist to fix-up the misdetected parameters.  It is not realistic
    to add a new NAND device to the *black list* every time we are
    hit by a misdetected case.  We would never be able to guarantee
    that all cases are covered.

[2] Because this feature is unreliable, it is disabled on some
    platforms.

The nand_scan_ident() detects device parameters in a more tested
way.  The hardware should not set the device parameter registers in
a different, unreliable way.  Instead, set the parameters from the
nand_scan_ident() back to the registers.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2: None

 drivers/mtd/nand/denali.c | 40 ++++++----------------------------------
 1 file changed, 6 insertions(+), 34 deletions(-)

diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 70e9f06..fba1908 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -338,36 +338,6 @@ static void get_samsung_nand_para(struct denali_nand_info *denali,
 	}
 }
 
-static void get_toshiba_nand_para(struct denali_nand_info *denali)
-{
-	/*
-	 * Workaround to fix a controller bug which reports a wrong
-	 * spare area size for some kind of Toshiba NAND device
-	 */
-	if ((ioread32(denali->flash_reg + DEVICE_MAIN_AREA_SIZE) == 4096) &&
-		(ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE) == 64))
-		iowrite32(216, denali->flash_reg + DEVICE_SPARE_AREA_SIZE);
-}
-
-static void get_hynix_nand_para(struct denali_nand_info *denali,
-							uint8_t device_id)
-{
-	switch (device_id) {
-	case 0xD5: /* Hynix H27UAG8T2A, H27UBG8U5A or H27UCG8VFA */
-	case 0xD7: /* Hynix H27UDG8VEM, H27UCG8UDM or H27UCG8V5A */
-		iowrite32(128, denali->flash_reg + PAGES_PER_BLOCK);
-		iowrite32(4096, denali->flash_reg + DEVICE_MAIN_AREA_SIZE);
-		iowrite32(224, denali->flash_reg + DEVICE_SPARE_AREA_SIZE);
-		iowrite32(0, denali->flash_reg + DEVICE_WIDTH);
-		break;
-	default:
-		dev_warn(denali->dev,
-			 "Unknown Hynix NAND (Device ID: 0x%x).\n"
-			 "Will use default parameter values instead.\n",
-			 device_id);
-	}
-}
-
 /*
  * determines how many NAND chips are connected to the controller. Note for
  * Intel CE4100 devices we don't support more than one device.
@@ -454,10 +424,6 @@ static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
 			return FAIL;
 	} else if (maf_id == 0xEC) { /* Samsung NAND */
 		get_samsung_nand_para(denali, device_id);
-	} else if (maf_id == 0x98) { /* Toshiba NAND */
-		get_toshiba_nand_para(denali);
-	} else if (maf_id == 0xAD) { /* Hynix NAND */
-		get_hynix_nand_para(denali, device_id);
 	}
 
 	dev_info(denali->dev,
@@ -1649,6 +1615,12 @@ int denali_init(struct denali_nand_info *denali)
 						chip->ecc.strength);
 
 	iowrite32(chip->ecc.strength, denali->flash_reg + ECC_CORRECTION);
+	iowrite32(mtd->erasesize / mtd->writesize,
+		  denali->flash_reg + PAGES_PER_BLOCK);
+	iowrite32(denali->nand.options & NAND_BUSWIDTH_16 ? 1 : 0,
+		  denali->flash_reg + DEVICE_WIDTH);
+	iowrite32(mtd->writesize, denali->flash_reg + DEVICE_MAIN_AREA_SIZE);
+	iowrite32(mtd->oobsize, denali->flash_reg + DEVICE_SPARE_AREA_SIZE);
 
 	iowrite32(chip->ecc.size, denali->flash_reg + CFG_DATA_BLOCK_SIZE);
 	iowrite32(chip->ecc.size, denali->flash_reg + CFG_LAST_DATA_BLOCK_SIZE);
-- 
2.7.4

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

* [RESEND PATCH v2 30/53] mtd: nand: denali_dt: add compatible strings for UniPhier SoC variants
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
  2017-03-23  0:17 ` [RESEND PATCH v2 28/53] mtd: nand: denali: support "nand-ecc-strength" DT property Masahiro Yamada
  2017-03-23  0:17 ` [RESEND PATCH v2 29/53] mtd: nand: denali: remove Toshiba and Hynix specific fixup code Masahiro Yamada
@ 2017-03-23  0:17 ` Masahiro Yamada
  2017-03-23  0:17 ` [RESEND PATCH v2 31/53] mtd: nand: denali: set NAND_ECC_CUSTOM_PAGE_ACCESS Masahiro Yamada
                   ` (22 subsequent siblings)
  25 siblings, 0 replies; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

Add two compatible strings for UniPhier SoCs.

"socionext,uniphier-denali-nand-v5a" is used on UniPhier sLD3, LD4,
Pro4, sLD8 SoCs.

"socionext,uniphier-denali-nand-v5b" is used on UniPhier Pro5, PXs2,
LD6b, LD11, LD20 SoCs.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
  - Change the compatible strings
  - Fix the ecc_strength_capability
  - Override revision number for the newer one

 .../devicetree/bindings/mtd/denali-nand.txt        |  6 ++++++
 drivers/mtd/nand/denali_dt.c                       | 23 ++++++++++++++++++++++
 2 files changed, 29 insertions(+)

diff --git a/Documentation/devicetree/bindings/mtd/denali-nand.txt b/Documentation/devicetree/bindings/mtd/denali-nand.txt
index 647618e..0b08ea5 100644
--- a/Documentation/devicetree/bindings/mtd/denali-nand.txt
+++ b/Documentation/devicetree/bindings/mtd/denali-nand.txt
@@ -3,6 +3,8 @@
 Required properties:
   - compatible : should be one of the following:
       "altr,socfpga-denali-nand"            - for Altera SOCFPGA
+      "socionext,uniphier-denali-nand-v5a"  - for Socionext UniPhier (v5a)
+      "socionext,uniphier-denali-nand-v5b"  - for Socionext UniPhier (v5b)
   - reg : should contain registers location and length for data and reg.
   - reg-names: Should contain the reg names "nand_data" and "denali_reg"
   - interrupts : The interrupt number.
@@ -10,9 +12,13 @@ Required properties:
 Optional properties:
   - nand-ecc-step-size: must be 512 or 1024.  If not specified, default to:
       512   for "altr,socfpga-denali-nand"
+      1024  for "socionext,uniphier-denali-nand-v5a"
+      1024  for "socionext,uniphier-denali-nand-v5b"
     see nand.txt for details.
   - nand-ecc-strength: see nand.txt for details.  Available values are:
       8, 15      for "altr,socfpga-denali-nand"
+      8, 16, 24  for "socionext,uniphier-denali-nand-v5a"
+      8, 16      for "socionext,uniphier-denali-nand-v5b"
   - nand-ecc-maximize: see nand.txt for details
 
 Note:
diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c
index c3bc333..1f2f68a 100644
--- a/drivers/mtd/nand/denali_dt.c
+++ b/drivers/mtd/nand/denali_dt.c
@@ -41,11 +41,34 @@ static const struct denali_dt_data denali_socfpga_data = {
 		DENALI_CAP_ECC_SIZE_512,
 };
 
+static const struct denali_dt_data denali_uniphier_v5a_data = {
+	.ecc_strength_avail = BIT(24) | BIT(16) | BIT(8),
+	.caps = DENALI_CAP_HW_ECC_FIXUP |
+		DENALI_CAP_DMA_64BIT |
+		DENALI_CAP_ECC_SIZE_1024,
+};
+
+static const struct denali_dt_data denali_uniphier_v5b_data = {
+	.revision = 0x0501,
+	.ecc_strength_avail = BIT(16) | BIT(8),
+	.caps = DENALI_CAP_HW_ECC_FIXUP |
+		DENALI_CAP_DMA_64BIT |
+		DENALI_CAP_ECC_SIZE_1024,
+};
+
 static const struct of_device_id denali_nand_dt_ids[] = {
 	{
 		.compatible = "altr,socfpga-denali-nand",
 		.data = &denali_socfpga_data,
 	},
+	{
+		.compatible = "socionext,uniphier-denali-nand-v5a",
+		.data = &denali_uniphier_v5a_data,
+	},
+	{
+		.compatible = "socionext,uniphier-denali-nand-v5b",
+		.data = &denali_uniphier_v5b_data,
+	},
 	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
-- 
2.7.4

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

* [RESEND PATCH v2 31/53] mtd: nand: denali: set NAND_ECC_CUSTOM_PAGE_ACCESS
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
                   ` (2 preceding siblings ...)
  2017-03-23  0:17 ` [RESEND PATCH v2 30/53] mtd: nand: denali_dt: add compatible strings for UniPhier SoC variants Masahiro Yamada
@ 2017-03-23  0:17 ` Masahiro Yamada
  2017-03-23  0:17 ` [RESEND PATCH v2 32/53] mtd: nand: denali: do not propagate NAND_STATUS_FAIL to waitfunc() Masahiro Yamada
                   ` (21 subsequent siblings)
  25 siblings, 0 replies; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

The denali_cmdfunc() actually does nothing variable for
NAND_CMD_{PAGEPROG,READ0,SEQIN}.

For NAND_CMD_{READ0,SEQIN}, it copies "page" to "denali->page", then
denali_read_page() and denali_read_page_raw() compare them to check
if the NAND framework called the callbacks in correct order.
(Inconsistetly, this check is missing from the denali_write_page()
and denali_write_page_raw().)

The framework is widely tested by many drivers, so this kind of
sanity check is unneeded.  The Denali controller is equipped with
high level interface for read/write, so let's skip unneeded call
of cmdfunc().

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
  - Newly added

 drivers/mtd/nand/denali.c | 29 ++++++++---------------------
 1 file changed, 8 insertions(+), 21 deletions(-)

diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index fba1908..ca00f22 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -989,7 +989,7 @@ static void denali_setup_dma(struct denali_nand_info *denali, int op)
  * configuration details.
  */
 static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
-			const uint8_t *buf, bool raw_xfer)
+			const uint8_t *buf, int page, bool raw_xfer)
 {
 	struct denali_nand_info *denali = mtd_to_denali(mtd);
 	dma_addr_t addr = denali->buf.dma_buf;
@@ -997,6 +997,8 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
 	uint32_t irq_status;
 	uint32_t irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
 
+	denali->page = page;
+
 	/*
 	 * if it is a raw xfer, we want to disable ecc and send the spare area.
 	 * !raw_xfer - enable ecc
@@ -1050,7 +1052,7 @@ static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
 	 * for regular page writes, we let HW handle all the ECC
 	 * data written to the device.
 	 */
-	return write_page(mtd, chip, buf, false);
+	return write_page(mtd, chip, buf, page, false);
 }
 
 /*
@@ -1066,7 +1068,7 @@ static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
 	 * for raw page writes, we want to disable ECC and simply write
 	 * whatever data is in the buffer.
 	 */
-	return write_page(mtd, chip, buf, true);
+	return write_page(mtd, chip, buf, page, true);
 }
 
 static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
@@ -1095,12 +1097,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
 				INTR__ECC_TRANSACTION_DONE | INTR__ECC_ERR;
 	int stat = 0;
 
-	if (page != denali->page) {
-		dev_err(denali->dev,
-			"IN %s: page %d is not equal to denali->page %d",
-			__func__, page, denali->page);
-		BUG();
-	}
+	denali->page = page;
 
 	setup_ecc_for_xfer(denali, true, false);
 
@@ -1154,12 +1151,7 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
 	size_t size = mtd->writesize + mtd->oobsize;
 	uint32_t irq_mask = INTR__DMA_CMD_COMP;
 
-	if (page != denali->page) {
-		dev_err(denali->dev,
-			"IN %s: page %d is not equal to denali->page %d",
-			__func__, page, denali->page);
-		BUG();
-	}
+	denali->page = page;
 
 	setup_ecc_for_xfer(denali, false, true);
 	denali_enable_dma(denali, true);
@@ -1238,8 +1230,6 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
 	int i;
 
 	switch (cmd) {
-	case NAND_CMD_PAGEPROG:
-		break;
 	case NAND_CMD_STATUS:
 		read_status(denali);
 		break;
@@ -1259,10 +1249,6 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
 			write_byte_to_buf(denali, id);
 		}
 		break;
-	case NAND_CMD_READ0:
-	case NAND_CMD_SEQIN:
-		denali->page = page;
-		break;
 	case NAND_CMD_RESET:
 		reset_bank(denali);
 		break;
@@ -1630,6 +1616,7 @@ int denali_init(struct denali_nand_info *denali)
 
 	mtd_set_ooblayout(mtd, &denali_ooblayout_ops);
 
+	chip->ecc.options |= NAND_ECC_CUSTOM_PAGE_ACCESS;
 	chip->ecc.read_page = denali_read_page;
 	chip->ecc.read_page_raw = denali_read_page_raw;
 	chip->ecc.write_page = denali_write_page;
-- 
2.7.4

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

* [RESEND PATCH v2 32/53] mtd: nand: denali: do not propagate NAND_STATUS_FAIL to waitfunc()
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
                   ` (3 preceding siblings ...)
  2017-03-23  0:17 ` [RESEND PATCH v2 31/53] mtd: nand: denali: set NAND_ECC_CUSTOM_PAGE_ACCESS Masahiro Yamada
@ 2017-03-23  0:17 ` Masahiro Yamada
  2017-03-23  0:17 ` [RESEND PATCH v2 33/53] mtd: nand: denali: use BIT() and GENMASK() for register macros Masahiro Yamada
                   ` (20 subsequent siblings)
  25 siblings, 0 replies; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

Currently, the error handling of denali_write_page(_raw) is a bit
complicated.  If the program command fails, NAND_STATUS_FAIL is set
to the driver internal denali->status, then read out later by
denali_waitfunc().

We can avoid it by exploiting the nand_write_page() implementation.
If chip->ecc.write_page(_raw) returns negative code (i.e. -EIO), it
errors out immediately.  This gives the same result as returning
NAND_STATUS_FAIL from chip->waitfunc.  In either way, -EIO is
returned to the upper MTD layer.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
  - Newly added

 drivers/mtd/nand/denali.c | 12 ++++--------
 drivers/mtd/nand/denali.h |  1 -
 2 files changed, 4 insertions(+), 9 deletions(-)

diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index ca00f22..9be9a1a 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -996,6 +996,7 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
 	size_t size = mtd->writesize + mtd->oobsize;
 	uint32_t irq_status;
 	uint32_t irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
+	int ret = 0;
 
 	denali->page = page;
 
@@ -1029,13 +1030,13 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
 	if (irq_status == 0) {
 		dev_err(denali->dev, "timeout on write_page (type = %d)\n",
 			raw_xfer);
-		denali->status = NAND_STATUS_FAIL;
+		ret = -EIO;
 	}
 
 	denali_enable_dma(denali, false);
 	dma_sync_single_for_cpu(denali->dev, addr, size, DMA_TO_DEVICE);
 
-	return 0;
+	return ret;
 }
 
 /* NAND core entry points */
@@ -1196,12 +1197,7 @@ static void denali_select_chip(struct mtd_info *mtd, int chip)
 
 static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
 {
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
-	int status = denali->status;
-
-	denali->status = 0;
-
-	return status;
+	return 0;
 }
 
 static int denali_erase(struct mtd_info *mtd, int page)
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index 003d234a..14e801c 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -334,7 +334,6 @@ struct nand_buf {
 struct denali_nand_info {
 	struct nand_chip nand;
 	int flash_bank; /* currently selected chip */
-	int status;
 	int platform;
 	struct nand_buf buf;
 	struct device *dev;
-- 
2.7.4

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

* [RESEND PATCH v2 33/53] mtd: nand: denali: use BIT() and GENMASK() for register macros
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
                   ` (4 preceding siblings ...)
  2017-03-23  0:17 ` [RESEND PATCH v2 32/53] mtd: nand: denali: do not propagate NAND_STATUS_FAIL to waitfunc() Masahiro Yamada
@ 2017-03-23  0:17 ` Masahiro Yamada
  2017-03-23  0:17 ` [RESEND PATCH v2 34/53] mtd: nand: denali: remove unneeded find_valid_banks() Masahiro Yamada
                   ` (19 subsequent siblings)
  25 siblings, 0 replies; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

Make register field macros more readable especially for comparing
the macros and the register description in the Denali User's Guide.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
  - Newly added

 drivers/mtd/nand/denali.h | 244 ++++++++++++++++++++++------------------------
 1 file changed, 119 insertions(+), 125 deletions(-)

diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index 14e801c..2fca7a5 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -24,251 +24,245 @@
 #include <linux/mtd/nand.h>
 
 #define DEVICE_RESET				0x0
-#define     DEVICE_RESET__BANK0				0x0001
-#define     DEVICE_RESET__BANK1				0x0002
-#define     DEVICE_RESET__BANK2				0x0004
-#define     DEVICE_RESET__BANK3				0x0008
+#define     DEVICE_RESET__BANK(bank)			BIT(bank)
 
 #define TRANSFER_SPARE_REG			0x10
-#define     TRANSFER_SPARE_REG__FLAG			0x0001
+#define     TRANSFER_SPARE_REG__FLAG			BIT(0)
 
 #define LOAD_WAIT_CNT				0x20
-#define     LOAD_WAIT_CNT__VALUE			0xffff
+#define     LOAD_WAIT_CNT__VALUE			GENMASK(15, 0)
 
 #define PROGRAM_WAIT_CNT			0x30
-#define     PROGRAM_WAIT_CNT__VALUE			0xffff
+#define     PROGRAM_WAIT_CNT__VALUE			GENMASK(15, 0)
 
 #define ERASE_WAIT_CNT				0x40
-#define     ERASE_WAIT_CNT__VALUE			0xffff
+#define     ERASE_WAIT_CNT__VALUE			GENMASK(15, 0)
 
 #define INT_MON_CYCCNT				0x50
-#define     INT_MON_CYCCNT__VALUE			0xffff
+#define     INT_MON_CYCCNT__VALUE			GENMASK(15, 0)
 
 #define RB_PIN_ENABLED				0x60
-#define     RB_PIN_ENABLED__BANK0			0x0001
-#define     RB_PIN_ENABLED__BANK1			0x0002
-#define     RB_PIN_ENABLED__BANK2			0x0004
-#define     RB_PIN_ENABLED__BANK3			0x0008
+#define     RB_PIN_ENABLED__BANK(bank)			BIT(bank)
 
 #define MULTIPLANE_OPERATION			0x70
-#define     MULTIPLANE_OPERATION__FLAG			0x0001
+#define     MULTIPLANE_OPERATION__FLAG			BIT(0)
 
 #define MULTIPLANE_READ_ENABLE			0x80
-#define     MULTIPLANE_READ_ENABLE__FLAG		0x0001
+#define     MULTIPLANE_READ_ENABLE__FLAG		BIT(0)
 
 #define COPYBACK_DISABLE			0x90
-#define     COPYBACK_DISABLE__FLAG			0x0001
+#define     COPYBACK_DISABLE__FLAG			BIT(0)
 
 #define CACHE_WRITE_ENABLE			0xa0
-#define     CACHE_WRITE_ENABLE__FLAG			0x0001
+#define     CACHE_WRITE_ENABLE__FLAG			BIT(0)
 
 #define CACHE_READ_ENABLE			0xb0
-#define     CACHE_READ_ENABLE__FLAG			0x0001
+#define     CACHE_READ_ENABLE__FLAG			BIT(0)
 
 #define PREFETCH_MODE				0xc0
-#define     PREFETCH_MODE__PREFETCH_EN			0x0001
-#define     PREFETCH_MODE__PREFETCH_BURST_LENGTH	0xfff0
+#define     PREFETCH_MODE__PREFETCH_EN			BIT(0)
+#define     PREFETCH_MODE__PREFETCH_BURST_LENGTH	GENMASK(15, 4)
 
 #define CHIP_ENABLE_DONT_CARE			0xd0
-#define     CHIP_EN_DONT_CARE__FLAG			0x01
+#define     CHIP_EN_DONT_CARE__FLAG			BIT(0)
 
 #define ECC_ENABLE				0xe0
-#define     ECC_ENABLE__FLAG				0x0001
+#define     ECC_ENABLE__FLAG				BIT(0)
 
 #define GLOBAL_INT_ENABLE			0xf0
-#define     GLOBAL_INT_EN_FLAG				0x01
+#define     GLOBAL_INT_EN_FLAG				BIT(0)
 
 #define WE_2_RE					0x100
-#define     WE_2_RE__VALUE				0x003f
+#define     WE_2_RE__VALUE				GENMASK(5, 0)
 
 #define ADDR_2_DATA				0x110
-#define     ADDR_2_DATA__VALUE				0x003f
+#define     ADDR_2_DATA__VALUE				GENMASK(5, 0)
 
 #define RE_2_WE					0x120
-#define     RE_2_WE__VALUE				0x003f
+#define     RE_2_WE__VALUE				GENMASK(5, 0)
 
 #define ACC_CLKS				0x130
-#define     ACC_CLKS__VALUE				0x000f
+#define     ACC_CLKS__VALUE				GENMASK(3, 0)
 
 #define NUMBER_OF_PLANES			0x140
-#define     NUMBER_OF_PLANES__VALUE			0x0007
+#define     NUMBER_OF_PLANES__VALUE			GENMASK(2, 0)
 
 #define PAGES_PER_BLOCK				0x150
-#define     PAGES_PER_BLOCK__VALUE			0xffff
+#define     PAGES_PER_BLOCK__VALUE			GENMASK(15, 0)
 
 #define DEVICE_WIDTH				0x160
-#define     DEVICE_WIDTH__VALUE				0x0003
+#define     DEVICE_WIDTH__VALUE				GENMASK(1, 0)
 
 #define DEVICE_MAIN_AREA_SIZE			0x170
-#define     DEVICE_MAIN_AREA_SIZE__VALUE		0xffff
+#define     DEVICE_MAIN_AREA_SIZE__VALUE		GENMASK(15, 0)
 
 #define DEVICE_SPARE_AREA_SIZE			0x180
-#define     DEVICE_SPARE_AREA_SIZE__VALUE		0xffff
+#define     DEVICE_SPARE_AREA_SIZE__VALUE		GENMASK(15, 0)
 
 #define TWO_ROW_ADDR_CYCLES			0x190
-#define     TWO_ROW_ADDR_CYCLES__FLAG			0x0001
+#define     TWO_ROW_ADDR_CYCLES__FLAG			BIT(0)
 
 #define MULTIPLANE_ADDR_RESTRICT		0x1a0
-#define     MULTIPLANE_ADDR_RESTRICT__FLAG		0x0001
+#define     MULTIPLANE_ADDR_RESTRICT__FLAG		BIT(0)
 
 #define ECC_CORRECTION				0x1b0
-#define     ECC_CORRECTION__VALUE			0x001f
+#define     ECC_CORRECTION__VALUE			GENMASK(4, 0)
 
 #define READ_MODE				0x1c0
-#define     READ_MODE__VALUE				0x000f
+#define     READ_MODE__VALUE				GENMASK(3, 0)
 
 #define WRITE_MODE				0x1d0
-#define     WRITE_MODE__VALUE				0x000f
+#define     WRITE_MODE__VALUE				GENMASK(3, 0)
 
 #define COPYBACK_MODE				0x1e0
-#define     COPYBACK_MODE__VALUE			0x000f
+#define     COPYBACK_MODE__VALUE			GENMASK(3, 0)
 
 #define RDWR_EN_LO_CNT				0x1f0
-#define     RDWR_EN_LO_CNT__VALUE			0x001f
+#define     RDWR_EN_LO_CNT__VALUE			GENMASK(4, 0)
 
 #define RDWR_EN_HI_CNT				0x200
-#define     RDWR_EN_HI_CNT__VALUE			0x001f
+#define     RDWR_EN_HI_CNT__VALUE			GENMASK(4, 0)
 
 #define MAX_RD_DELAY				0x210
-#define     MAX_RD_DELAY__VALUE				0x000f
+#define     MAX_RD_DELAY__VALUE				GENMASK(3, 0)
 
 #define CS_SETUP_CNT				0x220
-#define     CS_SETUP_CNT__VALUE				0x001f
+#define     CS_SETUP_CNT__VALUE				GENMASK(4, 0)
 
 #define SPARE_AREA_SKIP_BYTES			0x230
-#define     SPARE_AREA_SKIP_BYTES__VALUE		0x003f
+#define     SPARE_AREA_SKIP_BYTES__VALUE		GENMASK(5, 0)
 
 #define SPARE_AREA_MARKER			0x240
-#define     SPARE_AREA_MARKER__VALUE			0xffff
+#define     SPARE_AREA_MARKER__VALUE			GENMASK(15, 0)
 
 #define DEVICES_CONNECTED			0x250
-#define     DEVICES_CONNECTED__VALUE			0x0007
+#define     DEVICES_CONNECTED__VALUE			GENMASK(2, 0)
 
 #define DIE_MASK				0x260
-#define     DIE_MASK__VALUE				0x00ff
+#define     DIE_MASK__VALUE				GENMASK(7, 0)
 
 #define FIRST_BLOCK_OF_NEXT_PLANE		0x270
-#define     FIRST_BLOCK_OF_NEXT_PLANE__VALUE		0xffff
+#define     FIRST_BLOCK_OF_NEXT_PLANE__VALUE		GENMASK(15, 0)
 
 #define WRITE_PROTECT				0x280
-#define     WRITE_PROTECT__FLAG				0x0001
+#define     WRITE_PROTECT__FLAG				BIT(0)
 
 #define RE_2_RE					0x290
-#define     RE_2_RE__VALUE				0x003f
+#define     RE_2_RE__VALUE				GENMASK(5, 0)
 
 #define MANUFACTURER_ID				0x300
-#define     MANUFACTURER_ID__VALUE			0x00ff
+#define     MANUFACTURER_ID__VALUE			GENMASK(7, 0)
 
 #define DEVICE_ID				0x310
-#define     DEVICE_ID__VALUE				0x00ff
+#define     DEVICE_ID__VALUE				GENMASK(7, 0)
 
 #define DEVICE_PARAM_0				0x320
-#define     DEVICE_PARAM_0__VALUE			0x00ff
+#define     DEVICE_PARAM_0__VALUE			GENMASK(7, 0)
 
 #define DEVICE_PARAM_1				0x330
-#define     DEVICE_PARAM_1__VALUE			0x00ff
+#define     DEVICE_PARAM_1__VALUE			GENMASK(7, 0)
 
 #define DEVICE_PARAM_2				0x340
-#define     DEVICE_PARAM_2__VALUE			0x00ff
+#define     DEVICE_PARAM_2__VALUE			GENMASK(7, 0)
 
 #define LOGICAL_PAGE_DATA_SIZE			0x350
-#define     LOGICAL_PAGE_DATA_SIZE__VALUE		0xffff
+#define     LOGICAL_PAGE_DATA_SIZE__VALUE		GENMASK(15, 0)
 
 #define LOGICAL_PAGE_SPARE_SIZE			0x360
-#define     LOGICAL_PAGE_SPARE_SIZE__VALUE		0xffff
+#define     LOGICAL_PAGE_SPARE_SIZE__VALUE		GENMASK(15, 0)
 
 #define REVISION				0x370
-#define     REVISION__VALUE				0xffff
+#define     REVISION__VALUE				GENMASK(15, 0)
 
 #define ONFI_DEVICE_FEATURES			0x380
-#define     ONFI_DEVICE_FEATURES__VALUE			0x003f
+#define     ONFI_DEVICE_FEATURES__VALUE			GENMASK(5, 0)
 
 #define ONFI_OPTIONAL_COMMANDS			0x390
-#define     ONFI_OPTIONAL_COMMANDS__VALUE		0x003f
+#define     ONFI_OPTIONAL_COMMANDS__VALUE		GENMASK(5, 0)
 
 #define ONFI_TIMING_MODE			0x3a0
-#define     ONFI_TIMING_MODE__VALUE			0x003f
+#define     ONFI_TIMING_MODE__VALUE			GENMASK(5, 0)
 
 #define ONFI_PGM_CACHE_TIMING_MODE		0x3b0
-#define     ONFI_PGM_CACHE_TIMING_MODE__VALUE		0x003f
+#define     ONFI_PGM_CACHE_TIMING_MODE__VALUE		GENMASK(5, 0)
 
 #define ONFI_DEVICE_NO_OF_LUNS			0x3c0
-#define     ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS		0x00ff
-#define     ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE		0x0100
+#define     ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS		GENMASK(7, 0)
+#define     ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE		BIT(8)
 
 #define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L	0x3d0
-#define     ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L__VALUE	0xffff
+#define     ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L__VALUE	GENMASK(15, 0)
 
 #define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U	0x3e0
-#define     ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U__VALUE	0xffff
-
-#define FEATURES					0x3f0
-#define     FEATURES__N_BANKS				0x0003
-#define     FEATURES__ECC_MAX_ERR			0x003c
-#define     FEATURES__DMA				0x0040
-#define     FEATURES__CMD_DMA				0x0080
-#define     FEATURES__PARTITION				0x0100
-#define     FEATURES__XDMA_SIDEBAND			0x0200
-#define     FEATURES__GPREG				0x0400
-#define     FEATURES__INDEX_ADDR			0x0800
+#define     ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U__VALUE	GENMASK(15, 0)
+
+#define FEATURES				0x3f0
+#define     FEATURES__N_BANKS				GENMASK(1, 0)
+#define     FEATURES__ECC_MAX_ERR			GENMASK(5, 2)
+#define     FEATURES__DMA				BIT(6)
+#define     FEATURES__CMD_DMA				BIT(7)
+#define     FEATURES__PARTITION				BIT(8)
+#define     FEATURES__XDMA_SIDEBAND			BIT(9)
+#define     FEATURES__GPREG				BIT(10)
+#define     FEATURES__INDEX_ADDR			BIT(11)
 
 #define TRANSFER_MODE				0x400
-#define     TRANSFER_MODE__VALUE			0x0003
+#define     TRANSFER_MODE__VALUE			GENMASK(1, 0)
 
-#define INTR_STATUS(__bank)	(0x410 + ((__bank) * 0x50))
-#define INTR_EN(__bank)		(0x420 + ((__bank) * 0x50))
+#define INTR_STATUS(bank)			(0x410 + (bank) * 0x50)
+#define INTR_EN(bank)				(0x420 + (bank) * 0x50)
 /*
  * Some versions of the IP have the ECC fixup handled in hardware.  In this
  * configuration we only get interrupted when the error is uncorrectable.
  * Unfortunately this bit replaces INTR_STATUS__ECC_TRANSACTION_DONE from the
  * old IP.
  */
-#define     INTR__ECC_UNCOR_ERR				0x0001
-#define     INTR__ECC_TRANSACTION_DONE			0x0001
-#define     INTR__ECC_ERR				0x0002
-#define     INTR__DMA_CMD_COMP				0x0004
-#define     INTR__TIME_OUT				0x0008
-#define     INTR__PROGRAM_FAIL				0x0010
-#define     INTR__ERASE_FAIL				0x0020
-#define     INTR__LOAD_COMP				0x0040
-#define     INTR__PROGRAM_COMP				0x0080
-#define     INTR__ERASE_COMP				0x0100
-#define     INTR__PIPE_CPYBCK_CMD_COMP			0x0200
-#define     INTR__LOCKED_BLK				0x0400
-#define     INTR__UNSUP_CMD				0x0800
-#define     INTR__INT_ACT				0x1000
-#define     INTR__RST_COMP				0x2000
-#define     INTR__PIPE_CMD_ERR				0x4000
-#define     INTR__PAGE_XFER_INC				0x8000
-
-#define PAGE_CNT(__bank)	(0x430 + ((__bank) * 0x50))
-#define ERR_PAGE_ADDR(__bank)	(0x440 + ((__bank) * 0x50))
-#define ERR_BLOCK_ADDR(__bank)	(0x450 + ((__bank) * 0x50))
+#define     INTR__ECC_UNCOR_ERR				BIT(0)
+#define     INTR__ECC_TRANSACTION_DONE			BIT(0)
+#define     INTR__ECC_ERR				BIT(1)
+#define     INTR__DMA_CMD_COMP				BIT(2)
+#define     INTR__TIME_OUT				BIT(3)
+#define     INTR__PROGRAM_FAIL				BIT(4)
+#define     INTR__ERASE_FAIL				BIT(5)
+#define     INTR__LOAD_COMP				BIT(6)
+#define     INTR__PROGRAM_COMP				BIT(7)
+#define     INTR__ERASE_COMP				BIT(8)
+#define     INTR__PIPE_CPYBCK_CMD_COMP			BIT(9)
+#define     INTR__LOCKED_BLK				BIT(10)
+#define     INTR__UNSUP_CMD				BIT(11)
+#define     INTR__INT_ACT				BIT(12)
+#define     INTR__RST_COMP				BIT(13)
+#define     INTR__PIPE_CMD_ERR				BIT(14)
+#define     INTR__PAGE_XFER_INC				BIT(15)
+
+#define PAGE_CNT(bank)				(0x430 + (bank) * 0x50)
+#define ERR_PAGE_ADDR(bank)			(0x440 + (bank) * 0x50)
+#define ERR_BLOCK_ADDR(bank)			(0x450 + (bank) * 0x50)
 
 #define ECC_THRESHOLD				0x600
-#define     ECC_THRESHOLD__VALUE			0x03ff
+#define     ECC_THRESHOLD__VALUE			GENMASK(9, 0)
 
 #define ECC_ERROR_BLOCK_ADDRESS			0x610
-#define     ECC_ERROR_BLOCK_ADDRESS__VALUE		0xffff
+#define     ECC_ERROR_BLOCK_ADDRESS__VALUE		GENMASK(15, 0)
 
 #define ECC_ERROR_PAGE_ADDRESS			0x620
-#define     ECC_ERROR_PAGE_ADDRESS__VALUE		0x0fff
-#define     ECC_ERROR_PAGE_ADDRESS__BANK		0xf000
+#define     ECC_ERROR_PAGE_ADDRESS__VALUE		GENMASK(11, 0)
+#define     ECC_ERROR_PAGE_ADDRESS__BANK		GENMASK(15, 12)
 
 #define ECC_ERROR_ADDRESS			0x630
-#define     ECC_ERROR_ADDRESS__OFFSET			0x0fff
-#define     ECC_ERROR_ADDRESS__SECTOR_NR		0xf000
+#define     ECC_ERROR_ADDRESS__OFFSET			GENMASK(11, 0)
+#define     ECC_ERROR_ADDRESS__SECTOR_NR		GENMASK(15, 12)
 
 #define ERR_CORRECTION_INFO			0x640
-#define     ERR_CORRECTION_INFO__BYTEMASK		0x00ff
-#define     ERR_CORRECTION_INFO__DEVICE_NR		0x0f00
-#define     ERR_CORRECTION_INFO__ERROR_TYPE		0x4000
-#define     ERR_CORRECTION_INFO__LAST_ERR_INFO		0x8000
+#define     ERR_CORRECTION_INFO__BYTEMASK		GENMASK(7, 0)
+#define     ERR_CORRECTION_INFO__DEVICE_NR		GENMASK(11, 8)
+#define     ERR_CORRECTION_INFO__ERROR_TYPE		BIT(14)
+#define     ERR_CORRECTION_INFO__LAST_ERR_INFO		BIT(15)
 
 #define ECC_COR_INFO(bank)			(0x650 + (bank) / 2 * 0x10)
 #define     ECC_COR_INFO__SHIFT(bank)			((bank) % 2 * 8)
-#define     ECC_COR_INFO__MAX_ERRORS			0x007f
-#define     ECC_COR_INFO__UNCOR_ERR			0x0080
+#define     ECC_COR_INFO__MAX_ERRORS			GENMASK(6, 0)
+#define     ECC_COR_INFO__UNCOR_ERR			BIT(7)
 
 #define CFG_DATA_BLOCK_SIZE			0x6b0
 
@@ -279,31 +273,31 @@
 #define CFG_META_DATA_SIZE			0x6e0
 
 #define DMA_ENABLE				0x700
-#define     DMA_ENABLE__FLAG				0x0001
+#define     DMA_ENABLE__FLAG				BIT(0)
 
 #define IGNORE_ECC_DONE				0x710
-#define     IGNORE_ECC_DONE__FLAG			0x0001
+#define     IGNORE_ECC_DONE__FLAG			BIT(0)
 
 #define DMA_INTR				0x720
 #define DMA_INTR_EN				0x730
-#define     DMA_INTR__TARGET_ERROR			0x0001
-#define     DMA_INTR__DESC_COMP_CHANNEL0		0x0002
-#define     DMA_INTR__DESC_COMP_CHANNEL1		0x0004
-#define     DMA_INTR__DESC_COMP_CHANNEL2		0x0008
-#define     DMA_INTR__DESC_COMP_CHANNEL3		0x0010
-#define     DMA_INTR__MEMCOPY_DESC_COMP			0x0020
+#define     DMA_INTR__TARGET_ERROR			BIT(0)
+#define     DMA_INTR__DESC_COMP_CHANNEL0		BIT(1)
+#define     DMA_INTR__DESC_COMP_CHANNEL1		BIT(2)
+#define     DMA_INTR__DESC_COMP_CHANNEL2		BIT(3)
+#define     DMA_INTR__DESC_COMP_CHANNEL3		BIT(4)
+#define     DMA_INTR__MEMCOPY_DESC_COMP			BIT(5)
 
 #define TARGET_ERR_ADDR_LO			0x740
-#define     TARGET_ERR_ADDR_LO__VALUE			0xffff
+#define     TARGET_ERR_ADDR_LO__VALUE			GENMASK(15, 0)
 
 #define TARGET_ERR_ADDR_HI			0x750
-#define     TARGET_ERR_ADDR_HI__VALUE			0xffff
+#define     TARGET_ERR_ADDR_HI__VALUE			GENMASK(15, 0)
 
 #define CHNL_ACTIVE				0x760
-#define     CHNL_ACTIVE__CHANNEL0			0x0001
-#define     CHNL_ACTIVE__CHANNEL1			0x0002
-#define     CHNL_ACTIVE__CHANNEL2			0x0004
-#define     CHNL_ACTIVE__CHANNEL3			0x0008
+#define     CHNL_ACTIVE__CHANNEL0			BIT(0)
+#define     CHNL_ACTIVE__CHANNEL1			BIT(1)
+#define     CHNL_ACTIVE__CHANNEL2			BIT(2)
+#define     CHNL_ACTIVE__CHANNEL3			BIT(3)
 
 #define FAIL 1                  /*failed flag*/
 #define PASS 0                  /*success flag*/
-- 
2.7.4

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

* [RESEND PATCH v2 34/53] mtd: nand: denali: remove unneeded find_valid_banks()
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
                   ` (5 preceding siblings ...)
  2017-03-23  0:17 ` [RESEND PATCH v2 33/53] mtd: nand: denali: use BIT() and GENMASK() for register macros Masahiro Yamada
@ 2017-03-23  0:17 ` Masahiro Yamada
  2017-03-23  0:17 ` [RESEND PATCH v2 35/53] mtd: nand: denali: handle timing parameters by setup_data_interface() Masahiro Yamada
                   ` (18 subsequent siblings)
  25 siblings, 0 replies; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

The function find_valid_banks() issues the Read ID (0x90) command,
then compares the first byte (Manufacturer ID) of each bank with
the one of bank0.

This is equivalent to what nand_scan_ident() does.  The number of
chips is detected there, so this is unneeded.

What is worse for find_valid_banks() is that, if multiple chips are
connected to INTEL_CE4100 platform, it crashes the kernel by BUG().
This is what we should avoid.  This function is just harmful and
unneeded.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
  - Newly added

 drivers/mtd/nand/denali.c | 47 -----------------------------------------------
 drivers/mtd/nand/denali.h |  1 -
 2 files changed, 48 deletions(-)

diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 9be9a1a..bd09b7c 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -339,51 +339,6 @@ static void get_samsung_nand_para(struct denali_nand_info *denali,
 }
 
 /*
- * determines how many NAND chips are connected to the controller. Note for
- * Intel CE4100 devices we don't support more than one device.
- */
-static void find_valid_banks(struct denali_nand_info *denali)
-{
-	uint32_t id[denali->max_banks];
-	int i;
-
-	denali->total_used_banks = 1;
-	for (i = 0; i < denali->max_banks; i++) {
-		index_addr(denali, MODE_11 | (i << 24) | 0, 0x90);
-		index_addr(denali, MODE_11 | (i << 24) | 1, 0);
-		index_addr_read_data(denali, MODE_11 | (i << 24) | 2, &id[i]);
-
-		dev_dbg(denali->dev,
-			"Return 1st ID for bank[%d]: %x\n", i, id[i]);
-
-		if (i == 0) {
-			if (!(id[i] & 0x0ff))
-				break; /* WTF? */
-		} else {
-			if ((id[i] & 0x0ff) == (id[0] & 0x0ff))
-				denali->total_used_banks++;
-			else
-				break;
-		}
-	}
-
-	if (denali->platform == INTEL_CE4100) {
-		/*
-		 * Platform limitations of the CE4100 device limit
-		 * users to a single chip solution for NAND.
-		 * Multichip support is not enabled.
-		 */
-		if (denali->total_used_banks != 1) {
-			dev_err(denali->dev,
-				"Sorry, Intel CE4100 only supports a single NAND device.\n");
-			BUG();
-		}
-	}
-	dev_dbg(denali->dev,
-		"denali->total_used_banks: %d\n", denali->total_used_banks);
-}
-
-/*
  * Use the configuration feature register to determine the maximum number of
  * banks that the hardware supports.
  */
@@ -440,8 +395,6 @@ static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
 			ioread32(denali->flash_reg + RDWR_EN_HI_CNT),
 			ioread32(denali->flash_reg + CS_SETUP_CNT));
 
-	find_valid_banks(denali);
-
 	/*
 	 * If the user specified to override the default timings
 	 * with a specific ONFI mode, we apply those changes here.
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index 2fca7a5..055a53c 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -331,7 +331,6 @@ struct denali_nand_info {
 	int platform;
 	struct nand_buf buf;
 	struct device *dev;
-	int total_used_banks;
 	int page;
 	void __iomem *flash_reg;	/* Register Interface */
 	void __iomem *flash_mem;	/* Host Data/Command Interface */
-- 
2.7.4

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

* [RESEND PATCH v2 35/53] mtd: nand: denali: handle timing parameters by setup_data_interface()
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
                   ` (6 preceding siblings ...)
  2017-03-23  0:17 ` [RESEND PATCH v2 34/53] mtd: nand: denali: remove unneeded find_valid_banks() Masahiro Yamada
@ 2017-03-23  0:17 ` Masahiro Yamada
  2017-03-23  0:17 ` [RESEND PATCH v2 36/53] mtd: nand: denali: remove meaningless pipeline read-ahead operation Masahiro Yamada
                   ` (17 subsequent siblings)
  25 siblings, 0 replies; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

The timing parameter handling in this driver is bad in multiple ways.

[1] The ONFi Timing Mode is specified by the module parameter
"onfi_timing_mode".  Users can set any Timing Mode that may not be
supported on the device.

[2] The function nand_onfi_timing_set() holds many parameters in it.
It is duplicating efforts of drivers/mtd/nand/nand_timings.c

[3] The function get_onfi_nand_para() detects the supported Timing
Modes.  The driver need/should not have its own way to do so because
the ONFi parameters are read by nand_flash_detect_onfi() and the
supported Timing Modes are detected by nand_init_data_interface().

[4] Intel specific parameters such as CLK_X=5, CLK_MULTI=4, etc. are
hard-coded in the driver, so this function is not working for other
platforms.

[5] It is weird for a driver to take care of a particular device
(Samsung K9WAG08U1A) by get_samsung_nand_para().

Now, the core framework provides a common logic to handle timings.
Implement .setup_data_interface().

While I am working on this, I found even more issues in the current
code, so fix the following as well:

- In recent IP versions, WE_2_RE and TWHR2 share the same register.
  Likewise for ADDR_2_DATA and TCWAW, CS_SETUP_CNT and TWB.  When
  updating one, the other must be masked.  Otherwise, the other will
  be set to 0, then timing settings will be broken.

- The recent IP release expanded the ADDR_2_DATA to 7-bit wide.
  This register is related to tADL.  As commit 74a332e78e8f ("mtd:
  nand: timings: Fix tADL_min for ONFI 4.0 chips"), the ONFi 4.0
  increased the minimum of tADL to 400 nsec.  This may not fit in
  the 6-bit ADDR_2_DATA in older versions.  Check the IP revision
  and handle this correctly, otherwise the register value would wrap
  around.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
  - Newly added

 drivers/mtd/nand/denali.c     | 351 +++++++++++++++---------------------------
 drivers/mtd/nand/denali.h     |  24 +--
 drivers/mtd/nand/denali_dt.c  |   3 +-
 drivers/mtd/nand/denali_pci.c |   6 +-
 4 files changed, 141 insertions(+), 243 deletions(-)

diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index bd09b7c..332d520 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -28,17 +28,6 @@
 
 MODULE_LICENSE("GPL");
 
-/*
- * We define a module parameter that allows the user to override
- * the hardware and decide what timing mode should be used.
- */
-#define NAND_DEFAULT_TIMINGS	-1
-
-static int onfi_timing_mode = NAND_DEFAULT_TIMINGS;
-module_param(onfi_timing_mode, int, S_IRUGO);
-MODULE_PARM_DESC(onfi_timing_mode,
-	   "Overrides default ONFI setting. -1 indicates use default timings");
-
 #define DENALI_NAND_NAME    "denali-nand"
 
 /*
@@ -63,12 +52,6 @@ MODULE_PARM_DESC(onfi_timing_mode,
 #define CHIP_SELECT_INVALID	-1
 
 /*
- * This macro divides two integers and rounds fractional values up
- * to the nearest integer value.
- */
-#define CEIL_DIV(X, Y) (((X)%(Y)) ? ((X)/(Y)+1) : ((X)/(Y)))
-
-/*
  * this macro allows us to convert from an MTD structure to our own
  * device context (denali) structure.
  */
@@ -104,6 +87,14 @@ static void denali_irq_enable(struct denali_nand_info *denali,
 static uint32_t read_interrupt_status(struct denali_nand_info *denali);
 
 /*
+ * The bus interface clock, clk_x, is phase aligned with the core clock.  The
+ * clk_x is an integral multiple N of the core clk.  The value N is configured
+ * at IP delivery time, and its available value is 4, 5, or 6.  We need to align
+ * to the largest value to make it work with any possible configuration.
+ */
+#define DENALI_CLK_X_MULT	6
+
+/*
  * Certain operations for the denali NAND controller use an indexed mode to
  * read/write data. The operation is performed by writing the address value
  * of the command to the device memory followed by the data. This function
@@ -197,148 +188,6 @@ static uint16_t denali_nand_reset(struct denali_nand_info *denali)
 }
 
 /*
- * this routine calculates the ONFI timing values for a given mode and
- * programs the clocking register accordingly. The mode is determined by
- * the get_onfi_nand_para routine.
- */
-static void nand_onfi_timing_set(struct denali_nand_info *denali,
-								uint16_t mode)
-{
-	uint16_t Trea[6] = {40, 30, 25, 20, 20, 16};
-	uint16_t Trp[6] = {50, 25, 17, 15, 12, 10};
-	uint16_t Treh[6] = {30, 15, 15, 10, 10, 7};
-	uint16_t Trc[6] = {100, 50, 35, 30, 25, 20};
-	uint16_t Trhoh[6] = {0, 15, 15, 15, 15, 15};
-	uint16_t Trloh[6] = {0, 0, 0, 0, 5, 5};
-	uint16_t Tcea[6] = {100, 45, 30, 25, 25, 25};
-	uint16_t Tadl[6] = {200, 100, 100, 100, 70, 70};
-	uint16_t Trhw[6] = {200, 100, 100, 100, 100, 100};
-	uint16_t Trhz[6] = {200, 100, 100, 100, 100, 100};
-	uint16_t Twhr[6] = {120, 80, 80, 60, 60, 60};
-	uint16_t Tcs[6] = {70, 35, 25, 25, 20, 15};
-
-	uint16_t data_invalid_rhoh, data_invalid_rloh, data_invalid;
-	uint16_t dv_window = 0;
-	uint16_t en_lo, en_hi;
-	uint16_t acc_clks;
-	uint16_t addr_2_data, re_2_we, re_2_re, we_2_re, cs_cnt;
-
-	en_lo = CEIL_DIV(Trp[mode], CLK_X);
-	en_hi = CEIL_DIV(Treh[mode], CLK_X);
-#if ONFI_BLOOM_TIME
-	if ((en_hi * CLK_X) < (Treh[mode] + 2))
-		en_hi++;
-#endif
-
-	if ((en_lo + en_hi) * CLK_X < Trc[mode])
-		en_lo += CEIL_DIV((Trc[mode] - (en_lo + en_hi) * CLK_X), CLK_X);
-
-	if ((en_lo + en_hi) < CLK_MULTI)
-		en_lo += CLK_MULTI - en_lo - en_hi;
-
-	while (dv_window < 8) {
-		data_invalid_rhoh = en_lo * CLK_X + Trhoh[mode];
-
-		data_invalid_rloh = (en_lo + en_hi) * CLK_X + Trloh[mode];
-
-		data_invalid = data_invalid_rhoh < data_invalid_rloh ?
-					data_invalid_rhoh : data_invalid_rloh;
-
-		dv_window = data_invalid - Trea[mode];
-
-		if (dv_window < 8)
-			en_lo++;
-	}
-
-	acc_clks = CEIL_DIV(Trea[mode], CLK_X);
-
-	while (acc_clks * CLK_X - Trea[mode] < 3)
-		acc_clks++;
-
-	if (data_invalid - acc_clks * CLK_X < 2)
-		dev_warn(denali->dev, "%s, Line %d: Warning!\n",
-			 __FILE__, __LINE__);
-
-	addr_2_data = CEIL_DIV(Tadl[mode], CLK_X);
-	re_2_we = CEIL_DIV(Trhw[mode], CLK_X);
-	re_2_re = CEIL_DIV(Trhz[mode], CLK_X);
-	we_2_re = CEIL_DIV(Twhr[mode], CLK_X);
-	cs_cnt = CEIL_DIV((Tcs[mode] - Trp[mode]), CLK_X);
-	if (cs_cnt == 0)
-		cs_cnt = 1;
-
-	if (Tcea[mode]) {
-		while (cs_cnt * CLK_X + Trea[mode] < Tcea[mode])
-			cs_cnt++;
-	}
-
-#if MODE5_WORKAROUND
-	if (mode == 5)
-		acc_clks = 5;
-#endif
-
-	/* Sighting 3462430: Temporary hack for MT29F128G08CJABAWP:B */
-	if (ioread32(denali->flash_reg + MANUFACTURER_ID) == 0 &&
-		ioread32(denali->flash_reg + DEVICE_ID) == 0x88)
-		acc_clks = 6;
-
-	iowrite32(acc_clks, denali->flash_reg + ACC_CLKS);
-	iowrite32(re_2_we, denali->flash_reg + RE_2_WE);
-	iowrite32(re_2_re, denali->flash_reg + RE_2_RE);
-	iowrite32(we_2_re, denali->flash_reg + WE_2_RE);
-	iowrite32(addr_2_data, denali->flash_reg + ADDR_2_DATA);
-	iowrite32(en_lo, denali->flash_reg + RDWR_EN_LO_CNT);
-	iowrite32(en_hi, denali->flash_reg + RDWR_EN_HI_CNT);
-	iowrite32(cs_cnt, denali->flash_reg + CS_SETUP_CNT);
-}
-
-/* queries the NAND device to see what ONFI modes it supports. */
-static uint16_t get_onfi_nand_para(struct denali_nand_info *denali)
-{
-	int i;
-
-	/*
-	 * we needn't to do a reset here because driver has already
-	 * reset all the banks before
-	 */
-	if (!(ioread32(denali->flash_reg + ONFI_TIMING_MODE) &
-		ONFI_TIMING_MODE__VALUE))
-		return FAIL;
-
-	for (i = 5; i > 0; i--) {
-		if (ioread32(denali->flash_reg + ONFI_TIMING_MODE) &
-			(0x01 << i))
-			break;
-	}
-
-	nand_onfi_timing_set(denali, i);
-
-	/*
-	 * By now, all the ONFI devices we know support the page cache
-	 * rw feature. So here we enable the pipeline_rw_ahead feature
-	 */
-	/* iowrite32(1, denali->flash_reg + CACHE_WRITE_ENABLE); */
-	/* iowrite32(1, denali->flash_reg + CACHE_READ_ENABLE);  */
-
-	return PASS;
-}
-
-static void get_samsung_nand_para(struct denali_nand_info *denali,
-							uint8_t device_id)
-{
-	if (device_id == 0xd3) { /* Samsung K9WAG08U1A */
-		/* Set timing register values according to datasheet */
-		iowrite32(5, denali->flash_reg + ACC_CLKS);
-		iowrite32(20, denali->flash_reg + RE_2_WE);
-		iowrite32(12, denali->flash_reg + WE_2_RE);
-		iowrite32(14, denali->flash_reg + ADDR_2_DATA);
-		iowrite32(3, denali->flash_reg + RDWR_EN_LO_CNT);
-		iowrite32(2, denali->flash_reg + RDWR_EN_HI_CNT);
-		iowrite32(2, denali->flash_reg + CS_SETUP_CNT);
-	}
-}
-
-/*
  * Use the configuration feature register to determine the maximum number of
  * banks that the hardware supports.
  */
@@ -353,58 +202,6 @@ static void detect_max_banks(struct denali_nand_info *denali)
 		denali->max_banks <<= 1;
 }
 
-static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
-{
-	uint16_t status = PASS;
-	uint32_t id_bytes[8], addr;
-	uint8_t maf_id, device_id;
-	int i;
-
-	/*
-	 * Use read id method to get device ID and other params.
-	 * For some NAND chips, controller can't report the correct
-	 * device ID by reading from DEVICE_ID register
-	 */
-	addr = MODE_11 | BANK(denali->flash_bank);
-	index_addr(denali, addr | 0, 0x90);
-	index_addr(denali, addr | 1, 0);
-	for (i = 0; i < 8; i++)
-		index_addr_read_data(denali, addr | 2, &id_bytes[i]);
-	maf_id = id_bytes[0];
-	device_id = id_bytes[1];
-
-	if (ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_LUNS) &
-		ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE) { /* ONFI 1.0 NAND */
-		if (FAIL == get_onfi_nand_para(denali))
-			return FAIL;
-	} else if (maf_id == 0xEC) { /* Samsung NAND */
-		get_samsung_nand_para(denali, device_id);
-	}
-
-	dev_info(denali->dev,
-			"Dump timing register values:\n"
-			"acc_clks: %d, re_2_we: %d, re_2_re: %d\n"
-			"we_2_re: %d, addr_2_data: %d, rdwr_en_lo_cnt: %d\n"
-			"rdwr_en_hi_cnt: %d, cs_setup_cnt: %d\n",
-			ioread32(denali->flash_reg + ACC_CLKS),
-			ioread32(denali->flash_reg + RE_2_WE),
-			ioread32(denali->flash_reg + RE_2_RE),
-			ioread32(denali->flash_reg + WE_2_RE),
-			ioread32(denali->flash_reg + ADDR_2_DATA),
-			ioread32(denali->flash_reg + RDWR_EN_LO_CNT),
-			ioread32(denali->flash_reg + RDWR_EN_HI_CNT),
-			ioread32(denali->flash_reg + CS_SETUP_CNT));
-
-	/*
-	 * If the user specified to override the default timings
-	 * with a specific ONFI mode, we apply those changes here.
-	 */
-	if (onfi_timing_mode != NAND_DEFAULT_TIMINGS)
-		nand_onfi_timing_set(denali, onfi_timing_mode);
-
-	return status;
-}
-
 static void denali_set_intr_modes(struct denali_nand_info *denali,
 					uint16_t INT_ENABLE)
 {
@@ -1209,7 +1006,122 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
 		break;
 	}
 }
-/* end NAND core entry points */
+
+#define DIV_ROUND_DOWN_ULL(ll, d) \
+	({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; })
+
+static int denali_setup_data_interface(struct mtd_info *mtd,
+				       const struct nand_data_interface *conf,
+				       bool check_only)
+{
+	struct denali_nand_info *denali = mtd_to_denali(mtd);
+	const struct nand_sdr_timings *timings;
+	unsigned long t_clk;
+	int acc_clks, re_2_we, re_2_re, we_2_re, addr_2_data;
+	int rdwr_en_lo, rdwr_en_hi, rdwr_en_lo_hi, cs_setup;
+	int addr_2_data_mask;
+	uint32_t tmp;
+
+	timings = nand_get_sdr_timings(conf);
+	if (IS_ERR(timings))
+		return PTR_ERR(timings);
+
+	/* clk_x period in picoseconds */
+	t_clk = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate);
+	if (!t_clk)
+		return -EINVAL;
+
+	if (check_only)
+		return 0;
+
+	/* tREA -> ACC_CLKS */
+	acc_clks = DIV_ROUND_UP(timings->tREA_max, t_clk);
+	acc_clks = min_t(int, acc_clks, ACC_CLKS__VALUE);
+
+	tmp = ioread32(denali->flash_reg + ACC_CLKS);
+	tmp &= ~ACC_CLKS__VALUE;
+	tmp |= acc_clks;
+	iowrite32(tmp, denali->flash_reg + ACC_CLKS);
+
+	/* tRWH -> RE_2_WE */
+	re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_clk);
+	re_2_we = min_t(int, re_2_we, RE_2_WE__VALUE);
+
+	tmp = ioread32(denali->flash_reg + RE_2_WE);
+	tmp &= ~RE_2_WE__VALUE;
+	tmp |= re_2_we;
+	iowrite32(tmp, denali->flash_reg + RE_2_WE);
+
+	/* tRHZ -> RE_2_RE */
+	re_2_re = DIV_ROUND_UP(timings->tRHZ_max, t_clk);
+	re_2_re = min_t(int, re_2_re, RE_2_RE__VALUE);
+
+	tmp = ioread32(denali->flash_reg + RE_2_RE);
+	tmp &= ~RE_2_RE__VALUE;
+	tmp |= re_2_re;
+	iowrite32(tmp, denali->flash_reg + RE_2_RE);
+
+	/* tWHR -> WE_2_RE */
+	we_2_re = DIV_ROUND_UP(timings->tWHR_min, t_clk);
+	we_2_re = min_t(int, we_2_re, TWHR2_AND_WE_2_RE__WE_2_RE);
+
+	tmp = ioread32(denali->flash_reg + TWHR2_AND_WE_2_RE);
+	tmp &= ~TWHR2_AND_WE_2_RE__WE_2_RE;
+	tmp |= we_2_re;
+	iowrite32(tmp, denali->flash_reg + TWHR2_AND_WE_2_RE);
+
+	/* tADL -> ADDR_2_DATA */
+
+	/* for older versions, ADDR_2_DATA is only 6 bit wide */
+	addr_2_data_mask = TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA;
+	if (denali->revision < 0x0501)
+		addr_2_data_mask >>= 1;
+
+	addr_2_data = DIV_ROUND_UP(timings->tADL_min, t_clk);
+	addr_2_data = min_t(int, addr_2_data, addr_2_data_mask);
+
+	tmp = ioread32(denali->flash_reg + TCWAW_AND_ADDR_2_DATA);
+	tmp &= ~addr_2_data_mask;
+	tmp |= addr_2_data;
+	iowrite32(tmp, denali->flash_reg + TCWAW_AND_ADDR_2_DATA);
+
+	/* tREH, tWH -> RDWR_EN_HI_CNT */
+	rdwr_en_hi = DIV_ROUND_UP(max(timings->tREH_min, timings->tWH_min),
+				  t_clk);
+	rdwr_en_hi = min_t(int, rdwr_en_hi, RDWR_EN_HI_CNT__VALUE);
+
+	tmp = ioread32(denali->flash_reg + RDWR_EN_HI_CNT);
+	tmp &= ~RDWR_EN_HI_CNT__VALUE;
+	tmp |= rdwr_en_hi;
+	iowrite32(tmp, denali->flash_reg + RDWR_EN_HI_CNT);
+
+	/* tRP, tWP -> RDWR_EN_LO_CNT */
+	rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min),
+				  t_clk);
+	rdwr_en_lo_hi = DIV_ROUND_UP(max(timings->tRC_min, timings->tWC_min),
+				     t_clk);
+	rdwr_en_lo_hi = max(rdwr_en_lo_hi, DENALI_CLK_X_MULT);
+	rdwr_en_lo = max(rdwr_en_lo, rdwr_en_lo_hi - rdwr_en_hi);
+	rdwr_en_lo = min_t(int, rdwr_en_lo, RDWR_EN_LO_CNT__VALUE);
+
+	tmp = ioread32(denali->flash_reg + RDWR_EN_LO_CNT);
+	tmp &= ~RDWR_EN_LO_CNT__VALUE;
+	tmp |= rdwr_en_lo;
+	iowrite32(tmp, denali->flash_reg + RDWR_EN_LO_CNT);
+
+	/* tCS, tCEA -> CS_SETUP_CNT */
+	cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_clk) - rdwr_en_lo,
+			(int)DIV_ROUND_UP(timings->tCEA_max, t_clk) - acc_clks,
+			0);
+	cs_setup = min_t(int, cs_setup, CS_SETUP_CNT__VALUE);
+
+	tmp = ioread32(denali->flash_reg + CS_SETUP_CNT);
+	tmp &= ~CS_SETUP_CNT__VALUE;
+	tmp |= cs_setup;
+	iowrite32(tmp, denali->flash_reg + CS_SETUP_CNT);
+
+	return 0;
+}
 
 /* Initialization code to bring the device up to a known good state */
 static void denali_hw_init(struct denali_nand_info *denali)
@@ -1237,7 +1149,6 @@ static void denali_hw_init(struct denali_nand_info *denali)
 	/* Should set value for these registers when init */
 	iowrite32(0, denali->flash_reg + TWO_ROW_ADDR_CYCLES);
 	iowrite32(1, denali->flash_reg + ECC_ENABLE);
-	denali_nand_timing_set(denali);
 	denali_irq_init(denali);
 }
 
@@ -1418,17 +1329,6 @@ int denali_init(struct denali_nand_info *denali)
 	struct mtd_info *mtd = nand_to_mtd(chip);
 	int ret;
 
-	if (denali->platform == INTEL_CE4100) {
-		/*
-		 * Due to a silicon limitation, we can only support
-		 * ONFI timing mode 1 and below.
-		 */
-		if (onfi_timing_mode < -1 || onfi_timing_mode > 1) {
-			pr_err("Intel CE4100 only supports ONFI timing mode 1 or below\n");
-			return -EINVAL;
-		}
-	}
-
 	/* allocate a temporary buffer for nand_scan_ident() */
 	denali->buf.buf = devm_kzalloc(denali->dev, PAGE_SIZE,
 					GFP_DMA | GFP_KERNEL);
@@ -1456,6 +1356,9 @@ int denali_init(struct denali_nand_info *denali)
 	chip->cmdfunc = denali_cmdfunc;
 	chip->read_byte = denali_read_byte;
 	chip->waitfunc = denali_waitfunc;
+	/* clk rate info is needed for setup_data_interface */
+	if (denali->clk_x_rate)
+		chip->setup_data_interface = denali_setup_data_interface;
 
 	/*
 	 * scan for NAND devices attached to the controller
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index 055a53c..a3258f9 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -72,11 +72,13 @@
 #define GLOBAL_INT_ENABLE			0xf0
 #define     GLOBAL_INT_EN_FLAG				BIT(0)
 
-#define WE_2_RE					0x100
-#define     WE_2_RE__VALUE				GENMASK(5, 0)
+#define TWHR2_AND_WE_2_RE			0x100
+#define     TWHR2_AND_WE_2_RE__WE_2_RE			GENMASK(5, 0)
+#define     TWHR2_AND_WE_2_RE__TWHR2			GENMASK(13, 8)
 
-#define ADDR_2_DATA				0x110
-#define     ADDR_2_DATA__VALUE				GENMASK(5, 0)
+#define TCWAW_AND_ADDR_2_DATA			0x110
+#define     TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA		GENMASK(6, 0)
+#define     TCWAW_AND_ADDR_2_DATA__TCWAW		GENMASK(13, 8)
 
 #define RE_2_WE					0x120
 #define     RE_2_WE__VALUE				GENMASK(5, 0)
@@ -128,6 +130,7 @@
 
 #define CS_SETUP_CNT				0x220
 #define     CS_SETUP_CNT__VALUE				GENMASK(4, 0)
+#define     CS_SETUP_CNT__TWB				GENMASK(17, 12)
 
 #define SPARE_AREA_SKIP_BYTES			0x230
 #define     SPARE_AREA_SKIP_BYTES__VALUE		GENMASK(5, 0)
@@ -302,13 +305,6 @@
 #define FAIL 1                  /*failed flag*/
 #define PASS 0                  /*success flag*/
 
-#define CLK_X  5
-#define CLK_MULTI 4
-
-#define ONFI_BLOOM_TIME         1
-#define MODE5_WORKAROUND        0
-
-
 #define MODE_00    0x00000000
 #define MODE_01    0x04000000
 #define MODE_10    0x08000000
@@ -321,14 +317,10 @@ struct nand_buf {
 	dma_addr_t dma_buf;
 };
 
-#define INTEL_CE4100	1
-#define INTEL_MRST	2
-#define DT		3
-
 struct denali_nand_info {
 	struct nand_chip nand;
+	unsigned long clk_x_rate;	/* bus interface clock rate */
 	int flash_bank; /* currently selected chip */
-	int platform;
 	struct nand_buf buf;
 	struct device *dev;
 	int page;
diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c
index 1f2f68a..97a2633 100644
--- a/drivers/mtd/nand/denali_dt.c
+++ b/drivers/mtd/nand/denali_dt.c
@@ -93,7 +93,6 @@ static int denali_dt_probe(struct platform_device *pdev)
 		denali->caps = data->caps;
 	}
 
-	denali->platform = DT;
 	denali->dev = &pdev->dev;
 	denali->irq = platform_get_irq(pdev, 0);
 	if (denali->irq < 0) {
@@ -120,6 +119,8 @@ static int denali_dt_probe(struct platform_device *pdev)
 	}
 	clk_prepare_enable(dt->clk);
 
+	denali->clk_x_rate = clk_get_rate(dt->clk);
+
 	ret = denali_init(denali);
 	if (ret)
 		goto out_disable_clk;
diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/denali_pci.c
index a39682a5..117c182 100644
--- a/drivers/mtd/nand/denali_pci.c
+++ b/drivers/mtd/nand/denali_pci.c
@@ -19,6 +19,9 @@
 
 #define DENALI_NAND_NAME    "denali-nand-pci"
 
+#define INTEL_CE4100	1
+#define INTEL_MRST	2
+
 /* List of platforms this NAND controller has be integrated into */
 static const struct pci_device_id denali_pci_ids[] = {
 	{ PCI_VDEVICE(INTEL, 0x0701), INTEL_CE4100 },
@@ -45,13 +48,11 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 	}
 
 	if (id->driver_data == INTEL_CE4100) {
-		denali->platform = INTEL_CE4100;
 		mem_base = pci_resource_start(dev, 0);
 		mem_len = pci_resource_len(dev, 1);
 		csr_base = pci_resource_start(dev, 1);
 		csr_len = pci_resource_len(dev, 1);
 	} else {
-		denali->platform = INTEL_MRST;
 		csr_base = pci_resource_start(dev, 0);
 		csr_len = pci_resource_len(dev, 0);
 		mem_base = pci_resource_start(dev, 1);
@@ -65,6 +66,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 	pci_set_master(dev);
 	denali->dev = &dev->dev;
 	denali->irq = dev->irq;
+	denali->clk_x_rate = 200000000;		/* 200 MHz */
 
 	ret = pci_request_regions(dev, DENALI_NAND_NAME);
 	if (ret) {
-- 
2.7.4

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

* [RESEND PATCH v2 36/53] mtd: nand: denali: remove meaningless pipeline read-ahead operation
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
                   ` (7 preceding siblings ...)
  2017-03-23  0:17 ` [RESEND PATCH v2 35/53] mtd: nand: denali: handle timing parameters by setup_data_interface() Masahiro Yamada
@ 2017-03-23  0:17 ` Masahiro Yamada
  2017-03-23  0:17 ` [RESEND PATCH v2 37/53] mtd: nand: denali: rework interrupt handling Masahiro Yamada
                   ` (16 subsequent siblings)
  25 siblings, 0 replies; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

The pipeline read-ahead function of the Denali IP enables continuous
reading from the device; while data is being read out by a CPU, the
controller maintains additional commands for streaming data from the
device.  This will reduce the latency of the second page or later.

This feature is obviously no help for per-page accessors of Linux
NAND driver interface.

In the current implementation, the pipeline command is issued to
load a single page, then data are read out immediately.  The use of
the pipeline operation is not adding any advantage, but just adding
complexity to the code.  Remove.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
  - Newly added

 drivers/mtd/nand/denali.c | 42 +++---------------------------------------
 1 file changed, 3 insertions(+), 39 deletions(-)

diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 332d520..01a35ec 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -67,7 +67,6 @@ static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)
 #define SPARE_ACCESS		0x41
 #define MAIN_ACCESS		0x42
 #define MAIN_SPARE_ACCESS	0x43
-#define PIPELINE_ACCESS		0x2000
 
 #define DENALI_READ	0
 #define DENALI_WRITE	0x100
@@ -394,15 +393,7 @@ static int denali_send_pipeline_cmd(struct denali_nand_info *denali,
 				    int access_type, int op)
 {
 	int status = PASS;
-	uint32_t page_count = 1;
-	uint32_t addr, cmd, irq_status, irq_mask;
-
-	if (op == DENALI_READ)
-		irq_mask = INTR__LOAD_COMP;
-	else if (op == DENALI_WRITE)
-		irq_mask = 0;
-	else
-		BUG();
+	uint32_t addr, cmd;
 
 	setup_ecc_for_xfer(denali, ecc_en, transfer_spare);
 
@@ -425,35 +416,8 @@ static int denali_send_pipeline_cmd(struct denali_nand_info *denali,
 		cmd = MODE_10 | addr;
 		index_addr(denali, cmd, access_type);
 
-		/*
-		 * page 33 of the NAND controller spec indicates we should not
-		 * use the pipeline commands in Spare area only mode.
-		 * So we don't.
-		 */
-		if (access_type == SPARE_ACCESS) {
-			cmd = MODE_01 | addr;
-			iowrite32(cmd, denali->flash_mem);
-		} else {
-			index_addr(denali, cmd,
-					PIPELINE_ACCESS | op | page_count);
-
-			/*
-			 * wait for command to be accepted
-			 * can always use status0 bit as the
-			 * mask is identical for each bank.
-			 */
-			irq_status = wait_for_irq(denali, irq_mask);
-
-			if (irq_status == 0) {
-				dev_err(denali->dev,
-					"cmd, page, addr on timeout (0x%x, 0x%x, 0x%x)\n",
-					cmd, denali->page, addr);
-				status = FAIL;
-			} else {
-				cmd = MODE_01 | addr;
-				iowrite32(cmd, denali->flash_mem);
-			}
-		}
+		cmd = MODE_01 | addr;
+		iowrite32(cmd, denali->flash_mem);
 	}
 	return status;
 }
-- 
2.7.4

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

* [RESEND PATCH v2 37/53] mtd: nand: denali: rework interrupt handling
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
                   ` (8 preceding siblings ...)
  2017-03-23  0:17 ` [RESEND PATCH v2 36/53] mtd: nand: denali: remove meaningless pipeline read-ahead operation Masahiro Yamada
@ 2017-03-23  0:17 ` Masahiro Yamada
  2017-03-23  0:17 ` [RESEND PATCH v2 38/53] mtd: nand: denali: fix NAND_CMD_STATUS handling Masahiro Yamada
                   ` (15 subsequent siblings)
  25 siblings, 0 replies; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

Simplify the interrupt handling and fix issues:

- The register field view of INTR_EN / INTR_STATUS is different
  among IP versions.  The global macro DENALI_IRQ_ALL is hard-coded
  for Intel platforms.  The interrupt mask should be determined at
  run-time depending on the running platform.

- wait_for_irq() loops do {} while() until interested flags are
  asserted.  The logic can be simplified.

- The spin_lock() guard seems too complex (and not perfect).

- denali->complete is reused again and again, but reinit_completion()
  is missing.  Add it.

Re-work the code to make it more robust and easier to handle.

While we are here, also rename the jump label "failed_req_irq" to
more appropriate "disable_irq".

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
  - Newly added

 drivers/mtd/nand/denali.c | 322 +++++++++++++++++-----------------------------
 drivers/mtd/nand/denali.h |   1 +
 2 files changed, 119 insertions(+), 204 deletions(-)

diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 01a35ec..5aafa59 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -31,21 +31,6 @@ MODULE_LICENSE("GPL");
 #define DENALI_NAND_NAME    "denali-nand"
 
 /*
- * We define a macro here that combines all interrupts this driver uses into
- * a single constant value, for convenience.
- */
-#define DENALI_IRQ_ALL	(INTR__DMA_CMD_COMP | \
-			INTR__ECC_TRANSACTION_DONE | \
-			INTR__ECC_ERR | \
-			INTR__PROGRAM_FAIL | \
-			INTR__LOAD_COMP | \
-			INTR__PROGRAM_COMP | \
-			INTR__TIME_OUT | \
-			INTR__ERASE_FAIL | \
-			INTR__RST_COMP | \
-			INTR__ERASE_COMP)
-
-/*
  * indicates whether or not the internal value for the flash bank is
  * valid or not
  */
@@ -71,20 +56,14 @@ static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)
 #define DENALI_READ	0
 #define DENALI_WRITE	0x100
 
+#define DENALI_NR_BANKS		4
+
 /*
  * this is a helper macro that allows us to
  * format the bank into the proper bits for the controller
  */
 #define BANK(x) ((x) << 24)
 
-/* forward declarations */
-static void clear_interrupts(struct denali_nand_info *denali);
-static uint32_t wait_for_irq(struct denali_nand_info *denali,
-							uint32_t irq_mask);
-static void denali_irq_enable(struct denali_nand_info *denali,
-							uint32_t int_mask);
-static uint32_t read_interrupt_status(struct denali_nand_info *denali);
-
 /*
  * The bus interface clock, clk_x, is phase aligned with the core clock.  The
  * clk_x is an integral multiple N of the core clk.  The value N is configured
@@ -143,22 +122,6 @@ static void read_status(struct denali_nand_info *denali)
 		write_byte_to_buf(denali, 0);
 }
 
-/* resets a specific device connected to the core */
-static void reset_bank(struct denali_nand_info *denali)
-{
-	uint32_t irq_status;
-	uint32_t irq_mask = INTR__RST_COMP | INTR__TIME_OUT;
-
-	clear_interrupts(denali);
-
-	iowrite32(1 << denali->flash_bank, denali->flash_reg + DEVICE_RESET);
-
-	irq_status = wait_for_irq(denali, irq_mask);
-
-	if (irq_status & INTR__TIME_OUT)
-		dev_err(denali->dev, "reset bank failed.\n");
-}
-
 /* Reset the flash controller */
 static uint16_t denali_nand_reset(struct denali_nand_info *denali)
 {
@@ -201,169 +164,123 @@ static void detect_max_banks(struct denali_nand_info *denali)
 		denali->max_banks <<= 1;
 }
 
-static void denali_set_intr_modes(struct denali_nand_info *denali,
-					uint16_t INT_ENABLE)
+static void denali_enable_irq(struct denali_nand_info *denali)
 {
-	if (INT_ENABLE)
-		iowrite32(1, denali->flash_reg + GLOBAL_INT_ENABLE);
-	else
-		iowrite32(0, denali->flash_reg + GLOBAL_INT_ENABLE);
-}
+	int i;
 
-/*
- * validation function to verify that the controlling software is making
- * a valid request
- */
-static inline bool is_flash_bank_valid(int flash_bank)
-{
-	return flash_bank >= 0 && flash_bank < 4;
+	for (i = 0; i < DENALI_NR_BANKS; i++)
+		iowrite32(U32_MAX, denali->flash_reg + INTR_EN(i));
+	iowrite32(GLOBAL_INT_EN_FLAG, denali->flash_reg + GLOBAL_INT_ENABLE);
 }
 
-static void denali_irq_init(struct denali_nand_info *denali)
+static void denali_disable_irq(struct denali_nand_info *denali)
 {
-	uint32_t int_mask;
 	int i;
 
-	/* Disable global interrupts */
-	denali_set_intr_modes(denali, false);
-
-	int_mask = DENALI_IRQ_ALL;
-
-	/* Clear all status bits */
-	for (i = 0; i < denali->max_banks; ++i)
-		iowrite32(0xFFFF, denali->flash_reg + INTR_STATUS(i));
-
-	denali_irq_enable(denali, int_mask);
+	for (i = 0; i < DENALI_NR_BANKS; i++)
+		iowrite32(0, denali->flash_reg + INTR_EN(i));
+	iowrite32(0, denali->flash_reg + GLOBAL_INT_ENABLE);
 }
 
-static void denali_irq_cleanup(int irqnum, struct denali_nand_info *denali)
+static void denali_clear_irq(struct denali_nand_info *denali,
+			     int bank, uint32_t irq_status)
 {
-	denali_set_intr_modes(denali, false);
+	/* write one to clear bits */
+	iowrite32(irq_status, denali->flash_reg + INTR_STATUS(bank));
 }
 
-static void denali_irq_enable(struct denali_nand_info *denali,
-							uint32_t int_mask)
+static void denali_clear_irq_all(struct denali_nand_info *denali)
 {
 	int i;
 
-	for (i = 0; i < denali->max_banks; ++i)
-		iowrite32(int_mask, denali->flash_reg + INTR_EN(i));
+	for (i = 0; i < DENALI_NR_BANKS; i++)
+		denali_clear_irq(denali, i, U32_MAX);
 }
 
-/*
- * This function only returns when an interrupt that this driver cares about
- * occurs. This is to reduce the overhead of servicing interrupts
- */
-static inline uint32_t denali_irq_detected(struct denali_nand_info *denali)
+static irqreturn_t denali_isr(int irq, void *dev_id)
 {
-	return read_interrupt_status(denali) & DENALI_IRQ_ALL;
-}
+	struct denali_nand_info *denali = dev_id;
+	irqreturn_t ret = IRQ_NONE;
+	uint32_t irq_status;
+	int i;
 
-/* Interrupts are cleared by writing a 1 to the appropriate status bit */
-static inline void clear_interrupt(struct denali_nand_info *denali,
-							uint32_t irq_mask)
-{
-	uint32_t intr_status_reg;
+	spin_lock(&denali->irq_lock);
 
-	intr_status_reg = INTR_STATUS(denali->flash_bank);
+	for (i = 0; i < DENALI_NR_BANKS; i++) {
+		irq_status = ioread32(denali->flash_reg + INTR_STATUS(i));
+		if (irq_status)
+			ret = IRQ_HANDLED;
 
-	iowrite32(irq_mask, denali->flash_reg + intr_status_reg);
-}
+		denali_clear_irq(denali, i, irq_status);
 
-static void clear_interrupts(struct denali_nand_info *denali)
-{
-	uint32_t status;
+		if (i != denali->flash_bank)
+			continue;
 
-	spin_lock_irq(&denali->irq_lock);
+		denali->irq_status |= irq_status;
 
-	status = read_interrupt_status(denali);
-	clear_interrupt(denali, status);
+		if (denali->irq_status & denali->irq_mask)
+			complete(&denali->complete);
+	}
+
+	spin_unlock(&denali->irq_lock);
 
-	denali->irq_status = 0x0;
-	spin_unlock_irq(&denali->irq_lock);
+	return ret;
 }
 
-static uint32_t read_interrupt_status(struct denali_nand_info *denali)
+static void denali_reset_irq(struct denali_nand_info *denali)
 {
-	uint32_t intr_status_reg;
-
-	intr_status_reg = INTR_STATUS(denali->flash_bank);
+	unsigned long flags;
 
-	return ioread32(denali->flash_reg + intr_status_reg);
+	spin_lock_irqsave(&denali->irq_lock, flags);
+	denali->irq_status = 0;
+	denali->irq_mask = 0;
+	spin_unlock_irqrestore(&denali->irq_lock, flags);
 }
 
-/*
- * This is the interrupt service routine. It handles all interrupts
- * sent to this device. Note that on CE4100, this is a shared interrupt.
- */
-static irqreturn_t denali_isr(int irq, void *dev_id)
+static uint32_t denali_wait_for_irq(struct denali_nand_info *denali,
+				    uint32_t irq_mask)
 {
-	struct denali_nand_info *denali = dev_id;
+	unsigned long time_left, flags;
 	uint32_t irq_status;
-	irqreturn_t result = IRQ_NONE;
 
-	spin_lock(&denali->irq_lock);
+	spin_lock_irqsave(&denali->irq_lock, flags);
 
-	/* check to see if a valid NAND chip has been selected. */
-	if (is_flash_bank_valid(denali->flash_bank)) {
-		/*
-		 * check to see if controller generated the interrupt,
-		 * since this is a shared interrupt
-		 */
-		irq_status = denali_irq_detected(denali);
-		if (irq_status != 0) {
-			/* handle interrupt */
-			/* first acknowledge it */
-			clear_interrupt(denali, irq_status);
-			/*
-			 * store the status in the device context for someone
-			 * to read
-			 */
-			denali->irq_status |= irq_status;
-			/* notify anyone who cares that it happened */
-			complete(&denali->complete);
-			/* tell the OS that we've handled this */
-			result = IRQ_HANDLED;
-		}
+	irq_status = denali->irq_status;
+
+	if (irq_mask & irq_status) {
+		spin_unlock_irqrestore(&denali->irq_lock, flags);
+		return irq_status;
 	}
-	spin_unlock(&denali->irq_lock);
-	return result;
+
+	denali->irq_mask = irq_mask;
+	reinit_completion(&denali->complete);
+	spin_unlock_irqrestore(&denali->irq_lock, flags);
+
+	time_left = wait_for_completion_timeout(&denali->complete,
+						msecs_to_jiffies(1000));
+	if (!time_left) {
+		dev_err(denali->dev, "timeout while waiting for irq 0x%x\n",
+			denali->irq_mask);
+		return 0;
+	}
+
+	return denali->irq_status;
 }
 
-static uint32_t wait_for_irq(struct denali_nand_info *denali, uint32_t irq_mask)
+/* resets a specific device connected to the core */
+static void reset_bank(struct denali_nand_info *denali)
 {
-	unsigned long comp_res;
-	uint32_t intr_status;
-	unsigned long timeout = msecs_to_jiffies(1000);
+	uint32_t irq_status;
 
-	do {
-		comp_res =
-			wait_for_completion_timeout(&denali->complete, timeout);
-		spin_lock_irq(&denali->irq_lock);
-		intr_status = denali->irq_status;
-
-		if (intr_status & irq_mask) {
-			denali->irq_status &= ~irq_mask;
-			spin_unlock_irq(&denali->irq_lock);
-			/* our interrupt was detected */
-			break;
-		}
+	denali_reset_irq(denali);
 
-		/*
-		 * these are not the interrupts you are looking for -
-		 * need to wait again
-		 */
-		spin_unlock_irq(&denali->irq_lock);
-	} while (comp_res != 0);
+	iowrite32(1 << denali->flash_bank, denali->flash_reg + DEVICE_RESET);
 
-	if (comp_res == 0) {
-		/* timeout */
-		pr_err("timeout occurred, status = 0x%x, mask = 0x%x\n",
-				intr_status, irq_mask);
+	irq_status = denali_wait_for_irq(denali,
+					 INTR__RST_COMP | INTR__TIME_OUT);
 
-		intr_status = 0;
-	}
-	return intr_status;
+	if (!(irq_status & INTR__RST_COMP))
+		dev_err(denali->dev, "reset bank failed.\n");
 }
 
 /*
@@ -397,7 +314,7 @@ static int denali_send_pipeline_cmd(struct denali_nand_info *denali,
 
 	setup_ecc_for_xfer(denali, ecc_en, transfer_spare);
 
-	clear_interrupts(denali);
+	denali_reset_irq(denali);
 
 	addr = BANK(denali->flash_bank) | denali->page;
 
@@ -479,9 +396,9 @@ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
 		write_data_to_flash_mem(denali, buf, mtd->oobsize);
 
 		/* wait for operation to complete */
-		irq_status = wait_for_irq(denali, irq_mask);
+		irq_status = denali_wait_for_irq(denali, irq_mask);
 
-		if (irq_status == 0) {
+		if (!(irq_status & INTR__PROGRAM_COMP)) {
 			dev_err(denali->dev, "OOB write failed\n");
 			status = -EIO;
 		}
@@ -510,9 +427,9 @@ static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
 		 * can always use status0 bit as the
 		 * mask is identical for each bank.
 		 */
-		irq_status = wait_for_irq(denali, irq_mask);
+		irq_status = denali_wait_for_irq(denali, irq_mask);
 
-		if (irq_status == 0)
+		if (!(irq_status & INTR__LOAD_COMP))
 			dev_err(denali->dev, "page on OOB timeout %d\n",
 					denali->page);
 
@@ -573,10 +490,10 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
 	unsigned int err_byte, err_sector, err_device;
 	uint8_t err_cor_value;
 	unsigned int prev_sector = 0;
+	uint32_t irq_status;
 	int ret = 0;
 
-	/* read the ECC errors. we'll ignore them for now */
-	denali_set_intr_modes(denali, false);
+	denali_reset_irq(denali);
 
 	do {
 		err_addr = ioread32(denali->flash_reg + ECC_ERROR_ADDRESS);
@@ -624,10 +541,9 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
 	 * ECC_TRANSACTION_DONE interrupt, so here just wait for
 	 * a while for this interrupt
 	 */
-	while (!(read_interrupt_status(denali) & INTR__ECC_TRANSACTION_DONE))
-		cpu_relax();
-	clear_interrupts(denali);
-	denali_set_intr_modes(denali, true);
+	irq_status = denali_wait_for_irq(denali, INTR__ECC_TRANSACTION_DONE);
+	if (!(irq_status & INTR__ECC_TRANSACTION_DONE))
+		return -EIO;
 
 	if (ret)
 		return ret;
@@ -733,15 +649,14 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
 
 	dma_sync_single_for_device(denali->dev, addr, size, DMA_TO_DEVICE);
 
-	clear_interrupts(denali);
+	denali_reset_irq(denali);
 	denali_enable_dma(denali, true);
 
 	denali_setup_dma(denali, DENALI_WRITE);
 
 	/* wait for operation to complete */
-	irq_status = wait_for_irq(denali, irq_mask);
-
-	if (irq_status == 0) {
+	irq_status = denali_wait_for_irq(denali, irq_mask);
+	if (!(irq_status & INTR__DMA_CMD_COMP)) {
 		dev_err(denali->dev, "timeout on write_page (type = %d)\n",
 			raw_xfer);
 		ret = -EIO;
@@ -819,11 +734,11 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
 	denali_enable_dma(denali, true);
 	dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE);
 
-	clear_interrupts(denali);
+	denali_reset_irq(denali);
 	denali_setup_dma(denali, DENALI_READ);
 
 	/* wait for operation to complete */
-	irq_status = wait_for_irq(denali, irq_mask);
+	irq_status = denali_wait_for_irq(denali, irq_mask);
 
 	dma_sync_single_for_cpu(denali->dev, addr, size, DMA_FROM_DEVICE);
 
@@ -865,6 +780,7 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
 	dma_addr_t addr = denali->buf.dma_buf;
 	size_t size = mtd->writesize + mtd->oobsize;
 	uint32_t irq_mask = INTR__DMA_CMD_COMP;
+	uint32_t irq_status;
 
 	denali->page = page;
 
@@ -873,11 +789,13 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
 
 	dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE);
 
-	clear_interrupts(denali);
+	denali_reset_irq(denali);
 	denali_setup_dma(denali, DENALI_READ);
 
 	/* wait for operation to complete */
-	wait_for_irq(denali, irq_mask);
+	irq_status = denali_wait_for_irq(denali, irq_mask);
+	if (irq_status & INTR__DMA_CMD_COMP)
+		return -ETIMEDOUT;
 
 	dma_sync_single_for_cpu(denali->dev, addr, size, DMA_FROM_DEVICE);
 
@@ -904,9 +822,7 @@ static void denali_select_chip(struct mtd_info *mtd, int chip)
 {
 	struct denali_nand_info *denali = mtd_to_denali(mtd);
 
-	spin_lock_irq(&denali->irq_lock);
 	denali->flash_bank = chip;
-	spin_unlock_irq(&denali->irq_lock);
 }
 
 static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
@@ -917,19 +833,19 @@ static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
 static int denali_erase(struct mtd_info *mtd, int page)
 {
 	struct denali_nand_info *denali = mtd_to_denali(mtd);
-
 	uint32_t cmd, irq_status;
 
-	clear_interrupts(denali);
+	denali_reset_irq(denali);
 
 	/* setup page read request for access type */
 	cmd = MODE_10 | BANK(denali->flash_bank) | page;
 	index_addr(denali, cmd, 0x1);
 
 	/* wait for erase to complete or failure to occur */
-	irq_status = wait_for_irq(denali, INTR__ERASE_COMP | INTR__ERASE_FAIL);
+	irq_status = denali_wait_for_irq(denali,
+					 INTR__ERASE_COMP | INTR__ERASE_FAIL);
 
-	return irq_status & INTR__ERASE_FAIL ? NAND_STATUS_FAIL : PASS;
+	return irq_status & INTR__ERASE_COMP ? 0 : NAND_STATUS_FAIL;
 }
 
 static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
@@ -1113,7 +1029,6 @@ static void denali_hw_init(struct denali_nand_info *denali)
 	/* Should set value for these registers when init */
 	iowrite32(0, denali->flash_reg + TWO_ROW_ADDR_CYCLES);
 	iowrite32(1, denali->flash_reg + ECC_ENABLE);
-	denali_irq_init(denali);
 }
 
 static int denali_calc_ecc_bytes(int ecc_size, int ecc_strength)
@@ -1231,9 +1146,6 @@ static void denali_drv_init(struct denali_nand_info *denali)
 
 	/* indicate that MTD has not selected a valid bank yet */
 	denali->flash_bank = CHIP_SELECT_INVALID;
-
-	/* initialize our irq_status variable to indicate no interrupts */
-	denali->irq_status = 0;
 }
 
 static int denali_multidev_fixup(struct denali_nand_info *denali)
@@ -1303,6 +1215,8 @@ int denali_init(struct denali_nand_info *denali)
 	denali_hw_init(denali);
 	denali_drv_init(denali);
 
+	denali_clear_irq_all(denali);
+
 	/* Request IRQ after all the hardware initialization is finished */
 	ret = devm_request_irq(denali->dev, denali->irq, denali_isr,
 			       IRQF_SHARED, DENALI_NAND_NAME, denali);
@@ -1311,8 +1225,8 @@ int denali_init(struct denali_nand_info *denali)
 		return ret;
 	}
 
-	/* now that our ISR is registered, we can enable interrupts */
-	denali_set_intr_modes(denali, true);
+	denali_enable_irq(denali);
+
 	nand_set_flash_node(chip, denali->dev->of_node);
 
 	/* register the driver with the NAND core subsystem */
@@ -1331,7 +1245,7 @@ int denali_init(struct denali_nand_info *denali)
 	 */
 	ret = nand_scan_ident(mtd, denali->max_banks, NULL);
 	if (ret)
-		goto failed_req_irq;
+		goto disable_irq;
 
 	/* allocate the right size buffer now */
 	devm_kfree(denali->dev, denali->buf.buf);
@@ -1340,7 +1254,7 @@ int denali_init(struct denali_nand_info *denali)
 			     GFP_KERNEL);
 	if (!denali->buf.buf) {
 		ret = -ENOMEM;
-		goto failed_req_irq;
+		goto disable_irq;
 	}
 
 	ret = dma_set_mask(denali->dev,
@@ -1348,7 +1262,7 @@ int denali_init(struct denali_nand_info *denali)
 					64 : 32));
 	if (ret) {
 		dev_err(denali->dev, "No usable DMA configuration\n");
-		goto failed_req_irq;
+		goto disable_irq;
 	}
 
 	denali->buf.dma_buf = dma_map_single(denali->dev, denali->buf.buf,
@@ -1357,7 +1271,7 @@ int denali_init(struct denali_nand_info *denali)
 	if (dma_mapping_error(denali->dev, denali->buf.dma_buf)) {
 		dev_err(denali->dev, "Failed to map DMA buffer\n");
 		ret = -EIO;
-		goto failed_req_irq;
+		goto disable_irq;
 	}
 
 	/*
@@ -1384,7 +1298,7 @@ int denali_init(struct denali_nand_info *denali)
 		if (denali->caps & DENALI_CAP_ECC_SIZE_1024)
 			chip->ecc.size = 1024;
 		if (WARN(!chip->ecc.size, "must support at least 512 or 1024 ECC size"))
-			goto failed_req_irq;
+			goto disable_irq;
 	}
 
 	if ((chip->ecc.size != 512 && chip->ecc.size != 1024) ||
@@ -1392,7 +1306,7 @@ int denali_init(struct denali_nand_info *denali)
 	    (chip->ecc.size == 1024 && !(denali->caps & DENALI_CAP_ECC_SIZE_1024))) {
 		dev_err(denali->dev, "specified ECC size %d in not supported",
 			chip->ecc.size);
-		goto failed_req_irq;
+		goto disable_irq;
 	}
 
 	if (!chip->ecc.strength && !(chip->ecc.options & NAND_ECC_MAXIMIZE)) {
@@ -1404,13 +1318,13 @@ int denali_init(struct denali_nand_info *denali)
 	if (chip->ecc.options & NAND_ECC_MAXIMIZE) {
 		ret = denali_set_max_ecc_strength(denali);
 		if (ret)
-			goto failed_req_irq;
+			goto disable_irq;
 	} else if (!(denali->ecc_strength_avail & BIT(chip->ecc.strength))) {
 		dev_err(denali->dev,
 			"Specified ECC strength (%d) is not supported for this controller.\n",
 			chip->ecc.strength);
 		ret = -EINVAL;
-		goto failed_req_irq;
+		goto disable_irq;
 	}
 
 	chip->ecc.bytes = denali_calc_ecc_bytes(chip->ecc.size,
@@ -1443,21 +1357,21 @@ int denali_init(struct denali_nand_info *denali)
 
 	ret = denali_multidev_fixup(denali);
 	if (ret)
-		goto failed_req_irq;
+		goto disable_irq;
 
 	ret = nand_scan_tail(mtd);
 	if (ret)
-		goto failed_req_irq;
+		goto disable_irq;
 
 	ret = mtd_device_register(mtd, NULL, 0);
 	if (ret) {
 		dev_err(denali->dev, "Failed to register MTD: %d\n", ret);
-		goto failed_req_irq;
+		goto disable_irq;
 	}
 	return 0;
 
-failed_req_irq:
-	denali_irq_cleanup(denali->irq, denali);
+disable_irq:
+	denali_disable_irq(denali);
 
 	return ret;
 }
@@ -1475,7 +1389,7 @@ void denali_remove(struct denali_nand_info *denali)
 	int bufsize = mtd->writesize + mtd->oobsize;
 
 	nand_release(mtd);
-	denali_irq_cleanup(denali->irq, denali);
+	denali_disable_irq(denali);
 	dma_unmap_single(denali->dev, denali->buf.dma_buf, bufsize,
 			 DMA_BIDIRECTIONAL);
 }
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index a3258f9..ea709a4 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -330,6 +330,7 @@ struct denali_nand_info {
 	/* elements used by ISR */
 	struct completion complete;
 	spinlock_t irq_lock;
+	uint32_t irq_mask;
 	uint32_t irq_status;
 	int irq;
 
-- 
2.7.4

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

* [RESEND PATCH v2 38/53] mtd: nand: denali: fix NAND_CMD_STATUS handling
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
                   ` (9 preceding siblings ...)
  2017-03-23  0:17 ` [RESEND PATCH v2 37/53] mtd: nand: denali: rework interrupt handling Masahiro Yamada
@ 2017-03-23  0:17 ` Masahiro Yamada
  2017-03-23  0:17 ` [RESEND PATCH v2 39/53] mtd: nand: denali: fix NAND_CMD_PARAM handling Masahiro Yamada
                   ` (14 subsequent siblings)
  25 siblings, 0 replies; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

The current NAND_CMD_STATUS handling is weird; it just reads the
WRITE_PROTECT register, and returns NAND_STATUS_WP if it is set.

It does not send Read Status (0x70) command, so it is not helpful
for checking the current device status.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
  - Newly added

 drivers/mtd/nand/denali.c | 21 +++++----------------
 1 file changed, 5 insertions(+), 16 deletions(-)

diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 5aafa59..275f1fa 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -107,21 +107,6 @@ static void write_byte_to_buf(struct denali_nand_info *denali, uint8_t byte)
 	denali->buf.buf[denali->buf.tail++] = byte;
 }
 
-/* reads the status of the device */
-static void read_status(struct denali_nand_info *denali)
-{
-	uint32_t cmd;
-
-	/* initialize the data buffer to store status */
-	reset_buf(denali);
-
-	cmd = ioread32(denali->flash_reg + WRITE_PROTECT);
-	if (cmd)
-		write_byte_to_buf(denali, NAND_STATUS_WP);
-	else
-		write_byte_to_buf(denali, 0);
-}
-
 /* Reset the flash controller */
 static uint16_t denali_nand_reset(struct denali_nand_info *denali)
 {
@@ -857,7 +842,11 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
 
 	switch (cmd) {
 	case NAND_CMD_STATUS:
-		read_status(denali);
+		reset_buf(denali);
+		addr = MODE_11 | BANK(denali->flash_bank);
+		index_addr(denali, addr | 0, cmd);
+		index_addr_read_data(denali, addr | 2, &id);
+		write_byte_to_buf(denali, id);
 		break;
 	case NAND_CMD_READID:
 	case NAND_CMD_PARAM:
-- 
2.7.4

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

* [RESEND PATCH v2 39/53] mtd: nand: denali: fix NAND_CMD_PARAM handling
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
                   ` (10 preceding siblings ...)
  2017-03-23  0:17 ` [RESEND PATCH v2 38/53] mtd: nand: denali: fix NAND_CMD_STATUS handling Masahiro Yamada
@ 2017-03-23  0:17 ` Masahiro Yamada
  2017-03-23  0:17 ` [RESEND PATCH v2 40/53] mtd: nand: do not check R/B# for CMD_READID in nand_command(_lp) Masahiro Yamada
                   ` (13 subsequent siblings)
  25 siblings, 0 replies; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

NAND_CMD_PARAM is not working at all due to multiple bugs.

[1] The command 0x90 issued instead of 0xec

The command code 0x90 is hard-code as
   index_addr(denali, addr | 0, 0x90)
So, Read ID (0x90) command is issued for both NAND_CMD_READID and
NAND_CMD_PARAM.

[2] only first 8 byte can be read

Even if [1] is fixed, the current implementation is problematic.
Currently, the only first 8 byte are read by MAP11 command, and
put into the temporal buffer.

    for (i = 0; i < 8; i++) {
            index_addr_read_data(denali, addr | 2, &id);
            write_byte_to_buf(denali, id);
    }

Obviously, this is not sufficient for NAND_CMD_PARAM; the ONFi
parameters are 256-byte long.  This is still insufficient.
As you see in nand_flash_detect_onfi() reads out 256 * 3 bytes
at maximum (Redundant Parameter Pages).  However, changing the loop
to for (i = 0; i < 768; i++) is a crazy idea.  At the point of the
chip->cmdfunc() call, we cannot know how many times chip->read_byte()
will be called.  So, pre-reading enough number of bytes in the
chip->cmdfunc() is a design mistake.

[3] no wait for R/B#

The current code handles NAND_CMD_READID and NAND_CMD_PARAM in the
same way, but this is also wrong.  The difference between them is
that Read ID command does not toggle R/B# whereas the Read Parameter
Page command requires R/B#.  Without the wait for R/B# interrupt,
wrong data are retrieved.

In order to fix those problems, data read cycle of the MAP11 command
has been moved to chip->read_byte().  Data are read out as needed.
Another good thing is early temporal buffer is not needed any more.
The ugly devm_kzalloc()/devm_kfree() dance has been killed.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
  - Newly added

 drivers/mtd/nand/denali.c | 95 +++++++++++++++--------------------------------
 drivers/mtd/nand/denali.h |  2 -
 2 files changed, 30 insertions(+), 67 deletions(-)

diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 275f1fa..c0b8179 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -85,28 +85,6 @@ static void index_addr(struct denali_nand_info *denali,
 	iowrite32(data, denali->flash_mem + 0x10);
 }
 
-/* Perform an indexed read of the device */
-static void index_addr_read_data(struct denali_nand_info *denali,
-				 uint32_t address, uint32_t *pdata)
-{
-	iowrite32(address, denali->flash_mem);
-	*pdata = ioread32(denali->flash_mem + 0x10);
-}
-
-/*
- * We need to buffer some data for some of the NAND core routines.
- * The operations manage buffering that data.
- */
-static void reset_buf(struct denali_nand_info *denali)
-{
-	denali->buf.head = denali->buf.tail = 0;
-}
-
-static void write_byte_to_buf(struct denali_nand_info *denali, uint8_t byte)
-{
-	denali->buf.buf[denali->buf.tail++] = byte;
-}
-
 /* Reset the flash controller */
 static uint16_t denali_nand_reset(struct denali_nand_info *denali)
 {
@@ -286,6 +264,15 @@ static void setup_ecc_for_xfer(struct denali_nand_info *denali, bool ecc_en,
 	iowrite32(transfer_spare_flag, denali->flash_reg + TRANSFER_SPARE_REG);
 }
 
+static uint8_t denali_read_byte(struct mtd_info *mtd)
+{
+	struct denali_nand_info *denali = mtd_to_denali(mtd);
+
+	iowrite32(MODE_11 | BANK(denali->flash_bank) | 2, denali->flash_mem);
+
+	return ioread32(denali->flash_mem + 0x10);
+}
+
 /*
  * sends a pipeline command operation to the controller. See the Denali NAND
  * controller's user guide for more information (section 4.2.3.6).
@@ -792,17 +779,6 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
 	return 0;
 }
 
-static uint8_t denali_read_byte(struct mtd_info *mtd)
-{
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
-	uint8_t result = 0xff;
-
-	if (denali->buf.head < denali->buf.tail)
-		result = denali->buf.buf[denali->buf.head++];
-
-	return result;
-}
-
 static void denali_select_chip(struct mtd_info *mtd, int chip)
 {
 	struct denali_nand_info *denali = mtd_to_denali(mtd);
@@ -837,43 +813,40 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
 			   int page)
 {
 	struct denali_nand_info *denali = mtd_to_denali(mtd);
-	uint32_t addr, id;
-	int i;
+	uint32_t addr, irq_status;
+	int wait_ready = 0;
 
 	switch (cmd) {
-	case NAND_CMD_STATUS:
-		reset_buf(denali);
-		addr = MODE_11 | BANK(denali->flash_bank);
-		index_addr(denali, addr | 0, cmd);
-		index_addr_read_data(denali, addr | 2, &id);
-		write_byte_to_buf(denali, id);
+	case NAND_CMD_PARAM:
+		wait_ready = 1;
 		break;
+	case NAND_CMD_STATUS:
 	case NAND_CMD_READID:
-	case NAND_CMD_PARAM:
-		reset_buf(denali);
-		/*
-		 * sometimes ManufactureId read from register is not right
-		 * e.g. some of Micron MT29F32G08QAA MLC NAND chips
-		 * So here we send READID cmd to NAND insteand
-		 */
-		addr = MODE_11 | BANK(denali->flash_bank);
-		index_addr(denali, addr | 0, 0x90);
-		index_addr(denali, addr | 1, col);
-		for (i = 0; i < 8; i++) {
-			index_addr_read_data(denali, addr | 2, &id);
-			write_byte_to_buf(denali, id);
-		}
 		break;
 	case NAND_CMD_RESET:
 		reset_bank(denali);
 		break;
 	case NAND_CMD_READOOB:
 		/* TODO: Read OOB data */
-		break;
+		return;
 	default:
 		pr_err(": unsupported command received 0x%x\n", cmd);
-		break;
+		return;
 	}
+
+	denali_reset_irq(denali);
+
+	addr = MODE_11 | BANK(denali->flash_bank);
+	index_addr(denali, addr | 0, cmd);
+	if (col != -1)
+		index_addr(denali, addr | 1, col);
+
+	if (!wait_ready)
+		return;
+
+	irq_status = denali_wait_for_irq(denali, INTR__INT_ACT);
+	if (!(irq_status & INTR__INT_ACT))
+		dev_err(denali->dev, "failed to issue command 0x%x\n", cmd);
 }
 
 #define DIV_ROUND_DOWN_ULL(ll, d) \
@@ -1194,12 +1167,6 @@ int denali_init(struct denali_nand_info *denali)
 	struct mtd_info *mtd = nand_to_mtd(chip);
 	int ret;
 
-	/* allocate a temporary buffer for nand_scan_ident() */
-	denali->buf.buf = devm_kzalloc(denali->dev, PAGE_SIZE,
-					GFP_DMA | GFP_KERNEL);
-	if (!denali->buf.buf)
-		return -ENOMEM;
-
 	mtd->dev.parent = denali->dev;
 	denali_hw_init(denali);
 	denali_drv_init(denali);
@@ -1236,8 +1203,6 @@ int denali_init(struct denali_nand_info *denali)
 	if (ret)
 		goto disable_irq;
 
-	/* allocate the right size buffer now */
-	devm_kfree(denali->dev, denali->buf.buf);
 	denali->buf.buf = devm_kzalloc(denali->dev,
 			     mtd->writesize + mtd->oobsize,
 			     GFP_KERNEL);
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index ea709a4..0f4f528 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -311,8 +311,6 @@
 #define MODE_11    0x0C000000
 
 struct nand_buf {
-	int head;
-	int tail;
 	uint8_t *buf;
 	dma_addr_t dma_buf;
 };
-- 
2.7.4

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

* [RESEND PATCH v2 40/53] mtd: nand: do not check R/B# for CMD_READID in nand_command(_lp)
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
                   ` (11 preceding siblings ...)
  2017-03-23  0:17 ` [RESEND PATCH v2 39/53] mtd: nand: denali: fix NAND_CMD_PARAM handling Masahiro Yamada
@ 2017-03-23  0:17 ` Masahiro Yamada
  2017-03-23  0:17 ` [RESEND PATCH v2 41/53] mtd: nand: do not check R/B# for CMD_SET_FEATURES " Masahiro Yamada
                   ` (12 subsequent siblings)
  25 siblings, 0 replies; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

Read ID (0x90) command does not toggle the R/B# pin.  Without this
patch, NAND_CMD_READID falls into the default: label, then R/B# is
checked by chip->dev_ready().

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
  - Newly added

 drivers/mtd/nand/nand_base.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 807398d..c4c3329 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -668,6 +668,7 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
 	case NAND_CMD_ERASE2:
 	case NAND_CMD_SEQIN:
 	case NAND_CMD_STATUS:
+	case NAND_CMD_READID:
 		return;
 
 	case NAND_CMD_RESET:
@@ -786,6 +787,7 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
 	case NAND_CMD_ERASE2:
 	case NAND_CMD_SEQIN:
 	case NAND_CMD_STATUS:
+	case NAND_CMD_READID:
 		return;
 
 	case NAND_CMD_RNDIN:
-- 
2.7.4

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

* [RESEND PATCH v2 41/53] mtd: nand: do not check R/B# for CMD_SET_FEATURES in nand_command(_lp)
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
                   ` (12 preceding siblings ...)
  2017-03-23  0:17 ` [RESEND PATCH v2 40/53] mtd: nand: do not check R/B# for CMD_READID in nand_command(_lp) Masahiro Yamada
@ 2017-03-23  0:17 ` Masahiro Yamada
  2017-03-23  0:17 ` [RESEND PATCH v2 42/53] mtd: nand: denali: switch over to cmd_ctrl instead of cmdfunc Masahiro Yamada
                   ` (11 subsequent siblings)
  25 siblings, 0 replies; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

Set Features (0xEF) command toggles the R/B# pin after 4 sub feature
parameters are written.

Currently, nand_command(_lp) calls chip->dev_ready immediately after
the address cycle because NAND_CMD_SET_FEATURES falls into default:
label.  No wait is needed at this point.

If you see nand_onfi_set_features(), R/B# is already cared by the
chip->waitfunc call.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
  - Newly added

 drivers/mtd/nand/nand_base.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index c4c3329..e13f959 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -669,6 +669,7 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
 	case NAND_CMD_SEQIN:
 	case NAND_CMD_STATUS:
 	case NAND_CMD_READID:
+	case NAND_CMD_SET_FEATURES:
 		return;
 
 	case NAND_CMD_RESET:
@@ -788,6 +789,7 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
 	case NAND_CMD_SEQIN:
 	case NAND_CMD_STATUS:
 	case NAND_CMD_READID:
+	case NAND_CMD_SET_FEATURES:
 		return;
 
 	case NAND_CMD_RNDIN:
-- 
2.7.4

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

* [RESEND PATCH v2 42/53] mtd: nand: denali: switch over to cmd_ctrl instead of cmdfunc
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
                   ` (13 preceding siblings ...)
  2017-03-23  0:17 ` [RESEND PATCH v2 41/53] mtd: nand: do not check R/B# for CMD_SET_FEATURES " Masahiro Yamada
@ 2017-03-23  0:17 ` Masahiro Yamada
  2017-03-23  8:52   ` Boris Brezillon
  2017-03-23  0:17 ` [RESEND PATCH v2 43/53] mtd: nand: denali: fix bank reset function Masahiro Yamada
                   ` (10 subsequent siblings)
  25 siblings, 1 reply; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

The NAND_CMD_SET_FEATURES support is missing from denali_cmdfunc().
This is needed for nand_onfi_set_features().  It would be possible
to add it in the current implementation, but having ->cmd_ctrl()
seems a better approach from the discussion with Boris [1].

Rely on the default implementation for ->cmdfunc() and implement
the driver's own ->cmd_ctrl().

Also add ->write_byte(), which is needed to write parameters for
NAND_CMD_SET_FEATURES.

[1] https://lkml.org/lkml/2017/3/15/97

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
  - Newly added

 drivers/mtd/nand/denali.c | 104 +++++++++++++++++++++++-----------------------
 1 file changed, 52 insertions(+), 52 deletions(-)

diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index c0b8179..2d25b2f 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -230,20 +230,16 @@ static uint32_t denali_wait_for_irq(struct denali_nand_info *denali,
 	return denali->irq_status;
 }
 
-/* resets a specific device connected to the core */
-static void reset_bank(struct denali_nand_info *denali)
+static uint32_t denali_check_irq(struct denali_nand_info *denali)
 {
+	unsigned long flags;
 	uint32_t irq_status;
 
-	denali_reset_irq(denali);
-
-	iowrite32(1 << denali->flash_bank, denali->flash_reg + DEVICE_RESET);
-
-	irq_status = denali_wait_for_irq(denali,
-					 INTR__RST_COMP | INTR__TIME_OUT);
+	spin_lock_irqsave(&denali->irq_lock, flags);
+	irq_status = denali->irq_status;
+	spin_unlock_irqrestore(&denali->irq_lock, flags);
 
-	if (!(irq_status & INTR__RST_COMP))
-		dev_err(denali->dev, "reset bank failed.\n");
+	return irq_status;
 }
 
 /*
@@ -273,6 +269,42 @@ static uint8_t denali_read_byte(struct mtd_info *mtd)
 	return ioread32(denali->flash_mem + 0x10);
 }
 
+static void denali_write_byte(struct mtd_info *mtd, uint8_t byte)
+{
+	struct denali_nand_info *denali = mtd_to_denali(mtd);
+
+	index_addr(denali, MODE_11 | BANK(denali->flash_bank) | 2, byte);
+}
+
+static void denali_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
+{
+	struct denali_nand_info *denali = mtd_to_denali(mtd);
+	uint32_t type;
+
+	if (ctrl & NAND_CLE)
+		type = 0;
+	else if (ctrl & NAND_ALE)
+		type = 1;
+	else
+		return;
+
+	/*
+	 * Some commands are followed by chip->dev_ready or chip->waitfunc.
+	 * irq_status must be cleared here to catch the R/B# interrupt later.
+	 */
+	if (ctrl & NAND_CTRL_CHANGE)
+		denali_reset_irq(denali);
+
+	index_addr(denali, MODE_11 | BANK(denali->flash_bank) | type, dat);
+}
+
+static int denali_dev_ready(struct mtd_info *mtd)
+{
+	struct denali_nand_info *denali = mtd_to_denali(mtd);
+
+	return !!(denali_check_irq(denali) & INTR__INT_ACT);
+}
+
 /*
  * sends a pipeline command operation to the controller. See the Denali NAND
  * controller's user guide for more information (section 4.2.3.6).
@@ -788,7 +820,13 @@ static void denali_select_chip(struct mtd_info *mtd, int chip)
 
 static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
 {
-	return 0;
+	struct denali_nand_info *denali = mtd_to_denali(mtd);
+	uint32_t irq_status;
+
+	/* R/B# pin transitioned from low to high? */
+	irq_status = denali_wait_for_irq(denali, INTR__INT_ACT);
+
+	return irq_status & INTR__INT_ACT ? 0 : NAND_STATUS_FAIL;
 }
 
 static int denali_erase(struct mtd_info *mtd, int page)
@@ -809,46 +847,6 @@ static int denali_erase(struct mtd_info *mtd, int page)
 	return irq_status & INTR__ERASE_COMP ? 0 : NAND_STATUS_FAIL;
 }
 
-static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
-			   int page)
-{
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
-	uint32_t addr, irq_status;
-	int wait_ready = 0;
-
-	switch (cmd) {
-	case NAND_CMD_PARAM:
-		wait_ready = 1;
-		break;
-	case NAND_CMD_STATUS:
-	case NAND_CMD_READID:
-		break;
-	case NAND_CMD_RESET:
-		reset_bank(denali);
-		break;
-	case NAND_CMD_READOOB:
-		/* TODO: Read OOB data */
-		return;
-	default:
-		pr_err(": unsupported command received 0x%x\n", cmd);
-		return;
-	}
-
-	denali_reset_irq(denali);
-
-	addr = MODE_11 | BANK(denali->flash_bank);
-	index_addr(denali, addr | 0, cmd);
-	if (col != -1)
-		index_addr(denali, addr | 1, col);
-
-	if (!wait_ready)
-		return;
-
-	irq_status = denali_wait_for_irq(denali, INTR__INT_ACT);
-	if (!(irq_status & INTR__INT_ACT))
-		dev_err(denali->dev, "failed to issue command 0x%x\n", cmd);
-}
-
 #define DIV_ROUND_DOWN_ULL(ll, d) \
 	({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; })
 
@@ -1187,8 +1185,10 @@ int denali_init(struct denali_nand_info *denali)
 
 	/* register the driver with the NAND core subsystem */
 	chip->select_chip = denali_select_chip;
-	chip->cmdfunc = denali_cmdfunc;
 	chip->read_byte = denali_read_byte;
+	chip->write_byte = denali_write_byte;
+	chip->cmd_ctrl = denali_cmd_ctrl;
+	chip->dev_ready = denali_dev_ready;
 	chip->waitfunc = denali_waitfunc;
 	/* clk rate info is needed for setup_data_interface */
 	if (denali->clk_x_rate)
-- 
2.7.4

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

* [RESEND PATCH v2 43/53] mtd: nand: denali: fix bank reset function
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
                   ` (14 preceding siblings ...)
  2017-03-23  0:17 ` [RESEND PATCH v2 42/53] mtd: nand: denali: switch over to cmd_ctrl instead of cmdfunc Masahiro Yamada
@ 2017-03-23  0:17 ` Masahiro Yamada
  2017-03-23  0:17 ` [RESEND PATCH v2 44/53] mtd: nand: denali: use interrupt instead of polling for bank reset Masahiro Yamada
                   ` (9 subsequent siblings)
  25 siblings, 0 replies; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

The function denali_nand_reset() is called during the driver probe,
and polls the INTR__RST_COMP and INTR__TIME_OUT bits.  However,
INTR__RST_COMP is set anyway even if no NAND device is connected to
that bank.

This can be a problem for ONFi devices.  The nand_scan_ident()
iterates over maxchips, and calls nand_reset() for each chip.
Now, this driver implements ->setup_data_interface() method, so
nand_setup_data_interface() issues Set Features (0xEF) command to
each chip.  This can cause time-out error since denali_nand_reset()
did not check the chip existence.  If no chip there, the controller
will wait long for R/B# response, which never happens.  (The timeout
error is correctly handled in this driver, so the driver will be
successfully probed anyway, but it will take longer than needed.)

The Reset (0xFF) command also toggles the R/B# pin, and it sets
INTR__INT_ACT bit.  The driver should check this bit to see if the
chip has responded, then it can update denali->max_banks.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
  - Newly added

 drivers/mtd/nand/denali.c | 52 +++++++++++++++++++++--------------------------
 1 file changed, 23 insertions(+), 29 deletions(-)

diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 2d25b2f..ef28411 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -85,33 +85,6 @@ static void index_addr(struct denali_nand_info *denali,
 	iowrite32(data, denali->flash_mem + 0x10);
 }
 
-/* Reset the flash controller */
-static uint16_t denali_nand_reset(struct denali_nand_info *denali)
-{
-	int i;
-
-	for (i = 0; i < denali->max_banks; i++)
-		iowrite32(INTR__RST_COMP | INTR__TIME_OUT,
-		denali->flash_reg + INTR_STATUS(i));
-
-	for (i = 0; i < denali->max_banks; i++) {
-		iowrite32(1 << i, denali->flash_reg + DEVICE_RESET);
-		while (!(ioread32(denali->flash_reg + INTR_STATUS(i)) &
-			(INTR__RST_COMP | INTR__TIME_OUT)))
-			cpu_relax();
-		if (ioread32(denali->flash_reg + INTR_STATUS(i)) &
-			INTR__TIME_OUT)
-			dev_dbg(denali->dev,
-			"NAND Reset operation timed out on bank %d\n", i);
-	}
-
-	for (i = 0; i < denali->max_banks; i++)
-		iowrite32(INTR__RST_COMP | INTR__TIME_OUT,
-			  denali->flash_reg + INTR_STATUS(i));
-
-	return PASS;
-}
-
 /*
  * Use the configuration feature register to determine the maximum number of
  * banks that the hardware supports.
@@ -963,7 +936,28 @@ static int denali_setup_data_interface(struct mtd_info *mtd,
 	return 0;
 }
 
-/* Initialization code to bring the device up to a known good state */
+static void denali_reset_banks(struct denali_nand_info *denali)
+{
+	int i;
+
+	denali_clear_irq_all(denali);
+
+	for (i = 0; i < denali->max_banks; i++) {
+		iowrite32(1 << i, denali->flash_reg + DEVICE_RESET);
+		while (!(ioread32(denali->flash_reg + INTR_STATUS(i)) &
+			(INTR__RST_COMP | INTR__TIME_OUT)))
+			cpu_relax();
+		if (!(ioread32(denali->flash_reg + INTR_STATUS(i)) &
+		      INTR__INT_ACT))
+			break;
+	}
+
+	dev_dbg(denali->dev, "%d chips connected\n", i);
+	denali->max_banks = i;
+
+	denali_clear_irq_all(denali);
+}
+
 static void denali_hw_init(struct denali_nand_info *denali)
 {
 	if (!denali->revision)
@@ -979,7 +973,7 @@ static void denali_hw_init(struct denali_nand_info *denali)
 	denali->bbtskipbytes = ioread32(denali->flash_reg +
 						SPARE_AREA_SKIP_BYTES);
 	detect_max_banks(denali);
-	denali_nand_reset(denali);
+	denali_reset_banks(denali);
 	iowrite32(0x0F, denali->flash_reg + RB_PIN_ENABLED);
 	iowrite32(CHIP_EN_DONT_CARE__FLAG,
 			denali->flash_reg + CHIP_ENABLE_DONT_CARE);
-- 
2.7.4

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

* [RESEND PATCH v2 44/53] mtd: nand: denali: use interrupt instead of polling for bank reset
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
                   ` (15 preceding siblings ...)
  2017-03-23  0:17 ` [RESEND PATCH v2 43/53] mtd: nand: denali: fix bank reset function Masahiro Yamada
@ 2017-03-23  0:17 ` Masahiro Yamada
  2017-03-23  0:17 ` [RESEND PATCH v2 45/53] mtd: nand: denali: propagate page to helpers via function argument Masahiro Yamada
                   ` (8 subsequent siblings)
  25 siblings, 0 replies; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

The current bank reset implementation polls the INTR_STATUS register
until interested bits are set.  This is not good because:

- it simply wastes time-slice of the thread which the driver module
  is loaded from.

- The while() loop may continue eternally if no bit is set, for
  example, due to the controller problem.  The denali_wait_for_irq()
  uses wait_for_completion_timeout(), which is safer.

We can use interrupt by moving the denali_reset_bank() call below
the interrupt setup.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
  - Newly added

 drivers/mtd/nand/denali.c | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index ef28411..7609ad6 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -938,24 +938,25 @@ static int denali_setup_data_interface(struct mtd_info *mtd,
 
 static void denali_reset_banks(struct denali_nand_info *denali)
 {
+	u32 irq_status;
 	int i;
 
-	denali_clear_irq_all(denali);
-
 	for (i = 0; i < denali->max_banks; i++) {
-		iowrite32(1 << i, denali->flash_reg + DEVICE_RESET);
-		while (!(ioread32(denali->flash_reg + INTR_STATUS(i)) &
-			(INTR__RST_COMP | INTR__TIME_OUT)))
-			cpu_relax();
-		if (!(ioread32(denali->flash_reg + INTR_STATUS(i)) &
-		      INTR__INT_ACT))
+		denali->flash_bank = i;
+
+		denali_reset_irq(denali);
+
+		iowrite32(DEVICE_RESET__BANK(i),
+			  denali->flash_reg + DEVICE_RESET);
+
+		irq_status = denali_wait_for_irq(denali,
+			INTR__RST_COMP | INTR__INT_ACT | INTR__TIME_OUT);
+		if (!(irq_status & INTR__INT_ACT))
 			break;
 	}
 
 	dev_dbg(denali->dev, "%d chips connected\n", i);
 	denali->max_banks = i;
-
-	denali_clear_irq_all(denali);
 }
 
 static void denali_hw_init(struct denali_nand_info *denali)
@@ -973,7 +974,6 @@ static void denali_hw_init(struct denali_nand_info *denali)
 	denali->bbtskipbytes = ioread32(denali->flash_reg +
 						SPARE_AREA_SKIP_BYTES);
 	detect_max_banks(denali);
-	denali_reset_banks(denali);
 	iowrite32(0x0F, denali->flash_reg + RB_PIN_ENABLED);
 	iowrite32(CHIP_EN_DONT_CARE__FLAG,
 			denali->flash_reg + CHIP_ENABLE_DONT_CARE);
@@ -1097,9 +1097,6 @@ static void denali_drv_init(struct denali_nand_info *denali)
 	 * element that might be access shared data (interrupt status)
 	 */
 	spin_lock_init(&denali->irq_lock);
-
-	/* indicate that MTD has not selected a valid bank yet */
-	denali->flash_bank = CHIP_SELECT_INVALID;
 }
 
 static int denali_multidev_fixup(struct denali_nand_info *denali)
@@ -1174,6 +1171,9 @@ int denali_init(struct denali_nand_info *denali)
 	}
 
 	denali_enable_irq(denali);
+	denali_reset_banks(denali);
+
+	denali->flash_bank = CHIP_SELECT_INVALID;
 
 	nand_set_flash_node(chip, denali->dev->of_node);
 
-- 
2.7.4

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

* [RESEND PATCH v2 45/53] mtd: nand: denali: propagate page to helpers via function argument
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
                   ` (16 preceding siblings ...)
  2017-03-23  0:17 ` [RESEND PATCH v2 44/53] mtd: nand: denali: use interrupt instead of polling for bank reset Masahiro Yamada
@ 2017-03-23  0:17 ` Masahiro Yamada
  2017-03-23  0:17 ` [RESEND PATCH v2 46/53] mtd: nand: denali: merge struct nand_buf into struct denali_nand_info Masahiro Yamada
                   ` (7 subsequent siblings)
  25 siblings, 0 replies; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

This driver stores the currently addressed page into denali->page,
which is later read out by helper functions.  While I am tackling on
this driver, I often missed to insert "denali->page = page;" where
necessary.  This makes page_read/write callbacks to get access to a
wrong page, which is a bug hard to figure out.

Instead, I'd rather pass the page via function argument because the
compiler's prototype checks will help to detect bugs.

For the same reason, propagate dma_addr to the DMA helpers instead
of denali->buf.dma_buf .

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
  - Newly added

 drivers/mtd/nand/denali.c | 58 ++++++++++++++++++++---------------------------
 drivers/mtd/nand/denali.h |  1 -
 2 files changed, 24 insertions(+), 35 deletions(-)

diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 7609ad6..e08eab6 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -282,7 +282,7 @@ static int denali_dev_ready(struct mtd_info *mtd)
  * sends a pipeline command operation to the controller. See the Denali NAND
  * controller's user guide for more information (section 4.2.3.6).
  */
-static int denali_send_pipeline_cmd(struct denali_nand_info *denali,
+static int denali_send_pipeline_cmd(struct denali_nand_info *denali, int page,
 				    bool ecc_en, bool transfer_spare,
 				    int access_type, int op)
 {
@@ -293,7 +293,7 @@ static int denali_send_pipeline_cmd(struct denali_nand_info *denali,
 
 	denali_reset_irq(denali);
 
-	addr = BANK(denali->flash_bank) | denali->page;
+	addr = BANK(denali->flash_bank) | page;
 
 	if (op == DENALI_WRITE && access_type != SPARE_ACCESS) {
 		cmd = MODE_01 | addr;
@@ -366,9 +366,7 @@ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
 	uint32_t irq_mask = INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL;
 	int status = 0;
 
-	denali->page = page;
-
-	if (denali_send_pipeline_cmd(denali, false, false, SPARE_ACCESS,
+	if (denali_send_pipeline_cmd(denali, page, false, false, SPARE_ACCESS,
 							DENALI_WRITE) == PASS) {
 		write_data_to_flash_mem(denali, buf, mtd->oobsize);
 
@@ -393,9 +391,7 @@ static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
 	uint32_t irq_mask = INTR__LOAD_COMP;
 	uint32_t irq_status, addr, cmd;
 
-	denali->page = page;
-
-	if (denali_send_pipeline_cmd(denali, false, true, SPARE_ACCESS,
+	if (denali_send_pipeline_cmd(denali, page, false, true, SPARE_ACCESS,
 							DENALI_READ) == PASS) {
 		read_data_from_flash_mem(denali, buf, mtd->oobsize);
 
@@ -407,8 +403,7 @@ static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
 		irq_status = denali_wait_for_irq(denali, irq_mask);
 
 		if (!(irq_status & INTR__LOAD_COMP))
-			dev_err(denali->dev, "page on OOB timeout %d\n",
-					denali->page);
+			dev_err(denali->dev, "page on OOB timeout %d\n", page);
 
 		/*
 		 * We set the device back to MAIN_ACCESS here as I observed
@@ -417,7 +412,7 @@ static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
 		 * is reliable (according to the MTD test infrastructure)
 		 * if you are in MAIN_ACCESS.
 		 */
-		addr = BANK(denali->flash_bank) | denali->page;
+		addr = BANK(denali->flash_bank) | page;
 		cmd = MODE_10 | addr;
 		index_addr(denali, cmd, MAIN_ACCESS);
 	}
@@ -537,13 +532,13 @@ static void denali_enable_dma(struct denali_nand_info *denali, bool en)
 	ioread32(denali->flash_reg + DMA_ENABLE);
 }
 
-static void denali_setup_dma64(struct denali_nand_info *denali, int op)
+static void denali_setup_dma64(struct denali_nand_info *denali,
+			       dma_addr_t dma_addr, int page, int op)
 {
 	uint32_t mode;
 	const int page_count = 1;
-	uint64_t addr = denali->buf.dma_buf;
 
-	mode = MODE_10 | BANK(denali->flash_bank) | denali->page;
+	mode = MODE_10 | BANK(denali->flash_bank) | page;
 
 	/* DMA is a three step process */
 
@@ -554,41 +549,42 @@ static void denali_setup_dma64(struct denali_nand_info *denali, int op)
 	index_addr(denali, mode, 0x01002000 | (64 << 16) | op | page_count);
 
 	/* 2. set memory low address */
-	index_addr(denali, mode, addr);
+	index_addr(denali, mode, dma_addr);
 
 	/* 3. set memory high address */
-	index_addr(denali, mode, addr >> 32);
+	index_addr(denali, mode, (uint64_t)dma_addr >> 32);
 }
 
-static void denali_setup_dma32(struct denali_nand_info *denali, int op)
+static void denali_setup_dma32(struct denali_nand_info *denali,
+			       dma_addr_t dma_addr, int page, int op)
 {
 	uint32_t mode;
 	const int page_count = 1;
-	uint32_t addr = denali->buf.dma_buf;
 
 	mode = MODE_10 | BANK(denali->flash_bank);
 
 	/* DMA is a four step process */
 
 	/* 1. setup transfer type and # of pages */
-	index_addr(denali, mode | denali->page, 0x2000 | op | page_count);
+	index_addr(denali, mode | page, 0x2000 | op | page_count);
 
 	/* 2. set memory high address bits 23:8 */
-	index_addr(denali, mode | ((addr >> 16) << 8), 0x2200);
+	index_addr(denali, mode | ((dma_addr >> 16) << 8), 0x2200);
 
 	/* 3. set memory low address bits 23:8 */
-	index_addr(denali, mode | ((addr & 0xffff) << 8), 0x2300);
+	index_addr(denali, mode | ((dma_addr & 0xffff) << 8), 0x2300);
 
 	/* 4. interrupt when complete, burst len = 64 bytes */
 	index_addr(denali, mode | 0x14000, 0x2400);
 }
 
-static void denali_setup_dma(struct denali_nand_info *denali, int op)
+static void denali_setup_dma(struct denali_nand_info *denali,
+			     dma_addr_t dma_addr, int page, int op)
 {
 	if (denali->caps & DENALI_CAP_DMA_64BIT)
-		denali_setup_dma64(denali, op);
+		denali_setup_dma64(denali, dma_addr, page, op);
 	else
-		denali_setup_dma32(denali, op);
+		denali_setup_dma32(denali, dma_addr, page, op);
 }
 
 /*
@@ -605,8 +601,6 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
 	uint32_t irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
 	int ret = 0;
 
-	denali->page = page;
-
 	/*
 	 * if it is a raw xfer, we want to disable ecc and send the spare area.
 	 * !raw_xfer - enable ecc
@@ -629,7 +623,7 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
 	denali_reset_irq(denali);
 	denali_enable_dma(denali, true);
 
-	denali_setup_dma(denali, DENALI_WRITE);
+	denali_setup_dma(denali, addr, page, DENALI_WRITE);
 
 	/* wait for operation to complete */
 	irq_status = denali_wait_for_irq(denali, irq_mask);
@@ -704,15 +698,13 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
 				INTR__ECC_TRANSACTION_DONE | INTR__ECC_ERR;
 	int stat = 0;
 
-	denali->page = page;
-
 	setup_ecc_for_xfer(denali, true, false);
 
 	denali_enable_dma(denali, true);
 	dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE);
 
 	denali_reset_irq(denali);
-	denali_setup_dma(denali, DENALI_READ);
+	denali_setup_dma(denali, addr, page, DENALI_READ);
 
 	/* wait for operation to complete */
 	irq_status = denali_wait_for_irq(denali, irq_mask);
@@ -733,7 +725,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
 		 * see if it is an erased page. If so, then it's not a real ECC
 		 * error.
 		 */
-		read_oob_data(mtd, chip->oob_poi, denali->page);
+		read_oob_data(mtd, chip->oob_poi, page);
 
 		stat = nand_check_erased_ecc_chunk(
 					buf, mtd->writesize,
@@ -759,15 +751,13 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
 	uint32_t irq_mask = INTR__DMA_CMD_COMP;
 	uint32_t irq_status;
 
-	denali->page = page;
-
 	setup_ecc_for_xfer(denali, false, true);
 	denali_enable_dma(denali, true);
 
 	dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE);
 
 	denali_reset_irq(denali);
-	denali_setup_dma(denali, DENALI_READ);
+	denali_setup_dma(denali, addr, page, DENALI_READ);
 
 	/* wait for operation to complete */
 	irq_status = denali_wait_for_irq(denali, irq_mask);
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index 0f4f528..2cc270c6 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -321,7 +321,6 @@ struct denali_nand_info {
 	int flash_bank; /* currently selected chip */
 	struct nand_buf buf;
 	struct device *dev;
-	int page;
 	void __iomem *flash_reg;	/* Register Interface */
 	void __iomem *flash_mem;	/* Host Data/Command Interface */
 
-- 
2.7.4

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

* [RESEND PATCH v2 46/53] mtd: nand: denali: merge struct nand_buf into struct denali_nand_info
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
                   ` (17 preceding siblings ...)
  2017-03-23  0:17 ` [RESEND PATCH v2 45/53] mtd: nand: denali: propagate page to helpers via function argument Masahiro Yamada
@ 2017-03-23  0:17 ` Masahiro Yamada
  2017-03-23  0:17 ` [RESEND PATCH v2 47/53] mtd: nand: denali: use flag instead of register macro for direction Masahiro Yamada
                   ` (6 subsequent siblings)
  25 siblings, 0 replies; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

Now struct nand_buf has only two members, so I see no reason for the
separation.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
  - Newly added

 drivers/mtd/nand/denali.c | 29 ++++++++++++++---------------
 drivers/mtd/nand/denali.h |  8 ++------
 2 files changed, 16 insertions(+), 21 deletions(-)

diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index e08eab6..e9e164c 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -595,7 +595,7 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
 			const uint8_t *buf, int page, bool raw_xfer)
 {
 	struct denali_nand_info *denali = mtd_to_denali(mtd);
-	dma_addr_t addr = denali->buf.dma_buf;
+	dma_addr_t addr = denali->dma_addr;
 	size_t size = mtd->writesize + mtd->oobsize;
 	uint32_t irq_status;
 	uint32_t irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
@@ -609,11 +609,11 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
 	setup_ecc_for_xfer(denali, !raw_xfer, raw_xfer);
 
 	/* copy buffer into DMA buffer */
-	memcpy(denali->buf.buf, buf, mtd->writesize);
+	memcpy(denali->buf, buf, mtd->writesize);
 
 	if (raw_xfer) {
 		/* transfer the data to the spare area */
-		memcpy(denali->buf.buf + mtd->writesize,
+		memcpy(denali->buf + mtd->writesize,
 			chip->oob_poi,
 			mtd->oobsize);
 	}
@@ -690,7 +690,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
 			    uint8_t *buf, int oob_required, int page)
 {
 	struct denali_nand_info *denali = mtd_to_denali(mtd);
-	dma_addr_t addr = denali->buf.dma_buf;
+	dma_addr_t addr = denali->dma_addr;
 	size_t size = mtd->writesize + mtd->oobsize;
 	uint32_t irq_status;
 	uint32_t irq_mask = denali->caps & DENALI_CAP_HW_ECC_FIXUP ?
@@ -711,7 +711,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
 
 	dma_sync_single_for_cpu(denali->dev, addr, size, DMA_FROM_DEVICE);
 
-	memcpy(buf, denali->buf.buf, mtd->writesize);
+	memcpy(buf, denali->buf, mtd->writesize);
 
 	if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
 		stat = denali_hw_ecc_fixup(mtd, denali);
@@ -746,7 +746,7 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
 				uint8_t *buf, int oob_required, int page)
 {
 	struct denali_nand_info *denali = mtd_to_denali(mtd);
-	dma_addr_t addr = denali->buf.dma_buf;
+	dma_addr_t addr = denali->dma_addr;
 	size_t size = mtd->writesize + mtd->oobsize;
 	uint32_t irq_mask = INTR__DMA_CMD_COMP;
 	uint32_t irq_status;
@@ -768,8 +768,8 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
 
 	denali_enable_dma(denali, false);
 
-	memcpy(buf, denali->buf.buf, mtd->writesize);
-	memcpy(chip->oob_poi, denali->buf.buf + mtd->writesize, mtd->oobsize);
+	memcpy(buf, denali->buf, mtd->writesize);
+	memcpy(chip->oob_poi, denali->buf + mtd->writesize, mtd->oobsize);
 
 	return 0;
 }
@@ -1187,10 +1187,9 @@ int denali_init(struct denali_nand_info *denali)
 	if (ret)
 		goto disable_irq;
 
-	denali->buf.buf = devm_kzalloc(denali->dev,
-			     mtd->writesize + mtd->oobsize,
-			     GFP_KERNEL);
-	if (!denali->buf.buf) {
+	denali->buf = devm_kzalloc(denali->dev, mtd->writesize + mtd->oobsize,
+				   GFP_KERNEL);
+	if (!denali->buf) {
 		ret = -ENOMEM;
 		goto disable_irq;
 	}
@@ -1203,10 +1202,10 @@ int denali_init(struct denali_nand_info *denali)
 		goto disable_irq;
 	}
 
-	denali->buf.dma_buf = dma_map_single(denali->dev, denali->buf.buf,
+	denali->dma_addr = dma_map_single(denali->dev, denali->buf,
 			     mtd->writesize + mtd->oobsize,
 			     DMA_BIDIRECTIONAL);
-	if (dma_mapping_error(denali->dev, denali->buf.dma_buf)) {
+	if (dma_mapping_error(denali->dev, denali->dma_addr)) {
 		dev_err(denali->dev, "Failed to map DMA buffer\n");
 		ret = -EIO;
 		goto disable_irq;
@@ -1328,7 +1327,7 @@ void denali_remove(struct denali_nand_info *denali)
 
 	nand_release(mtd);
 	denali_disable_irq(denali);
-	dma_unmap_single(denali->dev, denali->buf.dma_buf, bufsize,
+	dma_unmap_single(denali->dev, denali->dma_addr, bufsize,
 			 DMA_BIDIRECTIONAL);
 }
 EXPORT_SYMBOL(denali_remove);
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index 2cc270c6..332db96 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -310,16 +310,10 @@
 #define MODE_10    0x08000000
 #define MODE_11    0x0C000000
 
-struct nand_buf {
-	uint8_t *buf;
-	dma_addr_t dma_buf;
-};
-
 struct denali_nand_info {
 	struct nand_chip nand;
 	unsigned long clk_x_rate;	/* bus interface clock rate */
 	int flash_bank; /* currently selected chip */
-	struct nand_buf buf;
 	struct device *dev;
 	void __iomem *flash_reg;	/* Register Interface */
 	void __iomem *flash_mem;	/* Host Data/Command Interface */
@@ -331,6 +325,8 @@ struct denali_nand_info {
 	uint32_t irq_status;
 	int irq;
 
+	void *buf;
+	dma_addr_t dma_addr;
 	int devnum;	/* represent how many nands connected */
 	int bbtskipbytes;
 	int max_banks;
-- 
2.7.4

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

* [RESEND PATCH v2 47/53] mtd: nand: denali: use flag instead of register macro for direction
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
                   ` (18 preceding siblings ...)
  2017-03-23  0:17 ` [RESEND PATCH v2 46/53] mtd: nand: denali: merge struct nand_buf into struct denali_nand_info Masahiro Yamada
@ 2017-03-23  0:17 ` Masahiro Yamada
  2017-03-23  0:17 ` [RESEND PATCH v2 48/53] mtd: nand: denali: fix raw and oob accessors for syndrome page layout Masahiro Yamada
                   ` (5 subsequent siblings)
  25 siblings, 0 replies; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

It is not a good idea to re-use macros that represent a specific
register bit field for the transfer direction.

It is true that bit 8 indicates the direction for the MAP10 pipeline
operation and the data DMA operation, but this is not valid across
the IP.

Use a simple flag (write: 1, read: 0) for the direction.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
  - Newly added

 drivers/mtd/nand/denali.c | 36 +++++++++++++++++-------------------
 1 file changed, 17 insertions(+), 19 deletions(-)

diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index e9e164c..9049583 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -53,9 +53,6 @@ static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)
 #define MAIN_ACCESS		0x42
 #define MAIN_SPARE_ACCESS	0x43
 
-#define DENALI_READ	0
-#define DENALI_WRITE	0x100
-
 #define DENALI_NR_BANKS		4
 
 /*
@@ -284,7 +281,7 @@ static int denali_dev_ready(struct mtd_info *mtd)
  */
 static int denali_send_pipeline_cmd(struct denali_nand_info *denali, int page,
 				    bool ecc_en, bool transfer_spare,
-				    int access_type, int op)
+				    int access_type, int write)
 {
 	int status = PASS;
 	uint32_t addr, cmd;
@@ -295,17 +292,17 @@ static int denali_send_pipeline_cmd(struct denali_nand_info *denali, int page,
 
 	addr = BANK(denali->flash_bank) | page;
 
-	if (op == DENALI_WRITE && access_type != SPARE_ACCESS) {
+	if (write && access_type != SPARE_ACCESS) {
 		cmd = MODE_01 | addr;
 		iowrite32(cmd, denali->flash_mem);
-	} else if (op == DENALI_WRITE && access_type == SPARE_ACCESS) {
+	} else if (write && access_type == SPARE_ACCESS) {
 		/* read spare area */
 		cmd = MODE_10 | addr;
 		index_addr(denali, cmd, access_type);
 
 		cmd = MODE_01 | addr;
 		iowrite32(cmd, denali->flash_mem);
-	} else if (op == DENALI_READ) {
+	} else {
 		/* setup page read request for access type */
 		cmd = MODE_10 | addr;
 		index_addr(denali, cmd, access_type);
@@ -367,7 +364,7 @@ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
 	int status = 0;
 
 	if (denali_send_pipeline_cmd(denali, page, false, false, SPARE_ACCESS,
-							DENALI_WRITE) == PASS) {
+							1) == PASS) {
 		write_data_to_flash_mem(denali, buf, mtd->oobsize);
 
 		/* wait for operation to complete */
@@ -392,7 +389,7 @@ static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
 	uint32_t irq_status, addr, cmd;
 
 	if (denali_send_pipeline_cmd(denali, page, false, true, SPARE_ACCESS,
-							DENALI_READ) == PASS) {
+							0) == PASS) {
 		read_data_from_flash_mem(denali, buf, mtd->oobsize);
 
 		/*
@@ -533,7 +530,7 @@ static void denali_enable_dma(struct denali_nand_info *denali, bool en)
 }
 
 static void denali_setup_dma64(struct denali_nand_info *denali,
-			       dma_addr_t dma_addr, int page, int op)
+			       dma_addr_t dma_addr, int page, int write)
 {
 	uint32_t mode;
 	const int page_count = 1;
@@ -546,7 +543,8 @@ static void denali_setup_dma64(struct denali_nand_info *denali,
 	 * 1. setup transfer type, interrupt when complete,
 	 *    burst len = 64 bytes, the number of pages
 	 */
-	index_addr(denali, mode, 0x01002000 | (64 << 16) | op | page_count);
+	index_addr(denali, mode,
+		   0x01002000 | (64 << 16) | (write << 8) | page_count);
 
 	/* 2. set memory low address */
 	index_addr(denali, mode, dma_addr);
@@ -556,7 +554,7 @@ static void denali_setup_dma64(struct denali_nand_info *denali,
 }
 
 static void denali_setup_dma32(struct denali_nand_info *denali,
-			       dma_addr_t dma_addr, int page, int op)
+			       dma_addr_t dma_addr, int page, int write)
 {
 	uint32_t mode;
 	const int page_count = 1;
@@ -566,7 +564,7 @@ static void denali_setup_dma32(struct denali_nand_info *denali,
 	/* DMA is a four step process */
 
 	/* 1. setup transfer type and # of pages */
-	index_addr(denali, mode | page, 0x2000 | op | page_count);
+	index_addr(denali, mode | page, 0x2000 | (write << 8) | page_count);
 
 	/* 2. set memory high address bits 23:8 */
 	index_addr(denali, mode | ((dma_addr >> 16) << 8), 0x2200);
@@ -579,12 +577,12 @@ static void denali_setup_dma32(struct denali_nand_info *denali,
 }
 
 static void denali_setup_dma(struct denali_nand_info *denali,
-			     dma_addr_t dma_addr, int page, int op)
+			     dma_addr_t dma_addr, int page, int write)
 {
 	if (denali->caps & DENALI_CAP_DMA_64BIT)
-		denali_setup_dma64(denali, dma_addr, page, op);
+		denali_setup_dma64(denali, dma_addr, page, write);
 	else
-		denali_setup_dma32(denali, dma_addr, page, op);
+		denali_setup_dma32(denali, dma_addr, page, write);
 }
 
 /*
@@ -623,7 +621,7 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
 	denali_reset_irq(denali);
 	denali_enable_dma(denali, true);
 
-	denali_setup_dma(denali, addr, page, DENALI_WRITE);
+	denali_setup_dma(denali, addr, page, 1);
 
 	/* wait for operation to complete */
 	irq_status = denali_wait_for_irq(denali, irq_mask);
@@ -704,7 +702,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
 	dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE);
 
 	denali_reset_irq(denali);
-	denali_setup_dma(denali, addr, page, DENALI_READ);
+	denali_setup_dma(denali, addr, page, 0);
 
 	/* wait for operation to complete */
 	irq_status = denali_wait_for_irq(denali, irq_mask);
@@ -757,7 +755,7 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
 	dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE);
 
 	denali_reset_irq(denali);
-	denali_setup_dma(denali, addr, page, DENALI_READ);
+	denali_setup_dma(denali, addr, page, 0);
 
 	/* wait for operation to complete */
 	irq_status = denali_wait_for_irq(denali, irq_mask);
-- 
2.7.4

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

* [RESEND PATCH v2 48/53] mtd: nand: denali: fix raw and oob accessors for syndrome page layout
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
                   ` (19 preceding siblings ...)
  2017-03-23  0:17 ` [RESEND PATCH v2 47/53] mtd: nand: denali: use flag instead of register macro for direction Masahiro Yamada
@ 2017-03-23  0:17 ` Masahiro Yamada
  2017-03-23  0:17 ` [RESEND PATCH v2 49/53] mtd: nand: denali: support hardware-assisted erased page detection Masahiro Yamada
                   ` (4 subsequent siblings)
  25 siblings, 0 replies; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

The Denali IP adopts the syndrome page layout; payload and ECC are
interleaved, with BBM area always placed at the beginning of OOB.

The figure below shows the page organization for ecc->steps == 2:

  |----------------|    |-----------|
  |                |    |           |
  |                |    |           |
  |    Payload0    |    |           |
  |                |    |           |
  |                |    |           |
  |                |    |           |
  |----------------|    |  in-band  |
  |      ECC0      |    |   area    |
  |----------------|    |           |
  |                |    |           |
  |                |    |           |
  |    Payload1    |    |           |
  |                |    |           |
  |                |    |           |
  |----------------|    |-----------|
  |      BBM       |    |           |
  |----------------|    |           |
  |Payload1 (cont.)|    |           |
  |----------------|    |out-of-band|
  |      ECC1      |    |    area   |
  |----------------|    |           |
  |    OOB free    |    |           |
  |----------------|    |-----------|

The current raw / oob accessors do not take that into consideration,
so in-band and out-of-band data are transferred as they are stored
in the device.  In the case above,

  in-band:      Payload0 + ECC0 + Payload1(partial)
  out-of-band:  BBM + Payload1(cont.) + ECC1 + OOB-free

This is wrong.  As the comment block of struct nand_ecc_ctrl says,
driver callbacks must hide the specific layout used by the hardware
and always return contiguous in-band and out-of-band data.

The current implementation is completely screwed-up, so read/write
callbacks must be re-worked.

Also, PIO transfer has been supported in case DMA may not work for
some reasons.  Actually, the Data DMA may not be equipped depending
on the configuration of the RTL.  This can be checked by reading the
bit 4 of the FEATURES register.  Even if the controller has the DMA
support, dma_set_mask() and dma_map_single() could fail.  In either
case, the driver can fall back to the PIO transfer.  Slower access
would be better than giving up.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
  - Newly added

 drivers/mtd/nand/denali.c | 601 +++++++++++++++++++++++++++++-----------------
 drivers/mtd/nand/denali.h |   1 +
 2 files changed, 384 insertions(+), 218 deletions(-)

diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 9049583..1b0e79c 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -246,6 +246,53 @@ static void denali_write_byte(struct mtd_info *mtd, uint8_t byte)
 	index_addr(denali, MODE_11 | BANK(denali->flash_bank) | 2, byte);
 }
 
+static void denali_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	struct denali_nand_info *denali = mtd_to_denali(mtd);
+	int i;
+
+	iowrite32(MODE_11 | BANK(denali->flash_bank) | 2, denali->flash_mem);
+
+	for (i = 0; i < len; i++)
+		buf[i] = ioread32(denali->flash_mem + 0x10);
+}
+
+static void denali_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+	struct denali_nand_info *denali = mtd_to_denali(mtd);
+	int i;
+
+	iowrite32(MODE_11 | BANK(denali->flash_bank) | 2, denali->flash_mem);
+
+	for (i = 0; i < len; i++)
+		iowrite32(buf[i], denali->flash_mem + 0x10);
+}
+
+static void denali_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	struct denali_nand_info *denali = mtd_to_denali(mtd);
+	uint16_t *buf16 = (uint16_t *)buf;
+	int i;
+
+	iowrite32(MODE_11 | BANK(denali->flash_bank) | 2, denali->flash_mem);
+
+	for (i = 0; i < len / 2; i++)
+		buf16[i] = ioread32(denali->flash_mem + 0x10);
+}
+
+static void denali_write_buf16(struct mtd_info *mtd, const uint8_t *buf,
+			       int len)
+{
+	struct denali_nand_info *denali = mtd_to_denali(mtd);
+	const uint16_t *buf16 = (const uint16_t *)buf;
+	int i;
+
+	iowrite32(MODE_11 | BANK(denali->flash_bank) | 2, denali->flash_mem);
+
+	for (i = 0; i < len / 2; i++)
+		iowrite32(buf16[i], denali->flash_mem + 0x10);
+}
+
 static void denali_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
 {
 	struct denali_nand_info *denali = mtd_to_denali(mtd);
@@ -275,44 +322,6 @@ static int denali_dev_ready(struct mtd_info *mtd)
 	return !!(denali_check_irq(denali) & INTR__INT_ACT);
 }
 
-/*
- * sends a pipeline command operation to the controller. See the Denali NAND
- * controller's user guide for more information (section 4.2.3.6).
- */
-static int denali_send_pipeline_cmd(struct denali_nand_info *denali, int page,
-				    bool ecc_en, bool transfer_spare,
-				    int access_type, int write)
-{
-	int status = PASS;
-	uint32_t addr, cmd;
-
-	setup_ecc_for_xfer(denali, ecc_en, transfer_spare);
-
-	denali_reset_irq(denali);
-
-	addr = BANK(denali->flash_bank) | page;
-
-	if (write && access_type != SPARE_ACCESS) {
-		cmd = MODE_01 | addr;
-		iowrite32(cmd, denali->flash_mem);
-	} else if (write && access_type == SPARE_ACCESS) {
-		/* read spare area */
-		cmd = MODE_10 | addr;
-		index_addr(denali, cmd, access_type);
-
-		cmd = MODE_01 | addr;
-		iowrite32(cmd, denali->flash_mem);
-	} else {
-		/* setup page read request for access type */
-		cmd = MODE_10 | addr;
-		index_addr(denali, cmd, access_type);
-
-		cmd = MODE_01 | addr;
-		iowrite32(cmd, denali->flash_mem);
-	}
-	return status;
-}
-
 /* helper function that simply writes a buffer to the flash */
 static int write_data_to_flash_mem(struct denali_nand_info *denali,
 				   const uint8_t *buf, int len)
@@ -355,66 +364,6 @@ static int read_data_from_flash_mem(struct denali_nand_info *denali,
 	return i * 4; /* intent is to return the number of bytes read */
 }
 
-/* writes OOB data to the device */
-static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
-{
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
-	uint32_t irq_status;
-	uint32_t irq_mask = INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL;
-	int status = 0;
-
-	if (denali_send_pipeline_cmd(denali, page, false, false, SPARE_ACCESS,
-							1) == PASS) {
-		write_data_to_flash_mem(denali, buf, mtd->oobsize);
-
-		/* wait for operation to complete */
-		irq_status = denali_wait_for_irq(denali, irq_mask);
-
-		if (!(irq_status & INTR__PROGRAM_COMP)) {
-			dev_err(denali->dev, "OOB write failed\n");
-			status = -EIO;
-		}
-	} else {
-		dev_err(denali->dev, "unable to send pipeline command\n");
-		status = -EIO;
-	}
-	return status;
-}
-
-/* reads OOB data from the device */
-static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
-{
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
-	uint32_t irq_mask = INTR__LOAD_COMP;
-	uint32_t irq_status, addr, cmd;
-
-	if (denali_send_pipeline_cmd(denali, page, false, true, SPARE_ACCESS,
-							0) == PASS) {
-		read_data_from_flash_mem(denali, buf, mtd->oobsize);
-
-		/*
-		 * wait for command to be accepted
-		 * can always use status0 bit as the
-		 * mask is identical for each bank.
-		 */
-		irq_status = denali_wait_for_irq(denali, irq_mask);
-
-		if (!(irq_status & INTR__LOAD_COMP))
-			dev_err(denali->dev, "page on OOB timeout %d\n", page);
-
-		/*
-		 * We set the device back to MAIN_ACCESS here as I observed
-		 * instability with the controller if you do a block erase
-		 * and the last transaction was a SPARE_ACCESS. Block erase
-		 * is reliable (according to the MTD test infrastructure)
-		 * if you are in MAIN_ACCESS.
-		 */
-		addr = BANK(denali->flash_bank) | page;
-		cmd = MODE_10 | addr;
-		index_addr(denali, cmd, MAIN_ACCESS);
-	}
-}
-
 static int denali_hw_ecc_fixup(struct mtd_info *mtd,
 			       struct denali_nand_info *denali)
 {
@@ -585,137 +534,283 @@ static void denali_setup_dma(struct denali_nand_info *denali,
 		denali_setup_dma32(denali, dma_addr, page, write);
 }
 
-/*
- * writes a page. user specifies type, and this function handles the
- * configuration details.
- */
-static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
-			const uint8_t *buf, int page, bool raw_xfer)
+static int denali_pio_read(struct denali_nand_info *denali, void *buf,
+			   size_t size, int page, int raw)
 {
-	struct denali_nand_info *denali = mtd_to_denali(mtd);
-	dma_addr_t addr = denali->dma_addr;
-	size_t size = mtd->writesize + mtd->oobsize;
-	uint32_t irq_status;
-	uint32_t irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
-	int ret = 0;
+	uint32_t addr = BANK(denali->flash_bank) | page;
+	uint32_t irq_status, ecc_err_mask;
 
-	/*
-	 * if it is a raw xfer, we want to disable ecc and send the spare area.
-	 * !raw_xfer - enable ecc
-	 * raw_xfer - transfer spare
-	 */
-	setup_ecc_for_xfer(denali, !raw_xfer, raw_xfer);
+	/* setup page read request for access type */
+	index_addr(denali, MODE_10 | addr,
+		   raw ? MAIN_SPARE_ACCESS : MAIN_ACCESS);
 
-	/* copy buffer into DMA buffer */
-	memcpy(denali->buf, buf, mtd->writesize);
+	iowrite32(MODE_01 | addr, denali->flash_mem);
 
-	if (raw_xfer) {
-		/* transfer the data to the spare area */
-		memcpy(denali->buf + mtd->writesize,
-			chip->oob_poi,
-			mtd->oobsize);
-	}
+	if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
+		ecc_err_mask = INTR__ECC_UNCOR_ERR;
+	else
+		ecc_err_mask = INTR__ECC_ERR;
+
+	denali_reset_irq(denali);
+
+	read_data_from_flash_mem(denali, buf, size);
+
+	irq_status = denali_wait_for_irq(denali, INTR__PAGE_XFER_INC);
+	if (!(irq_status & INTR__PAGE_XFER_INC))
+		return -EIO;
+
+	return irq_status & ecc_err_mask ? -EBADMSG : 0;
+}
+
+static int denali_pio_write(struct denali_nand_info *denali,
+			    const void *buf, size_t size, int page, int raw)
+{
+	uint32_t addr = BANK(denali->flash_bank) | page;
+	uint32_t irq_status;
+
+	/* setup page read request for access type */
+	index_addr(denali, MODE_10 | addr,
+		   raw ? MAIN_SPARE_ACCESS : MAIN_ACCESS);
 
-	dma_sync_single_for_device(denali->dev, addr, size, DMA_TO_DEVICE);
+	iowrite32(MODE_01 | addr, denali->flash_mem);
 
 	denali_reset_irq(denali);
+
+	write_data_to_flash_mem(denali, buf, size);
+
+	irq_status = denali_wait_for_irq(denali,
+				INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL);
+	if (!(irq_status & INTR__PROGRAM_COMP))
+		return -EIO;
+
+	return 0;
+}
+
+static int denali_pio_xfer(struct denali_nand_info *denali, void *buf,
+			   size_t size, int page, int raw, int write)
+{
+	if (write)
+		return denali_pio_write(denali, buf, size, page, raw);
+	else
+		return denali_pio_read(denali, buf, size, page, raw);
+}
+
+static int denali_dma_xfer(struct denali_nand_info *denali, void *buf,
+			   size_t size, int page, int raw, int write)
+{
+	dma_addr_t dma_addr = denali->dma_addr;
+	uint32_t irq_mask, irq_status, ecc_err_mask;
+	enum dma_data_direction dir = write ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+	int ret = 0;
+
+	dma_sync_single_for_device(denali->dev, dma_addr, size, dir);
+
+	if (write) {
+		irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
+		ecc_err_mask = 0;
+	} else if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) {
+		irq_mask = INTR__DMA_CMD_COMP;
+		ecc_err_mask = INTR__ECC_UNCOR_ERR;
+	} else {
+		irq_mask = INTR__DMA_CMD_COMP;
+		ecc_err_mask = INTR__ECC_ERR;
+	}
+
 	denali_enable_dma(denali, true);
 
-	denali_setup_dma(denali, addr, page, 1);
+	denali_reset_irq(denali);
+	denali_setup_dma(denali, dma_addr, page, write);
 
 	/* wait for operation to complete */
 	irq_status = denali_wait_for_irq(denali, irq_mask);
-	if (!(irq_status & INTR__DMA_CMD_COMP)) {
-		dev_err(denali->dev, "timeout on write_page (type = %d)\n",
-			raw_xfer);
+	if (!(irq_status & INTR__DMA_CMD_COMP))
 		ret = -EIO;
-	}
+	else if (irq_status & ecc_err_mask)
+		ret = -EBADMSG;
 
 	denali_enable_dma(denali, false);
-	dma_sync_single_for_cpu(denali->dev, addr, size, DMA_TO_DEVICE);
+	dma_sync_single_for_cpu(denali->dev, dma_addr, size, dir);
 
 	return ret;
 }
 
-/* NAND core entry points */
-
-/*
- * this is the callback that the NAND core calls to write a page. Since
- * writing a page with ECC or without is similar, all the work is done
- * by write_page above.
- */
-static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
-				const uint8_t *buf, int oob_required, int page)
+static int denali_data_xfer(struct denali_nand_info *denali, void *buf,
+			    size_t size, int page, int raw, int write)
 {
-	/*
-	 * for regular page writes, we let HW handle all the ECC
-	 * data written to the device.
-	 */
-	return write_page(mtd, chip, buf, page, false);
+	setup_ecc_for_xfer(denali, !raw, raw);
+
+	if (denali->dma_avail)
+		return denali_dma_xfer(denali, buf, size, page, raw, write);
+	else
+		return denali_pio_xfer(denali, buf, size, page, raw, write);
 }
 
-/*
- * This is the callback that the NAND core calls to write a page without ECC.
- * raw access is similar to ECC page writes, so all the work is done in the
- * write_page() function above.
- */
-static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
-				 const uint8_t *buf, int oob_required,
-				 int page)
+static int denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip,
+			   int page, int write)
 {
-	/*
-	 * for raw page writes, we want to disable ECC and simply write
-	 * whatever data is in the buffer.
-	 */
-	return write_page(mtd, chip, buf, page, true);
+	struct denali_nand_info *denali = mtd_to_denali(mtd);
+	int writesize = mtd->writesize;
+	int oobsize = mtd->oobsize;
+	uint8_t *bufpoi = chip->oob_poi;
+	int ecc_steps = chip->ecc.steps;
+	int ecc_size = chip->ecc.size;
+	int ecc_bytes = chip->ecc.bytes;
+	int bbm_skip = denali->bbtskipbytes;
+	size_t size = writesize + oobsize;
+	int i, pos, len;
+
+	/* BBM at the beginning of the OOB area */
+	chip->cmdfunc(mtd, write ? NAND_CMD_SEQIN : NAND_CMD_READ0,
+		      writesize, page);
+	if (write)
+		chip->write_buf(mtd, bufpoi, bbm_skip);
+	else
+		chip->read_buf(mtd, bufpoi, bbm_skip);
+	bufpoi += bbm_skip;
+
+	/* OOB ECC */
+	for (i = 0; i < ecc_steps; i++) {
+		pos = ecc_size + i * (ecc_size + ecc_bytes);
+		len = ecc_bytes;
+
+		if (pos >= writesize)
+			pos += bbm_skip;
+		else if (pos + len > writesize)
+			len = writesize - pos;
+
+		chip->cmdfunc(mtd, NAND_CMD_RNDOUT, pos, -1);
+		if (write)
+			chip->write_buf(mtd, bufpoi, len);
+		else
+			chip->read_buf(mtd, bufpoi, len);
+		bufpoi += len;
+		if (len < ecc_bytes) {
+			len = ecc_bytes - len;
+			chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
+				      writesize + bbm_skip, -1);
+			if (write)
+				chip->write_buf(mtd, bufpoi, len);
+			else
+				chip->read_buf(mtd, bufpoi, len);
+			bufpoi += len;
+		}
+	}
+
+	/* OOB free */
+	len = oobsize - (bufpoi - chip->oob_poi);
+	chip->cmdfunc(mtd, NAND_CMD_RNDOUT, size - len, -1);
+	if (write)
+		chip->write_buf(mtd, bufpoi, len);
+	else
+		chip->read_buf(mtd, bufpoi, len);
+
+	return 0;
 }
 
-static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
-			    int page)
+static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+				uint8_t *buf, int oob_required, int page)
 {
-	return write_oob_data(mtd, chip->oob_poi, page);
+	struct denali_nand_info *denali = mtd_to_denali(mtd);
+	int writesize = mtd->writesize;
+	int oobsize = mtd->oobsize;
+	int ecc_steps = chip->ecc.steps;
+	int ecc_size = chip->ecc.size;
+	int ecc_bytes = chip->ecc.bytes;
+	void *dma_buf = denali->buf;
+	int bbm_skip = denali->bbtskipbytes;
+	size_t size = writesize + oobsize;
+	int ret, i, pos, len;
+
+	ret = denali_data_xfer(denali, dma_buf, size, page, 1, 0);
+	if (ret)
+		return ret;
+
+	/* Arrange the buffer for syndrome payload/ecc layout */
+	if (buf) {
+		for (i = 0; i < ecc_steps; i++) {
+			pos = i * (ecc_size + ecc_bytes);
+			len = ecc_size;
+
+			if (pos >= writesize)
+				pos += bbm_skip;
+			else if (pos + len > writesize)
+				len = writesize - pos;
+
+			memcpy(buf, dma_buf + pos, len);
+			buf += len;
+			if (len < ecc_size) {
+				len = ecc_size - len;
+				memcpy(buf, dma_buf + writesize + bbm_skip,
+				       len);
+				buf += len;
+			}
+		}
+	}
+
+	if (oob_required) {
+		uint8_t *oob = chip->oob_poi;
+
+		/* BBM at the beginning of the OOB area */
+		memcpy(oob, dma_buf + writesize, bbm_skip);
+		oob += bbm_skip;
+
+		/* OOB ECC */
+		for (i = 0; i < ecc_steps; i++) {
+			pos = ecc_size + i * (ecc_size + ecc_bytes);
+			len = ecc_bytes;
+
+			if (pos >= writesize)
+				pos += bbm_skip;
+			else if (pos + len > writesize)
+				len = writesize - pos;
+
+			memcpy(oob, dma_buf + pos, len);
+			oob += len;
+			if (len < ecc_bytes) {
+				len = ecc_bytes - len;
+				memcpy(oob, dma_buf + writesize + bbm_skip,
+				       len);
+				oob += len;
+			}
+		}
+
+		/* OOB free */
+		len = oobsize - (oob - chip->oob_poi);
+		memcpy(oob, dma_buf + size - len, len);
+	}
+
+	return 0;
 }
 
 static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
 			   int page)
 {
-	read_oob_data(mtd, chip->oob_poi, page);
+	return denali_oob_xfer(mtd, chip, page, 0);
+}
 
-	return 0;
+static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+			   int page)
+{
+	return denali_oob_xfer(mtd, chip, page, 1);
 }
 
 static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
 			    uint8_t *buf, int oob_required, int page)
 {
 	struct denali_nand_info *denali = mtd_to_denali(mtd);
-	dma_addr_t addr = denali->dma_addr;
-	size_t size = mtd->writesize + mtd->oobsize;
-	uint32_t irq_status;
-	uint32_t irq_mask = denali->caps & DENALI_CAP_HW_ECC_FIXUP ?
-				INTR__DMA_CMD_COMP | INTR__ECC_UNCOR_ERR :
-				INTR__ECC_TRANSACTION_DONE | INTR__ECC_ERR;
 	int stat = 0;
+	int ret;
 
-	setup_ecc_for_xfer(denali, true, false);
-
-	denali_enable_dma(denali, true);
-	dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE);
-
-	denali_reset_irq(denali);
-	denali_setup_dma(denali, addr, page, 0);
-
-	/* wait for operation to complete */
-	irq_status = denali_wait_for_irq(denali, irq_mask);
-
-	dma_sync_single_for_cpu(denali->dev, addr, size, DMA_FROM_DEVICE);
+	ret = denali_data_xfer(denali, denali->buf, mtd->writesize, page, 0, 0);
+	if (ret && ret != -EBADMSG)
+		return ret;
 
 	memcpy(buf, denali->buf, mtd->writesize);
 
 	if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
 		stat = denali_hw_ecc_fixup(mtd, denali);
-	else if (irq_status & INTR__ECC_ERR)
+	else if (ret == -EBADMSG)
 		stat = denali_sw_ecc_fixup(mtd, denali, buf);
-	denali_enable_dma(denali, false);
 
 	if (stat == -EBADMSG) {
 		/*
@@ -723,7 +818,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
 		 * see if it is an erased page. If so, then it's not a real ECC
 		 * error.
 		 */
-		read_oob_data(mtd, chip->oob_poi, page);
+		denali_read_oob(mtd, chip, page);
 
 		stat = nand_check_erased_ecc_chunk(
 					buf, mtd->writesize,
@@ -740,36 +835,93 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
 	return stat;
 }
 
-static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
-				uint8_t *buf, int oob_required, int page)
+static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+				 const uint8_t *buf, int oob_required, int page)
 {
 	struct denali_nand_info *denali = mtd_to_denali(mtd);
-	dma_addr_t addr = denali->dma_addr;
-	size_t size = mtd->writesize + mtd->oobsize;
-	uint32_t irq_mask = INTR__DMA_CMD_COMP;
-	uint32_t irq_status;
-
-	setup_ecc_for_xfer(denali, false, true);
-	denali_enable_dma(denali, true);
+	int writesize = mtd->writesize;
+	int oobsize = mtd->oobsize;
+	int ecc_steps = chip->ecc.steps;
+	int ecc_size = chip->ecc.size;
+	int ecc_bytes = chip->ecc.bytes;
+	void *dma_buf = denali->buf;
+	int bbm_skip = denali->bbtskipbytes;
+	size_t size = writesize + oobsize;
+	int i, pos, len;
 
-	dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE);
+	/*
+	 * Fill the buffer with 0xff first except the full page transfer.
+	 * This simplifies the logic.
+	 */
+	if (!buf || !oob_required)
+		memset(dma_buf, 0xff, size);
+
+	/* Arrange the buffer for syndrome payload/ecc layout */
+	if (buf) {
+		for (i = 0; i < ecc_steps; i++) {
+			pos = i * (ecc_size + ecc_bytes);
+			len = ecc_size;
+
+			if (pos >= writesize)
+				pos += bbm_skip;
+			else if (pos + len > writesize)
+				len = writesize - pos;
+
+			memcpy(dma_buf + pos, buf, len);
+			buf += len;
+			if (len < ecc_size) {
+				len = ecc_size - len;
+				memcpy(dma_buf + writesize + bbm_skip, buf,
+				       len);
+				buf += len;
+			}
+		}
+	}
 
-	denali_reset_irq(denali);
-	denali_setup_dma(denali, addr, page, 0);
+	if (oob_required) {
+		const uint8_t *oob = chip->oob_poi;
+
+		/* BBM at the beginning of the OOB area */
+		memcpy(dma_buf + writesize, oob, bbm_skip);
+		oob += bbm_skip;
+
+		/* OOB ECC */
+		for (i = 0; i < ecc_steps; i++) {
+			pos = ecc_size + i * (ecc_size + ecc_bytes);
+			len = ecc_bytes;
+
+			if (pos >= writesize)
+				pos += bbm_skip;
+			else if (pos + len > writesize)
+				len = writesize - pos;
+
+			memcpy(dma_buf + pos, oob, len);
+			oob += len;
+			if (len < ecc_bytes) {
+				len = ecc_bytes - len;
+				memcpy(dma_buf + writesize + bbm_skip, oob,
+				       len);
+				oob += len;
+			}
+		}
 
-	/* wait for operation to complete */
-	irq_status = denali_wait_for_irq(denali, irq_mask);
-	if (irq_status & INTR__DMA_CMD_COMP)
-		return -ETIMEDOUT;
+		/* OOB free */
+		len = oobsize - (oob - chip->oob_poi);
+		memcpy(dma_buf + size - len, oob, len);
+	}
 
-	dma_sync_single_for_cpu(denali->dev, addr, size, DMA_FROM_DEVICE);
+	return denali_data_xfer(denali, dma_buf, size, page, 1, 1);
+}
 
-	denali_enable_dma(denali, false);
+static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+			     const uint8_t *buf, int oob_required, int page)
+{
+	struct denali_nand_info *denali = mtd_to_denali(mtd);
 
-	memcpy(buf, denali->buf, mtd->writesize);
-	memcpy(chip->oob_poi, denali->buf + mtd->writesize, mtd->oobsize);
+	memcpy(denali->buf, buf, mtd->writesize);
 
-	return 0;
+	return denali_data_xfer(denali, denali->buf, mtd->writesize, page,
+				0, 1);
 }
 
 static void denali_select_chip(struct mtd_info *mtd, int chip)
@@ -1192,21 +1344,27 @@ int denali_init(struct denali_nand_info *denali)
 		goto disable_irq;
 	}
 
-	ret = dma_set_mask(denali->dev,
-			   DMA_BIT_MASK(denali->caps & DENALI_CAP_DMA_64BIT ?
-					64 : 32));
-	if (ret) {
-		dev_err(denali->dev, "No usable DMA configuration\n");
-		goto disable_irq;
+	if (ioread32(denali->flash_reg + FEATURES) & FEATURES__DMA)
+		denali->dma_avail = 1;
+
+	if (denali->dma_avail) {
+		int dma_bit = denali->caps & DENALI_CAP_DMA_64BIT ? 64 : 32;
+
+		ret = dma_set_mask(denali->dev, DMA_BIT_MASK(dma_bit));
+		if (ret) {
+			dev_info(denali->dev, "Failed to set DMA mask. Disabling DMA.\n");
+			denali->dma_avail = 0;
+		}
 	}
 
-	denali->dma_addr = dma_map_single(denali->dev, denali->buf,
-			     mtd->writesize + mtd->oobsize,
-			     DMA_BIDIRECTIONAL);
-	if (dma_mapping_error(denali->dev, denali->dma_addr)) {
-		dev_err(denali->dev, "Failed to map DMA buffer\n");
-		ret = -EIO;
-		goto disable_irq;
+	if (denali->dma_avail) {
+		denali->dma_addr = dma_map_single(denali->dev, denali->buf,
+						  mtd->writesize + mtd->oobsize,
+						  DMA_BIDIRECTIONAL);
+		if (dma_mapping_error(denali->dev, denali->dma_addr)) {
+			dev_info(denali->dev, "Failed to map DMA buffer. Disabling DMA.\n");
+			denali->dma_avail = 0;
+		};
 	}
 
 	/*
@@ -1281,6 +1439,13 @@ int denali_init(struct denali_nand_info *denali)
 
 	mtd_set_ooblayout(mtd, &denali_ooblayout_ops);
 
+	if (denali->nand.options & NAND_BUSWIDTH_16) {
+		chip->read_buf = denali_read_buf16;
+		chip->write_buf = denali_write_buf16;
+	} else {
+		chip->read_buf = denali_read_buf;
+		chip->write_buf = denali_write_buf;
+	}
 	chip->ecc.options |= NAND_ECC_CUSTOM_PAGE_ACCESS;
 	chip->ecc.read_page = denali_read_page;
 	chip->ecc.read_page_raw = denali_read_page_raw;
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index 332db96..4696fd6 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -327,6 +327,7 @@ struct denali_nand_info {
 
 	void *buf;
 	dma_addr_t dma_addr;
+	int dma_avail;
 	int devnum;	/* represent how many nands connected */
 	int bbtskipbytes;
 	int max_banks;
-- 
2.7.4

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

* [RESEND PATCH v2 49/53] mtd: nand: denali: support hardware-assisted erased page detection
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
                   ` (20 preceding siblings ...)
  2017-03-23  0:17 ` [RESEND PATCH v2 48/53] mtd: nand: denali: fix raw and oob accessors for syndrome page layout Masahiro Yamada
@ 2017-03-23  0:17 ` Masahiro Yamada
  2017-03-23  0:17 ` [RESEND PATCH v2 50/53] mtd: nand: allocate aligned buffers if NAND_OWN_BUFFERS is unset Masahiro Yamada
                   ` (3 subsequent siblings)
  25 siblings, 0 replies; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

Recent versions of this IP support automatic erased page detection.
If an erased page is detected on reads, the controller does not set
INTR__ECC_UNCOR_ERR, but INTR__ERASED_PAGE.  If this feature is
supported, the driver can use this information instead of calling
nand_check_erased_ecc_chunk().

The detection of erased page is based on the number of zeros in the
page; if the number of zeros is less than the value in the field
ERASED_THRESHOLD, the page is assumed as erased.

Set the ERASED_THRESHOLD to (chip->ecc.strength + 1).  This is the
worst case where all the bitflips come from the same ECC sector.
This field is Reserved for older IP versions, so this commit has
no impact on them.

One thing worth mentioning is the driver still needs to fill the
buffer with 0xff in the case because the ECC correction engine has
already manipulated the data in the buffer.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
  - Newly added

 drivers/mtd/nand/denali.c | 10 +++++++++-
 drivers/mtd/nand/denali.h |  5 +++++
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 1b0e79c..c3a7f7b 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -559,6 +559,9 @@ static int denali_pio_read(struct denali_nand_info *denali, void *buf,
 	if (!(irq_status & INTR__PAGE_XFER_INC))
 		return -EIO;
 
+	if (irq_status & INTR__ERASED_PAGE)
+		memset(buf, 0xff, size);
+
 	return irq_status & ecc_err_mask ? -EBADMSG : 0;
 }
 
@@ -631,6 +634,9 @@ static int denali_dma_xfer(struct denali_nand_info *denali, void *buf,
 	denali_enable_dma(denali, false);
 	dma_sync_single_for_cpu(denali->dev, dma_addr, size, dir);
 
+	if (irq_status & INTR__ERASED_PAGE)
+		memset(buf, 0xff, size);
+
 	return ret;
 }
 
@@ -1423,7 +1429,9 @@ int denali_init(struct denali_nand_info *denali)
 	chip->ecc.bytes = denali_calc_ecc_bytes(chip->ecc.size,
 						chip->ecc.strength);
 
-	iowrite32(chip->ecc.strength, denali->flash_reg + ECC_CORRECTION);
+	iowrite32(MAKE_ECC_CORRECTION(chip->ecc.strength,
+				      chip->ecc.strength + 1),
+		  denali->flash_reg + ECC_CORRECTION);
 	iowrite32(mtd->erasesize / mtd->writesize,
 		  denali->flash_reg + PAGES_PER_BLOCK);
 	iowrite32(denali->nand.options & NAND_BUSWIDTH_16 ? 1 : 0,
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index 4696fd6..ecbec33 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -109,6 +109,10 @@
 
 #define ECC_CORRECTION				0x1b0
 #define     ECC_CORRECTION__VALUE			GENMASK(4, 0)
+#define     ECC_CORRECTION__ERASE_THRESHOLD		GENMASK(31, 16)
+#define     MAKE_ECC_CORRECTION(val, thresh)		\
+			(((val) & (ECC_CORRECTION__VALUE)) | \
+			(((thresh) << 16) & (ECC_CORRECTION__ERASE_THRESHOLD)))
 
 #define READ_MODE				0x1c0
 #define     READ_MODE__VALUE				GENMASK(3, 0)
@@ -237,6 +241,7 @@
 #define     INTR__RST_COMP				BIT(13)
 #define     INTR__PIPE_CMD_ERR				BIT(14)
 #define     INTR__PAGE_XFER_INC				BIT(15)
+#define     INTR__ERASED_PAGE				BIT(16)
 
 #define PAGE_CNT(bank)				(0x430 + (bank) * 0x50)
 #define ERR_PAGE_ADDR(bank)			(0x440 + (bank) * 0x50)
-- 
2.7.4

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

* [RESEND PATCH v2 50/53] mtd: nand: allocate aligned buffers if NAND_OWN_BUFFERS is unset
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
                   ` (21 preceding siblings ...)
  2017-03-23  0:17 ` [RESEND PATCH v2 49/53] mtd: nand: denali: support hardware-assisted erased page detection Masahiro Yamada
@ 2017-03-23  0:17 ` Masahiro Yamada
  2017-03-27  8:00   ` Boris Brezillon
  2017-03-23  0:18 ` [RESEND PATCH v2 51/53] mtd: nand: denali: skip driver internal bounce buffer when possible Masahiro Yamada
                   ` (2 subsequent siblings)
  25 siblings, 1 reply; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:17 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

Commit 66507c7bc889 ("mtd: nand: Add support to use nand_base poi
databuf as bounce buffer") fixed the issue that drivers can be
passed with a kmap()'d buffer.  This addressed the use_bufpoi = 0
case.

When use_bufpoi = 1, chip->buffers->databuf is used.  The databuf
allocated by nand_scan_tail() is not suitable for DMA due to the
offset, sizeof(*chip->buffers).

So, drivers using DMA are very likely to end up with setting the
NAND_OWN_BUFFERS flag and allocate buffers by themselves.  Drivers
tend to use devm_k*alloc to simplify the probe failure path, but
devm_k*alloc() does not return a cache line aligned buffer.

If used, it could violate the DMA-API requirement stated in
Documentation/DMA-API.txt:
  Warnings:  Memory coherency operates at a granularity called the
  cache line width.  In order for memory mapped by this API to
  operate correctly, the mapped region must begin exactly on a cache
  line boundary and end exactly on one (to prevent two separately
  mapped regions from sharing a single cache line).

To sum up, NAND_OWN_BUFFERS is not very convenient for drivers.
nand_scan_tail() can give a separate buffer for each of ecccalc,
ecccode, databuf.  It is guaranteed kmalloc'ed memory is aligned
with ARCH_DMA_MINALIGN.  This is enough for most drivers because
it is rare that DMA engines require larger alignment than CPU's
cache line.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
  - Newly added

 drivers/mtd/nand/nand_base.c | 34 +++++++++++++++++++++++++++-------
 1 file changed, 27 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index e13f959..6fc0422 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -4614,13 +4614,25 @@ int nand_scan_tail(struct mtd_info *mtd)
 	}
 
 	if (!(chip->options & NAND_OWN_BUFFERS)) {
-		nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize
-				+ mtd->oobsize * 3, GFP_KERNEL);
+		nbuf = kzalloc(sizeof(*nbuf), GFP_KERNEL);
 		if (!nbuf)
 			return -ENOMEM;
-		nbuf->ecccalc = (uint8_t *)(nbuf + 1);
-		nbuf->ecccode = nbuf->ecccalc + mtd->oobsize;
-		nbuf->databuf = nbuf->ecccode + mtd->oobsize;
+		nbuf->ecccalc = kmalloc(mtd->oobsize, GFP_KERNEL);
+		if (!nbuf->ecccalc) {
+			ret = -EINVAL;
+			goto err_free;
+		}
+		nbuf->ecccode = kmalloc(mtd->oobsize, GFP_KERNEL);
+		if (!nbuf->ecccode) {
+			ret = -EINVAL;
+			goto err_free;
+		}
+		nbuf->databuf = kmalloc(mtd->writesize + mtd->oobsize,
+					GFP_KERNEL);
+		if (!nbuf->databuf) {
+			ret = -EINVAL;
+			goto err_free;
+		}
 
 		chip->buffers = nbuf;
 	} else {
@@ -4863,8 +4875,12 @@ int nand_scan_tail(struct mtd_info *mtd)
 	/* Build bad block table */
 	return chip->scan_bbt(mtd);
 err_free:
-	if (!(chip->options & NAND_OWN_BUFFERS))
+	if (!(chip->options & NAND_OWN_BUFFERS)) {
+		kfree(chip->buffers->databuf);
+		kfree(chip->buffers->ecccode);
+		kfree(chip->buffers->ecccalc);
 		kfree(chip->buffers);
+	}
 	return ret;
 }
 EXPORT_SYMBOL(nand_scan_tail);
@@ -4915,8 +4931,12 @@ void nand_cleanup(struct nand_chip *chip)
 
 	/* Free bad block table memory */
 	kfree(chip->bbt);
-	if (!(chip->options & NAND_OWN_BUFFERS))
+	if (!(chip->options & NAND_OWN_BUFFERS)) {
+		kfree(chip->buffers->databuf);
+		kfree(chip->buffers->ecccode);
+		kfree(chip->buffers->ecccalc);
 		kfree(chip->buffers);
+	}
 
 	/* Free bad block descriptor memory */
 	if (chip->badblock_pattern && chip->badblock_pattern->options
-- 
2.7.4

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

* [RESEND PATCH v2 51/53] mtd: nand: denali: skip driver internal bounce buffer when possible
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
                   ` (22 preceding siblings ...)
  2017-03-23  0:17 ` [RESEND PATCH v2 50/53] mtd: nand: allocate aligned buffers if NAND_OWN_BUFFERS is unset Masahiro Yamada
@ 2017-03-23  0:18 ` Masahiro Yamada
  2017-03-23  0:18 ` [RESEND PATCH v2 52/53] mtd: nand: denali: use non-managed kmalloc() for DMA buffer Masahiro Yamada
  2017-03-23  0:18 ` [RESEND PATCH v2 53/53] mtd: nand: denali: enable bad block table scan Masahiro Yamada
  25 siblings, 0 replies; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:18 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

For ecc->read_page() and ecc->write_page(), it is possible to call
dma_map_single() against the given buffer.  This bypasses the driver
internal bounce buffer and save the memcpy().

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
  - Newly added

 drivers/mtd/nand/denali.c | 31 ++++++++++++-------------------
 1 file changed, 12 insertions(+), 19 deletions(-)

diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index c3a7f7b..4900745 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -601,12 +601,16 @@ static int denali_pio_xfer(struct denali_nand_info *denali, void *buf,
 static int denali_dma_xfer(struct denali_nand_info *denali, void *buf,
 			   size_t size, int page, int raw, int write)
 {
-	dma_addr_t dma_addr = denali->dma_addr;
+	dma_addr_t dma_addr;
 	uint32_t irq_mask, irq_status, ecc_err_mask;
 	enum dma_data_direction dir = write ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
 	int ret = 0;
 
-	dma_sync_single_for_device(denali->dev, dma_addr, size, dir);
+	dma_addr = dma_map_single(denali->dev, buf, size, dir);
+	if (dma_mapping_error(denali->dev, dma_addr)) {
+		dev_dbg(denali->dev, "Failed to DMA-map buffer. Trying PIO.\n");
+		return denali_pio_xfer(denali, buf, size, page, raw, write);
+	}
 
 	if (write) {
 		irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
@@ -632,7 +636,7 @@ static int denali_dma_xfer(struct denali_nand_info *denali, void *buf,
 		ret = -EBADMSG;
 
 	denali_enable_dma(denali, false);
-	dma_sync_single_for_cpu(denali->dev, dma_addr, size, dir);
+	dma_unmap_single(denali->dev, dma_addr, size, dir);
 
 	if (irq_status & INTR__ERASED_PAGE)
 		memset(buf, 0xff, size);
@@ -807,12 +811,10 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
 	int stat = 0;
 	int ret;
 
-	ret = denali_data_xfer(denali, denali->buf, mtd->writesize, page, 0, 0);
+	ret = denali_data_xfer(denali, buf, mtd->writesize, page, 0, 0);
 	if (ret && ret != -EBADMSG)
 		return ret;
 
-	memcpy(buf, denali->buf, mtd->writesize);
-
 	if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
 		stat = denali_hw_ecc_fixup(mtd, denali);
 	else if (ret == -EBADMSG)
@@ -924,10 +926,8 @@ static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
 {
 	struct denali_nand_info *denali = mtd_to_denali(mtd);
 
-	memcpy(denali->buf, buf, mtd->writesize);
-
-	return denali_data_xfer(denali, denali->buf, mtd->writesize, page,
-				0, 1);
+	return denali_data_xfer(denali, (void *)buf, mtd->writesize,
+				page, 0, 1);
 }
 
 static void denali_select_chip(struct mtd_info *mtd, int chip)
@@ -1363,15 +1363,8 @@ int denali_init(struct denali_nand_info *denali)
 		}
 	}
 
-	if (denali->dma_avail) {
-		denali->dma_addr = dma_map_single(denali->dev, denali->buf,
-						  mtd->writesize + mtd->oobsize,
-						  DMA_BIDIRECTIONAL);
-		if (dma_mapping_error(denali->dev, denali->dma_addr)) {
-			dev_info(denali->dev, "Failed to map DMA buffer. Disabling DMA.\n");
-			denali->dma_avail = 0;
-		};
-	}
+	if (denali->dma_avail)
+		chip->options |= NAND_USE_BOUNCE_BUFFER;
 
 	/*
 	 * second stage of the NAND scan
-- 
2.7.4

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

* [RESEND PATCH v2 52/53] mtd: nand: denali: use non-managed kmalloc() for DMA buffer
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
                   ` (23 preceding siblings ...)
  2017-03-23  0:18 ` [RESEND PATCH v2 51/53] mtd: nand: denali: skip driver internal bounce buffer when possible Masahiro Yamada
@ 2017-03-23  0:18 ` Masahiro Yamada
  2017-03-23 11:33   ` Robin Murphy
  2017-03-23  0:18 ` [RESEND PATCH v2 53/53] mtd: nand: denali: enable bad block table scan Masahiro Yamada
  25 siblings, 1 reply; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:18 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada,
	Russell King, Lars-Peter Clausen, Robin Murphy

As Russell and Lars stated in the discussion [1], using
devm_k*alloc() with DMA is not a good idea.

Let's use kmalloc (not kzalloc because no need for zero-out).
Also, allocate the buffer as late as possible because it must be
freed for any error that follows.

[1] https://lkml.org/lkml/2017/3/8/693

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
Cc: Russell King <rmk+kernel@armlinux.org.uk>
Cc: Lars-Peter Clausen <lars@metafoo.de>
Cc: Robin Murphy <robin.murphy@arm.com>
---

Changes in v2:
  - Newly added

 drivers/mtd/nand/denali.c | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)

diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 4900745..28622a9 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -23,6 +23,7 @@
 #include <linux/mutex.h>
 #include <linux/mtd/mtd.h>
 #include <linux/module.h>
+#include <linux/slab.h>
 
 #include "denali.h"
 
@@ -1343,13 +1344,6 @@ int denali_init(struct denali_nand_info *denali)
 	if (ret)
 		goto disable_irq;
 
-	denali->buf = devm_kzalloc(denali->dev, mtd->writesize + mtd->oobsize,
-				   GFP_KERNEL);
-	if (!denali->buf) {
-		ret = -ENOMEM;
-		goto disable_irq;
-	}
-
 	if (ioread32(denali->flash_reg + FEATURES) & FEATURES__DMA)
 		denali->dma_avail = 1;
 
@@ -1460,17 +1454,29 @@ int denali_init(struct denali_nand_info *denali)
 	if (ret)
 		goto disable_irq;
 
+	/*
+	 * This buffer may be DMA-mapped.  Do not use devm_kmalloc() because
+	 * the memory allocated by devm_ is not cache line aligned.
+	 */
+	denali->buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+	if (!denali->buf) {
+		ret = -ENOMEM;
+		goto disable_irq;
+	}
+
 	ret = nand_scan_tail(mtd);
 	if (ret)
-		goto disable_irq;
+		goto free_buf;
 
 	ret = mtd_device_register(mtd, NULL, 0);
 	if (ret) {
 		dev_err(denali->dev, "Failed to register MTD: %d\n", ret);
-		goto disable_irq;
+		goto free_buf;
 	}
 	return 0;
 
+free_buf:
+	kfree(denali->buf);
 disable_irq:
 	denali_disable_irq(denali);
 
@@ -1490,6 +1496,7 @@ void denali_remove(struct denali_nand_info *denali)
 	int bufsize = mtd->writesize + mtd->oobsize;
 
 	nand_release(mtd);
+	kfree(denali->buf);
 	denali_disable_irq(denali);
 	dma_unmap_single(denali->dev, denali->dma_addr, bufsize,
 			 DMA_BIDIRECTIONAL);
-- 
2.7.4

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

* [RESEND PATCH v2 53/53] mtd: nand: denali: enable bad block table scan
  2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
                   ` (24 preceding siblings ...)
  2017-03-23  0:18 ` [RESEND PATCH v2 52/53] mtd: nand: denali: use non-managed kmalloc() for DMA buffer Masahiro Yamada
@ 2017-03-23  0:18 ` Masahiro Yamada
  25 siblings, 0 replies; 41+ messages in thread
From: Masahiro Yamada @ 2017-03-23  0:18 UTC (permalink / raw)
  To: linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Masahiro Yamada

Now this driver is ready to remove NAND_SKIP_BBTSCAN.

The BBT descriptors in denali.c are equivalent to the ones in
nand_bbt.c.  There is no need to duplicate the equivalent structures.
The with-oob decriptors do not work for this driver anyway.

The bbt_pattern (offs = 8) and the version (veroffs = 12) area
overlaps the ECC area.  Set NAND_BBT_NO_OOB flag to use the no_oob
variant of the BBT descriptors.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

Changes in v2:
  - Newly added

 drivers/mtd/nand/denali.c | 31 ++-----------------------------
 1 file changed, 2 insertions(+), 29 deletions(-)

diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 28622a9..9cfc374 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -1207,29 +1207,6 @@ static const struct mtd_ooblayout_ops denali_ooblayout_ops = {
 	.free = denali_ooblayout_free,
 };
 
-static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
-static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
-
-static struct nand_bbt_descr bbt_main_descr = {
-	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
-		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
-	.offs =	8,
-	.len = 4,
-	.veroffs = 12,
-	.maxblocks = 4,
-	.pattern = bbt_pattern,
-};
-
-static struct nand_bbt_descr bbt_mirror_descr = {
-	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
-		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
-	.offs =	8,
-	.len = 4,
-	.veroffs = 12,
-	.maxblocks = 4,
-	.pattern = mirror_pattern,
-};
-
 /* initialize driver data structures */
 static void denali_drv_init(struct denali_nand_info *denali)
 {
@@ -1366,13 +1343,9 @@ int denali_init(struct denali_nand_info *denali)
 	 * bad block management.
 	 */
 
-	/* Bad block management */
-	chip->bbt_td = &bbt_main_descr;
-	chip->bbt_md = &bbt_mirror_descr;
-
-	/* skip the scan for now until we have OOB read and write support */
 	chip->bbt_options |= NAND_BBT_USE_FLASH;
-	chip->options |= NAND_SKIP_BBTSCAN;
+	chip->bbt_options |= NAND_BBT_NO_OOB;
+
 	chip->ecc.mode = NAND_ECC_HW_SYNDROME;
 
 	/* no subpage writes on denali */
-- 
2.7.4

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

* Re: [RESEND PATCH v2 28/53] mtd: nand: denali: support "nand-ecc-strength" DT property
  2017-03-23  0:17 ` [RESEND PATCH v2 28/53] mtd: nand: denali: support "nand-ecc-strength" DT property Masahiro Yamada
@ 2017-03-23  8:43   ` Boris Brezillon
  0 siblings, 0 replies; 41+ messages in thread
From: Boris Brezillon @ 2017-03-23  8:43 UTC (permalink / raw)
  To: Masahiro Yamada
  Cc: linux-mtd, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring

On Thu, 23 Mar 2017 09:17:37 +0900
Masahiro Yamada <yamada.masahiro@socionext.com> wrote:

> Historically, this driver tried to choose as big ECC strength as
> possible, but it would be reasonable to allow DT to set a particular
> ECC strength with "nand-ecc-strength" property.
> 
> Going forward, DT platforms should specify "nand-ecc-strength" or
> "nand-ecc-maximize" to show the ECC strength strategy explicitly.
> 
> If nothing is specified in DT, "nand-ecc-maximize" is implied since
> this was the original behavior.

I would expect the ->ecc_xxx_ds information to be taken into account
when nand-ecc-strength and nand-ecc-maximize are missing (see my reply
to patch 26).

> 
> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> Acked-by: Rob Herring <robh@kernel.org>
> ---
> 
> Changes in v2:
>   - Add available values in the binding document
> 
>  Documentation/devicetree/bindings/mtd/denali-nand.txt |  6 ++++++
>  drivers/mtd/nand/denali.c                             | 18 ++++++++++++++++--
>  drivers/mtd/nand/denali_pci.c                         |  1 +
>  3 files changed, 23 insertions(+), 2 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/mtd/denali-nand.txt b/Documentation/devicetree/bindings/mtd/denali-nand.txt
> index 25313c7..647618e 100644
> --- a/Documentation/devicetree/bindings/mtd/denali-nand.txt
> +++ b/Documentation/devicetree/bindings/mtd/denali-nand.txt
> @@ -11,6 +11,12 @@ Optional properties:
>    - nand-ecc-step-size: must be 512 or 1024.  If not specified, default to:
>        512   for "altr,socfpga-denali-nand"
>      see nand.txt for details.
> +  - nand-ecc-strength: see nand.txt for details.  Available values are:
> +      8, 15      for "altr,socfpga-denali-nand"
> +  - nand-ecc-maximize: see nand.txt for details
> +
> +Note:
> +Either nand-ecc-strength or nand-ecc-maximize should be specified.
>  
>  The device tree may optionally contain sub-nodes describing partitions of the
>  address space. See partition.txt for more detail.
> diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
> index 96074b8..70e9f06 100644
> --- a/drivers/mtd/nand/denali.c
> +++ b/drivers/mtd/nand/denali.c
> @@ -1627,9 +1627,23 @@ int denali_init(struct denali_nand_info *denali)
>  		goto failed_req_irq;
>  	}
>  
> -	ret = denali_set_max_ecc_strength(denali);
> -	if (ret)
> +	if (!chip->ecc.strength && !(chip->ecc.options & NAND_ECC_MAXIMIZE)) {
> +		dev_info(denali->dev,
> +			 "No ECC strength strategy is specified. Maximizing ECC strength\n");
> +		chip->ecc.options |= NAND_ECC_MAXIMIZE;
> +	}
> +
> +	if (chip->ecc.options & NAND_ECC_MAXIMIZE) {
> +		ret = denali_set_max_ecc_strength(denali);
> +		if (ret)
> +			goto failed_req_irq;
> +	} else if (!(denali->ecc_strength_avail & BIT(chip->ecc.strength))) {
> +		dev_err(denali->dev,
> +			"Specified ECC strength (%d) is not supported for this controller.\n",
> +			chip->ecc.strength);
> +		ret = -EINVAL;
>  		goto failed_req_irq;
> +	}
>  
>  	chip->ecc.bytes = denali_calc_ecc_bytes(chip->ecc.size,
>  						chip->ecc.strength);
> diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/denali_pci.c
> index a1ee9f8..a39682a5 100644
> --- a/drivers/mtd/nand/denali_pci.c
> +++ b/drivers/mtd/nand/denali_pci.c
> @@ -87,6 +87,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
>  
>  	denali->ecc_strength_avail = BIT(15) | BIT(8);
>  	denali->caps |= DENALI_CAP_ECC_SIZE_512;
> +	denali->nand.ecc.options |= NAND_ECC_MAXIMIZE;
>  
>  	ret = denali_init(denali);
>  	if (ret)

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

* Re: [RESEND PATCH v2 42/53] mtd: nand: denali: switch over to cmd_ctrl instead of cmdfunc
  2017-03-23  0:17 ` [RESEND PATCH v2 42/53] mtd: nand: denali: switch over to cmd_ctrl instead of cmdfunc Masahiro Yamada
@ 2017-03-23  8:52   ` Boris Brezillon
  0 siblings, 0 replies; 41+ messages in thread
From: Boris Brezillon @ 2017-03-23  8:52 UTC (permalink / raw)
  To: Masahiro Yamada
  Cc: linux-mtd, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring

On Thu, 23 Mar 2017 09:17:51 +0900
Masahiro Yamada <yamada.masahiro@socionext.com> wrote:

> The NAND_CMD_SET_FEATURES support is missing from denali_cmdfunc().
> This is needed for nand_onfi_set_features().  It would be possible
> to add it in the current implementation, but having ->cmd_ctrl()
> seems a better approach from the discussion with Boris [1].
> 
> Rely on the default implementation for ->cmdfunc() and implement
> the driver's own ->cmd_ctrl().
> 
> Also add ->write_byte(), which is needed to write parameters for
> NAND_CMD_SET_FEATURES.

I'll review more carefully, but it goes in the right direction.

Thanks a lot for doing that!

> 
> [1] https://lkml.org/lkml/2017/3/15/97
> 
> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> ---
> 
> Changes in v2:
>   - Newly added
> 
>  drivers/mtd/nand/denali.c | 104 +++++++++++++++++++++++-----------------------
>  1 file changed, 52 insertions(+), 52 deletions(-)
> 
> diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
> index c0b8179..2d25b2f 100644
> --- a/drivers/mtd/nand/denali.c
> +++ b/drivers/mtd/nand/denali.c
> @@ -230,20 +230,16 @@ static uint32_t denali_wait_for_irq(struct denali_nand_info *denali,
>  	return denali->irq_status;
>  }
>  
> -/* resets a specific device connected to the core */
> -static void reset_bank(struct denali_nand_info *denali)
> +static uint32_t denali_check_irq(struct denali_nand_info *denali)
>  {
> +	unsigned long flags;
>  	uint32_t irq_status;
>  
> -	denali_reset_irq(denali);
> -
> -	iowrite32(1 << denali->flash_bank, denali->flash_reg + DEVICE_RESET);
> -
> -	irq_status = denali_wait_for_irq(denali,
> -					 INTR__RST_COMP | INTR__TIME_OUT);
> +	spin_lock_irqsave(&denali->irq_lock, flags);
> +	irq_status = denali->irq_status;
> +	spin_unlock_irqrestore(&denali->irq_lock, flags);
>  
> -	if (!(irq_status & INTR__RST_COMP))
> -		dev_err(denali->dev, "reset bank failed.\n");
> +	return irq_status;
>  }
>  
>  /*
> @@ -273,6 +269,42 @@ static uint8_t denali_read_byte(struct mtd_info *mtd)
>  	return ioread32(denali->flash_mem + 0x10);
>  }
>  
> +static void denali_write_byte(struct mtd_info *mtd, uint8_t byte)
> +{
> +	struct denali_nand_info *denali = mtd_to_denali(mtd);
> +
> +	index_addr(denali, MODE_11 | BANK(denali->flash_bank) | 2, byte);
> +}
> +
> +static void denali_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
> +{
> +	struct denali_nand_info *denali = mtd_to_denali(mtd);
> +	uint32_t type;
> +
> +	if (ctrl & NAND_CLE)
> +		type = 0;
> +	else if (ctrl & NAND_ALE)
> +		type = 1;
> +	else
> +		return;
> +
> +	/*
> +	 * Some commands are followed by chip->dev_ready or chip->waitfunc.
> +	 * irq_status must be cleared here to catch the R/B# interrupt later.
> +	 */
> +	if (ctrl & NAND_CTRL_CHANGE)
> +		denali_reset_irq(denali);
> +
> +	index_addr(denali, MODE_11 | BANK(denali->flash_bank) | type, dat);
> +}
> +
> +static int denali_dev_ready(struct mtd_info *mtd)
> +{
> +	struct denali_nand_info *denali = mtd_to_denali(mtd);
> +
> +	return !!(denali_check_irq(denali) & INTR__INT_ACT);
> +}
> +
>  /*
>   * sends a pipeline command operation to the controller. See the Denali NAND
>   * controller's user guide for more information (section 4.2.3.6).
> @@ -788,7 +820,13 @@ static void denali_select_chip(struct mtd_info *mtd, int chip)
>  
>  static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
>  {
> -	return 0;
> +	struct denali_nand_info *denali = mtd_to_denali(mtd);
> +	uint32_t irq_status;
> +
> +	/* R/B# pin transitioned from low to high? */
> +	irq_status = denali_wait_for_irq(denali, INTR__INT_ACT);
> +
> +	return irq_status & INTR__INT_ACT ? 0 : NAND_STATUS_FAIL;
>  }
>  
>  static int denali_erase(struct mtd_info *mtd, int page)
> @@ -809,46 +847,6 @@ static int denali_erase(struct mtd_info *mtd, int page)
>  	return irq_status & INTR__ERASE_COMP ? 0 : NAND_STATUS_FAIL;
>  }
>  
> -static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
> -			   int page)
> -{
> -	struct denali_nand_info *denali = mtd_to_denali(mtd);
> -	uint32_t addr, irq_status;
> -	int wait_ready = 0;
> -
> -	switch (cmd) {
> -	case NAND_CMD_PARAM:
> -		wait_ready = 1;
> -		break;
> -	case NAND_CMD_STATUS:
> -	case NAND_CMD_READID:
> -		break;
> -	case NAND_CMD_RESET:
> -		reset_bank(denali);
> -		break;
> -	case NAND_CMD_READOOB:
> -		/* TODO: Read OOB data */
> -		return;
> -	default:
> -		pr_err(": unsupported command received 0x%x\n", cmd);
> -		return;
> -	}
> -
> -	denali_reset_irq(denali);
> -
> -	addr = MODE_11 | BANK(denali->flash_bank);
> -	index_addr(denali, addr | 0, cmd);
> -	if (col != -1)
> -		index_addr(denali, addr | 1, col);
> -
> -	if (!wait_ready)
> -		return;
> -
> -	irq_status = denali_wait_for_irq(denali, INTR__INT_ACT);
> -	if (!(irq_status & INTR__INT_ACT))
> -		dev_err(denali->dev, "failed to issue command 0x%x\n", cmd);
> -}
> -
>  #define DIV_ROUND_DOWN_ULL(ll, d) \
>  	({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; })
>  
> @@ -1187,8 +1185,10 @@ int denali_init(struct denali_nand_info *denali)
>  
>  	/* register the driver with the NAND core subsystem */
>  	chip->select_chip = denali_select_chip;
> -	chip->cmdfunc = denali_cmdfunc;
>  	chip->read_byte = denali_read_byte;
> +	chip->write_byte = denali_write_byte;
> +	chip->cmd_ctrl = denali_cmd_ctrl;
> +	chip->dev_ready = denali_dev_ready;
>  	chip->waitfunc = denali_waitfunc;
>  	/* clk rate info is needed for setup_data_interface */
>  	if (denali->clk_x_rate)

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

* Re: [RESEND PATCH v2 52/53] mtd: nand: denali: use non-managed kmalloc() for DMA buffer
  2017-03-23  0:18 ` [RESEND PATCH v2 52/53] mtd: nand: denali: use non-managed kmalloc() for DMA buffer Masahiro Yamada
@ 2017-03-23 11:33   ` Robin Murphy
  2017-03-24  1:41     ` yamada.masahiro
  0 siblings, 1 reply; 41+ messages in thread
From: Robin Murphy @ 2017-03-23 11:33 UTC (permalink / raw)
  To: Masahiro Yamada, linux-mtd
  Cc: Boris Brezillon, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring, Russell King,
	Lars-Peter Clausen

On 23/03/17 00:18, Masahiro Yamada wrote:
> As Russell and Lars stated in the discussion [1], using
> devm_k*alloc() with DMA is not a good idea.
> 
> Let's use kmalloc (not kzalloc because no need for zero-out).
> Also, allocate the buffer as late as possible because it must be
> freed for any error that follows.

I still think the way the driver actually uses this buffer looks very
suspect - keeping it permanently mapped for bidirectional (AKA "I don't
know") streaming DMA across multiple operations doesn't add up. If the
device might access it at any time (or the driver simply doesn't want to
have to care) it should be a coherent allocation. Otherwise, it should
just be mapped/unmapped with the appropriate direction around each
operation.

Either way though, this patch at least makes things somewhat *less*
incorrect, so:

Acked-by: Robin Murphy <robin.murphy@arm.com>

> [1] https://lkml.org/lkml/2017/3/8/693
> 
> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> Cc: Russell King <rmk+kernel@armlinux.org.uk>
> Cc: Lars-Peter Clausen <lars@metafoo.de>
> Cc: Robin Murphy <robin.murphy@arm.com>
> ---
> 
> Changes in v2:
>   - Newly added
> 
>  drivers/mtd/nand/denali.c | 25 ++++++++++++++++---------
>  1 file changed, 16 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
> index 4900745..28622a9 100644
> --- a/drivers/mtd/nand/denali.c
> +++ b/drivers/mtd/nand/denali.c
> @@ -23,6 +23,7 @@
>  #include <linux/mutex.h>
>  #include <linux/mtd/mtd.h>
>  #include <linux/module.h>
> +#include <linux/slab.h>
>  
>  #include "denali.h"
>  
> @@ -1343,13 +1344,6 @@ int denali_init(struct denali_nand_info *denali)
>  	if (ret)
>  		goto disable_irq;
>  
> -	denali->buf = devm_kzalloc(denali->dev, mtd->writesize + mtd->oobsize,
> -				   GFP_KERNEL);
> -	if (!denali->buf) {
> -		ret = -ENOMEM;
> -		goto disable_irq;
> -	}
> -
>  	if (ioread32(denali->flash_reg + FEATURES) & FEATURES__DMA)
>  		denali->dma_avail = 1;
>  
> @@ -1460,17 +1454,29 @@ int denali_init(struct denali_nand_info *denali)
>  	if (ret)
>  		goto disable_irq;
>  
> +	/*
> +	 * This buffer may be DMA-mapped.  Do not use devm_kmalloc() because
> +	 * the memory allocated by devm_ is not cache line aligned.
> +	 */
> +	denali->buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
> +	if (!denali->buf) {
> +		ret = -ENOMEM;
> +		goto disable_irq;
> +	}
> +
>  	ret = nand_scan_tail(mtd);
>  	if (ret)
> -		goto disable_irq;
> +		goto free_buf;
>  
>  	ret = mtd_device_register(mtd, NULL, 0);
>  	if (ret) {
>  		dev_err(denali->dev, "Failed to register MTD: %d\n", ret);
> -		goto disable_irq;
> +		goto free_buf;
>  	}
>  	return 0;
>  
> +free_buf:
> +	kfree(denali->buf);
>  disable_irq:
>  	denali_disable_irq(denali);
>  
> @@ -1490,6 +1496,7 @@ void denali_remove(struct denali_nand_info *denali)
>  	int bufsize = mtd->writesize + mtd->oobsize;
>  
>  	nand_release(mtd);
> +	kfree(denali->buf);
>  	denali_disable_irq(denali);
>  	dma_unmap_single(denali->dev, denali->dma_addr, bufsize,
>  			 DMA_BIDIRECTIONAL);
> 

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

* RE: [RESEND PATCH v2 52/53] mtd: nand: denali: use non-managed kmalloc() for DMA buffer
  2017-03-23 11:33   ` Robin Murphy
@ 2017-03-24  1:41     ` yamada.masahiro
  2017-03-24 17:09       ` Robin Murphy
  0 siblings, 1 reply; 41+ messages in thread
From: yamada.masahiro @ 2017-03-24  1:41 UTC (permalink / raw)
  To: robin.murphy, linux-mtd
  Cc: boris.brezillon, dwmw2, marek.vasut, computersforpeace,
	thorsten.christiansson, laurent.monat, dinguyen,
	artem.bityutskiy, grmoore, ejo, chuanxiao.dong, mhiramat,
	jaswinder.singh, robh, rmk+kernel, lars

Hi Robin,

> I still think the way the driver actually uses this buffer looks very
> suspect - keeping it permanently mapped for bidirectional (AKA "I don't
> know") streaming DMA across multiple operations doesn't add up. If the
> device might access it at any time (or the driver simply doesn't want to
> have to care) it should be a coherent allocation. Otherwise, it should
> just be mapped/unmapped with the appropriate direction around each
> operation.

This buffer is not permanently mapped for bidirectional any more.

I fixed it in 51/53:
http://patchwork.ozlabs.org/patch/742411/

When possible, the read/write callbacks
directly calls dma_map_single() for the buffer
passed from the core framework.
(either DMA_TO_DEVICE or DMA_FROM_DEVICE)

This will bypass the memcpy() to/from the internal buffer.



This driver internal buffer is still needed for raw read/write.
The Denali IP uses syndrome page layout.
So, the raw accessors must arrange the payload/ECC data layout.

This problem is addressed by 48/53:
http://patchwork.ozlabs.org/patch/742416/


I also examined the possibility for coherent allocation.
Even if I use dma_alloc_coherent, I need another bounce buffer anyway
for the syndrome page shuffling.  So, this way seems less efficient.


Masahiro


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

* Re: [RESEND PATCH v2 52/53] mtd: nand: denali: use non-managed kmalloc() for DMA buffer
  2017-03-24  1:41     ` yamada.masahiro
@ 2017-03-24 17:09       ` Robin Murphy
  0 siblings, 0 replies; 41+ messages in thread
From: Robin Murphy @ 2017-03-24 17:09 UTC (permalink / raw)
  To: yamada.masahiro, linux-mtd
  Cc: boris.brezillon, dwmw2, marek.vasut, computersforpeace,
	thorsten.christiansson, laurent.monat, dinguyen,
	artem.bityutskiy, grmoore, ejo, chuanxiao.dong, mhiramat,
	jaswinder.singh, robh, rmk+kernel, lars

On 24/03/17 01:41, yamada.masahiro@socionext.com wrote:
> Hi Robin,
> 
>> I still think the way the driver actually uses this buffer looks very
>> suspect - keeping it permanently mapped for bidirectional (AKA "I don't
>> know") streaming DMA across multiple operations doesn't add up. If the
>> device might access it at any time (or the driver simply doesn't want to
>> have to care) it should be a coherent allocation. Otherwise, it should
>> just be mapped/unmapped with the appropriate direction around each
>> operation.
> 
> This buffer is not permanently mapped for bidirectional any more.
> 
> I fixed it in 51/53:
> http://patchwork.ozlabs.org/patch/742411/
> 
> When possible, the read/write callbacks
> directly calls dma_map_single() for the buffer
> passed from the core framework.
> (either DMA_TO_DEVICE or DMA_FROM_DEVICE)
> 
> This will bypass the memcpy() to/from the internal buffer.
> 
> 
> 
> This driver internal buffer is still needed for raw read/write.
> The Denali IP uses syndrome page layout.
> So, the raw accessors must arrange the payload/ECC data layout.
> 
> This problem is addressed by 48/53:
> http://patchwork.ozlabs.org/patch/742416/
> 
> 
> I also examined the possibility for coherent allocation.
> Even if I use dma_alloc_coherent, I need another bounce buffer anyway
> for the syndrome page shuffling.  So, this way seems less efficient.

Ah, so it's only a visibility issue at my end, then. Great stuff!

(on balance, I'm fine with not being sent the other 52 patches just for
the sake of this one!)

Thanks,
Robin.

> 
> 
> Masahiro
> 

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

* Re: [RESEND PATCH v2 50/53] mtd: nand: allocate aligned buffers if NAND_OWN_BUFFERS is unset
  2017-03-23  0:17 ` [RESEND PATCH v2 50/53] mtd: nand: allocate aligned buffers if NAND_OWN_BUFFERS is unset Masahiro Yamada
@ 2017-03-27  8:00   ` Boris Brezillon
  2017-03-28  1:13     ` yamada.masahiro
  0 siblings, 1 reply; 41+ messages in thread
From: Boris Brezillon @ 2017-03-27  8:00 UTC (permalink / raw)
  To: Masahiro Yamada
  Cc: linux-mtd, David Woodhouse, Marek Vasut, Brian Norris,
	thorsten.christiansson, laurent.monat, Dinh Nguyen,
	Artem Bityutskiy, Graham Moore, Enrico Jorns, Chuanxiao Dong,
	Masami Hiramatsu, Jassi Brar, Rob Herring

Hi Masahiro,

On Thu, 23 Mar 2017 09:17:59 +0900
Masahiro Yamada <yamada.masahiro@socionext.com> wrote:

> Commit 66507c7bc889 ("mtd: nand: Add support to use nand_base poi
> databuf as bounce buffer") fixed the issue that drivers can be
> passed with a kmap()'d buffer.  This addressed the use_bufpoi = 0
> case.
> 
> When use_bufpoi = 1, chip->buffers->databuf is used.  The databuf
> allocated by nand_scan_tail() is not suitable for DMA due to the
> offset, sizeof(*chip->buffers).

As said earlier, I'm fine with the patch content, but I'm not sure
about your explanation. Alignment requirements are DMA controller
dependent so you're not exactly fixing a bug for all NAND controller
drivers, just those that require >4 bytes alignment.

Regarding the cache line alignment issue, in this case it shouldn't be
a problem, because the driver and the core shouldn't touch the
chip->buffers object during a DMA transfer, so dma_map/unmap_single()
should work fine (they may flush/invalidate one cache line entry that
contains non-payload data, but that's not a problem as long as nothing
is modified during the DMA transfer).

> 
> So, drivers using DMA are very likely to end up with setting the
> NAND_OWN_BUFFERS flag and allocate buffers by themselves.  Drivers
> tend to use devm_k*alloc to simplify the probe failure path, but
> devm_k*alloc() does not return a cache line aligned buffer.

Oh, I didn't know that. I had a closer look at the code, and indeed,
devm_kmalloc() does not guarantee any alignment.

> 
> If used, it could violate the DMA-API requirement stated in
> Documentation/DMA-API.txt:
>   Warnings:  Memory coherency operates at a granularity called the
>   cache line width.  In order for memory mapped by this API to
>   operate correctly, the mapped region must begin exactly on a cache
>   line boundary and end exactly on one (to prevent two separately
>   mapped regions from sharing a single cache line).
> 
> To sum up, NAND_OWN_BUFFERS is not very convenient for drivers.
> nand_scan_tail() can give a separate buffer for each of ecccalc,
> ecccode, databuf.  It is guaranteed kmalloc'ed memory is aligned
> with ARCH_DMA_MINALIGN.

Maybe I'm wrong, but AFAIU, kmalloc&co do not guarantee cache line
alignment for small buffers (< cache line), so even kmalloc can return
a buffer that is not cache line aligned.
This being said, we should be good because most NAND controllers are
only manipulating the page buffer (->databuf) which is large enough to
be cache line aligned.

Anyway, I'm not discussing the need for this patch, just the reasons we
need it ;-).

To me, it's more that we want to support as many cases as possible, no
matter the DMA controller requirements, and allocating each buffer
independently allows us to do that for almost no overhead.

How about simplifying the commit message to only focus on what this
patch is really fixing/improving?

"
Some NAND controllers are using DMA engine requiring a specific buffer
alignment. The core provides no guarantee on the nand_buffers pointers,
which forces some drivers to allocate their own buffers and pass the
NAND_OWN_BUFFERS flag.

Rework the nand_buffers allocation logic to allocate each buffer
independently. This should make most NAND controllers/DMA engine
happy, and allow us to get rid of these custom buf allocation in NAND
controller drivers.
"

> This is enough for most drivers because
> it is rare that DMA engines require larger alignment than CPU's
> cache line.
> 
> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> ---
> 
> Changes in v2:
>   - Newly added
> 
>  drivers/mtd/nand/nand_base.c | 34 +++++++++++++++++++++++++++-------
>  1 file changed, 27 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
> index e13f959..6fc0422 100644
> --- a/drivers/mtd/nand/nand_base.c
> +++ b/drivers/mtd/nand/nand_base.c
> @@ -4614,13 +4614,25 @@ int nand_scan_tail(struct mtd_info *mtd)
>  	}
>  
>  	if (!(chip->options & NAND_OWN_BUFFERS)) {
> -		nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize
> -				+ mtd->oobsize * 3, GFP_KERNEL);
> +		nbuf = kzalloc(sizeof(*nbuf), GFP_KERNEL);
>  		if (!nbuf)
>  			return -ENOMEM;
> -		nbuf->ecccalc = (uint8_t *)(nbuf + 1);
> -		nbuf->ecccode = nbuf->ecccalc + mtd->oobsize;
> -		nbuf->databuf = nbuf->ecccode + mtd->oobsize;
> +		nbuf->ecccalc = kmalloc(mtd->oobsize, GFP_KERNEL);
> +		if (!nbuf->ecccalc) {
> +			ret = -EINVAL;
> +			goto err_free;
> +		}
> +		nbuf->ecccode = kmalloc(mtd->oobsize, GFP_KERNEL);
> +		if (!nbuf->ecccode) {
> +			ret = -EINVAL;
> +			goto err_free;
> +		}
> +		nbuf->databuf = kmalloc(mtd->writesize + mtd->oobsize,
> +					GFP_KERNEL);
> +		if (!nbuf->databuf) {
> +			ret = -EINVAL;
> +			goto err_free;
> +		}
>  
>  		chip->buffers = nbuf;
>  	} else {
> @@ -4863,8 +4875,12 @@ int nand_scan_tail(struct mtd_info *mtd)
>  	/* Build bad block table */
>  	return chip->scan_bbt(mtd);
>  err_free:
> -	if (!(chip->options & NAND_OWN_BUFFERS))
> +	if (!(chip->options & NAND_OWN_BUFFERS)) {
> +		kfree(chip->buffers->databuf);
> +		kfree(chip->buffers->ecccode);
> +		kfree(chip->buffers->ecccalc);
>  		kfree(chip->buffers);
> +	}
>  	return ret;
>  }
>  EXPORT_SYMBOL(nand_scan_tail);
> @@ -4915,8 +4931,12 @@ void nand_cleanup(struct nand_chip *chip)
>  
>  	/* Free bad block table memory */
>  	kfree(chip->bbt);
> -	if (!(chip->options & NAND_OWN_BUFFERS))
> +	if (!(chip->options & NAND_OWN_BUFFERS)) {
> +		kfree(chip->buffers->databuf);
> +		kfree(chip->buffers->ecccode);
> +		kfree(chip->buffers->ecccalc);
>  		kfree(chip->buffers);
> +	}
>  
>  	/* Free bad block descriptor memory */
>  	if (chip->badblock_pattern && chip->badblock_pattern->options

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

* RE: [RESEND PATCH v2 50/53] mtd: nand: allocate aligned buffers if NAND_OWN_BUFFERS is unset
  2017-03-27  8:00   ` Boris Brezillon
@ 2017-03-28  1:13     ` yamada.masahiro
  2017-03-28  7:59       ` Boris Brezillon
  0 siblings, 1 reply; 41+ messages in thread
From: yamada.masahiro @ 2017-03-28  1:13 UTC (permalink / raw)
  To: boris.brezillon
  Cc: linux-mtd, dwmw2, marek.vasut, computersforpeace,
	thorsten.christiansson, laurent.monat, dinguyen,
	artem.bityutskiy, grmoore, ejo, chuanxiao.dong, mhiramat,
	jaswinder.singh, robh

Hi Boris,


> -----Original Message-----
> From: Boris Brezillon [mailto:boris.brezillon@free-electrons.com]
> Sent: Monday, March 27, 2017 5:01 PM
> To: Yamada, Masahiro/山田 真弘 <yamada.masahiro@socionext.com>
> Cc: linux-mtd@lists.infradead.org; David Woodhouse
> <dwmw2@infradead.org>; Marek Vasut <marek.vasut@gmail.com>; Brian Norris
> <computersforpeace@gmail.com>; thorsten.christiansson@idquantique.com;
> laurent.monat@idquantique.com; Dinh Nguyen <dinguyen@kernel.org>; Artem
> Bityutskiy <artem.bityutskiy@linux.intel.com>; Graham Moore
> <grmoore@opensource.altera.com>; Enrico Jorns <ejo@pengutronix.de>;
> Chuanxiao Dong <chuanxiao.dong@intel.com>; Masami Hiramatsu
> <mhiramat@kernel.org>; Jassi Brar <jaswinder.singh@linaro.org>; Rob
> Herring <robh@kernel.org>
> Subject: Re: [RESEND PATCH v2 50/53] mtd: nand: allocate aligned buffers
> if NAND_OWN_BUFFERS is unset
> 
> Hi Masahiro,
> 
> On Thu, 23 Mar 2017 09:17:59 +0900
> Masahiro Yamada <yamada.masahiro@socionext.com> wrote:
> 
> > Commit 66507c7bc889 ("mtd: nand: Add support to use nand_base poi
> > databuf as bounce buffer") fixed the issue that drivers can be
> > passed with a kmap()'d buffer.  This addressed the use_bufpoi = 0
> > case.
> >
> > When use_bufpoi = 1, chip->buffers->databuf is used.  The databuf
> > allocated by nand_scan_tail() is not suitable for DMA due to the
> > offset, sizeof(*chip->buffers).
> 
> As said earlier, I'm fine with the patch content, but I'm not sure
> about your explanation. Alignment requirements are DMA controller
> dependent so you're not exactly fixing a bug for all NAND controller
> drivers, just those that require >4 bytes alignment.


We have two contexts when we talk about alignment for DMA:

[A] Requirement by CPU architecture  (cache alignment)
[B] Requirement by the controller's DMA engine


As I will state below, having sizeof(*chip->buffers) in the same cache
line is no good.  This is the same reason as devm_* is not recommended for DMA.
(https://lkml.org/lkml/2017/3/8/693)


The current situation violates [A].

Usually [B] is less than [A].
So, if we meet [A] (by using kmalloc), [B] will be met as well.



> Regarding the cache line alignment issue, in this case it shouldn't be
> a problem, because the driver and the core shouldn't touch the
> chip->buffers object during a DMA transfer, so dma_map/unmap_single()
> should work fine (they may flush/invalidate one cache line entry that
> contains non-payload data, but that's not a problem as long as nothing
> is modified during the DMA transfer).


This is related to 52/53:
http://patchwork.ozlabs.org/patch/742409/

Can you also read this?
https://lkml.org/lkml/2017/3/8/693

Your comment is very similar to what was discussed in the thread.



> >
> > So, drivers using DMA are very likely to end up with setting the
> > NAND_OWN_BUFFERS flag and allocate buffers by themselves.  Drivers
> > tend to use devm_k*alloc to simplify the probe failure path, but
> > devm_k*alloc() does not return a cache line aligned buffer.
> 
> Oh, I didn't know that. I had a closer look at the code, and indeed,
> devm_kmalloc() does not guarantee any alignment.
> 
> >
> > If used, it could violate the DMA-API requirement stated in
> > Documentation/DMA-API.txt:
> >   Warnings:  Memory coherency operates at a granularity called the
> >   cache line width.  In order for memory mapped by this API to
> >   operate correctly, the mapped region must begin exactly on a cache
> >   line boundary and end exactly on one (to prevent two separately
> >   mapped regions from sharing a single cache line).
> >
> > To sum up, NAND_OWN_BUFFERS is not very convenient for drivers.
> > nand_scan_tail() can give a separate buffer for each of ecccalc,
> > ecccode, databuf.  It is guaranteed kmalloc'ed memory is aligned
> > with ARCH_DMA_MINALIGN.
> 
> Maybe I'm wrong, but AFAIU, kmalloc&co do not guarantee cache line
> alignment for small buffers (< cache line), so even kmalloc can return
> a buffer that is not cache line aligned.
> This being said, we should be good because most NAND controllers are
> only manipulating the page buffer (->databuf) which is large enough to
> be cache line aligned.


In my understanding kmalloc() returns cache aligned address even for 1 byte memory.

Can you read the following part of Documentation/DMA-API-HOWTO.txt?

------------------------------------>8----------------------------------------
2) ARCH_DMA_MINALIGN

   Architectures must ensure that kmalloc'ed buffer is
   DMA-safe. Drivers and subsystems depend on it. If an architecture
   isn't fully DMA-coherent (i.e. hardware doesn't ensure that data in
   the CPU cache is identical to data in main memory),
   ARCH_DMA_MINALIGN must be set so that the memory allocator
   makes sure that kmalloc'ed buffer doesn't share a cache line with
   the others. See arch/arm/include/asm/cache.h as an example.

   Note that ARCH_DMA_MINALIGN is about DMA memory alignment
   constraints. You don't need to worry about the architecture data
   alignment constraints (e.g. the alignment constraints about 64-bit
   objects).
------------------------------------>8----------------------------------------






> Anyway, I'm not discussing the need for this patch, just the reasons we
> need it ;-).
> 
> To me, it's more that we want to support as many cases as possible, no
> matter the DMA controller requirements, and allocating each buffer
> independently allows us to do that for almost no overhead.
> 
> How about simplifying the commit message to only focus on what this
> patch is really fixing/improving?


I am OK to reword the git-log,
but can you read the references I suggested first?

Please let me know if you suspicious something.

Masahiro





> "
> Some NAND controllers are using DMA engine requiring a specific buffer
> alignment. The core provides no guarantee on the nand_buffers pointers,
> which forces some drivers to allocate their own buffers and pass the
> NAND_OWN_BUFFERS flag.
> 
> Rework the nand_buffers allocation logic to allocate each buffer
> independently. This should make most NAND controllers/DMA engine
> happy, and allow us to get rid of these custom buf allocation in NAND
> controller drivers.
> "
> 
> > This is enough for most drivers because
> > it is rare that DMA engines require larger alignment than CPU's
> > cache line.
> >
> > Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> > ---
> >
> > Changes in v2:
> >   - Newly added
> >
> >  drivers/mtd/nand/nand_base.c | 34 +++++++++++++++++++++++++++-------
> >  1 file changed, 27 insertions(+), 7 deletions(-)
> >
> > diff --git a/drivers/mtd/nand/nand_base.c
> b/drivers/mtd/nand/nand_base.c
> > index e13f959..6fc0422 100644
> > --- a/drivers/mtd/nand/nand_base.c
> > +++ b/drivers/mtd/nand/nand_base.c
> > @@ -4614,13 +4614,25 @@ int nand_scan_tail(struct mtd_info *mtd)
> >  	}
> >
> >  	if (!(chip->options & NAND_OWN_BUFFERS)) {
> > -		nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize
> > -				+ mtd->oobsize * 3, GFP_KERNEL);
> > +		nbuf = kzalloc(sizeof(*nbuf), GFP_KERNEL);
> >  		if (!nbuf)
> >  			return -ENOMEM;
> > -		nbuf->ecccalc = (uint8_t *)(nbuf + 1);
> > -		nbuf->ecccode = nbuf->ecccalc + mtd->oobsize;
> > -		nbuf->databuf = nbuf->ecccode + mtd->oobsize;
> > +		nbuf->ecccalc = kmalloc(mtd->oobsize, GFP_KERNEL);
> > +		if (!nbuf->ecccalc) {
> > +			ret = -EINVAL;
> > +			goto err_free;
> > +		}
> > +		nbuf->ecccode = kmalloc(mtd->oobsize, GFP_KERNEL);
> > +		if (!nbuf->ecccode) {
> > +			ret = -EINVAL;
> > +			goto err_free;
> > +		}
> > +		nbuf->databuf = kmalloc(mtd->writesize + mtd->oobsize,
> > +					GFP_KERNEL);
> > +		if (!nbuf->databuf) {
> > +			ret = -EINVAL;
> > +			goto err_free;
> > +		}
> >
> >  		chip->buffers = nbuf;
> >  	} else {
> > @@ -4863,8 +4875,12 @@ int nand_scan_tail(struct mtd_info *mtd)
> >  	/* Build bad block table */
> >  	return chip->scan_bbt(mtd);
> >  err_free:
> > -	if (!(chip->options & NAND_OWN_BUFFERS))
> > +	if (!(chip->options & NAND_OWN_BUFFERS)) {
> > +		kfree(chip->buffers->databuf);
> > +		kfree(chip->buffers->ecccode);
> > +		kfree(chip->buffers->ecccalc);
> >  		kfree(chip->buffers);
> > +	}
> >  	return ret;
> >  }
> >  EXPORT_SYMBOL(nand_scan_tail);
> > @@ -4915,8 +4931,12 @@ void nand_cleanup(struct nand_chip *chip)
> >
> >  	/* Free bad block table memory */
> >  	kfree(chip->bbt);
> > -	if (!(chip->options & NAND_OWN_BUFFERS))
> > +	if (!(chip->options & NAND_OWN_BUFFERS)) {
> > +		kfree(chip->buffers->databuf);
> > +		kfree(chip->buffers->ecccode);
> > +		kfree(chip->buffers->ecccalc);
> >  		kfree(chip->buffers);
> > +	}
> >
> >  	/* Free bad block descriptor memory */
> >  	if (chip->badblock_pattern && chip->badblock_pattern->options


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

* Re: [RESEND PATCH v2 50/53] mtd: nand: allocate aligned buffers if NAND_OWN_BUFFERS is unset
  2017-03-28  1:13     ` yamada.masahiro
@ 2017-03-28  7:59       ` Boris Brezillon
  2017-03-28  8:07         ` Boris Brezillon
                           ` (2 more replies)
  0 siblings, 3 replies; 41+ messages in thread
From: Boris Brezillon @ 2017-03-28  7:59 UTC (permalink / raw)
  To: yamada.masahiro
  Cc: linux-mtd, dwmw2, marek.vasut, computersforpeace,
	thorsten.christiansson, laurent.monat, dinguyen,
	artem.bityutskiy, grmoore, ejo, chuanxiao.dong, mhiramat,
	jaswinder.singh, robh, Russell King - ARM Linux

+Russell to correct me if I'm wrong or give further information.

On Tue, 28 Mar 2017 01:13:10 +0000
<yamada.masahiro@socionext.com> wrote:

> Hi Boris,
> 
> 
> > -----Original Message-----
> > From: Boris Brezillon [mailto:boris.brezillon@free-electrons.com]
> > Sent: Monday, March 27, 2017 5:01 PM
> > To: Yamada, Masahiro/山田 真弘 <yamada.masahiro@socionext.com>
> > Cc: linux-mtd@lists.infradead.org; David Woodhouse
> > <dwmw2@infradead.org>; Marek Vasut <marek.vasut@gmail.com>; Brian Norris
> > <computersforpeace@gmail.com>; thorsten.christiansson@idquantique.com;
> > laurent.monat@idquantique.com; Dinh Nguyen <dinguyen@kernel.org>; Artem
> > Bityutskiy <artem.bityutskiy@linux.intel.com>; Graham Moore
> > <grmoore@opensource.altera.com>; Enrico Jorns <ejo@pengutronix.de>;
> > Chuanxiao Dong <chuanxiao.dong@intel.com>; Masami Hiramatsu
> > <mhiramat@kernel.org>; Jassi Brar <jaswinder.singh@linaro.org>; Rob
> > Herring <robh@kernel.org>
> > Subject: Re: [RESEND PATCH v2 50/53] mtd: nand: allocate aligned buffers
> > if NAND_OWN_BUFFERS is unset
> > 
> > Hi Masahiro,
> > 
> > On Thu, 23 Mar 2017 09:17:59 +0900
> > Masahiro Yamada <yamada.masahiro@socionext.com> wrote:
> >   
> > > Commit 66507c7bc889 ("mtd: nand: Add support to use nand_base poi
> > > databuf as bounce buffer") fixed the issue that drivers can be
> > > passed with a kmap()'d buffer.  This addressed the use_bufpoi = 0
> > > case.
> > >
> > > When use_bufpoi = 1, chip->buffers->databuf is used.  The databuf
> > > allocated by nand_scan_tail() is not suitable for DMA due to the
> > > offset, sizeof(*chip->buffers).  
> > 
> > As said earlier, I'm fine with the patch content, but I'm not sure
> > about your explanation. Alignment requirements are DMA controller
> > dependent so you're not exactly fixing a bug for all NAND controller
> > drivers, just those that require >4 bytes alignment.  
> 
> 
> We have two contexts when we talk about alignment for DMA:
> 
> [A] Requirement by CPU architecture  (cache alignment)
> [B] Requirement by the controller's DMA engine
> 
> 
> As I will state below, having sizeof(*chip->buffers) in the same cache
> line is no good.  This is the same reason as devm_* is not recommended for DMA.
> (https://lkml.org/lkml/2017/3/8/693)

Having non-cache line aligned buffers is definitely more dangerous,
but, AFAIU, it's not impossible.

Let's consider this case:


|        cache line        |        cache line        | ... |
-------------------------------------------------------------
| nand_buffers size |                    data               |


If you call dma_map_single(dev, data, size, DMA_TO_DEVICE), the first
cache line will be flushed (content written back to memory), and
assuming you don't touch nand_buffers content between dma_map_single()
and dma_unmap_single() you shouldn't have any problem (the cache line
cannot magically turn dirty and thus cannot be flushed in the
background).

For the DMA_FROM_DEVICE direction, the cache line is invalidated when
dma_unmap_single() is called, which means your nand_buffers content
might be updated with what is present in SDRAM, but it shouldn't have
changed since nand_buffers is only touched at initialization time (when
the buffer is created).

So, for our use case where nand_buffers is never modified between
dma_map_single() and dma_unmap_single(), it should be safe to have
non-cache line aligned buffers.

Russell, please let me know if my reasoning is incorrect.
Note that I'm not arguing against the new approach where we allocate
each buffer independently using kmalloc (having cache line aligned
buffers is definitely safer and does not imply any significant
overhead), I just want to make sure I understand things correctly.

> 
> 
> The current situation violates [A].

Do you have a real failure that is proven to be caused by mis cache
line alignment, or are you just speculating?

> 
> Usually [B] is less than [A].

Yep, it's likely the case.

> So, if we meet [A] (by using kmalloc), [B] will be met as well.

Sure.

> 
> 
> 
> > Regarding the cache line alignment issue, in this case it shouldn't be
> > a problem, because the driver and the core shouldn't touch the
> > chip->buffers object during a DMA transfer, so dma_map/unmap_single()
> > should work fine (they may flush/invalidate one cache line entry that
> > contains non-payload data, but that's not a problem as long as nothing
> > is modified during the DMA transfer).  
> 
> 
> This is related to 52/53:
> http://patchwork.ozlabs.org/patch/742409/
> 
> Can you also read this?
> https://lkml.org/lkml/2017/3/8/693
> 
> Your comment is very similar to what was discussed in the thread.

I read it.

> 
> 
> 
> > >
> > > So, drivers using DMA are very likely to end up with setting the
> > > NAND_OWN_BUFFERS flag and allocate buffers by themselves.  Drivers
> > > tend to use devm_k*alloc to simplify the probe failure path, but
> > > devm_k*alloc() does not return a cache line aligned buffer.  
> > 
> > Oh, I didn't know that. I had a closer look at the code, and indeed,
> > devm_kmalloc() does not guarantee any alignment.
> >   
> > >
> > > If used, it could violate the DMA-API requirement stated in
> > > Documentation/DMA-API.txt:
> > >   Warnings:  Memory coherency operates at a granularity called the
> > >   cache line width.  In order for memory mapped by this API to
> > >   operate correctly, the mapped region must begin exactly on a cache
> > >   line boundary and end exactly on one (to prevent two separately
> > >   mapped regions from sharing a single cache line).
> > >
> > > To sum up, NAND_OWN_BUFFERS is not very convenient for drivers.
> > > nand_scan_tail() can give a separate buffer for each of ecccalc,
> > > ecccode, databuf.  It is guaranteed kmalloc'ed memory is aligned
> > > with ARCH_DMA_MINALIGN.  
> > 
> > Maybe I'm wrong, but AFAIU, kmalloc&co do not guarantee cache line
> > alignment for small buffers (< cache line), so even kmalloc can return
> > a buffer that is not cache line aligned.
> > This being said, we should be good because most NAND controllers are
> > only manipulating the page buffer (->databuf) which is large enough to
> > be cache line aligned.  
> 
> 
> In my understanding kmalloc() returns cache aligned address even for 1 byte memory.

After digging into the SLAB code I found the calculate_alignment()
function [1] which is used to calculate the required alignment of
objects provided by a SLAB cache. For kmalloc caches SLAB_HWCACHE_ALIGN
is set, but if you look at the code, if the object size is smaller
than half a cache line the alignment constraint is relaxed, meaning that
elements smaller than cache_line/2 are not guaranteed to be aligned on
a cache line.

I didn't test, but that should be pretty easy to verify (kmalloc a
bunch of 1byte bufs and check their alignment).

[1]http://lxr.free-electrons.com/source/mm/slab_common.c#L301

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

* Re: [RESEND PATCH v2 50/53] mtd: nand: allocate aligned buffers if NAND_OWN_BUFFERS is unset
  2017-03-28  7:59       ` Boris Brezillon
@ 2017-03-28  8:07         ` Boris Brezillon
  2017-03-28 10:22           ` Russell King - ARM Linux
  2017-03-28 10:17         ` Russell King - ARM Linux
  2017-03-29  3:22         ` yamada.masahiro
  2 siblings, 1 reply; 41+ messages in thread
From: Boris Brezillon @ 2017-03-28  8:07 UTC (permalink / raw)
  To: yamada.masahiro
  Cc: linux-mtd, dwmw2, marek.vasut, computersforpeace,
	thorsten.christiansson, laurent.monat, dinguyen,
	artem.bityutskiy, grmoore, ejo, chuanxiao.dong, mhiramat,
	jaswinder.singh, robh, Russell King - ARM Linux

On Tue, 28 Mar 2017 09:59:07 +0200
Boris Brezillon <boris.brezillon@free-electrons.com> wrote:

> +Russell to correct me if I'm wrong or give further information.
> 
> On Tue, 28 Mar 2017 01:13:10 +0000
> <yamada.masahiro@socionext.com> wrote:
> 
> > Hi Boris,
> > 
> >   
> > > -----Original Message-----
> > > From: Boris Brezillon [mailto:boris.brezillon@free-electrons.com]
> > > Sent: Monday, March 27, 2017 5:01 PM
> > > To: Yamada, Masahiro/山田 真弘 <yamada.masahiro@socionext.com>
> > > Cc: linux-mtd@lists.infradead.org; David Woodhouse
> > > <dwmw2@infradead.org>; Marek Vasut <marek.vasut@gmail.com>; Brian Norris
> > > <computersforpeace@gmail.com>; thorsten.christiansson@idquantique.com;
> > > laurent.monat@idquantique.com; Dinh Nguyen <dinguyen@kernel.org>; Artem
> > > Bityutskiy <artem.bityutskiy@linux.intel.com>; Graham Moore
> > > <grmoore@opensource.altera.com>; Enrico Jorns <ejo@pengutronix.de>;
> > > Chuanxiao Dong <chuanxiao.dong@intel.com>; Masami Hiramatsu
> > > <mhiramat@kernel.org>; Jassi Brar <jaswinder.singh@linaro.org>; Rob
> > > Herring <robh@kernel.org>
> > > Subject: Re: [RESEND PATCH v2 50/53] mtd: nand: allocate aligned buffers
> > > if NAND_OWN_BUFFERS is unset
> > > 
> > > Hi Masahiro,
> > > 
> > > On Thu, 23 Mar 2017 09:17:59 +0900
> > > Masahiro Yamada <yamada.masahiro@socionext.com> wrote:
> > >     
> > > > Commit 66507c7bc889 ("mtd: nand: Add support to use nand_base poi
> > > > databuf as bounce buffer") fixed the issue that drivers can be
> > > > passed with a kmap()'d buffer.  This addressed the use_bufpoi = 0
> > > > case.
> > > >
> > > > When use_bufpoi = 1, chip->buffers->databuf is used.  The databuf
> > > > allocated by nand_scan_tail() is not suitable for DMA due to the
> > > > offset, sizeof(*chip->buffers).    
> > > 
> > > As said earlier, I'm fine with the patch content, but I'm not sure
> > > about your explanation. Alignment requirements are DMA controller
> > > dependent so you're not exactly fixing a bug for all NAND controller
> > > drivers, just those that require >4 bytes alignment.    
> > 
> > 
> > We have two contexts when we talk about alignment for DMA:
> > 
> > [A] Requirement by CPU architecture  (cache alignment)
> > [B] Requirement by the controller's DMA engine
> > 
> > 
> > As I will state below, having sizeof(*chip->buffers) in the same cache
> > line is no good.  This is the same reason as devm_* is not recommended for DMA.
> > (https://lkml.org/lkml/2017/3/8/693)  
> 
> Having non-cache line aligned buffers is definitely more dangerous,
> but, AFAIU, it's not impossible.
> 
> Let's consider this case:
> 
> 
> |        cache line        |        cache line        | ... |
> -------------------------------------------------------------
> | nand_buffers size |                    data               |
> 
> 
> If you call dma_map_single(dev, data, size, DMA_TO_DEVICE), the first
> cache line will be flushed (content written back to memory), and
> assuming you don't touch nand_buffers content between dma_map_single()
> and dma_unmap_single() you shouldn't have any problem (the cache line
> cannot magically turn dirty and thus cannot be flushed in the
> background).
> 
> For the DMA_FROM_DEVICE direction, the cache line is invalidated when
> dma_unmap_single() is called, which means your nand_buffers content
> might be updated with what is present in SDRAM, but it shouldn't have
> changed since nand_buffers is only touched at initialization time (when
> the buffer is created).
> 
> So, for our use case where nand_buffers is never modified between
> dma_map_single() and dma_unmap_single(), it should be safe to have
> non-cache line aligned buffers.
> 
> Russell, please let me know if my reasoning is incorrect.
> Note that I'm not arguing against the new approach where we allocate
> each buffer independently using kmalloc (having cache line aligned
> buffers is definitely safer and does not imply any significant
> overhead), I just want to make sure I understand things correctly.
> 
> > 
> > 
> > The current situation violates [A].  
> 
> Do you have a real failure that is proven to be caused by mis cache
> line alignment, or are you just speculating?
> 
> > 
> > Usually [B] is less than [A].  
> 
> Yep, it's likely the case.
> 
> > So, if we meet [A] (by using kmalloc), [B] will be met as well.  
> 
> Sure.
> 
> > 
> > 
> >   
> > > Regarding the cache line alignment issue, in this case it shouldn't be
> > > a problem, because the driver and the core shouldn't touch the
> > > chip->buffers object during a DMA transfer, so dma_map/unmap_single()
> > > should work fine (they may flush/invalidate one cache line entry that
> > > contains non-payload data, but that's not a problem as long as nothing
> > > is modified during the DMA transfer).    
> > 
> > 
> > This is related to 52/53:
> > http://patchwork.ozlabs.org/patch/742409/
> > 
> > Can you also read this?
> > https://lkml.org/lkml/2017/3/8/693
> > 
> > Your comment is very similar to what was discussed in the thread.  
> 
> I read it.
> 
> > 
> > 
> >   
> > > >
> > > > So, drivers using DMA are very likely to end up with setting the
> > > > NAND_OWN_BUFFERS flag and allocate buffers by themselves.  Drivers
> > > > tend to use devm_k*alloc to simplify the probe failure path, but
> > > > devm_k*alloc() does not return a cache line aligned buffer.    
> > > 
> > > Oh, I didn't know that. I had a closer look at the code, and indeed,
> > > devm_kmalloc() does not guarantee any alignment.
> > >     
> > > >
> > > > If used, it could violate the DMA-API requirement stated in
> > > > Documentation/DMA-API.txt:
> > > >   Warnings:  Memory coherency operates at a granularity called the
> > > >   cache line width.  In order for memory mapped by this API to
> > > >   operate correctly, the mapped region must begin exactly on a cache
> > > >   line boundary and end exactly on one (to prevent two separately
> > > >   mapped regions from sharing a single cache line).
> > > >
> > > > To sum up, NAND_OWN_BUFFERS is not very convenient for drivers.
> > > > nand_scan_tail() can give a separate buffer for each of ecccalc,
> > > > ecccode, databuf.  It is guaranteed kmalloc'ed memory is aligned
> > > > with ARCH_DMA_MINALIGN.    
> > > 
> > > Maybe I'm wrong, but AFAIU, kmalloc&co do not guarantee cache line
> > > alignment for small buffers (< cache line), so even kmalloc can return
> > > a buffer that is not cache line aligned.
> > > This being said, we should be good because most NAND controllers are
> > > only manipulating the page buffer (->databuf) which is large enough to
> > > be cache line aligned.    
> > 
> > 
> > In my understanding kmalloc() returns cache aligned address even for 1 byte memory.  
> 
> After digging into the SLAB code I found the calculate_alignment()
> function [1] which is used to calculate the required alignment of
> objects provided by a SLAB cache. For kmalloc caches SLAB_HWCACHE_ALIGN
> is set, but if you look at the code, if the object size is smaller
> than half a cache line the alignment constraint is relaxed, meaning that
> elements smaller than cache_line/2 are not guaranteed to be aligned on
> a cache line.

Hm, it seems that alignment for kmalloc SLAB caches is set to
ARCH_KMALLOC_MINALIGN which is set to ARCH_DMA_MINALIGN by default and
ARCH_DMA_MINALIGN is usually equal to the L1 cache line size, so I was
wrong here.

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

* Re: [RESEND PATCH v2 50/53] mtd: nand: allocate aligned buffers if NAND_OWN_BUFFERS is unset
  2017-03-28  7:59       ` Boris Brezillon
  2017-03-28  8:07         ` Boris Brezillon
@ 2017-03-28 10:17         ` Russell King - ARM Linux
  2017-03-28 12:13           ` Boris Brezillon
  2017-03-29  3:22         ` yamada.masahiro
  2 siblings, 1 reply; 41+ messages in thread
From: Russell King - ARM Linux @ 2017-03-28 10:17 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: yamada.masahiro, linux-mtd, dwmw2, marek.vasut,
	computersforpeace, thorsten.christiansson, laurent.monat,
	dinguyen, artem.bityutskiy, grmoore, ejo, chuanxiao.dong,
	mhiramat, jaswinder.singh, robh

On Tue, Mar 28, 2017 at 09:59:07AM +0200, Boris Brezillon wrote:
> Having non-cache line aligned buffers is definitely more dangerous,
> but, AFAIU, it's not impossible.
> 
> Let's consider this case:
> 
> 
> |        cache line        |        cache line        | ... |
> -------------------------------------------------------------
> | nand_buffers size |                    data               |
> 
> 
> If you call dma_map_single(dev, data, size, DMA_TO_DEVICE), the first
> cache line will be flushed (content written back to memory), and
> assuming you don't touch nand_buffers content between dma_map_single()
> and dma_unmap_single() you shouldn't have any problem (the cache line
> cannot magically turn dirty and thus cannot be flushed in the
> background).

In the DMA_TO_DEVICE case, you're not going to be modifying the data
to be DMA'd.  The DMA certainly is not going to modify the data it's
supposed to be reading.

So, reality is that reading and writing the "data" part including the
overlapping cache line should cause no problem to the DMA activity,
even if that cache line gets written back - the part that overlaps
the DMA data should _not_ modify that data.

More of an issue is the DMA_FROM_DEVICE case...

> For the DMA_FROM_DEVICE direction, the cache line is invalidated when
> dma_unmap_single() is called, which means your nand_buffers content
> might be updated with what is present in SDRAM, but it shouldn't have
> changed since nand_buffers is only touched at initialization time (when
> the buffer is created).

This is exactly where it matters.  When mapping for DMA from the device,
we obviously have to ensure that we aren't going to have any writebacks
from the cache into the DMA area.  Since we don't know whether the
overlapping cache lines contain important data, we write those back, but
invalidate the rest of the buffer when mapping it.

Reading from those cache lines while DMA is in progress is pretty benign,
just like the DMA_TO_DEVICE case.  However, writing to those cache lines
while DMA is in progress is disasterous, because we end up with a choice:

1. if we invalidate the overlapping cache lines, we lose updates that
   the CPU has made.

2. if we write-back the overlapping cache lines, we lose updates that
   the DMA has made.

So either way, there is a data loss risk - there's no getting away from
that.  I've chosen to implement (2) in the ARM code, but either is
equally valid.  (I note in your description above that you think (1)
applies...)

The only solution to that is to avoid all writes to these cache lines
while DMA from the device is in progress.

> So, for our use case where nand_buffers is never modified between
> dma_map_single() and dma_unmap_single(), it should be safe to have
> non-cache line aligned buffers.

Correct, with the exception of what happens at unmap.

> Russell, please let me know if my reasoning is incorrect.
> Note that I'm not arguing against the new approach where we allocate
> each buffer independently using kmalloc (having cache line aligned
> buffers is definitely safer and does not imply any significant
> overhead), I just want to make sure I understand things correctly.

Cache line aligned buffers is definitely preferable, but at the arch
level it's not something that can be relied upon.  Historically, if
I'd have chosen (1) then various drivers would have broken (like SCSI
drivers DMAing sense data directly into the scsi_cmnd struct, something
that has now been resolved.)

-- 
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

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

* Re: [RESEND PATCH v2 50/53] mtd: nand: allocate aligned buffers if NAND_OWN_BUFFERS is unset
  2017-03-28  8:07         ` Boris Brezillon
@ 2017-03-28 10:22           ` Russell King - ARM Linux
  0 siblings, 0 replies; 41+ messages in thread
From: Russell King - ARM Linux @ 2017-03-28 10:22 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: yamada.masahiro, linux-mtd, dwmw2, marek.vasut,
	computersforpeace, thorsten.christiansson, laurent.monat,
	dinguyen, artem.bityutskiy, grmoore, ejo, chuanxiao.dong,
	mhiramat, jaswinder.singh, robh

On Tue, Mar 28, 2017 at 10:07:21AM +0200, Boris Brezillon wrote:
> Hm, it seems that alignment for kmalloc SLAB caches is set to
> ARCH_KMALLOC_MINALIGN which is set to ARCH_DMA_MINALIGN by default and
> ARCH_DMA_MINALIGN is usually equal to the L1 cache line size, so I was
> wrong here.

Yes, we set kmalloc() up to always return L1 cache line aligned data,
where "L1 cache line aligned" is the _maximum_ cache line alignment
that the kernel has been built for, not the cache line alignment of
the CPU we're running on.

-- 
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

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

* Re: [RESEND PATCH v2 50/53] mtd: nand: allocate aligned buffers if NAND_OWN_BUFFERS is unset
  2017-03-28 10:17         ` Russell King - ARM Linux
@ 2017-03-28 12:13           ` Boris Brezillon
  0 siblings, 0 replies; 41+ messages in thread
From: Boris Brezillon @ 2017-03-28 12:13 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: yamada.masahiro, linux-mtd, dwmw2, marek.vasut,
	computersforpeace, thorsten.christiansson, laurent.monat,
	dinguyen, artem.bityutskiy, grmoore, ejo, chuanxiao.dong,
	mhiramat, jaswinder.singh, robh

On Tue, 28 Mar 2017 11:17:20 +0100
Russell King - ARM Linux <linux@armlinux.org.uk> wrote:

> On Tue, Mar 28, 2017 at 09:59:07AM +0200, Boris Brezillon wrote:
> > Having non-cache line aligned buffers is definitely more dangerous,
> > but, AFAIU, it's not impossible.
> > 
> > Let's consider this case:
> > 
> > 
> > |        cache line        |        cache line        | ... |
> > -------------------------------------------------------------
> > | nand_buffers size |                    data               |
> > 
> > 
> > If you call dma_map_single(dev, data, size, DMA_TO_DEVICE), the first
> > cache line will be flushed (content written back to memory), and
> > assuming you don't touch nand_buffers content between dma_map_single()
> > and dma_unmap_single() you shouldn't have any problem (the cache line
> > cannot magically turn dirty and thus cannot be flushed in the
> > background).  
> 
> In the DMA_TO_DEVICE case, you're not going to be modifying the data
> to be DMA'd.  The DMA certainly is not going to modify the data it's
> supposed to be reading.
> 
> So, reality is that reading and writing the "data" part including the
> overlapping cache line should cause no problem to the DMA activity,
> even if that cache line gets written back - the part that overlaps
> the DMA data should _not_ modify that data.
> 
> More of an issue is the DMA_FROM_DEVICE case...
> 
> > For the DMA_FROM_DEVICE direction, the cache line is invalidated when
> > dma_unmap_single() is called, which means your nand_buffers content
> > might be updated with what is present in SDRAM, but it shouldn't have
> > changed since nand_buffers is only touched at initialization time (when
> > the buffer is created).  
> 
> This is exactly where it matters.  When mapping for DMA from the device,
> we obviously have to ensure that we aren't going to have any writebacks
> from the cache into the DMA area.  Since we don't know whether the
> overlapping cache lines contain important data, we write those back, but
> invalidate the rest of the buffer when mapping it.
> 
> Reading from those cache lines while DMA is in progress is pretty benign,
> just like the DMA_TO_DEVICE case.  However, writing to those cache lines
> while DMA is in progress is disasterous, because we end up with a choice:
> 
> 1. if we invalidate the overlapping cache lines, we lose updates that
>    the CPU has made.
> 
> 2. if we write-back the overlapping cache lines, we lose updates that
>    the DMA has made.
> 
> So either way, there is a data loss risk - there's no getting away from
> that.  I've chosen to implement (2) in the ARM code, but either is
> equally valid.  (I note in your description above that you think (1)
> applies...)

Okay, got it.

> 
> The only solution to that is to avoid all writes to these cache lines
> while DMA from the device is in progress.

And we are in that case: the nand_buffers object will never be modified
between dma_map() and dma_unmap().

> 
> > So, for our use case where nand_buffers is never modified between
> > dma_map_single() and dma_unmap_single(), it should be safe to have
> > non-cache line aligned buffers.  
> 
> Correct, with the exception of what happens at unmap.

Now I'm lost again :-). Didn't you say it was safe to have overlapping
cache lines if nothing writes to these cache lines during the whole time
the buffer is DMA-mapped?
IIUC, the only case where unmap() will write-back cache lines is when
these cache entries are dirty (i.e. when they've been modified through
CPU accesses between map and unmap). Am I missing something?

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

* RE: [RESEND PATCH v2 50/53] mtd: nand: allocate aligned buffers if NAND_OWN_BUFFERS is unset
  2017-03-28  7:59       ` Boris Brezillon
  2017-03-28  8:07         ` Boris Brezillon
  2017-03-28 10:17         ` Russell King - ARM Linux
@ 2017-03-29  3:22         ` yamada.masahiro
  2017-03-29  7:03           ` Boris Brezillon
  2 siblings, 1 reply; 41+ messages in thread
From: yamada.masahiro @ 2017-03-29  3:22 UTC (permalink / raw)
  To: boris.brezillon
  Cc: linux-mtd, dwmw2, marek.vasut, computersforpeace,
	thorsten.christiansson, laurent.monat, dinguyen,
	artem.bityutskiy, grmoore, ejo, chuanxiao.dong, mhiramat,
	jaswinder.singh, robh, linux, masahiroy

Hi Boris

> -----Original Message-----
> > The current situation violates [A].
> 
> Do you have a real failure that is proven to be caused by mis cache
> line alignment, or are you just speculating?

No real problem.
Rather, I am following the line 226 of DMA-API.txt
"Warnings: Memory coherency ...".


In my case, the cache line is 64 byte (for ARM),
But this does not really matter for the reason
nand_buffers is only touched at initialization as you mentioned.


[B] is a real problem for me
because Denali DMA engine requires >4byte alignment.




So, I can replace the git-log in the next version:

"
Some NAND controllers are using DMA engine requiring a specific buffer
alignment. The core provides no guarantee on the nand_buffers pointers,
which forces some drivers to allocate their own buffers and pass the
NAND_OWN_BUFFERS flag.

Rework the nand_buffers allocation logic to allocate each buffer
independently. This should make most NAND controllers/DMA engine
happy, and allow us to get rid of these custom buf allocation in NAND
controller drivers.
"

Masahiro













> >
> > Usually [B] is less than [A].
> 
> Yep, it's likely the case.
> 
> > So, if we meet [A] (by using kmalloc), [B] will be met as well.
> 
> Sure.
> 
> >
> >
> >
> > > Regarding the cache line alignment issue, in this case it shouldn't
> be
> > > a problem, because the driver and the core shouldn't touch the
> > > chip->buffers object during a DMA transfer, so dma_map/unmap_single()
> > > should work fine (they may flush/invalidate one cache line entry that
> > > contains non-payload data, but that's not a problem as long as nothing
> > > is modified during the DMA transfer).
> >
> >
> > This is related to 52/53:
> > http://patchwork.ozlabs.org/patch/742409/
> >
> > Can you also read this?
> > https://lkml.org/lkml/2017/3/8/693
> >
> > Your comment is very similar to what was discussed in the thread.
> 
> I read it.
> 
> >
> >
> >
> > > >
> > > > So, drivers using DMA are very likely to end up with setting the
> > > > NAND_OWN_BUFFERS flag and allocate buffers by themselves.  Drivers
> > > > tend to use devm_k*alloc to simplify the probe failure path, but
> > > > devm_k*alloc() does not return a cache line aligned buffer.
> > >
> > > Oh, I didn't know that. I had a closer look at the code, and indeed,
> > > devm_kmalloc() does not guarantee any alignment.
> > >
> > > >
> > > > If used, it could violate the DMA-API requirement stated in
> > > > Documentation/DMA-API.txt:
> > > >   Warnings:  Memory coherency operates at a granularity called the
> > > >   cache line width.  In order for memory mapped by this API to
> > > >   operate correctly, the mapped region must begin exactly on a cache
> > > >   line boundary and end exactly on one (to prevent two separately
> > > >   mapped regions from sharing a single cache line).
> > > >
> > > > To sum up, NAND_OWN_BUFFERS is not very convenient for drivers.
> > > > nand_scan_tail() can give a separate buffer for each of ecccalc,
> > > > ecccode, databuf.  It is guaranteed kmalloc'ed memory is aligned
> > > > with ARCH_DMA_MINALIGN.
> > >
> > > Maybe I'm wrong, but AFAIU, kmalloc&co do not guarantee cache line
> > > alignment for small buffers (< cache line), so even kmalloc can return
> > > a buffer that is not cache line aligned.
> > > This being said, we should be good because most NAND controllers are
> > > only manipulating the page buffer (->databuf) which is large enough
> to
> > > be cache line aligned.
> >
> >
> > In my understanding kmalloc() returns cache aligned address even for 1
> byte memory.
> 
> After digging into the SLAB code I found the calculate_alignment()
> function [1] which is used to calculate the required alignment of
> objects provided by a SLAB cache. For kmalloc caches SLAB_HWCACHE_ALIGN
> is set, but if you look at the code, if the object size is smaller
> than half a cache line the alignment constraint is relaxed, meaning that
> elements smaller than cache_line/2 are not guaranteed to be aligned on
> a cache line.
> 
> I didn't test, but that should be pretty easy to verify (kmalloc a
> bunch of 1byte bufs and check their alignment).
> 
> [1]http://lxr.free-electrons.com/source/mm/slab_common.c#L301

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

* Re: [RESEND PATCH v2 50/53] mtd: nand: allocate aligned buffers if NAND_OWN_BUFFERS is unset
  2017-03-29  3:22         ` yamada.masahiro
@ 2017-03-29  7:03           ` Boris Brezillon
  0 siblings, 0 replies; 41+ messages in thread
From: Boris Brezillon @ 2017-03-29  7:03 UTC (permalink / raw)
  To: yamada.masahiro
  Cc: linux-mtd, dwmw2, marek.vasut, computersforpeace,
	thorsten.christiansson, laurent.monat, dinguyen,
	artem.bityutskiy, grmoore, ejo, chuanxiao.dong, mhiramat,
	jaswinder.singh, robh, linux, masahiroy

On Wed, 29 Mar 2017 03:22:01 +0000
<yamada.masahiro@socionext.com> wrote:

> Hi Boris
> 
> > -----Original Message-----  
> > > The current situation violates [A].  
> > 
> > Do you have a real failure that is proven to be caused by mis cache
> > line alignment, or are you just speculating?  
> 
> No real problem.
> Rather, I am following the line 226 of DMA-API.txt
> "Warnings: Memory coherency ...".
> 
> 
> In my case, the cache line is 64 byte (for ARM),
> But this does not really matter for the reason
> nand_buffers is only touched at initialization as you mentioned.
> 
> 
> [B] is a real problem for me
> because Denali DMA engine requires >4byte alignment.
> 
> 
> 
> 
> So, I can replace the git-log in the next version:
> 
> "
> Some NAND controllers are using DMA engine requiring a specific buffer
> alignment. The core provides no guarantee on the nand_buffers pointers,
> which forces some drivers to allocate their own buffers and pass the
> NAND_OWN_BUFFERS flag.
> 
> Rework the nand_buffers allocation logic to allocate each buffer
> independently. This should make most NAND controllers/DMA engine
> happy, and allow us to get rid of these custom buf allocation in NAND
> controller drivers.
> "

Yep, sounds better to me.

Thanks,

Boris

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

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

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-03-23  0:17 [RESEND PATCH v2 27/53] mtd: nand: denali: avoid hard-coding ecc.strength and ecc.bytes Masahiro Yamada
2017-03-23  0:17 ` [RESEND PATCH v2 28/53] mtd: nand: denali: support "nand-ecc-strength" DT property Masahiro Yamada
2017-03-23  8:43   ` Boris Brezillon
2017-03-23  0:17 ` [RESEND PATCH v2 29/53] mtd: nand: denali: remove Toshiba and Hynix specific fixup code Masahiro Yamada
2017-03-23  0:17 ` [RESEND PATCH v2 30/53] mtd: nand: denali_dt: add compatible strings for UniPhier SoC variants Masahiro Yamada
2017-03-23  0:17 ` [RESEND PATCH v2 31/53] mtd: nand: denali: set NAND_ECC_CUSTOM_PAGE_ACCESS Masahiro Yamada
2017-03-23  0:17 ` [RESEND PATCH v2 32/53] mtd: nand: denali: do not propagate NAND_STATUS_FAIL to waitfunc() Masahiro Yamada
2017-03-23  0:17 ` [RESEND PATCH v2 33/53] mtd: nand: denali: use BIT() and GENMASK() for register macros Masahiro Yamada
2017-03-23  0:17 ` [RESEND PATCH v2 34/53] mtd: nand: denali: remove unneeded find_valid_banks() Masahiro Yamada
2017-03-23  0:17 ` [RESEND PATCH v2 35/53] mtd: nand: denali: handle timing parameters by setup_data_interface() Masahiro Yamada
2017-03-23  0:17 ` [RESEND PATCH v2 36/53] mtd: nand: denali: remove meaningless pipeline read-ahead operation Masahiro Yamada
2017-03-23  0:17 ` [RESEND PATCH v2 37/53] mtd: nand: denali: rework interrupt handling Masahiro Yamada
2017-03-23  0:17 ` [RESEND PATCH v2 38/53] mtd: nand: denali: fix NAND_CMD_STATUS handling Masahiro Yamada
2017-03-23  0:17 ` [RESEND PATCH v2 39/53] mtd: nand: denali: fix NAND_CMD_PARAM handling Masahiro Yamada
2017-03-23  0:17 ` [RESEND PATCH v2 40/53] mtd: nand: do not check R/B# for CMD_READID in nand_command(_lp) Masahiro Yamada
2017-03-23  0:17 ` [RESEND PATCH v2 41/53] mtd: nand: do not check R/B# for CMD_SET_FEATURES " Masahiro Yamada
2017-03-23  0:17 ` [RESEND PATCH v2 42/53] mtd: nand: denali: switch over to cmd_ctrl instead of cmdfunc Masahiro Yamada
2017-03-23  8:52   ` Boris Brezillon
2017-03-23  0:17 ` [RESEND PATCH v2 43/53] mtd: nand: denali: fix bank reset function Masahiro Yamada
2017-03-23  0:17 ` [RESEND PATCH v2 44/53] mtd: nand: denali: use interrupt instead of polling for bank reset Masahiro Yamada
2017-03-23  0:17 ` [RESEND PATCH v2 45/53] mtd: nand: denali: propagate page to helpers via function argument Masahiro Yamada
2017-03-23  0:17 ` [RESEND PATCH v2 46/53] mtd: nand: denali: merge struct nand_buf into struct denali_nand_info Masahiro Yamada
2017-03-23  0:17 ` [RESEND PATCH v2 47/53] mtd: nand: denali: use flag instead of register macro for direction Masahiro Yamada
2017-03-23  0:17 ` [RESEND PATCH v2 48/53] mtd: nand: denali: fix raw and oob accessors for syndrome page layout Masahiro Yamada
2017-03-23  0:17 ` [RESEND PATCH v2 49/53] mtd: nand: denali: support hardware-assisted erased page detection Masahiro Yamada
2017-03-23  0:17 ` [RESEND PATCH v2 50/53] mtd: nand: allocate aligned buffers if NAND_OWN_BUFFERS is unset Masahiro Yamada
2017-03-27  8:00   ` Boris Brezillon
2017-03-28  1:13     ` yamada.masahiro
2017-03-28  7:59       ` Boris Brezillon
2017-03-28  8:07         ` Boris Brezillon
2017-03-28 10:22           ` Russell King - ARM Linux
2017-03-28 10:17         ` Russell King - ARM Linux
2017-03-28 12:13           ` Boris Brezillon
2017-03-29  3:22         ` yamada.masahiro
2017-03-29  7:03           ` Boris Brezillon
2017-03-23  0:18 ` [RESEND PATCH v2 51/53] mtd: nand: denali: skip driver internal bounce buffer when possible Masahiro Yamada
2017-03-23  0:18 ` [RESEND PATCH v2 52/53] mtd: nand: denali: use non-managed kmalloc() for DMA buffer Masahiro Yamada
2017-03-23 11:33   ` Robin Murphy
2017-03-24  1:41     ` yamada.masahiro
2017-03-24 17:09       ` Robin Murphy
2017-03-23  0:18 ` [RESEND PATCH v2 53/53] mtd: nand: denali: enable bad block table scan Masahiro Yamada

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.