linux-mtd.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v5 0/8] New Arasan NAND controller driver
@ 2020-05-19  7:45 Miquel Raynal
  2020-05-19  7:45 ` [PATCH v5 1/8] lib/bch: Rework a little bit the exported function names Miquel Raynal
                   ` (7 more replies)
  0 siblings, 8 replies; 23+ messages in thread
From: Miquel Raynal @ 2020-05-19  7:45 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus, linux-mtd
  Cc: Michal Simek, Boris Brezillon, Naga Sureshkumar Relli,
	Thomas Petazzoni, Miquel Raynal

Hello,

This is a deep rework of Naga's Arasan NAND controller driver. This
version is the final version and works with software ECC. It relies on
a previous series called "Supporting restricted NAND controllers" that
brings more flexibility to the NAND with the goal to support
restricted controllers like this one.

Cheers,
Miquèl

Changes in v5:
* Added a FIXME and two TODO comments in the code, explaining how/hy
  the NAND core should be extended.
* Created a real function that returns the packet size and number, or
  an error if the exact requested amount cannot be reached.
* Created a new helper to check if an operation is actually supported
  or not. Used the above new helper for checking the validity of an
  ->exec_op() operation on demand.
* Collected Rob's Reviewed-by tag.

Changes in v4:
* Collected Reviewed-by tags.
* Dropped Ivan from the list (did not find his contact).
* Wrapped commit log.
* Fixed typos in the commit logs/comments.
* Took authorship of all patches.
* Added more details on the BCH changes.
* Documented the new bch_control entry.
* Made the swap bit calls conditionals in bch_encode to avoid
  penalizing people that do not use it.
* Patched bch_init() to take an extra argument.
* Dropped calls to nand_release(), use the construction proposed by
  Boris instead.
* Dropped a useless NFC struct field.
* Added a comment on not having an interrupt for RB.
* Checked the number of steps requested is compatible with the
  controller limitations.
* Reworked anfc_exec_op as suggested to treat the check_only argument
  another way.
* Change the comment stating that the controller has only one CS.
* Clarified the interrupts signals vs. interrupt status bits.

Changes in v3:
* Prefix specific clock definitions with XLNX as they do not apply for
  any other SoC and are attached to a single compatible.
* Used field getters/setters as defined in bitfield.h.
* Force casting to u32 before shifting u8 values by 8 16 or 24 bits.
* Comply with the recent core changes and select manually
  nand_monolithic_read/write_page_raw() helpers.
* Add MAINTAINER patch.
* Add a bit extraction helper in the core.
* Rename BCH functions.
* Add a swapping bit mechanism to BCH.
* Support the hardware ECC engine.

Changes in v2:
* Working ->exec_op() implementation relying on core changes.
* Dropped the ECC support for now, will be part of another series if
  this patch is accepted.


Miquel Raynal (8):
  lib/bch: Rework a little bit the exported function names
  lib/bch: Allow easy bit swapping
  mtd: rawnand: Ensure the number of bitflips is consistent
  mtd: rawnand: Add nand_extract_bits()
  MAINTAINERS: Add Arasan NAND controller and bindings
  dt-bindings: mtd: Document ARASAN NAND bindings
  mtd: rawnand: arasan: Add new Arasan NAND controller
  mtd: rawnand: arasan: Support the hardware BCH ECC engine

 .../bindings/mtd/arasan,nand-controller.yaml  |   63 +
 MAINTAINERS                                   |    7 +
 drivers/mtd/devices/docg3.c                   |   10 +-
 drivers/mtd/nand/raw/Kconfig                  |    7 +
 drivers/mtd/nand/raw/Makefile                 |    1 +
 drivers/mtd/nand/raw/arasan-nand-controller.c | 1297 +++++++++++++++++
 drivers/mtd/nand/raw/nand_base.c              |   52 +-
 drivers/mtd/nand/raw/nand_bch.c               |   10 +-
 include/linux/bch.h                           |   11 +-
 include/linux/mtd/rawnand.h                   |    4 +
 lib/bch.c                                     |  152 +-
 11 files changed, 1550 insertions(+), 64 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mtd/arasan,nand-controller.yaml
 create mode 100644 drivers/mtd/nand/raw/arasan-nand-controller.c

-- 
2.20.1


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* [PATCH v5 1/8] lib/bch: Rework a little bit the exported function names
  2020-05-19  7:45 [PATCH v5 0/8] New Arasan NAND controller driver Miquel Raynal
@ 2020-05-19  7:45 ` Miquel Raynal
  2020-05-24 19:10   ` Miquel Raynal
  2020-05-19  7:45 ` [PATCH v5 2/8] lib/bch: Allow easy bit swapping Miquel Raynal
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 23+ messages in thread
From: Miquel Raynal @ 2020-05-19  7:45 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus, linux-mtd
  Cc: Michal Simek, Boris Brezillon, Naga Sureshkumar Relli,
	Thomas Petazzoni, Miquel Raynal

There are four exported functions, all suffixed by _bch, which is
clearly not the norm. Let's rename them by prefixing them with bch_
instead.

This is a mechanical change:
    init_bch -> bch_init
    free_bch -> bch_free
    encode_bch -> bch_encode
    decode_bch -> bch_decode

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
---
 drivers/mtd/devices/docg3.c     | 10 +++---
 drivers/mtd/nand/raw/nand_bch.c | 10 +++---
 include/linux/bch.h             |  8 ++---
 lib/bch.c                       | 64 ++++++++++++++++-----------------
 4 files changed, 46 insertions(+), 46 deletions(-)

diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index eb0f4600efd1..799df8d03357 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -647,7 +647,7 @@ static int doc_ecc_bch_fix_data(struct docg3 *docg3, void *buf, u8 *hwecc)
 
 	for (i = 0; i < DOC_ECC_BCH_SIZE; i++)
 		ecc[i] = bitrev8(hwecc[i]);
-	numerrs = decode_bch(docg3->cascade->bch, NULL,
+	numerrs = bch_decode(docg3->cascade->bch, NULL,
 			     DOC_ECC_BCH_COVERED_BYTES,
 			     NULL, ecc, NULL, errorpos);
 	BUG_ON(numerrs == -EINVAL);
@@ -1984,8 +1984,8 @@ static int __init docg3_probe(struct platform_device *pdev)
 		return ret;
 	cascade->base = base;
 	mutex_init(&cascade->lock);
-	cascade->bch = init_bch(DOC_ECC_BCH_M, DOC_ECC_BCH_T,
-			     DOC_ECC_BCH_PRIMPOLY);
+	cascade->bch = bch_init(DOC_ECC_BCH_M, DOC_ECC_BCH_T,
+				DOC_ECC_BCH_PRIMPOLY);
 	if (!cascade->bch)
 		return ret;
 
@@ -2021,7 +2021,7 @@ static int __init docg3_probe(struct platform_device *pdev)
 	ret = -ENODEV;
 	dev_info(dev, "No supported DiskOnChip found\n");
 err_probe:
-	free_bch(cascade->bch);
+	bch_free(cascade->bch);
 	for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++)
 		if (cascade->floors[floor])
 			doc_release_device(cascade->floors[floor]);
@@ -2045,7 +2045,7 @@ static int docg3_release(struct platform_device *pdev)
 		if (cascade->floors[floor])
 			doc_release_device(cascade->floors[floor]);
 
-	free_bch(docg3->cascade->bch);
+	bch_free(docg3->cascade->bch);
 	return 0;
 }
 
diff --git a/drivers/mtd/nand/raw/nand_bch.c b/drivers/mtd/nand/raw/nand_bch.c
index 17527310c3a1..d95fcc7358e9 100644
--- a/drivers/mtd/nand/raw/nand_bch.c
+++ b/drivers/mtd/nand/raw/nand_bch.c
@@ -41,7 +41,7 @@ int nand_bch_calculate_ecc(struct nand_chip *chip, const unsigned char *buf,
 	unsigned int i;
 
 	memset(code, 0, chip->ecc.bytes);
-	encode_bch(nbc->bch, buf, chip->ecc.size, code);
+	bch_encode(nbc->bch, buf, chip->ecc.size, code);
 
 	/* apply mask so that an erased page is a valid codeword */
 	for (i = 0; i < chip->ecc.bytes; i++)
@@ -67,7 +67,7 @@ int nand_bch_correct_data(struct nand_chip *chip, unsigned char *buf,
 	unsigned int *errloc = nbc->errloc;
 	int i, count;
 
-	count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc,
+	count = bch_decode(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc,
 			   NULL, errloc);
 	if (count > 0) {
 		for (i = 0; i < count; i++) {
@@ -130,7 +130,7 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
 	if (!nbc)
 		goto fail;
 
-	nbc->bch = init_bch(m, t, 0);
+	nbc->bch = bch_init(m, t, 0);
 	if (!nbc->bch)
 		goto fail;
 
@@ -182,7 +182,7 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
 		goto fail;
 
 	memset(erased_page, 0xff, eccsize);
-	encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask);
+	bch_encode(nbc->bch, erased_page, eccsize, nbc->eccmask);
 	kfree(erased_page);
 
 	for (i = 0; i < eccbytes; i++)
@@ -205,7 +205,7 @@ EXPORT_SYMBOL(nand_bch_init);
 void nand_bch_free(struct nand_bch_control *nbc)
 {
 	if (nbc) {
-		free_bch(nbc->bch);
+		bch_free(nbc->bch);
 		kfree(nbc->errloc);
 		kfree(nbc->eccmask);
 		kfree(nbc);
diff --git a/include/linux/bch.h b/include/linux/bch.h
index aa765af85c38..9c35e7cd5890 100644
--- a/include/linux/bch.h
+++ b/include/linux/bch.h
@@ -53,14 +53,14 @@ struct bch_control {
 	struct gf_poly *poly_2t[4];
 };
 
-struct bch_control *init_bch(int m, int t, unsigned int prim_poly);
+struct bch_control *bch_init(int m, int t, unsigned int prim_poly);
 
-void free_bch(struct bch_control *bch);
+void bch_free(struct bch_control *bch);
 
-void encode_bch(struct bch_control *bch, const uint8_t *data,
+void bch_encode(struct bch_control *bch, const uint8_t *data,
 		unsigned int len, uint8_t *ecc);
 
-int decode_bch(struct bch_control *bch, const uint8_t *data, unsigned int len,
+int bch_decode(struct bch_control *bch, const uint8_t *data, unsigned int len,
 	       const uint8_t *recv_ecc, const uint8_t *calc_ecc,
 	       const unsigned int *syn, unsigned int *errloc);
 
diff --git a/lib/bch.c b/lib/bch.c
index 052d3fb753a0..1091841ac716 100644
--- a/lib/bch.c
+++ b/lib/bch.c
@@ -23,15 +23,15 @@
  * This library provides runtime configurable encoding/decoding of binary
  * Bose-Chaudhuri-Hocquenghem (BCH) codes.
  *
- * Call init_bch to get a pointer to a newly allocated bch_control structure for
+ * Call bch_init to get a pointer to a newly allocated bch_control structure for
  * the given m (Galois field order), t (error correction capability) and
  * (optional) primitive polynomial parameters.
  *
- * Call encode_bch to compute and store ecc parity bytes to a given buffer.
- * Call decode_bch to detect and locate errors in received data.
+ * Call bch_encode to compute and store ecc parity bytes to a given buffer.
+ * Call bch_decode to detect and locate errors in received data.
  *
  * On systems supporting hw BCH features, intermediate results may be provided
- * to decode_bch in order to skip certain steps. See decode_bch() documentation
+ * to bch_decode in order to skip certain steps. See bch_decode() documentation
  * for details.
  *
  * Option CONFIG_BCH_CONST_PARAMS can be used to force fixed values of
@@ -115,9 +115,9 @@ struct gf_poly_deg1 {
 };
 
 /*
- * same as encode_bch(), but process input data one byte at a time
+ * same as bch_encode(), but process input data one byte at a time
  */
-static void encode_bch_unaligned(struct bch_control *bch,
+static void bch_encode_unaligned(struct bch_control *bch,
 				 const unsigned char *data, unsigned int len,
 				 uint32_t *ecc)
 {
@@ -174,7 +174,7 @@ static void store_ecc8(struct bch_control *bch, uint8_t *dst,
 }
 
 /**
- * encode_bch - calculate BCH ecc parity of data
+ * bch_encode - calculate BCH ecc parity of data
  * @bch:   BCH control structure
  * @data:  data to encode
  * @len:   data length in bytes
@@ -187,7 +187,7 @@ static void store_ecc8(struct bch_control *bch, uint8_t *dst,
  * The exact number of computed ecc parity bits is given by member @ecc_bits of
  * @bch; it may be less than m*t for large values of t.
  */
-void encode_bch(struct bch_control *bch, const uint8_t *data,
+void bch_encode(struct bch_control *bch, const uint8_t *data,
 		unsigned int len, uint8_t *ecc)
 {
 	const unsigned int l = BCH_ECC_WORDS(bch)-1;
@@ -215,7 +215,7 @@ void encode_bch(struct bch_control *bch, const uint8_t *data,
 	m = ((unsigned long)data) & 3;
 	if (m) {
 		mlen = (len < (4-m)) ? len : 4-m;
-		encode_bch_unaligned(bch, data, mlen, bch->ecc_buf);
+		bch_encode_unaligned(bch, data, mlen, bch->ecc_buf);
 		data += mlen;
 		len  -= mlen;
 	}
@@ -255,13 +255,13 @@ void encode_bch(struct bch_control *bch, const uint8_t *data,
 
 	/* process last unaligned bytes */
 	if (len)
-		encode_bch_unaligned(bch, data, len, bch->ecc_buf);
+		bch_encode_unaligned(bch, data, len, bch->ecc_buf);
 
 	/* store ecc parity bytes into original parity buffer */
 	if (ecc)
 		store_ecc8(bch, ecc, bch->ecc_buf);
 }
-EXPORT_SYMBOL_GPL(encode_bch);
+EXPORT_SYMBOL_GPL(bch_encode);
 
 static inline int modulo(struct bch_control *bch, unsigned int v)
 {
@@ -952,7 +952,7 @@ static int chien_search(struct bch_control *bch, unsigned int len,
 #endif /* USE_CHIEN_SEARCH */
 
 /**
- * decode_bch - decode received codeword and find bit error locations
+ * bch_decode - decode received codeword and find bit error locations
  * @bch:      BCH control structure
  * @data:     received data, ignored if @calc_ecc is provided
  * @len:      data length in bytes, must always be provided
@@ -966,22 +966,22 @@ static int chien_search(struct bch_control *bch, unsigned int len,
  *  invalid parameters were provided
  *
  * Depending on the available hw BCH support and the need to compute @calc_ecc
- * separately (using encode_bch()), this function should be called with one of
+ * separately (using bch_encode()), this function should be called with one of
  * the following parameter configurations -
  *
  * by providing @data and @recv_ecc only:
- *   decode_bch(@bch, @data, @len, @recv_ecc, NULL, NULL, @errloc)
+ *   bch_decode(@bch, @data, @len, @recv_ecc, NULL, NULL, @errloc)
  *
  * by providing @recv_ecc and @calc_ecc:
- *   decode_bch(@bch, NULL, @len, @recv_ecc, @calc_ecc, NULL, @errloc)
+ *   bch_decode(@bch, NULL, @len, @recv_ecc, @calc_ecc, NULL, @errloc)
  *
  * by providing ecc = recv_ecc XOR calc_ecc:
- *   decode_bch(@bch, NULL, @len, NULL, ecc, NULL, @errloc)
+ *   bch_decode(@bch, NULL, @len, NULL, ecc, NULL, @errloc)
  *
  * by providing syndrome results @syn:
- *   decode_bch(@bch, NULL, @len, NULL, NULL, @syn, @errloc)
+ *   bch_decode(@bch, NULL, @len, NULL, NULL, @syn, @errloc)
  *
- * Once decode_bch() has successfully returned with a positive value, error
+ * Once bch_decode() has successfully returned with a positive value, error
  * locations returned in array @errloc should be interpreted as follows -
  *
  * if (errloc[n] >= 8*len), then n-th error is located in ecc (no need for
@@ -993,7 +993,7 @@ static int chien_search(struct bch_control *bch, unsigned int len,
  * Note that this function does not perform any data correction by itself, it
  * merely indicates error locations.
  */
-int decode_bch(struct bch_control *bch, const uint8_t *data, unsigned int len,
+int bch_decode(struct bch_control *bch, const uint8_t *data, unsigned int len,
 	       const uint8_t *recv_ecc, const uint8_t *calc_ecc,
 	       const unsigned int *syn, unsigned int *errloc)
 {
@@ -1012,7 +1012,7 @@ int decode_bch(struct bch_control *bch, const uint8_t *data, unsigned int len,
 			/* compute received data ecc into an internal buffer */
 			if (!data || !recv_ecc)
 				return -EINVAL;
-			encode_bch(bch, data, len, NULL);
+			bch_encode(bch, data, len, NULL);
 		} else {
 			/* load provided calculated ecc */
 			load_ecc8(bch, bch->ecc_buf, calc_ecc);
@@ -1053,7 +1053,7 @@ int decode_bch(struct bch_control *bch, const uint8_t *data, unsigned int len,
 	}
 	return (err >= 0) ? err : -EBADMSG;
 }
-EXPORT_SYMBOL_GPL(decode_bch);
+EXPORT_SYMBOL_GPL(bch_decode);
 
 /*
  * generate Galois field lookup tables
@@ -1236,7 +1236,7 @@ static uint32_t *compute_generator_polynomial(struct bch_control *bch)
 }
 
 /**
- * init_bch - initialize a BCH encoder/decoder
+ * bch_init - initialize a BCH encoder/decoder
  * @m:          Galois field order, should be in the range 5-15
  * @t:          maximum error correction capability, in bits
  * @prim_poly:  user-provided primitive polynomial (or 0 to use default)
@@ -1246,17 +1246,17 @@ static uint32_t *compute_generator_polynomial(struct bch_control *bch)
  *
  * This initialization can take some time, as lookup tables are built for fast
  * encoding/decoding; make sure not to call this function from a time critical
- * path. Usually, init_bch() should be called on module/driver init and
- * free_bch() should be called to release memory on exit.
+ * path. Usually, bch_init() should be called on module/driver init and
+ * bch_free() should be called to release memory on exit.
  *
  * You may provide your own primitive polynomial of degree @m in argument
- * @prim_poly, or let init_bch() use its default polynomial.
+ * @prim_poly, or let bch_init() use its default polynomial.
  *
- * Once init_bch() has successfully returned a pointer to a newly allocated
+ * Once bch_init() has successfully returned a pointer to a newly allocated
  * BCH control structure, ecc length in bytes is given by member @ecc_bytes of
  * the structure.
  */
-struct bch_control *init_bch(int m, int t, unsigned int prim_poly)
+struct bch_control *bch_init(int m, int t, unsigned int prim_poly)
 {
 	int err = 0;
 	unsigned int i, words;
@@ -1347,16 +1347,16 @@ struct bch_control *init_bch(int m, int t, unsigned int prim_poly)
 	return bch;
 
 fail:
-	free_bch(bch);
+	bch_free(bch);
 	return NULL;
 }
-EXPORT_SYMBOL_GPL(init_bch);
+EXPORT_SYMBOL_GPL(bch_init);
 
 /**
- *  free_bch - free the BCH control structure
+ *  bch_free - free the BCH control structure
  *  @bch:    BCH control structure to release
  */
-void free_bch(struct bch_control *bch)
+void bch_free(struct bch_control *bch)
 {
 	unsigned int i;
 
@@ -1377,7 +1377,7 @@ void free_bch(struct bch_control *bch)
 		kfree(bch);
 	}
 }
-EXPORT_SYMBOL_GPL(free_bch);
+EXPORT_SYMBOL_GPL(bch_free);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Ivan Djelic <ivan.djelic@parrot.com>");
-- 
2.20.1


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* [PATCH v5 2/8] lib/bch: Allow easy bit swapping
  2020-05-19  7:45 [PATCH v5 0/8] New Arasan NAND controller driver Miquel Raynal
  2020-05-19  7:45 ` [PATCH v5 1/8] lib/bch: Rework a little bit the exported function names Miquel Raynal
@ 2020-05-19  7:45 ` Miquel Raynal
  2020-05-19  8:41   ` Boris Brezillon
  2020-05-24 19:09   ` Miquel Raynal
  2020-05-19  7:45 ` [PATCH v5 3/8] mtd: rawnand: Ensure the number of bitflips is consistent Miquel Raynal
                   ` (5 subsequent siblings)
  7 siblings, 2 replies; 23+ messages in thread
From: Miquel Raynal @ 2020-05-19  7:45 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus, linux-mtd
  Cc: Michal Simek, Boris Brezillon, Naga Sureshkumar Relli,
	Thomas Petazzoni, Miquel Raynal

It seems that several hardware ECC engine use a swapped representation
of bytes compared to software. This might having to do with how the
ECC engine is wired to the NAND controller or the order the bits are
passed to the hardware BCH logic.

This means that when the software BCH engine is working in conjunction
with data generated with hardware, sometimes we might need to swap the
bits inside bytes, eg:

    0x0A = b0000_1010 -> b0101_0000 = 0x50

Make it possible by adding a boolean to the BCH initialization routine.

Regarding the implementation itself, this is a rather simple approach
that can probably be enhanced in the future by preparing the
->a_{mod,pow}_tab tables with the swapping in mind.

Suggested-by: Boris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 drivers/mtd/devices/docg3.c     |  2 +-
 drivers/mtd/nand/raw/nand_bch.c |  2 +-
 include/linux/bch.h             |  5 +-
 lib/bch.c                       | 90 ++++++++++++++++++++++++++++-----
 4 files changed, 82 insertions(+), 17 deletions(-)

diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
index 799df8d03357..a030792115bc 100644
--- a/drivers/mtd/devices/docg3.c
+++ b/drivers/mtd/devices/docg3.c
@@ -1985,7 +1985,7 @@ static int __init docg3_probe(struct platform_device *pdev)
 	cascade->base = base;
 	mutex_init(&cascade->lock);
 	cascade->bch = bch_init(DOC_ECC_BCH_M, DOC_ECC_BCH_T,
-				DOC_ECC_BCH_PRIMPOLY);
+				DOC_ECC_BCH_PRIMPOLY, false);
 	if (!cascade->bch)
 		return ret;
 
diff --git a/drivers/mtd/nand/raw/nand_bch.c b/drivers/mtd/nand/raw/nand_bch.c
index d95fcc7358e9..d5af8c5fd02f 100644
--- a/drivers/mtd/nand/raw/nand_bch.c
+++ b/drivers/mtd/nand/raw/nand_bch.c
@@ -130,7 +130,7 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
 	if (!nbc)
 		goto fail;
 
-	nbc->bch = bch_init(m, t, 0);
+	nbc->bch = bch_init(m, t, 0, false);
 	if (!nbc->bch)
 		goto fail;
 
diff --git a/include/linux/bch.h b/include/linux/bch.h
index 9c35e7cd5890..85fdce83d4e2 100644
--- a/include/linux/bch.h
+++ b/include/linux/bch.h
@@ -33,6 +33,7 @@
  * @cache:      log-based polynomial representation buffer
  * @elp:        error locator polynomial
  * @poly_2t:    temporary polynomials of degree 2t
+ * @swap_bits:  swap bits within data and syndrome bytes
  */
 struct bch_control {
 	unsigned int    m;
@@ -51,9 +52,11 @@ struct bch_control {
 	int            *cache;
 	struct gf_poly *elp;
 	struct gf_poly *poly_2t[4];
+	bool		swap_bits;
 };
 
-struct bch_control *bch_init(int m, int t, unsigned int prim_poly);
+struct bch_control *bch_init(int m, int t, unsigned int prim_poly,
+			     bool swap_bits);
 
 void bch_free(struct bch_control *bch);
 
diff --git a/lib/bch.c b/lib/bch.c
index 1091841ac716..7c031ee8b93b 100644
--- a/lib/bch.c
+++ b/lib/bch.c
@@ -114,6 +114,49 @@ struct gf_poly_deg1 {
 	unsigned int   c[2];
 };
 
+static u8 swap_bits_table[] = {
+	0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
+	0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+	0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+	0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+	0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
+	0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+	0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
+	0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+	0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+	0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+	0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
+	0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+	0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
+	0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+	0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+	0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+	0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
+	0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+	0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
+	0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+	0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+	0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+	0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
+	0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+	0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
+	0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+	0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+	0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+	0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
+	0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+	0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
+	0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
+};
+
+static u8 swap_bits(struct bch_control *bch, u8 in)
+{
+	if (!bch->swap_bits)
+		return in;
+
+	return swap_bits_table[in];
+}
+
 /*
  * same as bch_encode(), but process input data one byte at a time
  */
@@ -126,7 +169,9 @@ static void bch_encode_unaligned(struct bch_control *bch,
 	const int l = BCH_ECC_WORDS(bch)-1;
 
 	while (len--) {
-		p = bch->mod8_tab + (l+1)*(((ecc[0] >> 24)^(*data++)) & 0xff);
+		u8 tmp = swap_bits(bch, *data++);
+
+		p = bch->mod8_tab + (l+1)*(((ecc[0] >> 24)^(tmp)) & 0xff);
 
 		for (i = 0; i < l; i++)
 			ecc[i] = ((ecc[i] << 8)|(ecc[i+1] >> 24))^(*p++);
@@ -145,10 +190,16 @@ static void load_ecc8(struct bch_control *bch, uint32_t *dst,
 	unsigned int i, nwords = BCH_ECC_WORDS(bch)-1;
 
 	for (i = 0; i < nwords; i++, src += 4)
-		dst[i] = (src[0] << 24)|(src[1] << 16)|(src[2] << 8)|src[3];
+		dst[i] = ((u32)swap_bits(bch, src[0]) << 24) |
+			((u32)swap_bits(bch, src[1]) << 16) |
+			((u32)swap_bits(bch, src[2]) << 8) |
+			swap_bits(bch, src[3]);
 
 	memcpy(pad, src, BCH_ECC_BYTES(bch)-4*nwords);
-	dst[nwords] = (pad[0] << 24)|(pad[1] << 16)|(pad[2] << 8)|pad[3];
+	dst[nwords] = ((u32)swap_bits(bch, pad[0]) << 24) |
+		((u32)swap_bits(bch, pad[1]) << 16) |
+		((u32)swap_bits(bch, pad[2]) << 8) |
+		swap_bits(bch, pad[3]);
 }
 
 /*
@@ -161,15 +212,15 @@ static void store_ecc8(struct bch_control *bch, uint8_t *dst,
 	unsigned int i, nwords = BCH_ECC_WORDS(bch)-1;
 
 	for (i = 0; i < nwords; i++) {
-		*dst++ = (src[i] >> 24);
-		*dst++ = (src[i] >> 16) & 0xff;
-		*dst++ = (src[i] >>  8) & 0xff;
-		*dst++ = (src[i] >>  0) & 0xff;
+		*dst++ = swap_bits(bch, src[i] >> 24);
+		*dst++ = swap_bits(bch, src[i] >> 16);
+		*dst++ = swap_bits(bch, src[i] >> 8);
+		*dst++ = swap_bits(bch, src[i]);
 	}
-	pad[0] = (src[nwords] >> 24);
-	pad[1] = (src[nwords] >> 16) & 0xff;
-	pad[2] = (src[nwords] >>  8) & 0xff;
-	pad[3] = (src[nwords] >>  0) & 0xff;
+	pad[0] = swap_bits(bch, src[nwords] >> 24);
+	pad[1] = swap_bits(bch, src[nwords] >> 16);
+	pad[2] = swap_bits(bch, src[nwords] >> 8);
+	pad[3] = swap_bits(bch, src[nwords]);
 	memcpy(dst, pad, BCH_ECC_BYTES(bch)-4*nwords);
 }
 
@@ -240,7 +291,13 @@ void bch_encode(struct bch_control *bch, const uint8_t *data,
 	 */
 	while (mlen--) {
 		/* input data is read in big-endian format */
-		w = r[0]^cpu_to_be32(*pdata++);
+		w = cpu_to_be32(*pdata++);
+		if (bch->swap_bits)
+			w = (u32)swap_bits(bch, w) |
+			    ((u32)swap_bits(bch, w >> 8) << 8) |
+			    ((u32)swap_bits(bch, w >> 16) << 16) |
+			    ((u32)swap_bits(bch, w >> 24) << 24);
+		w ^= r[0];
 		p0 = tab0 + (l+1)*((w >>  0) & 0xff);
 		p1 = tab1 + (l+1)*((w >>  8) & 0xff);
 		p2 = tab2 + (l+1)*((w >> 16) & 0xff);
@@ -1048,7 +1105,9 @@ int bch_decode(struct bch_control *bch, const uint8_t *data, unsigned int len,
 				break;
 			}
 			errloc[i] = nbits-1-errloc[i];
-			errloc[i] = (errloc[i] & ~7)|(7-(errloc[i] & 7));
+			if (!bch->swap_bits)
+				errloc[i] = (errloc[i] & ~7) |
+					    (7-(errloc[i] & 7));
 		}
 	}
 	return (err >= 0) ? err : -EBADMSG;
@@ -1240,6 +1299,7 @@ static uint32_t *compute_generator_polynomial(struct bch_control *bch)
  * @m:          Galois field order, should be in the range 5-15
  * @t:          maximum error correction capability, in bits
  * @prim_poly:  user-provided primitive polynomial (or 0 to use default)
+ * @swap_bits:  swap bits within data and syndrome bytes
  *
  * Returns:
  *  a newly allocated BCH control structure if successful, NULL otherwise
@@ -1256,7 +1316,8 @@ static uint32_t *compute_generator_polynomial(struct bch_control *bch)
  * BCH control structure, ecc length in bytes is given by member @ecc_bytes of
  * the structure.
  */
-struct bch_control *bch_init(int m, int t, unsigned int prim_poly)
+struct bch_control *bch_init(int m, int t, unsigned int prim_poly,
+			     bool swap_bits)
 {
 	int err = 0;
 	unsigned int i, words;
@@ -1321,6 +1382,7 @@ struct bch_control *bch_init(int m, int t, unsigned int prim_poly)
 	bch->syn       = bch_alloc(2*t*sizeof(*bch->syn), &err);
 	bch->cache     = bch_alloc(2*t*sizeof(*bch->cache), &err);
 	bch->elp       = bch_alloc((t+1)*sizeof(struct gf_poly_deg1), &err);
+	bch->swap_bits = swap_bits;
 
 	for (i = 0; i < ARRAY_SIZE(bch->poly_2t); i++)
 		bch->poly_2t[i] = bch_alloc(GF_POLY_SZ(2*t), &err);
-- 
2.20.1


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* [PATCH v5 3/8] mtd: rawnand: Ensure the number of bitflips is consistent
  2020-05-19  7:45 [PATCH v5 0/8] New Arasan NAND controller driver Miquel Raynal
  2020-05-19  7:45 ` [PATCH v5 1/8] lib/bch: Rework a little bit the exported function names Miquel Raynal
  2020-05-19  7:45 ` [PATCH v5 2/8] lib/bch: Allow easy bit swapping Miquel Raynal
@ 2020-05-19  7:45 ` Miquel Raynal
  2020-05-19  8:42   ` Boris Brezillon
  2020-05-24 19:09   ` Miquel Raynal
  2020-05-19  7:45 ` [PATCH v5 4/8] mtd: rawnand: Add nand_extract_bits() Miquel Raynal
                   ` (4 subsequent siblings)
  7 siblings, 2 replies; 23+ messages in thread
From: Miquel Raynal @ 2020-05-19  7:45 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus, linux-mtd
  Cc: Michal Simek, Boris Brezillon, Naga Sureshkumar Relli,
	Thomas Petazzoni, Miquel Raynal

The main NAND read page function can loop over "page reads" many times
in if the reading reports uncorrectable error(s) and if the chip
supports the read_retry feature.

In this case, the number of bitflips is summarized between
attempts. Fix this by re-initializing the entire mtd_ecc_stats object
each time we retry.

Suggested-by: Boris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 drivers/mtd/nand/raw/nand_base.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 2bef01e21533..65e9b2fa2fc5 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -3288,7 +3288,7 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
 	oob_required = oob ? 1 : 0;
 
 	while (1) {
-		unsigned int ecc_failures = mtd->ecc_stats.failed;
+		struct mtd_ecc_stats ecc_stats = mtd->ecc_stats;
 
 		bytes = min(mtd->writesize - col, readlen);
 		aligned = (bytes == mtd->writesize);
@@ -3339,7 +3339,7 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
 			 */
 			if (use_bounce_buf) {
 				if (!NAND_HAS_SUBPAGE_READ(chip) && !oob &&
-				    !(mtd->ecc_stats.failed - ecc_failures) &&
+				    !(mtd->ecc_stats.failed - ecc_stats.failed) &&
 				    (ops->mode != MTD_OPS_RAW)) {
 					chip->pagecache.page = realpage;
 					chip->pagecache.bitflips = ret;
@@ -3362,7 +3362,7 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
 
 			nand_wait_readrdy(chip);
 
-			if (mtd->ecc_stats.failed - ecc_failures) {
+			if (mtd->ecc_stats.failed - ecc_stats.failed) {
 				if (retry_mode + 1 < chip->read_retries) {
 					retry_mode++;
 					ret = nand_setup_read_retry(chip,
@@ -3370,8 +3370,8 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
 					if (ret < 0)
 						break;
 
-					/* Reset failures; retry */
-					mtd->ecc_stats.failed = ecc_failures;
+					/* Reset ecc_stats; retry */
+					mtd->ecc_stats = ecc_stats;
 					goto read_retry;
 				} else {
 					/* No more retry modes; real failure */
-- 
2.20.1


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* [PATCH v5 4/8] mtd: rawnand: Add nand_extract_bits()
  2020-05-19  7:45 [PATCH v5 0/8] New Arasan NAND controller driver Miquel Raynal
                   ` (2 preceding siblings ...)
  2020-05-19  7:45 ` [PATCH v5 3/8] mtd: rawnand: Ensure the number of bitflips is consistent Miquel Raynal
@ 2020-05-19  7:45 ` Miquel Raynal
  2020-05-19  8:48   ` Boris Brezillon
  2020-05-24 19:09   ` Miquel Raynal
  2020-05-19  7:45 ` [PATCH v5 5/8] MAINTAINERS: Add Arasan NAND controller and bindings Miquel Raynal
                   ` (3 subsequent siblings)
  7 siblings, 2 replies; 23+ messages in thread
From: Miquel Raynal @ 2020-05-19  7:45 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus, linux-mtd
  Cc: Michal Simek, Boris Brezillon, Naga Sureshkumar Relli,
	Thomas Petazzoni, Miquel Raynal

There are cases where ECC bytes are not byte-aligned. Indeed, BCH
implies using a number of ECC bits, which are not always a multiple of
8. We then need a helper like nand_extract_bits() to extract these
syndromes from a buffer.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 drivers/mtd/nand/raw/nand_base.c | 42 ++++++++++++++++++++++++++++++++
 include/linux/mtd/rawnand.h      |  4 +++
 2 files changed, 46 insertions(+)

diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 65e9b2fa2fc5..14387b967e8b 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -274,6 +274,48 @@ static int check_offs_len(struct nand_chip *chip, loff_t ofs, uint64_t len)
 	return ret;
 }
 
+/** nand_extract_bits - Copy unaligned bits from one buffer to another one
+ * @dst: destination buffer
+ * @dst_off: bit offset at which the writing starts
+ * @src: source buffer
+ * @src_off: bit offset at which the reading starts
+ * @nbits: number of bits to copy from @src to @dst
+ *
+ * Copy bits from one memory region to another (overlap authorized).
+ */
+void nand_extract_bits(u8 *dst, unsigned int dst_off, const u8 *src,
+		       unsigned int src_off, unsigned int nbits)
+{
+	unsigned int tmp, n;
+
+	dst += dst_off / 8;
+	dst_off %= 8;
+	src += src_off / 8;
+	src_off %= 8;
+
+	while (nbits) {
+		n = min3(8 - dst_off, 8 - src_off, nbits);
+
+		tmp = (*src >> src_off) & GENMASK(n - 1, 0);
+		*dst &= ~GENMASK(n - 1 + dst_off, dst_off);
+		*dst |= tmp << dst_off;
+
+		dst_off += n;
+		if (dst_off >= 8) {
+			dst++;
+			dst_off -= 8;
+		}
+
+		src_off += n;
+		if (src_off >= 8) {
+			src++;
+			src_off -= 8;
+		}
+
+		nbits -= n;
+	}
+}
+
 /**
  * nand_select_target() - Select a NAND target (A.K.A. die)
  * @chip: NAND chip object
diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h
index 0f45b6984ad1..45dd57e2a223 100644
--- a/include/linux/mtd/rawnand.h
+++ b/include/linux/mtd/rawnand.h
@@ -1412,6 +1412,10 @@ int nand_gpio_waitrdy(struct nand_chip *chip, struct gpio_desc *gpiod,
 void nand_select_target(struct nand_chip *chip, unsigned int cs);
 void nand_deselect_target(struct nand_chip *chip);
 
+/* Bitops */
+void nand_extract_bits(u8 *dst, unsigned int dst_off, const u8 *src,
+		       unsigned int src_off, unsigned int nbits);
+
 /**
  * nand_get_data_buf() - Get the internal page buffer
  * @chip: NAND chip object
-- 
2.20.1


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* [PATCH v5 5/8] MAINTAINERS: Add Arasan NAND controller and bindings
  2020-05-19  7:45 [PATCH v5 0/8] New Arasan NAND controller driver Miquel Raynal
                   ` (3 preceding siblings ...)
  2020-05-19  7:45 ` [PATCH v5 4/8] mtd: rawnand: Add nand_extract_bits() Miquel Raynal
@ 2020-05-19  7:45 ` Miquel Raynal
  2020-05-24 19:09   ` Miquel Raynal
  2020-05-19  7:45 ` [PATCH v5 6/8] dt-bindings: mtd: Document ARASAN NAND bindings Miquel Raynal
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 23+ messages in thread
From: Miquel Raynal @ 2020-05-19  7:45 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus, linux-mtd
  Cc: Michal Simek, Boris Brezillon, Naga Sureshkumar Relli,
	Thomas Petazzoni, Miquel Raynal

Fill a new entry for the Arasan NAND controller.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 26f281d9f32a..8864b9320aaa 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1284,6 +1284,13 @@ S:	Supported
 W:	http://www.aquantia.com
 F:	drivers/net/ethernet/aquantia/atlantic/aq_ptp*
 
+ARASAN NAND CONTROLLER DRIVER
+M:	Naga Sureshkumar Relli <nagasure@xilinx.com>
+L:	linux-mtd@lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/mtd/arasan,nand-controller.yaml
+F:	drivers/mtd/nand/raw/arasan-nand-controller.c
+
 ARC FRAMEBUFFER DRIVER
 M:	Jaya Kumar <jayalk@intworks.biz>
 S:	Maintained
-- 
2.20.1


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* [PATCH v5 6/8] dt-bindings: mtd: Document ARASAN NAND bindings
  2020-05-19  7:45 [PATCH v5 0/8] New Arasan NAND controller driver Miquel Raynal
                   ` (4 preceding siblings ...)
  2020-05-19  7:45 ` [PATCH v5 5/8] MAINTAINERS: Add Arasan NAND controller and bindings Miquel Raynal
@ 2020-05-19  7:45 ` Miquel Raynal
  2020-05-24 19:09   ` Miquel Raynal
  2020-05-19  7:45 ` [PATCH v5 7/8] mtd: rawnand: arasan: Add new Arasan NAND controller Miquel Raynal
  2020-05-19  7:45 ` [PATCH v5 8/8] mtd: rawnand: arasan: Support the hardware BCH ECC engine Miquel Raynal
  7 siblings, 1 reply; 23+ messages in thread
From: Miquel Raynal @ 2020-05-19  7:45 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus, linux-mtd
  Cc: Rob Herring, Michal Simek, Boris Brezillon,
	Naga Sureshkumar Relli, Thomas Petazzoni, Miquel Raynal

Document the Arasan NAND controller bindings.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../bindings/mtd/arasan,nand-controller.yaml  | 63 +++++++++++++++++++
 1 file changed, 63 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mtd/arasan,nand-controller.yaml

diff --git a/Documentation/devicetree/bindings/mtd/arasan,nand-controller.yaml b/Documentation/devicetree/bindings/mtd/arasan,nand-controller.yaml
new file mode 100644
index 000000000000..db8f115a13ec
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/arasan,nand-controller.yaml
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mtd/arasan,nand-controller.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Arasan NAND Flash Controller with ONFI 3.1 support device tree bindings
+
+allOf:
+  - $ref: "nand-controller.yaml"
+
+maintainers:
+  - Naga Sureshkumar Relli <naga.sureshkumar.relli@xilinx.com>
+
+properties:
+  compatible:
+    oneOf:
+      - items:
+        - enum:
+          - xlnx,zynqmp-nand-controller
+        - enum:
+          - arasan,nfc-v3p10
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: Controller clock
+      - description: NAND bus clock
+
+  clock-names:
+    items:
+      - const: controller
+      - const: bus
+
+  interrupts:
+    maxItems: 1
+
+  "#address-cells": true
+  "#size-cells": true
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - interrupts
+
+additionalProperties: true
+
+examples:
+  - |
+    nfc: nand-controller@ff100000 {
+        compatible = "xlnx,zynqmp-nand-controller", "arasan,nfc-v3p10";
+        reg = <0x0 0xff100000 0x0 0x1000>;
+        clock-names = "controller", "bus";
+        clocks = <&clk200>, <&clk100>;
+        interrupt-parent = <&gic>;
+        interrupts = <0 14 4>;
+        #address-cells = <1>;
+        #size-cells = <0>;
+    };
-- 
2.20.1


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* [PATCH v5 7/8] mtd: rawnand: arasan: Add new Arasan NAND controller
  2020-05-19  7:45 [PATCH v5 0/8] New Arasan NAND controller driver Miquel Raynal
                   ` (5 preceding siblings ...)
  2020-05-19  7:45 ` [PATCH v5 6/8] dt-bindings: mtd: Document ARASAN NAND bindings Miquel Raynal
@ 2020-05-19  7:45 ` Miquel Raynal
  2020-05-19  8:50   ` Boris Brezillon
  2020-05-24 19:09   ` Miquel Raynal
  2020-05-19  7:45 ` [PATCH v5 8/8] mtd: rawnand: arasan: Support the hardware BCH ECC engine Miquel Raynal
  7 siblings, 2 replies; 23+ messages in thread
From: Miquel Raynal @ 2020-05-19  7:45 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus, linux-mtd
  Cc: Michal Simek, Boris Brezillon, Naga Sureshkumar Relli,
	Thomas Petazzoni, Miquel Raynal

Add the Arasan NAND controller driver. This brings only NAND
controller support. The ECC engine being a bit subtle, hardware ECC
support will be added in a second time.

This work is based on contributions from Naga Sureshkumar Relli.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 drivers/mtd/nand/raw/Kconfig                  |   7 +
 drivers/mtd/nand/raw/Makefile                 |   1 +
 drivers/mtd/nand/raw/arasan-nand-controller.c | 955 ++++++++++++++++++
 3 files changed, 963 insertions(+)
 create mode 100644 drivers/mtd/nand/raw/arasan-nand-controller.c

diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index a80a46bb5b8b..579474baa75d 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -457,6 +457,13 @@ config MTD_NAND_CADENCE
 	  Enable the driver for NAND flash on platforms using a Cadence NAND
 	  controller.
 
+config MTD_NAND_ARASAN
+	tristate "Support for Arasan NAND flash controller"
+	depends on HAS_IOMEM && HAS_DMA
+	help
+	  Enables the driver for the Arasan NAND flash controller on
+	  Zynq Ultrascale+ MPSoC.
+
 comment "Misc"
 
 config MTD_SM_COMMON
diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
index 2d136b158fb7..6f80a205e940 100644
--- a/drivers/mtd/nand/raw/Makefile
+++ b/drivers/mtd/nand/raw/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_MTD_NAND_TEGRA)		+= tegra_nand.o
 obj-$(CONFIG_MTD_NAND_STM32_FMC2)	+= stm32_fmc2_nand.o
 obj-$(CONFIG_MTD_NAND_MESON)		+= meson_nand.o
 obj-$(CONFIG_MTD_NAND_CADENCE)		+= cadence-nand-controller.o
+obj-$(CONFIG_MTD_NAND_ARASAN)		+= arasan-nand-controller.o
 
 nand-objs := nand_base.o nand_legacy.o nand_bbt.o nand_timings.o nand_ids.o
 nand-objs += nand_onfi.o
diff --git a/drivers/mtd/nand/raw/arasan-nand-controller.c b/drivers/mtd/nand/raw/arasan-nand-controller.c
new file mode 100644
index 000000000000..03d95ee1488b
--- /dev/null
+++ b/drivers/mtd/nand/raw/arasan-nand-controller.c
@@ -0,0 +1,955 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Arasan NAND Flash Controller Driver
+ *
+ * Copyright (C) 2014 - 2020 Xilinx, Inc.
+ * Author:
+ *   Miquel Raynal <miquel.raynal@bootlin.com>
+ * Original work (fully rewritten):
+ *   Punnaiah Choudary Kalluri <punnaia@xilinx.com>
+ *   Naga Sureshkumar Relli <nagasure@xilinx.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define PKT_REG				0x00
+#define   PKT_SIZE(x)			FIELD_PREP(GENMASK(10, 0), (x))
+#define   PKT_STEPS(x)			FIELD_PREP(GENMASK(23, 12), (x))
+
+#define MEM_ADDR1_REG			0x04
+
+#define MEM_ADDR2_REG			0x08
+#define   ADDR2_STRENGTH(x)		FIELD_PREP(GENMASK(27, 25), (x))
+#define   ADDR2_CS(x)			FIELD_PREP(GENMASK(31, 30), (x))
+
+#define CMD_REG				0x0C
+#define   CMD_1(x)			FIELD_PREP(GENMASK(7, 0), (x))
+#define   CMD_2(x)			FIELD_PREP(GENMASK(15, 8), (x))
+#define   CMD_PAGE_SIZE(x)		FIELD_PREP(GENMASK(25, 23), (x))
+#define   CMD_DMA_ENABLE		BIT(27)
+#define   CMD_NADDRS(x)			FIELD_PREP(GENMASK(30, 28), (x))
+#define   CMD_ECC_ENABLE		BIT(31)
+
+#define PROG_REG			0x10
+#define   PROG_PGRD			BIT(0)
+#define   PROG_ERASE			BIT(2)
+#define   PROG_STATUS			BIT(3)
+#define   PROG_PGPROG			BIT(4)
+#define   PROG_RDID			BIT(6)
+#define   PROG_RDPARAM			BIT(7)
+#define   PROG_RST			BIT(8)
+#define   PROG_GET_FEATURE		BIT(9)
+#define   PROG_SET_FEATURE		BIT(10)
+
+#define INTR_STS_EN_REG			0x14
+#define INTR_SIG_EN_REG			0x18
+#define INTR_STS_REG			0x1C
+#define   WRITE_READY			BIT(0)
+#define   READ_READY			BIT(1)
+#define   XFER_COMPLETE			BIT(2)
+#define   DMA_BOUNDARY			BIT(6)
+#define   EVENT_MASK			GENMASK(7, 0)
+
+#define READY_STS_REG			0x20
+
+#define DMA_ADDR0_REG			0x50
+#define DMA_ADDR1_REG			0x24
+
+#define FLASH_STS_REG			0x28
+
+#define DATA_PORT_REG			0x30
+
+#define ECC_CONF_REG			0x34
+#define   ECC_CONF_COL(x)		FIELD_PREP(GENMASK(15, 0), (x))
+#define   ECC_CONF_LEN(x)		FIELD_PREP(GENMASK(26, 16), (x))
+#define   ECC_CONF_BCH_EN		BIT(27)
+
+#define ECC_ERR_CNT_REG			0x38
+#define   GET_PKT_ERR_CNT(x)		FIELD_GET(GENMASK(7, 0), (x))
+#define   GET_PAGE_ERR_CNT(x)		FIELD_GET(GENMASK(16, 8), (x))
+
+#define ECC_SP_REG			0x3C
+#define   ECC_SP_CMD1(x)		FIELD_PREP(GENMASK(7, 0), (x))
+#define   ECC_SP_CMD2(x)		FIELD_PREP(GENMASK(15, 8), (x))
+#define   ECC_SP_ADDRS(x)		FIELD_PREP(GENMASK(30, 28), (x))
+
+#define ECC_1ERR_CNT_REG		0x40
+#define ECC_2ERR_CNT_REG		0x44
+
+#define DATA_INTERFACE_REG		0x6C
+#define   DIFACE_SDR_MODE(x)		FIELD_PREP(GENMASK(2, 0), (x))
+#define   DIFACE_DDR_MODE(x)		FIELD_PREP(GENMASK(5, 3), (X))
+#define   DIFACE_SDR			0
+#define   DIFACE_NVDDR			BIT(9)
+
+#define ANFC_MAX_CS			2
+#define ANFC_DFLT_TIMEOUT_US		1000000
+#define ANFC_MAX_CHUNK_SIZE		SZ_1M
+#define ANFC_MAX_PARAM_SIZE		SZ_4K
+#define ANFC_MAX_STEPS			SZ_2K
+#define ANFC_MAX_PKT_SIZE		(SZ_2K - 1)
+#define ANFC_MAX_ADDR_CYC		5U
+#define ANFC_RSVD_ECC_BYTES		21
+
+#define ANFC_XLNX_SDR_DFLT_CORE_CLK	100000000
+#define ANFC_XLNX_SDR_HS_CORE_CLK	80000000
+
+/**
+ * struct anfc_op - Defines how to execute an operation
+ * @pkt_reg: Packet register
+ * @addr1_reg: Memory address 1 register
+ * @addr2_reg: Memory address 2 register
+ * @cmd_reg: Command register
+ * @prog_reg: Program register
+ * @steps: Number of "packets" to read/write
+ * @rdy_timeout_ms: Timeout for waits on Ready/Busy pin
+ * @len: Data transfer length
+ * @read: Data transfer direction from the controller point of view
+ */
+struct anfc_op {
+	u32 pkt_reg;
+	u32 addr1_reg;
+	u32 addr2_reg;
+	u32 cmd_reg;
+	u32 prog_reg;
+	int steps;
+	unsigned int rdy_timeout_ms;
+	unsigned int len;
+	bool read;
+	u8 *buf;
+};
+
+/**
+ * struct anand - Defines the NAND chip related information
+ * @node:		Used to store NAND chips into a list
+ * @chip:		NAND chip information structure
+ * @cs:			Chip select line
+ * @rb:			Ready-busy line
+ * @page_sz:		Register value of the page_sz field to use
+ * @clk:		Expected clock frequency to use
+ * @timings:		Data interface timing mode to use
+ * @ecc_conf:		Hardware ECC configuration value
+ * @strength:		Register value of the ECC strength
+ * @raddr_cycles:	Row address cycle information
+ * @caddr_cycles:	Column address cycle information
+ */
+struct anand {
+	struct list_head node;
+	struct nand_chip chip;
+	unsigned int cs;
+	unsigned int rb;
+	unsigned int page_sz;
+	unsigned long clk;
+	u32 timings;
+	u32 ecc_conf;
+	u32 strength;
+	u16 raddr_cycles;
+	u16 caddr_cycles;
+};
+
+/**
+ * struct arasan_nfc - Defines the Arasan NAND flash controller driver instance
+ * @dev:		Pointer to the device structure
+ * @base:		Remapped register area
+ * @controller_clk:		Pointer to the system clock
+ * @bus_clk:		Pointer to the flash clock
+ * @controller:		Base controller structure
+ * @chips:		List of all NAND chips attached to the controller
+ * @assigned_cs:	Bitmask describing already assigned CS lines
+ * @cur_clk:		Current clock rate
+ */
+struct arasan_nfc {
+	struct device *dev;
+	void __iomem *base;
+	struct clk *controller_clk;
+	struct clk *bus_clk;
+	struct nand_controller controller;
+	struct list_head chips;
+	unsigned long assigned_cs;
+	unsigned int cur_clk;
+};
+
+static struct anand *to_anand(struct nand_chip *nand)
+{
+	return container_of(nand, struct anand, chip);
+}
+
+static struct arasan_nfc *to_anfc(struct nand_controller *ctrl)
+{
+	return container_of(ctrl, struct arasan_nfc, controller);
+}
+
+static int anfc_wait_for_event(struct arasan_nfc *nfc, unsigned int event)
+{
+	u32 val;
+	int ret;
+
+	ret = readl_relaxed_poll_timeout(nfc->base + INTR_STS_REG, val,
+					 val & event, 0,
+					 ANFC_DFLT_TIMEOUT_US);
+	if (ret) {
+		dev_err(nfc->dev, "Timeout waiting for event 0x%x\n", event);
+		return -ETIMEDOUT;
+	}
+
+	writel_relaxed(event, nfc->base + INTR_STS_REG);
+
+	return 0;
+}
+
+static int anfc_wait_for_rb(struct arasan_nfc *nfc, struct nand_chip *chip,
+			    unsigned int timeout_ms)
+{
+	struct anand *anand = to_anand(chip);
+	u32 val;
+	int ret;
+
+	/* There is no R/B interrupt, we must poll a register */
+	ret = readl_relaxed_poll_timeout(nfc->base + READY_STS_REG, val,
+					 val & BIT(anand->rb),
+					 1, timeout_ms * 1000);
+	if (ret) {
+		dev_err(nfc->dev, "Timeout waiting for R/B 0x%x\n",
+			readl_relaxed(nfc->base + READY_STS_REG));
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static void anfc_trigger_op(struct arasan_nfc *nfc, struct anfc_op *nfc_op)
+{
+	writel_relaxed(nfc_op->pkt_reg, nfc->base + PKT_REG);
+	writel_relaxed(nfc_op->addr1_reg, nfc->base + MEM_ADDR1_REG);
+	writel_relaxed(nfc_op->addr2_reg, nfc->base + MEM_ADDR2_REG);
+	writel_relaxed(nfc_op->cmd_reg, nfc->base + CMD_REG);
+	writel_relaxed(nfc_op->prog_reg, nfc->base + PROG_REG);
+}
+
+static int anfc_pkt_len_config(unsigned int len, unsigned int *steps,
+			       unsigned int *pktsize)
+{
+	unsigned int nb, sz;
+
+	for (nb = 1; nb < ANFC_MAX_STEPS; nb *= 2) {
+		sz = len / nb;
+		if (sz <= ANFC_MAX_PKT_SIZE)
+			break;
+	}
+
+	if (sz * nb != len)
+		return -ENOTSUPP;
+
+	if (steps)
+		*steps = nb;
+
+	if (pktsize)
+		*pktsize = sz;
+
+	return 0;
+}
+
+/* NAND framework ->exec_op() hooks and related helpers */
+static int anfc_parse_instructions(struct nand_chip *chip,
+				   const struct nand_subop *subop,
+				   struct anfc_op *nfc_op)
+{
+	struct anand *anand = to_anand(chip);
+	const struct nand_op_instr *instr = NULL;
+	bool first_cmd = true;
+	unsigned int op_id;
+	int ret, i;
+
+	memset(nfc_op, 0, sizeof(*nfc_op));
+	nfc_op->addr2_reg = ADDR2_CS(anand->cs);
+	nfc_op->cmd_reg = CMD_PAGE_SIZE(anand->page_sz);
+
+	for (op_id = 0; op_id < subop->ninstrs; op_id++) {
+		unsigned int offset, naddrs, pktsize;
+		const u8 *addrs;
+		u8 *buf;
+
+		instr = &subop->instrs[op_id];
+
+		switch (instr->type) {
+		case NAND_OP_CMD_INSTR:
+			if (first_cmd)
+				nfc_op->cmd_reg |= CMD_1(instr->ctx.cmd.opcode);
+			else
+				nfc_op->cmd_reg |= CMD_2(instr->ctx.cmd.opcode);
+
+			first_cmd = false;
+			break;
+
+		case NAND_OP_ADDR_INSTR:
+			offset = nand_subop_get_addr_start_off(subop, op_id);
+			naddrs = nand_subop_get_num_addr_cyc(subop, op_id);
+			addrs = &instr->ctx.addr.addrs[offset];
+			nfc_op->cmd_reg |= CMD_NADDRS(naddrs);
+
+			for (i = 0; i < min(ANFC_MAX_ADDR_CYC, naddrs); i++) {
+				if (i < 4)
+					nfc_op->addr1_reg |= (u32)addrs[i] << i * 8;
+				else
+					nfc_op->addr2_reg |= addrs[i];
+			}
+
+			break;
+		case NAND_OP_DATA_IN_INSTR:
+			nfc_op->read = true;
+			fallthrough;
+		case NAND_OP_DATA_OUT_INSTR:
+			offset = nand_subop_get_data_start_off(subop, op_id);
+			buf = instr->ctx.data.buf.in;
+			nfc_op->buf = &buf[offset];
+			nfc_op->len = nand_subop_get_data_len(subop, op_id);
+			ret = anfc_pkt_len_config(nfc_op->len, &nfc_op->steps,
+						  &pktsize);
+			if (ret)
+				return ret;
+
+			/*
+			 * Number of DATA cycles must be aligned on 4, this
+			 * means the controller might read/write more than
+			 * requested. This is harmless most of the time as extra
+			 * DATA are discarded in the write path and read pointer
+			 * adjusted in the read path.
+			 *
+			 * FIXME: The core should mark operations where
+			 * reading/writing more is allowed so the exec_op()
+			 * implementation can take the right decision when the
+			 * alignment constraint is not met: adjust the number of
+			 * DATA cycles when it's allowed, reject the operation
+			 * otherwise.
+			 */
+			nfc_op->pkt_reg |= PKT_SIZE(round_up(pktsize, 4)) |
+					   PKT_STEPS(nfc_op->steps);
+			break;
+		case NAND_OP_WAITRDY_INSTR:
+			nfc_op->rdy_timeout_ms = instr->ctx.waitrdy.timeout_ms;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int anfc_rw_pio_op(struct arasan_nfc *nfc, struct anfc_op *nfc_op)
+{
+	unsigned int dwords = (nfc_op->len / 4) / nfc_op->steps;
+	unsigned int last_len = nfc_op->len % 4;
+	unsigned int offset, dir;
+	u8 *buf = nfc_op->buf;
+	int ret, i;
+
+	for (i = 0; i < nfc_op->steps; i++) {
+		dir = nfc_op->read ? READ_READY : WRITE_READY;
+		ret = anfc_wait_for_event(nfc, dir);
+		if (ret) {
+			dev_err(nfc->dev, "PIO %s ready signal not received\n",
+				nfc_op->read ? "Read" : "Write");
+			return ret;
+		}
+
+		offset = i * (dwords * 4);
+		if (nfc_op->read)
+			ioread32_rep(nfc->base + DATA_PORT_REG, &buf[offset],
+				     dwords);
+		else
+			iowrite32_rep(nfc->base + DATA_PORT_REG, &buf[offset],
+				      dwords);
+	}
+
+	if (last_len) {
+		u32 remainder;
+
+		offset = nfc_op->len - last_len;
+
+		if (nfc_op->read) {
+			remainder = readl_relaxed(nfc->base + DATA_PORT_REG);
+			memcpy(&buf[offset], &remainder, last_len);
+		} else {
+			memcpy(&remainder, &buf[offset], last_len);
+			writel_relaxed(remainder, nfc->base + DATA_PORT_REG);
+		}
+	}
+
+	return anfc_wait_for_event(nfc, XFER_COMPLETE);
+}
+
+static int anfc_misc_data_type_exec(struct nand_chip *chip,
+				    const struct nand_subop *subop,
+				    u32 prog_reg)
+{
+	struct arasan_nfc *nfc = to_anfc(chip->controller);
+	struct anfc_op nfc_op = {};
+	int ret;
+
+	ret = anfc_parse_instructions(chip, subop, &nfc_op);
+	if (ret)
+		return ret;
+
+	nfc_op.prog_reg = prog_reg;
+	anfc_trigger_op(nfc, &nfc_op);
+
+	if (nfc_op.rdy_timeout_ms) {
+		ret = anfc_wait_for_rb(nfc, chip, nfc_op.rdy_timeout_ms);
+		if (ret)
+			return ret;
+	}
+
+	return anfc_rw_pio_op(nfc, &nfc_op);
+}
+
+static int anfc_param_read_type_exec(struct nand_chip *chip,
+				     const struct nand_subop *subop)
+{
+	return anfc_misc_data_type_exec(chip, subop, PROG_RDPARAM);
+}
+
+static int anfc_data_read_type_exec(struct nand_chip *chip,
+				    const struct nand_subop *subop)
+{
+	return anfc_misc_data_type_exec(chip, subop, PROG_PGRD);
+}
+
+static int anfc_param_write_type_exec(struct nand_chip *chip,
+				      const struct nand_subop *subop)
+{
+	return anfc_misc_data_type_exec(chip, subop, PROG_SET_FEATURE);
+}
+
+static int anfc_data_write_type_exec(struct nand_chip *chip,
+				     const struct nand_subop *subop)
+{
+	return anfc_misc_data_type_exec(chip, subop, PROG_PGPROG);
+}
+
+static int anfc_misc_zerolen_type_exec(struct nand_chip *chip,
+				       const struct nand_subop *subop,
+				       u32 prog_reg)
+{
+	struct arasan_nfc *nfc = to_anfc(chip->controller);
+	struct anfc_op nfc_op = {};
+	int ret;
+
+	ret = anfc_parse_instructions(chip, subop, &nfc_op);
+	if (ret)
+		return ret;
+
+	nfc_op.prog_reg = prog_reg;
+	anfc_trigger_op(nfc, &nfc_op);
+
+	ret = anfc_wait_for_event(nfc, XFER_COMPLETE);
+	if (ret)
+		return ret;
+
+	if (nfc_op.rdy_timeout_ms)
+		ret = anfc_wait_for_rb(nfc, chip, nfc_op.rdy_timeout_ms);
+
+	return ret;
+}
+
+static int anfc_status_type_exec(struct nand_chip *chip,
+				 const struct nand_subop *subop)
+{
+	struct arasan_nfc *nfc = to_anfc(chip->controller);
+	u32 tmp;
+	int ret;
+
+	/* See anfc_check_op() for details about this constraint */
+	if (subop->instrs[0].ctx.cmd.opcode != NAND_CMD_STATUS)
+		return -ENOTSUPP;
+
+	ret = anfc_misc_zerolen_type_exec(chip, subop, PROG_STATUS);
+	if (ret)
+		return ret;
+
+	tmp = readl_relaxed(nfc->base + FLASH_STS_REG);
+	memcpy(subop->instrs[1].ctx.data.buf.in, &tmp, 1);
+
+	return 0;
+}
+
+static int anfc_reset_type_exec(struct nand_chip *chip,
+				const struct nand_subop *subop)
+{
+	return anfc_misc_zerolen_type_exec(chip, subop, PROG_RST);
+}
+
+static int anfc_erase_type_exec(struct nand_chip *chip,
+				const struct nand_subop *subop)
+{
+	return anfc_misc_zerolen_type_exec(chip, subop, PROG_ERASE);
+}
+
+static int anfc_wait_type_exec(struct nand_chip *chip,
+			       const struct nand_subop *subop)
+{
+	struct arasan_nfc *nfc = to_anfc(chip->controller);
+	struct anfc_op nfc_op = {};
+	int ret;
+
+	ret = anfc_parse_instructions(chip, subop, &nfc_op);
+	if (ret)
+		return ret;
+
+	return anfc_wait_for_rb(nfc, chip, nfc_op.rdy_timeout_ms);
+}
+
+static const struct nand_op_parser anfc_op_parser = NAND_OP_PARSER(
+	NAND_OP_PARSER_PATTERN(
+		anfc_param_read_type_exec,
+		NAND_OP_PARSER_PAT_CMD_ELEM(false),
+		NAND_OP_PARSER_PAT_ADDR_ELEM(false, ANFC_MAX_ADDR_CYC),
+		NAND_OP_PARSER_PAT_WAITRDY_ELEM(true),
+		NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, ANFC_MAX_CHUNK_SIZE)),
+	NAND_OP_PARSER_PATTERN(
+		anfc_param_write_type_exec,
+		NAND_OP_PARSER_PAT_CMD_ELEM(false),
+		NAND_OP_PARSER_PAT_ADDR_ELEM(false, ANFC_MAX_ADDR_CYC),
+		NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, ANFC_MAX_PARAM_SIZE)),
+	NAND_OP_PARSER_PATTERN(
+		anfc_data_read_type_exec,
+		NAND_OP_PARSER_PAT_CMD_ELEM(false),
+		NAND_OP_PARSER_PAT_ADDR_ELEM(false, ANFC_MAX_ADDR_CYC),
+		NAND_OP_PARSER_PAT_CMD_ELEM(false),
+		NAND_OP_PARSER_PAT_WAITRDY_ELEM(true),
+		NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, ANFC_MAX_CHUNK_SIZE)),
+	NAND_OP_PARSER_PATTERN(
+		anfc_data_write_type_exec,
+		NAND_OP_PARSER_PAT_CMD_ELEM(false),
+		NAND_OP_PARSER_PAT_ADDR_ELEM(false, ANFC_MAX_ADDR_CYC),
+		NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, ANFC_MAX_CHUNK_SIZE),
+		NAND_OP_PARSER_PAT_CMD_ELEM(false)),
+	NAND_OP_PARSER_PATTERN(
+		anfc_reset_type_exec,
+		NAND_OP_PARSER_PAT_CMD_ELEM(false),
+		NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)),
+	NAND_OP_PARSER_PATTERN(
+		anfc_erase_type_exec,
+		NAND_OP_PARSER_PAT_CMD_ELEM(false),
+		NAND_OP_PARSER_PAT_ADDR_ELEM(false, ANFC_MAX_ADDR_CYC),
+		NAND_OP_PARSER_PAT_CMD_ELEM(false),
+		NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)),
+	NAND_OP_PARSER_PATTERN(
+		anfc_status_type_exec,
+		NAND_OP_PARSER_PAT_CMD_ELEM(false),
+		NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, ANFC_MAX_CHUNK_SIZE)),
+	NAND_OP_PARSER_PATTERN(
+		anfc_wait_type_exec,
+		NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)),
+	);
+
+static int anfc_select_target(struct nand_chip *chip, int target)
+{
+	struct anand *anand = to_anand(chip);
+	struct arasan_nfc *nfc = to_anfc(chip->controller);
+	int ret;
+
+	/* Update the controller timings and the potential ECC configuration */
+	writel_relaxed(anand->timings, nfc->base + DATA_INTERFACE_REG);
+
+	/* Update clock frequency */
+	if (nfc->cur_clk != anand->clk) {
+		clk_disable_unprepare(nfc->controller_clk);
+		ret = clk_set_rate(nfc->controller_clk, anand->clk);
+		if (ret) {
+			dev_err(nfc->dev, "Failed to change clock rate\n");
+			return ret;
+		}
+
+		ret = clk_prepare_enable(nfc->controller_clk);
+		if (ret) {
+			dev_err(nfc->dev,
+				"Failed to re-enable the controller clock\n");
+			return ret;
+		}
+
+		nfc->cur_clk = anand->clk;
+	}
+
+	return 0;
+}
+
+static int anfc_check_op(struct nand_chip *chip,
+			 const struct nand_operation *op)
+{
+	const struct nand_op_instr *instr;
+	int op_id;
+
+	/*
+	 * The controller abstracts all the NAND operations and do not support
+	 * data only operations.
+	 *
+	 * TODO: The nand_op_parser framework should be extended to
+	 * support custom checks on DATA instructions.
+	 */
+	for (op_id = 0; op_id < op->ninstrs; op_id++) {
+		instr = &op->instrs[op_id];
+
+		switch (instr->type) {
+		case NAND_OP_ADDR_INSTR:
+			if (instr->ctx.addr.naddrs > ANFC_MAX_ADDR_CYC)
+				return -ENOTSUPP;
+
+			break;
+		case NAND_OP_DATA_IN_INSTR:
+		case NAND_OP_DATA_OUT_INSTR:
+			if (instr->ctx.data.len > ANFC_MAX_CHUNK_SIZE)
+				return -ENOTSUPP;
+
+			if (anfc_pkt_len_config(instr->ctx.data.len, 0, 0))
+				return -ENOTSUPP;
+
+			break;
+		default:
+			break;
+		}
+	}
+
+	/*
+	 * The controller does not allow to proceed with a CMD+DATA_IN cycle
+	 * manually on the bus by reading data from the data register. Instead,
+	 * the controller abstract a status read operation with its own status
+	 * register after ordering a read status operation. Hence, we cannot
+	 * support any CMD+DATA_IN operation other than a READ STATUS.
+	 *
+	 * TODO: The nand_op_parser() framework should be extended to describe
+	 * fixed patterns instead of open-coding this check here.
+	 */
+	if (op->ninstrs == 2 &&
+	    op->instrs[0].type == NAND_OP_CMD_INSTR &&
+	    op->instrs[0].ctx.cmd.opcode != NAND_CMD_STATUS &&
+	    op->instrs[1].type == NAND_OP_DATA_IN_INSTR)
+		return -ENOTSUPP;
+
+	return nand_op_parser_exec_op(chip, &anfc_op_parser, op, true);
+}
+
+static int anfc_exec_op(struct nand_chip *chip,
+			const struct nand_operation *op,
+			bool check_only)
+{
+	int ret;
+
+	if (check_only)
+		return anfc_check_op(chip, op);
+
+	ret = anfc_select_target(chip, op->cs);
+	if (ret)
+		return ret;
+
+	return nand_op_parser_exec_op(chip, &anfc_op_parser, op, check_only);
+}
+
+static int anfc_setup_data_interface(struct nand_chip *chip, int target,
+				     const struct nand_data_interface *conf)
+{
+	struct anand *anand = to_anand(chip);
+	struct arasan_nfc *nfc = to_anfc(chip->controller);
+	struct device_node *np = nfc->dev->of_node;
+
+	if (target < 0)
+		return 0;
+
+	anand->timings = DIFACE_SDR | DIFACE_SDR_MODE(conf->timings.mode);
+	anand->clk = ANFC_XLNX_SDR_DFLT_CORE_CLK;
+
+	/*
+	 * Due to a hardware bug in the ZynqMP SoC, SDR timing modes 0-1 work
+	 * with f > 90MHz (default clock is 100MHz) but signals are unstable
+	 * with higher modes. Hence we decrease a little bit the clock rate to
+	 * 80MHz when using modes 2-5 with this SoC.
+	 */
+	if (of_device_is_compatible(np, "xlnx,zynqmp-nand-controller") &&
+	    conf->timings.mode >= 2)
+		anand->clk = ANFC_XLNX_SDR_HS_CORE_CLK;
+
+	return 0;
+}
+
+static int anfc_attach_chip(struct nand_chip *chip)
+{
+	struct anand *anand = to_anand(chip);
+	struct arasan_nfc *nfc = to_anfc(chip->controller);
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	int ret = 0;
+
+	if (mtd->writesize <= SZ_512)
+		anand->caddr_cycles = 1;
+	else
+		anand->caddr_cycles = 2;
+
+	if (chip->options & NAND_ROW_ADDR_3)
+		anand->raddr_cycles = 3;
+	else
+		anand->raddr_cycles = 2;
+
+	switch (mtd->writesize) {
+	case 512:
+		anand->page_sz = 0;
+		break;
+	case 1024:
+		anand->page_sz = 5;
+		break;
+	case 2048:
+		anand->page_sz = 1;
+		break;
+	case 4096:
+		anand->page_sz = 2;
+		break;
+	case 8192:
+		anand->page_sz = 3;
+		break;
+	case 16384:
+		anand->page_sz = 4;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* These hooks are valid for all ECC providers */
+	chip->ecc.read_page_raw = nand_monolithic_read_page_raw;
+	chip->ecc.write_page_raw = nand_monolithic_write_page_raw;
+
+	switch (chip->ecc.mode) {
+	case NAND_ECC_NONE:
+	case NAND_ECC_SOFT:
+	case NAND_ECC_ON_DIE:
+		break;
+	case NAND_ECC_HW:
+	default:
+		dev_err(nfc->dev, "Unsupported ECC mode: %d\n",
+			chip->ecc.mode);
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static const struct nand_controller_ops anfc_ops = {
+	.exec_op = anfc_exec_op,
+	.setup_data_interface = anfc_setup_data_interface,
+	.attach_chip = anfc_attach_chip,
+};
+
+static int anfc_chip_init(struct arasan_nfc *nfc, struct device_node *np)
+{
+	struct anand *anand;
+	struct nand_chip *chip;
+	struct mtd_info *mtd;
+	int cs, rb, ret;
+
+	anand = devm_kzalloc(nfc->dev, sizeof(*anand), GFP_KERNEL);
+	if (!anand)
+		return -ENOMEM;
+
+	/* We do not support multiple CS per chip yet */
+	if (of_property_count_elems_of_size(np, "reg", sizeof(u32)) != 1) {
+		dev_err(nfc->dev, "Invalid reg property\n");
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32(np, "reg", &cs);
+	if (ret)
+		return ret;
+
+	ret = of_property_read_u32(np, "nand-rb", &rb);
+	if (ret)
+		return ret;
+
+	if (cs >= ANFC_MAX_CS || rb >= ANFC_MAX_CS) {
+		dev_err(nfc->dev, "Wrong CS %d or RB %d\n", cs, rb);
+		return -EINVAL;
+	}
+
+	if (test_and_set_bit(cs, &nfc->assigned_cs)) {
+		dev_err(nfc->dev, "Already assigned CS %d\n", cs);
+		return -EINVAL;
+	}
+
+	anand->cs = cs;
+	anand->rb = rb;
+
+	chip = &anand->chip;
+	mtd = nand_to_mtd(chip);
+	mtd->dev.parent = nfc->dev;
+	chip->controller = &nfc->controller;
+	chip->options = NAND_BUSWIDTH_AUTO | NAND_NO_SUBPAGE_WRITE |
+			NAND_USES_DMA;
+
+	nand_set_flash_node(chip, np);
+	if (!mtd->name) {
+		dev_err(nfc->dev, "NAND label property is mandatory\n");
+		return -EINVAL;
+	}
+
+	ret = nand_scan(chip, 1);
+	if (ret) {
+		dev_err(nfc->dev, "Scan operation failed\n");
+		return ret;
+	}
+
+	ret = mtd_device_register(mtd, NULL, 0);
+	if (ret) {
+		nand_cleanup(chip);
+		return ret;
+	}
+
+	list_add_tail(&anand->node, &nfc->chips);
+
+	return 0;
+}
+
+static void anfc_chips_cleanup(struct arasan_nfc *nfc)
+{
+	struct anand *anand, *tmp;
+	struct nand_chip *chip;
+	int ret;
+
+	list_for_each_entry_safe(anand, tmp, &nfc->chips, node) {
+		chip = &anand->chip;
+		ret = mtd_device_unregister(nand_to_mtd(chip));
+		WARN_ON(ret);
+		nand_cleanup(chip);
+		list_del(&anand->node);
+	}
+}
+
+static int anfc_chips_init(struct arasan_nfc *nfc)
+{
+	struct device_node *np = nfc->dev->of_node, *nand_np;
+	int nchips = of_get_child_count(np);
+	int ret;
+
+	if (!nchips || nchips > ANFC_MAX_CS) {
+		dev_err(nfc->dev, "Incorrect number of NAND chips (%d)\n",
+			nchips);
+		return -EINVAL;
+	}
+
+	for_each_child_of_node(np, nand_np) {
+		ret = anfc_chip_init(nfc, nand_np);
+		if (ret) {
+			of_node_put(nand_np);
+			anfc_chips_cleanup(nfc);
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static void anfc_reset(struct arasan_nfc *nfc)
+{
+	/* Disable interrupt signals */
+	writel_relaxed(0, nfc->base + INTR_SIG_EN_REG);
+
+	/* Enable interrupt status */
+	writel_relaxed(EVENT_MASK, nfc->base + INTR_STS_EN_REG);
+}
+
+static int anfc_probe(struct platform_device *pdev)
+{
+	struct arasan_nfc *nfc;
+	int ret;
+
+	nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL);
+	if (!nfc)
+		return -ENOMEM;
+
+	nfc->dev = &pdev->dev;
+	nand_controller_init(&nfc->controller);
+	nfc->controller.ops = &anfc_ops;
+	INIT_LIST_HEAD(&nfc->chips);
+
+	nfc->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(nfc->base))
+		return PTR_ERR(nfc->base);
+
+	anfc_reset(nfc);
+
+	nfc->controller_clk = devm_clk_get(&pdev->dev, "controller");
+	if (IS_ERR(nfc->controller_clk))
+		return PTR_ERR(nfc->controller_clk);
+
+	nfc->bus_clk = devm_clk_get(&pdev->dev, "bus");
+	if (IS_ERR(nfc->bus_clk))
+		return PTR_ERR(nfc->bus_clk);
+
+	ret = clk_prepare_enable(nfc->controller_clk);
+	if (ret)
+		return ret;
+
+	ret = clk_prepare_enable(nfc->bus_clk);
+	if (ret)
+		goto disable_controller_clk;
+
+	ret = anfc_chips_init(nfc);
+	if (ret)
+		goto disable_bus_clk;
+
+	platform_set_drvdata(pdev, nfc);
+
+	return 0;
+
+disable_bus_clk:
+	clk_disable_unprepare(nfc->bus_clk);
+
+disable_controller_clk:
+	clk_disable_unprepare(nfc->controller_clk);
+
+	return ret;
+}
+
+static int anfc_remove(struct platform_device *pdev)
+{
+	struct arasan_nfc *nfc = platform_get_drvdata(pdev);
+
+	anfc_chips_cleanup(nfc);
+
+	clk_disable_unprepare(nfc->bus_clk);
+	clk_disable_unprepare(nfc->controller_clk);
+
+	return 0;
+}
+
+static const struct of_device_id anfc_ids[] = {
+	{
+		.compatible = "xlnx,zynqmp-nand-controller",
+	},
+	{
+		.compatible = "arasan,nfc-v3p10",
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(of, anfc_ids);
+
+static struct platform_driver anfc_driver = {
+	.driver = {
+		.name = "arasan-nand-controller",
+		.of_match_table = anfc_ids,
+	},
+	.probe = anfc_probe,
+	.remove = anfc_remove,
+};
+module_platform_driver(anfc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Punnaiah Choudary Kalluri <punnaia@xilinx.com>");
+MODULE_AUTHOR("Naga Sureshkumar Relli <nagasure@xilinx.com>");
+MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
+MODULE_DESCRIPTION("Arasan NAND Flash Controller Driver");
-- 
2.20.1


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* [PATCH v5 8/8] mtd: rawnand: arasan: Support the hardware BCH ECC engine
  2020-05-19  7:45 [PATCH v5 0/8] New Arasan NAND controller driver Miquel Raynal
                   ` (6 preceding siblings ...)
  2020-05-19  7:45 ` [PATCH v5 7/8] mtd: rawnand: arasan: Add new Arasan NAND controller Miquel Raynal
@ 2020-05-19  7:45 ` Miquel Raynal
  2020-05-19  8:51   ` Boris Brezillon
  2020-05-24 19:09   ` Miquel Raynal
  7 siblings, 2 replies; 23+ messages in thread
From: Miquel Raynal @ 2020-05-19  7:45 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus, linux-mtd
  Cc: Michal Simek, Boris Brezillon, Naga Sureshkumar Relli,
	Thomas Petazzoni, Miquel Raynal

Add support for the hardware ECC BCH engine.

Please mind that this engine has an important limitation:
BCH implementation does not inform the user when an uncorrectable ECC
error occurs. To workaround this, we avoid using the hardware engine
in the read path and do the computation with the software BCH
implementation, which is faster than mixing hardware (for correction)
and software (for verification).

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 drivers/mtd/nand/raw/arasan-nand-controller.c | 342 ++++++++++++++++++
 1 file changed, 342 insertions(+)

diff --git a/drivers/mtd/nand/raw/arasan-nand-controller.c b/drivers/mtd/nand/raw/arasan-nand-controller.c
index 03d95ee1488b..cd0e11a25ec7 100644
--- a/drivers/mtd/nand/raw/arasan-nand-controller.c
+++ b/drivers/mtd/nand/raw/arasan-nand-controller.c
@@ -10,6 +10,7 @@
  *   Naga Sureshkumar Relli <nagasure@xilinx.com>
  */
 
+#include <linux/bch.h>
 #include <linux/bitfield.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
@@ -144,6 +145,11 @@ struct anfc_op {
  * @strength:		Register value of the ECC strength
  * @raddr_cycles:	Row address cycle information
  * @caddr_cycles:	Column address cycle information
+ * @ecc_bits:		Exact number of ECC bits per syndrome
+ * @ecc_total:		Total number of ECC bytes
+ * @errloc:		Array of errors located with soft BCH
+ * @hw_ecc:		Buffer to store syndromes computed by hardware
+ * @bch:		BCH structure
  */
 struct anand {
 	struct list_head node;
@@ -157,6 +163,11 @@ struct anand {
 	u32 strength;
 	u16 raddr_cycles;
 	u16 caddr_cycles;
+	unsigned int ecc_bits;
+	unsigned int ecc_total;
+	unsigned int *errloc;
+	u8 *hw_ecc;
+	struct bch_control *bch;
 };
 
 /**
@@ -261,6 +272,194 @@ static int anfc_pkt_len_config(unsigned int len, unsigned int *steps,
 	return 0;
 }
 
+/*
+ * When using the embedded hardware ECC engine, the controller is in charge of
+ * feeding the engine with, first, the ECC residue present in the data array.
+ * A typical read operation is:
+ * 1/ Assert the read operation by sending the relevant command/address cycles
+ *    but targeting the column of the first ECC bytes in the OOB area instead of
+ *    the main data directly.
+ * 2/ After having read the relevant number of ECC bytes, the controller uses
+ *    the RNDOUT/RNDSTART commands which are set into the "ECC Spare Command
+ *    Register" to move the pointer back at the beginning of the main data.
+ * 3/ It will read the content of the main area for a given size (pktsize) and
+ *    will feed the ECC engine with this buffer again.
+ * 4/ The ECC engine derives the ECC bytes for the given data and compare them
+ *    with the ones already received. It eventually trigger status flags and
+ *    then set the "Buffer Read Ready" flag.
+ * 5/ The corrected data is then available for reading from the data port
+ *    register.
+ *
+ * The hardware BCH ECC engine is known to be inconstent in BCH mode and never
+ * reports uncorrectable errors. Because of this bug, we have to use the
+ * software BCH implementation in the read path.
+ */
+static int anfc_read_page_hw_ecc(struct nand_chip *chip, u8 *buf,
+				 int oob_required, int page)
+{
+	struct arasan_nfc *nfc = to_anfc(chip->controller);
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	struct anand *anand = to_anand(chip);
+	unsigned int len = mtd->writesize + (oob_required ? mtd->oobsize : 0);
+	unsigned int max_bitflips = 0;
+	dma_addr_t paddr;
+	int step, ret;
+	struct anfc_op nfc_op = {
+		.pkt_reg =
+			PKT_SIZE(chip->ecc.size) |
+			PKT_STEPS(chip->ecc.steps),
+		.addr1_reg =
+			(page & 0xFF) << (8 * (anand->caddr_cycles)) |
+			(((page >> 8) & 0xFF) << (8 * (1 + anand->caddr_cycles))),
+		.addr2_reg =
+			((page >> 16) & 0xFF) |
+			ADDR2_STRENGTH(anand->strength) |
+			ADDR2_CS(anand->cs),
+		.cmd_reg =
+			CMD_1(NAND_CMD_READ0) |
+			CMD_2(NAND_CMD_READSTART) |
+			CMD_PAGE_SIZE(anand->page_sz) |
+			CMD_DMA_ENABLE |
+			CMD_NADDRS(anand->caddr_cycles +
+				   anand->raddr_cycles),
+		.prog_reg = PROG_PGRD,
+	};
+
+	paddr = dma_map_single(nfc->dev, (void *)buf, len, DMA_FROM_DEVICE);
+	if (dma_mapping_error(nfc->dev, paddr)) {
+		dev_err(nfc->dev, "Buffer mapping error");
+		return -EIO;
+	}
+
+	writel_relaxed(paddr, nfc->base + DMA_ADDR0_REG);
+	writel_relaxed((paddr >> 32), nfc->base + DMA_ADDR1_REG);
+
+	anfc_trigger_op(nfc, &nfc_op);
+
+	ret = anfc_wait_for_event(nfc, XFER_COMPLETE);
+	dma_unmap_single(nfc->dev, paddr, len, DMA_FROM_DEVICE);
+	if (ret) {
+		dev_err(nfc->dev, "Error reading page %d\n", page);
+		return ret;
+	}
+
+	/* Store the raw OOB bytes as well */
+	ret = nand_change_read_column_op(chip, mtd->writesize, chip->oob_poi,
+					 mtd->oobsize, 0);
+	if (ret)
+		return ret;
+
+	/*
+	 * For each step, compute by softare the BCH syndrome over the raw data.
+	 * Compare the theoretical amount of errors and compare with the
+	 * hardware engine feedback.
+	 */
+	for (step = 0; step < chip->ecc.steps; step++) {
+		u8 *raw_buf = &buf[step * chip->ecc.size];
+		unsigned int bit, byte;
+		int bf, i;
+
+		/* Extract the syndrome, it is not necessarily aligned */
+		memset(anand->hw_ecc, 0, chip->ecc.bytes);
+		nand_extract_bits(anand->hw_ecc, 0,
+				  &chip->oob_poi[mtd->oobsize - anand->ecc_total],
+				  anand->ecc_bits * step, anand->ecc_bits);
+
+		bf = bch_decode(anand->bch, raw_buf, chip->ecc.size,
+				anand->hw_ecc, NULL, NULL, anand->errloc);
+		if (!bf) {
+			continue;
+		} else if (bf > 0) {
+			for (i = 0; i < bf; i++) {
+				/* Only correct the data, not the syndrome */
+				if (anand->errloc[i] < (chip->ecc.size * 8)) {
+					bit = BIT(anand->errloc[i] & 7);
+					byte = anand->errloc[i] >> 3;
+					raw_buf[byte] ^= bit;
+				}
+			}
+
+			mtd->ecc_stats.corrected += bf;
+			max_bitflips = max_t(unsigned int, max_bitflips, bf);
+
+			continue;
+		}
+
+		bf = nand_check_erased_ecc_chunk(raw_buf, chip->ecc.size,
+						 NULL, 0, NULL, 0,
+						 chip->ecc.strength);
+		if (bf > 0) {
+			mtd->ecc_stats.corrected += bf;
+			max_bitflips = max_t(unsigned int, max_bitflips, bf);
+			memset(raw_buf, 0xFF, chip->ecc.size);
+		} else if (bf < 0) {
+			mtd->ecc_stats.failed++;
+		}
+	}
+
+	return 0;
+}
+
+static int anfc_write_page_hw_ecc(struct nand_chip *chip, const u8 *buf,
+				  int oob_required, int page)
+{
+	struct anand *anand = to_anand(chip);
+	struct arasan_nfc *nfc = to_anfc(chip->controller);
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	unsigned int len = mtd->writesize + (oob_required ? mtd->oobsize : 0);
+	dma_addr_t paddr;
+	int ret;
+	struct anfc_op nfc_op = {
+		.pkt_reg =
+			PKT_SIZE(chip->ecc.size) |
+			PKT_STEPS(chip->ecc.steps),
+		.addr1_reg =
+			(page & 0xFF) << (8 * (anand->caddr_cycles)) |
+			(((page >> 8) & 0xFF) << (8 * (1 + anand->caddr_cycles))),
+		.addr2_reg =
+			((page >> 16) & 0xFF) |
+			ADDR2_STRENGTH(anand->strength) |
+			ADDR2_CS(anand->cs),
+		.cmd_reg =
+			CMD_1(NAND_CMD_SEQIN) |
+			CMD_2(NAND_CMD_PAGEPROG) |
+			CMD_PAGE_SIZE(anand->page_sz) |
+			CMD_DMA_ENABLE |
+			CMD_NADDRS(anand->caddr_cycles +
+				   anand->raddr_cycles) |
+			CMD_ECC_ENABLE,
+		.prog_reg = PROG_PGPROG,
+	};
+
+	writel_relaxed(anand->ecc_conf, nfc->base + ECC_CONF_REG);
+	writel_relaxed(ECC_SP_CMD1(NAND_CMD_RNDIN) |
+		       ECC_SP_ADDRS(anand->caddr_cycles),
+		       nfc->base + ECC_SP_REG);
+
+	paddr = dma_map_single(nfc->dev, (void *)buf, len, DMA_TO_DEVICE);
+	if (dma_mapping_error(nfc->dev, paddr)) {
+		dev_err(nfc->dev, "Buffer mapping error");
+		return -EIO;
+	}
+
+	writel_relaxed(paddr, nfc->base + DMA_ADDR0_REG);
+	writel_relaxed((paddr >> 32), nfc->base + DMA_ADDR1_REG);
+
+	anfc_trigger_op(nfc, &nfc_op);
+	ret = anfc_wait_for_event(nfc, XFER_COMPLETE);
+	dma_unmap_single(nfc->dev, paddr, len, DMA_TO_DEVICE);
+	if (ret) {
+		dev_err(nfc->dev, "Error writing page %d\n", page);
+		return ret;
+	}
+
+	/* Spare data is not protected */
+	if (oob_required)
+		ret = nand_write_oob_std(chip, page);
+
+	return ret;
+}
+
 /* NAND framework ->exec_op() hooks and related helpers */
 static int anfc_parse_instructions(struct nand_chip *chip,
 				   const struct nand_subop *subop,
@@ -681,6 +880,138 @@ static int anfc_setup_data_interface(struct nand_chip *chip, int target,
 	return 0;
 }
 
+static int anfc_calc_hw_ecc_bytes(int step_size, int strength)
+{
+	unsigned int bch_gf_mag, ecc_bits;
+
+	switch (step_size) {
+	case SZ_512:
+		bch_gf_mag = 13;
+		break;
+	case SZ_1K:
+		bch_gf_mag = 14;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ecc_bits = bch_gf_mag * strength;
+
+	return DIV_ROUND_UP(ecc_bits, 8);
+}
+
+static const int anfc_hw_ecc_512_strengths[] = {4, 8, 12};
+
+static const int anfc_hw_ecc_1024_strengths[] = {24};
+
+static const struct nand_ecc_step_info anfc_hw_ecc_step_infos[] = {
+	{
+		.stepsize = SZ_512,
+		.strengths = anfc_hw_ecc_512_strengths,
+		.nstrengths = ARRAY_SIZE(anfc_hw_ecc_512_strengths),
+	},
+	{
+		.stepsize = SZ_1K,
+		.strengths = anfc_hw_ecc_1024_strengths,
+		.nstrengths = ARRAY_SIZE(anfc_hw_ecc_1024_strengths),
+	},
+};
+
+static const struct nand_ecc_caps anfc_hw_ecc_caps = {
+	.stepinfos = anfc_hw_ecc_step_infos,
+	.nstepinfos = ARRAY_SIZE(anfc_hw_ecc_step_infos),
+	.calc_ecc_bytes = anfc_calc_hw_ecc_bytes,
+};
+
+static int anfc_init_hw_ecc_controller(struct arasan_nfc *nfc,
+				       struct nand_chip *chip)
+{
+	struct anand *anand = to_anand(chip);
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	struct nand_ecc_ctrl *ecc = &chip->ecc;
+	unsigned int bch_prim_poly = 0, bch_gf_mag = 0, ecc_offset;
+	int ret;
+
+	switch (mtd->writesize) {
+	case SZ_512:
+	case SZ_2K:
+	case SZ_4K:
+	case SZ_8K:
+	case SZ_16K:
+		break;
+	default:
+		dev_err(nfc->dev, "Unsupported page size %d\n", mtd->writesize);
+		return -EINVAL;
+	}
+
+	ret = nand_ecc_choose_conf(chip, &anfc_hw_ecc_caps, mtd->oobsize);
+	if (ret)
+		return ret;
+
+	switch (ecc->strength) {
+	case 12:
+		anand->strength = 0x1;
+		break;
+	case 8:
+		anand->strength = 0x2;
+		break;
+	case 4:
+		anand->strength = 0x3;
+		break;
+	case 24:
+		anand->strength = 0x4;
+		break;
+	default:
+		dev_err(nfc->dev, "Unsupported strength %d\n", ecc->strength);
+		return -EINVAL;
+	}
+
+	switch (ecc->size) {
+	case SZ_512:
+		bch_gf_mag = 13;
+		bch_prim_poly = 0x201b;
+		break;
+	case SZ_1K:
+		bch_gf_mag = 14;
+		bch_prim_poly = 0x4443;
+		break;
+	default:
+		dev_err(nfc->dev, "Unsupported step size %d\n", ecc->strength);
+		return -EINVAL;
+	}
+
+	mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
+
+	ecc->steps = mtd->writesize / ecc->size;
+	ecc->algo = NAND_ECC_BCH;
+	anand->ecc_bits = bch_gf_mag * ecc->strength;
+	ecc->bytes = DIV_ROUND_UP(anand->ecc_bits, 8);
+	anand->ecc_total = DIV_ROUND_UP(anand->ecc_bits * ecc->steps, 8);
+	ecc_offset = mtd->writesize + mtd->oobsize - anand->ecc_total;
+	anand->ecc_conf = ECC_CONF_COL(ecc_offset) |
+			  ECC_CONF_LEN(anand->ecc_total) |
+			  ECC_CONF_BCH_EN;
+
+	anand->errloc = devm_kmalloc_array(nfc->dev, ecc->strength,
+					   sizeof(*anand->errloc), GFP_KERNEL);
+	if (!anand->errloc)
+		return -ENOMEM;
+
+	anand->hw_ecc = devm_kmalloc(nfc->dev, ecc->bytes, GFP_KERNEL);
+	if (!anand->hw_ecc)
+		return -ENOMEM;
+
+	/* Enforce bit swapping to fit the hardware */
+	anand->bch = bch_init(bch_gf_mag, ecc->strength, bch_prim_poly, true);
+	if (!anand->bch)
+		return -EINVAL;
+
+	ecc->read_page = anfc_read_page_hw_ecc;
+	ecc->write_page = anfc_write_page_hw_ecc;
+
+	return 0;
+}
+
 static int anfc_attach_chip(struct nand_chip *chip)
 {
 	struct anand *anand = to_anand(chip);
@@ -731,6 +1062,8 @@ static int anfc_attach_chip(struct nand_chip *chip)
 	case NAND_ECC_ON_DIE:
 		break;
 	case NAND_ECC_HW:
+		ret = anfc_init_hw_ecc_controller(nfc, chip);
+		break;
 	default:
 		dev_err(nfc->dev, "Unsupported ECC mode: %d\n",
 			chip->ecc.mode);
@@ -740,10 +1073,19 @@ static int anfc_attach_chip(struct nand_chip *chip)
 	return ret;
 }
 
+static void anfc_detach_chip(struct nand_chip *chip)
+{
+	struct anand *anand = to_anand(chip);
+
+	if (anand->bch)
+		bch_free(anand->bch);
+}
+
 static const struct nand_controller_ops anfc_ops = {
 	.exec_op = anfc_exec_op,
 	.setup_data_interface = anfc_setup_data_interface,
 	.attach_chip = anfc_attach_chip,
+	.detach_chip = anfc_detach_chip,
 };
 
 static int anfc_chip_init(struct arasan_nfc *nfc, struct device_node *np)
-- 
2.20.1


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* Re: [PATCH v5 2/8] lib/bch: Allow easy bit swapping
  2020-05-19  7:45 ` [PATCH v5 2/8] lib/bch: Allow easy bit swapping Miquel Raynal
@ 2020-05-19  8:41   ` Boris Brezillon
  2020-05-24 19:09   ` Miquel Raynal
  1 sibling, 0 replies; 23+ messages in thread
From: Boris Brezillon @ 2020-05-19  8:41 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Michal Simek, Vignesh Raghavendra, Tudor Ambarus,
	Richard Weinberger, linux-mtd, Thomas Petazzoni,
	Naga Sureshkumar Relli

On Tue, 19 May 2020 09:45:43 +0200
Miquel Raynal <miquel.raynal@bootlin.com> wrote:

> It seems that several hardware ECC engine use a swapped representation
> of bytes compared to software. This might having to do with how the
> ECC engine is wired to the NAND controller or the order the bits are
> passed to the hardware BCH logic.
> 
> This means that when the software BCH engine is working in conjunction
> with data generated with hardware, sometimes we might need to swap the
> bits inside bytes, eg:
> 
>     0x0A = b0000_1010 -> b0101_0000 = 0x50
> 
> Make it possible by adding a boolean to the BCH initialization routine.
> 
> Regarding the implementation itself, this is a rather simple approach
> that can probably be enhanced in the future by preparing the
> ->a_{mod,pow}_tab tables with the swapping in mind.  
> 
> Suggested-by: Boris Brezillon <boris.brezillon@collabora.com>
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>

Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>

> ---
>  drivers/mtd/devices/docg3.c     |  2 +-
>  drivers/mtd/nand/raw/nand_bch.c |  2 +-
>  include/linux/bch.h             |  5 +-
>  lib/bch.c                       | 90 ++++++++++++++++++++++++++++-----
>  4 files changed, 82 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c
> index 799df8d03357..a030792115bc 100644
> --- a/drivers/mtd/devices/docg3.c
> +++ b/drivers/mtd/devices/docg3.c
> @@ -1985,7 +1985,7 @@ static int __init docg3_probe(struct platform_device *pdev)
>  	cascade->base = base;
>  	mutex_init(&cascade->lock);
>  	cascade->bch = bch_init(DOC_ECC_BCH_M, DOC_ECC_BCH_T,
> -				DOC_ECC_BCH_PRIMPOLY);
> +				DOC_ECC_BCH_PRIMPOLY, false);
>  	if (!cascade->bch)
>  		return ret;
>  
> diff --git a/drivers/mtd/nand/raw/nand_bch.c b/drivers/mtd/nand/raw/nand_bch.c
> index d95fcc7358e9..d5af8c5fd02f 100644
> --- a/drivers/mtd/nand/raw/nand_bch.c
> +++ b/drivers/mtd/nand/raw/nand_bch.c
> @@ -130,7 +130,7 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd)
>  	if (!nbc)
>  		goto fail;
>  
> -	nbc->bch = bch_init(m, t, 0);
> +	nbc->bch = bch_init(m, t, 0, false);
>  	if (!nbc->bch)
>  		goto fail;
>  
> diff --git a/include/linux/bch.h b/include/linux/bch.h
> index 9c35e7cd5890..85fdce83d4e2 100644
> --- a/include/linux/bch.h
> +++ b/include/linux/bch.h
> @@ -33,6 +33,7 @@
>   * @cache:      log-based polynomial representation buffer
>   * @elp:        error locator polynomial
>   * @poly_2t:    temporary polynomials of degree 2t
> + * @swap_bits:  swap bits within data and syndrome bytes
>   */
>  struct bch_control {
>  	unsigned int    m;
> @@ -51,9 +52,11 @@ struct bch_control {
>  	int            *cache;
>  	struct gf_poly *elp;
>  	struct gf_poly *poly_2t[4];
> +	bool		swap_bits;
>  };
>  
> -struct bch_control *bch_init(int m, int t, unsigned int prim_poly);
> +struct bch_control *bch_init(int m, int t, unsigned int prim_poly,
> +			     bool swap_bits);
>  
>  void bch_free(struct bch_control *bch);
>  
> diff --git a/lib/bch.c b/lib/bch.c
> index 1091841ac716..7c031ee8b93b 100644
> --- a/lib/bch.c
> +++ b/lib/bch.c
> @@ -114,6 +114,49 @@ struct gf_poly_deg1 {
>  	unsigned int   c[2];
>  };
>  
> +static u8 swap_bits_table[] = {
> +	0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
> +	0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
> +	0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
> +	0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
> +	0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
> +	0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
> +	0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
> +	0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
> +	0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
> +	0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
> +	0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
> +	0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
> +	0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
> +	0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
> +	0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
> +	0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
> +	0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
> +	0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
> +	0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
> +	0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
> +	0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
> +	0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
> +	0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
> +	0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
> +	0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
> +	0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
> +	0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
> +	0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
> +	0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
> +	0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
> +	0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
> +	0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
> +};
> +
> +static u8 swap_bits(struct bch_control *bch, u8 in)
> +{
> +	if (!bch->swap_bits)
> +		return in;
> +
> +	return swap_bits_table[in];
> +}
> +
>  /*
>   * same as bch_encode(), but process input data one byte at a time
>   */
> @@ -126,7 +169,9 @@ static void bch_encode_unaligned(struct bch_control *bch,
>  	const int l = BCH_ECC_WORDS(bch)-1;
>  
>  	while (len--) {
> -		p = bch->mod8_tab + (l+1)*(((ecc[0] >> 24)^(*data++)) & 0xff);
> +		u8 tmp = swap_bits(bch, *data++);
> +
> +		p = bch->mod8_tab + (l+1)*(((ecc[0] >> 24)^(tmp)) & 0xff);
>  
>  		for (i = 0; i < l; i++)
>  			ecc[i] = ((ecc[i] << 8)|(ecc[i+1] >> 24))^(*p++);
> @@ -145,10 +190,16 @@ static void load_ecc8(struct bch_control *bch, uint32_t *dst,
>  	unsigned int i, nwords = BCH_ECC_WORDS(bch)-1;
>  
>  	for (i = 0; i < nwords; i++, src += 4)
> -		dst[i] = (src[0] << 24)|(src[1] << 16)|(src[2] << 8)|src[3];
> +		dst[i] = ((u32)swap_bits(bch, src[0]) << 24) |
> +			((u32)swap_bits(bch, src[1]) << 16) |
> +			((u32)swap_bits(bch, src[2]) << 8) |
> +			swap_bits(bch, src[3]);
>  
>  	memcpy(pad, src, BCH_ECC_BYTES(bch)-4*nwords);
> -	dst[nwords] = (pad[0] << 24)|(pad[1] << 16)|(pad[2] << 8)|pad[3];
> +	dst[nwords] = ((u32)swap_bits(bch, pad[0]) << 24) |
> +		((u32)swap_bits(bch, pad[1]) << 16) |
> +		((u32)swap_bits(bch, pad[2]) << 8) |
> +		swap_bits(bch, pad[3]);
>  }
>  
>  /*
> @@ -161,15 +212,15 @@ static void store_ecc8(struct bch_control *bch, uint8_t *dst,
>  	unsigned int i, nwords = BCH_ECC_WORDS(bch)-1;
>  
>  	for (i = 0; i < nwords; i++) {
> -		*dst++ = (src[i] >> 24);
> -		*dst++ = (src[i] >> 16) & 0xff;
> -		*dst++ = (src[i] >>  8) & 0xff;
> -		*dst++ = (src[i] >>  0) & 0xff;
> +		*dst++ = swap_bits(bch, src[i] >> 24);
> +		*dst++ = swap_bits(bch, src[i] >> 16);
> +		*dst++ = swap_bits(bch, src[i] >> 8);
> +		*dst++ = swap_bits(bch, src[i]);
>  	}
> -	pad[0] = (src[nwords] >> 24);
> -	pad[1] = (src[nwords] >> 16) & 0xff;
> -	pad[2] = (src[nwords] >>  8) & 0xff;
> -	pad[3] = (src[nwords] >>  0) & 0xff;
> +	pad[0] = swap_bits(bch, src[nwords] >> 24);
> +	pad[1] = swap_bits(bch, src[nwords] >> 16);
> +	pad[2] = swap_bits(bch, src[nwords] >> 8);
> +	pad[3] = swap_bits(bch, src[nwords]);
>  	memcpy(dst, pad, BCH_ECC_BYTES(bch)-4*nwords);
>  }
>  
> @@ -240,7 +291,13 @@ void bch_encode(struct bch_control *bch, const uint8_t *data,
>  	 */
>  	while (mlen--) {
>  		/* input data is read in big-endian format */
> -		w = r[0]^cpu_to_be32(*pdata++);
> +		w = cpu_to_be32(*pdata++);
> +		if (bch->swap_bits)
> +			w = (u32)swap_bits(bch, w) |
> +			    ((u32)swap_bits(bch, w >> 8) << 8) |
> +			    ((u32)swap_bits(bch, w >> 16) << 16) |
> +			    ((u32)swap_bits(bch, w >> 24) << 24);
> +		w ^= r[0];
>  		p0 = tab0 + (l+1)*((w >>  0) & 0xff);
>  		p1 = tab1 + (l+1)*((w >>  8) & 0xff);
>  		p2 = tab2 + (l+1)*((w >> 16) & 0xff);
> @@ -1048,7 +1105,9 @@ int bch_decode(struct bch_control *bch, const uint8_t *data, unsigned int len,
>  				break;
>  			}
>  			errloc[i] = nbits-1-errloc[i];
> -			errloc[i] = (errloc[i] & ~7)|(7-(errloc[i] & 7));
> +			if (!bch->swap_bits)
> +				errloc[i] = (errloc[i] & ~7) |
> +					    (7-(errloc[i] & 7));
>  		}
>  	}
>  	return (err >= 0) ? err : -EBADMSG;
> @@ -1240,6 +1299,7 @@ static uint32_t *compute_generator_polynomial(struct bch_control *bch)
>   * @m:          Galois field order, should be in the range 5-15
>   * @t:          maximum error correction capability, in bits
>   * @prim_poly:  user-provided primitive polynomial (or 0 to use default)
> + * @swap_bits:  swap bits within data and syndrome bytes
>   *
>   * Returns:
>   *  a newly allocated BCH control structure if successful, NULL otherwise
> @@ -1256,7 +1316,8 @@ static uint32_t *compute_generator_polynomial(struct bch_control *bch)
>   * BCH control structure, ecc length in bytes is given by member @ecc_bytes of
>   * the structure.
>   */
> -struct bch_control *bch_init(int m, int t, unsigned int prim_poly)
> +struct bch_control *bch_init(int m, int t, unsigned int prim_poly,
> +			     bool swap_bits)
>  {
>  	int err = 0;
>  	unsigned int i, words;
> @@ -1321,6 +1382,7 @@ struct bch_control *bch_init(int m, int t, unsigned int prim_poly)
>  	bch->syn       = bch_alloc(2*t*sizeof(*bch->syn), &err);
>  	bch->cache     = bch_alloc(2*t*sizeof(*bch->cache), &err);
>  	bch->elp       = bch_alloc((t+1)*sizeof(struct gf_poly_deg1), &err);
> +	bch->swap_bits = swap_bits;
>  
>  	for (i = 0; i < ARRAY_SIZE(bch->poly_2t); i++)
>  		bch->poly_2t[i] = bch_alloc(GF_POLY_SZ(2*t), &err);


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* Re: [PATCH v5 3/8] mtd: rawnand: Ensure the number of bitflips is consistent
  2020-05-19  7:45 ` [PATCH v5 3/8] mtd: rawnand: Ensure the number of bitflips is consistent Miquel Raynal
@ 2020-05-19  8:42   ` Boris Brezillon
  2020-05-24 19:09   ` Miquel Raynal
  1 sibling, 0 replies; 23+ messages in thread
From: Boris Brezillon @ 2020-05-19  8:42 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Michal Simek, Vignesh Raghavendra, Tudor Ambarus,
	Richard Weinberger, linux-mtd, Thomas Petazzoni,
	Naga Sureshkumar Relli

On Tue, 19 May 2020 09:45:44 +0200
Miquel Raynal <miquel.raynal@bootlin.com> wrote:

> The main NAND read page function can loop over "page reads" many times
> in if the reading reports uncorrectable error(s) and if the chip
> supports the read_retry feature.
> 
> In this case, the number of bitflips is summarized between
> attempts. Fix this by re-initializing the entire mtd_ecc_stats object
> each time we retry.
> 
> Suggested-by: Boris Brezillon <boris.brezillon@collabora.com>
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>

Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>

> ---
>  drivers/mtd/nand/raw/nand_base.c | 10 +++++-----
>  1 file changed, 5 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
> index 2bef01e21533..65e9b2fa2fc5 100644
> --- a/drivers/mtd/nand/raw/nand_base.c
> +++ b/drivers/mtd/nand/raw/nand_base.c
> @@ -3288,7 +3288,7 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
>  	oob_required = oob ? 1 : 0;
>  
>  	while (1) {
> -		unsigned int ecc_failures = mtd->ecc_stats.failed;
> +		struct mtd_ecc_stats ecc_stats = mtd->ecc_stats;
>  
>  		bytes = min(mtd->writesize - col, readlen);
>  		aligned = (bytes == mtd->writesize);
> @@ -3339,7 +3339,7 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
>  			 */
>  			if (use_bounce_buf) {
>  				if (!NAND_HAS_SUBPAGE_READ(chip) && !oob &&
> -				    !(mtd->ecc_stats.failed - ecc_failures) &&
> +				    !(mtd->ecc_stats.failed - ecc_stats.failed) &&
>  				    (ops->mode != MTD_OPS_RAW)) {
>  					chip->pagecache.page = realpage;
>  					chip->pagecache.bitflips = ret;
> @@ -3362,7 +3362,7 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
>  
>  			nand_wait_readrdy(chip);
>  
> -			if (mtd->ecc_stats.failed - ecc_failures) {
> +			if (mtd->ecc_stats.failed - ecc_stats.failed) {
>  				if (retry_mode + 1 < chip->read_retries) {
>  					retry_mode++;
>  					ret = nand_setup_read_retry(chip,
> @@ -3370,8 +3370,8 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
>  					if (ret < 0)
>  						break;
>  
> -					/* Reset failures; retry */
> -					mtd->ecc_stats.failed = ecc_failures;
> +					/* Reset ecc_stats; retry */
> +					mtd->ecc_stats = ecc_stats;
>  					goto read_retry;
>  				} else {
>  					/* No more retry modes; real failure */


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* Re: [PATCH v5 4/8] mtd: rawnand: Add nand_extract_bits()
  2020-05-19  7:45 ` [PATCH v5 4/8] mtd: rawnand: Add nand_extract_bits() Miquel Raynal
@ 2020-05-19  8:48   ` Boris Brezillon
  2020-05-19 12:15     ` Miquel Raynal
  2020-05-24 19:09   ` Miquel Raynal
  1 sibling, 1 reply; 23+ messages in thread
From: Boris Brezillon @ 2020-05-19  8:48 UTC (permalink / raw)
  To: Miquel Raynal, Vignesh Raghavendra
  Cc: Michal Simek, Tudor Ambarus, Richard Weinberger,
	Naga Sureshkumar Relli, Thomas Petazzoni, linux-mtd

On Tue, 19 May 2020 09:45:45 +0200
Miquel Raynal <miquel.raynal@bootlin.com> wrote:

> There are cases where ECC bytes are not byte-aligned. Indeed, BCH
> implies using a number of ECC bits, which are not always a multiple of
> 8. We then need a helper like nand_extract_bits() to extract these
> syndromes from a buffer.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> ---
>  drivers/mtd/nand/raw/nand_base.c | 42 ++++++++++++++++++++++++++++++++
>  include/linux/mtd/rawnand.h      |  4 +++
>  2 files changed, 46 insertions(+)
> 
> diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
> index 65e9b2fa2fc5..14387b967e8b 100644
> --- a/drivers/mtd/nand/raw/nand_base.c
> +++ b/drivers/mtd/nand/raw/nand_base.c
> @@ -274,6 +274,48 @@ static int check_offs_len(struct nand_chip *chip, loff_t ofs, uint64_t len)
>  	return ret;
>  }
>  
> +/** nand_extract_bits - Copy unaligned bits from one buffer to another one

It should be

/**
 * nand_extract_bits - Copy unaligned bits from one buffer to another

Once addressed you can add

Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>

If someone reads that and has enough energy, it'd be great to have a
generic bitcpy() function (looks like the fbdev [1] subsystem has a
private implementation too).

> + * @dst: destination buffer
> + * @dst_off: bit offset at which the writing starts
> + * @src: source buffer
> + * @src_off: bit offset at which the reading starts
> + * @nbits: number of bits to copy from @src to @dst
> + *
> + * Copy bits from one memory region to another (overlap authorized).
> + */
> +void nand_extract_bits(u8 *dst, unsigned int dst_off, const u8 *src,
> +		       unsigned int src_off, unsigned int nbits)
> +{
> +	unsigned int tmp, n;
> +
> +	dst += dst_off / 8;
> +	dst_off %= 8;
> +	src += src_off / 8;
> +	src_off %= 8;
> +
> +	while (nbits) {
> +		n = min3(8 - dst_off, 8 - src_off, nbits);
> +
> +		tmp = (*src >> src_off) & GENMASK(n - 1, 0);
> +		*dst &= ~GENMASK(n - 1 + dst_off, dst_off);
> +		*dst |= tmp << dst_off;
> +
> +		dst_off += n;
> +		if (dst_off >= 8) {
> +			dst++;
> +			dst_off -= 8;
> +		}
> +
> +		src_off += n;
> +		if (src_off >= 8) {
> +			src++;
> +			src_off -= 8;
> +		}
> +
> +		nbits -= n;
> +	}
> +}
> +
>  /**
>   * nand_select_target() - Select a NAND target (A.K.A. die)
>   * @chip: NAND chip object
> diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h
> index 0f45b6984ad1..45dd57e2a223 100644
> --- a/include/linux/mtd/rawnand.h
> +++ b/include/linux/mtd/rawnand.h
> @@ -1412,6 +1412,10 @@ int nand_gpio_waitrdy(struct nand_chip *chip, struct gpio_desc *gpiod,
>  void nand_select_target(struct nand_chip *chip, unsigned int cs);
>  void nand_deselect_target(struct nand_chip *chip);
>  
> +/* Bitops */
> +void nand_extract_bits(u8 *dst, unsigned int dst_off, const u8 *src,
> +		       unsigned int src_off, unsigned int nbits);
> +
>  /**
>   * nand_get_data_buf() - Get the internal page buffer
>   * @chip: NAND chip object

[1]https://elixir.bootlin.com/linux/latest/source/drivers/video/fbdev/amifb.c#L2598

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* Re: [PATCH v5 7/8] mtd: rawnand: arasan: Add new Arasan NAND controller
  2020-05-19  7:45 ` [PATCH v5 7/8] mtd: rawnand: arasan: Add new Arasan NAND controller Miquel Raynal
@ 2020-05-19  8:50   ` Boris Brezillon
  2020-05-24 19:09   ` Miquel Raynal
  1 sibling, 0 replies; 23+ messages in thread
From: Boris Brezillon @ 2020-05-19  8:50 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Michal Simek, Vignesh Raghavendra, Tudor Ambarus,
	Richard Weinberger, linux-mtd, Thomas Petazzoni,
	Naga Sureshkumar Relli

On Tue, 19 May 2020 09:45:48 +0200
Miquel Raynal <miquel.raynal@bootlin.com> wrote:

> Add the Arasan NAND controller driver. This brings only NAND
> controller support. The ECC engine being a bit subtle, hardware ECC
> support will be added in a second time.
> 
> This work is based on contributions from Naga Sureshkumar Relli.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>

Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* Re: [PATCH v5 8/8] mtd: rawnand: arasan: Support the hardware BCH ECC engine
  2020-05-19  7:45 ` [PATCH v5 8/8] mtd: rawnand: arasan: Support the hardware BCH ECC engine Miquel Raynal
@ 2020-05-19  8:51   ` Boris Brezillon
  2020-05-24 19:09   ` Miquel Raynal
  1 sibling, 0 replies; 23+ messages in thread
From: Boris Brezillon @ 2020-05-19  8:51 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Michal Simek, Vignesh Raghavendra, Tudor Ambarus,
	Richard Weinberger, linux-mtd, Thomas Petazzoni,
	Naga Sureshkumar Relli

On Tue, 19 May 2020 09:45:49 +0200
Miquel Raynal <miquel.raynal@bootlin.com> wrote:

> Add support for the hardware ECC BCH engine.
> 
> Please mind that this engine has an important limitation:
> BCH implementation does not inform the user when an uncorrectable ECC
> error occurs. To workaround this, we avoid using the hardware engine
> in the read path and do the computation with the software BCH
> implementation, which is faster than mixing hardware (for correction)
> and software (for verification).
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>

Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>

> ---
>  drivers/mtd/nand/raw/arasan-nand-controller.c | 342 ++++++++++++++++++
>  1 file changed, 342 insertions(+)
> 
> diff --git a/drivers/mtd/nand/raw/arasan-nand-controller.c b/drivers/mtd/nand/raw/arasan-nand-controller.c
> index 03d95ee1488b..cd0e11a25ec7 100644
> --- a/drivers/mtd/nand/raw/arasan-nand-controller.c
> +++ b/drivers/mtd/nand/raw/arasan-nand-controller.c
> @@ -10,6 +10,7 @@
>   *   Naga Sureshkumar Relli <nagasure@xilinx.com>
>   */
>  
> +#include <linux/bch.h>
>  #include <linux/bitfield.h>
>  #include <linux/clk.h>
>  #include <linux/delay.h>
> @@ -144,6 +145,11 @@ struct anfc_op {
>   * @strength:		Register value of the ECC strength
>   * @raddr_cycles:	Row address cycle information
>   * @caddr_cycles:	Column address cycle information
> + * @ecc_bits:		Exact number of ECC bits per syndrome
> + * @ecc_total:		Total number of ECC bytes
> + * @errloc:		Array of errors located with soft BCH
> + * @hw_ecc:		Buffer to store syndromes computed by hardware
> + * @bch:		BCH structure
>   */
>  struct anand {
>  	struct list_head node;
> @@ -157,6 +163,11 @@ struct anand {
>  	u32 strength;
>  	u16 raddr_cycles;
>  	u16 caddr_cycles;
> +	unsigned int ecc_bits;
> +	unsigned int ecc_total;
> +	unsigned int *errloc;
> +	u8 *hw_ecc;
> +	struct bch_control *bch;
>  };
>  
>  /**
> @@ -261,6 +272,194 @@ static int anfc_pkt_len_config(unsigned int len, unsigned int *steps,
>  	return 0;
>  }
>  
> +/*
> + * When using the embedded hardware ECC engine, the controller is in charge of
> + * feeding the engine with, first, the ECC residue present in the data array.
> + * A typical read operation is:
> + * 1/ Assert the read operation by sending the relevant command/address cycles
> + *    but targeting the column of the first ECC bytes in the OOB area instead of
> + *    the main data directly.
> + * 2/ After having read the relevant number of ECC bytes, the controller uses
> + *    the RNDOUT/RNDSTART commands which are set into the "ECC Spare Command
> + *    Register" to move the pointer back at the beginning of the main data.
> + * 3/ It will read the content of the main area for a given size (pktsize) and
> + *    will feed the ECC engine with this buffer again.
> + * 4/ The ECC engine derives the ECC bytes for the given data and compare them
> + *    with the ones already received. It eventually trigger status flags and
> + *    then set the "Buffer Read Ready" flag.
> + * 5/ The corrected data is then available for reading from the data port
> + *    register.
> + *
> + * The hardware BCH ECC engine is known to be inconstent in BCH mode and never
> + * reports uncorrectable errors. Because of this bug, we have to use the
> + * software BCH implementation in the read path.
> + */
> +static int anfc_read_page_hw_ecc(struct nand_chip *chip, u8 *buf,
> +				 int oob_required, int page)
> +{
> +	struct arasan_nfc *nfc = to_anfc(chip->controller);
> +	struct mtd_info *mtd = nand_to_mtd(chip);
> +	struct anand *anand = to_anand(chip);
> +	unsigned int len = mtd->writesize + (oob_required ? mtd->oobsize : 0);
> +	unsigned int max_bitflips = 0;
> +	dma_addr_t paddr;
> +	int step, ret;
> +	struct anfc_op nfc_op = {
> +		.pkt_reg =
> +			PKT_SIZE(chip->ecc.size) |
> +			PKT_STEPS(chip->ecc.steps),
> +		.addr1_reg =
> +			(page & 0xFF) << (8 * (anand->caddr_cycles)) |
> +			(((page >> 8) & 0xFF) << (8 * (1 + anand->caddr_cycles))),
> +		.addr2_reg =
> +			((page >> 16) & 0xFF) |
> +			ADDR2_STRENGTH(anand->strength) |
> +			ADDR2_CS(anand->cs),
> +		.cmd_reg =
> +			CMD_1(NAND_CMD_READ0) |
> +			CMD_2(NAND_CMD_READSTART) |
> +			CMD_PAGE_SIZE(anand->page_sz) |
> +			CMD_DMA_ENABLE |
> +			CMD_NADDRS(anand->caddr_cycles +
> +				   anand->raddr_cycles),
> +		.prog_reg = PROG_PGRD,
> +	};
> +
> +	paddr = dma_map_single(nfc->dev, (void *)buf, len, DMA_FROM_DEVICE);
> +	if (dma_mapping_error(nfc->dev, paddr)) {
> +		dev_err(nfc->dev, "Buffer mapping error");
> +		return -EIO;
> +	}
> +
> +	writel_relaxed(paddr, nfc->base + DMA_ADDR0_REG);
> +	writel_relaxed((paddr >> 32), nfc->base + DMA_ADDR1_REG);
> +
> +	anfc_trigger_op(nfc, &nfc_op);
> +
> +	ret = anfc_wait_for_event(nfc, XFER_COMPLETE);
> +	dma_unmap_single(nfc->dev, paddr, len, DMA_FROM_DEVICE);
> +	if (ret) {
> +		dev_err(nfc->dev, "Error reading page %d\n", page);
> +		return ret;
> +	}
> +
> +	/* Store the raw OOB bytes as well */
> +	ret = nand_change_read_column_op(chip, mtd->writesize, chip->oob_poi,
> +					 mtd->oobsize, 0);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * For each step, compute by softare the BCH syndrome over the raw data.
> +	 * Compare the theoretical amount of errors and compare with the
> +	 * hardware engine feedback.
> +	 */
> +	for (step = 0; step < chip->ecc.steps; step++) {
> +		u8 *raw_buf = &buf[step * chip->ecc.size];
> +		unsigned int bit, byte;
> +		int bf, i;
> +
> +		/* Extract the syndrome, it is not necessarily aligned */
> +		memset(anand->hw_ecc, 0, chip->ecc.bytes);
> +		nand_extract_bits(anand->hw_ecc, 0,
> +				  &chip->oob_poi[mtd->oobsize - anand->ecc_total],
> +				  anand->ecc_bits * step, anand->ecc_bits);
> +
> +		bf = bch_decode(anand->bch, raw_buf, chip->ecc.size,
> +				anand->hw_ecc, NULL, NULL, anand->errloc);
> +		if (!bf) {
> +			continue;
> +		} else if (bf > 0) {
> +			for (i = 0; i < bf; i++) {
> +				/* Only correct the data, not the syndrome */
> +				if (anand->errloc[i] < (chip->ecc.size * 8)) {
> +					bit = BIT(anand->errloc[i] & 7);
> +					byte = anand->errloc[i] >> 3;
> +					raw_buf[byte] ^= bit;
> +				}
> +			}
> +
> +			mtd->ecc_stats.corrected += bf;
> +			max_bitflips = max_t(unsigned int, max_bitflips, bf);
> +
> +			continue;
> +		}
> +
> +		bf = nand_check_erased_ecc_chunk(raw_buf, chip->ecc.size,
> +						 NULL, 0, NULL, 0,
> +						 chip->ecc.strength);
> +		if (bf > 0) {
> +			mtd->ecc_stats.corrected += bf;
> +			max_bitflips = max_t(unsigned int, max_bitflips, bf);
> +			memset(raw_buf, 0xFF, chip->ecc.size);
> +		} else if (bf < 0) {
> +			mtd->ecc_stats.failed++;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int anfc_write_page_hw_ecc(struct nand_chip *chip, const u8 *buf,
> +				  int oob_required, int page)
> +{
> +	struct anand *anand = to_anand(chip);
> +	struct arasan_nfc *nfc = to_anfc(chip->controller);
> +	struct mtd_info *mtd = nand_to_mtd(chip);
> +	unsigned int len = mtd->writesize + (oob_required ? mtd->oobsize : 0);
> +	dma_addr_t paddr;
> +	int ret;
> +	struct anfc_op nfc_op = {
> +		.pkt_reg =
> +			PKT_SIZE(chip->ecc.size) |
> +			PKT_STEPS(chip->ecc.steps),
> +		.addr1_reg =
> +			(page & 0xFF) << (8 * (anand->caddr_cycles)) |
> +			(((page >> 8) & 0xFF) << (8 * (1 + anand->caddr_cycles))),
> +		.addr2_reg =
> +			((page >> 16) & 0xFF) |
> +			ADDR2_STRENGTH(anand->strength) |
> +			ADDR2_CS(anand->cs),
> +		.cmd_reg =
> +			CMD_1(NAND_CMD_SEQIN) |
> +			CMD_2(NAND_CMD_PAGEPROG) |
> +			CMD_PAGE_SIZE(anand->page_sz) |
> +			CMD_DMA_ENABLE |
> +			CMD_NADDRS(anand->caddr_cycles +
> +				   anand->raddr_cycles) |
> +			CMD_ECC_ENABLE,
> +		.prog_reg = PROG_PGPROG,
> +	};
> +
> +	writel_relaxed(anand->ecc_conf, nfc->base + ECC_CONF_REG);
> +	writel_relaxed(ECC_SP_CMD1(NAND_CMD_RNDIN) |
> +		       ECC_SP_ADDRS(anand->caddr_cycles),
> +		       nfc->base + ECC_SP_REG);
> +
> +	paddr = dma_map_single(nfc->dev, (void *)buf, len, DMA_TO_DEVICE);
> +	if (dma_mapping_error(nfc->dev, paddr)) {
> +		dev_err(nfc->dev, "Buffer mapping error");
> +		return -EIO;
> +	}
> +
> +	writel_relaxed(paddr, nfc->base + DMA_ADDR0_REG);
> +	writel_relaxed((paddr >> 32), nfc->base + DMA_ADDR1_REG);
> +
> +	anfc_trigger_op(nfc, &nfc_op);
> +	ret = anfc_wait_for_event(nfc, XFER_COMPLETE);
> +	dma_unmap_single(nfc->dev, paddr, len, DMA_TO_DEVICE);
> +	if (ret) {
> +		dev_err(nfc->dev, "Error writing page %d\n", page);
> +		return ret;
> +	}
> +
> +	/* Spare data is not protected */
> +	if (oob_required)
> +		ret = nand_write_oob_std(chip, page);
> +
> +	return ret;
> +}
> +
>  /* NAND framework ->exec_op() hooks and related helpers */
>  static int anfc_parse_instructions(struct nand_chip *chip,
>  				   const struct nand_subop *subop,
> @@ -681,6 +880,138 @@ static int anfc_setup_data_interface(struct nand_chip *chip, int target,
>  	return 0;
>  }
>  
> +static int anfc_calc_hw_ecc_bytes(int step_size, int strength)
> +{
> +	unsigned int bch_gf_mag, ecc_bits;
> +
> +	switch (step_size) {
> +	case SZ_512:
> +		bch_gf_mag = 13;
> +		break;
> +	case SZ_1K:
> +		bch_gf_mag = 14;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	ecc_bits = bch_gf_mag * strength;
> +
> +	return DIV_ROUND_UP(ecc_bits, 8);
> +}
> +
> +static const int anfc_hw_ecc_512_strengths[] = {4, 8, 12};
> +
> +static const int anfc_hw_ecc_1024_strengths[] = {24};
> +
> +static const struct nand_ecc_step_info anfc_hw_ecc_step_infos[] = {
> +	{
> +		.stepsize = SZ_512,
> +		.strengths = anfc_hw_ecc_512_strengths,
> +		.nstrengths = ARRAY_SIZE(anfc_hw_ecc_512_strengths),
> +	},
> +	{
> +		.stepsize = SZ_1K,
> +		.strengths = anfc_hw_ecc_1024_strengths,
> +		.nstrengths = ARRAY_SIZE(anfc_hw_ecc_1024_strengths),
> +	},
> +};
> +
> +static const struct nand_ecc_caps anfc_hw_ecc_caps = {
> +	.stepinfos = anfc_hw_ecc_step_infos,
> +	.nstepinfos = ARRAY_SIZE(anfc_hw_ecc_step_infos),
> +	.calc_ecc_bytes = anfc_calc_hw_ecc_bytes,
> +};
> +
> +static int anfc_init_hw_ecc_controller(struct arasan_nfc *nfc,
> +				       struct nand_chip *chip)
> +{
> +	struct anand *anand = to_anand(chip);
> +	struct mtd_info *mtd = nand_to_mtd(chip);
> +	struct nand_ecc_ctrl *ecc = &chip->ecc;
> +	unsigned int bch_prim_poly = 0, bch_gf_mag = 0, ecc_offset;
> +	int ret;
> +
> +	switch (mtd->writesize) {
> +	case SZ_512:
> +	case SZ_2K:
> +	case SZ_4K:
> +	case SZ_8K:
> +	case SZ_16K:
> +		break;
> +	default:
> +		dev_err(nfc->dev, "Unsupported page size %d\n", mtd->writesize);
> +		return -EINVAL;
> +	}
> +
> +	ret = nand_ecc_choose_conf(chip, &anfc_hw_ecc_caps, mtd->oobsize);
> +	if (ret)
> +		return ret;
> +
> +	switch (ecc->strength) {
> +	case 12:
> +		anand->strength = 0x1;
> +		break;
> +	case 8:
> +		anand->strength = 0x2;
> +		break;
> +	case 4:
> +		anand->strength = 0x3;
> +		break;
> +	case 24:
> +		anand->strength = 0x4;
> +		break;
> +	default:
> +		dev_err(nfc->dev, "Unsupported strength %d\n", ecc->strength);
> +		return -EINVAL;
> +	}
> +
> +	switch (ecc->size) {
> +	case SZ_512:
> +		bch_gf_mag = 13;
> +		bch_prim_poly = 0x201b;
> +		break;
> +	case SZ_1K:
> +		bch_gf_mag = 14;
> +		bch_prim_poly = 0x4443;
> +		break;
> +	default:
> +		dev_err(nfc->dev, "Unsupported step size %d\n", ecc->strength);
> +		return -EINVAL;
> +	}
> +
> +	mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
> +
> +	ecc->steps = mtd->writesize / ecc->size;
> +	ecc->algo = NAND_ECC_BCH;
> +	anand->ecc_bits = bch_gf_mag * ecc->strength;
> +	ecc->bytes = DIV_ROUND_UP(anand->ecc_bits, 8);
> +	anand->ecc_total = DIV_ROUND_UP(anand->ecc_bits * ecc->steps, 8);
> +	ecc_offset = mtd->writesize + mtd->oobsize - anand->ecc_total;
> +	anand->ecc_conf = ECC_CONF_COL(ecc_offset) |
> +			  ECC_CONF_LEN(anand->ecc_total) |
> +			  ECC_CONF_BCH_EN;
> +
> +	anand->errloc = devm_kmalloc_array(nfc->dev, ecc->strength,
> +					   sizeof(*anand->errloc), GFP_KERNEL);
> +	if (!anand->errloc)
> +		return -ENOMEM;
> +
> +	anand->hw_ecc = devm_kmalloc(nfc->dev, ecc->bytes, GFP_KERNEL);
> +	if (!anand->hw_ecc)
> +		return -ENOMEM;
> +
> +	/* Enforce bit swapping to fit the hardware */
> +	anand->bch = bch_init(bch_gf_mag, ecc->strength, bch_prim_poly, true);
> +	if (!anand->bch)
> +		return -EINVAL;
> +
> +	ecc->read_page = anfc_read_page_hw_ecc;
> +	ecc->write_page = anfc_write_page_hw_ecc;
> +
> +	return 0;
> +}
> +
>  static int anfc_attach_chip(struct nand_chip *chip)
>  {
>  	struct anand *anand = to_anand(chip);
> @@ -731,6 +1062,8 @@ static int anfc_attach_chip(struct nand_chip *chip)
>  	case NAND_ECC_ON_DIE:
>  		break;
>  	case NAND_ECC_HW:
> +		ret = anfc_init_hw_ecc_controller(nfc, chip);
> +		break;
>  	default:
>  		dev_err(nfc->dev, "Unsupported ECC mode: %d\n",
>  			chip->ecc.mode);
> @@ -740,10 +1073,19 @@ static int anfc_attach_chip(struct nand_chip *chip)
>  	return ret;
>  }
>  
> +static void anfc_detach_chip(struct nand_chip *chip)
> +{
> +	struct anand *anand = to_anand(chip);
> +
> +	if (anand->bch)
> +		bch_free(anand->bch);
> +}
> +
>  static const struct nand_controller_ops anfc_ops = {
>  	.exec_op = anfc_exec_op,
>  	.setup_data_interface = anfc_setup_data_interface,
>  	.attach_chip = anfc_attach_chip,
> +	.detach_chip = anfc_detach_chip,
>  };
>  
>  static int anfc_chip_init(struct arasan_nfc *nfc, struct device_node *np)


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* Re: [PATCH v5 4/8] mtd: rawnand: Add nand_extract_bits()
  2020-05-19  8:48   ` Boris Brezillon
@ 2020-05-19 12:15     ` Miquel Raynal
  0 siblings, 0 replies; 23+ messages in thread
From: Miquel Raynal @ 2020-05-19 12:15 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Michal Simek, Vignesh Raghavendra, Tudor Ambarus,
	Richard Weinberger, linux-mtd, Thomas Petazzoni,
	Naga Sureshkumar Relli

Hi Boris,

Boris Brezillon <boris.brezillon@collabora.com> wrote on Tue, 19 May
2020 10:48:27 +0200:

> On Tue, 19 May 2020 09:45:45 +0200
> Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> 
> > There are cases where ECC bytes are not byte-aligned. Indeed, BCH
> > implies using a number of ECC bits, which are not always a multiple of
> > 8. We then need a helper like nand_extract_bits() to extract these
> > syndromes from a buffer.
> > 
> > Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> > ---
> >  drivers/mtd/nand/raw/nand_base.c | 42 ++++++++++++++++++++++++++++++++
> >  include/linux/mtd/rawnand.h      |  4 +++
> >  2 files changed, 46 insertions(+)
> > 
> > diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
> > index 65e9b2fa2fc5..14387b967e8b 100644
> > --- a/drivers/mtd/nand/raw/nand_base.c
> > +++ b/drivers/mtd/nand/raw/nand_base.c
> > @@ -274,6 +274,48 @@ static int check_offs_len(struct nand_chip *chip, loff_t ofs, uint64_t len)
> >  	return ret;
> >  }
> >  
> > +/** nand_extract_bits - Copy unaligned bits from one buffer to another one  
> 
> It should be
> 
> /**
>  * nand_extract_bits - Copy unaligned bits from one buffer to another
> 
> Once addressed you can add
> 
> Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>

Oh yeah, right.

I'll fix it when applying.

> 
> If someone reads that and has enough energy, it'd be great to have a
> generic bitcpy() function (looks like the fbdev [1] subsystem has a
> private implementation too).

Absolutely!

> 
> > + * @dst: destination buffer
> > + * @dst_off: bit offset at which the writing starts
> > + * @src: source buffer
> > + * @src_off: bit offset at which the reading starts
> > + * @nbits: number of bits to copy from @src to @dst
> > + *
> > + * Copy bits from one memory region to another (overlap authorized).
> > + */
> > +void nand_extract_bits(u8 *dst, unsigned int dst_off, const u8 *src,
> > +		       unsigned int src_off, unsigned int nbits)
> > +{
> > +	unsigned int tmp, n;
> > +
> > +	dst += dst_off / 8;
> > +	dst_off %= 8;
> > +	src += src_off / 8;
> > +	src_off %= 8;
> > +
> > +	while (nbits) {
> > +		n = min3(8 - dst_off, 8 - src_off, nbits);
> > +
> > +		tmp = (*src >> src_off) & GENMASK(n - 1, 0);
> > +		*dst &= ~GENMASK(n - 1 + dst_off, dst_off);
> > +		*dst |= tmp << dst_off;
> > +
> > +		dst_off += n;
> > +		if (dst_off >= 8) {
> > +			dst++;
> > +			dst_off -= 8;
> > +		}
> > +
> > +		src_off += n;
> > +		if (src_off >= 8) {
> > +			src++;
> > +			src_off -= 8;
> > +		}
> > +
> > +		nbits -= n;
> > +	}
> > +}
> > +
> >  /**
> >   * nand_select_target() - Select a NAND target (A.K.A. die)
> >   * @chip: NAND chip object
> > diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h
> > index 0f45b6984ad1..45dd57e2a223 100644
> > --- a/include/linux/mtd/rawnand.h
> > +++ b/include/linux/mtd/rawnand.h
> > @@ -1412,6 +1412,10 @@ int nand_gpio_waitrdy(struct nand_chip *chip, struct gpio_desc *gpiod,
> >  void nand_select_target(struct nand_chip *chip, unsigned int cs);
> >  void nand_deselect_target(struct nand_chip *chip);
> >  
> > +/* Bitops */
> > +void nand_extract_bits(u8 *dst, unsigned int dst_off, const u8 *src,
> > +		       unsigned int src_off, unsigned int nbits);
> > +
> >  /**
> >   * nand_get_data_buf() - Get the internal page buffer
> >   * @chip: NAND chip object  
> 
> [1]https://elixir.bootlin.com/linux/latest/source/drivers/video/fbdev/amifb.c#L2598

Thanks,
Miquèl

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* Re: [PATCH v5 8/8] mtd: rawnand: arasan: Support the hardware BCH ECC engine
  2020-05-19  7:45 ` [PATCH v5 8/8] mtd: rawnand: arasan: Support the hardware BCH ECC engine Miquel Raynal
  2020-05-19  8:51   ` Boris Brezillon
@ 2020-05-24 19:09   ` Miquel Raynal
  1 sibling, 0 replies; 23+ messages in thread
From: Miquel Raynal @ 2020-05-24 19:09 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Tudor Ambarus, linux-mtd
  Cc: Michal Simek, Boris Brezillon, Naga Sureshkumar Relli, Thomas Petazzoni

On Tue, 2020-05-19 at 07:45:49 UTC, Miquel Raynal wrote:
> Add support for the hardware ECC BCH engine.
> 
> Please mind that this engine has an important limitation:
> BCH implementation does not inform the user when an uncorrectable ECC
> error occurs. To workaround this, we avoid using the hardware engine
> in the read path and do the computation with the software BCH
> implementation, which is faster than mixing hardware (for correction)
> and software (for verification).
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>

Applied to https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git nand/next.

Miquel

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* Re: [PATCH v5 7/8] mtd: rawnand: arasan: Add new Arasan NAND controller
  2020-05-19  7:45 ` [PATCH v5 7/8] mtd: rawnand: arasan: Add new Arasan NAND controller Miquel Raynal
  2020-05-19  8:50   ` Boris Brezillon
@ 2020-05-24 19:09   ` Miquel Raynal
  1 sibling, 0 replies; 23+ messages in thread
From: Miquel Raynal @ 2020-05-24 19:09 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Tudor Ambarus, linux-mtd
  Cc: Michal Simek, Boris Brezillon, Naga Sureshkumar Relli, Thomas Petazzoni

On Tue, 2020-05-19 at 07:45:48 UTC, Miquel Raynal wrote:
> Add the Arasan NAND controller driver. This brings only NAND
> controller support. The ECC engine being a bit subtle, hardware ECC
> support will be added in a second time.
> 
> This work is based on contributions from Naga Sureshkumar Relli.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>

Applied to https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git nand/next.

Miquel

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* Re: [PATCH v5 6/8] dt-bindings: mtd: Document ARASAN NAND bindings
  2020-05-19  7:45 ` [PATCH v5 6/8] dt-bindings: mtd: Document ARASAN NAND bindings Miquel Raynal
@ 2020-05-24 19:09   ` Miquel Raynal
  0 siblings, 0 replies; 23+ messages in thread
From: Miquel Raynal @ 2020-05-24 19:09 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Tudor Ambarus, linux-mtd
  Cc: Rob Herring, Michal Simek, Boris Brezillon,
	Naga Sureshkumar Relli, Thomas Petazzoni

On Tue, 2020-05-19 at 07:45:47 UTC, Miquel Raynal wrote:
> Document the Arasan NAND controller bindings.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> Reviewed-by: Rob Herring <robh@kernel.org>

Applied to https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git nand/next.

Miquel

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* Re: [PATCH v5 5/8] MAINTAINERS: Add Arasan NAND controller and bindings
  2020-05-19  7:45 ` [PATCH v5 5/8] MAINTAINERS: Add Arasan NAND controller and bindings Miquel Raynal
@ 2020-05-24 19:09   ` Miquel Raynal
  0 siblings, 0 replies; 23+ messages in thread
From: Miquel Raynal @ 2020-05-24 19:09 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Tudor Ambarus, linux-mtd
  Cc: Michal Simek, Boris Brezillon, Naga Sureshkumar Relli, Thomas Petazzoni

On Tue, 2020-05-19 at 07:45:46 UTC, Miquel Raynal wrote:
> Fill a new entry for the Arasan NAND controller.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>

Applied to https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git nand/next.

Miquel

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* Re: [PATCH v5 4/8] mtd: rawnand: Add nand_extract_bits()
  2020-05-19  7:45 ` [PATCH v5 4/8] mtd: rawnand: Add nand_extract_bits() Miquel Raynal
  2020-05-19  8:48   ` Boris Brezillon
@ 2020-05-24 19:09   ` Miquel Raynal
  1 sibling, 0 replies; 23+ messages in thread
From: Miquel Raynal @ 2020-05-24 19:09 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Tudor Ambarus, linux-mtd
  Cc: Michal Simek, Boris Brezillon, Naga Sureshkumar Relli, Thomas Petazzoni

On Tue, 2020-05-19 at 07:45:45 UTC, Miquel Raynal wrote:
> There are cases where ECC bytes are not byte-aligned. Indeed, BCH
> implies using a number of ECC bits, which are not always a multiple of
> 8. We then need a helper like nand_extract_bits() to extract these
> syndromes from a buffer.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>

Applied to https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git nand/next.

Miquel

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* Re: [PATCH v5 3/8] mtd: rawnand: Ensure the number of bitflips is consistent
  2020-05-19  7:45 ` [PATCH v5 3/8] mtd: rawnand: Ensure the number of bitflips is consistent Miquel Raynal
  2020-05-19  8:42   ` Boris Brezillon
@ 2020-05-24 19:09   ` Miquel Raynal
  1 sibling, 0 replies; 23+ messages in thread
From: Miquel Raynal @ 2020-05-24 19:09 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Tudor Ambarus, linux-mtd
  Cc: Michal Simek, Boris Brezillon, Naga Sureshkumar Relli, Thomas Petazzoni

On Tue, 2020-05-19 at 07:45:44 UTC, Miquel Raynal wrote:
> The main NAND read page function can loop over "page reads" many times
> in if the reading reports uncorrectable error(s) and if the chip
> supports the read_retry feature.
> 
> In this case, the number of bitflips is summarized between
> attempts. Fix this by re-initializing the entire mtd_ecc_stats object
> each time we retry.
> 
> Suggested-by: Boris Brezillon <boris.brezillon@collabora.com>
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>

Applied to https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git nand/next.

Miquel

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* Re: [PATCH v5 2/8] lib/bch: Allow easy bit swapping
  2020-05-19  7:45 ` [PATCH v5 2/8] lib/bch: Allow easy bit swapping Miquel Raynal
  2020-05-19  8:41   ` Boris Brezillon
@ 2020-05-24 19:09   ` Miquel Raynal
  1 sibling, 0 replies; 23+ messages in thread
From: Miquel Raynal @ 2020-05-24 19:09 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Tudor Ambarus, linux-mtd
  Cc: Michal Simek, Boris Brezillon, Naga Sureshkumar Relli, Thomas Petazzoni

On Tue, 2020-05-19 at 07:45:43 UTC, Miquel Raynal wrote:
> It seems that several hardware ECC engine use a swapped representation
> of bytes compared to software. This might having to do with how the
> ECC engine is wired to the NAND controller or the order the bits are
> passed to the hardware BCH logic.
> 
> This means that when the software BCH engine is working in conjunction
> with data generated with hardware, sometimes we might need to swap the
> bits inside bytes, eg:
> 
>     0x0A = b0000_1010 -> b0101_0000 = 0x50
> 
> Make it possible by adding a boolean to the BCH initialization routine.
> 
> Regarding the implementation itself, this is a rather simple approach
> that can probably be enhanced in the future by preparing the
> ->a_{mod,pow}_tab tables with the swapping in mind.
> 
> Suggested-by: Boris Brezillon <boris.brezillon@collabora.com>
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>

Applied to https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git nand/next.

Miquel

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

* Re: [PATCH v5 1/8] lib/bch: Rework a little bit the exported function names
  2020-05-19  7:45 ` [PATCH v5 1/8] lib/bch: Rework a little bit the exported function names Miquel Raynal
@ 2020-05-24 19:10   ` Miquel Raynal
  0 siblings, 0 replies; 23+ messages in thread
From: Miquel Raynal @ 2020-05-24 19:10 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Tudor Ambarus, linux-mtd
  Cc: Michal Simek, Boris Brezillon, Naga Sureshkumar Relli, Thomas Petazzoni

On Tue, 2020-05-19 at 07:45:42 UTC, Miquel Raynal wrote:
> There are four exported functions, all suffixed by _bch, which is
> clearly not the norm. Let's rename them by prefixing them with bch_
> instead.
> 
> This is a mechanical change:
>     init_bch -> bch_init
>     free_bch -> bch_free
>     encode_bch -> bch_encode
>     decode_bch -> bch_decode
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>

Applied to https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git nand/next.

Miquel

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

end of thread, other threads:[~2020-05-24 19:24 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-19  7:45 [PATCH v5 0/8] New Arasan NAND controller driver Miquel Raynal
2020-05-19  7:45 ` [PATCH v5 1/8] lib/bch: Rework a little bit the exported function names Miquel Raynal
2020-05-24 19:10   ` Miquel Raynal
2020-05-19  7:45 ` [PATCH v5 2/8] lib/bch: Allow easy bit swapping Miquel Raynal
2020-05-19  8:41   ` Boris Brezillon
2020-05-24 19:09   ` Miquel Raynal
2020-05-19  7:45 ` [PATCH v5 3/8] mtd: rawnand: Ensure the number of bitflips is consistent Miquel Raynal
2020-05-19  8:42   ` Boris Brezillon
2020-05-24 19:09   ` Miquel Raynal
2020-05-19  7:45 ` [PATCH v5 4/8] mtd: rawnand: Add nand_extract_bits() Miquel Raynal
2020-05-19  8:48   ` Boris Brezillon
2020-05-19 12:15     ` Miquel Raynal
2020-05-24 19:09   ` Miquel Raynal
2020-05-19  7:45 ` [PATCH v5 5/8] MAINTAINERS: Add Arasan NAND controller and bindings Miquel Raynal
2020-05-24 19:09   ` Miquel Raynal
2020-05-19  7:45 ` [PATCH v5 6/8] dt-bindings: mtd: Document ARASAN NAND bindings Miquel Raynal
2020-05-24 19:09   ` Miquel Raynal
2020-05-19  7:45 ` [PATCH v5 7/8] mtd: rawnand: arasan: Add new Arasan NAND controller Miquel Raynal
2020-05-19  8:50   ` Boris Brezillon
2020-05-24 19:09   ` Miquel Raynal
2020-05-19  7:45 ` [PATCH v5 8/8] mtd: rawnand: arasan: Support the hardware BCH ECC engine Miquel Raynal
2020-05-19  8:51   ` Boris Brezillon
2020-05-24 19:09   ` Miquel Raynal

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).