All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/5] refactoring and fix for Meson NAND
@ 2023-05-15  9:44 ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-15  9:44 UTC (permalink / raw)
  To: Liang Yang, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Jianxin Pan, Yixun Lan
  Cc: oxffffaa, kernel, Arseniy Krasnov, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

Hello,

this patchset does several things:

1) It fixes unstable behaviour of Meson driver, for example random ECC
   errors during reads. It is done by changing 'meson_nfc_queue_rb()'
   implementation. Sequence of commands inside this function is updated.
   This patch is suggested by Liang Yang <liang.yang@amlogic.com>.

   Here is link to discussion:
   https://lore.kernel.org/linux-mtd/a9f8307a-77d7-a69f-ce11-2629909172d2@sberdevices.ru/T/#ma8097bad0228f81a3d11a14389cdec44f45b86c8

2) It moves OOB free bytes to non-protected by ECC area. Here are some
   details:

   Current OOB free bytes are 4 bytes (2 x 2 user bytes) under ECC engine.
   Here is how it looks like in the current implementation:

   [ 2B user bytes ][     14B ECC codes    ]
   [ 2B user bytes ][     14B ECC codes    ]
   [ 16B unused area, not protected by ECC ]
   [ 16B unused area, not protected by ECC ]

   All 4 user bytes are protected by ECC. This patch changes OOB free
   bytes in this way:

   [ 2B unused area ][     14B ECC codes     ]
   [ 2B unused area ][     14B ECC codes     ]
   [  16B user bytes, not protected by ECC   ]
   [  16B user bytes, not protected by ECC   ]

   Now OOB user bytes are 32 bytes instead of 4 bytes and not protected
   by ECC.

   Motivation of this layout comes from problem with JFFS2. It uses OOB
   free bytes for cleanmarkers. Each cleanmarker is 4 bytes and written
   by JFFS2 driver (small remark - cleanmarkers are always written in
   case of NAND storage for JFFS2).
   We have two ways to write this data to OOB (e.g. user bytes):

   1) ECC mode. In this case it will be ECC covered user bytes, e.g.
      writing this bytes will update ECC codes. Problem fires, when
      JFFS2 tries to write this page later - this write makes controller
      to update ECC codes again, but it is impossible to do it correctly,
      because we can't update bits from 0 to 1 (only from 1 to 0).

   2) Raw mode. In this case ECC codes won't be updated. But later, it
      will be impossible to read this page in ECC mode, because we have
      some user bytes, but ECC codes are missed.

   So let's move OOB free bytes out of ECC area. In this case we can
   read/write OOB separately in raw mode and at the same time work with
   data in ECC mode. JFFS2 is happy now. User bytes are untouched - all
   of them are ignored during non-OOB access.

   I've tested this with mount/unmount/read/write cases for JFFS2 and
   nanddump/nandwrite utlities on AXG family (A113X SoC).

   Here is link to discussion:
   https://lore.kernel.org/linux-mtd/a9f8307a-77d7-a69f-ce11-2629909172d2@sberdevices.ru/T/#m3087bd06386a7f430cd5e343e22b25d724d3e2d7

3) Changes size of OOB read access - now, in both ECC and raw modes whole
   OOB (e.g. 64 bytes) is returned to the caller.

4) Checks buffer length on accesses to NAND controller.

5) Removes useless bitwise OR with zeroes.

Link to v1:
https://lore.kernel.org/linux-mtd/20230412061700.1492474-1-AVKrasnov@sberdevices.ru/
Link to v2:
https://lore.kernel.org/linux-mtd/20230426073632.3905682-1-AVKrasnov@sberdevices.ru/
Link to v3:
https://lore.kernel.org/linux-mtd/20230510110835.26115-1-AVKrasnov@sberdevices.ru/

Changelog:

v1 -> v2:
 * Add patch which renames dts value for chip select.
 * Add patch which moves OOB to non-protected ECC area.
v2 -> v3:
 * Change patch which fixes read/write access according discussion link
   in 1) above.
v3 -> v4:
 * Remove patch which renames dts value for chip select.
   Here is link to discussion:
   https://lore.kernel.org/linux-mtd/20230510110835.26115-7-AVKrasnov@sberdevices.ru/
 * Pass 1 to 'meson_nfc_queue_rb()' in case of NAND_OP_WAITRDY_INSTR.
   This fixes ONFI page processing during NAND driver initialization.

Arseniy Krasnov (5):
  mtd: rawnand: meson: fix command sequence for read/write
  mtd: rawnand: meson: move OOB to non-protected ECC area
  mtd: rawnand: meson: always read whole OOB bytes
  mtd: rawnand: meson: check buffer length
  mtd: rawnand: meson: remove unneeded bitwise OR with zeroes

 drivers/mtd/nand/raw/meson_nand.c | 270 ++++++++++++++++++++++++------
 1 file changed, 220 insertions(+), 50 deletions(-)

-- 
2.35.0


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

* [PATCH v4 0/5] refactoring and fix for Meson NAND
@ 2023-05-15  9:44 ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-15  9:44 UTC (permalink / raw)
  To: Liang Yang, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Jianxin Pan, Yixun Lan
  Cc: oxffffaa, kernel, Arseniy Krasnov, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

Hello,

this patchset does several things:

1) It fixes unstable behaviour of Meson driver, for example random ECC
   errors during reads. It is done by changing 'meson_nfc_queue_rb()'
   implementation. Sequence of commands inside this function is updated.
   This patch is suggested by Liang Yang <liang.yang@amlogic.com>.

   Here is link to discussion:
   https://lore.kernel.org/linux-mtd/a9f8307a-77d7-a69f-ce11-2629909172d2@sberdevices.ru/T/#ma8097bad0228f81a3d11a14389cdec44f45b86c8

2) It moves OOB free bytes to non-protected by ECC area. Here are some
   details:

   Current OOB free bytes are 4 bytes (2 x 2 user bytes) under ECC engine.
   Here is how it looks like in the current implementation:

   [ 2B user bytes ][     14B ECC codes    ]
   [ 2B user bytes ][     14B ECC codes    ]
   [ 16B unused area, not protected by ECC ]
   [ 16B unused area, not protected by ECC ]

   All 4 user bytes are protected by ECC. This patch changes OOB free
   bytes in this way:

   [ 2B unused area ][     14B ECC codes     ]
   [ 2B unused area ][     14B ECC codes     ]
   [  16B user bytes, not protected by ECC   ]
   [  16B user bytes, not protected by ECC   ]

   Now OOB user bytes are 32 bytes instead of 4 bytes and not protected
   by ECC.

   Motivation of this layout comes from problem with JFFS2. It uses OOB
   free bytes for cleanmarkers. Each cleanmarker is 4 bytes and written
   by JFFS2 driver (small remark - cleanmarkers are always written in
   case of NAND storage for JFFS2).
   We have two ways to write this data to OOB (e.g. user bytes):

   1) ECC mode. In this case it will be ECC covered user bytes, e.g.
      writing this bytes will update ECC codes. Problem fires, when
      JFFS2 tries to write this page later - this write makes controller
      to update ECC codes again, but it is impossible to do it correctly,
      because we can't update bits from 0 to 1 (only from 1 to 0).

   2) Raw mode. In this case ECC codes won't be updated. But later, it
      will be impossible to read this page in ECC mode, because we have
      some user bytes, but ECC codes are missed.

   So let's move OOB free bytes out of ECC area. In this case we can
   read/write OOB separately in raw mode and at the same time work with
   data in ECC mode. JFFS2 is happy now. User bytes are untouched - all
   of them are ignored during non-OOB access.

   I've tested this with mount/unmount/read/write cases for JFFS2 and
   nanddump/nandwrite utlities on AXG family (A113X SoC).

   Here is link to discussion:
   https://lore.kernel.org/linux-mtd/a9f8307a-77d7-a69f-ce11-2629909172d2@sberdevices.ru/T/#m3087bd06386a7f430cd5e343e22b25d724d3e2d7

3) Changes size of OOB read access - now, in both ECC and raw modes whole
   OOB (e.g. 64 bytes) is returned to the caller.

4) Checks buffer length on accesses to NAND controller.

5) Removes useless bitwise OR with zeroes.

Link to v1:
https://lore.kernel.org/linux-mtd/20230412061700.1492474-1-AVKrasnov@sberdevices.ru/
Link to v2:
https://lore.kernel.org/linux-mtd/20230426073632.3905682-1-AVKrasnov@sberdevices.ru/
Link to v3:
https://lore.kernel.org/linux-mtd/20230510110835.26115-1-AVKrasnov@sberdevices.ru/

Changelog:

v1 -> v2:
 * Add patch which renames dts value for chip select.
 * Add patch which moves OOB to non-protected ECC area.
v2 -> v3:
 * Change patch which fixes read/write access according discussion link
   in 1) above.
v3 -> v4:
 * Remove patch which renames dts value for chip select.
   Here is link to discussion:
   https://lore.kernel.org/linux-mtd/20230510110835.26115-7-AVKrasnov@sberdevices.ru/
 * Pass 1 to 'meson_nfc_queue_rb()' in case of NAND_OP_WAITRDY_INSTR.
   This fixes ONFI page processing during NAND driver initialization.

Arseniy Krasnov (5):
  mtd: rawnand: meson: fix command sequence for read/write
  mtd: rawnand: meson: move OOB to non-protected ECC area
  mtd: rawnand: meson: always read whole OOB bytes
  mtd: rawnand: meson: check buffer length
  mtd: rawnand: meson: remove unneeded bitwise OR with zeroes

 drivers/mtd/nand/raw/meson_nand.c | 270 ++++++++++++++++++++++++------
 1 file changed, 220 insertions(+), 50 deletions(-)

-- 
2.35.0


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

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

* [PATCH v4 0/5] refactoring and fix for Meson NAND
@ 2023-05-15  9:44 ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-15  9:44 UTC (permalink / raw)
  To: Liang Yang, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Jianxin Pan, Yixun Lan
  Cc: oxffffaa, kernel, Arseniy Krasnov, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

Hello,

this patchset does several things:

1) It fixes unstable behaviour of Meson driver, for example random ECC
   errors during reads. It is done by changing 'meson_nfc_queue_rb()'
   implementation. Sequence of commands inside this function is updated.
   This patch is suggested by Liang Yang <liang.yang@amlogic.com>.

   Here is link to discussion:
   https://lore.kernel.org/linux-mtd/a9f8307a-77d7-a69f-ce11-2629909172d2@sberdevices.ru/T/#ma8097bad0228f81a3d11a14389cdec44f45b86c8

2) It moves OOB free bytes to non-protected by ECC area. Here are some
   details:

   Current OOB free bytes are 4 bytes (2 x 2 user bytes) under ECC engine.
   Here is how it looks like in the current implementation:

   [ 2B user bytes ][     14B ECC codes    ]
   [ 2B user bytes ][     14B ECC codes    ]
   [ 16B unused area, not protected by ECC ]
   [ 16B unused area, not protected by ECC ]

   All 4 user bytes are protected by ECC. This patch changes OOB free
   bytes in this way:

   [ 2B unused area ][     14B ECC codes     ]
   [ 2B unused area ][     14B ECC codes     ]
   [  16B user bytes, not protected by ECC   ]
   [  16B user bytes, not protected by ECC   ]

   Now OOB user bytes are 32 bytes instead of 4 bytes and not protected
   by ECC.

   Motivation of this layout comes from problem with JFFS2. It uses OOB
   free bytes for cleanmarkers. Each cleanmarker is 4 bytes and written
   by JFFS2 driver (small remark - cleanmarkers are always written in
   case of NAND storage for JFFS2).
   We have two ways to write this data to OOB (e.g. user bytes):

   1) ECC mode. In this case it will be ECC covered user bytes, e.g.
      writing this bytes will update ECC codes. Problem fires, when
      JFFS2 tries to write this page later - this write makes controller
      to update ECC codes again, but it is impossible to do it correctly,
      because we can't update bits from 0 to 1 (only from 1 to 0).

   2) Raw mode. In this case ECC codes won't be updated. But later, it
      will be impossible to read this page in ECC mode, because we have
      some user bytes, but ECC codes are missed.

   So let's move OOB free bytes out of ECC area. In this case we can
   read/write OOB separately in raw mode and at the same time work with
   data in ECC mode. JFFS2 is happy now. User bytes are untouched - all
   of them are ignored during non-OOB access.

   I've tested this with mount/unmount/read/write cases for JFFS2 and
   nanddump/nandwrite utlities on AXG family (A113X SoC).

   Here is link to discussion:
   https://lore.kernel.org/linux-mtd/a9f8307a-77d7-a69f-ce11-2629909172d2@sberdevices.ru/T/#m3087bd06386a7f430cd5e343e22b25d724d3e2d7

3) Changes size of OOB read access - now, in both ECC and raw modes whole
   OOB (e.g. 64 bytes) is returned to the caller.

4) Checks buffer length on accesses to NAND controller.

5) Removes useless bitwise OR with zeroes.

Link to v1:
https://lore.kernel.org/linux-mtd/20230412061700.1492474-1-AVKrasnov@sberdevices.ru/
Link to v2:
https://lore.kernel.org/linux-mtd/20230426073632.3905682-1-AVKrasnov@sberdevices.ru/
Link to v3:
https://lore.kernel.org/linux-mtd/20230510110835.26115-1-AVKrasnov@sberdevices.ru/

Changelog:

v1 -> v2:
 * Add patch which renames dts value for chip select.
 * Add patch which moves OOB to non-protected ECC area.
v2 -> v3:
 * Change patch which fixes read/write access according discussion link
   in 1) above.
v3 -> v4:
 * Remove patch which renames dts value for chip select.
   Here is link to discussion:
   https://lore.kernel.org/linux-mtd/20230510110835.26115-7-AVKrasnov@sberdevices.ru/
 * Pass 1 to 'meson_nfc_queue_rb()' in case of NAND_OP_WAITRDY_INSTR.
   This fixes ONFI page processing during NAND driver initialization.

Arseniy Krasnov (5):
  mtd: rawnand: meson: fix command sequence for read/write
  mtd: rawnand: meson: move OOB to non-protected ECC area
  mtd: rawnand: meson: always read whole OOB bytes
  mtd: rawnand: meson: check buffer length
  mtd: rawnand: meson: remove unneeded bitwise OR with zeroes

 drivers/mtd/nand/raw/meson_nand.c | 270 ++++++++++++++++++++++++------
 1 file changed, 220 insertions(+), 50 deletions(-)

-- 
2.35.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* [PATCH v4 0/5] refactoring and fix for Meson NAND
@ 2023-05-15  9:44 ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-15  9:44 UTC (permalink / raw)
  To: Liang Yang, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Jianxin Pan, Yixun Lan
  Cc: oxffffaa, kernel, Arseniy Krasnov, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

Hello,

this patchset does several things:

1) It fixes unstable behaviour of Meson driver, for example random ECC
   errors during reads. It is done by changing 'meson_nfc_queue_rb()'
   implementation. Sequence of commands inside this function is updated.
   This patch is suggested by Liang Yang <liang.yang@amlogic.com>.

   Here is link to discussion:
   https://lore.kernel.org/linux-mtd/a9f8307a-77d7-a69f-ce11-2629909172d2@sberdevices.ru/T/#ma8097bad0228f81a3d11a14389cdec44f45b86c8

2) It moves OOB free bytes to non-protected by ECC area. Here are some
   details:

   Current OOB free bytes are 4 bytes (2 x 2 user bytes) under ECC engine.
   Here is how it looks like in the current implementation:

   [ 2B user bytes ][     14B ECC codes    ]
   [ 2B user bytes ][     14B ECC codes    ]
   [ 16B unused area, not protected by ECC ]
   [ 16B unused area, not protected by ECC ]

   All 4 user bytes are protected by ECC. This patch changes OOB free
   bytes in this way:

   [ 2B unused area ][     14B ECC codes     ]
   [ 2B unused area ][     14B ECC codes     ]
   [  16B user bytes, not protected by ECC   ]
   [  16B user bytes, not protected by ECC   ]

   Now OOB user bytes are 32 bytes instead of 4 bytes and not protected
   by ECC.

   Motivation of this layout comes from problem with JFFS2. It uses OOB
   free bytes for cleanmarkers. Each cleanmarker is 4 bytes and written
   by JFFS2 driver (small remark - cleanmarkers are always written in
   case of NAND storage for JFFS2).
   We have two ways to write this data to OOB (e.g. user bytes):

   1) ECC mode. In this case it will be ECC covered user bytes, e.g.
      writing this bytes will update ECC codes. Problem fires, when
      JFFS2 tries to write this page later - this write makes controller
      to update ECC codes again, but it is impossible to do it correctly,
      because we can't update bits from 0 to 1 (only from 1 to 0).

   2) Raw mode. In this case ECC codes won't be updated. But later, it
      will be impossible to read this page in ECC mode, because we have
      some user bytes, but ECC codes are missed.

   So let's move OOB free bytes out of ECC area. In this case we can
   read/write OOB separately in raw mode and at the same time work with
   data in ECC mode. JFFS2 is happy now. User bytes are untouched - all
   of them are ignored during non-OOB access.

   I've tested this with mount/unmount/read/write cases for JFFS2 and
   nanddump/nandwrite utlities on AXG family (A113X SoC).

   Here is link to discussion:
   https://lore.kernel.org/linux-mtd/a9f8307a-77d7-a69f-ce11-2629909172d2@sberdevices.ru/T/#m3087bd06386a7f430cd5e343e22b25d724d3e2d7

3) Changes size of OOB read access - now, in both ECC and raw modes whole
   OOB (e.g. 64 bytes) is returned to the caller.

4) Checks buffer length on accesses to NAND controller.

5) Removes useless bitwise OR with zeroes.

Link to v1:
https://lore.kernel.org/linux-mtd/20230412061700.1492474-1-AVKrasnov@sberdevices.ru/
Link to v2:
https://lore.kernel.org/linux-mtd/20230426073632.3905682-1-AVKrasnov@sberdevices.ru/
Link to v3:
https://lore.kernel.org/linux-mtd/20230510110835.26115-1-AVKrasnov@sberdevices.ru/

Changelog:

v1 -> v2:
 * Add patch which renames dts value for chip select.
 * Add patch which moves OOB to non-protected ECC area.
v2 -> v3:
 * Change patch which fixes read/write access according discussion link
   in 1) above.
v3 -> v4:
 * Remove patch which renames dts value for chip select.
   Here is link to discussion:
   https://lore.kernel.org/linux-mtd/20230510110835.26115-7-AVKrasnov@sberdevices.ru/
 * Pass 1 to 'meson_nfc_queue_rb()' in case of NAND_OP_WAITRDY_INSTR.
   This fixes ONFI page processing during NAND driver initialization.

Arseniy Krasnov (5):
  mtd: rawnand: meson: fix command sequence for read/write
  mtd: rawnand: meson: move OOB to non-protected ECC area
  mtd: rawnand: meson: always read whole OOB bytes
  mtd: rawnand: meson: check buffer length
  mtd: rawnand: meson: remove unneeded bitwise OR with zeroes

 drivers/mtd/nand/raw/meson_nand.c | 270 ++++++++++++++++++++++++------
 1 file changed, 220 insertions(+), 50 deletions(-)

-- 
2.35.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
  2023-05-15  9:44 ` Arseniy Krasnov
  (?)
  (?)
@ 2023-05-15  9:44   ` Arseniy Krasnov
  -1 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-15  9:44 UTC (permalink / raw)
  To: Liang Yang, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Yixun Lan, Jianxin Pan
  Cc: oxffffaa, kernel, Arseniy Krasnov, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

This fixes read/write functionality by:
1) Changing NFC_CMD_RB_INT bit value.
2) Adding extra NAND_CMD_STATUS command on each r/w request.
3) Adding extra idle commands during r/w request.
4) Adding extra NAND_CMD_READ0 on each read request.

Without this patch driver works unstable, for example there are a lot
of ECC errors.

Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
Suggested-by: Liang Yang <liang.yang@amlogic.com>
Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
---
 drivers/mtd/nand/raw/meson_nand.c | 30 +++++++++++++++++++++---------
 1 file changed, 21 insertions(+), 9 deletions(-)

diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
index 074e14225c06..2f4d8c84186b 100644
--- a/drivers/mtd/nand/raw/meson_nand.c
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -37,7 +37,7 @@
 #define NFC_CMD_SCRAMBLER_ENABLE	BIT(19)
 #define NFC_CMD_SCRAMBLER_DISABLE	0
 #define NFC_CMD_SHORTMODE_DISABLE	0
-#define NFC_CMD_RB_INT		BIT(14)
+#define NFC_CMD_RB_INT ((0xb << 10) | BIT(18) | BIT(16))
 
 #define NFC_CMD_GET_SIZE(x)	(((x) >> 22) & GENMASK(4, 0))
 
@@ -392,7 +392,7 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
 	}
 }
 
-static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
+static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
 {
 	u32 cmd, cfg;
 	int ret = 0;
@@ -407,17 +407,29 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
 
 	reinit_completion(&nfc->completion);
 
+	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+	meson_nfc_cmd_idle(nfc, 5);
+
 	/* use the max erase time as the maximum clock for waiting R/B */
-	cmd = NFC_CMD_RB | NFC_CMD_RB_INT
-		| nfc->param.chip_select | nfc->timing.tbers_max;
+	cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
 	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+	meson_nfc_cmd_idle(nfc, 2);
 
 	ret = wait_for_completion_timeout(&nfc->completion,
 					  msecs_to_jiffies(timeout_ms));
 	if (ret == 0)
-		ret = -1;
+		return -1;
 
-	return ret;
+	if (!cmd_read0)
+		return 0;
+
+	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_READ0;
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+	meson_nfc_drain_cmd(nfc);
+	meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
+
+	return 0;
 }
 
 static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
@@ -623,7 +635,7 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
 	if (in) {
 		nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART;
 		writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
-		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max));
+		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max), 1);
 	} else {
 		meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
 	}
@@ -669,7 +681,7 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
 
 	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
 	writel(cmd, nfc->reg_base + NFC_REG_CMD);
-	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
+	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max), 0);
 
 	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
 
@@ -952,7 +964,7 @@ static int meson_nfc_exec_op(struct nand_chip *nand,
 			break;
 
 		case NAND_OP_WAITRDY_INSTR:
-			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms);
+			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms, 1);
 			if (instr->delay_ns)
 				meson_nfc_cmd_idle(nfc, delay_idle);
 			break;
-- 
2.35.0


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

* [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-15  9:44   ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-15  9:44 UTC (permalink / raw)
  To: Liang Yang, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Yixun Lan, Jianxin Pan
  Cc: oxffffaa, kernel, Arseniy Krasnov, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

This fixes read/write functionality by:
1) Changing NFC_CMD_RB_INT bit value.
2) Adding extra NAND_CMD_STATUS command on each r/w request.
3) Adding extra idle commands during r/w request.
4) Adding extra NAND_CMD_READ0 on each read request.

Without this patch driver works unstable, for example there are a lot
of ECC errors.

Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
Suggested-by: Liang Yang <liang.yang@amlogic.com>
Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
---
 drivers/mtd/nand/raw/meson_nand.c | 30 +++++++++++++++++++++---------
 1 file changed, 21 insertions(+), 9 deletions(-)

diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
index 074e14225c06..2f4d8c84186b 100644
--- a/drivers/mtd/nand/raw/meson_nand.c
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -37,7 +37,7 @@
 #define NFC_CMD_SCRAMBLER_ENABLE	BIT(19)
 #define NFC_CMD_SCRAMBLER_DISABLE	0
 #define NFC_CMD_SHORTMODE_DISABLE	0
-#define NFC_CMD_RB_INT		BIT(14)
+#define NFC_CMD_RB_INT ((0xb << 10) | BIT(18) | BIT(16))
 
 #define NFC_CMD_GET_SIZE(x)	(((x) >> 22) & GENMASK(4, 0))
 
@@ -392,7 +392,7 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
 	}
 }
 
-static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
+static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
 {
 	u32 cmd, cfg;
 	int ret = 0;
@@ -407,17 +407,29 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
 
 	reinit_completion(&nfc->completion);
 
+	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+	meson_nfc_cmd_idle(nfc, 5);
+
 	/* use the max erase time as the maximum clock for waiting R/B */
-	cmd = NFC_CMD_RB | NFC_CMD_RB_INT
-		| nfc->param.chip_select | nfc->timing.tbers_max;
+	cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
 	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+	meson_nfc_cmd_idle(nfc, 2);
 
 	ret = wait_for_completion_timeout(&nfc->completion,
 					  msecs_to_jiffies(timeout_ms));
 	if (ret == 0)
-		ret = -1;
+		return -1;
 
-	return ret;
+	if (!cmd_read0)
+		return 0;
+
+	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_READ0;
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+	meson_nfc_drain_cmd(nfc);
+	meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
+
+	return 0;
 }
 
 static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
@@ -623,7 +635,7 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
 	if (in) {
 		nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART;
 		writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
-		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max));
+		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max), 1);
 	} else {
 		meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
 	}
@@ -669,7 +681,7 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
 
 	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
 	writel(cmd, nfc->reg_base + NFC_REG_CMD);
-	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
+	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max), 0);
 
 	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
 
@@ -952,7 +964,7 @@ static int meson_nfc_exec_op(struct nand_chip *nand,
 			break;
 
 		case NAND_OP_WAITRDY_INSTR:
-			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms);
+			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms, 1);
 			if (instr->delay_ns)
 				meson_nfc_cmd_idle(nfc, delay_idle);
 			break;
-- 
2.35.0


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

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

* [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-15  9:44   ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-15  9:44 UTC (permalink / raw)
  To: Liang Yang, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Yixun Lan, Jianxin Pan
  Cc: oxffffaa, kernel, Arseniy Krasnov, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

This fixes read/write functionality by:
1) Changing NFC_CMD_RB_INT bit value.
2) Adding extra NAND_CMD_STATUS command on each r/w request.
3) Adding extra idle commands during r/w request.
4) Adding extra NAND_CMD_READ0 on each read request.

Without this patch driver works unstable, for example there are a lot
of ECC errors.

Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
Suggested-by: Liang Yang <liang.yang@amlogic.com>
Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
---
 drivers/mtd/nand/raw/meson_nand.c | 30 +++++++++++++++++++++---------
 1 file changed, 21 insertions(+), 9 deletions(-)

diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
index 074e14225c06..2f4d8c84186b 100644
--- a/drivers/mtd/nand/raw/meson_nand.c
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -37,7 +37,7 @@
 #define NFC_CMD_SCRAMBLER_ENABLE	BIT(19)
 #define NFC_CMD_SCRAMBLER_DISABLE	0
 #define NFC_CMD_SHORTMODE_DISABLE	0
-#define NFC_CMD_RB_INT		BIT(14)
+#define NFC_CMD_RB_INT ((0xb << 10) | BIT(18) | BIT(16))
 
 #define NFC_CMD_GET_SIZE(x)	(((x) >> 22) & GENMASK(4, 0))
 
@@ -392,7 +392,7 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
 	}
 }
 
-static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
+static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
 {
 	u32 cmd, cfg;
 	int ret = 0;
@@ -407,17 +407,29 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
 
 	reinit_completion(&nfc->completion);
 
+	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+	meson_nfc_cmd_idle(nfc, 5);
+
 	/* use the max erase time as the maximum clock for waiting R/B */
-	cmd = NFC_CMD_RB | NFC_CMD_RB_INT
-		| nfc->param.chip_select | nfc->timing.tbers_max;
+	cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
 	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+	meson_nfc_cmd_idle(nfc, 2);
 
 	ret = wait_for_completion_timeout(&nfc->completion,
 					  msecs_to_jiffies(timeout_ms));
 	if (ret == 0)
-		ret = -1;
+		return -1;
 
-	return ret;
+	if (!cmd_read0)
+		return 0;
+
+	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_READ0;
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+	meson_nfc_drain_cmd(nfc);
+	meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
+
+	return 0;
 }
 
 static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
@@ -623,7 +635,7 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
 	if (in) {
 		nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART;
 		writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
-		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max));
+		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max), 1);
 	} else {
 		meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
 	}
@@ -669,7 +681,7 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
 
 	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
 	writel(cmd, nfc->reg_base + NFC_REG_CMD);
-	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
+	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max), 0);
 
 	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
 
@@ -952,7 +964,7 @@ static int meson_nfc_exec_op(struct nand_chip *nand,
 			break;
 
 		case NAND_OP_WAITRDY_INSTR:
-			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms);
+			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms, 1);
 			if (instr->delay_ns)
 				meson_nfc_cmd_idle(nfc, delay_idle);
 			break;
-- 
2.35.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-15  9:44   ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-15  9:44 UTC (permalink / raw)
  To: Liang Yang, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Yixun Lan, Jianxin Pan
  Cc: oxffffaa, kernel, Arseniy Krasnov, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

This fixes read/write functionality by:
1) Changing NFC_CMD_RB_INT bit value.
2) Adding extra NAND_CMD_STATUS command on each r/w request.
3) Adding extra idle commands during r/w request.
4) Adding extra NAND_CMD_READ0 on each read request.

Without this patch driver works unstable, for example there are a lot
of ECC errors.

Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
Suggested-by: Liang Yang <liang.yang@amlogic.com>
Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
---
 drivers/mtd/nand/raw/meson_nand.c | 30 +++++++++++++++++++++---------
 1 file changed, 21 insertions(+), 9 deletions(-)

diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
index 074e14225c06..2f4d8c84186b 100644
--- a/drivers/mtd/nand/raw/meson_nand.c
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -37,7 +37,7 @@
 #define NFC_CMD_SCRAMBLER_ENABLE	BIT(19)
 #define NFC_CMD_SCRAMBLER_DISABLE	0
 #define NFC_CMD_SHORTMODE_DISABLE	0
-#define NFC_CMD_RB_INT		BIT(14)
+#define NFC_CMD_RB_INT ((0xb << 10) | BIT(18) | BIT(16))
 
 #define NFC_CMD_GET_SIZE(x)	(((x) >> 22) & GENMASK(4, 0))
 
@@ -392,7 +392,7 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
 	}
 }
 
-static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
+static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
 {
 	u32 cmd, cfg;
 	int ret = 0;
@@ -407,17 +407,29 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
 
 	reinit_completion(&nfc->completion);
 
+	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+	meson_nfc_cmd_idle(nfc, 5);
+
 	/* use the max erase time as the maximum clock for waiting R/B */
-	cmd = NFC_CMD_RB | NFC_CMD_RB_INT
-		| nfc->param.chip_select | nfc->timing.tbers_max;
+	cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
 	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+	meson_nfc_cmd_idle(nfc, 2);
 
 	ret = wait_for_completion_timeout(&nfc->completion,
 					  msecs_to_jiffies(timeout_ms));
 	if (ret == 0)
-		ret = -1;
+		return -1;
 
-	return ret;
+	if (!cmd_read0)
+		return 0;
+
+	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_READ0;
+	writel(cmd, nfc->reg_base + NFC_REG_CMD);
+	meson_nfc_drain_cmd(nfc);
+	meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
+
+	return 0;
 }
 
 static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
@@ -623,7 +635,7 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
 	if (in) {
 		nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART;
 		writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
-		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max));
+		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max), 1);
 	} else {
 		meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
 	}
@@ -669,7 +681,7 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
 
 	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
 	writel(cmd, nfc->reg_base + NFC_REG_CMD);
-	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
+	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max), 0);
 
 	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
 
@@ -952,7 +964,7 @@ static int meson_nfc_exec_op(struct nand_chip *nand,
 			break;
 
 		case NAND_OP_WAITRDY_INSTR:
-			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms);
+			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms, 1);
 			if (instr->delay_ns)
 				meson_nfc_cmd_idle(nfc, delay_idle);
 			break;
-- 
2.35.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
  2023-05-15  9:44 ` Arseniy Krasnov
  (?)
  (?)
@ 2023-05-15  9:44   ` Arseniy Krasnov
  -1 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-15  9:44 UTC (permalink / raw)
  To: Liang Yang, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Yixun Lan, Jianxin Pan
  Cc: oxffffaa, kernel, Arseniy Krasnov, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

This moves free bytes of OOB to non-protected ECC area. It is needed to
make JFFS2 works correctly with this NAND controller. Problem fires when
JFFS2 driver writes cleanmarker to some page and later it tries to write
to this page - write will be done successfully, but after that such page
becomes unreadable due to invalid ECC codes. This happens because second
write needs to update ECC codes, but it is impossible to do it correctly
without block erase. So idea of this patch is to split accesses to OOB
free bytes and data on each page - now both of them does not depends on
each other.

Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
---
 drivers/mtd/nand/raw/meson_nand.c | 192 ++++++++++++++++++++++++------
 1 file changed, 155 insertions(+), 37 deletions(-)

diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
index 2f4d8c84186b..8526a6b87720 100644
--- a/drivers/mtd/nand/raw/meson_nand.c
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -108,6 +108,9 @@
 
 #define PER_INFO_BYTE		8
 
+#define NFC_USER_BYTES		2
+#define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
+
 struct meson_nfc_nand_chip {
 	struct list_head node;
 	struct nand_chip nand;
@@ -122,6 +125,7 @@ struct meson_nfc_nand_chip {
 	u8 *data_buf;
 	__le64 *info_buf;
 	u32 nsels;
+	u8 *oob_buf;
 	u8 sels[];
 };
 
@@ -338,7 +342,7 @@ static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
 	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 	int len;
 
-	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
+	len = nand->ecc.size * (i + 1) + NFC_OOB_PER_ECC(nand) * i;
 
 	return meson_chip->data_buf + len;
 }
@@ -349,7 +353,7 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
 	int len, temp;
 
 	temp = nand->ecc.size + nand->ecc.bytes;
-	len = (temp + 2) * i;
+	len = (temp + NFC_USER_BYTES) * i;
 
 	return meson_chip->data_buf + len;
 }
@@ -357,29 +361,47 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
 static void meson_nfc_get_data_oob(struct nand_chip *nand,
 				   u8 *buf, u8 *oobbuf)
 {
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	struct mtd_info *mtd = nand_to_mtd(nand);
 	int i, oob_len = 0;
 	u8 *dsrc, *osrc;
+	u8 *oobtail;
 
-	oob_len = nand->ecc.bytes + 2;
+	oob_len = NFC_OOB_PER_ECC(nand);
 	for (i = 0; i < nand->ecc.steps; i++) {
 		if (buf) {
 			dsrc = meson_nfc_data_ptr(nand, i);
 			memcpy(buf, dsrc, nand->ecc.size);
 			buf += nand->ecc.size;
 		}
-		osrc = meson_nfc_oob_ptr(nand, i);
-		memcpy(oobbuf, osrc, oob_len);
-		oobbuf += oob_len;
+
+		if (oobbuf) {
+			osrc = meson_nfc_oob_ptr(nand, i);
+			memcpy(oobbuf, osrc, oob_len);
+			oobbuf += oob_len;
+		}
 	}
+
+	if (!oobbuf)
+		return;
+
+	oobtail = meson_chip->data_buf + nand->ecc.steps *
+		  (nand->ecc.size + oob_len);
+
+	/* 'oobbuf' if already shifted to the start of unused area. */
+	memcpy(oobbuf, oobtail, mtd->oobsize - nand->ecc.steps * oob_len);
 }
 
 static void meson_nfc_set_data_oob(struct nand_chip *nand,
 				   const u8 *buf, u8 *oobbuf)
 {
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	struct mtd_info *mtd = nand_to_mtd(nand);
 	int i, oob_len = 0;
 	u8 *dsrc, *osrc;
+	u8 *oobtail;
 
-	oob_len = nand->ecc.bytes + 2;
+	oob_len = NFC_OOB_PER_ECC(nand);
 	for (i = 0; i < nand->ecc.steps; i++) {
 		if (buf) {
 			dsrc = meson_nfc_data_ptr(nand, i);
@@ -390,6 +412,12 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
 		memcpy(osrc, oobbuf, oob_len);
 		oobbuf += oob_len;
 	}
+
+	oobtail = meson_chip->data_buf + nand->ecc.steps *
+		  (nand->ecc.size + oob_len);
+
+	/* 'oobbuf' if already shifted to the start of unused area. */
+	memcpy(oobtail, oobbuf, mtd->oobsize - nand->ecc.steps * oob_len);
 }
 
 static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
@@ -436,25 +464,12 @@ static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
 {
 	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 	__le64 *info;
-	int i, count;
-
-	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
-		info = &meson_chip->info_buf[i];
-		*info |= oob_buf[count];
-		*info |= oob_buf[count + 1] << 8;
-	}
-}
-
-static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
-{
-	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
-	__le64 *info;
-	int i, count;
+	int i;
 
-	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
+	for (i = 0; i < nand->ecc.steps; i++) {
 		info = &meson_chip->info_buf[i];
-		oob_buf[count] = *info;
-		oob_buf[count + 1] = *info >> 8;
+		/* Always ignore user bytes programming. */
+		*info |= 0xffff;
 	}
 }
 
@@ -698,18 +713,92 @@ static int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf,
 	return meson_nfc_write_page_sub(nand, page, 1);
 }
 
+static u32 meson_nfc_get_oob_bytes(struct nand_chip *nand)
+{
+	struct mtd_info *mtd = nand_to_mtd(nand);
+
+	return mtd->oobsize - nand->ecc.steps * NFC_OOB_PER_ECC(nand);
+}
+
+static int __meson_nfc_write_oob(struct nand_chip *nand, int page, u8 *oob_buf)
+{
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	struct mtd_info *mtd = nand_to_mtd(nand);
+	u32 page_size = mtd->writesize + mtd->oobsize;
+	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
+	int ret;
+
+	if (!oob_bytes)
+		return 0;
+
+	ret = nand_prog_page_begin_op(nand, page, 0, NULL, 0);
+	if (ret)
+		return ret;
+
+	memcpy(meson_chip->oob_buf, oob_buf + (mtd->oobsize - oob_bytes),
+	       oob_bytes);
+
+	ret = nand_change_write_column_op(nand, page_size - oob_bytes,
+					  meson_chip->oob_buf,
+					  oob_bytes, false);
+	if (ret)
+		return ret;
+
+	return nand_prog_page_end_op(nand);
+}
+
+static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
+				u8 *oob_buf)
+{
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	struct mtd_info *mtd = nand_to_mtd(nand);
+	u32 oob_bytes;
+	u32 page_size;
+	int ret;
+
+	oob_bytes = meson_nfc_get_oob_bytes(nand);
+
+	if (!oob_bytes)
+		return 0;
+
+	ret = nand_read_page_op(nand, page, 0, NULL, 0);
+	if (ret)
+		return ret;
+
+	page_size = mtd->writesize + mtd->oobsize;
+
+	ret = nand_change_read_column_op(nand, page_size - oob_bytes,
+					 meson_chip->oob_buf,
+					 oob_bytes, false);
+
+	if (!ret)
+		memcpy(oob_buf + (mtd->oobsize - oob_bytes),
+		       meson_chip->oob_buf,
+		       oob_bytes);
+
+	return ret;
+}
+
 static int meson_nfc_write_page_hwecc(struct nand_chip *nand,
 				      const u8 *buf, int oob_required, int page)
 {
 	struct mtd_info *mtd = nand_to_mtd(nand);
 	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 	u8 *oob_buf = nand->oob_poi;
+	int ret;
 
 	memcpy(meson_chip->data_buf, buf, mtd->writesize);
 	memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE);
 	meson_nfc_set_user_byte(nand, oob_buf);
 
-	return meson_nfc_write_page_sub(nand, page, 0);
+	ret = meson_nfc_write_page_sub(nand, page, 0);
+	if (ret)
+		return ret;
+
+	if (oob_required)
+		ret = __meson_nfc_write_oob(nand, page, oob_buf);
+
+	return ret;
 }
 
 static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
@@ -783,7 +872,7 @@ static int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf,
 	if (ret)
 		return ret;
 
-	meson_nfc_get_data_oob(nand, buf, oob_buf);
+	meson_nfc_get_data_oob(nand, buf, oob_required ? oob_buf : NULL);
 
 	return 0;
 }
@@ -803,12 +892,12 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
 	if (ret)
 		return ret;
 
-	meson_nfc_get_user_byte(nand, oob_buf);
 	ret = meson_nfc_ecc_correct(nand, &bitflips, &correct_bitmap);
 	if (ret == ECC_CHECK_RETURN_FF) {
 		if (buf)
 			memset(buf, 0xff, mtd->writesize);
 		memset(oob_buf, 0xff, mtd->oobsize);
+		return bitflips;
 	} else if (ret < 0) {
 		if ((nand->options & NAND_NEED_SCRAMBLING) || !buf) {
 			mtd->ecc_stats.failed++;
@@ -820,12 +909,14 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
 
 		for (i = 0; i < nand->ecc.steps ; i++) {
 			u8 *data = buf + i * ecc->size;
-			u8 *oob = nand->oob_poi + i * (ecc->bytes + 2);
+			u8 *oob = nand->oob_poi + i * NFC_OOB_PER_ECC(nand);
 
 			if (correct_bitmap & BIT_ULL(i))
 				continue;
+
 			ret = nand_check_erased_ecc_chunk(data,	ecc->size,
-							  oob, ecc->bytes + 2,
+							  oob,
+							  NFC_OOB_PER_ECC(nand),
 							  NULL, 0,
 							  ecc->strength);
 			if (ret < 0) {
@@ -839,17 +930,30 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
 		memcpy(buf, meson_chip->data_buf, mtd->writesize);
 	}
 
+	if (oob_required)
+		__meson_nfc_read_oob(nand, page, oob_buf);
+
 	return bitflips;
 }
 
 static int meson_nfc_read_oob_raw(struct nand_chip *nand, int page)
 {
-	return meson_nfc_read_page_raw(nand, NULL, 1, page);
+	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
 }
 
 static int meson_nfc_read_oob(struct nand_chip *nand, int page)
 {
-	return meson_nfc_read_page_hwecc(nand, NULL, 1, page);
+	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
+}
+
+static int meson_nfc_write_oob_raw(struct nand_chip *nand, int page)
+{
+	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
+}
+
+static int meson_nfc_write_oob(struct nand_chip *nand, int page)
+{
+	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
 }
 
 static bool meson_nfc_is_buffer_dma_safe(const void *buffer)
@@ -982,7 +1086,7 @@ static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
 	if (section >= nand->ecc.steps)
 		return -ERANGE;
 
-	oobregion->offset =  2 + (section * (2 + nand->ecc.bytes));
+	oobregion->offset = NFC_USER_BYTES + section * NFC_OOB_PER_ECC(nand);
 	oobregion->length = nand->ecc.bytes;
 
 	return 0;
@@ -992,12 +1096,16 @@ static int meson_ooblayout_free(struct mtd_info *mtd, int section,
 				struct mtd_oob_region *oobregion)
 {
 	struct nand_chip *nand = mtd_to_nand(mtd);
+	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
 
 	if (section >= nand->ecc.steps)
 		return -ERANGE;
 
-	oobregion->offset = section * (2 + nand->ecc.bytes);
-	oobregion->length = 2;
+	/* Split rest of OOB area (not covered by ECC engine) per each
+	 * ECC section. This will be OOB data available to user.
+	 */
+	oobregion->offset = (section + nand->ecc.steps) * NFC_OOB_PER_ECC(nand);
+	oobregion->length = oob_bytes / nand->ecc.steps;
 
 	return 0;
 }
@@ -1184,6 +1292,9 @@ static int meson_nand_bch_mode(struct nand_chip *nand)
 
 static void meson_nand_detach_chip(struct nand_chip *nand)
 {
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+
+	kfree(meson_chip->oob_buf);
 	meson_nfc_free_buffer(nand);
 }
 
@@ -1225,9 +1336,9 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
 	nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
 	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
 	nand->ecc.write_page = meson_nfc_write_page_hwecc;
-	nand->ecc.write_oob_raw = nand_write_oob_std;
-	nand->ecc.write_oob = nand_write_oob_std;
 
+	nand->ecc.write_oob_raw = meson_nfc_write_oob_raw;
+	nand->ecc.write_oob = meson_nfc_write_oob;
 	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
 	nand->ecc.read_page = meson_nfc_read_page_hwecc;
 	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
@@ -1237,9 +1348,16 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
 		dev_err(nfc->dev, "16bits bus width not supported");
 		return -EINVAL;
 	}
+
+	meson_chip->oob_buf = kmalloc(nand->ecc.bytes, GFP_KERNEL);
+	if (!meson_chip->oob_buf)
+		return -ENOMEM;
+
 	ret = meson_chip_buffer_init(nand);
-	if (ret)
+	if (ret) {
+		kfree(meson_chip->oob_buf);
 		return -ENOMEM;
+	}
 
 	return ret;
 }
-- 
2.35.0


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

* [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-15  9:44   ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-15  9:44 UTC (permalink / raw)
  To: Liang Yang, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Yixun Lan, Jianxin Pan
  Cc: oxffffaa, kernel, Arseniy Krasnov, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

This moves free bytes of OOB to non-protected ECC area. It is needed to
make JFFS2 works correctly with this NAND controller. Problem fires when
JFFS2 driver writes cleanmarker to some page and later it tries to write
to this page - write will be done successfully, but after that such page
becomes unreadable due to invalid ECC codes. This happens because second
write needs to update ECC codes, but it is impossible to do it correctly
without block erase. So idea of this patch is to split accesses to OOB
free bytes and data on each page - now both of them does not depends on
each other.

Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
---
 drivers/mtd/nand/raw/meson_nand.c | 192 ++++++++++++++++++++++++------
 1 file changed, 155 insertions(+), 37 deletions(-)

diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
index 2f4d8c84186b..8526a6b87720 100644
--- a/drivers/mtd/nand/raw/meson_nand.c
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -108,6 +108,9 @@
 
 #define PER_INFO_BYTE		8
 
+#define NFC_USER_BYTES		2
+#define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
+
 struct meson_nfc_nand_chip {
 	struct list_head node;
 	struct nand_chip nand;
@@ -122,6 +125,7 @@ struct meson_nfc_nand_chip {
 	u8 *data_buf;
 	__le64 *info_buf;
 	u32 nsels;
+	u8 *oob_buf;
 	u8 sels[];
 };
 
@@ -338,7 +342,7 @@ static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
 	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 	int len;
 
-	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
+	len = nand->ecc.size * (i + 1) + NFC_OOB_PER_ECC(nand) * i;
 
 	return meson_chip->data_buf + len;
 }
@@ -349,7 +353,7 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
 	int len, temp;
 
 	temp = nand->ecc.size + nand->ecc.bytes;
-	len = (temp + 2) * i;
+	len = (temp + NFC_USER_BYTES) * i;
 
 	return meson_chip->data_buf + len;
 }
@@ -357,29 +361,47 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
 static void meson_nfc_get_data_oob(struct nand_chip *nand,
 				   u8 *buf, u8 *oobbuf)
 {
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	struct mtd_info *mtd = nand_to_mtd(nand);
 	int i, oob_len = 0;
 	u8 *dsrc, *osrc;
+	u8 *oobtail;
 
-	oob_len = nand->ecc.bytes + 2;
+	oob_len = NFC_OOB_PER_ECC(nand);
 	for (i = 0; i < nand->ecc.steps; i++) {
 		if (buf) {
 			dsrc = meson_nfc_data_ptr(nand, i);
 			memcpy(buf, dsrc, nand->ecc.size);
 			buf += nand->ecc.size;
 		}
-		osrc = meson_nfc_oob_ptr(nand, i);
-		memcpy(oobbuf, osrc, oob_len);
-		oobbuf += oob_len;
+
+		if (oobbuf) {
+			osrc = meson_nfc_oob_ptr(nand, i);
+			memcpy(oobbuf, osrc, oob_len);
+			oobbuf += oob_len;
+		}
 	}
+
+	if (!oobbuf)
+		return;
+
+	oobtail = meson_chip->data_buf + nand->ecc.steps *
+		  (nand->ecc.size + oob_len);
+
+	/* 'oobbuf' if already shifted to the start of unused area. */
+	memcpy(oobbuf, oobtail, mtd->oobsize - nand->ecc.steps * oob_len);
 }
 
 static void meson_nfc_set_data_oob(struct nand_chip *nand,
 				   const u8 *buf, u8 *oobbuf)
 {
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	struct mtd_info *mtd = nand_to_mtd(nand);
 	int i, oob_len = 0;
 	u8 *dsrc, *osrc;
+	u8 *oobtail;
 
-	oob_len = nand->ecc.bytes + 2;
+	oob_len = NFC_OOB_PER_ECC(nand);
 	for (i = 0; i < nand->ecc.steps; i++) {
 		if (buf) {
 			dsrc = meson_nfc_data_ptr(nand, i);
@@ -390,6 +412,12 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
 		memcpy(osrc, oobbuf, oob_len);
 		oobbuf += oob_len;
 	}
+
+	oobtail = meson_chip->data_buf + nand->ecc.steps *
+		  (nand->ecc.size + oob_len);
+
+	/* 'oobbuf' if already shifted to the start of unused area. */
+	memcpy(oobtail, oobbuf, mtd->oobsize - nand->ecc.steps * oob_len);
 }
 
 static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
@@ -436,25 +464,12 @@ static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
 {
 	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 	__le64 *info;
-	int i, count;
-
-	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
-		info = &meson_chip->info_buf[i];
-		*info |= oob_buf[count];
-		*info |= oob_buf[count + 1] << 8;
-	}
-}
-
-static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
-{
-	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
-	__le64 *info;
-	int i, count;
+	int i;
 
-	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
+	for (i = 0; i < nand->ecc.steps; i++) {
 		info = &meson_chip->info_buf[i];
-		oob_buf[count] = *info;
-		oob_buf[count + 1] = *info >> 8;
+		/* Always ignore user bytes programming. */
+		*info |= 0xffff;
 	}
 }
 
@@ -698,18 +713,92 @@ static int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf,
 	return meson_nfc_write_page_sub(nand, page, 1);
 }
 
+static u32 meson_nfc_get_oob_bytes(struct nand_chip *nand)
+{
+	struct mtd_info *mtd = nand_to_mtd(nand);
+
+	return mtd->oobsize - nand->ecc.steps * NFC_OOB_PER_ECC(nand);
+}
+
+static int __meson_nfc_write_oob(struct nand_chip *nand, int page, u8 *oob_buf)
+{
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	struct mtd_info *mtd = nand_to_mtd(nand);
+	u32 page_size = mtd->writesize + mtd->oobsize;
+	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
+	int ret;
+
+	if (!oob_bytes)
+		return 0;
+
+	ret = nand_prog_page_begin_op(nand, page, 0, NULL, 0);
+	if (ret)
+		return ret;
+
+	memcpy(meson_chip->oob_buf, oob_buf + (mtd->oobsize - oob_bytes),
+	       oob_bytes);
+
+	ret = nand_change_write_column_op(nand, page_size - oob_bytes,
+					  meson_chip->oob_buf,
+					  oob_bytes, false);
+	if (ret)
+		return ret;
+
+	return nand_prog_page_end_op(nand);
+}
+
+static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
+				u8 *oob_buf)
+{
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	struct mtd_info *mtd = nand_to_mtd(nand);
+	u32 oob_bytes;
+	u32 page_size;
+	int ret;
+
+	oob_bytes = meson_nfc_get_oob_bytes(nand);
+
+	if (!oob_bytes)
+		return 0;
+
+	ret = nand_read_page_op(nand, page, 0, NULL, 0);
+	if (ret)
+		return ret;
+
+	page_size = mtd->writesize + mtd->oobsize;
+
+	ret = nand_change_read_column_op(nand, page_size - oob_bytes,
+					 meson_chip->oob_buf,
+					 oob_bytes, false);
+
+	if (!ret)
+		memcpy(oob_buf + (mtd->oobsize - oob_bytes),
+		       meson_chip->oob_buf,
+		       oob_bytes);
+
+	return ret;
+}
+
 static int meson_nfc_write_page_hwecc(struct nand_chip *nand,
 				      const u8 *buf, int oob_required, int page)
 {
 	struct mtd_info *mtd = nand_to_mtd(nand);
 	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 	u8 *oob_buf = nand->oob_poi;
+	int ret;
 
 	memcpy(meson_chip->data_buf, buf, mtd->writesize);
 	memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE);
 	meson_nfc_set_user_byte(nand, oob_buf);
 
-	return meson_nfc_write_page_sub(nand, page, 0);
+	ret = meson_nfc_write_page_sub(nand, page, 0);
+	if (ret)
+		return ret;
+
+	if (oob_required)
+		ret = __meson_nfc_write_oob(nand, page, oob_buf);
+
+	return ret;
 }
 
 static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
@@ -783,7 +872,7 @@ static int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf,
 	if (ret)
 		return ret;
 
-	meson_nfc_get_data_oob(nand, buf, oob_buf);
+	meson_nfc_get_data_oob(nand, buf, oob_required ? oob_buf : NULL);
 
 	return 0;
 }
@@ -803,12 +892,12 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
 	if (ret)
 		return ret;
 
-	meson_nfc_get_user_byte(nand, oob_buf);
 	ret = meson_nfc_ecc_correct(nand, &bitflips, &correct_bitmap);
 	if (ret == ECC_CHECK_RETURN_FF) {
 		if (buf)
 			memset(buf, 0xff, mtd->writesize);
 		memset(oob_buf, 0xff, mtd->oobsize);
+		return bitflips;
 	} else if (ret < 0) {
 		if ((nand->options & NAND_NEED_SCRAMBLING) || !buf) {
 			mtd->ecc_stats.failed++;
@@ -820,12 +909,14 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
 
 		for (i = 0; i < nand->ecc.steps ; i++) {
 			u8 *data = buf + i * ecc->size;
-			u8 *oob = nand->oob_poi + i * (ecc->bytes + 2);
+			u8 *oob = nand->oob_poi + i * NFC_OOB_PER_ECC(nand);
 
 			if (correct_bitmap & BIT_ULL(i))
 				continue;
+
 			ret = nand_check_erased_ecc_chunk(data,	ecc->size,
-							  oob, ecc->bytes + 2,
+							  oob,
+							  NFC_OOB_PER_ECC(nand),
 							  NULL, 0,
 							  ecc->strength);
 			if (ret < 0) {
@@ -839,17 +930,30 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
 		memcpy(buf, meson_chip->data_buf, mtd->writesize);
 	}
 
+	if (oob_required)
+		__meson_nfc_read_oob(nand, page, oob_buf);
+
 	return bitflips;
 }
 
 static int meson_nfc_read_oob_raw(struct nand_chip *nand, int page)
 {
-	return meson_nfc_read_page_raw(nand, NULL, 1, page);
+	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
 }
 
 static int meson_nfc_read_oob(struct nand_chip *nand, int page)
 {
-	return meson_nfc_read_page_hwecc(nand, NULL, 1, page);
+	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
+}
+
+static int meson_nfc_write_oob_raw(struct nand_chip *nand, int page)
+{
+	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
+}
+
+static int meson_nfc_write_oob(struct nand_chip *nand, int page)
+{
+	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
 }
 
 static bool meson_nfc_is_buffer_dma_safe(const void *buffer)
@@ -982,7 +1086,7 @@ static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
 	if (section >= nand->ecc.steps)
 		return -ERANGE;
 
-	oobregion->offset =  2 + (section * (2 + nand->ecc.bytes));
+	oobregion->offset = NFC_USER_BYTES + section * NFC_OOB_PER_ECC(nand);
 	oobregion->length = nand->ecc.bytes;
 
 	return 0;
@@ -992,12 +1096,16 @@ static int meson_ooblayout_free(struct mtd_info *mtd, int section,
 				struct mtd_oob_region *oobregion)
 {
 	struct nand_chip *nand = mtd_to_nand(mtd);
+	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
 
 	if (section >= nand->ecc.steps)
 		return -ERANGE;
 
-	oobregion->offset = section * (2 + nand->ecc.bytes);
-	oobregion->length = 2;
+	/* Split rest of OOB area (not covered by ECC engine) per each
+	 * ECC section. This will be OOB data available to user.
+	 */
+	oobregion->offset = (section + nand->ecc.steps) * NFC_OOB_PER_ECC(nand);
+	oobregion->length = oob_bytes / nand->ecc.steps;
 
 	return 0;
 }
@@ -1184,6 +1292,9 @@ static int meson_nand_bch_mode(struct nand_chip *nand)
 
 static void meson_nand_detach_chip(struct nand_chip *nand)
 {
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+
+	kfree(meson_chip->oob_buf);
 	meson_nfc_free_buffer(nand);
 }
 
@@ -1225,9 +1336,9 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
 	nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
 	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
 	nand->ecc.write_page = meson_nfc_write_page_hwecc;
-	nand->ecc.write_oob_raw = nand_write_oob_std;
-	nand->ecc.write_oob = nand_write_oob_std;
 
+	nand->ecc.write_oob_raw = meson_nfc_write_oob_raw;
+	nand->ecc.write_oob = meson_nfc_write_oob;
 	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
 	nand->ecc.read_page = meson_nfc_read_page_hwecc;
 	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
@@ -1237,9 +1348,16 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
 		dev_err(nfc->dev, "16bits bus width not supported");
 		return -EINVAL;
 	}
+
+	meson_chip->oob_buf = kmalloc(nand->ecc.bytes, GFP_KERNEL);
+	if (!meson_chip->oob_buf)
+		return -ENOMEM;
+
 	ret = meson_chip_buffer_init(nand);
-	if (ret)
+	if (ret) {
+		kfree(meson_chip->oob_buf);
 		return -ENOMEM;
+	}
 
 	return ret;
 }
-- 
2.35.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-15  9:44   ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-15  9:44 UTC (permalink / raw)
  To: Liang Yang, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Yixun Lan, Jianxin Pan
  Cc: oxffffaa, kernel, Arseniy Krasnov, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

This moves free bytes of OOB to non-protected ECC area. It is needed to
make JFFS2 works correctly with this NAND controller. Problem fires when
JFFS2 driver writes cleanmarker to some page and later it tries to write
to this page - write will be done successfully, but after that such page
becomes unreadable due to invalid ECC codes. This happens because second
write needs to update ECC codes, but it is impossible to do it correctly
without block erase. So idea of this patch is to split accesses to OOB
free bytes and data on each page - now both of them does not depends on
each other.

Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
---
 drivers/mtd/nand/raw/meson_nand.c | 192 ++++++++++++++++++++++++------
 1 file changed, 155 insertions(+), 37 deletions(-)

diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
index 2f4d8c84186b..8526a6b87720 100644
--- a/drivers/mtd/nand/raw/meson_nand.c
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -108,6 +108,9 @@
 
 #define PER_INFO_BYTE		8
 
+#define NFC_USER_BYTES		2
+#define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
+
 struct meson_nfc_nand_chip {
 	struct list_head node;
 	struct nand_chip nand;
@@ -122,6 +125,7 @@ struct meson_nfc_nand_chip {
 	u8 *data_buf;
 	__le64 *info_buf;
 	u32 nsels;
+	u8 *oob_buf;
 	u8 sels[];
 };
 
@@ -338,7 +342,7 @@ static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
 	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 	int len;
 
-	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
+	len = nand->ecc.size * (i + 1) + NFC_OOB_PER_ECC(nand) * i;
 
 	return meson_chip->data_buf + len;
 }
@@ -349,7 +353,7 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
 	int len, temp;
 
 	temp = nand->ecc.size + nand->ecc.bytes;
-	len = (temp + 2) * i;
+	len = (temp + NFC_USER_BYTES) * i;
 
 	return meson_chip->data_buf + len;
 }
@@ -357,29 +361,47 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
 static void meson_nfc_get_data_oob(struct nand_chip *nand,
 				   u8 *buf, u8 *oobbuf)
 {
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	struct mtd_info *mtd = nand_to_mtd(nand);
 	int i, oob_len = 0;
 	u8 *dsrc, *osrc;
+	u8 *oobtail;
 
-	oob_len = nand->ecc.bytes + 2;
+	oob_len = NFC_OOB_PER_ECC(nand);
 	for (i = 0; i < nand->ecc.steps; i++) {
 		if (buf) {
 			dsrc = meson_nfc_data_ptr(nand, i);
 			memcpy(buf, dsrc, nand->ecc.size);
 			buf += nand->ecc.size;
 		}
-		osrc = meson_nfc_oob_ptr(nand, i);
-		memcpy(oobbuf, osrc, oob_len);
-		oobbuf += oob_len;
+
+		if (oobbuf) {
+			osrc = meson_nfc_oob_ptr(nand, i);
+			memcpy(oobbuf, osrc, oob_len);
+			oobbuf += oob_len;
+		}
 	}
+
+	if (!oobbuf)
+		return;
+
+	oobtail = meson_chip->data_buf + nand->ecc.steps *
+		  (nand->ecc.size + oob_len);
+
+	/* 'oobbuf' if already shifted to the start of unused area. */
+	memcpy(oobbuf, oobtail, mtd->oobsize - nand->ecc.steps * oob_len);
 }
 
 static void meson_nfc_set_data_oob(struct nand_chip *nand,
 				   const u8 *buf, u8 *oobbuf)
 {
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	struct mtd_info *mtd = nand_to_mtd(nand);
 	int i, oob_len = 0;
 	u8 *dsrc, *osrc;
+	u8 *oobtail;
 
-	oob_len = nand->ecc.bytes + 2;
+	oob_len = NFC_OOB_PER_ECC(nand);
 	for (i = 0; i < nand->ecc.steps; i++) {
 		if (buf) {
 			dsrc = meson_nfc_data_ptr(nand, i);
@@ -390,6 +412,12 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
 		memcpy(osrc, oobbuf, oob_len);
 		oobbuf += oob_len;
 	}
+
+	oobtail = meson_chip->data_buf + nand->ecc.steps *
+		  (nand->ecc.size + oob_len);
+
+	/* 'oobbuf' if already shifted to the start of unused area. */
+	memcpy(oobtail, oobbuf, mtd->oobsize - nand->ecc.steps * oob_len);
 }
 
 static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
@@ -436,25 +464,12 @@ static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
 {
 	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 	__le64 *info;
-	int i, count;
-
-	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
-		info = &meson_chip->info_buf[i];
-		*info |= oob_buf[count];
-		*info |= oob_buf[count + 1] << 8;
-	}
-}
-
-static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
-{
-	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
-	__le64 *info;
-	int i, count;
+	int i;
 
-	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
+	for (i = 0; i < nand->ecc.steps; i++) {
 		info = &meson_chip->info_buf[i];
-		oob_buf[count] = *info;
-		oob_buf[count + 1] = *info >> 8;
+		/* Always ignore user bytes programming. */
+		*info |= 0xffff;
 	}
 }
 
@@ -698,18 +713,92 @@ static int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf,
 	return meson_nfc_write_page_sub(nand, page, 1);
 }
 
+static u32 meson_nfc_get_oob_bytes(struct nand_chip *nand)
+{
+	struct mtd_info *mtd = nand_to_mtd(nand);
+
+	return mtd->oobsize - nand->ecc.steps * NFC_OOB_PER_ECC(nand);
+}
+
+static int __meson_nfc_write_oob(struct nand_chip *nand, int page, u8 *oob_buf)
+{
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	struct mtd_info *mtd = nand_to_mtd(nand);
+	u32 page_size = mtd->writesize + mtd->oobsize;
+	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
+	int ret;
+
+	if (!oob_bytes)
+		return 0;
+
+	ret = nand_prog_page_begin_op(nand, page, 0, NULL, 0);
+	if (ret)
+		return ret;
+
+	memcpy(meson_chip->oob_buf, oob_buf + (mtd->oobsize - oob_bytes),
+	       oob_bytes);
+
+	ret = nand_change_write_column_op(nand, page_size - oob_bytes,
+					  meson_chip->oob_buf,
+					  oob_bytes, false);
+	if (ret)
+		return ret;
+
+	return nand_prog_page_end_op(nand);
+}
+
+static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
+				u8 *oob_buf)
+{
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	struct mtd_info *mtd = nand_to_mtd(nand);
+	u32 oob_bytes;
+	u32 page_size;
+	int ret;
+
+	oob_bytes = meson_nfc_get_oob_bytes(nand);
+
+	if (!oob_bytes)
+		return 0;
+
+	ret = nand_read_page_op(nand, page, 0, NULL, 0);
+	if (ret)
+		return ret;
+
+	page_size = mtd->writesize + mtd->oobsize;
+
+	ret = nand_change_read_column_op(nand, page_size - oob_bytes,
+					 meson_chip->oob_buf,
+					 oob_bytes, false);
+
+	if (!ret)
+		memcpy(oob_buf + (mtd->oobsize - oob_bytes),
+		       meson_chip->oob_buf,
+		       oob_bytes);
+
+	return ret;
+}
+
 static int meson_nfc_write_page_hwecc(struct nand_chip *nand,
 				      const u8 *buf, int oob_required, int page)
 {
 	struct mtd_info *mtd = nand_to_mtd(nand);
 	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 	u8 *oob_buf = nand->oob_poi;
+	int ret;
 
 	memcpy(meson_chip->data_buf, buf, mtd->writesize);
 	memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE);
 	meson_nfc_set_user_byte(nand, oob_buf);
 
-	return meson_nfc_write_page_sub(nand, page, 0);
+	ret = meson_nfc_write_page_sub(nand, page, 0);
+	if (ret)
+		return ret;
+
+	if (oob_required)
+		ret = __meson_nfc_write_oob(nand, page, oob_buf);
+
+	return ret;
 }
 
 static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
@@ -783,7 +872,7 @@ static int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf,
 	if (ret)
 		return ret;
 
-	meson_nfc_get_data_oob(nand, buf, oob_buf);
+	meson_nfc_get_data_oob(nand, buf, oob_required ? oob_buf : NULL);
 
 	return 0;
 }
@@ -803,12 +892,12 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
 	if (ret)
 		return ret;
 
-	meson_nfc_get_user_byte(nand, oob_buf);
 	ret = meson_nfc_ecc_correct(nand, &bitflips, &correct_bitmap);
 	if (ret == ECC_CHECK_RETURN_FF) {
 		if (buf)
 			memset(buf, 0xff, mtd->writesize);
 		memset(oob_buf, 0xff, mtd->oobsize);
+		return bitflips;
 	} else if (ret < 0) {
 		if ((nand->options & NAND_NEED_SCRAMBLING) || !buf) {
 			mtd->ecc_stats.failed++;
@@ -820,12 +909,14 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
 
 		for (i = 0; i < nand->ecc.steps ; i++) {
 			u8 *data = buf + i * ecc->size;
-			u8 *oob = nand->oob_poi + i * (ecc->bytes + 2);
+			u8 *oob = nand->oob_poi + i * NFC_OOB_PER_ECC(nand);
 
 			if (correct_bitmap & BIT_ULL(i))
 				continue;
+
 			ret = nand_check_erased_ecc_chunk(data,	ecc->size,
-							  oob, ecc->bytes + 2,
+							  oob,
+							  NFC_OOB_PER_ECC(nand),
 							  NULL, 0,
 							  ecc->strength);
 			if (ret < 0) {
@@ -839,17 +930,30 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
 		memcpy(buf, meson_chip->data_buf, mtd->writesize);
 	}
 
+	if (oob_required)
+		__meson_nfc_read_oob(nand, page, oob_buf);
+
 	return bitflips;
 }
 
 static int meson_nfc_read_oob_raw(struct nand_chip *nand, int page)
 {
-	return meson_nfc_read_page_raw(nand, NULL, 1, page);
+	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
 }
 
 static int meson_nfc_read_oob(struct nand_chip *nand, int page)
 {
-	return meson_nfc_read_page_hwecc(nand, NULL, 1, page);
+	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
+}
+
+static int meson_nfc_write_oob_raw(struct nand_chip *nand, int page)
+{
+	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
+}
+
+static int meson_nfc_write_oob(struct nand_chip *nand, int page)
+{
+	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
 }
 
 static bool meson_nfc_is_buffer_dma_safe(const void *buffer)
@@ -982,7 +1086,7 @@ static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
 	if (section >= nand->ecc.steps)
 		return -ERANGE;
 
-	oobregion->offset =  2 + (section * (2 + nand->ecc.bytes));
+	oobregion->offset = NFC_USER_BYTES + section * NFC_OOB_PER_ECC(nand);
 	oobregion->length = nand->ecc.bytes;
 
 	return 0;
@@ -992,12 +1096,16 @@ static int meson_ooblayout_free(struct mtd_info *mtd, int section,
 				struct mtd_oob_region *oobregion)
 {
 	struct nand_chip *nand = mtd_to_nand(mtd);
+	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
 
 	if (section >= nand->ecc.steps)
 		return -ERANGE;
 
-	oobregion->offset = section * (2 + nand->ecc.bytes);
-	oobregion->length = 2;
+	/* Split rest of OOB area (not covered by ECC engine) per each
+	 * ECC section. This will be OOB data available to user.
+	 */
+	oobregion->offset = (section + nand->ecc.steps) * NFC_OOB_PER_ECC(nand);
+	oobregion->length = oob_bytes / nand->ecc.steps;
 
 	return 0;
 }
@@ -1184,6 +1292,9 @@ static int meson_nand_bch_mode(struct nand_chip *nand)
 
 static void meson_nand_detach_chip(struct nand_chip *nand)
 {
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+
+	kfree(meson_chip->oob_buf);
 	meson_nfc_free_buffer(nand);
 }
 
@@ -1225,9 +1336,9 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
 	nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
 	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
 	nand->ecc.write_page = meson_nfc_write_page_hwecc;
-	nand->ecc.write_oob_raw = nand_write_oob_std;
-	nand->ecc.write_oob = nand_write_oob_std;
 
+	nand->ecc.write_oob_raw = meson_nfc_write_oob_raw;
+	nand->ecc.write_oob = meson_nfc_write_oob;
 	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
 	nand->ecc.read_page = meson_nfc_read_page_hwecc;
 	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
@@ -1237,9 +1348,16 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
 		dev_err(nfc->dev, "16bits bus width not supported");
 		return -EINVAL;
 	}
+
+	meson_chip->oob_buf = kmalloc(nand->ecc.bytes, GFP_KERNEL);
+	if (!meson_chip->oob_buf)
+		return -ENOMEM;
+
 	ret = meson_chip_buffer_init(nand);
-	if (ret)
+	if (ret) {
+		kfree(meson_chip->oob_buf);
 		return -ENOMEM;
+	}
 
 	return ret;
 }
-- 
2.35.0


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

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

* [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-15  9:44   ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-15  9:44 UTC (permalink / raw)
  To: Liang Yang, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Yixun Lan, Jianxin Pan
  Cc: oxffffaa, kernel, Arseniy Krasnov, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

This moves free bytes of OOB to non-protected ECC area. It is needed to
make JFFS2 works correctly with this NAND controller. Problem fires when
JFFS2 driver writes cleanmarker to some page and later it tries to write
to this page - write will be done successfully, but after that such page
becomes unreadable due to invalid ECC codes. This happens because second
write needs to update ECC codes, but it is impossible to do it correctly
without block erase. So idea of this patch is to split accesses to OOB
free bytes and data on each page - now both of them does not depends on
each other.

Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
---
 drivers/mtd/nand/raw/meson_nand.c | 192 ++++++++++++++++++++++++------
 1 file changed, 155 insertions(+), 37 deletions(-)

diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
index 2f4d8c84186b..8526a6b87720 100644
--- a/drivers/mtd/nand/raw/meson_nand.c
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -108,6 +108,9 @@
 
 #define PER_INFO_BYTE		8
 
+#define NFC_USER_BYTES		2
+#define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
+
 struct meson_nfc_nand_chip {
 	struct list_head node;
 	struct nand_chip nand;
@@ -122,6 +125,7 @@ struct meson_nfc_nand_chip {
 	u8 *data_buf;
 	__le64 *info_buf;
 	u32 nsels;
+	u8 *oob_buf;
 	u8 sels[];
 };
 
@@ -338,7 +342,7 @@ static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
 	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 	int len;
 
-	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
+	len = nand->ecc.size * (i + 1) + NFC_OOB_PER_ECC(nand) * i;
 
 	return meson_chip->data_buf + len;
 }
@@ -349,7 +353,7 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
 	int len, temp;
 
 	temp = nand->ecc.size + nand->ecc.bytes;
-	len = (temp + 2) * i;
+	len = (temp + NFC_USER_BYTES) * i;
 
 	return meson_chip->data_buf + len;
 }
@@ -357,29 +361,47 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
 static void meson_nfc_get_data_oob(struct nand_chip *nand,
 				   u8 *buf, u8 *oobbuf)
 {
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	struct mtd_info *mtd = nand_to_mtd(nand);
 	int i, oob_len = 0;
 	u8 *dsrc, *osrc;
+	u8 *oobtail;
 
-	oob_len = nand->ecc.bytes + 2;
+	oob_len = NFC_OOB_PER_ECC(nand);
 	for (i = 0; i < nand->ecc.steps; i++) {
 		if (buf) {
 			dsrc = meson_nfc_data_ptr(nand, i);
 			memcpy(buf, dsrc, nand->ecc.size);
 			buf += nand->ecc.size;
 		}
-		osrc = meson_nfc_oob_ptr(nand, i);
-		memcpy(oobbuf, osrc, oob_len);
-		oobbuf += oob_len;
+
+		if (oobbuf) {
+			osrc = meson_nfc_oob_ptr(nand, i);
+			memcpy(oobbuf, osrc, oob_len);
+			oobbuf += oob_len;
+		}
 	}
+
+	if (!oobbuf)
+		return;
+
+	oobtail = meson_chip->data_buf + nand->ecc.steps *
+		  (nand->ecc.size + oob_len);
+
+	/* 'oobbuf' if already shifted to the start of unused area. */
+	memcpy(oobbuf, oobtail, mtd->oobsize - nand->ecc.steps * oob_len);
 }
 
 static void meson_nfc_set_data_oob(struct nand_chip *nand,
 				   const u8 *buf, u8 *oobbuf)
 {
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	struct mtd_info *mtd = nand_to_mtd(nand);
 	int i, oob_len = 0;
 	u8 *dsrc, *osrc;
+	u8 *oobtail;
 
-	oob_len = nand->ecc.bytes + 2;
+	oob_len = NFC_OOB_PER_ECC(nand);
 	for (i = 0; i < nand->ecc.steps; i++) {
 		if (buf) {
 			dsrc = meson_nfc_data_ptr(nand, i);
@@ -390,6 +412,12 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
 		memcpy(osrc, oobbuf, oob_len);
 		oobbuf += oob_len;
 	}
+
+	oobtail = meson_chip->data_buf + nand->ecc.steps *
+		  (nand->ecc.size + oob_len);
+
+	/* 'oobbuf' if already shifted to the start of unused area. */
+	memcpy(oobtail, oobbuf, mtd->oobsize - nand->ecc.steps * oob_len);
 }
 
 static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
@@ -436,25 +464,12 @@ static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
 {
 	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 	__le64 *info;
-	int i, count;
-
-	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
-		info = &meson_chip->info_buf[i];
-		*info |= oob_buf[count];
-		*info |= oob_buf[count + 1] << 8;
-	}
-}
-
-static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
-{
-	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
-	__le64 *info;
-	int i, count;
+	int i;
 
-	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
+	for (i = 0; i < nand->ecc.steps; i++) {
 		info = &meson_chip->info_buf[i];
-		oob_buf[count] = *info;
-		oob_buf[count + 1] = *info >> 8;
+		/* Always ignore user bytes programming. */
+		*info |= 0xffff;
 	}
 }
 
@@ -698,18 +713,92 @@ static int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf,
 	return meson_nfc_write_page_sub(nand, page, 1);
 }
 
+static u32 meson_nfc_get_oob_bytes(struct nand_chip *nand)
+{
+	struct mtd_info *mtd = nand_to_mtd(nand);
+
+	return mtd->oobsize - nand->ecc.steps * NFC_OOB_PER_ECC(nand);
+}
+
+static int __meson_nfc_write_oob(struct nand_chip *nand, int page, u8 *oob_buf)
+{
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	struct mtd_info *mtd = nand_to_mtd(nand);
+	u32 page_size = mtd->writesize + mtd->oobsize;
+	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
+	int ret;
+
+	if (!oob_bytes)
+		return 0;
+
+	ret = nand_prog_page_begin_op(nand, page, 0, NULL, 0);
+	if (ret)
+		return ret;
+
+	memcpy(meson_chip->oob_buf, oob_buf + (mtd->oobsize - oob_bytes),
+	       oob_bytes);
+
+	ret = nand_change_write_column_op(nand, page_size - oob_bytes,
+					  meson_chip->oob_buf,
+					  oob_bytes, false);
+	if (ret)
+		return ret;
+
+	return nand_prog_page_end_op(nand);
+}
+
+static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
+				u8 *oob_buf)
+{
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+	struct mtd_info *mtd = nand_to_mtd(nand);
+	u32 oob_bytes;
+	u32 page_size;
+	int ret;
+
+	oob_bytes = meson_nfc_get_oob_bytes(nand);
+
+	if (!oob_bytes)
+		return 0;
+
+	ret = nand_read_page_op(nand, page, 0, NULL, 0);
+	if (ret)
+		return ret;
+
+	page_size = mtd->writesize + mtd->oobsize;
+
+	ret = nand_change_read_column_op(nand, page_size - oob_bytes,
+					 meson_chip->oob_buf,
+					 oob_bytes, false);
+
+	if (!ret)
+		memcpy(oob_buf + (mtd->oobsize - oob_bytes),
+		       meson_chip->oob_buf,
+		       oob_bytes);
+
+	return ret;
+}
+
 static int meson_nfc_write_page_hwecc(struct nand_chip *nand,
 				      const u8 *buf, int oob_required, int page)
 {
 	struct mtd_info *mtd = nand_to_mtd(nand);
 	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 	u8 *oob_buf = nand->oob_poi;
+	int ret;
 
 	memcpy(meson_chip->data_buf, buf, mtd->writesize);
 	memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE);
 	meson_nfc_set_user_byte(nand, oob_buf);
 
-	return meson_nfc_write_page_sub(nand, page, 0);
+	ret = meson_nfc_write_page_sub(nand, page, 0);
+	if (ret)
+		return ret;
+
+	if (oob_required)
+		ret = __meson_nfc_write_oob(nand, page, oob_buf);
+
+	return ret;
 }
 
 static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
@@ -783,7 +872,7 @@ static int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf,
 	if (ret)
 		return ret;
 
-	meson_nfc_get_data_oob(nand, buf, oob_buf);
+	meson_nfc_get_data_oob(nand, buf, oob_required ? oob_buf : NULL);
 
 	return 0;
 }
@@ -803,12 +892,12 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
 	if (ret)
 		return ret;
 
-	meson_nfc_get_user_byte(nand, oob_buf);
 	ret = meson_nfc_ecc_correct(nand, &bitflips, &correct_bitmap);
 	if (ret == ECC_CHECK_RETURN_FF) {
 		if (buf)
 			memset(buf, 0xff, mtd->writesize);
 		memset(oob_buf, 0xff, mtd->oobsize);
+		return bitflips;
 	} else if (ret < 0) {
 		if ((nand->options & NAND_NEED_SCRAMBLING) || !buf) {
 			mtd->ecc_stats.failed++;
@@ -820,12 +909,14 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
 
 		for (i = 0; i < nand->ecc.steps ; i++) {
 			u8 *data = buf + i * ecc->size;
-			u8 *oob = nand->oob_poi + i * (ecc->bytes + 2);
+			u8 *oob = nand->oob_poi + i * NFC_OOB_PER_ECC(nand);
 
 			if (correct_bitmap & BIT_ULL(i))
 				continue;
+
 			ret = nand_check_erased_ecc_chunk(data,	ecc->size,
-							  oob, ecc->bytes + 2,
+							  oob,
+							  NFC_OOB_PER_ECC(nand),
 							  NULL, 0,
 							  ecc->strength);
 			if (ret < 0) {
@@ -839,17 +930,30 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
 		memcpy(buf, meson_chip->data_buf, mtd->writesize);
 	}
 
+	if (oob_required)
+		__meson_nfc_read_oob(nand, page, oob_buf);
+
 	return bitflips;
 }
 
 static int meson_nfc_read_oob_raw(struct nand_chip *nand, int page)
 {
-	return meson_nfc_read_page_raw(nand, NULL, 1, page);
+	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
 }
 
 static int meson_nfc_read_oob(struct nand_chip *nand, int page)
 {
-	return meson_nfc_read_page_hwecc(nand, NULL, 1, page);
+	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
+}
+
+static int meson_nfc_write_oob_raw(struct nand_chip *nand, int page)
+{
+	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
+}
+
+static int meson_nfc_write_oob(struct nand_chip *nand, int page)
+{
+	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
 }
 
 static bool meson_nfc_is_buffer_dma_safe(const void *buffer)
@@ -982,7 +1086,7 @@ static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
 	if (section >= nand->ecc.steps)
 		return -ERANGE;
 
-	oobregion->offset =  2 + (section * (2 + nand->ecc.bytes));
+	oobregion->offset = NFC_USER_BYTES + section * NFC_OOB_PER_ECC(nand);
 	oobregion->length = nand->ecc.bytes;
 
 	return 0;
@@ -992,12 +1096,16 @@ static int meson_ooblayout_free(struct mtd_info *mtd, int section,
 				struct mtd_oob_region *oobregion)
 {
 	struct nand_chip *nand = mtd_to_nand(mtd);
+	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
 
 	if (section >= nand->ecc.steps)
 		return -ERANGE;
 
-	oobregion->offset = section * (2 + nand->ecc.bytes);
-	oobregion->length = 2;
+	/* Split rest of OOB area (not covered by ECC engine) per each
+	 * ECC section. This will be OOB data available to user.
+	 */
+	oobregion->offset = (section + nand->ecc.steps) * NFC_OOB_PER_ECC(nand);
+	oobregion->length = oob_bytes / nand->ecc.steps;
 
 	return 0;
 }
@@ -1184,6 +1292,9 @@ static int meson_nand_bch_mode(struct nand_chip *nand)
 
 static void meson_nand_detach_chip(struct nand_chip *nand)
 {
+	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+
+	kfree(meson_chip->oob_buf);
 	meson_nfc_free_buffer(nand);
 }
 
@@ -1225,9 +1336,9 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
 	nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
 	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
 	nand->ecc.write_page = meson_nfc_write_page_hwecc;
-	nand->ecc.write_oob_raw = nand_write_oob_std;
-	nand->ecc.write_oob = nand_write_oob_std;
 
+	nand->ecc.write_oob_raw = meson_nfc_write_oob_raw;
+	nand->ecc.write_oob = meson_nfc_write_oob;
 	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
 	nand->ecc.read_page = meson_nfc_read_page_hwecc;
 	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
@@ -1237,9 +1348,16 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
 		dev_err(nfc->dev, "16bits bus width not supported");
 		return -EINVAL;
 	}
+
+	meson_chip->oob_buf = kmalloc(nand->ecc.bytes, GFP_KERNEL);
+	if (!meson_chip->oob_buf)
+		return -ENOMEM;
+
 	ret = meson_chip_buffer_init(nand);
-	if (ret)
+	if (ret) {
+		kfree(meson_chip->oob_buf);
 		return -ENOMEM;
+	}
 
 	return ret;
 }
-- 
2.35.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH v4 3/5] mtd: rawnand: meson: always read whole OOB bytes
  2023-05-15  9:44 ` Arseniy Krasnov
  (?)
  (?)
@ 2023-05-15  9:44   ` Arseniy Krasnov
  -1 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-15  9:44 UTC (permalink / raw)
  To: Liang Yang, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Jianxin Pan, Yixun Lan
  Cc: oxffffaa, kernel, Arseniy Krasnov, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

This changes size of read access to OOB area by reading all bytes of
OOB (free bytes + ECC engine bytes).

Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
---
 drivers/mtd/nand/raw/meson_nand.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
index 8526a6b87720..a31106c943d7 100644
--- a/drivers/mtd/nand/raw/meson_nand.c
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -755,6 +755,30 @@ static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
 	u32 oob_bytes;
 	u32 page_size;
 	int ret;
+	int i;
+
+	/* Read ECC codes and user bytes. */
+	for (i = 0; i < nand->ecc.steps; i++) {
+		u32 ecc_offs = nand->ecc.size * (i + 1) +
+			       NFC_OOB_PER_ECC(nand) * i;
+
+		ret = nand_read_page_op(nand, page, 0, NULL, 0);
+		if (ret)
+			return ret;
+
+		/* Use temporary buffer, because 'nand_change_read_column_op()'
+		 * seems work with some alignment, so we can't read data to
+		 * 'oob_buf' directly.
+		 */
+		ret = nand_change_read_column_op(nand, ecc_offs, meson_chip->oob_buf,
+						 NFC_OOB_PER_ECC(nand), false);
+		if (ret)
+			return ret;
+
+		memcpy(oob_buf + i * NFC_OOB_PER_ECC(nand),
+		       meson_chip->oob_buf,
+		       NFC_OOB_PER_ECC(nand));
+	}
 
 	oob_bytes = meson_nfc_get_oob_bytes(nand);
 
-- 
2.35.0


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

* [PATCH v4 3/5] mtd: rawnand: meson: always read whole OOB bytes
@ 2023-05-15  9:44   ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-15  9:44 UTC (permalink / raw)
  To: Liang Yang, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Jianxin Pan, Yixun Lan
  Cc: oxffffaa, kernel, Arseniy Krasnov, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

This changes size of read access to OOB area by reading all bytes of
OOB (free bytes + ECC engine bytes).

Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
---
 drivers/mtd/nand/raw/meson_nand.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
index 8526a6b87720..a31106c943d7 100644
--- a/drivers/mtd/nand/raw/meson_nand.c
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -755,6 +755,30 @@ static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
 	u32 oob_bytes;
 	u32 page_size;
 	int ret;
+	int i;
+
+	/* Read ECC codes and user bytes. */
+	for (i = 0; i < nand->ecc.steps; i++) {
+		u32 ecc_offs = nand->ecc.size * (i + 1) +
+			       NFC_OOB_PER_ECC(nand) * i;
+
+		ret = nand_read_page_op(nand, page, 0, NULL, 0);
+		if (ret)
+			return ret;
+
+		/* Use temporary buffer, because 'nand_change_read_column_op()'
+		 * seems work with some alignment, so we can't read data to
+		 * 'oob_buf' directly.
+		 */
+		ret = nand_change_read_column_op(nand, ecc_offs, meson_chip->oob_buf,
+						 NFC_OOB_PER_ECC(nand), false);
+		if (ret)
+			return ret;
+
+		memcpy(oob_buf + i * NFC_OOB_PER_ECC(nand),
+		       meson_chip->oob_buf,
+		       NFC_OOB_PER_ECC(nand));
+	}
 
 	oob_bytes = meson_nfc_get_oob_bytes(nand);
 
-- 
2.35.0


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

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

* [PATCH v4 3/5] mtd: rawnand: meson: always read whole OOB bytes
@ 2023-05-15  9:44   ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-15  9:44 UTC (permalink / raw)
  To: Liang Yang, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Jianxin Pan, Yixun Lan
  Cc: oxffffaa, kernel, Arseniy Krasnov, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

This changes size of read access to OOB area by reading all bytes of
OOB (free bytes + ECC engine bytes).

Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
---
 drivers/mtd/nand/raw/meson_nand.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
index 8526a6b87720..a31106c943d7 100644
--- a/drivers/mtd/nand/raw/meson_nand.c
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -755,6 +755,30 @@ static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
 	u32 oob_bytes;
 	u32 page_size;
 	int ret;
+	int i;
+
+	/* Read ECC codes and user bytes. */
+	for (i = 0; i < nand->ecc.steps; i++) {
+		u32 ecc_offs = nand->ecc.size * (i + 1) +
+			       NFC_OOB_PER_ECC(nand) * i;
+
+		ret = nand_read_page_op(nand, page, 0, NULL, 0);
+		if (ret)
+			return ret;
+
+		/* Use temporary buffer, because 'nand_change_read_column_op()'
+		 * seems work with some alignment, so we can't read data to
+		 * 'oob_buf' directly.
+		 */
+		ret = nand_change_read_column_op(nand, ecc_offs, meson_chip->oob_buf,
+						 NFC_OOB_PER_ECC(nand), false);
+		if (ret)
+			return ret;
+
+		memcpy(oob_buf + i * NFC_OOB_PER_ECC(nand),
+		       meson_chip->oob_buf,
+		       NFC_OOB_PER_ECC(nand));
+	}
 
 	oob_bytes = meson_nfc_get_oob_bytes(nand);
 
-- 
2.35.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* [PATCH v4 3/5] mtd: rawnand: meson: always read whole OOB bytes
@ 2023-05-15  9:44   ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-15  9:44 UTC (permalink / raw)
  To: Liang Yang, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Jianxin Pan, Yixun Lan
  Cc: oxffffaa, kernel, Arseniy Krasnov, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

This changes size of read access to OOB area by reading all bytes of
OOB (free bytes + ECC engine bytes).

Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
---
 drivers/mtd/nand/raw/meson_nand.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
index 8526a6b87720..a31106c943d7 100644
--- a/drivers/mtd/nand/raw/meson_nand.c
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -755,6 +755,30 @@ static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
 	u32 oob_bytes;
 	u32 page_size;
 	int ret;
+	int i;
+
+	/* Read ECC codes and user bytes. */
+	for (i = 0; i < nand->ecc.steps; i++) {
+		u32 ecc_offs = nand->ecc.size * (i + 1) +
+			       NFC_OOB_PER_ECC(nand) * i;
+
+		ret = nand_read_page_op(nand, page, 0, NULL, 0);
+		if (ret)
+			return ret;
+
+		/* Use temporary buffer, because 'nand_change_read_column_op()'
+		 * seems work with some alignment, so we can't read data to
+		 * 'oob_buf' directly.
+		 */
+		ret = nand_change_read_column_op(nand, ecc_offs, meson_chip->oob_buf,
+						 NFC_OOB_PER_ECC(nand), false);
+		if (ret)
+			return ret;
+
+		memcpy(oob_buf + i * NFC_OOB_PER_ECC(nand),
+		       meson_chip->oob_buf,
+		       NFC_OOB_PER_ECC(nand));
+	}
 
 	oob_bytes = meson_nfc_get_oob_bytes(nand);
 
-- 
2.35.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH v4 4/5] mtd: rawnand: meson: check buffer length
  2023-05-15  9:44 ` Arseniy Krasnov
  (?)
  (?)
@ 2023-05-15  9:44   ` Arseniy Krasnov
  -1 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-15  9:44 UTC (permalink / raw)
  To: Liang Yang, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Yixun Lan, Jianxin Pan
  Cc: oxffffaa, kernel, Arseniy Krasnov, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

This NAND controller has limited buffer length, so check it before
command execution to avoid length trim. Also check MTD write size on
chip attach.

Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
---
 drivers/mtd/nand/raw/meson_nand.c | 22 +++++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
index a31106c943d7..dc0d7160520b 100644
--- a/drivers/mtd/nand/raw/meson_nand.c
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -111,6 +111,8 @@
 #define NFC_USER_BYTES		2
 #define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
 
+#define NFC_CMD_RAW_LEN		GENMASK(13, 0)
+
 struct meson_nfc_nand_chip {
 	struct list_head node;
 	struct nand_chip nand;
@@ -284,7 +286,7 @@ static void meson_nfc_cmd_access(struct nand_chip *nand, int raw, bool dir,
 
 	if (raw) {
 		len = mtd->writesize + mtd->oobsize;
-		cmd = (len & GENMASK(13, 0)) | scrambler | DMA_DIR(dir);
+		cmd = len | scrambler | DMA_DIR(dir);
 		writel(cmd, nfc->reg_base + NFC_REG_CMD);
 		return;
 	}
@@ -562,6 +564,9 @@ static int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len)
 	u32 cmd;
 	u8 *info;
 
+	if (len > NFC_CMD_RAW_LEN)
+		return -EINVAL;
+
 	info = kzalloc(PER_INFO_BYTE, GFP_KERNEL);
 	if (!info)
 		return -ENOMEM;
@@ -571,7 +576,7 @@ static int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len)
 	if (ret)
 		goto out;
 
-	cmd = NFC_CMD_N2M | (len & GENMASK(13, 0));
+	cmd = NFC_CMD_N2M | len;
 	writel(cmd, nfc->reg_base + NFC_REG_CMD);
 
 	meson_nfc_drain_cmd(nfc);
@@ -590,12 +595,15 @@ static int meson_nfc_write_buf(struct nand_chip *nand, u8 *buf, int len)
 	int ret = 0;
 	u32 cmd;
 
+	if (len > NFC_CMD_RAW_LEN)
+		return -EINVAL;
+
 	ret = meson_nfc_dma_buffer_setup(nand, buf, len, NULL,
 					 0, DMA_TO_DEVICE);
 	if (ret)
 		return ret;
 
-	cmd = NFC_CMD_M2N | (len & GENMASK(13, 0));
+	cmd = NFC_CMD_M2N | len;
 	writel(cmd, nfc->reg_base + NFC_REG_CMD);
 
 	meson_nfc_drain_cmd(nfc);
@@ -1328,6 +1336,7 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
 	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 	struct mtd_info *mtd = nand_to_mtd(nand);
 	int nsectors = mtd->writesize / 1024;
+	int raw_writesize;
 	int ret;
 
 	if (!mtd->name) {
@@ -1339,6 +1348,13 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
 			return -ENOMEM;
 	}
 
+	raw_writesize = mtd->writesize + mtd->oobsize;
+	if (raw_writesize > NFC_CMD_RAW_LEN) {
+		dev_err(nfc->dev, "too big write size in raw mode: %d > %ld\n",
+			raw_writesize, NFC_CMD_RAW_LEN);
+		return -EINVAL;
+	}
+
 	if (nand->bbt_options & NAND_BBT_USE_FLASH)
 		nand->bbt_options |= NAND_BBT_NO_OOB;
 
-- 
2.35.0


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

* [PATCH v4 4/5] mtd: rawnand: meson: check buffer length
@ 2023-05-15  9:44   ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-15  9:44 UTC (permalink / raw)
  To: Liang Yang, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Yixun Lan, Jianxin Pan
  Cc: oxffffaa, kernel, Arseniy Krasnov, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

This NAND controller has limited buffer length, so check it before
command execution to avoid length trim. Also check MTD write size on
chip attach.

Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
---
 drivers/mtd/nand/raw/meson_nand.c | 22 +++++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
index a31106c943d7..dc0d7160520b 100644
--- a/drivers/mtd/nand/raw/meson_nand.c
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -111,6 +111,8 @@
 #define NFC_USER_BYTES		2
 #define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
 
+#define NFC_CMD_RAW_LEN		GENMASK(13, 0)
+
 struct meson_nfc_nand_chip {
 	struct list_head node;
 	struct nand_chip nand;
@@ -284,7 +286,7 @@ static void meson_nfc_cmd_access(struct nand_chip *nand, int raw, bool dir,
 
 	if (raw) {
 		len = mtd->writesize + mtd->oobsize;
-		cmd = (len & GENMASK(13, 0)) | scrambler | DMA_DIR(dir);
+		cmd = len | scrambler | DMA_DIR(dir);
 		writel(cmd, nfc->reg_base + NFC_REG_CMD);
 		return;
 	}
@@ -562,6 +564,9 @@ static int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len)
 	u32 cmd;
 	u8 *info;
 
+	if (len > NFC_CMD_RAW_LEN)
+		return -EINVAL;
+
 	info = kzalloc(PER_INFO_BYTE, GFP_KERNEL);
 	if (!info)
 		return -ENOMEM;
@@ -571,7 +576,7 @@ static int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len)
 	if (ret)
 		goto out;
 
-	cmd = NFC_CMD_N2M | (len & GENMASK(13, 0));
+	cmd = NFC_CMD_N2M | len;
 	writel(cmd, nfc->reg_base + NFC_REG_CMD);
 
 	meson_nfc_drain_cmd(nfc);
@@ -590,12 +595,15 @@ static int meson_nfc_write_buf(struct nand_chip *nand, u8 *buf, int len)
 	int ret = 0;
 	u32 cmd;
 
+	if (len > NFC_CMD_RAW_LEN)
+		return -EINVAL;
+
 	ret = meson_nfc_dma_buffer_setup(nand, buf, len, NULL,
 					 0, DMA_TO_DEVICE);
 	if (ret)
 		return ret;
 
-	cmd = NFC_CMD_M2N | (len & GENMASK(13, 0));
+	cmd = NFC_CMD_M2N | len;
 	writel(cmd, nfc->reg_base + NFC_REG_CMD);
 
 	meson_nfc_drain_cmd(nfc);
@@ -1328,6 +1336,7 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
 	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 	struct mtd_info *mtd = nand_to_mtd(nand);
 	int nsectors = mtd->writesize / 1024;
+	int raw_writesize;
 	int ret;
 
 	if (!mtd->name) {
@@ -1339,6 +1348,13 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
 			return -ENOMEM;
 	}
 
+	raw_writesize = mtd->writesize + mtd->oobsize;
+	if (raw_writesize > NFC_CMD_RAW_LEN) {
+		dev_err(nfc->dev, "too big write size in raw mode: %d > %ld\n",
+			raw_writesize, NFC_CMD_RAW_LEN);
+		return -EINVAL;
+	}
+
 	if (nand->bbt_options & NAND_BBT_USE_FLASH)
 		nand->bbt_options |= NAND_BBT_NO_OOB;
 
-- 
2.35.0


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

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

* [PATCH v4 4/5] mtd: rawnand: meson: check buffer length
@ 2023-05-15  9:44   ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-15  9:44 UTC (permalink / raw)
  To: Liang Yang, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Yixun Lan, Jianxin Pan
  Cc: oxffffaa, kernel, Arseniy Krasnov, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

This NAND controller has limited buffer length, so check it before
command execution to avoid length trim. Also check MTD write size on
chip attach.

Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
---
 drivers/mtd/nand/raw/meson_nand.c | 22 +++++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
index a31106c943d7..dc0d7160520b 100644
--- a/drivers/mtd/nand/raw/meson_nand.c
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -111,6 +111,8 @@
 #define NFC_USER_BYTES		2
 #define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
 
+#define NFC_CMD_RAW_LEN		GENMASK(13, 0)
+
 struct meson_nfc_nand_chip {
 	struct list_head node;
 	struct nand_chip nand;
@@ -284,7 +286,7 @@ static void meson_nfc_cmd_access(struct nand_chip *nand, int raw, bool dir,
 
 	if (raw) {
 		len = mtd->writesize + mtd->oobsize;
-		cmd = (len & GENMASK(13, 0)) | scrambler | DMA_DIR(dir);
+		cmd = len | scrambler | DMA_DIR(dir);
 		writel(cmd, nfc->reg_base + NFC_REG_CMD);
 		return;
 	}
@@ -562,6 +564,9 @@ static int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len)
 	u32 cmd;
 	u8 *info;
 
+	if (len > NFC_CMD_RAW_LEN)
+		return -EINVAL;
+
 	info = kzalloc(PER_INFO_BYTE, GFP_KERNEL);
 	if (!info)
 		return -ENOMEM;
@@ -571,7 +576,7 @@ static int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len)
 	if (ret)
 		goto out;
 
-	cmd = NFC_CMD_N2M | (len & GENMASK(13, 0));
+	cmd = NFC_CMD_N2M | len;
 	writel(cmd, nfc->reg_base + NFC_REG_CMD);
 
 	meson_nfc_drain_cmd(nfc);
@@ -590,12 +595,15 @@ static int meson_nfc_write_buf(struct nand_chip *nand, u8 *buf, int len)
 	int ret = 0;
 	u32 cmd;
 
+	if (len > NFC_CMD_RAW_LEN)
+		return -EINVAL;
+
 	ret = meson_nfc_dma_buffer_setup(nand, buf, len, NULL,
 					 0, DMA_TO_DEVICE);
 	if (ret)
 		return ret;
 
-	cmd = NFC_CMD_M2N | (len & GENMASK(13, 0));
+	cmd = NFC_CMD_M2N | len;
 	writel(cmd, nfc->reg_base + NFC_REG_CMD);
 
 	meson_nfc_drain_cmd(nfc);
@@ -1328,6 +1336,7 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
 	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 	struct mtd_info *mtd = nand_to_mtd(nand);
 	int nsectors = mtd->writesize / 1024;
+	int raw_writesize;
 	int ret;
 
 	if (!mtd->name) {
@@ -1339,6 +1348,13 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
 			return -ENOMEM;
 	}
 
+	raw_writesize = mtd->writesize + mtd->oobsize;
+	if (raw_writesize > NFC_CMD_RAW_LEN) {
+		dev_err(nfc->dev, "too big write size in raw mode: %d > %ld\n",
+			raw_writesize, NFC_CMD_RAW_LEN);
+		return -EINVAL;
+	}
+
 	if (nand->bbt_options & NAND_BBT_USE_FLASH)
 		nand->bbt_options |= NAND_BBT_NO_OOB;
 
-- 
2.35.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* [PATCH v4 4/5] mtd: rawnand: meson: check buffer length
@ 2023-05-15  9:44   ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-15  9:44 UTC (permalink / raw)
  To: Liang Yang, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Yixun Lan, Jianxin Pan
  Cc: oxffffaa, kernel, Arseniy Krasnov, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

This NAND controller has limited buffer length, so check it before
command execution to avoid length trim. Also check MTD write size on
chip attach.

Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
---
 drivers/mtd/nand/raw/meson_nand.c | 22 +++++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
index a31106c943d7..dc0d7160520b 100644
--- a/drivers/mtd/nand/raw/meson_nand.c
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -111,6 +111,8 @@
 #define NFC_USER_BYTES		2
 #define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
 
+#define NFC_CMD_RAW_LEN		GENMASK(13, 0)
+
 struct meson_nfc_nand_chip {
 	struct list_head node;
 	struct nand_chip nand;
@@ -284,7 +286,7 @@ static void meson_nfc_cmd_access(struct nand_chip *nand, int raw, bool dir,
 
 	if (raw) {
 		len = mtd->writesize + mtd->oobsize;
-		cmd = (len & GENMASK(13, 0)) | scrambler | DMA_DIR(dir);
+		cmd = len | scrambler | DMA_DIR(dir);
 		writel(cmd, nfc->reg_base + NFC_REG_CMD);
 		return;
 	}
@@ -562,6 +564,9 @@ static int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len)
 	u32 cmd;
 	u8 *info;
 
+	if (len > NFC_CMD_RAW_LEN)
+		return -EINVAL;
+
 	info = kzalloc(PER_INFO_BYTE, GFP_KERNEL);
 	if (!info)
 		return -ENOMEM;
@@ -571,7 +576,7 @@ static int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len)
 	if (ret)
 		goto out;
 
-	cmd = NFC_CMD_N2M | (len & GENMASK(13, 0));
+	cmd = NFC_CMD_N2M | len;
 	writel(cmd, nfc->reg_base + NFC_REG_CMD);
 
 	meson_nfc_drain_cmd(nfc);
@@ -590,12 +595,15 @@ static int meson_nfc_write_buf(struct nand_chip *nand, u8 *buf, int len)
 	int ret = 0;
 	u32 cmd;
 
+	if (len > NFC_CMD_RAW_LEN)
+		return -EINVAL;
+
 	ret = meson_nfc_dma_buffer_setup(nand, buf, len, NULL,
 					 0, DMA_TO_DEVICE);
 	if (ret)
 		return ret;
 
-	cmd = NFC_CMD_M2N | (len & GENMASK(13, 0));
+	cmd = NFC_CMD_M2N | len;
 	writel(cmd, nfc->reg_base + NFC_REG_CMD);
 
 	meson_nfc_drain_cmd(nfc);
@@ -1328,6 +1336,7 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
 	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
 	struct mtd_info *mtd = nand_to_mtd(nand);
 	int nsectors = mtd->writesize / 1024;
+	int raw_writesize;
 	int ret;
 
 	if (!mtd->name) {
@@ -1339,6 +1348,13 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
 			return -ENOMEM;
 	}
 
+	raw_writesize = mtd->writesize + mtd->oobsize;
+	if (raw_writesize > NFC_CMD_RAW_LEN) {
+		dev_err(nfc->dev, "too big write size in raw mode: %d > %ld\n",
+			raw_writesize, NFC_CMD_RAW_LEN);
+		return -EINVAL;
+	}
+
 	if (nand->bbt_options & NAND_BBT_USE_FLASH)
 		nand->bbt_options |= NAND_BBT_NO_OOB;
 
-- 
2.35.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* [PATCH v4 5/5] mtd: rawnand: meson: remove unneeded bitwise OR with zeroes
  2023-05-15  9:44 ` Arseniy Krasnov
  (?)
  (?)
@ 2023-05-15  9:44   ` Arseniy Krasnov
  -1 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-15  9:44 UTC (permalink / raw)
  To: Liang Yang, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Yixun Lan, Jianxin Pan
  Cc: oxffffaa, kernel, Arseniy Krasnov, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

Both operations have no effect.

Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
---
 drivers/mtd/nand/raw/meson_nand.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
index dc0d7160520b..0aac784075a5 100644
--- a/drivers/mtd/nand/raw/meson_nand.c
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -630,12 +630,12 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
 	cmd0 = in ? NAND_CMD_READ0 : NAND_CMD_SEQIN;
 	nfc->cmdfifo.rw.cmd0 = cs | NFC_CMD_CLE | cmd0;
 
-	addrs[0] = cs | NFC_CMD_ALE | 0;
+	addrs[0] = cs | NFC_CMD_ALE;
 	if (mtd->writesize <= 512) {
 		cmd_num--;
 		row_start = 1;
 	} else {
-		addrs[1] = cs | NFC_CMD_ALE | 0;
+		addrs[1] = cs | NFC_CMD_ALE;
 		row_start = 2;
 	}
 
-- 
2.35.0


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

* [PATCH v4 5/5] mtd: rawnand: meson: remove unneeded bitwise OR with zeroes
@ 2023-05-15  9:44   ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-15  9:44 UTC (permalink / raw)
  To: Liang Yang, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Yixun Lan, Jianxin Pan
  Cc: oxffffaa, kernel, Arseniy Krasnov, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

Both operations have no effect.

Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
---
 drivers/mtd/nand/raw/meson_nand.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
index dc0d7160520b..0aac784075a5 100644
--- a/drivers/mtd/nand/raw/meson_nand.c
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -630,12 +630,12 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
 	cmd0 = in ? NAND_CMD_READ0 : NAND_CMD_SEQIN;
 	nfc->cmdfifo.rw.cmd0 = cs | NFC_CMD_CLE | cmd0;
 
-	addrs[0] = cs | NFC_CMD_ALE | 0;
+	addrs[0] = cs | NFC_CMD_ALE;
 	if (mtd->writesize <= 512) {
 		cmd_num--;
 		row_start = 1;
 	} else {
-		addrs[1] = cs | NFC_CMD_ALE | 0;
+		addrs[1] = cs | NFC_CMD_ALE;
 		row_start = 2;
 	}
 
-- 
2.35.0


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

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

* [PATCH v4 5/5] mtd: rawnand: meson: remove unneeded bitwise OR with zeroes
@ 2023-05-15  9:44   ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-15  9:44 UTC (permalink / raw)
  To: Liang Yang, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Yixun Lan, Jianxin Pan
  Cc: oxffffaa, kernel, Arseniy Krasnov, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

Both operations have no effect.

Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
---
 drivers/mtd/nand/raw/meson_nand.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
index dc0d7160520b..0aac784075a5 100644
--- a/drivers/mtd/nand/raw/meson_nand.c
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -630,12 +630,12 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
 	cmd0 = in ? NAND_CMD_READ0 : NAND_CMD_SEQIN;
 	nfc->cmdfifo.rw.cmd0 = cs | NFC_CMD_CLE | cmd0;
 
-	addrs[0] = cs | NFC_CMD_ALE | 0;
+	addrs[0] = cs | NFC_CMD_ALE;
 	if (mtd->writesize <= 512) {
 		cmd_num--;
 		row_start = 1;
 	} else {
-		addrs[1] = cs | NFC_CMD_ALE | 0;
+		addrs[1] = cs | NFC_CMD_ALE;
 		row_start = 2;
 	}
 
-- 
2.35.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* [PATCH v4 5/5] mtd: rawnand: meson: remove unneeded bitwise OR with zeroes
@ 2023-05-15  9:44   ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-15  9:44 UTC (permalink / raw)
  To: Liang Yang, Miquel Raynal, Richard Weinberger,
	Vignesh Raghavendra, Neil Armstrong, Kevin Hilman, Jerome Brunet,
	Martin Blumenstingl, Yixun Lan, Jianxin Pan
  Cc: oxffffaa, kernel, Arseniy Krasnov, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

Both operations have no effect.

Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
---
 drivers/mtd/nand/raw/meson_nand.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
index dc0d7160520b..0aac784075a5 100644
--- a/drivers/mtd/nand/raw/meson_nand.c
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -630,12 +630,12 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
 	cmd0 = in ? NAND_CMD_READ0 : NAND_CMD_SEQIN;
 	nfc->cmdfifo.rw.cmd0 = cs | NFC_CMD_CLE | cmd0;
 
-	addrs[0] = cs | NFC_CMD_ALE | 0;
+	addrs[0] = cs | NFC_CMD_ALE;
 	if (mtd->writesize <= 512) {
 		cmd_num--;
 		row_start = 1;
 	} else {
-		addrs[1] = cs | NFC_CMD_ALE | 0;
+		addrs[1] = cs | NFC_CMD_ALE;
 		row_start = 2;
 	}
 
-- 
2.35.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
  2023-05-15  9:44   ` Arseniy Krasnov
  (?)
  (?)
@ 2023-05-22 15:05     ` Miquel Raynal
  -1 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-22 15:05 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:

> This fixes read/write functionality by:
> 1) Changing NFC_CMD_RB_INT bit value.

I guess this is a separate fix

> 2) Adding extra NAND_CMD_STATUS command on each r/w request.

Is this really needed? Looks like you're delaying the next op only. Is
using a delay enough? If yes, then it's probably the wrong approach.

> 3) Adding extra idle commands during r/w request.

Question about this below, might also be a patch on its own?

> 4) Adding extra NAND_CMD_READ0 on each read request.
> 
> Without this patch driver works unstable, for example there are a lot
> of ECC errors.

I believe all the fixes should be Cc'ed to stable, please add in your
commits:

Cc: stable@...

> 
> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
> Suggested-by: Liang Yang <liang.yang@amlogic.com>
> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> ---
>  drivers/mtd/nand/raw/meson_nand.c | 30 +++++++++++++++++++++---------
>  1 file changed, 21 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> index 074e14225c06..2f4d8c84186b 100644
> --- a/drivers/mtd/nand/raw/meson_nand.c
> +++ b/drivers/mtd/nand/raw/meson_nand.c
> @@ -37,7 +37,7 @@
>  #define NFC_CMD_SCRAMBLER_ENABLE	BIT(19)
>  #define NFC_CMD_SCRAMBLER_DISABLE	0
>  #define NFC_CMD_SHORTMODE_DISABLE	0
> -#define NFC_CMD_RB_INT		BIT(14)
> +#define NFC_CMD_RB_INT ((0xb << 10) | BIT(18) | BIT(16))
>  
>  #define NFC_CMD_GET_SIZE(x)	(((x) >> 22) & GENMASK(4, 0))
>  
> @@ -392,7 +392,7 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>  	}
>  }
>  
> -static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
> +static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
>  {
>  	u32 cmd, cfg;
>  	int ret = 0;
> @@ -407,17 +407,29 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>  
>  	reinit_completion(&nfc->completion);
>  
> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +	meson_nfc_cmd_idle(nfc, 5);

Why 5 and 2 below? They look like magic values. Is this totally
experimental?

> +
>  	/* use the max erase time as the maximum clock for waiting R/B */
> -	cmd = NFC_CMD_RB | NFC_CMD_RB_INT
> -		| nfc->param.chip_select | nfc->timing.tbers_max;

This is not documented in the commit log, is it?

> +	cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +	meson_nfc_cmd_idle(nfc, 2);
>  
>  	ret = wait_for_completion_timeout(&nfc->completion,
>  					  msecs_to_jiffies(timeout_ms));
>  	if (ret == 0)
> -		ret = -1;
> +		return -1;

Please use real error codes, such as ETIMEDOUT.

>  
> -	return ret;
> +	if (!cmd_read0)
> +		return 0;
> +
> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_READ0;

This looks really wrong, I don't get why you would need to send an
expensive READ0 command.

> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +	meson_nfc_drain_cmd(nfc);
> +	meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
> +
> +	return 0;
>  }
>  
>  static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
> @@ -623,7 +635,7 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>  	if (in) {
>  		nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART;
>  		writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
> -		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max));
> +		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max), 1);
>  	} else {
>  		meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
>  	}
> @@ -669,7 +681,7 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>  
>  	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
> -	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
> +	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max), 0);
>  
>  	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>  
> @@ -952,7 +964,7 @@ static int meson_nfc_exec_op(struct nand_chip *nand,
>  			break;
>  
>  		case NAND_OP_WAITRDY_INSTR:
> -			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms);
> +			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms, 1);
>  			if (instr->delay_ns)
>  				meson_nfc_cmd_idle(nfc, delay_idle);
>  			break;


Thanks,
Miquèl

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

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-22 15:05     ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-22 15:05 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:

> This fixes read/write functionality by:
> 1) Changing NFC_CMD_RB_INT bit value.

I guess this is a separate fix

> 2) Adding extra NAND_CMD_STATUS command on each r/w request.

Is this really needed? Looks like you're delaying the next op only. Is
using a delay enough? If yes, then it's probably the wrong approach.

> 3) Adding extra idle commands during r/w request.

Question about this below, might also be a patch on its own?

> 4) Adding extra NAND_CMD_READ0 on each read request.
> 
> Without this patch driver works unstable, for example there are a lot
> of ECC errors.

I believe all the fixes should be Cc'ed to stable, please add in your
commits:

Cc: stable@...

> 
> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
> Suggested-by: Liang Yang <liang.yang@amlogic.com>
> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> ---
>  drivers/mtd/nand/raw/meson_nand.c | 30 +++++++++++++++++++++---------
>  1 file changed, 21 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> index 074e14225c06..2f4d8c84186b 100644
> --- a/drivers/mtd/nand/raw/meson_nand.c
> +++ b/drivers/mtd/nand/raw/meson_nand.c
> @@ -37,7 +37,7 @@
>  #define NFC_CMD_SCRAMBLER_ENABLE	BIT(19)
>  #define NFC_CMD_SCRAMBLER_DISABLE	0
>  #define NFC_CMD_SHORTMODE_DISABLE	0
> -#define NFC_CMD_RB_INT		BIT(14)
> +#define NFC_CMD_RB_INT ((0xb << 10) | BIT(18) | BIT(16))
>  
>  #define NFC_CMD_GET_SIZE(x)	(((x) >> 22) & GENMASK(4, 0))
>  
> @@ -392,7 +392,7 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>  	}
>  }
>  
> -static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
> +static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
>  {
>  	u32 cmd, cfg;
>  	int ret = 0;
> @@ -407,17 +407,29 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>  
>  	reinit_completion(&nfc->completion);
>  
> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +	meson_nfc_cmd_idle(nfc, 5);

Why 5 and 2 below? They look like magic values. Is this totally
experimental?

> +
>  	/* use the max erase time as the maximum clock for waiting R/B */
> -	cmd = NFC_CMD_RB | NFC_CMD_RB_INT
> -		| nfc->param.chip_select | nfc->timing.tbers_max;

This is not documented in the commit log, is it?

> +	cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +	meson_nfc_cmd_idle(nfc, 2);
>  
>  	ret = wait_for_completion_timeout(&nfc->completion,
>  					  msecs_to_jiffies(timeout_ms));
>  	if (ret == 0)
> -		ret = -1;
> +		return -1;

Please use real error codes, such as ETIMEDOUT.

>  
> -	return ret;
> +	if (!cmd_read0)
> +		return 0;
> +
> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_READ0;

This looks really wrong, I don't get why you would need to send an
expensive READ0 command.

> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +	meson_nfc_drain_cmd(nfc);
> +	meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
> +
> +	return 0;
>  }
>  
>  static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
> @@ -623,7 +635,7 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>  	if (in) {
>  		nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART;
>  		writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
> -		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max));
> +		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max), 1);
>  	} else {
>  		meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
>  	}
> @@ -669,7 +681,7 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>  
>  	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
> -	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
> +	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max), 0);
>  
>  	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>  
> @@ -952,7 +964,7 @@ static int meson_nfc_exec_op(struct nand_chip *nand,
>  			break;
>  
>  		case NAND_OP_WAITRDY_INSTR:
> -			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms);
> +			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms, 1);
>  			if (instr->delay_ns)
>  				meson_nfc_cmd_idle(nfc, delay_idle);
>  			break;


Thanks,
Miquèl

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-22 15:05     ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-22 15:05 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:

> This fixes read/write functionality by:
> 1) Changing NFC_CMD_RB_INT bit value.

I guess this is a separate fix

> 2) Adding extra NAND_CMD_STATUS command on each r/w request.

Is this really needed? Looks like you're delaying the next op only. Is
using a delay enough? If yes, then it's probably the wrong approach.

> 3) Adding extra idle commands during r/w request.

Question about this below, might also be a patch on its own?

> 4) Adding extra NAND_CMD_READ0 on each read request.
> 
> Without this patch driver works unstable, for example there are a lot
> of ECC errors.

I believe all the fixes should be Cc'ed to stable, please add in your
commits:

Cc: stable@...

> 
> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
> Suggested-by: Liang Yang <liang.yang@amlogic.com>
> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> ---
>  drivers/mtd/nand/raw/meson_nand.c | 30 +++++++++++++++++++++---------
>  1 file changed, 21 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> index 074e14225c06..2f4d8c84186b 100644
> --- a/drivers/mtd/nand/raw/meson_nand.c
> +++ b/drivers/mtd/nand/raw/meson_nand.c
> @@ -37,7 +37,7 @@
>  #define NFC_CMD_SCRAMBLER_ENABLE	BIT(19)
>  #define NFC_CMD_SCRAMBLER_DISABLE	0
>  #define NFC_CMD_SHORTMODE_DISABLE	0
> -#define NFC_CMD_RB_INT		BIT(14)
> +#define NFC_CMD_RB_INT ((0xb << 10) | BIT(18) | BIT(16))
>  
>  #define NFC_CMD_GET_SIZE(x)	(((x) >> 22) & GENMASK(4, 0))
>  
> @@ -392,7 +392,7 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>  	}
>  }
>  
> -static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
> +static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
>  {
>  	u32 cmd, cfg;
>  	int ret = 0;
> @@ -407,17 +407,29 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>  
>  	reinit_completion(&nfc->completion);
>  
> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +	meson_nfc_cmd_idle(nfc, 5);

Why 5 and 2 below? They look like magic values. Is this totally
experimental?

> +
>  	/* use the max erase time as the maximum clock for waiting R/B */
> -	cmd = NFC_CMD_RB | NFC_CMD_RB_INT
> -		| nfc->param.chip_select | nfc->timing.tbers_max;

This is not documented in the commit log, is it?

> +	cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +	meson_nfc_cmd_idle(nfc, 2);
>  
>  	ret = wait_for_completion_timeout(&nfc->completion,
>  					  msecs_to_jiffies(timeout_ms));
>  	if (ret == 0)
> -		ret = -1;
> +		return -1;

Please use real error codes, such as ETIMEDOUT.

>  
> -	return ret;
> +	if (!cmd_read0)
> +		return 0;
> +
> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_READ0;

This looks really wrong, I don't get why you would need to send an
expensive READ0 command.

> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +	meson_nfc_drain_cmd(nfc);
> +	meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
> +
> +	return 0;
>  }
>  
>  static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
> @@ -623,7 +635,7 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>  	if (in) {
>  		nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART;
>  		writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
> -		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max));
> +		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max), 1);
>  	} else {
>  		meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
>  	}
> @@ -669,7 +681,7 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>  
>  	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
> -	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
> +	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max), 0);
>  
>  	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>  
> @@ -952,7 +964,7 @@ static int meson_nfc_exec_op(struct nand_chip *nand,
>  			break;
>  
>  		case NAND_OP_WAITRDY_INSTR:
> -			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms);
> +			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms, 1);
>  			if (instr->delay_ns)
>  				meson_nfc_cmd_idle(nfc, delay_idle);
>  			break;


Thanks,
Miquèl

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-22 15:05     ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-22 15:05 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:

> This fixes read/write functionality by:
> 1) Changing NFC_CMD_RB_INT bit value.

I guess this is a separate fix

> 2) Adding extra NAND_CMD_STATUS command on each r/w request.

Is this really needed? Looks like you're delaying the next op only. Is
using a delay enough? If yes, then it's probably the wrong approach.

> 3) Adding extra idle commands during r/w request.

Question about this below, might also be a patch on its own?

> 4) Adding extra NAND_CMD_READ0 on each read request.
> 
> Without this patch driver works unstable, for example there are a lot
> of ECC errors.

I believe all the fixes should be Cc'ed to stable, please add in your
commits:

Cc: stable@...

> 
> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
> Suggested-by: Liang Yang <liang.yang@amlogic.com>
> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> ---
>  drivers/mtd/nand/raw/meson_nand.c | 30 +++++++++++++++++++++---------
>  1 file changed, 21 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> index 074e14225c06..2f4d8c84186b 100644
> --- a/drivers/mtd/nand/raw/meson_nand.c
> +++ b/drivers/mtd/nand/raw/meson_nand.c
> @@ -37,7 +37,7 @@
>  #define NFC_CMD_SCRAMBLER_ENABLE	BIT(19)
>  #define NFC_CMD_SCRAMBLER_DISABLE	0
>  #define NFC_CMD_SHORTMODE_DISABLE	0
> -#define NFC_CMD_RB_INT		BIT(14)
> +#define NFC_CMD_RB_INT ((0xb << 10) | BIT(18) | BIT(16))
>  
>  #define NFC_CMD_GET_SIZE(x)	(((x) >> 22) & GENMASK(4, 0))
>  
> @@ -392,7 +392,7 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>  	}
>  }
>  
> -static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
> +static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
>  {
>  	u32 cmd, cfg;
>  	int ret = 0;
> @@ -407,17 +407,29 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>  
>  	reinit_completion(&nfc->completion);
>  
> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +	meson_nfc_cmd_idle(nfc, 5);

Why 5 and 2 below? They look like magic values. Is this totally
experimental?

> +
>  	/* use the max erase time as the maximum clock for waiting R/B */
> -	cmd = NFC_CMD_RB | NFC_CMD_RB_INT
> -		| nfc->param.chip_select | nfc->timing.tbers_max;

This is not documented in the commit log, is it?

> +	cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +	meson_nfc_cmd_idle(nfc, 2);
>  
>  	ret = wait_for_completion_timeout(&nfc->completion,
>  					  msecs_to_jiffies(timeout_ms));
>  	if (ret == 0)
> -		ret = -1;
> +		return -1;

Please use real error codes, such as ETIMEDOUT.

>  
> -	return ret;
> +	if (!cmd_read0)
> +		return 0;
> +
> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_READ0;

This looks really wrong, I don't get why you would need to send an
expensive READ0 command.

> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
> +	meson_nfc_drain_cmd(nfc);
> +	meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
> +
> +	return 0;
>  }
>  
>  static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
> @@ -623,7 +635,7 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>  	if (in) {
>  		nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART;
>  		writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
> -		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max));
> +		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max), 1);
>  	} else {
>  		meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
>  	}
> @@ -669,7 +681,7 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>  
>  	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
> -	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
> +	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max), 0);
>  
>  	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>  
> @@ -952,7 +964,7 @@ static int meson_nfc_exec_op(struct nand_chip *nand,
>  			break;
>  
>  		case NAND_OP_WAITRDY_INSTR:
> -			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms);
> +			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms, 1);
>  			if (instr->delay_ns)
>  				meson_nfc_cmd_idle(nfc, delay_idle);
>  			break;


Thanks,
Miquèl

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
  2023-05-15  9:44   ` Arseniy Krasnov
  (?)
  (?)
@ 2023-05-22 15:33     ` Miquel Raynal
  -1 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-22 15:33 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:36 +0300:

The title should perhaps be "only expose unprotected user OOB bytes".

> This moves free bytes of OOB to non-protected ECC area. It is needed to
> make JFFS2 works correctly with this NAND controller. Problem fires when
> JFFS2 driver writes cleanmarker to some page and later it tries to write
> to this page - write will be done successfully, but after that such page
> becomes unreadable due to invalid ECC codes. This happens because second
> write needs to update ECC codes, but it is impossible to do it correctly
> without block erase. So idea of this patch is to

"... is to use the unprotected OOB area to store the cleanmarkers, so
that they can be written by the filesystem without caring much about
the page being empty or not: the ECC codes will not be written anyway."
?

JFFS2 is only useful on tiny NAND devices, where UBI does not fit,
which are usually true SLC flashes, with the capability of writing
a page with empty (0xFF) data, and still be able to write actual data
to it later in a second write.

> split accesses to OOB
> free bytes and data on each page - now both of them does not depends on
> each other.
> 
> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> ---
>  drivers/mtd/nand/raw/meson_nand.c | 192 ++++++++++++++++++++++++------
>  1 file changed, 155 insertions(+), 37 deletions(-)
> 
> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> index 2f4d8c84186b..8526a6b87720 100644
> --- a/drivers/mtd/nand/raw/meson_nand.c
> +++ b/drivers/mtd/nand/raw/meson_nand.c
> @@ -108,6 +108,9 @@
>  
>  #define PER_INFO_BYTE		8
>  
> +#define NFC_USER_BYTES		2
> +#define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
> +
>  struct meson_nfc_nand_chip {
>  	struct list_head node;
>  	struct nand_chip nand;
> @@ -122,6 +125,7 @@ struct meson_nfc_nand_chip {
>  	u8 *data_buf;
>  	__le64 *info_buf;
>  	u32 nsels;
> +	u8 *oob_buf;
>  	u8 sels[];
>  };
>  
> @@ -338,7 +342,7 @@ static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>  	int len;
>  
> -	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
> +	len = nand->ecc.size * (i + 1) + NFC_OOB_PER_ECC(nand) * i;

This...

>  
>  	return meson_chip->data_buf + len;
>  }
> @@ -349,7 +353,7 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>  	int len, temp;
>  
>  	temp = nand->ecc.size + nand->ecc.bytes;
> -	len = (temp + 2) * i;
> +	len = (temp + NFC_USER_BYTES) * i;

... and this (same below)

are purely cosmetic, should be in a patch aside.

>  
>  	return meson_chip->data_buf + len;
>  }
> @@ -357,29 +361,47 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>  static void meson_nfc_get_data_oob(struct nand_chip *nand,
>  				   u8 *buf, u8 *oobbuf)
>  {
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	struct mtd_info *mtd = nand_to_mtd(nand);
>  	int i, oob_len = 0;
>  	u8 *dsrc, *osrc;
> +	u8 *oobtail;
>  
> -	oob_len = nand->ecc.bytes + 2;
> +	oob_len = NFC_OOB_PER_ECC(nand);
>  	for (i = 0; i < nand->ecc.steps; i++) {
>  		if (buf) {
>  			dsrc = meson_nfc_data_ptr(nand, i);
>  			memcpy(buf, dsrc, nand->ecc.size);
>  			buf += nand->ecc.size;
>  		}
> -		osrc = meson_nfc_oob_ptr(nand, i);
> -		memcpy(oobbuf, osrc, oob_len);
> -		oobbuf += oob_len;
> +
> +		if (oobbuf) {
> +			osrc = meson_nfc_oob_ptr(nand, i);
> +			memcpy(oobbuf, osrc, oob_len);
> +			oobbuf += oob_len;
> +		}
>  	}
> +
> +	if (!oobbuf)
> +		return;

The whole "if (oobbuf)" logic is nice to have, but should as well be in
a dedicated patch.

> +
> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
> +		  (nand->ecc.size + oob_len);
> +
> +	/* 'oobbuf' if already shifted to the start of unused area. */

		    is?
s/unused/user/? I'm not sure I get the comment.

> +	memcpy(oobbuf, oobtail, mtd->oobsize - nand->ecc.steps * oob_len);
>  }
>  
>  static void meson_nfc_set_data_oob(struct nand_chip *nand,
>  				   const u8 *buf, u8 *oobbuf)
>  {
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	struct mtd_info *mtd = nand_to_mtd(nand);
>  	int i, oob_len = 0;
>  	u8 *dsrc, *osrc;
> +	u8 *oobtail;
>  
> -	oob_len = nand->ecc.bytes + 2;
> +	oob_len = NFC_OOB_PER_ECC(nand);
>  	for (i = 0; i < nand->ecc.steps; i++) {
>  		if (buf) {
>  			dsrc = meson_nfc_data_ptr(nand, i);
> @@ -390,6 +412,12 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>  		memcpy(osrc, oobbuf, oob_len);
>  		oobbuf += oob_len;
>  	}
> +
> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
> +		  (nand->ecc.size + oob_len);
> +
> +	/* 'oobbuf' if already shifted to the start of unused area. */
> +	memcpy(oobtail, oobbuf, mtd->oobsize - nand->ecc.steps * oob_len);
>  }
>  
>  static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
> @@ -436,25 +464,12 @@ static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
>  {
>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>  	__le64 *info;
> -	int i, count;
> -
> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> -		info = &meson_chip->info_buf[i];
> -		*info |= oob_buf[count];
> -		*info |= oob_buf[count + 1] << 8;
> -	}
> -}
> -
> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
> -{
> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> -	__le64 *info;
> -	int i, count;
> +	int i;
>  
> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> +	for (i = 0; i < nand->ecc.steps; i++) {
>  		info = &meson_chip->info_buf[i];
> -		oob_buf[count] = *info;
> -		oob_buf[count + 1] = *info >> 8;
> +		/* Always ignore user bytes programming. */

Why?

Also, maybe I should mention the helpers:
mtd_ooblayout_get_eccbytes and co
They are very useful to deal with OOB bytes. Everything seems extremely
hardcoded in this driver, while the user can tune read/write OOB
operations.

> +		*info |= 0xffff;
>  	}
>  }
>  
> @@ -698,18 +713,92 @@ static int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf,
>  	return meson_nfc_write_page_sub(nand, page, 1);
>  }
>  
> +static u32 meson_nfc_get_oob_bytes(struct nand_chip *nand)
> +{
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +
> +	return mtd->oobsize - nand->ecc.steps * NFC_OOB_PER_ECC(nand);
> +}
> +
> +static int __meson_nfc_write_oob(struct nand_chip *nand, int page, u8 *oob_buf)
> +{
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +	u32 page_size = mtd->writesize + mtd->oobsize;
> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
> +	int ret;
> +
> +	if (!oob_bytes)
> +		return 0;
> +
> +	ret = nand_prog_page_begin_op(nand, page, 0, NULL, 0);
> +	if (ret)
> +		return ret;
> +
> +	memcpy(meson_chip->oob_buf, oob_buf + (mtd->oobsize - oob_bytes),
> +	       oob_bytes);
> +
> +	ret = nand_change_write_column_op(nand, page_size - oob_bytes,
> +					  meson_chip->oob_buf,
> +					  oob_bytes, false);
> +	if (ret)
> +		return ret;
> +
> +	return nand_prog_page_end_op(nand);
> +}
> +
> +static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
> +				u8 *oob_buf)
> +{
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +	u32 oob_bytes;
> +	u32 page_size;
> +	int ret;
> +
> +	oob_bytes = meson_nfc_get_oob_bytes(nand);
> +
> +	if (!oob_bytes)
> +		return 0;
> +
> +	ret = nand_read_page_op(nand, page, 0, NULL, 0);
> +	if (ret)
> +		return ret;
> +
> +	page_size = mtd->writesize + mtd->oobsize;
> +
> +	ret = nand_change_read_column_op(nand, page_size - oob_bytes,
> +					 meson_chip->oob_buf,
> +					 oob_bytes, false);
> +
> +	if (!ret)
> +		memcpy(oob_buf + (mtd->oobsize - oob_bytes),
> +		       meson_chip->oob_buf,
> +		       oob_bytes);
> +
> +	return ret;
> +}
> +
>  static int meson_nfc_write_page_hwecc(struct nand_chip *nand,
>  				      const u8 *buf, int oob_required, int page)
>  {
>  	struct mtd_info *mtd = nand_to_mtd(nand);
>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>  	u8 *oob_buf = nand->oob_poi;
> +	int ret;
>  
>  	memcpy(meson_chip->data_buf, buf, mtd->writesize);
>  	memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE);
>  	meson_nfc_set_user_byte(nand, oob_buf);
>  
> -	return meson_nfc_write_page_sub(nand, page, 0);
> +	ret = meson_nfc_write_page_sub(nand, page, 0);
> +	if (ret)
> +		return ret;
> +
> +	if (oob_required)
> +		ret = __meson_nfc_write_oob(nand, page, oob_buf);

You should provide all the data including OOB bytes in a single write
call, otherwise you perform two writes on the same page, that's not
what this helper is expected to do.

> +
> +	return ret;
>  }
>  
>  static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
> @@ -783,7 +872,7 @@ static int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf,
>  	if (ret)
>  		return ret;
>  
> -	meson_nfc_get_data_oob(nand, buf, oob_buf);
> +	meson_nfc_get_data_oob(nand, buf, oob_required ? oob_buf : NULL);
>  
>  	return 0;
>  }
> @@ -803,12 +892,12 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>  	if (ret)
>  		return ret;
>  
> -	meson_nfc_get_user_byte(nand, oob_buf);
>  	ret = meson_nfc_ecc_correct(nand, &bitflips, &correct_bitmap);
>  	if (ret == ECC_CHECK_RETURN_FF) {
>  		if (buf)
>  			memset(buf, 0xff, mtd->writesize);
>  		memset(oob_buf, 0xff, mtd->oobsize);
> +		return bitflips;

That is something else => other fix => other patch?

>  	} else if (ret < 0) {
>  		if ((nand->options & NAND_NEED_SCRAMBLING) || !buf) {
>  			mtd->ecc_stats.failed++;
> @@ -820,12 +909,14 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>  
>  		for (i = 0; i < nand->ecc.steps ; i++) {
>  			u8 *data = buf + i * ecc->size;
> -			u8 *oob = nand->oob_poi + i * (ecc->bytes + 2);
> +			u8 *oob = nand->oob_poi + i * NFC_OOB_PER_ECC(nand);
>  
>  			if (correct_bitmap & BIT_ULL(i))
>  				continue;
> +
>  			ret = nand_check_erased_ecc_chunk(data,	ecc->size,
> -							  oob, ecc->bytes + 2,
> +							  oob,
> +							  NFC_OOB_PER_ECC(nand),
>  							  NULL, 0,
>  							  ecc->strength);
>  			if (ret < 0) {
> @@ -839,17 +930,30 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>  		memcpy(buf, meson_chip->data_buf, mtd->writesize);
>  	}
>  
> +	if (oob_required)
> +		__meson_nfc_read_oob(nand, page, oob_buf);

In the standalone "read_oob" hook, you have to send a READ0 command,
but not when you are in the read_page hook. It is a big waste of time.

> +
>  	return bitflips;
>  }
>  
>  static int meson_nfc_read_oob_raw(struct nand_chip *nand, int page)
>  {
> -	return meson_nfc_read_page_raw(nand, NULL, 1, page);
> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
>  }
>  
>  static int meson_nfc_read_oob(struct nand_chip *nand, int page)
>  {
> -	return meson_nfc_read_page_hwecc(nand, NULL, 1, page);
> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
> +}
> +
> +static int meson_nfc_write_oob_raw(struct nand_chip *nand, int page)
> +{
> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
> +}
> +
> +static int meson_nfc_write_oob(struct nand_chip *nand, int page)
> +{
> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);

Do we really need these indirections?

>  }
>  
>  static bool meson_nfc_is_buffer_dma_safe(const void *buffer)
> @@ -982,7 +1086,7 @@ static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
>  	if (section >= nand->ecc.steps)
>  		return -ERANGE;
>  
> -	oobregion->offset =  2 + (section * (2 + nand->ecc.bytes));
> +	oobregion->offset = NFC_USER_BYTES + section * NFC_OOB_PER_ECC(nand);

The () are still needed around the '*'

>  	oobregion->length = nand->ecc.bytes;
>  
>  	return 0;
> @@ -992,12 +1096,16 @@ static int meson_ooblayout_free(struct mtd_info *mtd, int section,
>  				struct mtd_oob_region *oobregion)
>  {
>  	struct nand_chip *nand = mtd_to_nand(mtd);
> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
>  
>  	if (section >= nand->ecc.steps)
>  		return -ERANGE;
>  
> -	oobregion->offset = section * (2 + nand->ecc.bytes);
> -	oobregion->length = 2;
> +	/* Split rest of OOB area (not covered by ECC engine) per each
> +	 * ECC section. This will be OOB data available to user.
> +	 */
> +	oobregion->offset = (section + nand->ecc.steps) * NFC_OOB_PER_ECC(nand);
> +	oobregion->length = oob_bytes / nand->ecc.steps;
>  
>  	return 0;
>  }
> @@ -1184,6 +1292,9 @@ static int meson_nand_bch_mode(struct nand_chip *nand)
>  
>  static void meson_nand_detach_chip(struct nand_chip *nand)
>  {
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +
> +	kfree(meson_chip->oob_buf);
>  	meson_nfc_free_buffer(nand);
>  }
>  
> @@ -1225,9 +1336,9 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>  	nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
>  	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
>  	nand->ecc.write_page = meson_nfc_write_page_hwecc;
> -	nand->ecc.write_oob_raw = nand_write_oob_std;
> -	nand->ecc.write_oob = nand_write_oob_std;
>  
> +	nand->ecc.write_oob_raw = meson_nfc_write_oob_raw;
> +	nand->ecc.write_oob = meson_nfc_write_oob;

Actually if you define the right OOB layouts, are these really needed
?? I would expect the right bytes to be picked up by the default
implementation. I see nothing specific in your current implementation?

>  	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
>  	nand->ecc.read_page = meson_nfc_read_page_hwecc;
>  	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
> @@ -1237,9 +1348,16 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>  		dev_err(nfc->dev, "16bits bus width not supported");
>  		return -EINVAL;
>  	}
> +
> +	meson_chip->oob_buf = kmalloc(nand->ecc.bytes, GFP_KERNEL);

devm_kmalloc?

> +	if (!meson_chip->oob_buf)
> +		return -ENOMEM;
> +
>  	ret = meson_chip_buffer_init(nand);
> -	if (ret)
> +	if (ret) {
> +		kfree(meson_chip->oob_buf);
>  		return -ENOMEM;
> +	}
>  
>  	return ret;
>  }


Thanks,
Miquèl

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-22 15:33     ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-22 15:33 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:36 +0300:

The title should perhaps be "only expose unprotected user OOB bytes".

> This moves free bytes of OOB to non-protected ECC area. It is needed to
> make JFFS2 works correctly with this NAND controller. Problem fires when
> JFFS2 driver writes cleanmarker to some page and later it tries to write
> to this page - write will be done successfully, but after that such page
> becomes unreadable due to invalid ECC codes. This happens because second
> write needs to update ECC codes, but it is impossible to do it correctly
> without block erase. So idea of this patch is to

"... is to use the unprotected OOB area to store the cleanmarkers, so
that they can be written by the filesystem without caring much about
the page being empty or not: the ECC codes will not be written anyway."
?

JFFS2 is only useful on tiny NAND devices, where UBI does not fit,
which are usually true SLC flashes, with the capability of writing
a page with empty (0xFF) data, and still be able to write actual data
to it later in a second write.

> split accesses to OOB
> free bytes and data on each page - now both of them does not depends on
> each other.
> 
> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> ---
>  drivers/mtd/nand/raw/meson_nand.c | 192 ++++++++++++++++++++++++------
>  1 file changed, 155 insertions(+), 37 deletions(-)
> 
> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> index 2f4d8c84186b..8526a6b87720 100644
> --- a/drivers/mtd/nand/raw/meson_nand.c
> +++ b/drivers/mtd/nand/raw/meson_nand.c
> @@ -108,6 +108,9 @@
>  
>  #define PER_INFO_BYTE		8
>  
> +#define NFC_USER_BYTES		2
> +#define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
> +
>  struct meson_nfc_nand_chip {
>  	struct list_head node;
>  	struct nand_chip nand;
> @@ -122,6 +125,7 @@ struct meson_nfc_nand_chip {
>  	u8 *data_buf;
>  	__le64 *info_buf;
>  	u32 nsels;
> +	u8 *oob_buf;
>  	u8 sels[];
>  };
>  
> @@ -338,7 +342,7 @@ static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>  	int len;
>  
> -	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
> +	len = nand->ecc.size * (i + 1) + NFC_OOB_PER_ECC(nand) * i;

This...

>  
>  	return meson_chip->data_buf + len;
>  }
> @@ -349,7 +353,7 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>  	int len, temp;
>  
>  	temp = nand->ecc.size + nand->ecc.bytes;
> -	len = (temp + 2) * i;
> +	len = (temp + NFC_USER_BYTES) * i;

... and this (same below)

are purely cosmetic, should be in a patch aside.

>  
>  	return meson_chip->data_buf + len;
>  }
> @@ -357,29 +361,47 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>  static void meson_nfc_get_data_oob(struct nand_chip *nand,
>  				   u8 *buf, u8 *oobbuf)
>  {
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	struct mtd_info *mtd = nand_to_mtd(nand);
>  	int i, oob_len = 0;
>  	u8 *dsrc, *osrc;
> +	u8 *oobtail;
>  
> -	oob_len = nand->ecc.bytes + 2;
> +	oob_len = NFC_OOB_PER_ECC(nand);
>  	for (i = 0; i < nand->ecc.steps; i++) {
>  		if (buf) {
>  			dsrc = meson_nfc_data_ptr(nand, i);
>  			memcpy(buf, dsrc, nand->ecc.size);
>  			buf += nand->ecc.size;
>  		}
> -		osrc = meson_nfc_oob_ptr(nand, i);
> -		memcpy(oobbuf, osrc, oob_len);
> -		oobbuf += oob_len;
> +
> +		if (oobbuf) {
> +			osrc = meson_nfc_oob_ptr(nand, i);
> +			memcpy(oobbuf, osrc, oob_len);
> +			oobbuf += oob_len;
> +		}
>  	}
> +
> +	if (!oobbuf)
> +		return;

The whole "if (oobbuf)" logic is nice to have, but should as well be in
a dedicated patch.

> +
> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
> +		  (nand->ecc.size + oob_len);
> +
> +	/* 'oobbuf' if already shifted to the start of unused area. */

		    is?
s/unused/user/? I'm not sure I get the comment.

> +	memcpy(oobbuf, oobtail, mtd->oobsize - nand->ecc.steps * oob_len);
>  }
>  
>  static void meson_nfc_set_data_oob(struct nand_chip *nand,
>  				   const u8 *buf, u8 *oobbuf)
>  {
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	struct mtd_info *mtd = nand_to_mtd(nand);
>  	int i, oob_len = 0;
>  	u8 *dsrc, *osrc;
> +	u8 *oobtail;
>  
> -	oob_len = nand->ecc.bytes + 2;
> +	oob_len = NFC_OOB_PER_ECC(nand);
>  	for (i = 0; i < nand->ecc.steps; i++) {
>  		if (buf) {
>  			dsrc = meson_nfc_data_ptr(nand, i);
> @@ -390,6 +412,12 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>  		memcpy(osrc, oobbuf, oob_len);
>  		oobbuf += oob_len;
>  	}
> +
> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
> +		  (nand->ecc.size + oob_len);
> +
> +	/* 'oobbuf' if already shifted to the start of unused area. */
> +	memcpy(oobtail, oobbuf, mtd->oobsize - nand->ecc.steps * oob_len);
>  }
>  
>  static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
> @@ -436,25 +464,12 @@ static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
>  {
>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>  	__le64 *info;
> -	int i, count;
> -
> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> -		info = &meson_chip->info_buf[i];
> -		*info |= oob_buf[count];
> -		*info |= oob_buf[count + 1] << 8;
> -	}
> -}
> -
> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
> -{
> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> -	__le64 *info;
> -	int i, count;
> +	int i;
>  
> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> +	for (i = 0; i < nand->ecc.steps; i++) {
>  		info = &meson_chip->info_buf[i];
> -		oob_buf[count] = *info;
> -		oob_buf[count + 1] = *info >> 8;
> +		/* Always ignore user bytes programming. */

Why?

Also, maybe I should mention the helpers:
mtd_ooblayout_get_eccbytes and co
They are very useful to deal with OOB bytes. Everything seems extremely
hardcoded in this driver, while the user can tune read/write OOB
operations.

> +		*info |= 0xffff;
>  	}
>  }
>  
> @@ -698,18 +713,92 @@ static int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf,
>  	return meson_nfc_write_page_sub(nand, page, 1);
>  }
>  
> +static u32 meson_nfc_get_oob_bytes(struct nand_chip *nand)
> +{
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +
> +	return mtd->oobsize - nand->ecc.steps * NFC_OOB_PER_ECC(nand);
> +}
> +
> +static int __meson_nfc_write_oob(struct nand_chip *nand, int page, u8 *oob_buf)
> +{
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +	u32 page_size = mtd->writesize + mtd->oobsize;
> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
> +	int ret;
> +
> +	if (!oob_bytes)
> +		return 0;
> +
> +	ret = nand_prog_page_begin_op(nand, page, 0, NULL, 0);
> +	if (ret)
> +		return ret;
> +
> +	memcpy(meson_chip->oob_buf, oob_buf + (mtd->oobsize - oob_bytes),
> +	       oob_bytes);
> +
> +	ret = nand_change_write_column_op(nand, page_size - oob_bytes,
> +					  meson_chip->oob_buf,
> +					  oob_bytes, false);
> +	if (ret)
> +		return ret;
> +
> +	return nand_prog_page_end_op(nand);
> +}
> +
> +static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
> +				u8 *oob_buf)
> +{
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +	u32 oob_bytes;
> +	u32 page_size;
> +	int ret;
> +
> +	oob_bytes = meson_nfc_get_oob_bytes(nand);
> +
> +	if (!oob_bytes)
> +		return 0;
> +
> +	ret = nand_read_page_op(nand, page, 0, NULL, 0);
> +	if (ret)
> +		return ret;
> +
> +	page_size = mtd->writesize + mtd->oobsize;
> +
> +	ret = nand_change_read_column_op(nand, page_size - oob_bytes,
> +					 meson_chip->oob_buf,
> +					 oob_bytes, false);
> +
> +	if (!ret)
> +		memcpy(oob_buf + (mtd->oobsize - oob_bytes),
> +		       meson_chip->oob_buf,
> +		       oob_bytes);
> +
> +	return ret;
> +}
> +
>  static int meson_nfc_write_page_hwecc(struct nand_chip *nand,
>  				      const u8 *buf, int oob_required, int page)
>  {
>  	struct mtd_info *mtd = nand_to_mtd(nand);
>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>  	u8 *oob_buf = nand->oob_poi;
> +	int ret;
>  
>  	memcpy(meson_chip->data_buf, buf, mtd->writesize);
>  	memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE);
>  	meson_nfc_set_user_byte(nand, oob_buf);
>  
> -	return meson_nfc_write_page_sub(nand, page, 0);
> +	ret = meson_nfc_write_page_sub(nand, page, 0);
> +	if (ret)
> +		return ret;
> +
> +	if (oob_required)
> +		ret = __meson_nfc_write_oob(nand, page, oob_buf);

You should provide all the data including OOB bytes in a single write
call, otherwise you perform two writes on the same page, that's not
what this helper is expected to do.

> +
> +	return ret;
>  }
>  
>  static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
> @@ -783,7 +872,7 @@ static int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf,
>  	if (ret)
>  		return ret;
>  
> -	meson_nfc_get_data_oob(nand, buf, oob_buf);
> +	meson_nfc_get_data_oob(nand, buf, oob_required ? oob_buf : NULL);
>  
>  	return 0;
>  }
> @@ -803,12 +892,12 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>  	if (ret)
>  		return ret;
>  
> -	meson_nfc_get_user_byte(nand, oob_buf);
>  	ret = meson_nfc_ecc_correct(nand, &bitflips, &correct_bitmap);
>  	if (ret == ECC_CHECK_RETURN_FF) {
>  		if (buf)
>  			memset(buf, 0xff, mtd->writesize);
>  		memset(oob_buf, 0xff, mtd->oobsize);
> +		return bitflips;

That is something else => other fix => other patch?

>  	} else if (ret < 0) {
>  		if ((nand->options & NAND_NEED_SCRAMBLING) || !buf) {
>  			mtd->ecc_stats.failed++;
> @@ -820,12 +909,14 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>  
>  		for (i = 0; i < nand->ecc.steps ; i++) {
>  			u8 *data = buf + i * ecc->size;
> -			u8 *oob = nand->oob_poi + i * (ecc->bytes + 2);
> +			u8 *oob = nand->oob_poi + i * NFC_OOB_PER_ECC(nand);
>  
>  			if (correct_bitmap & BIT_ULL(i))
>  				continue;
> +
>  			ret = nand_check_erased_ecc_chunk(data,	ecc->size,
> -							  oob, ecc->bytes + 2,
> +							  oob,
> +							  NFC_OOB_PER_ECC(nand),
>  							  NULL, 0,
>  							  ecc->strength);
>  			if (ret < 0) {
> @@ -839,17 +930,30 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>  		memcpy(buf, meson_chip->data_buf, mtd->writesize);
>  	}
>  
> +	if (oob_required)
> +		__meson_nfc_read_oob(nand, page, oob_buf);

In the standalone "read_oob" hook, you have to send a READ0 command,
but not when you are in the read_page hook. It is a big waste of time.

> +
>  	return bitflips;
>  }
>  
>  static int meson_nfc_read_oob_raw(struct nand_chip *nand, int page)
>  {
> -	return meson_nfc_read_page_raw(nand, NULL, 1, page);
> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
>  }
>  
>  static int meson_nfc_read_oob(struct nand_chip *nand, int page)
>  {
> -	return meson_nfc_read_page_hwecc(nand, NULL, 1, page);
> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
> +}
> +
> +static int meson_nfc_write_oob_raw(struct nand_chip *nand, int page)
> +{
> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
> +}
> +
> +static int meson_nfc_write_oob(struct nand_chip *nand, int page)
> +{
> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);

Do we really need these indirections?

>  }
>  
>  static bool meson_nfc_is_buffer_dma_safe(const void *buffer)
> @@ -982,7 +1086,7 @@ static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
>  	if (section >= nand->ecc.steps)
>  		return -ERANGE;
>  
> -	oobregion->offset =  2 + (section * (2 + nand->ecc.bytes));
> +	oobregion->offset = NFC_USER_BYTES + section * NFC_OOB_PER_ECC(nand);

The () are still needed around the '*'

>  	oobregion->length = nand->ecc.bytes;
>  
>  	return 0;
> @@ -992,12 +1096,16 @@ static int meson_ooblayout_free(struct mtd_info *mtd, int section,
>  				struct mtd_oob_region *oobregion)
>  {
>  	struct nand_chip *nand = mtd_to_nand(mtd);
> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
>  
>  	if (section >= nand->ecc.steps)
>  		return -ERANGE;
>  
> -	oobregion->offset = section * (2 + nand->ecc.bytes);
> -	oobregion->length = 2;
> +	/* Split rest of OOB area (not covered by ECC engine) per each
> +	 * ECC section. This will be OOB data available to user.
> +	 */
> +	oobregion->offset = (section + nand->ecc.steps) * NFC_OOB_PER_ECC(nand);
> +	oobregion->length = oob_bytes / nand->ecc.steps;
>  
>  	return 0;
>  }
> @@ -1184,6 +1292,9 @@ static int meson_nand_bch_mode(struct nand_chip *nand)
>  
>  static void meson_nand_detach_chip(struct nand_chip *nand)
>  {
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +
> +	kfree(meson_chip->oob_buf);
>  	meson_nfc_free_buffer(nand);
>  }
>  
> @@ -1225,9 +1336,9 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>  	nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
>  	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
>  	nand->ecc.write_page = meson_nfc_write_page_hwecc;
> -	nand->ecc.write_oob_raw = nand_write_oob_std;
> -	nand->ecc.write_oob = nand_write_oob_std;
>  
> +	nand->ecc.write_oob_raw = meson_nfc_write_oob_raw;
> +	nand->ecc.write_oob = meson_nfc_write_oob;

Actually if you define the right OOB layouts, are these really needed
?? I would expect the right bytes to be picked up by the default
implementation. I see nothing specific in your current implementation?

>  	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
>  	nand->ecc.read_page = meson_nfc_read_page_hwecc;
>  	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
> @@ -1237,9 +1348,16 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>  		dev_err(nfc->dev, "16bits bus width not supported");
>  		return -EINVAL;
>  	}
> +
> +	meson_chip->oob_buf = kmalloc(nand->ecc.bytes, GFP_KERNEL);

devm_kmalloc?

> +	if (!meson_chip->oob_buf)
> +		return -ENOMEM;
> +
>  	ret = meson_chip_buffer_init(nand);
> -	if (ret)
> +	if (ret) {
> +		kfree(meson_chip->oob_buf);
>  		return -ENOMEM;
> +	}
>  
>  	return ret;
>  }


Thanks,
Miquèl

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

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-22 15:33     ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-22 15:33 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:36 +0300:

The title should perhaps be "only expose unprotected user OOB bytes".

> This moves free bytes of OOB to non-protected ECC area. It is needed to
> make JFFS2 works correctly with this NAND controller. Problem fires when
> JFFS2 driver writes cleanmarker to some page and later it tries to write
> to this page - write will be done successfully, but after that such page
> becomes unreadable due to invalid ECC codes. This happens because second
> write needs to update ECC codes, but it is impossible to do it correctly
> without block erase. So idea of this patch is to

"... is to use the unprotected OOB area to store the cleanmarkers, so
that they can be written by the filesystem without caring much about
the page being empty or not: the ECC codes will not be written anyway."
?

JFFS2 is only useful on tiny NAND devices, where UBI does not fit,
which are usually true SLC flashes, with the capability of writing
a page with empty (0xFF) data, and still be able to write actual data
to it later in a second write.

> split accesses to OOB
> free bytes and data on each page - now both of them does not depends on
> each other.
> 
> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> ---
>  drivers/mtd/nand/raw/meson_nand.c | 192 ++++++++++++++++++++++++------
>  1 file changed, 155 insertions(+), 37 deletions(-)
> 
> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> index 2f4d8c84186b..8526a6b87720 100644
> --- a/drivers/mtd/nand/raw/meson_nand.c
> +++ b/drivers/mtd/nand/raw/meson_nand.c
> @@ -108,6 +108,9 @@
>  
>  #define PER_INFO_BYTE		8
>  
> +#define NFC_USER_BYTES		2
> +#define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
> +
>  struct meson_nfc_nand_chip {
>  	struct list_head node;
>  	struct nand_chip nand;
> @@ -122,6 +125,7 @@ struct meson_nfc_nand_chip {
>  	u8 *data_buf;
>  	__le64 *info_buf;
>  	u32 nsels;
> +	u8 *oob_buf;
>  	u8 sels[];
>  };
>  
> @@ -338,7 +342,7 @@ static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>  	int len;
>  
> -	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
> +	len = nand->ecc.size * (i + 1) + NFC_OOB_PER_ECC(nand) * i;

This...

>  
>  	return meson_chip->data_buf + len;
>  }
> @@ -349,7 +353,7 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>  	int len, temp;
>  
>  	temp = nand->ecc.size + nand->ecc.bytes;
> -	len = (temp + 2) * i;
> +	len = (temp + NFC_USER_BYTES) * i;

... and this (same below)

are purely cosmetic, should be in a patch aside.

>  
>  	return meson_chip->data_buf + len;
>  }
> @@ -357,29 +361,47 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>  static void meson_nfc_get_data_oob(struct nand_chip *nand,
>  				   u8 *buf, u8 *oobbuf)
>  {
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	struct mtd_info *mtd = nand_to_mtd(nand);
>  	int i, oob_len = 0;
>  	u8 *dsrc, *osrc;
> +	u8 *oobtail;
>  
> -	oob_len = nand->ecc.bytes + 2;
> +	oob_len = NFC_OOB_PER_ECC(nand);
>  	for (i = 0; i < nand->ecc.steps; i++) {
>  		if (buf) {
>  			dsrc = meson_nfc_data_ptr(nand, i);
>  			memcpy(buf, dsrc, nand->ecc.size);
>  			buf += nand->ecc.size;
>  		}
> -		osrc = meson_nfc_oob_ptr(nand, i);
> -		memcpy(oobbuf, osrc, oob_len);
> -		oobbuf += oob_len;
> +
> +		if (oobbuf) {
> +			osrc = meson_nfc_oob_ptr(nand, i);
> +			memcpy(oobbuf, osrc, oob_len);
> +			oobbuf += oob_len;
> +		}
>  	}
> +
> +	if (!oobbuf)
> +		return;

The whole "if (oobbuf)" logic is nice to have, but should as well be in
a dedicated patch.

> +
> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
> +		  (nand->ecc.size + oob_len);
> +
> +	/* 'oobbuf' if already shifted to the start of unused area. */

		    is?
s/unused/user/? I'm not sure I get the comment.

> +	memcpy(oobbuf, oobtail, mtd->oobsize - nand->ecc.steps * oob_len);
>  }
>  
>  static void meson_nfc_set_data_oob(struct nand_chip *nand,
>  				   const u8 *buf, u8 *oobbuf)
>  {
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	struct mtd_info *mtd = nand_to_mtd(nand);
>  	int i, oob_len = 0;
>  	u8 *dsrc, *osrc;
> +	u8 *oobtail;
>  
> -	oob_len = nand->ecc.bytes + 2;
> +	oob_len = NFC_OOB_PER_ECC(nand);
>  	for (i = 0; i < nand->ecc.steps; i++) {
>  		if (buf) {
>  			dsrc = meson_nfc_data_ptr(nand, i);
> @@ -390,6 +412,12 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>  		memcpy(osrc, oobbuf, oob_len);
>  		oobbuf += oob_len;
>  	}
> +
> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
> +		  (nand->ecc.size + oob_len);
> +
> +	/* 'oobbuf' if already shifted to the start of unused area. */
> +	memcpy(oobtail, oobbuf, mtd->oobsize - nand->ecc.steps * oob_len);
>  }
>  
>  static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
> @@ -436,25 +464,12 @@ static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
>  {
>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>  	__le64 *info;
> -	int i, count;
> -
> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> -		info = &meson_chip->info_buf[i];
> -		*info |= oob_buf[count];
> -		*info |= oob_buf[count + 1] << 8;
> -	}
> -}
> -
> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
> -{
> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> -	__le64 *info;
> -	int i, count;
> +	int i;
>  
> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> +	for (i = 0; i < nand->ecc.steps; i++) {
>  		info = &meson_chip->info_buf[i];
> -		oob_buf[count] = *info;
> -		oob_buf[count + 1] = *info >> 8;
> +		/* Always ignore user bytes programming. */

Why?

Also, maybe I should mention the helpers:
mtd_ooblayout_get_eccbytes and co
They are very useful to deal with OOB bytes. Everything seems extremely
hardcoded in this driver, while the user can tune read/write OOB
operations.

> +		*info |= 0xffff;
>  	}
>  }
>  
> @@ -698,18 +713,92 @@ static int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf,
>  	return meson_nfc_write_page_sub(nand, page, 1);
>  }
>  
> +static u32 meson_nfc_get_oob_bytes(struct nand_chip *nand)
> +{
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +
> +	return mtd->oobsize - nand->ecc.steps * NFC_OOB_PER_ECC(nand);
> +}
> +
> +static int __meson_nfc_write_oob(struct nand_chip *nand, int page, u8 *oob_buf)
> +{
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +	u32 page_size = mtd->writesize + mtd->oobsize;
> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
> +	int ret;
> +
> +	if (!oob_bytes)
> +		return 0;
> +
> +	ret = nand_prog_page_begin_op(nand, page, 0, NULL, 0);
> +	if (ret)
> +		return ret;
> +
> +	memcpy(meson_chip->oob_buf, oob_buf + (mtd->oobsize - oob_bytes),
> +	       oob_bytes);
> +
> +	ret = nand_change_write_column_op(nand, page_size - oob_bytes,
> +					  meson_chip->oob_buf,
> +					  oob_bytes, false);
> +	if (ret)
> +		return ret;
> +
> +	return nand_prog_page_end_op(nand);
> +}
> +
> +static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
> +				u8 *oob_buf)
> +{
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +	u32 oob_bytes;
> +	u32 page_size;
> +	int ret;
> +
> +	oob_bytes = meson_nfc_get_oob_bytes(nand);
> +
> +	if (!oob_bytes)
> +		return 0;
> +
> +	ret = nand_read_page_op(nand, page, 0, NULL, 0);
> +	if (ret)
> +		return ret;
> +
> +	page_size = mtd->writesize + mtd->oobsize;
> +
> +	ret = nand_change_read_column_op(nand, page_size - oob_bytes,
> +					 meson_chip->oob_buf,
> +					 oob_bytes, false);
> +
> +	if (!ret)
> +		memcpy(oob_buf + (mtd->oobsize - oob_bytes),
> +		       meson_chip->oob_buf,
> +		       oob_bytes);
> +
> +	return ret;
> +}
> +
>  static int meson_nfc_write_page_hwecc(struct nand_chip *nand,
>  				      const u8 *buf, int oob_required, int page)
>  {
>  	struct mtd_info *mtd = nand_to_mtd(nand);
>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>  	u8 *oob_buf = nand->oob_poi;
> +	int ret;
>  
>  	memcpy(meson_chip->data_buf, buf, mtd->writesize);
>  	memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE);
>  	meson_nfc_set_user_byte(nand, oob_buf);
>  
> -	return meson_nfc_write_page_sub(nand, page, 0);
> +	ret = meson_nfc_write_page_sub(nand, page, 0);
> +	if (ret)
> +		return ret;
> +
> +	if (oob_required)
> +		ret = __meson_nfc_write_oob(nand, page, oob_buf);

You should provide all the data including OOB bytes in a single write
call, otherwise you perform two writes on the same page, that's not
what this helper is expected to do.

> +
> +	return ret;
>  }
>  
>  static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
> @@ -783,7 +872,7 @@ static int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf,
>  	if (ret)
>  		return ret;
>  
> -	meson_nfc_get_data_oob(nand, buf, oob_buf);
> +	meson_nfc_get_data_oob(nand, buf, oob_required ? oob_buf : NULL);
>  
>  	return 0;
>  }
> @@ -803,12 +892,12 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>  	if (ret)
>  		return ret;
>  
> -	meson_nfc_get_user_byte(nand, oob_buf);
>  	ret = meson_nfc_ecc_correct(nand, &bitflips, &correct_bitmap);
>  	if (ret == ECC_CHECK_RETURN_FF) {
>  		if (buf)
>  			memset(buf, 0xff, mtd->writesize);
>  		memset(oob_buf, 0xff, mtd->oobsize);
> +		return bitflips;

That is something else => other fix => other patch?

>  	} else if (ret < 0) {
>  		if ((nand->options & NAND_NEED_SCRAMBLING) || !buf) {
>  			mtd->ecc_stats.failed++;
> @@ -820,12 +909,14 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>  
>  		for (i = 0; i < nand->ecc.steps ; i++) {
>  			u8 *data = buf + i * ecc->size;
> -			u8 *oob = nand->oob_poi + i * (ecc->bytes + 2);
> +			u8 *oob = nand->oob_poi + i * NFC_OOB_PER_ECC(nand);
>  
>  			if (correct_bitmap & BIT_ULL(i))
>  				continue;
> +
>  			ret = nand_check_erased_ecc_chunk(data,	ecc->size,
> -							  oob, ecc->bytes + 2,
> +							  oob,
> +							  NFC_OOB_PER_ECC(nand),
>  							  NULL, 0,
>  							  ecc->strength);
>  			if (ret < 0) {
> @@ -839,17 +930,30 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>  		memcpy(buf, meson_chip->data_buf, mtd->writesize);
>  	}
>  
> +	if (oob_required)
> +		__meson_nfc_read_oob(nand, page, oob_buf);

In the standalone "read_oob" hook, you have to send a READ0 command,
but not when you are in the read_page hook. It is a big waste of time.

> +
>  	return bitflips;
>  }
>  
>  static int meson_nfc_read_oob_raw(struct nand_chip *nand, int page)
>  {
> -	return meson_nfc_read_page_raw(nand, NULL, 1, page);
> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
>  }
>  
>  static int meson_nfc_read_oob(struct nand_chip *nand, int page)
>  {
> -	return meson_nfc_read_page_hwecc(nand, NULL, 1, page);
> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
> +}
> +
> +static int meson_nfc_write_oob_raw(struct nand_chip *nand, int page)
> +{
> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
> +}
> +
> +static int meson_nfc_write_oob(struct nand_chip *nand, int page)
> +{
> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);

Do we really need these indirections?

>  }
>  
>  static bool meson_nfc_is_buffer_dma_safe(const void *buffer)
> @@ -982,7 +1086,7 @@ static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
>  	if (section >= nand->ecc.steps)
>  		return -ERANGE;
>  
> -	oobregion->offset =  2 + (section * (2 + nand->ecc.bytes));
> +	oobregion->offset = NFC_USER_BYTES + section * NFC_OOB_PER_ECC(nand);

The () are still needed around the '*'

>  	oobregion->length = nand->ecc.bytes;
>  
>  	return 0;
> @@ -992,12 +1096,16 @@ static int meson_ooblayout_free(struct mtd_info *mtd, int section,
>  				struct mtd_oob_region *oobregion)
>  {
>  	struct nand_chip *nand = mtd_to_nand(mtd);
> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
>  
>  	if (section >= nand->ecc.steps)
>  		return -ERANGE;
>  
> -	oobregion->offset = section * (2 + nand->ecc.bytes);
> -	oobregion->length = 2;
> +	/* Split rest of OOB area (not covered by ECC engine) per each
> +	 * ECC section. This will be OOB data available to user.
> +	 */
> +	oobregion->offset = (section + nand->ecc.steps) * NFC_OOB_PER_ECC(nand);
> +	oobregion->length = oob_bytes / nand->ecc.steps;
>  
>  	return 0;
>  }
> @@ -1184,6 +1292,9 @@ static int meson_nand_bch_mode(struct nand_chip *nand)
>  
>  static void meson_nand_detach_chip(struct nand_chip *nand)
>  {
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +
> +	kfree(meson_chip->oob_buf);
>  	meson_nfc_free_buffer(nand);
>  }
>  
> @@ -1225,9 +1336,9 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>  	nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
>  	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
>  	nand->ecc.write_page = meson_nfc_write_page_hwecc;
> -	nand->ecc.write_oob_raw = nand_write_oob_std;
> -	nand->ecc.write_oob = nand_write_oob_std;
>  
> +	nand->ecc.write_oob_raw = meson_nfc_write_oob_raw;
> +	nand->ecc.write_oob = meson_nfc_write_oob;

Actually if you define the right OOB layouts, are these really needed
?? I would expect the right bytes to be picked up by the default
implementation. I see nothing specific in your current implementation?

>  	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
>  	nand->ecc.read_page = meson_nfc_read_page_hwecc;
>  	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
> @@ -1237,9 +1348,16 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>  		dev_err(nfc->dev, "16bits bus width not supported");
>  		return -EINVAL;
>  	}
> +
> +	meson_chip->oob_buf = kmalloc(nand->ecc.bytes, GFP_KERNEL);

devm_kmalloc?

> +	if (!meson_chip->oob_buf)
> +		return -ENOMEM;
> +
>  	ret = meson_chip_buffer_init(nand);
> -	if (ret)
> +	if (ret) {
> +		kfree(meson_chip->oob_buf);
>  		return -ENOMEM;
> +	}
>  
>  	return ret;
>  }


Thanks,
Miquèl

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-22 15:33     ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-22 15:33 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:36 +0300:

The title should perhaps be "only expose unprotected user OOB bytes".

> This moves free bytes of OOB to non-protected ECC area. It is needed to
> make JFFS2 works correctly with this NAND controller. Problem fires when
> JFFS2 driver writes cleanmarker to some page and later it tries to write
> to this page - write will be done successfully, but after that such page
> becomes unreadable due to invalid ECC codes. This happens because second
> write needs to update ECC codes, but it is impossible to do it correctly
> without block erase. So idea of this patch is to

"... is to use the unprotected OOB area to store the cleanmarkers, so
that they can be written by the filesystem without caring much about
the page being empty or not: the ECC codes will not be written anyway."
?

JFFS2 is only useful on tiny NAND devices, where UBI does not fit,
which are usually true SLC flashes, with the capability of writing
a page with empty (0xFF) data, and still be able to write actual data
to it later in a second write.

> split accesses to OOB
> free bytes and data on each page - now both of them does not depends on
> each other.
> 
> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> ---
>  drivers/mtd/nand/raw/meson_nand.c | 192 ++++++++++++++++++++++++------
>  1 file changed, 155 insertions(+), 37 deletions(-)
> 
> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> index 2f4d8c84186b..8526a6b87720 100644
> --- a/drivers/mtd/nand/raw/meson_nand.c
> +++ b/drivers/mtd/nand/raw/meson_nand.c
> @@ -108,6 +108,9 @@
>  
>  #define PER_INFO_BYTE		8
>  
> +#define NFC_USER_BYTES		2
> +#define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
> +
>  struct meson_nfc_nand_chip {
>  	struct list_head node;
>  	struct nand_chip nand;
> @@ -122,6 +125,7 @@ struct meson_nfc_nand_chip {
>  	u8 *data_buf;
>  	__le64 *info_buf;
>  	u32 nsels;
> +	u8 *oob_buf;
>  	u8 sels[];
>  };
>  
> @@ -338,7 +342,7 @@ static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>  	int len;
>  
> -	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
> +	len = nand->ecc.size * (i + 1) + NFC_OOB_PER_ECC(nand) * i;

This...

>  
>  	return meson_chip->data_buf + len;
>  }
> @@ -349,7 +353,7 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>  	int len, temp;
>  
>  	temp = nand->ecc.size + nand->ecc.bytes;
> -	len = (temp + 2) * i;
> +	len = (temp + NFC_USER_BYTES) * i;

... and this (same below)

are purely cosmetic, should be in a patch aside.

>  
>  	return meson_chip->data_buf + len;
>  }
> @@ -357,29 +361,47 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>  static void meson_nfc_get_data_oob(struct nand_chip *nand,
>  				   u8 *buf, u8 *oobbuf)
>  {
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	struct mtd_info *mtd = nand_to_mtd(nand);
>  	int i, oob_len = 0;
>  	u8 *dsrc, *osrc;
> +	u8 *oobtail;
>  
> -	oob_len = nand->ecc.bytes + 2;
> +	oob_len = NFC_OOB_PER_ECC(nand);
>  	for (i = 0; i < nand->ecc.steps; i++) {
>  		if (buf) {
>  			dsrc = meson_nfc_data_ptr(nand, i);
>  			memcpy(buf, dsrc, nand->ecc.size);
>  			buf += nand->ecc.size;
>  		}
> -		osrc = meson_nfc_oob_ptr(nand, i);
> -		memcpy(oobbuf, osrc, oob_len);
> -		oobbuf += oob_len;
> +
> +		if (oobbuf) {
> +			osrc = meson_nfc_oob_ptr(nand, i);
> +			memcpy(oobbuf, osrc, oob_len);
> +			oobbuf += oob_len;
> +		}
>  	}
> +
> +	if (!oobbuf)
> +		return;

The whole "if (oobbuf)" logic is nice to have, but should as well be in
a dedicated patch.

> +
> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
> +		  (nand->ecc.size + oob_len);
> +
> +	/* 'oobbuf' if already shifted to the start of unused area. */

		    is?
s/unused/user/? I'm not sure I get the comment.

> +	memcpy(oobbuf, oobtail, mtd->oobsize - nand->ecc.steps * oob_len);
>  }
>  
>  static void meson_nfc_set_data_oob(struct nand_chip *nand,
>  				   const u8 *buf, u8 *oobbuf)
>  {
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	struct mtd_info *mtd = nand_to_mtd(nand);
>  	int i, oob_len = 0;
>  	u8 *dsrc, *osrc;
> +	u8 *oobtail;
>  
> -	oob_len = nand->ecc.bytes + 2;
> +	oob_len = NFC_OOB_PER_ECC(nand);
>  	for (i = 0; i < nand->ecc.steps; i++) {
>  		if (buf) {
>  			dsrc = meson_nfc_data_ptr(nand, i);
> @@ -390,6 +412,12 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>  		memcpy(osrc, oobbuf, oob_len);
>  		oobbuf += oob_len;
>  	}
> +
> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
> +		  (nand->ecc.size + oob_len);
> +
> +	/* 'oobbuf' if already shifted to the start of unused area. */
> +	memcpy(oobtail, oobbuf, mtd->oobsize - nand->ecc.steps * oob_len);
>  }
>  
>  static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
> @@ -436,25 +464,12 @@ static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
>  {
>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>  	__le64 *info;
> -	int i, count;
> -
> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> -		info = &meson_chip->info_buf[i];
> -		*info |= oob_buf[count];
> -		*info |= oob_buf[count + 1] << 8;
> -	}
> -}
> -
> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
> -{
> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> -	__le64 *info;
> -	int i, count;
> +	int i;
>  
> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> +	for (i = 0; i < nand->ecc.steps; i++) {
>  		info = &meson_chip->info_buf[i];
> -		oob_buf[count] = *info;
> -		oob_buf[count + 1] = *info >> 8;
> +		/* Always ignore user bytes programming. */

Why?

Also, maybe I should mention the helpers:
mtd_ooblayout_get_eccbytes and co
They are very useful to deal with OOB bytes. Everything seems extremely
hardcoded in this driver, while the user can tune read/write OOB
operations.

> +		*info |= 0xffff;
>  	}
>  }
>  
> @@ -698,18 +713,92 @@ static int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf,
>  	return meson_nfc_write_page_sub(nand, page, 1);
>  }
>  
> +static u32 meson_nfc_get_oob_bytes(struct nand_chip *nand)
> +{
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +
> +	return mtd->oobsize - nand->ecc.steps * NFC_OOB_PER_ECC(nand);
> +}
> +
> +static int __meson_nfc_write_oob(struct nand_chip *nand, int page, u8 *oob_buf)
> +{
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +	u32 page_size = mtd->writesize + mtd->oobsize;
> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
> +	int ret;
> +
> +	if (!oob_bytes)
> +		return 0;
> +
> +	ret = nand_prog_page_begin_op(nand, page, 0, NULL, 0);
> +	if (ret)
> +		return ret;
> +
> +	memcpy(meson_chip->oob_buf, oob_buf + (mtd->oobsize - oob_bytes),
> +	       oob_bytes);
> +
> +	ret = nand_change_write_column_op(nand, page_size - oob_bytes,
> +					  meson_chip->oob_buf,
> +					  oob_bytes, false);
> +	if (ret)
> +		return ret;
> +
> +	return nand_prog_page_end_op(nand);
> +}
> +
> +static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
> +				u8 *oob_buf)
> +{
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +	struct mtd_info *mtd = nand_to_mtd(nand);
> +	u32 oob_bytes;
> +	u32 page_size;
> +	int ret;
> +
> +	oob_bytes = meson_nfc_get_oob_bytes(nand);
> +
> +	if (!oob_bytes)
> +		return 0;
> +
> +	ret = nand_read_page_op(nand, page, 0, NULL, 0);
> +	if (ret)
> +		return ret;
> +
> +	page_size = mtd->writesize + mtd->oobsize;
> +
> +	ret = nand_change_read_column_op(nand, page_size - oob_bytes,
> +					 meson_chip->oob_buf,
> +					 oob_bytes, false);
> +
> +	if (!ret)
> +		memcpy(oob_buf + (mtd->oobsize - oob_bytes),
> +		       meson_chip->oob_buf,
> +		       oob_bytes);
> +
> +	return ret;
> +}
> +
>  static int meson_nfc_write_page_hwecc(struct nand_chip *nand,
>  				      const u8 *buf, int oob_required, int page)
>  {
>  	struct mtd_info *mtd = nand_to_mtd(nand);
>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>  	u8 *oob_buf = nand->oob_poi;
> +	int ret;
>  
>  	memcpy(meson_chip->data_buf, buf, mtd->writesize);
>  	memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE);
>  	meson_nfc_set_user_byte(nand, oob_buf);
>  
> -	return meson_nfc_write_page_sub(nand, page, 0);
> +	ret = meson_nfc_write_page_sub(nand, page, 0);
> +	if (ret)
> +		return ret;
> +
> +	if (oob_required)
> +		ret = __meson_nfc_write_oob(nand, page, oob_buf);

You should provide all the data including OOB bytes in a single write
call, otherwise you perform two writes on the same page, that's not
what this helper is expected to do.

> +
> +	return ret;
>  }
>  
>  static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
> @@ -783,7 +872,7 @@ static int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf,
>  	if (ret)
>  		return ret;
>  
> -	meson_nfc_get_data_oob(nand, buf, oob_buf);
> +	meson_nfc_get_data_oob(nand, buf, oob_required ? oob_buf : NULL);
>  
>  	return 0;
>  }
> @@ -803,12 +892,12 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>  	if (ret)
>  		return ret;
>  
> -	meson_nfc_get_user_byte(nand, oob_buf);
>  	ret = meson_nfc_ecc_correct(nand, &bitflips, &correct_bitmap);
>  	if (ret == ECC_CHECK_RETURN_FF) {
>  		if (buf)
>  			memset(buf, 0xff, mtd->writesize);
>  		memset(oob_buf, 0xff, mtd->oobsize);
> +		return bitflips;

That is something else => other fix => other patch?

>  	} else if (ret < 0) {
>  		if ((nand->options & NAND_NEED_SCRAMBLING) || !buf) {
>  			mtd->ecc_stats.failed++;
> @@ -820,12 +909,14 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>  
>  		for (i = 0; i < nand->ecc.steps ; i++) {
>  			u8 *data = buf + i * ecc->size;
> -			u8 *oob = nand->oob_poi + i * (ecc->bytes + 2);
> +			u8 *oob = nand->oob_poi + i * NFC_OOB_PER_ECC(nand);
>  
>  			if (correct_bitmap & BIT_ULL(i))
>  				continue;
> +
>  			ret = nand_check_erased_ecc_chunk(data,	ecc->size,
> -							  oob, ecc->bytes + 2,
> +							  oob,
> +							  NFC_OOB_PER_ECC(nand),
>  							  NULL, 0,
>  							  ecc->strength);
>  			if (ret < 0) {
> @@ -839,17 +930,30 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>  		memcpy(buf, meson_chip->data_buf, mtd->writesize);
>  	}
>  
> +	if (oob_required)
> +		__meson_nfc_read_oob(nand, page, oob_buf);

In the standalone "read_oob" hook, you have to send a READ0 command,
but not when you are in the read_page hook. It is a big waste of time.

> +
>  	return bitflips;
>  }
>  
>  static int meson_nfc_read_oob_raw(struct nand_chip *nand, int page)
>  {
> -	return meson_nfc_read_page_raw(nand, NULL, 1, page);
> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
>  }
>  
>  static int meson_nfc_read_oob(struct nand_chip *nand, int page)
>  {
> -	return meson_nfc_read_page_hwecc(nand, NULL, 1, page);
> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
> +}
> +
> +static int meson_nfc_write_oob_raw(struct nand_chip *nand, int page)
> +{
> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
> +}
> +
> +static int meson_nfc_write_oob(struct nand_chip *nand, int page)
> +{
> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);

Do we really need these indirections?

>  }
>  
>  static bool meson_nfc_is_buffer_dma_safe(const void *buffer)
> @@ -982,7 +1086,7 @@ static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
>  	if (section >= nand->ecc.steps)
>  		return -ERANGE;
>  
> -	oobregion->offset =  2 + (section * (2 + nand->ecc.bytes));
> +	oobregion->offset = NFC_USER_BYTES + section * NFC_OOB_PER_ECC(nand);

The () are still needed around the '*'

>  	oobregion->length = nand->ecc.bytes;
>  
>  	return 0;
> @@ -992,12 +1096,16 @@ static int meson_ooblayout_free(struct mtd_info *mtd, int section,
>  				struct mtd_oob_region *oobregion)
>  {
>  	struct nand_chip *nand = mtd_to_nand(mtd);
> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
>  
>  	if (section >= nand->ecc.steps)
>  		return -ERANGE;
>  
> -	oobregion->offset = section * (2 + nand->ecc.bytes);
> -	oobregion->length = 2;
> +	/* Split rest of OOB area (not covered by ECC engine) per each
> +	 * ECC section. This will be OOB data available to user.
> +	 */
> +	oobregion->offset = (section + nand->ecc.steps) * NFC_OOB_PER_ECC(nand);
> +	oobregion->length = oob_bytes / nand->ecc.steps;
>  
>  	return 0;
>  }
> @@ -1184,6 +1292,9 @@ static int meson_nand_bch_mode(struct nand_chip *nand)
>  
>  static void meson_nand_detach_chip(struct nand_chip *nand)
>  {
> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> +
> +	kfree(meson_chip->oob_buf);
>  	meson_nfc_free_buffer(nand);
>  }
>  
> @@ -1225,9 +1336,9 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>  	nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
>  	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
>  	nand->ecc.write_page = meson_nfc_write_page_hwecc;
> -	nand->ecc.write_oob_raw = nand_write_oob_std;
> -	nand->ecc.write_oob = nand_write_oob_std;
>  
> +	nand->ecc.write_oob_raw = meson_nfc_write_oob_raw;
> +	nand->ecc.write_oob = meson_nfc_write_oob;

Actually if you define the right OOB layouts, are these really needed
?? I would expect the right bytes to be picked up by the default
implementation. I see nothing specific in your current implementation?

>  	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
>  	nand->ecc.read_page = meson_nfc_read_page_hwecc;
>  	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
> @@ -1237,9 +1348,16 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>  		dev_err(nfc->dev, "16bits bus width not supported");
>  		return -EINVAL;
>  	}
> +
> +	meson_chip->oob_buf = kmalloc(nand->ecc.bytes, GFP_KERNEL);

devm_kmalloc?

> +	if (!meson_chip->oob_buf)
> +		return -ENOMEM;
> +
>  	ret = meson_chip_buffer_init(nand);
> -	if (ret)
> +	if (ret) {
> +		kfree(meson_chip->oob_buf);
>  		return -ENOMEM;
> +	}
>  
>  	return ret;
>  }


Thanks,
Miquèl

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* Re: [PATCH v4 3/5] mtd: rawnand: meson: always read whole OOB bytes
  2023-05-15  9:44   ` Arseniy Krasnov
  (?)
  (?)
@ 2023-05-22 15:38     ` Miquel Raynal
  -1 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-22 15:38 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Jianxin Pan, Yixun Lan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:37 +0300:

> This changes size of read access to OOB area by reading all bytes of
> OOB (free bytes + ECC engine bytes).

This is normally up to the user (user in your case == jffs2). The
controller driver should expose a number of user accessible bytes and
then when users want the OOB area, they should access it entirely. On
top of that read, they can extract (or "write only") the user bytes.

> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> ---
>  drivers/mtd/nand/raw/meson_nand.c | 24 ++++++++++++++++++++++++
>  1 file changed, 24 insertions(+)
> 
> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> index 8526a6b87720..a31106c943d7 100644
> --- a/drivers/mtd/nand/raw/meson_nand.c
> +++ b/drivers/mtd/nand/raw/meson_nand.c
> @@ -755,6 +755,30 @@ static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
>  	u32 oob_bytes;
>  	u32 page_size;
>  	int ret;
> +	int i;
> +
> +	/* Read ECC codes and user bytes. */
> +	for (i = 0; i < nand->ecc.steps; i++) {
> +		u32 ecc_offs = nand->ecc.size * (i + 1) +
> +			       NFC_OOB_PER_ECC(nand) * i;
> +
> +		ret = nand_read_page_op(nand, page, 0, NULL, 0);
> +		if (ret)
> +			return ret;
> +
> +		/* Use temporary buffer, because 'nand_change_read_column_op()'
> +		 * seems work with some alignment, so we can't read data to
> +		 * 'oob_buf' directly.

DMA?

> +		 */
> +		ret = nand_change_read_column_op(nand, ecc_offs, meson_chip->oob_buf,
> +						 NFC_OOB_PER_ECC(nand), false);
> +		if (ret)
> +			return ret;
> +
> +		memcpy(oob_buf + i * NFC_OOB_PER_ECC(nand),
> +		       meson_chip->oob_buf,
> +		       NFC_OOB_PER_ECC(nand));
> +	}
>  
>  	oob_bytes = meson_nfc_get_oob_bytes(nand);
>  


Thanks,
Miquèl

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

* Re: [PATCH v4 3/5] mtd: rawnand: meson: always read whole OOB bytes
@ 2023-05-22 15:38     ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-22 15:38 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Jianxin Pan, Yixun Lan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:37 +0300:

> This changes size of read access to OOB area by reading all bytes of
> OOB (free bytes + ECC engine bytes).

This is normally up to the user (user in your case == jffs2). The
controller driver should expose a number of user accessible bytes and
then when users want the OOB area, they should access it entirely. On
top of that read, they can extract (or "write only") the user bytes.

> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> ---
>  drivers/mtd/nand/raw/meson_nand.c | 24 ++++++++++++++++++++++++
>  1 file changed, 24 insertions(+)
> 
> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> index 8526a6b87720..a31106c943d7 100644
> --- a/drivers/mtd/nand/raw/meson_nand.c
> +++ b/drivers/mtd/nand/raw/meson_nand.c
> @@ -755,6 +755,30 @@ static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
>  	u32 oob_bytes;
>  	u32 page_size;
>  	int ret;
> +	int i;
> +
> +	/* Read ECC codes and user bytes. */
> +	for (i = 0; i < nand->ecc.steps; i++) {
> +		u32 ecc_offs = nand->ecc.size * (i + 1) +
> +			       NFC_OOB_PER_ECC(nand) * i;
> +
> +		ret = nand_read_page_op(nand, page, 0, NULL, 0);
> +		if (ret)
> +			return ret;
> +
> +		/* Use temporary buffer, because 'nand_change_read_column_op()'
> +		 * seems work with some alignment, so we can't read data to
> +		 * 'oob_buf' directly.

DMA?

> +		 */
> +		ret = nand_change_read_column_op(nand, ecc_offs, meson_chip->oob_buf,
> +						 NFC_OOB_PER_ECC(nand), false);
> +		if (ret)
> +			return ret;
> +
> +		memcpy(oob_buf + i * NFC_OOB_PER_ECC(nand),
> +		       meson_chip->oob_buf,
> +		       NFC_OOB_PER_ECC(nand));
> +	}
>  
>  	oob_bytes = meson_nfc_get_oob_bytes(nand);
>  


Thanks,
Miquèl

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

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

* Re: [PATCH v4 3/5] mtd: rawnand: meson: always read whole OOB bytes
@ 2023-05-22 15:38     ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-22 15:38 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Jianxin Pan, Yixun Lan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:37 +0300:

> This changes size of read access to OOB area by reading all bytes of
> OOB (free bytes + ECC engine bytes).

This is normally up to the user (user in your case == jffs2). The
controller driver should expose a number of user accessible bytes and
then when users want the OOB area, they should access it entirely. On
top of that read, they can extract (or "write only") the user bytes.

> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> ---
>  drivers/mtd/nand/raw/meson_nand.c | 24 ++++++++++++++++++++++++
>  1 file changed, 24 insertions(+)
> 
> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> index 8526a6b87720..a31106c943d7 100644
> --- a/drivers/mtd/nand/raw/meson_nand.c
> +++ b/drivers/mtd/nand/raw/meson_nand.c
> @@ -755,6 +755,30 @@ static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
>  	u32 oob_bytes;
>  	u32 page_size;
>  	int ret;
> +	int i;
> +
> +	/* Read ECC codes and user bytes. */
> +	for (i = 0; i < nand->ecc.steps; i++) {
> +		u32 ecc_offs = nand->ecc.size * (i + 1) +
> +			       NFC_OOB_PER_ECC(nand) * i;
> +
> +		ret = nand_read_page_op(nand, page, 0, NULL, 0);
> +		if (ret)
> +			return ret;
> +
> +		/* Use temporary buffer, because 'nand_change_read_column_op()'
> +		 * seems work with some alignment, so we can't read data to
> +		 * 'oob_buf' directly.

DMA?

> +		 */
> +		ret = nand_change_read_column_op(nand, ecc_offs, meson_chip->oob_buf,
> +						 NFC_OOB_PER_ECC(nand), false);
> +		if (ret)
> +			return ret;
> +
> +		memcpy(oob_buf + i * NFC_OOB_PER_ECC(nand),
> +		       meson_chip->oob_buf,
> +		       NFC_OOB_PER_ECC(nand));
> +	}
>  
>  	oob_bytes = meson_nfc_get_oob_bytes(nand);
>  


Thanks,
Miquèl

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v4 3/5] mtd: rawnand: meson: always read whole OOB bytes
@ 2023-05-22 15:38     ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-22 15:38 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Jianxin Pan, Yixun Lan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:37 +0300:

> This changes size of read access to OOB area by reading all bytes of
> OOB (free bytes + ECC engine bytes).

This is normally up to the user (user in your case == jffs2). The
controller driver should expose a number of user accessible bytes and
then when users want the OOB area, they should access it entirely. On
top of that read, they can extract (or "write only") the user bytes.

> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> ---
>  drivers/mtd/nand/raw/meson_nand.c | 24 ++++++++++++++++++++++++
>  1 file changed, 24 insertions(+)
> 
> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> index 8526a6b87720..a31106c943d7 100644
> --- a/drivers/mtd/nand/raw/meson_nand.c
> +++ b/drivers/mtd/nand/raw/meson_nand.c
> @@ -755,6 +755,30 @@ static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
>  	u32 oob_bytes;
>  	u32 page_size;
>  	int ret;
> +	int i;
> +
> +	/* Read ECC codes and user bytes. */
> +	for (i = 0; i < nand->ecc.steps; i++) {
> +		u32 ecc_offs = nand->ecc.size * (i + 1) +
> +			       NFC_OOB_PER_ECC(nand) * i;
> +
> +		ret = nand_read_page_op(nand, page, 0, NULL, 0);
> +		if (ret)
> +			return ret;
> +
> +		/* Use temporary buffer, because 'nand_change_read_column_op()'
> +		 * seems work with some alignment, so we can't read data to
> +		 * 'oob_buf' directly.

DMA?

> +		 */
> +		ret = nand_change_read_column_op(nand, ecc_offs, meson_chip->oob_buf,
> +						 NFC_OOB_PER_ECC(nand), false);
> +		if (ret)
> +			return ret;
> +
> +		memcpy(oob_buf + i * NFC_OOB_PER_ECC(nand),
> +		       meson_chip->oob_buf,
> +		       NFC_OOB_PER_ECC(nand));
> +	}
>  
>  	oob_bytes = meson_nfc_get_oob_bytes(nand);
>  


Thanks,
Miquèl

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* Re: [PATCH v4 4/5] mtd: rawnand: meson: check buffer length
  2023-05-15  9:44   ` Arseniy Krasnov
  (?)
  (?)
@ 2023-05-22 15:43     ` Miquel Raynal
  -1 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-22 15:43 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:38 +0300:

> This NAND controller has limited buffer length, so check it before
> command execution to avoid length trim. Also check MTD write size on
> chip attach.
> 
> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> ---
>  drivers/mtd/nand/raw/meson_nand.c | 22 +++++++++++++++++++---
>  1 file changed, 19 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> index a31106c943d7..dc0d7160520b 100644
> --- a/drivers/mtd/nand/raw/meson_nand.c
> +++ b/drivers/mtd/nand/raw/meson_nand.c
> @@ -111,6 +111,8 @@
>  #define NFC_USER_BYTES		2
>  #define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
>  
> +#define NFC_CMD_RAW_LEN		GENMASK(13, 0)
> +
>  struct meson_nfc_nand_chip {
>  	struct list_head node;
>  	struct nand_chip nand;
> @@ -284,7 +286,7 @@ static void meson_nfc_cmd_access(struct nand_chip *nand, int raw, bool dir,
>  
>  	if (raw) {
>  		len = mtd->writesize + mtd->oobsize;
> -		cmd = (len & GENMASK(13, 0)) | scrambler | DMA_DIR(dir);
> +		cmd = len | scrambler | DMA_DIR(dir);
>  		writel(cmd, nfc->reg_base + NFC_REG_CMD);
>  		return;
>  	}
> @@ -562,6 +564,9 @@ static int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len)
>  	u32 cmd;
>  	u8 *info;
>  
> +	if (len > NFC_CMD_RAW_LEN)
> +		return -EINVAL;
> +
>  	info = kzalloc(PER_INFO_BYTE, GFP_KERNEL);
>  	if (!info)
>  		return -ENOMEM;
> @@ -571,7 +576,7 @@ static int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len)
>  	if (ret)
>  		goto out;
>  
> -	cmd = NFC_CMD_N2M | (len & GENMASK(13, 0));
> +	cmd = NFC_CMD_N2M | len;
>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>  
>  	meson_nfc_drain_cmd(nfc);
> @@ -590,12 +595,15 @@ static int meson_nfc_write_buf(struct nand_chip *nand, u8 *buf, int len)
>  	int ret = 0;
>  	u32 cmd;
>  
> +	if (len > NFC_CMD_RAW_LEN)
> +		return -EINVAL;

I think this helper is related to exec_op, if yes, you should probably
ask an early check in the exec_op implementation (perhaps as part of a
dedicated helper).

Same above.

Otherwise looks good.

> +
>  	ret = meson_nfc_dma_buffer_setup(nand, buf, len, NULL,
>  					 0, DMA_TO_DEVICE);
>  	if (ret)
>  		return ret;
>  
> -	cmd = NFC_CMD_M2N | (len & GENMASK(13, 0));
> +	cmd = NFC_CMD_M2N | len;
>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>  
>  	meson_nfc_drain_cmd(nfc);
> @@ -1328,6 +1336,7 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>  	struct mtd_info *mtd = nand_to_mtd(nand);
>  	int nsectors = mtd->writesize / 1024;
> +	int raw_writesize;
>  	int ret;
>  
>  	if (!mtd->name) {
> @@ -1339,6 +1348,13 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>  			return -ENOMEM;
>  	}
>  
> +	raw_writesize = mtd->writesize + mtd->oobsize;
> +	if (raw_writesize > NFC_CMD_RAW_LEN) {
> +		dev_err(nfc->dev, "too big write size in raw mode: %d > %ld\n",
> +			raw_writesize, NFC_CMD_RAW_LEN);
> +		return -EINVAL;
> +	}
> +
>  	if (nand->bbt_options & NAND_BBT_USE_FLASH)
>  		nand->bbt_options |= NAND_BBT_NO_OOB;
>  


Thanks,
Miquèl

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

* Re: [PATCH v4 4/5] mtd: rawnand: meson: check buffer length
@ 2023-05-22 15:43     ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-22 15:43 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:38 +0300:

> This NAND controller has limited buffer length, so check it before
> command execution to avoid length trim. Also check MTD write size on
> chip attach.
> 
> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> ---
>  drivers/mtd/nand/raw/meson_nand.c | 22 +++++++++++++++++++---
>  1 file changed, 19 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> index a31106c943d7..dc0d7160520b 100644
> --- a/drivers/mtd/nand/raw/meson_nand.c
> +++ b/drivers/mtd/nand/raw/meson_nand.c
> @@ -111,6 +111,8 @@
>  #define NFC_USER_BYTES		2
>  #define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
>  
> +#define NFC_CMD_RAW_LEN		GENMASK(13, 0)
> +
>  struct meson_nfc_nand_chip {
>  	struct list_head node;
>  	struct nand_chip nand;
> @@ -284,7 +286,7 @@ static void meson_nfc_cmd_access(struct nand_chip *nand, int raw, bool dir,
>  
>  	if (raw) {
>  		len = mtd->writesize + mtd->oobsize;
> -		cmd = (len & GENMASK(13, 0)) | scrambler | DMA_DIR(dir);
> +		cmd = len | scrambler | DMA_DIR(dir);
>  		writel(cmd, nfc->reg_base + NFC_REG_CMD);
>  		return;
>  	}
> @@ -562,6 +564,9 @@ static int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len)
>  	u32 cmd;
>  	u8 *info;
>  
> +	if (len > NFC_CMD_RAW_LEN)
> +		return -EINVAL;
> +
>  	info = kzalloc(PER_INFO_BYTE, GFP_KERNEL);
>  	if (!info)
>  		return -ENOMEM;
> @@ -571,7 +576,7 @@ static int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len)
>  	if (ret)
>  		goto out;
>  
> -	cmd = NFC_CMD_N2M | (len & GENMASK(13, 0));
> +	cmd = NFC_CMD_N2M | len;
>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>  
>  	meson_nfc_drain_cmd(nfc);
> @@ -590,12 +595,15 @@ static int meson_nfc_write_buf(struct nand_chip *nand, u8 *buf, int len)
>  	int ret = 0;
>  	u32 cmd;
>  
> +	if (len > NFC_CMD_RAW_LEN)
> +		return -EINVAL;

I think this helper is related to exec_op, if yes, you should probably
ask an early check in the exec_op implementation (perhaps as part of a
dedicated helper).

Same above.

Otherwise looks good.

> +
>  	ret = meson_nfc_dma_buffer_setup(nand, buf, len, NULL,
>  					 0, DMA_TO_DEVICE);
>  	if (ret)
>  		return ret;
>  
> -	cmd = NFC_CMD_M2N | (len & GENMASK(13, 0));
> +	cmd = NFC_CMD_M2N | len;
>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>  
>  	meson_nfc_drain_cmd(nfc);
> @@ -1328,6 +1336,7 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>  	struct mtd_info *mtd = nand_to_mtd(nand);
>  	int nsectors = mtd->writesize / 1024;
> +	int raw_writesize;
>  	int ret;
>  
>  	if (!mtd->name) {
> @@ -1339,6 +1348,13 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>  			return -ENOMEM;
>  	}
>  
> +	raw_writesize = mtd->writesize + mtd->oobsize;
> +	if (raw_writesize > NFC_CMD_RAW_LEN) {
> +		dev_err(nfc->dev, "too big write size in raw mode: %d > %ld\n",
> +			raw_writesize, NFC_CMD_RAW_LEN);
> +		return -EINVAL;
> +	}
> +
>  	if (nand->bbt_options & NAND_BBT_USE_FLASH)
>  		nand->bbt_options |= NAND_BBT_NO_OOB;
>  


Thanks,
Miquèl

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

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

* Re: [PATCH v4 4/5] mtd: rawnand: meson: check buffer length
@ 2023-05-22 15:43     ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-22 15:43 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:38 +0300:

> This NAND controller has limited buffer length, so check it before
> command execution to avoid length trim. Also check MTD write size on
> chip attach.
> 
> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> ---
>  drivers/mtd/nand/raw/meson_nand.c | 22 +++++++++++++++++++---
>  1 file changed, 19 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> index a31106c943d7..dc0d7160520b 100644
> --- a/drivers/mtd/nand/raw/meson_nand.c
> +++ b/drivers/mtd/nand/raw/meson_nand.c
> @@ -111,6 +111,8 @@
>  #define NFC_USER_BYTES		2
>  #define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
>  
> +#define NFC_CMD_RAW_LEN		GENMASK(13, 0)
> +
>  struct meson_nfc_nand_chip {
>  	struct list_head node;
>  	struct nand_chip nand;
> @@ -284,7 +286,7 @@ static void meson_nfc_cmd_access(struct nand_chip *nand, int raw, bool dir,
>  
>  	if (raw) {
>  		len = mtd->writesize + mtd->oobsize;
> -		cmd = (len & GENMASK(13, 0)) | scrambler | DMA_DIR(dir);
> +		cmd = len | scrambler | DMA_DIR(dir);
>  		writel(cmd, nfc->reg_base + NFC_REG_CMD);
>  		return;
>  	}
> @@ -562,6 +564,9 @@ static int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len)
>  	u32 cmd;
>  	u8 *info;
>  
> +	if (len > NFC_CMD_RAW_LEN)
> +		return -EINVAL;
> +
>  	info = kzalloc(PER_INFO_BYTE, GFP_KERNEL);
>  	if (!info)
>  		return -ENOMEM;
> @@ -571,7 +576,7 @@ static int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len)
>  	if (ret)
>  		goto out;
>  
> -	cmd = NFC_CMD_N2M | (len & GENMASK(13, 0));
> +	cmd = NFC_CMD_N2M | len;
>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>  
>  	meson_nfc_drain_cmd(nfc);
> @@ -590,12 +595,15 @@ static int meson_nfc_write_buf(struct nand_chip *nand, u8 *buf, int len)
>  	int ret = 0;
>  	u32 cmd;
>  
> +	if (len > NFC_CMD_RAW_LEN)
> +		return -EINVAL;

I think this helper is related to exec_op, if yes, you should probably
ask an early check in the exec_op implementation (perhaps as part of a
dedicated helper).

Same above.

Otherwise looks good.

> +
>  	ret = meson_nfc_dma_buffer_setup(nand, buf, len, NULL,
>  					 0, DMA_TO_DEVICE);
>  	if (ret)
>  		return ret;
>  
> -	cmd = NFC_CMD_M2N | (len & GENMASK(13, 0));
> +	cmd = NFC_CMD_M2N | len;
>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>  
>  	meson_nfc_drain_cmd(nfc);
> @@ -1328,6 +1336,7 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>  	struct mtd_info *mtd = nand_to_mtd(nand);
>  	int nsectors = mtd->writesize / 1024;
> +	int raw_writesize;
>  	int ret;
>  
>  	if (!mtd->name) {
> @@ -1339,6 +1348,13 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>  			return -ENOMEM;
>  	}
>  
> +	raw_writesize = mtd->writesize + mtd->oobsize;
> +	if (raw_writesize > NFC_CMD_RAW_LEN) {
> +		dev_err(nfc->dev, "too big write size in raw mode: %d > %ld\n",
> +			raw_writesize, NFC_CMD_RAW_LEN);
> +		return -EINVAL;
> +	}
> +
>  	if (nand->bbt_options & NAND_BBT_USE_FLASH)
>  		nand->bbt_options |= NAND_BBT_NO_OOB;
>  


Thanks,
Miquèl

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* Re: [PATCH v4 4/5] mtd: rawnand: meson: check buffer length
@ 2023-05-22 15:43     ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-22 15:43 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:38 +0300:

> This NAND controller has limited buffer length, so check it before
> command execution to avoid length trim. Also check MTD write size on
> chip attach.
> 
> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> ---
>  drivers/mtd/nand/raw/meson_nand.c | 22 +++++++++++++++++++---
>  1 file changed, 19 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> index a31106c943d7..dc0d7160520b 100644
> --- a/drivers/mtd/nand/raw/meson_nand.c
> +++ b/drivers/mtd/nand/raw/meson_nand.c
> @@ -111,6 +111,8 @@
>  #define NFC_USER_BYTES		2
>  #define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
>  
> +#define NFC_CMD_RAW_LEN		GENMASK(13, 0)
> +
>  struct meson_nfc_nand_chip {
>  	struct list_head node;
>  	struct nand_chip nand;
> @@ -284,7 +286,7 @@ static void meson_nfc_cmd_access(struct nand_chip *nand, int raw, bool dir,
>  
>  	if (raw) {
>  		len = mtd->writesize + mtd->oobsize;
> -		cmd = (len & GENMASK(13, 0)) | scrambler | DMA_DIR(dir);
> +		cmd = len | scrambler | DMA_DIR(dir);
>  		writel(cmd, nfc->reg_base + NFC_REG_CMD);
>  		return;
>  	}
> @@ -562,6 +564,9 @@ static int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len)
>  	u32 cmd;
>  	u8 *info;
>  
> +	if (len > NFC_CMD_RAW_LEN)
> +		return -EINVAL;
> +
>  	info = kzalloc(PER_INFO_BYTE, GFP_KERNEL);
>  	if (!info)
>  		return -ENOMEM;
> @@ -571,7 +576,7 @@ static int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len)
>  	if (ret)
>  		goto out;
>  
> -	cmd = NFC_CMD_N2M | (len & GENMASK(13, 0));
> +	cmd = NFC_CMD_N2M | len;
>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>  
>  	meson_nfc_drain_cmd(nfc);
> @@ -590,12 +595,15 @@ static int meson_nfc_write_buf(struct nand_chip *nand, u8 *buf, int len)
>  	int ret = 0;
>  	u32 cmd;
>  
> +	if (len > NFC_CMD_RAW_LEN)
> +		return -EINVAL;

I think this helper is related to exec_op, if yes, you should probably
ask an early check in the exec_op implementation (perhaps as part of a
dedicated helper).

Same above.

Otherwise looks good.

> +
>  	ret = meson_nfc_dma_buffer_setup(nand, buf, len, NULL,
>  					 0, DMA_TO_DEVICE);
>  	if (ret)
>  		return ret;
>  
> -	cmd = NFC_CMD_M2N | (len & GENMASK(13, 0));
> +	cmd = NFC_CMD_M2N | len;
>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>  
>  	meson_nfc_drain_cmd(nfc);
> @@ -1328,6 +1336,7 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>  	struct mtd_info *mtd = nand_to_mtd(nand);
>  	int nsectors = mtd->writesize / 1024;
> +	int raw_writesize;
>  	int ret;
>  
>  	if (!mtd->name) {
> @@ -1339,6 +1348,13 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>  			return -ENOMEM;
>  	}
>  
> +	raw_writesize = mtd->writesize + mtd->oobsize;
> +	if (raw_writesize > NFC_CMD_RAW_LEN) {
> +		dev_err(nfc->dev, "too big write size in raw mode: %d > %ld\n",
> +			raw_writesize, NFC_CMD_RAW_LEN);
> +		return -EINVAL;
> +	}
> +
>  	if (nand->bbt_options & NAND_BBT_USE_FLASH)
>  		nand->bbt_options |= NAND_BBT_NO_OOB;
>  


Thanks,
Miquèl

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
  2023-05-22 15:05     ` Miquel Raynal
  (?)
  (?)
@ 2023-05-23  9:12       ` Arseniy Krasnov
  -1 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-23  9:12 UTC (permalink / raw)
  To: Miquel Raynal, Liang Yang
  Cc: Richard Weinberger, Vignesh Raghavendra, Neil Armstrong,
	Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Yixun Lan,
	Jianxin Pan, oxffffaa, kernel, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

Hello Miquel, Liang

On 22.05.2023 18:05, Miquel Raynal wrote:
> Hi Arseniy,
> 
> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
> 
>> This fixes read/write functionality by:
>> 1) Changing NFC_CMD_RB_INT bit value.
> 
> I guess this is a separate fix
> 

Ok, I'll move it to separate patch

>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.
> 
> Is this really needed? Looks like you're delaying the next op only. Is
> using a delay enough? If yes, then it's probably the wrong approach.
> 
>> 3) Adding extra idle commands during r/w request.
> 
> Question about this below, might also be a patch on its own?
> 
>> 4) Adding extra NAND_CMD_READ0 on each read request.
>>
>> Without this patch driver works unstable, for example there are a lot
>> of ECC errors.
> 
> I believe all the fixes should be Cc'ed to stable, please add in your
> commits:
> 
> Cc: stable@...
> 

Ack, after everything will be clear with this patch :)

>>
>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>> Suggested-by: Liang Yang <liang.yang@amlogic.com>
>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>> ---
>>  drivers/mtd/nand/raw/meson_nand.c | 30 +++++++++++++++++++++---------
>>  1 file changed, 21 insertions(+), 9 deletions(-)
>>
>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>> index 074e14225c06..2f4d8c84186b 100644
>> --- a/drivers/mtd/nand/raw/meson_nand.c
>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>> @@ -37,7 +37,7 @@
>>  #define NFC_CMD_SCRAMBLER_ENABLE	BIT(19)
>>  #define NFC_CMD_SCRAMBLER_DISABLE	0
>>  #define NFC_CMD_SHORTMODE_DISABLE	0
>> -#define NFC_CMD_RB_INT		BIT(14)
>> +#define NFC_CMD_RB_INT ((0xb << 10) | BIT(18) | BIT(16))
>>  
>>  #define NFC_CMD_GET_SIZE(x)	(((x) >> 22) & GENMASK(4, 0))
>>  
>> @@ -392,7 +392,7 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>  	}
>>  }
>>  
>> -static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>> +static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
>>  {
>>  	u32 cmd, cfg;
>>  	int ret = 0;
>> @@ -407,17 +407,29 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>  
>>  	reinit_completion(&nfc->completion);
>>  
>> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +	meson_nfc_cmd_idle(nfc, 5);
> 
> Why 5 and 2 below? They look like magic values. Is this totally
> experimental?

@Liang, may be change it to defines ?

> 
>> +
>>  	/* use the max erase time as the maximum clock for waiting R/B */
>> -	cmd = NFC_CMD_RB | NFC_CMD_RB_INT
>> -		| nfc->param.chip_select | nfc->timing.tbers_max;
> 
> This is not documented in the commit log, is it?
> 
>> +	cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +	meson_nfc_cmd_idle(nfc, 2);
>>  
>>  	ret = wait_for_completion_timeout(&nfc->completion,
>>  					  msecs_to_jiffies(timeout_ms));
>>  	if (ret == 0)
>> -		ret = -1;
>> +		return -1;
> 
> Please use real error codes, such as ETIMEDOUT.

Ack

> 
>>  
>> -	return ret;
>> +	if (!cmd_read0)
>> +		return 0;
>> +
>> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_READ0;
> 
> This looks really wrong, I don't get why you would need to send an
> expensive READ0 command.

This logic was suggested by @Liang Yang here to fix this driver (suggested as patch):
https://lore.kernel.org/linux-mtd/8537e736-44a8-d17b-7923-25d5bd406dcc@sberdevices.ru/T/#m0df09d2ab2cac98431fb268a4ce3c00dc5c7a69e
@Liang, could You please give us more details?

Thanks, Arseniy

> 
>> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +	meson_nfc_drain_cmd(nfc);
>> +	meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
>> +
>> +	return 0;
>>  }
>>  
>>  static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
>> @@ -623,7 +635,7 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>  	if (in) {
>>  		nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART;
>>  		writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
>> -		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max));
>> +		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max), 1);
>>  	} else {
>>  		meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
>>  	}
>> @@ -669,7 +681,7 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>  
>>  	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> -	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>> +	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max), 0);
>>  
>>  	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>  
>> @@ -952,7 +964,7 @@ static int meson_nfc_exec_op(struct nand_chip *nand,
>>  			break;
>>  
>>  		case NAND_OP_WAITRDY_INSTR:
>> -			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms);
>> +			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms, 1);
>>  			if (instr->delay_ns)
>>  				meson_nfc_cmd_idle(nfc, delay_idle);
>>  			break;
> 
> 
> Thanks,
> Miquèl

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-23  9:12       ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-23  9:12 UTC (permalink / raw)
  To: Miquel Raynal, Liang Yang
  Cc: Richard Weinberger, Vignesh Raghavendra, Neil Armstrong,
	Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Yixun Lan,
	Jianxin Pan, oxffffaa, kernel, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

Hello Miquel, Liang

On 22.05.2023 18:05, Miquel Raynal wrote:
> Hi Arseniy,
> 
> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
> 
>> This fixes read/write functionality by:
>> 1) Changing NFC_CMD_RB_INT bit value.
> 
> I guess this is a separate fix
> 

Ok, I'll move it to separate patch

>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.
> 
> Is this really needed? Looks like you're delaying the next op only. Is
> using a delay enough? If yes, then it's probably the wrong approach.
> 
>> 3) Adding extra idle commands during r/w request.
> 
> Question about this below, might also be a patch on its own?
> 
>> 4) Adding extra NAND_CMD_READ0 on each read request.
>>
>> Without this patch driver works unstable, for example there are a lot
>> of ECC errors.
> 
> I believe all the fixes should be Cc'ed to stable, please add in your
> commits:
> 
> Cc: stable@...
> 

Ack, after everything will be clear with this patch :)

>>
>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>> Suggested-by: Liang Yang <liang.yang@amlogic.com>
>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>> ---
>>  drivers/mtd/nand/raw/meson_nand.c | 30 +++++++++++++++++++++---------
>>  1 file changed, 21 insertions(+), 9 deletions(-)
>>
>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>> index 074e14225c06..2f4d8c84186b 100644
>> --- a/drivers/mtd/nand/raw/meson_nand.c
>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>> @@ -37,7 +37,7 @@
>>  #define NFC_CMD_SCRAMBLER_ENABLE	BIT(19)
>>  #define NFC_CMD_SCRAMBLER_DISABLE	0
>>  #define NFC_CMD_SHORTMODE_DISABLE	0
>> -#define NFC_CMD_RB_INT		BIT(14)
>> +#define NFC_CMD_RB_INT ((0xb << 10) | BIT(18) | BIT(16))
>>  
>>  #define NFC_CMD_GET_SIZE(x)	(((x) >> 22) & GENMASK(4, 0))
>>  
>> @@ -392,7 +392,7 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>  	}
>>  }
>>  
>> -static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>> +static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
>>  {
>>  	u32 cmd, cfg;
>>  	int ret = 0;
>> @@ -407,17 +407,29 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>  
>>  	reinit_completion(&nfc->completion);
>>  
>> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +	meson_nfc_cmd_idle(nfc, 5);
> 
> Why 5 and 2 below? They look like magic values. Is this totally
> experimental?

@Liang, may be change it to defines ?

> 
>> +
>>  	/* use the max erase time as the maximum clock for waiting R/B */
>> -	cmd = NFC_CMD_RB | NFC_CMD_RB_INT
>> -		| nfc->param.chip_select | nfc->timing.tbers_max;
> 
> This is not documented in the commit log, is it?
> 
>> +	cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +	meson_nfc_cmd_idle(nfc, 2);
>>  
>>  	ret = wait_for_completion_timeout(&nfc->completion,
>>  					  msecs_to_jiffies(timeout_ms));
>>  	if (ret == 0)
>> -		ret = -1;
>> +		return -1;
> 
> Please use real error codes, such as ETIMEDOUT.

Ack

> 
>>  
>> -	return ret;
>> +	if (!cmd_read0)
>> +		return 0;
>> +
>> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_READ0;
> 
> This looks really wrong, I don't get why you would need to send an
> expensive READ0 command.

This logic was suggested by @Liang Yang here to fix this driver (suggested as patch):
https://lore.kernel.org/linux-mtd/8537e736-44a8-d17b-7923-25d5bd406dcc@sberdevices.ru/T/#m0df09d2ab2cac98431fb268a4ce3c00dc5c7a69e
@Liang, could You please give us more details?

Thanks, Arseniy

> 
>> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +	meson_nfc_drain_cmd(nfc);
>> +	meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
>> +
>> +	return 0;
>>  }
>>  
>>  static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
>> @@ -623,7 +635,7 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>  	if (in) {
>>  		nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART;
>>  		writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
>> -		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max));
>> +		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max), 1);
>>  	} else {
>>  		meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
>>  	}
>> @@ -669,7 +681,7 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>  
>>  	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> -	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>> +	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max), 0);
>>  
>>  	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>  
>> @@ -952,7 +964,7 @@ static int meson_nfc_exec_op(struct nand_chip *nand,
>>  			break;
>>  
>>  		case NAND_OP_WAITRDY_INSTR:
>> -			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms);
>> +			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms, 1);
>>  			if (instr->delay_ns)
>>  				meson_nfc_cmd_idle(nfc, delay_idle);
>>  			break;
> 
> 
> Thanks,
> Miquèl

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

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-23  9:12       ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-23  9:12 UTC (permalink / raw)
  To: Miquel Raynal, Liang Yang
  Cc: Richard Weinberger, Vignesh Raghavendra, Neil Armstrong,
	Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Yixun Lan,
	Jianxin Pan, oxffffaa, kernel, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

Hello Miquel, Liang

On 22.05.2023 18:05, Miquel Raynal wrote:
> Hi Arseniy,
> 
> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
> 
>> This fixes read/write functionality by:
>> 1) Changing NFC_CMD_RB_INT bit value.
> 
> I guess this is a separate fix
> 

Ok, I'll move it to separate patch

>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.
> 
> Is this really needed? Looks like you're delaying the next op only. Is
> using a delay enough? If yes, then it's probably the wrong approach.
> 
>> 3) Adding extra idle commands during r/w request.
> 
> Question about this below, might also be a patch on its own?
> 
>> 4) Adding extra NAND_CMD_READ0 on each read request.
>>
>> Without this patch driver works unstable, for example there are a lot
>> of ECC errors.
> 
> I believe all the fixes should be Cc'ed to stable, please add in your
> commits:
> 
> Cc: stable@...
> 

Ack, after everything will be clear with this patch :)

>>
>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>> Suggested-by: Liang Yang <liang.yang@amlogic.com>
>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>> ---
>>  drivers/mtd/nand/raw/meson_nand.c | 30 +++++++++++++++++++++---------
>>  1 file changed, 21 insertions(+), 9 deletions(-)
>>
>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>> index 074e14225c06..2f4d8c84186b 100644
>> --- a/drivers/mtd/nand/raw/meson_nand.c
>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>> @@ -37,7 +37,7 @@
>>  #define NFC_CMD_SCRAMBLER_ENABLE	BIT(19)
>>  #define NFC_CMD_SCRAMBLER_DISABLE	0
>>  #define NFC_CMD_SHORTMODE_DISABLE	0
>> -#define NFC_CMD_RB_INT		BIT(14)
>> +#define NFC_CMD_RB_INT ((0xb << 10) | BIT(18) | BIT(16))
>>  
>>  #define NFC_CMD_GET_SIZE(x)	(((x) >> 22) & GENMASK(4, 0))
>>  
>> @@ -392,7 +392,7 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>  	}
>>  }
>>  
>> -static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>> +static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
>>  {
>>  	u32 cmd, cfg;
>>  	int ret = 0;
>> @@ -407,17 +407,29 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>  
>>  	reinit_completion(&nfc->completion);
>>  
>> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +	meson_nfc_cmd_idle(nfc, 5);
> 
> Why 5 and 2 below? They look like magic values. Is this totally
> experimental?

@Liang, may be change it to defines ?

> 
>> +
>>  	/* use the max erase time as the maximum clock for waiting R/B */
>> -	cmd = NFC_CMD_RB | NFC_CMD_RB_INT
>> -		| nfc->param.chip_select | nfc->timing.tbers_max;
> 
> This is not documented in the commit log, is it?
> 
>> +	cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +	meson_nfc_cmd_idle(nfc, 2);
>>  
>>  	ret = wait_for_completion_timeout(&nfc->completion,
>>  					  msecs_to_jiffies(timeout_ms));
>>  	if (ret == 0)
>> -		ret = -1;
>> +		return -1;
> 
> Please use real error codes, such as ETIMEDOUT.

Ack

> 
>>  
>> -	return ret;
>> +	if (!cmd_read0)
>> +		return 0;
>> +
>> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_READ0;
> 
> This looks really wrong, I don't get why you would need to send an
> expensive READ0 command.

This logic was suggested by @Liang Yang here to fix this driver (suggested as patch):
https://lore.kernel.org/linux-mtd/8537e736-44a8-d17b-7923-25d5bd406dcc@sberdevices.ru/T/#m0df09d2ab2cac98431fb268a4ce3c00dc5c7a69e
@Liang, could You please give us more details?

Thanks, Arseniy

> 
>> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +	meson_nfc_drain_cmd(nfc);
>> +	meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
>> +
>> +	return 0;
>>  }
>>  
>>  static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
>> @@ -623,7 +635,7 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>  	if (in) {
>>  		nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART;
>>  		writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
>> -		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max));
>> +		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max), 1);
>>  	} else {
>>  		meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
>>  	}
>> @@ -669,7 +681,7 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>  
>>  	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> -	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>> +	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max), 0);
>>  
>>  	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>  
>> @@ -952,7 +964,7 @@ static int meson_nfc_exec_op(struct nand_chip *nand,
>>  			break;
>>  
>>  		case NAND_OP_WAITRDY_INSTR:
>> -			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms);
>> +			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms, 1);
>>  			if (instr->delay_ns)
>>  				meson_nfc_cmd_idle(nfc, delay_idle);
>>  			break;
> 
> 
> Thanks,
> Miquèl

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-23  9:12       ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-23  9:12 UTC (permalink / raw)
  To: Miquel Raynal, Liang Yang
  Cc: Richard Weinberger, Vignesh Raghavendra, Neil Armstrong,
	Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Yixun Lan,
	Jianxin Pan, oxffffaa, kernel, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

Hello Miquel, Liang

On 22.05.2023 18:05, Miquel Raynal wrote:
> Hi Arseniy,
> 
> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
> 
>> This fixes read/write functionality by:
>> 1) Changing NFC_CMD_RB_INT bit value.
> 
> I guess this is a separate fix
> 

Ok, I'll move it to separate patch

>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.
> 
> Is this really needed? Looks like you're delaying the next op only. Is
> using a delay enough? If yes, then it's probably the wrong approach.
> 
>> 3) Adding extra idle commands during r/w request.
> 
> Question about this below, might also be a patch on its own?
> 
>> 4) Adding extra NAND_CMD_READ0 on each read request.
>>
>> Without this patch driver works unstable, for example there are a lot
>> of ECC errors.
> 
> I believe all the fixes should be Cc'ed to stable, please add in your
> commits:
> 
> Cc: stable@...
> 

Ack, after everything will be clear with this patch :)

>>
>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>> Suggested-by: Liang Yang <liang.yang@amlogic.com>
>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>> ---
>>  drivers/mtd/nand/raw/meson_nand.c | 30 +++++++++++++++++++++---------
>>  1 file changed, 21 insertions(+), 9 deletions(-)
>>
>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>> index 074e14225c06..2f4d8c84186b 100644
>> --- a/drivers/mtd/nand/raw/meson_nand.c
>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>> @@ -37,7 +37,7 @@
>>  #define NFC_CMD_SCRAMBLER_ENABLE	BIT(19)
>>  #define NFC_CMD_SCRAMBLER_DISABLE	0
>>  #define NFC_CMD_SHORTMODE_DISABLE	0
>> -#define NFC_CMD_RB_INT		BIT(14)
>> +#define NFC_CMD_RB_INT ((0xb << 10) | BIT(18) | BIT(16))
>>  
>>  #define NFC_CMD_GET_SIZE(x)	(((x) >> 22) & GENMASK(4, 0))
>>  
>> @@ -392,7 +392,7 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>  	}
>>  }
>>  
>> -static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>> +static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
>>  {
>>  	u32 cmd, cfg;
>>  	int ret = 0;
>> @@ -407,17 +407,29 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>  
>>  	reinit_completion(&nfc->completion);
>>  
>> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +	meson_nfc_cmd_idle(nfc, 5);
> 
> Why 5 and 2 below? They look like magic values. Is this totally
> experimental?

@Liang, may be change it to defines ?

> 
>> +
>>  	/* use the max erase time as the maximum clock for waiting R/B */
>> -	cmd = NFC_CMD_RB | NFC_CMD_RB_INT
>> -		| nfc->param.chip_select | nfc->timing.tbers_max;
> 
> This is not documented in the commit log, is it?
> 
>> +	cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +	meson_nfc_cmd_idle(nfc, 2);
>>  
>>  	ret = wait_for_completion_timeout(&nfc->completion,
>>  					  msecs_to_jiffies(timeout_ms));
>>  	if (ret == 0)
>> -		ret = -1;
>> +		return -1;
> 
> Please use real error codes, such as ETIMEDOUT.

Ack

> 
>>  
>> -	return ret;
>> +	if (!cmd_read0)
>> +		return 0;
>> +
>> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_READ0;
> 
> This looks really wrong, I don't get why you would need to send an
> expensive READ0 command.

This logic was suggested by @Liang Yang here to fix this driver (suggested as patch):
https://lore.kernel.org/linux-mtd/8537e736-44a8-d17b-7923-25d5bd406dcc@sberdevices.ru/T/#m0df09d2ab2cac98431fb268a4ce3c00dc5c7a69e
@Liang, could You please give us more details?

Thanks, Arseniy

> 
>> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> +	meson_nfc_drain_cmd(nfc);
>> +	meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
>> +
>> +	return 0;
>>  }
>>  
>>  static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
>> @@ -623,7 +635,7 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>  	if (in) {
>>  		nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART;
>>  		writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
>> -		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max));
>> +		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max), 1);
>>  	} else {
>>  		meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
>>  	}
>> @@ -669,7 +681,7 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>  
>>  	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>> -	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>> +	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max), 0);
>>  
>>  	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>  
>> @@ -952,7 +964,7 @@ static int meson_nfc_exec_op(struct nand_chip *nand,
>>  			break;
>>  
>>  		case NAND_OP_WAITRDY_INSTR:
>> -			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms);
>> +			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms, 1);
>>  			if (instr->delay_ns)
>>  				meson_nfc_cmd_idle(nfc, delay_idle);
>>  			break;
> 
> 
> Thanks,
> Miquèl

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
  2023-05-22 15:33     ` Miquel Raynal
  (?)
  (?)
@ 2023-05-23 17:17       ` Arseniy Krasnov
  -1 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-23 17:17 UTC (permalink / raw)
  To: Miquel Raynal, Liang Yang
  Cc: Richard Weinberger, Vignesh Raghavendra, Neil Armstrong,
	Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Yixun Lan,
	Jianxin Pan, oxffffaa, kernel, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

Hello Miquel! Thanks for detailed review!

On 22.05.2023 18:33, Miquel Raynal wrote:
> Hi Arseniy,
> 
> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:36 +0300:
> 
> The title should perhaps be "only expose unprotected user OOB bytes".
> 
>> This moves free bytes of OOB to non-protected ECC area. It is needed to
>> make JFFS2 works correctly with this NAND controller. Problem fires when
>> JFFS2 driver writes cleanmarker to some page and later it tries to write
>> to this page - write will be done successfully, but after that such page
>> becomes unreadable due to invalid ECC codes. This happens because second
>> write needs to update ECC codes, but it is impossible to do it correctly
>> without block erase. So idea of this patch is to
> 
> "... is to use the unprotected OOB area to store the cleanmarkers, so
> that they can be written by the filesystem without caring much about
> the page being empty or not: the ECC codes will not be written anyway."
> ?

Ok

> 
> JFFS2 is only useful on tiny NAND devices, where UBI does not fit,
> which are usually true SLC flashes, with the capability of writing
> a page with empty (0xFF) data, and still be able to write actual data
> to it later in a second write.

You mean to include text above to commit message also?

> 
>> split accesses to OOB
>> free bytes and data on each page - now both of them does not depends on
>> each other.
>>
>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>> ---
>>  drivers/mtd/nand/raw/meson_nand.c | 192 ++++++++++++++++++++++++------
>>  1 file changed, 155 insertions(+), 37 deletions(-)
>>
>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>> index 2f4d8c84186b..8526a6b87720 100644
>> --- a/drivers/mtd/nand/raw/meson_nand.c
>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>> @@ -108,6 +108,9 @@
>>  
>>  #define PER_INFO_BYTE		8
>>  
>> +#define NFC_USER_BYTES		2
>> +#define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
>> +
>>  struct meson_nfc_nand_chip {
>>  	struct list_head node;
>>  	struct nand_chip nand;
>> @@ -122,6 +125,7 @@ struct meson_nfc_nand_chip {
>>  	u8 *data_buf;
>>  	__le64 *info_buf;
>>  	u32 nsels;
>> +	u8 *oob_buf;
>>  	u8 sels[];
>>  };
>>  
>> @@ -338,7 +342,7 @@ static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
>>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>  	int len;
>>  
>> -	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
>> +	len = nand->ecc.size * (i + 1) + NFC_OOB_PER_ECC(nand) * i;
> 
> This...
> 
>>  
>>  	return meson_chip->data_buf + len;
>>  }
>> @@ -349,7 +353,7 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>>  	int len, temp;
>>  
>>  	temp = nand->ecc.size + nand->ecc.bytes;
>> -	len = (temp + 2) * i;
>> +	len = (temp + NFC_USER_BYTES) * i;
> 
> ... and this (same below)
> 
> are purely cosmetic, should be in a patch aside.
> 

Ack, i'll move cosmetic updates to separate patch.

>>  
>>  	return meson_chip->data_buf + len;
>>  }
>> @@ -357,29 +361,47 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>>  static void meson_nfc_get_data_oob(struct nand_chip *nand,
>>  				   u8 *buf, u8 *oobbuf)
>>  {
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>  	int i, oob_len = 0;
>>  	u8 *dsrc, *osrc;
>> +	u8 *oobtail;
>>  
>> -	oob_len = nand->ecc.bytes + 2;
>> +	oob_len = NFC_OOB_PER_ECC(nand);
>>  	for (i = 0; i < nand->ecc.steps; i++) {
>>  		if (buf) {
>>  			dsrc = meson_nfc_data_ptr(nand, i);
>>  			memcpy(buf, dsrc, nand->ecc.size);
>>  			buf += nand->ecc.size;
>>  		}
>> -		osrc = meson_nfc_oob_ptr(nand, i);
>> -		memcpy(oobbuf, osrc, oob_len);
>> -		oobbuf += oob_len;
>> +
>> +		if (oobbuf) {
>> +			osrc = meson_nfc_oob_ptr(nand, i);
>> +			memcpy(oobbuf, osrc, oob_len);
>> +			oobbuf += oob_len;
>> +		}
>>  	}
>> +
>> +	if (!oobbuf)
>> +		return;
> 
> The whole "if (oobbuf)" logic is nice to have, but should as well be in
> a dedicated patch.

Sorry, You mean that this logic implements read of ECC codes? And not
related to OOB layout update in this patch?

> 
>> +
>> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
>> +		  (nand->ecc.size + oob_len);
>> +
>> +	/* 'oobbuf' if already shifted to the start of unused area. */
> 
> 		    is?
> s/unused/user/? I'm not sure I get the comment.

Yes, not clear comment.

> 
>> +	memcpy(oobbuf, oobtail, mtd->oobsize - nand->ecc.steps * oob_len);
>>  }
>>  
>>  static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>  				   const u8 *buf, u8 *oobbuf)
>>  {
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>  	int i, oob_len = 0;
>>  	u8 *dsrc, *osrc;
>> +	u8 *oobtail;
>>  
>> -	oob_len = nand->ecc.bytes + 2;
>> +	oob_len = NFC_OOB_PER_ECC(nand);
>>  	for (i = 0; i < nand->ecc.steps; i++) {
>>  		if (buf) {
>>  			dsrc = meson_nfc_data_ptr(nand, i);
>> @@ -390,6 +412,12 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>  		memcpy(osrc, oobbuf, oob_len);
>>  		oobbuf += oob_len;
>>  	}
>> +
>> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
>> +		  (nand->ecc.size + oob_len);
>> +
>> +	/* 'oobbuf' if already shifted to the start of unused area. */
>> +	memcpy(oobtail, oobbuf, mtd->oobsize - nand->ecc.steps * oob_len);
>>  }
>>  
>>  static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
>> @@ -436,25 +464,12 @@ static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>  {
>>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>  	__le64 *info;
>> -	int i, count;
>> -
>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>> -		info = &meson_chip->info_buf[i];
>> -		*info |= oob_buf[count];
>> -		*info |= oob_buf[count + 1] << 8;
>> -	}
>> -}
>> -
>> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
>> -{
>> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> -	__le64 *info;
>> -	int i, count;
>> +	int i;
>>  
>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>> +	for (i = 0; i < nand->ecc.steps; i++) {
>>  		info = &meson_chip->info_buf[i];
>> -		oob_buf[count] = *info;
>> -		oob_buf[count + 1] = *info >> 8;
>> +		/* Always ignore user bytes programming. */
> 
> Why?

I think comment message is wrong a little bit. Here "user bytes" are
user bytes protected by ECC (e.g. location of these bytes differs from new
OOB layout introduced by this patch). During page write this hardware
always writes these bytes along with data. But, new OOB layout always ignores
these 4 bytes, so set them to 0xFF always.

> 
> Also, maybe I should mention the helpers:
> mtd_ooblayout_get_eccbytes and co
> They are very useful to deal with OOB bytes. Everything seems extremely
> hardcoded in this driver, while the user can tune read/write OOB
> operations.

Thanks for details, IIUC these helpers will work correctly with this driver.
What means "tune" here?

> 
>> +		*info |= 0xffff;
>>  	}
>>  }
>>  
>> @@ -698,18 +713,92 @@ static int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf,
>>  	return meson_nfc_write_page_sub(nand, page, 1);
>>  }
>>  
>> +static u32 meson_nfc_get_oob_bytes(struct nand_chip *nand)
>> +{
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>> +
>> +	return mtd->oobsize - nand->ecc.steps * NFC_OOB_PER_ECC(nand);
>> +}
>> +
>> +static int __meson_nfc_write_oob(struct nand_chip *nand, int page, u8 *oob_buf)
>> +{
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>> +	u32 page_size = mtd->writesize + mtd->oobsize;
>> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
>> +	int ret;
>> +
>> +	if (!oob_bytes)
>> +		return 0;
>> +
>> +	ret = nand_prog_page_begin_op(nand, page, 0, NULL, 0);
>> +	if (ret)
>> +		return ret;
>> +
>> +	memcpy(meson_chip->oob_buf, oob_buf + (mtd->oobsize - oob_bytes),
>> +	       oob_bytes);
>> +
>> +	ret = nand_change_write_column_op(nand, page_size - oob_bytes,
>> +					  meson_chip->oob_buf,
>> +					  oob_bytes, false);
>> +	if (ret)
>> +		return ret;
>> +
>> +	return nand_prog_page_end_op(nand);
>> +}
>> +
>> +static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
>> +				u8 *oob_buf)
>> +{
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>> +	u32 oob_bytes;
>> +	u32 page_size;
>> +	int ret;
>> +
>> +	oob_bytes = meson_nfc_get_oob_bytes(nand);
>> +
>> +	if (!oob_bytes)
>> +		return 0;
>> +
>> +	ret = nand_read_page_op(nand, page, 0, NULL, 0);
>> +	if (ret)
>> +		return ret;
>> +
>> +	page_size = mtd->writesize + mtd->oobsize;
>> +
>> +	ret = nand_change_read_column_op(nand, page_size - oob_bytes,
>> +					 meson_chip->oob_buf,
>> +					 oob_bytes, false);
>> +
>> +	if (!ret)
>> +		memcpy(oob_buf + (mtd->oobsize - oob_bytes),
>> +		       meson_chip->oob_buf,
>> +		       oob_bytes);
>> +
>> +	return ret;
>> +}
>> +
>>  static int meson_nfc_write_page_hwecc(struct nand_chip *nand,
>>  				      const u8 *buf, int oob_required, int page)
>>  {
>>  	struct mtd_info *mtd = nand_to_mtd(nand);
>>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>  	u8 *oob_buf = nand->oob_poi;
>> +	int ret;
>>  
>>  	memcpy(meson_chip->data_buf, buf, mtd->writesize);
>>  	memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE);
>>  	meson_nfc_set_user_byte(nand, oob_buf);
>>  
>> -	return meson_nfc_write_page_sub(nand, page, 0);
>> +	ret = meson_nfc_write_page_sub(nand, page, 0);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (oob_required)
>> +		ret = __meson_nfc_write_oob(nand, page, oob_buf);
> 
> You should provide all the data including OOB bytes in a single write
> call, otherwise you perform two writes on the same page, that's not
> what this helper is expected to do.

I see, so I need to check 'oob_required' here before programming page data?
If it is set -> place OOB data to DMA buffer and then call PAGEPROG once?

> 
>> +
>> +	return ret;
>>  }
>>  
>>  static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>> @@ -783,7 +872,7 @@ static int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf,
>>  	if (ret)
>>  		return ret;
>>  
>> -	meson_nfc_get_data_oob(nand, buf, oob_buf);
>> +	meson_nfc_get_data_oob(nand, buf, oob_required ? oob_buf : NULL);
>>  
>>  	return 0;
>>  }
>> @@ -803,12 +892,12 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>>  	if (ret)
>>  		return ret;
>>  
>> -	meson_nfc_get_user_byte(nand, oob_buf);
>>  	ret = meson_nfc_ecc_correct(nand, &bitflips, &correct_bitmap);
>>  	if (ret == ECC_CHECK_RETURN_FF) {
>>  		if (buf)
>>  			memset(buf, 0xff, mtd->writesize);
>>  		memset(oob_buf, 0xff, mtd->oobsize);
>> +		return bitflips;
> 
> That is something else => other fix => other patch?

Idea of this 'return' is that when read fails, we return from this function
without reading OOB below.

> 
>>  	} else if (ret < 0) {
>>  		if ((nand->options & NAND_NEED_SCRAMBLING) || !buf) {
>>  			mtd->ecc_stats.failed++;
>> @@ -820,12 +909,14 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>>  
>>  		for (i = 0; i < nand->ecc.steps ; i++) {
>>  			u8 *data = buf + i * ecc->size;
>> -			u8 *oob = nand->oob_poi + i * (ecc->bytes + 2);
>> +			u8 *oob = nand->oob_poi + i * NFC_OOB_PER_ECC(nand);
>>  
>>  			if (correct_bitmap & BIT_ULL(i))
>>  				continue;
>> +
>>  			ret = nand_check_erased_ecc_chunk(data,	ecc->size,
>> -							  oob, ecc->bytes + 2,
>> +							  oob,
>> +							  NFC_OOB_PER_ECC(nand),
>>  							  NULL, 0,
>>  							  ecc->strength);
>>  			if (ret < 0) {
>> @@ -839,17 +930,30 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>>  		memcpy(buf, meson_chip->data_buf, mtd->writesize);
>>  	}
>>  
>> +	if (oob_required)
>> +		__meson_nfc_read_oob(nand, page, oob_buf);
> 
> In the standalone "read_oob" hook, you have to send a READ0 command,
> but not when you are in the read_page hook. It is a big waste of time.

IIUC approach here must be exactly the same as in write? E.g. i need to
send single READ0 and then fill provided OOB buffer if needed?

> 
>> +
>>  	return bitflips;
>>  }
>>  
>>  static int meson_nfc_read_oob_raw(struct nand_chip *nand, int page)
>>  {
>> -	return meson_nfc_read_page_raw(nand, NULL, 1, page);
>> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
>>  }
>>  
>>  static int meson_nfc_read_oob(struct nand_chip *nand, int page)
>>  {
>> -	return meson_nfc_read_page_hwecc(nand, NULL, 1, page);
>> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
>> +}
>> +
>> +static int meson_nfc_write_oob_raw(struct nand_chip *nand, int page)
>> +{
>> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
>> +}
>> +
>> +static int meson_nfc_write_oob(struct nand_chip *nand, int page)
>> +{
>> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
> 
> Do we really need these indirections?

Right, I think I can use only one function for OOB write in both ECC and raw modes.

> 
>>  }
>>  
>>  static bool meson_nfc_is_buffer_dma_safe(const void *buffer)
>> @@ -982,7 +1086,7 @@ static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
>>  	if (section >= nand->ecc.steps)
>>  		return -ERANGE;
>>  
>> -	oobregion->offset =  2 + (section * (2 + nand->ecc.bytes));
>> +	oobregion->offset = NFC_USER_BYTES + section * NFC_OOB_PER_ECC(nand);
> 
> The () are still needed around the '*'
> 
>>  	oobregion->length = nand->ecc.bytes;
>>  
>>  	return 0;
>> @@ -992,12 +1096,16 @@ static int meson_ooblayout_free(struct mtd_info *mtd, int section,
>>  				struct mtd_oob_region *oobregion)
>>  {
>>  	struct nand_chip *nand = mtd_to_nand(mtd);
>> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
>>  
>>  	if (section >= nand->ecc.steps)
>>  		return -ERANGE;
>>  
>> -	oobregion->offset = section * (2 + nand->ecc.bytes);
>> -	oobregion->length = 2;
>> +	/* Split rest of OOB area (not covered by ECC engine) per each
>> +	 * ECC section. This will be OOB data available to user.
>> +	 */
>> +	oobregion->offset = (section + nand->ecc.steps) * NFC_OOB_PER_ECC(nand);
>> +	oobregion->length = oob_bytes / nand->ecc.steps;
>>  
>>  	return 0;
>>  }
>> @@ -1184,6 +1292,9 @@ static int meson_nand_bch_mode(struct nand_chip *nand)
>>  
>>  static void meson_nand_detach_chip(struct nand_chip *nand)
>>  {
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +
>> +	kfree(meson_chip->oob_buf);
>>  	meson_nfc_free_buffer(nand);
>>  }
>>  
>> @@ -1225,9 +1336,9 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>>  	nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
>>  	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
>>  	nand->ecc.write_page = meson_nfc_write_page_hwecc;
>> -	nand->ecc.write_oob_raw = nand_write_oob_std;
>> -	nand->ecc.write_oob = nand_write_oob_std;
>>  
>> +	nand->ecc.write_oob_raw = meson_nfc_write_oob_raw;
>> +	nand->ecc.write_oob = meson_nfc_write_oob;
> 
> Actually if you define the right OOB layouts, are these really needed
> ?? I would expect the right bytes to be picked up by the default
> implementation. I see nothing specific in your current implementation?

You mean to use 'nand_write_oob_std()'? If so, I think it won't work,
because it tries to write OOB data right after 'mtd->writesize', but
page layout for this controller is like this:
[       1024 bytes of data        ]
[ 14 bytes ECC + 2 bytes for user ] <- with new layout we don't touch these 2 bytes
[       1024 bytes of data        ]
[ 14 bytes ECC + 2 bytes for user ] <- with new layout we don't touch these 2 bytes
[      32 bytes of user bytes     ] <- we use there 32 bytes as free(user) bytes in OOB

So with 'mtd->writesize' of 2048 we won't get into last 32 bytes in the picture above.
I'm not goot in NAND/MTD area, but I think that why i need special OOB access callbacks.
Also in previous patches @Liang said, that default OOB read/write functions won't work
on this controller and it is wrong to use them in currently merged driver.

> 
>>  	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
>>  	nand->ecc.read_page = meson_nfc_read_page_hwecc;
>>  	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
>> @@ -1237,9 +1348,16 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>>  		dev_err(nfc->dev, "16bits bus width not supported");
>>  		return -EINVAL;
>>  	}
>> +
>> +	meson_chip->oob_buf = kmalloc(nand->ecc.bytes, GFP_KERNEL);
> 
> devm_kmalloc?

Ack

> 
>> +	if (!meson_chip->oob_buf)
>> +		return -ENOMEM;
>> +
>>  	ret = meson_chip_buffer_init(nand);
>> -	if (ret)
>> +	if (ret) {
>> +		kfree(meson_chip->oob_buf);
>>  		return -ENOMEM;
>> +	}
>>  
>>  	return ret;
>>  }
> 
> 
> Thanks,
> Miquèl

Thanks, Arseniy

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-23 17:17       ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-23 17:17 UTC (permalink / raw)
  To: Miquel Raynal, Liang Yang
  Cc: Richard Weinberger, Vignesh Raghavendra, Neil Armstrong,
	Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Yixun Lan,
	Jianxin Pan, oxffffaa, kernel, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

Hello Miquel! Thanks for detailed review!

On 22.05.2023 18:33, Miquel Raynal wrote:
> Hi Arseniy,
> 
> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:36 +0300:
> 
> The title should perhaps be "only expose unprotected user OOB bytes".
> 
>> This moves free bytes of OOB to non-protected ECC area. It is needed to
>> make JFFS2 works correctly with this NAND controller. Problem fires when
>> JFFS2 driver writes cleanmarker to some page and later it tries to write
>> to this page - write will be done successfully, but after that such page
>> becomes unreadable due to invalid ECC codes. This happens because second
>> write needs to update ECC codes, but it is impossible to do it correctly
>> without block erase. So idea of this patch is to
> 
> "... is to use the unprotected OOB area to store the cleanmarkers, so
> that they can be written by the filesystem without caring much about
> the page being empty or not: the ECC codes will not be written anyway."
> ?

Ok

> 
> JFFS2 is only useful on tiny NAND devices, where UBI does not fit,
> which are usually true SLC flashes, with the capability of writing
> a page with empty (0xFF) data, and still be able to write actual data
> to it later in a second write.

You mean to include text above to commit message also?

> 
>> split accesses to OOB
>> free bytes and data on each page - now both of them does not depends on
>> each other.
>>
>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>> ---
>>  drivers/mtd/nand/raw/meson_nand.c | 192 ++++++++++++++++++++++++------
>>  1 file changed, 155 insertions(+), 37 deletions(-)
>>
>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>> index 2f4d8c84186b..8526a6b87720 100644
>> --- a/drivers/mtd/nand/raw/meson_nand.c
>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>> @@ -108,6 +108,9 @@
>>  
>>  #define PER_INFO_BYTE		8
>>  
>> +#define NFC_USER_BYTES		2
>> +#define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
>> +
>>  struct meson_nfc_nand_chip {
>>  	struct list_head node;
>>  	struct nand_chip nand;
>> @@ -122,6 +125,7 @@ struct meson_nfc_nand_chip {
>>  	u8 *data_buf;
>>  	__le64 *info_buf;
>>  	u32 nsels;
>> +	u8 *oob_buf;
>>  	u8 sels[];
>>  };
>>  
>> @@ -338,7 +342,7 @@ static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
>>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>  	int len;
>>  
>> -	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
>> +	len = nand->ecc.size * (i + 1) + NFC_OOB_PER_ECC(nand) * i;
> 
> This...
> 
>>  
>>  	return meson_chip->data_buf + len;
>>  }
>> @@ -349,7 +353,7 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>>  	int len, temp;
>>  
>>  	temp = nand->ecc.size + nand->ecc.bytes;
>> -	len = (temp + 2) * i;
>> +	len = (temp + NFC_USER_BYTES) * i;
> 
> ... and this (same below)
> 
> are purely cosmetic, should be in a patch aside.
> 

Ack, i'll move cosmetic updates to separate patch.

>>  
>>  	return meson_chip->data_buf + len;
>>  }
>> @@ -357,29 +361,47 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>>  static void meson_nfc_get_data_oob(struct nand_chip *nand,
>>  				   u8 *buf, u8 *oobbuf)
>>  {
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>  	int i, oob_len = 0;
>>  	u8 *dsrc, *osrc;
>> +	u8 *oobtail;
>>  
>> -	oob_len = nand->ecc.bytes + 2;
>> +	oob_len = NFC_OOB_PER_ECC(nand);
>>  	for (i = 0; i < nand->ecc.steps; i++) {
>>  		if (buf) {
>>  			dsrc = meson_nfc_data_ptr(nand, i);
>>  			memcpy(buf, dsrc, nand->ecc.size);
>>  			buf += nand->ecc.size;
>>  		}
>> -		osrc = meson_nfc_oob_ptr(nand, i);
>> -		memcpy(oobbuf, osrc, oob_len);
>> -		oobbuf += oob_len;
>> +
>> +		if (oobbuf) {
>> +			osrc = meson_nfc_oob_ptr(nand, i);
>> +			memcpy(oobbuf, osrc, oob_len);
>> +			oobbuf += oob_len;
>> +		}
>>  	}
>> +
>> +	if (!oobbuf)
>> +		return;
> 
> The whole "if (oobbuf)" logic is nice to have, but should as well be in
> a dedicated patch.

Sorry, You mean that this logic implements read of ECC codes? And not
related to OOB layout update in this patch?

> 
>> +
>> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
>> +		  (nand->ecc.size + oob_len);
>> +
>> +	/* 'oobbuf' if already shifted to the start of unused area. */
> 
> 		    is?
> s/unused/user/? I'm not sure I get the comment.

Yes, not clear comment.

> 
>> +	memcpy(oobbuf, oobtail, mtd->oobsize - nand->ecc.steps * oob_len);
>>  }
>>  
>>  static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>  				   const u8 *buf, u8 *oobbuf)
>>  {
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>  	int i, oob_len = 0;
>>  	u8 *dsrc, *osrc;
>> +	u8 *oobtail;
>>  
>> -	oob_len = nand->ecc.bytes + 2;
>> +	oob_len = NFC_OOB_PER_ECC(nand);
>>  	for (i = 0; i < nand->ecc.steps; i++) {
>>  		if (buf) {
>>  			dsrc = meson_nfc_data_ptr(nand, i);
>> @@ -390,6 +412,12 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>  		memcpy(osrc, oobbuf, oob_len);
>>  		oobbuf += oob_len;
>>  	}
>> +
>> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
>> +		  (nand->ecc.size + oob_len);
>> +
>> +	/* 'oobbuf' if already shifted to the start of unused area. */
>> +	memcpy(oobtail, oobbuf, mtd->oobsize - nand->ecc.steps * oob_len);
>>  }
>>  
>>  static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
>> @@ -436,25 +464,12 @@ static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>  {
>>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>  	__le64 *info;
>> -	int i, count;
>> -
>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>> -		info = &meson_chip->info_buf[i];
>> -		*info |= oob_buf[count];
>> -		*info |= oob_buf[count + 1] << 8;
>> -	}
>> -}
>> -
>> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
>> -{
>> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> -	__le64 *info;
>> -	int i, count;
>> +	int i;
>>  
>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>> +	for (i = 0; i < nand->ecc.steps; i++) {
>>  		info = &meson_chip->info_buf[i];
>> -		oob_buf[count] = *info;
>> -		oob_buf[count + 1] = *info >> 8;
>> +		/* Always ignore user bytes programming. */
> 
> Why?

I think comment message is wrong a little bit. Here "user bytes" are
user bytes protected by ECC (e.g. location of these bytes differs from new
OOB layout introduced by this patch). During page write this hardware
always writes these bytes along with data. But, new OOB layout always ignores
these 4 bytes, so set them to 0xFF always.

> 
> Also, maybe I should mention the helpers:
> mtd_ooblayout_get_eccbytes and co
> They are very useful to deal with OOB bytes. Everything seems extremely
> hardcoded in this driver, while the user can tune read/write OOB
> operations.

Thanks for details, IIUC these helpers will work correctly with this driver.
What means "tune" here?

> 
>> +		*info |= 0xffff;
>>  	}
>>  }
>>  
>> @@ -698,18 +713,92 @@ static int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf,
>>  	return meson_nfc_write_page_sub(nand, page, 1);
>>  }
>>  
>> +static u32 meson_nfc_get_oob_bytes(struct nand_chip *nand)
>> +{
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>> +
>> +	return mtd->oobsize - nand->ecc.steps * NFC_OOB_PER_ECC(nand);
>> +}
>> +
>> +static int __meson_nfc_write_oob(struct nand_chip *nand, int page, u8 *oob_buf)
>> +{
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>> +	u32 page_size = mtd->writesize + mtd->oobsize;
>> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
>> +	int ret;
>> +
>> +	if (!oob_bytes)
>> +		return 0;
>> +
>> +	ret = nand_prog_page_begin_op(nand, page, 0, NULL, 0);
>> +	if (ret)
>> +		return ret;
>> +
>> +	memcpy(meson_chip->oob_buf, oob_buf + (mtd->oobsize - oob_bytes),
>> +	       oob_bytes);
>> +
>> +	ret = nand_change_write_column_op(nand, page_size - oob_bytes,
>> +					  meson_chip->oob_buf,
>> +					  oob_bytes, false);
>> +	if (ret)
>> +		return ret;
>> +
>> +	return nand_prog_page_end_op(nand);
>> +}
>> +
>> +static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
>> +				u8 *oob_buf)
>> +{
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>> +	u32 oob_bytes;
>> +	u32 page_size;
>> +	int ret;
>> +
>> +	oob_bytes = meson_nfc_get_oob_bytes(nand);
>> +
>> +	if (!oob_bytes)
>> +		return 0;
>> +
>> +	ret = nand_read_page_op(nand, page, 0, NULL, 0);
>> +	if (ret)
>> +		return ret;
>> +
>> +	page_size = mtd->writesize + mtd->oobsize;
>> +
>> +	ret = nand_change_read_column_op(nand, page_size - oob_bytes,
>> +					 meson_chip->oob_buf,
>> +					 oob_bytes, false);
>> +
>> +	if (!ret)
>> +		memcpy(oob_buf + (mtd->oobsize - oob_bytes),
>> +		       meson_chip->oob_buf,
>> +		       oob_bytes);
>> +
>> +	return ret;
>> +}
>> +
>>  static int meson_nfc_write_page_hwecc(struct nand_chip *nand,
>>  				      const u8 *buf, int oob_required, int page)
>>  {
>>  	struct mtd_info *mtd = nand_to_mtd(nand);
>>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>  	u8 *oob_buf = nand->oob_poi;
>> +	int ret;
>>  
>>  	memcpy(meson_chip->data_buf, buf, mtd->writesize);
>>  	memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE);
>>  	meson_nfc_set_user_byte(nand, oob_buf);
>>  
>> -	return meson_nfc_write_page_sub(nand, page, 0);
>> +	ret = meson_nfc_write_page_sub(nand, page, 0);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (oob_required)
>> +		ret = __meson_nfc_write_oob(nand, page, oob_buf);
> 
> You should provide all the data including OOB bytes in a single write
> call, otherwise you perform two writes on the same page, that's not
> what this helper is expected to do.

I see, so I need to check 'oob_required' here before programming page data?
If it is set -> place OOB data to DMA buffer and then call PAGEPROG once?

> 
>> +
>> +	return ret;
>>  }
>>  
>>  static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>> @@ -783,7 +872,7 @@ static int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf,
>>  	if (ret)
>>  		return ret;
>>  
>> -	meson_nfc_get_data_oob(nand, buf, oob_buf);
>> +	meson_nfc_get_data_oob(nand, buf, oob_required ? oob_buf : NULL);
>>  
>>  	return 0;
>>  }
>> @@ -803,12 +892,12 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>>  	if (ret)
>>  		return ret;
>>  
>> -	meson_nfc_get_user_byte(nand, oob_buf);
>>  	ret = meson_nfc_ecc_correct(nand, &bitflips, &correct_bitmap);
>>  	if (ret == ECC_CHECK_RETURN_FF) {
>>  		if (buf)
>>  			memset(buf, 0xff, mtd->writesize);
>>  		memset(oob_buf, 0xff, mtd->oobsize);
>> +		return bitflips;
> 
> That is something else => other fix => other patch?

Idea of this 'return' is that when read fails, we return from this function
without reading OOB below.

> 
>>  	} else if (ret < 0) {
>>  		if ((nand->options & NAND_NEED_SCRAMBLING) || !buf) {
>>  			mtd->ecc_stats.failed++;
>> @@ -820,12 +909,14 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>>  
>>  		for (i = 0; i < nand->ecc.steps ; i++) {
>>  			u8 *data = buf + i * ecc->size;
>> -			u8 *oob = nand->oob_poi + i * (ecc->bytes + 2);
>> +			u8 *oob = nand->oob_poi + i * NFC_OOB_PER_ECC(nand);
>>  
>>  			if (correct_bitmap & BIT_ULL(i))
>>  				continue;
>> +
>>  			ret = nand_check_erased_ecc_chunk(data,	ecc->size,
>> -							  oob, ecc->bytes + 2,
>> +							  oob,
>> +							  NFC_OOB_PER_ECC(nand),
>>  							  NULL, 0,
>>  							  ecc->strength);
>>  			if (ret < 0) {
>> @@ -839,17 +930,30 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>>  		memcpy(buf, meson_chip->data_buf, mtd->writesize);
>>  	}
>>  
>> +	if (oob_required)
>> +		__meson_nfc_read_oob(nand, page, oob_buf);
> 
> In the standalone "read_oob" hook, you have to send a READ0 command,
> but not when you are in the read_page hook. It is a big waste of time.

IIUC approach here must be exactly the same as in write? E.g. i need to
send single READ0 and then fill provided OOB buffer if needed?

> 
>> +
>>  	return bitflips;
>>  }
>>  
>>  static int meson_nfc_read_oob_raw(struct nand_chip *nand, int page)
>>  {
>> -	return meson_nfc_read_page_raw(nand, NULL, 1, page);
>> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
>>  }
>>  
>>  static int meson_nfc_read_oob(struct nand_chip *nand, int page)
>>  {
>> -	return meson_nfc_read_page_hwecc(nand, NULL, 1, page);
>> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
>> +}
>> +
>> +static int meson_nfc_write_oob_raw(struct nand_chip *nand, int page)
>> +{
>> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
>> +}
>> +
>> +static int meson_nfc_write_oob(struct nand_chip *nand, int page)
>> +{
>> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
> 
> Do we really need these indirections?

Right, I think I can use only one function for OOB write in both ECC and raw modes.

> 
>>  }
>>  
>>  static bool meson_nfc_is_buffer_dma_safe(const void *buffer)
>> @@ -982,7 +1086,7 @@ static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
>>  	if (section >= nand->ecc.steps)
>>  		return -ERANGE;
>>  
>> -	oobregion->offset =  2 + (section * (2 + nand->ecc.bytes));
>> +	oobregion->offset = NFC_USER_BYTES + section * NFC_OOB_PER_ECC(nand);
> 
> The () are still needed around the '*'
> 
>>  	oobregion->length = nand->ecc.bytes;
>>  
>>  	return 0;
>> @@ -992,12 +1096,16 @@ static int meson_ooblayout_free(struct mtd_info *mtd, int section,
>>  				struct mtd_oob_region *oobregion)
>>  {
>>  	struct nand_chip *nand = mtd_to_nand(mtd);
>> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
>>  
>>  	if (section >= nand->ecc.steps)
>>  		return -ERANGE;
>>  
>> -	oobregion->offset = section * (2 + nand->ecc.bytes);
>> -	oobregion->length = 2;
>> +	/* Split rest of OOB area (not covered by ECC engine) per each
>> +	 * ECC section. This will be OOB data available to user.
>> +	 */
>> +	oobregion->offset = (section + nand->ecc.steps) * NFC_OOB_PER_ECC(nand);
>> +	oobregion->length = oob_bytes / nand->ecc.steps;
>>  
>>  	return 0;
>>  }
>> @@ -1184,6 +1292,9 @@ static int meson_nand_bch_mode(struct nand_chip *nand)
>>  
>>  static void meson_nand_detach_chip(struct nand_chip *nand)
>>  {
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +
>> +	kfree(meson_chip->oob_buf);
>>  	meson_nfc_free_buffer(nand);
>>  }
>>  
>> @@ -1225,9 +1336,9 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>>  	nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
>>  	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
>>  	nand->ecc.write_page = meson_nfc_write_page_hwecc;
>> -	nand->ecc.write_oob_raw = nand_write_oob_std;
>> -	nand->ecc.write_oob = nand_write_oob_std;
>>  
>> +	nand->ecc.write_oob_raw = meson_nfc_write_oob_raw;
>> +	nand->ecc.write_oob = meson_nfc_write_oob;
> 
> Actually if you define the right OOB layouts, are these really needed
> ?? I would expect the right bytes to be picked up by the default
> implementation. I see nothing specific in your current implementation?

You mean to use 'nand_write_oob_std()'? If so, I think it won't work,
because it tries to write OOB data right after 'mtd->writesize', but
page layout for this controller is like this:
[       1024 bytes of data        ]
[ 14 bytes ECC + 2 bytes for user ] <- with new layout we don't touch these 2 bytes
[       1024 bytes of data        ]
[ 14 bytes ECC + 2 bytes for user ] <- with new layout we don't touch these 2 bytes
[      32 bytes of user bytes     ] <- we use there 32 bytes as free(user) bytes in OOB

So with 'mtd->writesize' of 2048 we won't get into last 32 bytes in the picture above.
I'm not goot in NAND/MTD area, but I think that why i need special OOB access callbacks.
Also in previous patches @Liang said, that default OOB read/write functions won't work
on this controller and it is wrong to use them in currently merged driver.

> 
>>  	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
>>  	nand->ecc.read_page = meson_nfc_read_page_hwecc;
>>  	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
>> @@ -1237,9 +1348,16 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>>  		dev_err(nfc->dev, "16bits bus width not supported");
>>  		return -EINVAL;
>>  	}
>> +
>> +	meson_chip->oob_buf = kmalloc(nand->ecc.bytes, GFP_KERNEL);
> 
> devm_kmalloc?

Ack

> 
>> +	if (!meson_chip->oob_buf)
>> +		return -ENOMEM;
>> +
>>  	ret = meson_chip_buffer_init(nand);
>> -	if (ret)
>> +	if (ret) {
>> +		kfree(meson_chip->oob_buf);
>>  		return -ENOMEM;
>> +	}
>>  
>>  	return ret;
>>  }
> 
> 
> Thanks,
> Miquèl

Thanks, Arseniy

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-23 17:17       ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-23 17:17 UTC (permalink / raw)
  To: Miquel Raynal, Liang Yang
  Cc: Richard Weinberger, Vignesh Raghavendra, Neil Armstrong,
	Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Yixun Lan,
	Jianxin Pan, oxffffaa, kernel, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

Hello Miquel! Thanks for detailed review!

On 22.05.2023 18:33, Miquel Raynal wrote:
> Hi Arseniy,
> 
> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:36 +0300:
> 
> The title should perhaps be "only expose unprotected user OOB bytes".
> 
>> This moves free bytes of OOB to non-protected ECC area. It is needed to
>> make JFFS2 works correctly with this NAND controller. Problem fires when
>> JFFS2 driver writes cleanmarker to some page and later it tries to write
>> to this page - write will be done successfully, but after that such page
>> becomes unreadable due to invalid ECC codes. This happens because second
>> write needs to update ECC codes, but it is impossible to do it correctly
>> without block erase. So idea of this patch is to
> 
> "... is to use the unprotected OOB area to store the cleanmarkers, so
> that they can be written by the filesystem without caring much about
> the page being empty or not: the ECC codes will not be written anyway."
> ?

Ok

> 
> JFFS2 is only useful on tiny NAND devices, where UBI does not fit,
> which are usually true SLC flashes, with the capability of writing
> a page with empty (0xFF) data, and still be able to write actual data
> to it later in a second write.

You mean to include text above to commit message also?

> 
>> split accesses to OOB
>> free bytes and data on each page - now both of them does not depends on
>> each other.
>>
>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>> ---
>>  drivers/mtd/nand/raw/meson_nand.c | 192 ++++++++++++++++++++++++------
>>  1 file changed, 155 insertions(+), 37 deletions(-)
>>
>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>> index 2f4d8c84186b..8526a6b87720 100644
>> --- a/drivers/mtd/nand/raw/meson_nand.c
>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>> @@ -108,6 +108,9 @@
>>  
>>  #define PER_INFO_BYTE		8
>>  
>> +#define NFC_USER_BYTES		2
>> +#define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
>> +
>>  struct meson_nfc_nand_chip {
>>  	struct list_head node;
>>  	struct nand_chip nand;
>> @@ -122,6 +125,7 @@ struct meson_nfc_nand_chip {
>>  	u8 *data_buf;
>>  	__le64 *info_buf;
>>  	u32 nsels;
>> +	u8 *oob_buf;
>>  	u8 sels[];
>>  };
>>  
>> @@ -338,7 +342,7 @@ static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
>>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>  	int len;
>>  
>> -	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
>> +	len = nand->ecc.size * (i + 1) + NFC_OOB_PER_ECC(nand) * i;
> 
> This...
> 
>>  
>>  	return meson_chip->data_buf + len;
>>  }
>> @@ -349,7 +353,7 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>>  	int len, temp;
>>  
>>  	temp = nand->ecc.size + nand->ecc.bytes;
>> -	len = (temp + 2) * i;
>> +	len = (temp + NFC_USER_BYTES) * i;
> 
> ... and this (same below)
> 
> are purely cosmetic, should be in a patch aside.
> 

Ack, i'll move cosmetic updates to separate patch.

>>  
>>  	return meson_chip->data_buf + len;
>>  }
>> @@ -357,29 +361,47 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>>  static void meson_nfc_get_data_oob(struct nand_chip *nand,
>>  				   u8 *buf, u8 *oobbuf)
>>  {
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>  	int i, oob_len = 0;
>>  	u8 *dsrc, *osrc;
>> +	u8 *oobtail;
>>  
>> -	oob_len = nand->ecc.bytes + 2;
>> +	oob_len = NFC_OOB_PER_ECC(nand);
>>  	for (i = 0; i < nand->ecc.steps; i++) {
>>  		if (buf) {
>>  			dsrc = meson_nfc_data_ptr(nand, i);
>>  			memcpy(buf, dsrc, nand->ecc.size);
>>  			buf += nand->ecc.size;
>>  		}
>> -		osrc = meson_nfc_oob_ptr(nand, i);
>> -		memcpy(oobbuf, osrc, oob_len);
>> -		oobbuf += oob_len;
>> +
>> +		if (oobbuf) {
>> +			osrc = meson_nfc_oob_ptr(nand, i);
>> +			memcpy(oobbuf, osrc, oob_len);
>> +			oobbuf += oob_len;
>> +		}
>>  	}
>> +
>> +	if (!oobbuf)
>> +		return;
> 
> The whole "if (oobbuf)" logic is nice to have, but should as well be in
> a dedicated patch.

Sorry, You mean that this logic implements read of ECC codes? And not
related to OOB layout update in this patch?

> 
>> +
>> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
>> +		  (nand->ecc.size + oob_len);
>> +
>> +	/* 'oobbuf' if already shifted to the start of unused area. */
> 
> 		    is?
> s/unused/user/? I'm not sure I get the comment.

Yes, not clear comment.

> 
>> +	memcpy(oobbuf, oobtail, mtd->oobsize - nand->ecc.steps * oob_len);
>>  }
>>  
>>  static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>  				   const u8 *buf, u8 *oobbuf)
>>  {
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>  	int i, oob_len = 0;
>>  	u8 *dsrc, *osrc;
>> +	u8 *oobtail;
>>  
>> -	oob_len = nand->ecc.bytes + 2;
>> +	oob_len = NFC_OOB_PER_ECC(nand);
>>  	for (i = 0; i < nand->ecc.steps; i++) {
>>  		if (buf) {
>>  			dsrc = meson_nfc_data_ptr(nand, i);
>> @@ -390,6 +412,12 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>  		memcpy(osrc, oobbuf, oob_len);
>>  		oobbuf += oob_len;
>>  	}
>> +
>> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
>> +		  (nand->ecc.size + oob_len);
>> +
>> +	/* 'oobbuf' if already shifted to the start of unused area. */
>> +	memcpy(oobtail, oobbuf, mtd->oobsize - nand->ecc.steps * oob_len);
>>  }
>>  
>>  static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
>> @@ -436,25 +464,12 @@ static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>  {
>>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>  	__le64 *info;
>> -	int i, count;
>> -
>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>> -		info = &meson_chip->info_buf[i];
>> -		*info |= oob_buf[count];
>> -		*info |= oob_buf[count + 1] << 8;
>> -	}
>> -}
>> -
>> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
>> -{
>> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> -	__le64 *info;
>> -	int i, count;
>> +	int i;
>>  
>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>> +	for (i = 0; i < nand->ecc.steps; i++) {
>>  		info = &meson_chip->info_buf[i];
>> -		oob_buf[count] = *info;
>> -		oob_buf[count + 1] = *info >> 8;
>> +		/* Always ignore user bytes programming. */
> 
> Why?

I think comment message is wrong a little bit. Here "user bytes" are
user bytes protected by ECC (e.g. location of these bytes differs from new
OOB layout introduced by this patch). During page write this hardware
always writes these bytes along with data. But, new OOB layout always ignores
these 4 bytes, so set them to 0xFF always.

> 
> Also, maybe I should mention the helpers:
> mtd_ooblayout_get_eccbytes and co
> They are very useful to deal with OOB bytes. Everything seems extremely
> hardcoded in this driver, while the user can tune read/write OOB
> operations.

Thanks for details, IIUC these helpers will work correctly with this driver.
What means "tune" here?

> 
>> +		*info |= 0xffff;
>>  	}
>>  }
>>  
>> @@ -698,18 +713,92 @@ static int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf,
>>  	return meson_nfc_write_page_sub(nand, page, 1);
>>  }
>>  
>> +static u32 meson_nfc_get_oob_bytes(struct nand_chip *nand)
>> +{
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>> +
>> +	return mtd->oobsize - nand->ecc.steps * NFC_OOB_PER_ECC(nand);
>> +}
>> +
>> +static int __meson_nfc_write_oob(struct nand_chip *nand, int page, u8 *oob_buf)
>> +{
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>> +	u32 page_size = mtd->writesize + mtd->oobsize;
>> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
>> +	int ret;
>> +
>> +	if (!oob_bytes)
>> +		return 0;
>> +
>> +	ret = nand_prog_page_begin_op(nand, page, 0, NULL, 0);
>> +	if (ret)
>> +		return ret;
>> +
>> +	memcpy(meson_chip->oob_buf, oob_buf + (mtd->oobsize - oob_bytes),
>> +	       oob_bytes);
>> +
>> +	ret = nand_change_write_column_op(nand, page_size - oob_bytes,
>> +					  meson_chip->oob_buf,
>> +					  oob_bytes, false);
>> +	if (ret)
>> +		return ret;
>> +
>> +	return nand_prog_page_end_op(nand);
>> +}
>> +
>> +static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
>> +				u8 *oob_buf)
>> +{
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>> +	u32 oob_bytes;
>> +	u32 page_size;
>> +	int ret;
>> +
>> +	oob_bytes = meson_nfc_get_oob_bytes(nand);
>> +
>> +	if (!oob_bytes)
>> +		return 0;
>> +
>> +	ret = nand_read_page_op(nand, page, 0, NULL, 0);
>> +	if (ret)
>> +		return ret;
>> +
>> +	page_size = mtd->writesize + mtd->oobsize;
>> +
>> +	ret = nand_change_read_column_op(nand, page_size - oob_bytes,
>> +					 meson_chip->oob_buf,
>> +					 oob_bytes, false);
>> +
>> +	if (!ret)
>> +		memcpy(oob_buf + (mtd->oobsize - oob_bytes),
>> +		       meson_chip->oob_buf,
>> +		       oob_bytes);
>> +
>> +	return ret;
>> +}
>> +
>>  static int meson_nfc_write_page_hwecc(struct nand_chip *nand,
>>  				      const u8 *buf, int oob_required, int page)
>>  {
>>  	struct mtd_info *mtd = nand_to_mtd(nand);
>>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>  	u8 *oob_buf = nand->oob_poi;
>> +	int ret;
>>  
>>  	memcpy(meson_chip->data_buf, buf, mtd->writesize);
>>  	memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE);
>>  	meson_nfc_set_user_byte(nand, oob_buf);
>>  
>> -	return meson_nfc_write_page_sub(nand, page, 0);
>> +	ret = meson_nfc_write_page_sub(nand, page, 0);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (oob_required)
>> +		ret = __meson_nfc_write_oob(nand, page, oob_buf);
> 
> You should provide all the data including OOB bytes in a single write
> call, otherwise you perform two writes on the same page, that's not
> what this helper is expected to do.

I see, so I need to check 'oob_required' here before programming page data?
If it is set -> place OOB data to DMA buffer and then call PAGEPROG once?

> 
>> +
>> +	return ret;
>>  }
>>  
>>  static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>> @@ -783,7 +872,7 @@ static int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf,
>>  	if (ret)
>>  		return ret;
>>  
>> -	meson_nfc_get_data_oob(nand, buf, oob_buf);
>> +	meson_nfc_get_data_oob(nand, buf, oob_required ? oob_buf : NULL);
>>  
>>  	return 0;
>>  }
>> @@ -803,12 +892,12 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>>  	if (ret)
>>  		return ret;
>>  
>> -	meson_nfc_get_user_byte(nand, oob_buf);
>>  	ret = meson_nfc_ecc_correct(nand, &bitflips, &correct_bitmap);
>>  	if (ret == ECC_CHECK_RETURN_FF) {
>>  		if (buf)
>>  			memset(buf, 0xff, mtd->writesize);
>>  		memset(oob_buf, 0xff, mtd->oobsize);
>> +		return bitflips;
> 
> That is something else => other fix => other patch?

Idea of this 'return' is that when read fails, we return from this function
without reading OOB below.

> 
>>  	} else if (ret < 0) {
>>  		if ((nand->options & NAND_NEED_SCRAMBLING) || !buf) {
>>  			mtd->ecc_stats.failed++;
>> @@ -820,12 +909,14 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>>  
>>  		for (i = 0; i < nand->ecc.steps ; i++) {
>>  			u8 *data = buf + i * ecc->size;
>> -			u8 *oob = nand->oob_poi + i * (ecc->bytes + 2);
>> +			u8 *oob = nand->oob_poi + i * NFC_OOB_PER_ECC(nand);
>>  
>>  			if (correct_bitmap & BIT_ULL(i))
>>  				continue;
>> +
>>  			ret = nand_check_erased_ecc_chunk(data,	ecc->size,
>> -							  oob, ecc->bytes + 2,
>> +							  oob,
>> +							  NFC_OOB_PER_ECC(nand),
>>  							  NULL, 0,
>>  							  ecc->strength);
>>  			if (ret < 0) {
>> @@ -839,17 +930,30 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>>  		memcpy(buf, meson_chip->data_buf, mtd->writesize);
>>  	}
>>  
>> +	if (oob_required)
>> +		__meson_nfc_read_oob(nand, page, oob_buf);
> 
> In the standalone "read_oob" hook, you have to send a READ0 command,
> but not when you are in the read_page hook. It is a big waste of time.

IIUC approach here must be exactly the same as in write? E.g. i need to
send single READ0 and then fill provided OOB buffer if needed?

> 
>> +
>>  	return bitflips;
>>  }
>>  
>>  static int meson_nfc_read_oob_raw(struct nand_chip *nand, int page)
>>  {
>> -	return meson_nfc_read_page_raw(nand, NULL, 1, page);
>> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
>>  }
>>  
>>  static int meson_nfc_read_oob(struct nand_chip *nand, int page)
>>  {
>> -	return meson_nfc_read_page_hwecc(nand, NULL, 1, page);
>> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
>> +}
>> +
>> +static int meson_nfc_write_oob_raw(struct nand_chip *nand, int page)
>> +{
>> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
>> +}
>> +
>> +static int meson_nfc_write_oob(struct nand_chip *nand, int page)
>> +{
>> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
> 
> Do we really need these indirections?

Right, I think I can use only one function for OOB write in both ECC and raw modes.

> 
>>  }
>>  
>>  static bool meson_nfc_is_buffer_dma_safe(const void *buffer)
>> @@ -982,7 +1086,7 @@ static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
>>  	if (section >= nand->ecc.steps)
>>  		return -ERANGE;
>>  
>> -	oobregion->offset =  2 + (section * (2 + nand->ecc.bytes));
>> +	oobregion->offset = NFC_USER_BYTES + section * NFC_OOB_PER_ECC(nand);
> 
> The () are still needed around the '*'
> 
>>  	oobregion->length = nand->ecc.bytes;
>>  
>>  	return 0;
>> @@ -992,12 +1096,16 @@ static int meson_ooblayout_free(struct mtd_info *mtd, int section,
>>  				struct mtd_oob_region *oobregion)
>>  {
>>  	struct nand_chip *nand = mtd_to_nand(mtd);
>> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
>>  
>>  	if (section >= nand->ecc.steps)
>>  		return -ERANGE;
>>  
>> -	oobregion->offset = section * (2 + nand->ecc.bytes);
>> -	oobregion->length = 2;
>> +	/* Split rest of OOB area (not covered by ECC engine) per each
>> +	 * ECC section. This will be OOB data available to user.
>> +	 */
>> +	oobregion->offset = (section + nand->ecc.steps) * NFC_OOB_PER_ECC(nand);
>> +	oobregion->length = oob_bytes / nand->ecc.steps;
>>  
>>  	return 0;
>>  }
>> @@ -1184,6 +1292,9 @@ static int meson_nand_bch_mode(struct nand_chip *nand)
>>  
>>  static void meson_nand_detach_chip(struct nand_chip *nand)
>>  {
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +
>> +	kfree(meson_chip->oob_buf);
>>  	meson_nfc_free_buffer(nand);
>>  }
>>  
>> @@ -1225,9 +1336,9 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>>  	nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
>>  	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
>>  	nand->ecc.write_page = meson_nfc_write_page_hwecc;
>> -	nand->ecc.write_oob_raw = nand_write_oob_std;
>> -	nand->ecc.write_oob = nand_write_oob_std;
>>  
>> +	nand->ecc.write_oob_raw = meson_nfc_write_oob_raw;
>> +	nand->ecc.write_oob = meson_nfc_write_oob;
> 
> Actually if you define the right OOB layouts, are these really needed
> ?? I would expect the right bytes to be picked up by the default
> implementation. I see nothing specific in your current implementation?

You mean to use 'nand_write_oob_std()'? If so, I think it won't work,
because it tries to write OOB data right after 'mtd->writesize', but
page layout for this controller is like this:
[       1024 bytes of data        ]
[ 14 bytes ECC + 2 bytes for user ] <- with new layout we don't touch these 2 bytes
[       1024 bytes of data        ]
[ 14 bytes ECC + 2 bytes for user ] <- with new layout we don't touch these 2 bytes
[      32 bytes of user bytes     ] <- we use there 32 bytes as free(user) bytes in OOB

So with 'mtd->writesize' of 2048 we won't get into last 32 bytes in the picture above.
I'm not goot in NAND/MTD area, but I think that why i need special OOB access callbacks.
Also in previous patches @Liang said, that default OOB read/write functions won't work
on this controller and it is wrong to use them in currently merged driver.

> 
>>  	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
>>  	nand->ecc.read_page = meson_nfc_read_page_hwecc;
>>  	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
>> @@ -1237,9 +1348,16 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>>  		dev_err(nfc->dev, "16bits bus width not supported");
>>  		return -EINVAL;
>>  	}
>> +
>> +	meson_chip->oob_buf = kmalloc(nand->ecc.bytes, GFP_KERNEL);
> 
> devm_kmalloc?

Ack

> 
>> +	if (!meson_chip->oob_buf)
>> +		return -ENOMEM;
>> +
>>  	ret = meson_chip_buffer_init(nand);
>> -	if (ret)
>> +	if (ret) {
>> +		kfree(meson_chip->oob_buf);
>>  		return -ENOMEM;
>> +	}
>>  
>>  	return ret;
>>  }
> 
> 
> Thanks,
> Miquèl

Thanks, Arseniy

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

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-23 17:17       ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-23 17:17 UTC (permalink / raw)
  To: Miquel Raynal, Liang Yang
  Cc: Richard Weinberger, Vignesh Raghavendra, Neil Armstrong,
	Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Yixun Lan,
	Jianxin Pan, oxffffaa, kernel, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel

Hello Miquel! Thanks for detailed review!

On 22.05.2023 18:33, Miquel Raynal wrote:
> Hi Arseniy,
> 
> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:36 +0300:
> 
> The title should perhaps be "only expose unprotected user OOB bytes".
> 
>> This moves free bytes of OOB to non-protected ECC area. It is needed to
>> make JFFS2 works correctly with this NAND controller. Problem fires when
>> JFFS2 driver writes cleanmarker to some page and later it tries to write
>> to this page - write will be done successfully, but after that such page
>> becomes unreadable due to invalid ECC codes. This happens because second
>> write needs to update ECC codes, but it is impossible to do it correctly
>> without block erase. So idea of this patch is to
> 
> "... is to use the unprotected OOB area to store the cleanmarkers, so
> that they can be written by the filesystem without caring much about
> the page being empty or not: the ECC codes will not be written anyway."
> ?

Ok

> 
> JFFS2 is only useful on tiny NAND devices, where UBI does not fit,
> which are usually true SLC flashes, with the capability of writing
> a page with empty (0xFF) data, and still be able to write actual data
> to it later in a second write.

You mean to include text above to commit message also?

> 
>> split accesses to OOB
>> free bytes and data on each page - now both of them does not depends on
>> each other.
>>
>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>> ---
>>  drivers/mtd/nand/raw/meson_nand.c | 192 ++++++++++++++++++++++++------
>>  1 file changed, 155 insertions(+), 37 deletions(-)
>>
>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>> index 2f4d8c84186b..8526a6b87720 100644
>> --- a/drivers/mtd/nand/raw/meson_nand.c
>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>> @@ -108,6 +108,9 @@
>>  
>>  #define PER_INFO_BYTE		8
>>  
>> +#define NFC_USER_BYTES		2
>> +#define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
>> +
>>  struct meson_nfc_nand_chip {
>>  	struct list_head node;
>>  	struct nand_chip nand;
>> @@ -122,6 +125,7 @@ struct meson_nfc_nand_chip {
>>  	u8 *data_buf;
>>  	__le64 *info_buf;
>>  	u32 nsels;
>> +	u8 *oob_buf;
>>  	u8 sels[];
>>  };
>>  
>> @@ -338,7 +342,7 @@ static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
>>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>  	int len;
>>  
>> -	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
>> +	len = nand->ecc.size * (i + 1) + NFC_OOB_PER_ECC(nand) * i;
> 
> This...
> 
>>  
>>  	return meson_chip->data_buf + len;
>>  }
>> @@ -349,7 +353,7 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>>  	int len, temp;
>>  
>>  	temp = nand->ecc.size + nand->ecc.bytes;
>> -	len = (temp + 2) * i;
>> +	len = (temp + NFC_USER_BYTES) * i;
> 
> ... and this (same below)
> 
> are purely cosmetic, should be in a patch aside.
> 

Ack, i'll move cosmetic updates to separate patch.

>>  
>>  	return meson_chip->data_buf + len;
>>  }
>> @@ -357,29 +361,47 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>>  static void meson_nfc_get_data_oob(struct nand_chip *nand,
>>  				   u8 *buf, u8 *oobbuf)
>>  {
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>  	int i, oob_len = 0;
>>  	u8 *dsrc, *osrc;
>> +	u8 *oobtail;
>>  
>> -	oob_len = nand->ecc.bytes + 2;
>> +	oob_len = NFC_OOB_PER_ECC(nand);
>>  	for (i = 0; i < nand->ecc.steps; i++) {
>>  		if (buf) {
>>  			dsrc = meson_nfc_data_ptr(nand, i);
>>  			memcpy(buf, dsrc, nand->ecc.size);
>>  			buf += nand->ecc.size;
>>  		}
>> -		osrc = meson_nfc_oob_ptr(nand, i);
>> -		memcpy(oobbuf, osrc, oob_len);
>> -		oobbuf += oob_len;
>> +
>> +		if (oobbuf) {
>> +			osrc = meson_nfc_oob_ptr(nand, i);
>> +			memcpy(oobbuf, osrc, oob_len);
>> +			oobbuf += oob_len;
>> +		}
>>  	}
>> +
>> +	if (!oobbuf)
>> +		return;
> 
> The whole "if (oobbuf)" logic is nice to have, but should as well be in
> a dedicated patch.

Sorry, You mean that this logic implements read of ECC codes? And not
related to OOB layout update in this patch?

> 
>> +
>> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
>> +		  (nand->ecc.size + oob_len);
>> +
>> +	/* 'oobbuf' if already shifted to the start of unused area. */
> 
> 		    is?
> s/unused/user/? I'm not sure I get the comment.

Yes, not clear comment.

> 
>> +	memcpy(oobbuf, oobtail, mtd->oobsize - nand->ecc.steps * oob_len);
>>  }
>>  
>>  static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>  				   const u8 *buf, u8 *oobbuf)
>>  {
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>  	int i, oob_len = 0;
>>  	u8 *dsrc, *osrc;
>> +	u8 *oobtail;
>>  
>> -	oob_len = nand->ecc.bytes + 2;
>> +	oob_len = NFC_OOB_PER_ECC(nand);
>>  	for (i = 0; i < nand->ecc.steps; i++) {
>>  		if (buf) {
>>  			dsrc = meson_nfc_data_ptr(nand, i);
>> @@ -390,6 +412,12 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>  		memcpy(osrc, oobbuf, oob_len);
>>  		oobbuf += oob_len;
>>  	}
>> +
>> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
>> +		  (nand->ecc.size + oob_len);
>> +
>> +	/* 'oobbuf' if already shifted to the start of unused area. */
>> +	memcpy(oobtail, oobbuf, mtd->oobsize - nand->ecc.steps * oob_len);
>>  }
>>  
>>  static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
>> @@ -436,25 +464,12 @@ static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>  {
>>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>  	__le64 *info;
>> -	int i, count;
>> -
>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>> -		info = &meson_chip->info_buf[i];
>> -		*info |= oob_buf[count];
>> -		*info |= oob_buf[count + 1] << 8;
>> -	}
>> -}
>> -
>> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
>> -{
>> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> -	__le64 *info;
>> -	int i, count;
>> +	int i;
>>  
>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>> +	for (i = 0; i < nand->ecc.steps; i++) {
>>  		info = &meson_chip->info_buf[i];
>> -		oob_buf[count] = *info;
>> -		oob_buf[count + 1] = *info >> 8;
>> +		/* Always ignore user bytes programming. */
> 
> Why?

I think comment message is wrong a little bit. Here "user bytes" are
user bytes protected by ECC (e.g. location of these bytes differs from new
OOB layout introduced by this patch). During page write this hardware
always writes these bytes along with data. But, new OOB layout always ignores
these 4 bytes, so set them to 0xFF always.

> 
> Also, maybe I should mention the helpers:
> mtd_ooblayout_get_eccbytes and co
> They are very useful to deal with OOB bytes. Everything seems extremely
> hardcoded in this driver, while the user can tune read/write OOB
> operations.

Thanks for details, IIUC these helpers will work correctly with this driver.
What means "tune" here?

> 
>> +		*info |= 0xffff;
>>  	}
>>  }
>>  
>> @@ -698,18 +713,92 @@ static int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf,
>>  	return meson_nfc_write_page_sub(nand, page, 1);
>>  }
>>  
>> +static u32 meson_nfc_get_oob_bytes(struct nand_chip *nand)
>> +{
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>> +
>> +	return mtd->oobsize - nand->ecc.steps * NFC_OOB_PER_ECC(nand);
>> +}
>> +
>> +static int __meson_nfc_write_oob(struct nand_chip *nand, int page, u8 *oob_buf)
>> +{
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>> +	u32 page_size = mtd->writesize + mtd->oobsize;
>> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
>> +	int ret;
>> +
>> +	if (!oob_bytes)
>> +		return 0;
>> +
>> +	ret = nand_prog_page_begin_op(nand, page, 0, NULL, 0);
>> +	if (ret)
>> +		return ret;
>> +
>> +	memcpy(meson_chip->oob_buf, oob_buf + (mtd->oobsize - oob_bytes),
>> +	       oob_bytes);
>> +
>> +	ret = nand_change_write_column_op(nand, page_size - oob_bytes,
>> +					  meson_chip->oob_buf,
>> +					  oob_bytes, false);
>> +	if (ret)
>> +		return ret;
>> +
>> +	return nand_prog_page_end_op(nand);
>> +}
>> +
>> +static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
>> +				u8 *oob_buf)
>> +{
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>> +	u32 oob_bytes;
>> +	u32 page_size;
>> +	int ret;
>> +
>> +	oob_bytes = meson_nfc_get_oob_bytes(nand);
>> +
>> +	if (!oob_bytes)
>> +		return 0;
>> +
>> +	ret = nand_read_page_op(nand, page, 0, NULL, 0);
>> +	if (ret)
>> +		return ret;
>> +
>> +	page_size = mtd->writesize + mtd->oobsize;
>> +
>> +	ret = nand_change_read_column_op(nand, page_size - oob_bytes,
>> +					 meson_chip->oob_buf,
>> +					 oob_bytes, false);
>> +
>> +	if (!ret)
>> +		memcpy(oob_buf + (mtd->oobsize - oob_bytes),
>> +		       meson_chip->oob_buf,
>> +		       oob_bytes);
>> +
>> +	return ret;
>> +}
>> +
>>  static int meson_nfc_write_page_hwecc(struct nand_chip *nand,
>>  				      const u8 *buf, int oob_required, int page)
>>  {
>>  	struct mtd_info *mtd = nand_to_mtd(nand);
>>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>  	u8 *oob_buf = nand->oob_poi;
>> +	int ret;
>>  
>>  	memcpy(meson_chip->data_buf, buf, mtd->writesize);
>>  	memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE);
>>  	meson_nfc_set_user_byte(nand, oob_buf);
>>  
>> -	return meson_nfc_write_page_sub(nand, page, 0);
>> +	ret = meson_nfc_write_page_sub(nand, page, 0);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (oob_required)
>> +		ret = __meson_nfc_write_oob(nand, page, oob_buf);
> 
> You should provide all the data including OOB bytes in a single write
> call, otherwise you perform two writes on the same page, that's not
> what this helper is expected to do.

I see, so I need to check 'oob_required' here before programming page data?
If it is set -> place OOB data to DMA buffer and then call PAGEPROG once?

> 
>> +
>> +	return ret;
>>  }
>>  
>>  static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>> @@ -783,7 +872,7 @@ static int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf,
>>  	if (ret)
>>  		return ret;
>>  
>> -	meson_nfc_get_data_oob(nand, buf, oob_buf);
>> +	meson_nfc_get_data_oob(nand, buf, oob_required ? oob_buf : NULL);
>>  
>>  	return 0;
>>  }
>> @@ -803,12 +892,12 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>>  	if (ret)
>>  		return ret;
>>  
>> -	meson_nfc_get_user_byte(nand, oob_buf);
>>  	ret = meson_nfc_ecc_correct(nand, &bitflips, &correct_bitmap);
>>  	if (ret == ECC_CHECK_RETURN_FF) {
>>  		if (buf)
>>  			memset(buf, 0xff, mtd->writesize);
>>  		memset(oob_buf, 0xff, mtd->oobsize);
>> +		return bitflips;
> 
> That is something else => other fix => other patch?

Idea of this 'return' is that when read fails, we return from this function
without reading OOB below.

> 
>>  	} else if (ret < 0) {
>>  		if ((nand->options & NAND_NEED_SCRAMBLING) || !buf) {
>>  			mtd->ecc_stats.failed++;
>> @@ -820,12 +909,14 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>>  
>>  		for (i = 0; i < nand->ecc.steps ; i++) {
>>  			u8 *data = buf + i * ecc->size;
>> -			u8 *oob = nand->oob_poi + i * (ecc->bytes + 2);
>> +			u8 *oob = nand->oob_poi + i * NFC_OOB_PER_ECC(nand);
>>  
>>  			if (correct_bitmap & BIT_ULL(i))
>>  				continue;
>> +
>>  			ret = nand_check_erased_ecc_chunk(data,	ecc->size,
>> -							  oob, ecc->bytes + 2,
>> +							  oob,
>> +							  NFC_OOB_PER_ECC(nand),
>>  							  NULL, 0,
>>  							  ecc->strength);
>>  			if (ret < 0) {
>> @@ -839,17 +930,30 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>>  		memcpy(buf, meson_chip->data_buf, mtd->writesize);
>>  	}
>>  
>> +	if (oob_required)
>> +		__meson_nfc_read_oob(nand, page, oob_buf);
> 
> In the standalone "read_oob" hook, you have to send a READ0 command,
> but not when you are in the read_page hook. It is a big waste of time.

IIUC approach here must be exactly the same as in write? E.g. i need to
send single READ0 and then fill provided OOB buffer if needed?

> 
>> +
>>  	return bitflips;
>>  }
>>  
>>  static int meson_nfc_read_oob_raw(struct nand_chip *nand, int page)
>>  {
>> -	return meson_nfc_read_page_raw(nand, NULL, 1, page);
>> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
>>  }
>>  
>>  static int meson_nfc_read_oob(struct nand_chip *nand, int page)
>>  {
>> -	return meson_nfc_read_page_hwecc(nand, NULL, 1, page);
>> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
>> +}
>> +
>> +static int meson_nfc_write_oob_raw(struct nand_chip *nand, int page)
>> +{
>> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
>> +}
>> +
>> +static int meson_nfc_write_oob(struct nand_chip *nand, int page)
>> +{
>> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
> 
> Do we really need these indirections?

Right, I think I can use only one function for OOB write in both ECC and raw modes.

> 
>>  }
>>  
>>  static bool meson_nfc_is_buffer_dma_safe(const void *buffer)
>> @@ -982,7 +1086,7 @@ static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
>>  	if (section >= nand->ecc.steps)
>>  		return -ERANGE;
>>  
>> -	oobregion->offset =  2 + (section * (2 + nand->ecc.bytes));
>> +	oobregion->offset = NFC_USER_BYTES + section * NFC_OOB_PER_ECC(nand);
> 
> The () are still needed around the '*'
> 
>>  	oobregion->length = nand->ecc.bytes;
>>  
>>  	return 0;
>> @@ -992,12 +1096,16 @@ static int meson_ooblayout_free(struct mtd_info *mtd, int section,
>>  				struct mtd_oob_region *oobregion)
>>  {
>>  	struct nand_chip *nand = mtd_to_nand(mtd);
>> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
>>  
>>  	if (section >= nand->ecc.steps)
>>  		return -ERANGE;
>>  
>> -	oobregion->offset = section * (2 + nand->ecc.bytes);
>> -	oobregion->length = 2;
>> +	/* Split rest of OOB area (not covered by ECC engine) per each
>> +	 * ECC section. This will be OOB data available to user.
>> +	 */
>> +	oobregion->offset = (section + nand->ecc.steps) * NFC_OOB_PER_ECC(nand);
>> +	oobregion->length = oob_bytes / nand->ecc.steps;
>>  
>>  	return 0;
>>  }
>> @@ -1184,6 +1292,9 @@ static int meson_nand_bch_mode(struct nand_chip *nand)
>>  
>>  static void meson_nand_detach_chip(struct nand_chip *nand)
>>  {
>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>> +
>> +	kfree(meson_chip->oob_buf);
>>  	meson_nfc_free_buffer(nand);
>>  }
>>  
>> @@ -1225,9 +1336,9 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>>  	nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
>>  	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
>>  	nand->ecc.write_page = meson_nfc_write_page_hwecc;
>> -	nand->ecc.write_oob_raw = nand_write_oob_std;
>> -	nand->ecc.write_oob = nand_write_oob_std;
>>  
>> +	nand->ecc.write_oob_raw = meson_nfc_write_oob_raw;
>> +	nand->ecc.write_oob = meson_nfc_write_oob;
> 
> Actually if you define the right OOB layouts, are these really needed
> ?? I would expect the right bytes to be picked up by the default
> implementation. I see nothing specific in your current implementation?

You mean to use 'nand_write_oob_std()'? If so, I think it won't work,
because it tries to write OOB data right after 'mtd->writesize', but
page layout for this controller is like this:
[       1024 bytes of data        ]
[ 14 bytes ECC + 2 bytes for user ] <- with new layout we don't touch these 2 bytes
[       1024 bytes of data        ]
[ 14 bytes ECC + 2 bytes for user ] <- with new layout we don't touch these 2 bytes
[      32 bytes of user bytes     ] <- we use there 32 bytes as free(user) bytes in OOB

So with 'mtd->writesize' of 2048 we won't get into last 32 bytes in the picture above.
I'm not goot in NAND/MTD area, but I think that why i need special OOB access callbacks.
Also in previous patches @Liang said, that default OOB read/write functions won't work
on this controller and it is wrong to use them in currently merged driver.

> 
>>  	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
>>  	nand->ecc.read_page = meson_nfc_read_page_hwecc;
>>  	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
>> @@ -1237,9 +1348,16 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>>  		dev_err(nfc->dev, "16bits bus width not supported");
>>  		return -EINVAL;
>>  	}
>> +
>> +	meson_chip->oob_buf = kmalloc(nand->ecc.bytes, GFP_KERNEL);
> 
> devm_kmalloc?

Ack

> 
>> +	if (!meson_chip->oob_buf)
>> +		return -ENOMEM;
>> +
>>  	ret = meson_chip_buffer_init(nand);
>> -	if (ret)
>> +	if (ret) {
>> +		kfree(meson_chip->oob_buf);
>>  		return -ENOMEM;
>> +	}
>>  
>>  	return ret;
>>  }
> 
> 
> Thanks,
> Miquèl

Thanks, Arseniy

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v4 3/5] mtd: rawnand: meson: always read whole OOB bytes
  2023-05-22 15:38     ` Miquel Raynal
  (?)
  (?)
@ 2023-05-23 17:27       ` Arseniy Krasnov
  -1 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-23 17:27 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Jianxin Pan, Yixun Lan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel



On 22.05.2023 18:38, Miquel Raynal wrote:
> Hi Arseniy,
> 
> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:37 +0300:
> 
>> This changes size of read access to OOB area by reading all bytes of
>> OOB (free bytes + ECC engine bytes).
> 
> This is normally up to the user (user in your case == jffs2). The
> controller driver should expose a number of user accessible bytes and
> then when users want the OOB area, they should access it entirely. On
> top of that read, they can extract (or "write only") the user bytes.

Sorry, I didn't get it. If driver exposes N bytes of user accessible bytes,
I must always return whole OOB yes? E.g. N + rest of OOB

> 
>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>> ---
>>  drivers/mtd/nand/raw/meson_nand.c | 24 ++++++++++++++++++++++++
>>  1 file changed, 24 insertions(+)
>>
>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>> index 8526a6b87720..a31106c943d7 100644
>> --- a/drivers/mtd/nand/raw/meson_nand.c
>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>> @@ -755,6 +755,30 @@ static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
>>  	u32 oob_bytes;
>>  	u32 page_size;
>>  	int ret;
>> +	int i;
>> +
>> +	/* Read ECC codes and user bytes. */
>> +	for (i = 0; i < nand->ecc.steps; i++) {
>> +		u32 ecc_offs = nand->ecc.size * (i + 1) +
>> +			       NFC_OOB_PER_ECC(nand) * i;
>> +
>> +		ret = nand_read_page_op(nand, page, 0, NULL, 0);
>> +		if (ret)
>> +			return ret;
>> +
>> +		/* Use temporary buffer, because 'nand_change_read_column_op()'
>> +		 * seems work with some alignment, so we can't read data to
>> +		 * 'oob_buf' directly.
> 
> DMA?

Yes I guess, this address passed to exec_op code and used as DMA.

> 
>> +		 */
>> +		ret = nand_change_read_column_op(nand, ecc_offs, meson_chip->oob_buf,
>> +						 NFC_OOB_PER_ECC(nand), false);
>> +		if (ret)
>> +			return ret;
>> +
>> +		memcpy(oob_buf + i * NFC_OOB_PER_ECC(nand),
>> +		       meson_chip->oob_buf,
>> +		       NFC_OOB_PER_ECC(nand));
>> +	}
>>  
>>  	oob_bytes = meson_nfc_get_oob_bytes(nand);
>>  
> 
> 
> Thanks,
> Miquèl

Thanks, Arseniy

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

* Re: [PATCH v4 3/5] mtd: rawnand: meson: always read whole OOB bytes
@ 2023-05-23 17:27       ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-23 17:27 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Jianxin Pan, Yixun Lan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel



On 22.05.2023 18:38, Miquel Raynal wrote:
> Hi Arseniy,
> 
> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:37 +0300:
> 
>> This changes size of read access to OOB area by reading all bytes of
>> OOB (free bytes + ECC engine bytes).
> 
> This is normally up to the user (user in your case == jffs2). The
> controller driver should expose a number of user accessible bytes and
> then when users want the OOB area, they should access it entirely. On
> top of that read, they can extract (or "write only") the user bytes.

Sorry, I didn't get it. If driver exposes N bytes of user accessible bytes,
I must always return whole OOB yes? E.g. N + rest of OOB

> 
>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>> ---
>>  drivers/mtd/nand/raw/meson_nand.c | 24 ++++++++++++++++++++++++
>>  1 file changed, 24 insertions(+)
>>
>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>> index 8526a6b87720..a31106c943d7 100644
>> --- a/drivers/mtd/nand/raw/meson_nand.c
>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>> @@ -755,6 +755,30 @@ static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
>>  	u32 oob_bytes;
>>  	u32 page_size;
>>  	int ret;
>> +	int i;
>> +
>> +	/* Read ECC codes and user bytes. */
>> +	for (i = 0; i < nand->ecc.steps; i++) {
>> +		u32 ecc_offs = nand->ecc.size * (i + 1) +
>> +			       NFC_OOB_PER_ECC(nand) * i;
>> +
>> +		ret = nand_read_page_op(nand, page, 0, NULL, 0);
>> +		if (ret)
>> +			return ret;
>> +
>> +		/* Use temporary buffer, because 'nand_change_read_column_op()'
>> +		 * seems work with some alignment, so we can't read data to
>> +		 * 'oob_buf' directly.
> 
> DMA?

Yes I guess, this address passed to exec_op code and used as DMA.

> 
>> +		 */
>> +		ret = nand_change_read_column_op(nand, ecc_offs, meson_chip->oob_buf,
>> +						 NFC_OOB_PER_ECC(nand), false);
>> +		if (ret)
>> +			return ret;
>> +
>> +		memcpy(oob_buf + i * NFC_OOB_PER_ECC(nand),
>> +		       meson_chip->oob_buf,
>> +		       NFC_OOB_PER_ECC(nand));
>> +	}
>>  
>>  	oob_bytes = meson_nfc_get_oob_bytes(nand);
>>  
> 
> 
> Thanks,
> Miquèl

Thanks, Arseniy

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

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

* Re: [PATCH v4 3/5] mtd: rawnand: meson: always read whole OOB bytes
@ 2023-05-23 17:27       ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-23 17:27 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Jianxin Pan, Yixun Lan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel



On 22.05.2023 18:38, Miquel Raynal wrote:
> Hi Arseniy,
> 
> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:37 +0300:
> 
>> This changes size of read access to OOB area by reading all bytes of
>> OOB (free bytes + ECC engine bytes).
> 
> This is normally up to the user (user in your case == jffs2). The
> controller driver should expose a number of user accessible bytes and
> then when users want the OOB area, they should access it entirely. On
> top of that read, they can extract (or "write only") the user bytes.

Sorry, I didn't get it. If driver exposes N bytes of user accessible bytes,
I must always return whole OOB yes? E.g. N + rest of OOB

> 
>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>> ---
>>  drivers/mtd/nand/raw/meson_nand.c | 24 ++++++++++++++++++++++++
>>  1 file changed, 24 insertions(+)
>>
>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>> index 8526a6b87720..a31106c943d7 100644
>> --- a/drivers/mtd/nand/raw/meson_nand.c
>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>> @@ -755,6 +755,30 @@ static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
>>  	u32 oob_bytes;
>>  	u32 page_size;
>>  	int ret;
>> +	int i;
>> +
>> +	/* Read ECC codes and user bytes. */
>> +	for (i = 0; i < nand->ecc.steps; i++) {
>> +		u32 ecc_offs = nand->ecc.size * (i + 1) +
>> +			       NFC_OOB_PER_ECC(nand) * i;
>> +
>> +		ret = nand_read_page_op(nand, page, 0, NULL, 0);
>> +		if (ret)
>> +			return ret;
>> +
>> +		/* Use temporary buffer, because 'nand_change_read_column_op()'
>> +		 * seems work with some alignment, so we can't read data to
>> +		 * 'oob_buf' directly.
> 
> DMA?

Yes I guess, this address passed to exec_op code and used as DMA.

> 
>> +		 */
>> +		ret = nand_change_read_column_op(nand, ecc_offs, meson_chip->oob_buf,
>> +						 NFC_OOB_PER_ECC(nand), false);
>> +		if (ret)
>> +			return ret;
>> +
>> +		memcpy(oob_buf + i * NFC_OOB_PER_ECC(nand),
>> +		       meson_chip->oob_buf,
>> +		       NFC_OOB_PER_ECC(nand));
>> +	}
>>  
>>  	oob_bytes = meson_nfc_get_oob_bytes(nand);
>>  
> 
> 
> Thanks,
> Miquèl

Thanks, Arseniy

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v4 3/5] mtd: rawnand: meson: always read whole OOB bytes
@ 2023-05-23 17:27       ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-23 17:27 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Jianxin Pan, Yixun Lan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel



On 22.05.2023 18:38, Miquel Raynal wrote:
> Hi Arseniy,
> 
> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:37 +0300:
> 
>> This changes size of read access to OOB area by reading all bytes of
>> OOB (free bytes + ECC engine bytes).
> 
> This is normally up to the user (user in your case == jffs2). The
> controller driver should expose a number of user accessible bytes and
> then when users want the OOB area, they should access it entirely. On
> top of that read, they can extract (or "write only") the user bytes.

Sorry, I didn't get it. If driver exposes N bytes of user accessible bytes,
I must always return whole OOB yes? E.g. N + rest of OOB

> 
>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>> ---
>>  drivers/mtd/nand/raw/meson_nand.c | 24 ++++++++++++++++++++++++
>>  1 file changed, 24 insertions(+)
>>
>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>> index 8526a6b87720..a31106c943d7 100644
>> --- a/drivers/mtd/nand/raw/meson_nand.c
>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>> @@ -755,6 +755,30 @@ static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
>>  	u32 oob_bytes;
>>  	u32 page_size;
>>  	int ret;
>> +	int i;
>> +
>> +	/* Read ECC codes and user bytes. */
>> +	for (i = 0; i < nand->ecc.steps; i++) {
>> +		u32 ecc_offs = nand->ecc.size * (i + 1) +
>> +			       NFC_OOB_PER_ECC(nand) * i;
>> +
>> +		ret = nand_read_page_op(nand, page, 0, NULL, 0);
>> +		if (ret)
>> +			return ret;
>> +
>> +		/* Use temporary buffer, because 'nand_change_read_column_op()'
>> +		 * seems work with some alignment, so we can't read data to
>> +		 * 'oob_buf' directly.
> 
> DMA?

Yes I guess, this address passed to exec_op code and used as DMA.

> 
>> +		 */
>> +		ret = nand_change_read_column_op(nand, ecc_offs, meson_chip->oob_buf,
>> +						 NFC_OOB_PER_ECC(nand), false);
>> +		if (ret)
>> +			return ret;
>> +
>> +		memcpy(oob_buf + i * NFC_OOB_PER_ECC(nand),
>> +		       meson_chip->oob_buf,
>> +		       NFC_OOB_PER_ECC(nand));
>> +	}
>>  
>>  	oob_bytes = meson_nfc_get_oob_bytes(nand);
>>  
> 
> 
> Thanks,
> Miquèl

Thanks, Arseniy

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
  2023-05-23  9:12       ` Arseniy Krasnov
  (?)
  (?)
@ 2023-05-24  9:05         ` Arseniy Krasnov
  -1 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-24  9:05 UTC (permalink / raw)
  To: Miquel Raynal, Liang Yang
  Cc: Richard Weinberger, Vignesh Raghavendra, Neil Armstrong,
	Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Yixun Lan,
	Jianxin Pan, oxffffaa, kernel, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel



On 23.05.2023 12:12, Arseniy Krasnov wrote:
> Hello Miquel, Liang
> 
> On 22.05.2023 18:05, Miquel Raynal wrote:
>> Hi Arseniy,
>>
>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
>>
>>> This fixes read/write functionality by:
>>> 1) Changing NFC_CMD_RB_INT bit value.
>>
>> I guess this is a separate fix
>>
> 
> Ok, I'll move it to separate patch
> 
>>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.
>>
>> Is this really needed? Looks like you're delaying the next op only. Is
>> using a delay enough? If yes, then it's probably the wrong approach.

Hi Miquel, small update, I found some details from @Liang's message in v1 talks from the last month:

*
After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send
NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device. but previous meson_nfc_queue_rb()
only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
i think there is something wrong with the Ready/Busy pin(please check the hardware whether this
Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the
nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the
data bus and generate IRQ if ready.
*

I guess, that sequence of commands from this patch is described in datasheet (unfortunately I don't have it and relied on the old driver).
Yesterday I tried to remove sending of NAND_CMD_STATUS from this patch, but it broke current driver - i had ECC errors, so it looks like
"shot in the dark" situation, to understand this logic.

Thanks, Arseniy

>>
>>> 3) Adding extra idle commands during r/w request.
>>
>> Question about this below, might also be a patch on its own?
>>
>>> 4) Adding extra NAND_CMD_READ0 on each read request.
>>>
>>> Without this patch driver works unstable, for example there are a lot
>>> of ECC errors.
>>
>> I believe all the fixes should be Cc'ed to stable, please add in your
>> commits:
>>
>> Cc: stable@...
>>
> 
> Ack, after everything will be clear with this patch :)
> 
>>>
>>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>>> Suggested-by: Liang Yang <liang.yang@amlogic.com>
>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>> ---
>>>  drivers/mtd/nand/raw/meson_nand.c | 30 +++++++++++++++++++++---------
>>>  1 file changed, 21 insertions(+), 9 deletions(-)
>>>
>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>> index 074e14225c06..2f4d8c84186b 100644
>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>> @@ -37,7 +37,7 @@
>>>  #define NFC_CMD_SCRAMBLER_ENABLE	BIT(19)
>>>  #define NFC_CMD_SCRAMBLER_DISABLE	0
>>>  #define NFC_CMD_SHORTMODE_DISABLE	0
>>> -#define NFC_CMD_RB_INT		BIT(14)
>>> +#define NFC_CMD_RB_INT ((0xb << 10) | BIT(18) | BIT(16))
>>>  
>>>  #define NFC_CMD_GET_SIZE(x)	(((x) >> 22) & GENMASK(4, 0))
>>>  
>>> @@ -392,7 +392,7 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>>  	}
>>>  }
>>>  
>>> -static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>> +static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
>>>  {
>>>  	u32 cmd, cfg;
>>>  	int ret = 0;
>>> @@ -407,17 +407,29 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>>  
>>>  	reinit_completion(&nfc->completion);
>>>  
>>> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>> +	meson_nfc_cmd_idle(nfc, 5);
>>
>> Why 5 and 2 below? They look like magic values. Is this totally
>> experimental?
> 
> @Liang, may be change it to defines ?
> 
>>
>>> +
>>>  	/* use the max erase time as the maximum clock for waiting R/B */
>>> -	cmd = NFC_CMD_RB | NFC_CMD_RB_INT
>>> -		| nfc->param.chip_select | nfc->timing.tbers_max;
>>
>> This is not documented in the commit log, is it?
>>
>>> +	cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>> +	meson_nfc_cmd_idle(nfc, 2);
>>>  
>>>  	ret = wait_for_completion_timeout(&nfc->completion,
>>>  					  msecs_to_jiffies(timeout_ms));
>>>  	if (ret == 0)
>>> -		ret = -1;
>>> +		return -1;
>>
>> Please use real error codes, such as ETIMEDOUT.
> 
> Ack
> 
>>
>>>  
>>> -	return ret;
>>> +	if (!cmd_read0)
>>> +		return 0;
>>> +
>>> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_READ0;
>>
>> This looks really wrong, I don't get why you would need to send an
>> expensive READ0 command.
> 
> This logic was suggested by @Liang Yang here to fix this driver (suggested as patch):
> https://lore.kernel.org/linux-mtd/8537e736-44a8-d17b-7923-25d5bd406dcc@sberdevices.ru/T/#m0df09d2ab2cac98431fb268a4ce3c00dc5c7a69e
> @Liang, could You please give us more details?
> 
> Thanks, Arseniy
> 
>>
>>> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>> +	meson_nfc_drain_cmd(nfc);
>>> +	meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
>>> +
>>> +	return 0;
>>>  }
>>>  
>>>  static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>> @@ -623,7 +635,7 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>>  	if (in) {
>>>  		nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART;
>>>  		writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
>>> -		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max));
>>> +		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max), 1);
>>>  	} else {
>>>  		meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
>>>  	}
>>> @@ -669,7 +681,7 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>  
>>>  	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>> -	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>>> +	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max), 0);
>>>  
>>>  	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>>  
>>> @@ -952,7 +964,7 @@ static int meson_nfc_exec_op(struct nand_chip *nand,
>>>  			break;
>>>  
>>>  		case NAND_OP_WAITRDY_INSTR:
>>> -			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms);
>>> +			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms, 1);
>>>  			if (instr->delay_ns)
>>>  				meson_nfc_cmd_idle(nfc, delay_idle);
>>>  			break;
>>
>>
>> Thanks,
>> Miquèl

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-24  9:05         ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-24  9:05 UTC (permalink / raw)
  To: Miquel Raynal, Liang Yang
  Cc: Richard Weinberger, Vignesh Raghavendra, Neil Armstrong,
	Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Yixun Lan,
	Jianxin Pan, oxffffaa, kernel, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel



On 23.05.2023 12:12, Arseniy Krasnov wrote:
> Hello Miquel, Liang
> 
> On 22.05.2023 18:05, Miquel Raynal wrote:
>> Hi Arseniy,
>>
>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
>>
>>> This fixes read/write functionality by:
>>> 1) Changing NFC_CMD_RB_INT bit value.
>>
>> I guess this is a separate fix
>>
> 
> Ok, I'll move it to separate patch
> 
>>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.
>>
>> Is this really needed? Looks like you're delaying the next op only. Is
>> using a delay enough? If yes, then it's probably the wrong approach.

Hi Miquel, small update, I found some details from @Liang's message in v1 talks from the last month:

*
After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send
NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device. but previous meson_nfc_queue_rb()
only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
i think there is something wrong with the Ready/Busy pin(please check the hardware whether this
Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the
nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the
data bus and generate IRQ if ready.
*

I guess, that sequence of commands from this patch is described in datasheet (unfortunately I don't have it and relied on the old driver).
Yesterday I tried to remove sending of NAND_CMD_STATUS from this patch, but it broke current driver - i had ECC errors, so it looks like
"shot in the dark" situation, to understand this logic.

Thanks, Arseniy

>>
>>> 3) Adding extra idle commands during r/w request.
>>
>> Question about this below, might also be a patch on its own?
>>
>>> 4) Adding extra NAND_CMD_READ0 on each read request.
>>>
>>> Without this patch driver works unstable, for example there are a lot
>>> of ECC errors.
>>
>> I believe all the fixes should be Cc'ed to stable, please add in your
>> commits:
>>
>> Cc: stable@...
>>
> 
> Ack, after everything will be clear with this patch :)
> 
>>>
>>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>>> Suggested-by: Liang Yang <liang.yang@amlogic.com>
>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>> ---
>>>  drivers/mtd/nand/raw/meson_nand.c | 30 +++++++++++++++++++++---------
>>>  1 file changed, 21 insertions(+), 9 deletions(-)
>>>
>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>> index 074e14225c06..2f4d8c84186b 100644
>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>> @@ -37,7 +37,7 @@
>>>  #define NFC_CMD_SCRAMBLER_ENABLE	BIT(19)
>>>  #define NFC_CMD_SCRAMBLER_DISABLE	0
>>>  #define NFC_CMD_SHORTMODE_DISABLE	0
>>> -#define NFC_CMD_RB_INT		BIT(14)
>>> +#define NFC_CMD_RB_INT ((0xb << 10) | BIT(18) | BIT(16))
>>>  
>>>  #define NFC_CMD_GET_SIZE(x)	(((x) >> 22) & GENMASK(4, 0))
>>>  
>>> @@ -392,7 +392,7 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>>  	}
>>>  }
>>>  
>>> -static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>> +static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
>>>  {
>>>  	u32 cmd, cfg;
>>>  	int ret = 0;
>>> @@ -407,17 +407,29 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>>  
>>>  	reinit_completion(&nfc->completion);
>>>  
>>> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>> +	meson_nfc_cmd_idle(nfc, 5);
>>
>> Why 5 and 2 below? They look like magic values. Is this totally
>> experimental?
> 
> @Liang, may be change it to defines ?
> 
>>
>>> +
>>>  	/* use the max erase time as the maximum clock for waiting R/B */
>>> -	cmd = NFC_CMD_RB | NFC_CMD_RB_INT
>>> -		| nfc->param.chip_select | nfc->timing.tbers_max;
>>
>> This is not documented in the commit log, is it?
>>
>>> +	cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>> +	meson_nfc_cmd_idle(nfc, 2);
>>>  
>>>  	ret = wait_for_completion_timeout(&nfc->completion,
>>>  					  msecs_to_jiffies(timeout_ms));
>>>  	if (ret == 0)
>>> -		ret = -1;
>>> +		return -1;
>>
>> Please use real error codes, such as ETIMEDOUT.
> 
> Ack
> 
>>
>>>  
>>> -	return ret;
>>> +	if (!cmd_read0)
>>> +		return 0;
>>> +
>>> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_READ0;
>>
>> This looks really wrong, I don't get why you would need to send an
>> expensive READ0 command.
> 
> This logic was suggested by @Liang Yang here to fix this driver (suggested as patch):
> https://lore.kernel.org/linux-mtd/8537e736-44a8-d17b-7923-25d5bd406dcc@sberdevices.ru/T/#m0df09d2ab2cac98431fb268a4ce3c00dc5c7a69e
> @Liang, could You please give us more details?
> 
> Thanks, Arseniy
> 
>>
>>> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>> +	meson_nfc_drain_cmd(nfc);
>>> +	meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
>>> +
>>> +	return 0;
>>>  }
>>>  
>>>  static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>> @@ -623,7 +635,7 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>>  	if (in) {
>>>  		nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART;
>>>  		writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
>>> -		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max));
>>> +		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max), 1);
>>>  	} else {
>>>  		meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
>>>  	}
>>> @@ -669,7 +681,7 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>  
>>>  	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>> -	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>>> +	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max), 0);
>>>  
>>>  	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>>  
>>> @@ -952,7 +964,7 @@ static int meson_nfc_exec_op(struct nand_chip *nand,
>>>  			break;
>>>  
>>>  		case NAND_OP_WAITRDY_INSTR:
>>> -			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms);
>>> +			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms, 1);
>>>  			if (instr->delay_ns)
>>>  				meson_nfc_cmd_idle(nfc, delay_idle);
>>>  			break;
>>
>>
>> Thanks,
>> Miquèl

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

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-24  9:05         ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-24  9:05 UTC (permalink / raw)
  To: Miquel Raynal, Liang Yang
  Cc: Richard Weinberger, Vignesh Raghavendra, Neil Armstrong,
	Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Yixun Lan,
	Jianxin Pan, oxffffaa, kernel, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel



On 23.05.2023 12:12, Arseniy Krasnov wrote:
> Hello Miquel, Liang
> 
> On 22.05.2023 18:05, Miquel Raynal wrote:
>> Hi Arseniy,
>>
>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
>>
>>> This fixes read/write functionality by:
>>> 1) Changing NFC_CMD_RB_INT bit value.
>>
>> I guess this is a separate fix
>>
> 
> Ok, I'll move it to separate patch
> 
>>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.
>>
>> Is this really needed? Looks like you're delaying the next op only. Is
>> using a delay enough? If yes, then it's probably the wrong approach.

Hi Miquel, small update, I found some details from @Liang's message in v1 talks from the last month:

*
After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send
NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device. but previous meson_nfc_queue_rb()
only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
i think there is something wrong with the Ready/Busy pin(please check the hardware whether this
Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the
nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the
data bus and generate IRQ if ready.
*

I guess, that sequence of commands from this patch is described in datasheet (unfortunately I don't have it and relied on the old driver).
Yesterday I tried to remove sending of NAND_CMD_STATUS from this patch, but it broke current driver - i had ECC errors, so it looks like
"shot in the dark" situation, to understand this logic.

Thanks, Arseniy

>>
>>> 3) Adding extra idle commands during r/w request.
>>
>> Question about this below, might also be a patch on its own?
>>
>>> 4) Adding extra NAND_CMD_READ0 on each read request.
>>>
>>> Without this patch driver works unstable, for example there are a lot
>>> of ECC errors.
>>
>> I believe all the fixes should be Cc'ed to stable, please add in your
>> commits:
>>
>> Cc: stable@...
>>
> 
> Ack, after everything will be clear with this patch :)
> 
>>>
>>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>>> Suggested-by: Liang Yang <liang.yang@amlogic.com>
>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>> ---
>>>  drivers/mtd/nand/raw/meson_nand.c | 30 +++++++++++++++++++++---------
>>>  1 file changed, 21 insertions(+), 9 deletions(-)
>>>
>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>> index 074e14225c06..2f4d8c84186b 100644
>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>> @@ -37,7 +37,7 @@
>>>  #define NFC_CMD_SCRAMBLER_ENABLE	BIT(19)
>>>  #define NFC_CMD_SCRAMBLER_DISABLE	0
>>>  #define NFC_CMD_SHORTMODE_DISABLE	0
>>> -#define NFC_CMD_RB_INT		BIT(14)
>>> +#define NFC_CMD_RB_INT ((0xb << 10) | BIT(18) | BIT(16))
>>>  
>>>  #define NFC_CMD_GET_SIZE(x)	(((x) >> 22) & GENMASK(4, 0))
>>>  
>>> @@ -392,7 +392,7 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>>  	}
>>>  }
>>>  
>>> -static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>> +static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
>>>  {
>>>  	u32 cmd, cfg;
>>>  	int ret = 0;
>>> @@ -407,17 +407,29 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>>  
>>>  	reinit_completion(&nfc->completion);
>>>  
>>> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>> +	meson_nfc_cmd_idle(nfc, 5);
>>
>> Why 5 and 2 below? They look like magic values. Is this totally
>> experimental?
> 
> @Liang, may be change it to defines ?
> 
>>
>>> +
>>>  	/* use the max erase time as the maximum clock for waiting R/B */
>>> -	cmd = NFC_CMD_RB | NFC_CMD_RB_INT
>>> -		| nfc->param.chip_select | nfc->timing.tbers_max;
>>
>> This is not documented in the commit log, is it?
>>
>>> +	cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>> +	meson_nfc_cmd_idle(nfc, 2);
>>>  
>>>  	ret = wait_for_completion_timeout(&nfc->completion,
>>>  					  msecs_to_jiffies(timeout_ms));
>>>  	if (ret == 0)
>>> -		ret = -1;
>>> +		return -1;
>>
>> Please use real error codes, such as ETIMEDOUT.
> 
> Ack
> 
>>
>>>  
>>> -	return ret;
>>> +	if (!cmd_read0)
>>> +		return 0;
>>> +
>>> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_READ0;
>>
>> This looks really wrong, I don't get why you would need to send an
>> expensive READ0 command.
> 
> This logic was suggested by @Liang Yang here to fix this driver (suggested as patch):
> https://lore.kernel.org/linux-mtd/8537e736-44a8-d17b-7923-25d5bd406dcc@sberdevices.ru/T/#m0df09d2ab2cac98431fb268a4ce3c00dc5c7a69e
> @Liang, could You please give us more details?
> 
> Thanks, Arseniy
> 
>>
>>> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>> +	meson_nfc_drain_cmd(nfc);
>>> +	meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
>>> +
>>> +	return 0;
>>>  }
>>>  
>>>  static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>> @@ -623,7 +635,7 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>>  	if (in) {
>>>  		nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART;
>>>  		writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
>>> -		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max));
>>> +		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max), 1);
>>>  	} else {
>>>  		meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
>>>  	}
>>> @@ -669,7 +681,7 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>  
>>>  	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>> -	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>>> +	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max), 0);
>>>  
>>>  	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>>  
>>> @@ -952,7 +964,7 @@ static int meson_nfc_exec_op(struct nand_chip *nand,
>>>  			break;
>>>  
>>>  		case NAND_OP_WAITRDY_INSTR:
>>> -			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms);
>>> +			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms, 1);
>>>  			if (instr->delay_ns)
>>>  				meson_nfc_cmd_idle(nfc, delay_idle);
>>>  			break;
>>
>>
>> Thanks,
>> Miquèl

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-24  9:05         ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-24  9:05 UTC (permalink / raw)
  To: Miquel Raynal, Liang Yang
  Cc: Richard Weinberger, Vignesh Raghavendra, Neil Armstrong,
	Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Yixun Lan,
	Jianxin Pan, oxffffaa, kernel, linux-mtd, linux-arm-kernel,
	linux-amlogic, linux-kernel



On 23.05.2023 12:12, Arseniy Krasnov wrote:
> Hello Miquel, Liang
> 
> On 22.05.2023 18:05, Miquel Raynal wrote:
>> Hi Arseniy,
>>
>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
>>
>>> This fixes read/write functionality by:
>>> 1) Changing NFC_CMD_RB_INT bit value.
>>
>> I guess this is a separate fix
>>
> 
> Ok, I'll move it to separate patch
> 
>>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.
>>
>> Is this really needed? Looks like you're delaying the next op only. Is
>> using a delay enough? If yes, then it's probably the wrong approach.

Hi Miquel, small update, I found some details from @Liang's message in v1 talks from the last month:

*
After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send
NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device. but previous meson_nfc_queue_rb()
only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
i think there is something wrong with the Ready/Busy pin(please check the hardware whether this
Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the
nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the
data bus and generate IRQ if ready.
*

I guess, that sequence of commands from this patch is described in datasheet (unfortunately I don't have it and relied on the old driver).
Yesterday I tried to remove sending of NAND_CMD_STATUS from this patch, but it broke current driver - i had ECC errors, so it looks like
"shot in the dark" situation, to understand this logic.

Thanks, Arseniy

>>
>>> 3) Adding extra idle commands during r/w request.
>>
>> Question about this below, might also be a patch on its own?
>>
>>> 4) Adding extra NAND_CMD_READ0 on each read request.
>>>
>>> Without this patch driver works unstable, for example there are a lot
>>> of ECC errors.
>>
>> I believe all the fixes should be Cc'ed to stable, please add in your
>> commits:
>>
>> Cc: stable@...
>>
> 
> Ack, after everything will be clear with this patch :)
> 
>>>
>>> Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller")
>>> Suggested-by: Liang Yang <liang.yang@amlogic.com>
>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>> ---
>>>  drivers/mtd/nand/raw/meson_nand.c | 30 +++++++++++++++++++++---------
>>>  1 file changed, 21 insertions(+), 9 deletions(-)
>>>
>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>> index 074e14225c06..2f4d8c84186b 100644
>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>> @@ -37,7 +37,7 @@
>>>  #define NFC_CMD_SCRAMBLER_ENABLE	BIT(19)
>>>  #define NFC_CMD_SCRAMBLER_DISABLE	0
>>>  #define NFC_CMD_SHORTMODE_DISABLE	0
>>> -#define NFC_CMD_RB_INT		BIT(14)
>>> +#define NFC_CMD_RB_INT ((0xb << 10) | BIT(18) | BIT(16))
>>>  
>>>  #define NFC_CMD_GET_SIZE(x)	(((x) >> 22) & GENMASK(4, 0))
>>>  
>>> @@ -392,7 +392,7 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>>  	}
>>>  }
>>>  
>>> -static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>> +static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
>>>  {
>>>  	u32 cmd, cfg;
>>>  	int ret = 0;
>>> @@ -407,17 +407,29 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms)
>>>  
>>>  	reinit_completion(&nfc->completion);
>>>  
>>> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS;
>>> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>> +	meson_nfc_cmd_idle(nfc, 5);
>>
>> Why 5 and 2 below? They look like magic values. Is this totally
>> experimental?
> 
> @Liang, may be change it to defines ?
> 
>>
>>> +
>>>  	/* use the max erase time as the maximum clock for waiting R/B */
>>> -	cmd = NFC_CMD_RB | NFC_CMD_RB_INT
>>> -		| nfc->param.chip_select | nfc->timing.tbers_max;
>>
>> This is not documented in the commit log, is it?
>>
>>> +	cmd = NFC_CMD_RB | NFC_CMD_RB_INT | nfc->timing.tbers_max;
>>>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>> +	meson_nfc_cmd_idle(nfc, 2);
>>>  
>>>  	ret = wait_for_completion_timeout(&nfc->completion,
>>>  					  msecs_to_jiffies(timeout_ms));
>>>  	if (ret == 0)
>>> -		ret = -1;
>>> +		return -1;
>>
>> Please use real error codes, such as ETIMEDOUT.
> 
> Ack
> 
>>
>>>  
>>> -	return ret;
>>> +	if (!cmd_read0)
>>> +		return 0;
>>> +
>>> +	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_READ0;
>>
>> This looks really wrong, I don't get why you would need to send an
>> expensive READ0 command.
> 
> This logic was suggested by @Liang Yang here to fix this driver (suggested as patch):
> https://lore.kernel.org/linux-mtd/8537e736-44a8-d17b-7923-25d5bd406dcc@sberdevices.ru/T/#m0df09d2ab2cac98431fb268a4ce3c00dc5c7a69e
> @Liang, could You please give us more details?
> 
> Thanks, Arseniy
> 
>>
>>> +	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>> +	meson_nfc_drain_cmd(nfc);
>>> +	meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
>>> +
>>> +	return 0;
>>>  }
>>>  
>>>  static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>> @@ -623,7 +635,7 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
>>>  	if (in) {
>>>  		nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART;
>>>  		writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
>>> -		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max));
>>> +		meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max), 1);
>>>  	} else {
>>>  		meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
>>>  	}
>>> @@ -669,7 +681,7 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand,
>>>  
>>>  	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
>>>  	writel(cmd, nfc->reg_base + NFC_REG_CMD);
>>> -	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max));
>>> +	meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max), 0);
>>>  
>>>  	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
>>>  
>>> @@ -952,7 +964,7 @@ static int meson_nfc_exec_op(struct nand_chip *nand,
>>>  			break;
>>>  
>>>  		case NAND_OP_WAITRDY_INSTR:
>>> -			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms);
>>> +			meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms, 1);
>>>  			if (instr->delay_ns)
>>>  				meson_nfc_cmd_idle(nfc, delay_idle);
>>>  			break;
>>
>>
>> Thanks,
>> Miquèl

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
  2023-05-23 17:17       ` Arseniy Krasnov
  (?)
  (?)
@ 2023-05-26 17:03         ` Miquel Raynal
  -1 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-26 17:03 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

avkrasnov@sberdevices.ru wrote on Tue, 23 May 2023 20:17:14 +0300:

> Hello Miquel! Thanks for detailed review!
> 
> On 22.05.2023 18:33, Miquel Raynal wrote:
> > Hi Arseniy,
> > 
> > AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:36 +0300:
> > 
> > The title should perhaps be "only expose unprotected user OOB bytes".
> >   
> >> This moves free bytes of OOB to non-protected ECC area. It is needed to
> >> make JFFS2 works correctly with this NAND controller. Problem fires when
> >> JFFS2 driver writes cleanmarker to some page and later it tries to write
> >> to this page - write will be done successfully, but after that such page
> >> becomes unreadable due to invalid ECC codes. This happens because second
> >> write needs to update ECC codes, but it is impossible to do it correctly
> >> without block erase. So idea of this patch is to  
> > 
> > "... is to use the unprotected OOB area to store the cleanmarkers, so
> > that they can be written by the filesystem without caring much about
> > the page being empty or not: the ECC codes will not be written anyway."
> > ?  
> 
> Ok
> 
> > 
> > JFFS2 is only useful on tiny NAND devices, where UBI does not fit,
> > which are usually true SLC flashes, with the capability of writing
> > a page with empty (0xFF) data, and still be able to write actual data
> > to it later in a second write.  
> 
> You mean to include text above to commit message also?

Yes, I believe this deserves to be in the commit message as well :)

> 
> >   
> >> split accesses to OOB
> >> free bytes and data on each page - now both of them does not depends on
> >> each other.
> >>
> >> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> >> ---
> >>  drivers/mtd/nand/raw/meson_nand.c | 192 ++++++++++++++++++++++++------
> >>  1 file changed, 155 insertions(+), 37 deletions(-)
> >>
> >> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> >> index 2f4d8c84186b..8526a6b87720 100644
> >> --- a/drivers/mtd/nand/raw/meson_nand.c
> >> +++ b/drivers/mtd/nand/raw/meson_nand.c
> >> @@ -108,6 +108,9 @@
> >>  
> >>  #define PER_INFO_BYTE		8
> >>  
> >> +#define NFC_USER_BYTES		2
> >> +#define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
> >> +
> >>  struct meson_nfc_nand_chip {
> >>  	struct list_head node;
> >>  	struct nand_chip nand;
> >> @@ -122,6 +125,7 @@ struct meson_nfc_nand_chip {
> >>  	u8 *data_buf;
> >>  	__le64 *info_buf;
> >>  	u32 nsels;
> >> +	u8 *oob_buf;
> >>  	u8 sels[];
> >>  };
> >>  
> >> @@ -338,7 +342,7 @@ static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
> >>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >>  	int len;
> >>  
> >> -	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
> >> +	len = nand->ecc.size * (i + 1) + NFC_OOB_PER_ECC(nand) * i;  
> > 
> > This...
> >   
> >>  
> >>  	return meson_chip->data_buf + len;
> >>  }
> >> @@ -349,7 +353,7 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
> >>  	int len, temp;
> >>  
> >>  	temp = nand->ecc.size + nand->ecc.bytes;
> >> -	len = (temp + 2) * i;
> >> +	len = (temp + NFC_USER_BYTES) * i;  
> > 
> > ... and this (same below)
> > 
> > are purely cosmetic, should be in a patch aside.
> >   
> 
> Ack, i'll move cosmetic updates to separate patch.
> 
> >>  
> >>  	return meson_chip->data_buf + len;
> >>  }
> >> @@ -357,29 +361,47 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
> >>  static void meson_nfc_get_data_oob(struct nand_chip *nand,
> >>  				   u8 *buf, u8 *oobbuf)
> >>  {
> >> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >> +	struct mtd_info *mtd = nand_to_mtd(nand);
> >>  	int i, oob_len = 0;
> >>  	u8 *dsrc, *osrc;
> >> +	u8 *oobtail;
> >>  
> >> -	oob_len = nand->ecc.bytes + 2;
> >> +	oob_len = NFC_OOB_PER_ECC(nand);
> >>  	for (i = 0; i < nand->ecc.steps; i++) {
> >>  		if (buf) {
> >>  			dsrc = meson_nfc_data_ptr(nand, i);
> >>  			memcpy(buf, dsrc, nand->ecc.size);
> >>  			buf += nand->ecc.size;
> >>  		}
> >> -		osrc = meson_nfc_oob_ptr(nand, i);
> >> -		memcpy(oobbuf, osrc, oob_len);
> >> -		oobbuf += oob_len;
> >> +
> >> +		if (oobbuf) {
> >> +			osrc = meson_nfc_oob_ptr(nand, i);
> >> +			memcpy(oobbuf, osrc, oob_len);
> >> +			oobbuf += oob_len;
> >> +		}
> >>  	}
> >> +
> >> +	if (!oobbuf)
> >> +		return;  
> > 
> > The whole "if (oobbuf)" logic is nice to have, but should as well be in
> > a dedicated patch.  
> 
> Sorry, You mean that this logic implements read of ECC codes? And not
> related to OOB layout update in this patch?

You need to make cosmetic changes in a first patch (or even in several
patches if they are not related), then in another patch you might make
additions like a better handling of the OOB, and any change in the
layout might come last. I just want to split the patch to make
understandable atomic changes (also easier to review).

1 atomic change == 1 patch

> >   
> >> +
> >> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
> >> +		  (nand->ecc.size + oob_len);
> >> +
> >> +	/* 'oobbuf' if already shifted to the start of unused area. */  
> > 
> > 		    is?
> > s/unused/user/? I'm not sure I get the comment.  
> 
> Yes, not clear comment.
> 
> >   
> >> +	memcpy(oobbuf, oobtail, mtd->oobsize - nand->ecc.steps * oob_len);
> >>  }
> >>  
> >>  static void meson_nfc_set_data_oob(struct nand_chip *nand,
> >>  				   const u8 *buf, u8 *oobbuf)
> >>  {
> >> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >> +	struct mtd_info *mtd = nand_to_mtd(nand);
> >>  	int i, oob_len = 0;
> >>  	u8 *dsrc, *osrc;
> >> +	u8 *oobtail;
> >>  
> >> -	oob_len = nand->ecc.bytes + 2;
> >> +	oob_len = NFC_OOB_PER_ECC(nand);
> >>  	for (i = 0; i < nand->ecc.steps; i++) {
> >>  		if (buf) {
> >>  			dsrc = meson_nfc_data_ptr(nand, i);
> >> @@ -390,6 +412,12 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
> >>  		memcpy(osrc, oobbuf, oob_len);
> >>  		oobbuf += oob_len;
> >>  	}
> >> +
> >> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
> >> +		  (nand->ecc.size + oob_len);
> >> +
> >> +	/* 'oobbuf' if already shifted to the start of unused area. */
> >> +	memcpy(oobtail, oobbuf, mtd->oobsize - nand->ecc.steps * oob_len);
> >>  }
> >>  
> >>  static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
> >> @@ -436,25 +464,12 @@ static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
> >>  {
> >>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >>  	__le64 *info;
> >> -	int i, count;
> >> -
> >> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> >> -		info = &meson_chip->info_buf[i];
> >> -		*info |= oob_buf[count];
> >> -		*info |= oob_buf[count + 1] << 8;
> >> -	}
> >> -}
> >> -
> >> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
> >> -{
> >> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >> -	__le64 *info;
> >> -	int i, count;
> >> +	int i;
> >>  
> >> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> >> +	for (i = 0; i < nand->ecc.steps; i++) {
> >>  		info = &meson_chip->info_buf[i];
> >> -		oob_buf[count] = *info;
> >> -		oob_buf[count + 1] = *info >> 8;
> >> +		/* Always ignore user bytes programming. */  
> > 
> > Why?  
> 
> I think comment message is wrong a little bit. Here "user bytes" are
> user bytes protected by ECC (e.g. location of these bytes differs from new
> OOB layout introduced by this patch). During page write this hardware
> always writes these bytes along with data. But, new OOB layout always ignores
> these 4 bytes, so set them to 0xFF always.

When performing page reads/writes, you need to take the data as it's
been provided. You may move the data around in the buffer provided to
the controller, so that it get the ECC data at the right location, and
you need of course to reorganize the data when reading as well, so that
the user sees XkiB of data + YB of OOB. That's all you need to do in
these helpers.

> > Also, maybe I should mention the helpers:
> > mtd_ooblayout_get_eccbytes and co
> > They are very useful to deal with OOB bytes. Everything seems extremely
> > hardcoded in this driver, while the user can tune read/write OOB
> > operations.  
> 
> Thanks for details, IIUC these helpers will work correctly with this driver.
> What means "tune" here?

The user can provide data for the OOB part and tell the mtd layer to
place the bytes in the ->oob_buf following different constraints:
- the user bytes are already when they need to be (MTD_OPS_PLACE_OOB
  and MTD_OPS_RAW)
- the user bytes are packed at the beginning of the buffer, please move
  them where they belong (MTD_OPS_AUTO_OOB).

So in the controller driver, what needs to be done is to make the
switch between the "data1+data2+oob1+oob2" layout into your
ECC controller's layout, eg "data1+oob1+data2+oob2".

Hence you should not need anything like that:
> >> +		*info |= 0xffff;

> >>  	}
> >>  }
> >>  
> >> @@ -698,18 +713,92 @@ static int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf,
> >>  	return meson_nfc_write_page_sub(nand, page, 1);
> >>  }
> >>  
> >> +static u32 meson_nfc_get_oob_bytes(struct nand_chip *nand)
> >> +{
> >> +	struct mtd_info *mtd = nand_to_mtd(nand);
> >> +
> >> +	return mtd->oobsize - nand->ecc.steps * NFC_OOB_PER_ECC(nand);
> >> +}
> >> +
> >> +static int __meson_nfc_write_oob(struct nand_chip *nand, int page, u8 *oob_buf)
> >> +{
> >> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >> +	struct mtd_info *mtd = nand_to_mtd(nand);
> >> +	u32 page_size = mtd->writesize + mtd->oobsize;
> >> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
> >> +	int ret;
> >> +
> >> +	if (!oob_bytes)
> >> +		return 0;
> >> +
> >> +	ret = nand_prog_page_begin_op(nand, page, 0, NULL, 0);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	memcpy(meson_chip->oob_buf, oob_buf + (mtd->oobsize - oob_bytes),
> >> +	       oob_bytes);
> >> +
> >> +	ret = nand_change_write_column_op(nand, page_size - oob_bytes,
> >> +					  meson_chip->oob_buf,
> >> +					  oob_bytes, false);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	return nand_prog_page_end_op(nand);
> >> +}
> >> +
> >> +static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
> >> +				u8 *oob_buf)
> >> +{
> >> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >> +	struct mtd_info *mtd = nand_to_mtd(nand);
> >> +	u32 oob_bytes;
> >> +	u32 page_size;
> >> +	int ret;
> >> +
> >> +	oob_bytes = meson_nfc_get_oob_bytes(nand);
> >> +
> >> +	if (!oob_bytes)
> >> +		return 0;
> >> +
> >> +	ret = nand_read_page_op(nand, page, 0, NULL, 0);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	page_size = mtd->writesize + mtd->oobsize;
> >> +
> >> +	ret = nand_change_read_column_op(nand, page_size - oob_bytes,
> >> +					 meson_chip->oob_buf,
> >> +					 oob_bytes, false);
> >> +
> >> +	if (!ret)
> >> +		memcpy(oob_buf + (mtd->oobsize - oob_bytes),
> >> +		       meson_chip->oob_buf,
> >> +		       oob_bytes);
> >> +
> >> +	return ret;
> >> +}
> >> +
> >>  static int meson_nfc_write_page_hwecc(struct nand_chip *nand,
> >>  				      const u8 *buf, int oob_required, int page)
> >>  {
> >>  	struct mtd_info *mtd = nand_to_mtd(nand);
> >>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >>  	u8 *oob_buf = nand->oob_poi;
> >> +	int ret;
> >>  
> >>  	memcpy(meson_chip->data_buf, buf, mtd->writesize);
> >>  	memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE);
> >>  	meson_nfc_set_user_byte(nand, oob_buf);
> >>  
> >> -	return meson_nfc_write_page_sub(nand, page, 0);
> >> +	ret = meson_nfc_write_page_sub(nand, page, 0);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	if (oob_required)
> >> +		ret = __meson_nfc_write_oob(nand, page, oob_buf);  
> > 
> > You should provide all the data including OOB bytes in a single write
> > call, otherwise you perform two writes on the same page, that's not
> > what this helper is expected to do.  
> 
> I see, so I need to check 'oob_required' here before programming page data?

Yes!

> If it is set -> place OOB data to DMA buffer and then call PAGEPROG once?

Exactly.

> 
> >   
> >> +
> >> +	return ret;
> >>  }
> >>  
> >>  static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
> >> @@ -783,7 +872,7 @@ static int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf,
> >>  	if (ret)
> >>  		return ret;
> >>  
> >> -	meson_nfc_get_data_oob(nand, buf, oob_buf);
> >> +	meson_nfc_get_data_oob(nand, buf, oob_required ? oob_buf : NULL);
> >>  
> >>  	return 0;
> >>  }
> >> @@ -803,12 +892,12 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
> >>  	if (ret)
> >>  		return ret;
> >>  
> >> -	meson_nfc_get_user_byte(nand, oob_buf);
> >>  	ret = meson_nfc_ecc_correct(nand, &bitflips, &correct_bitmap);
> >>  	if (ret == ECC_CHECK_RETURN_FF) {
> >>  		if (buf)
> >>  			memset(buf, 0xff, mtd->writesize);
> >>  		memset(oob_buf, 0xff, mtd->oobsize);
> >> +		return bitflips;  
> > 
> > That is something else => other fix => other patch?  
> 
> Idea of this 'return' is that when read fails, we return from this function
> without reading OOB below.

It seemed like the "return bitflips" thing was new, if it's just due to
the diff not being wide enough, then it's fine, otherwise if this is
something new, it should be in a dedicated patch.

> 
> >   
> >>  	} else if (ret < 0) {
> >>  		if ((nand->options & NAND_NEED_SCRAMBLING) || !buf) {
> >>  			mtd->ecc_stats.failed++;
> >> @@ -820,12 +909,14 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
> >>  
> >>  		for (i = 0; i < nand->ecc.steps ; i++) {
> >>  			u8 *data = buf + i * ecc->size;
> >> -			u8 *oob = nand->oob_poi + i * (ecc->bytes + 2);
> >> +			u8 *oob = nand->oob_poi + i * NFC_OOB_PER_ECC(nand);
> >>  
> >>  			if (correct_bitmap & BIT_ULL(i))
> >>  				continue;
> >> +
> >>  			ret = nand_check_erased_ecc_chunk(data,	ecc->size,
> >> -							  oob, ecc->bytes + 2,
> >> +							  oob,
> >> +							  NFC_OOB_PER_ECC(nand),
> >>  							  NULL, 0,
> >>  							  ecc->strength);
> >>  			if (ret < 0) {
> >> @@ -839,17 +930,30 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
> >>  		memcpy(buf, meson_chip->data_buf, mtd->writesize);
> >>  	}
> >>  
> >> +	if (oob_required)
> >> +		__meson_nfc_read_oob(nand, page, oob_buf);  
> > 
> > In the standalone "read_oob" hook, you have to send a READ0 command,
> > but not when you are in the read_page hook. It is a big waste of time.  
> 
> IIUC approach here must be exactly the same as in write? E.g. i need to
> send single READ0 and then fill provided OOB buffer if needed?

Yes.

For both reads and writes, the logic is:
- there are commands which are actually time consuming: there is
  something happening on the nand array which either reads or writes
  data to/from the internal sram.
- there are other commands which just change the "pointer" in the
  internal sram.

So you can basically say "I want to write into the sram at offset X,
then at offset Y" and when the sram has been filled you can commit the
write and that's the operation which should happen only once. In the
read path it's the opposite, you request a read from the NAND array into
the sram cache, and you can then request data randomly. Of course both
operations cannot be too random either, you need to follow the ECC
engine pattern which must be fed with X bytes of data and then will
produce the ECC bytes to write/compare.

> 
> >   
> >> +
> >>  	return bitflips;
> >>  }
> >>  
> >>  static int meson_nfc_read_oob_raw(struct nand_chip *nand, int page)
> >>  {
> >> -	return meson_nfc_read_page_raw(nand, NULL, 1, page);
> >> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
> >>  }
> >>  
> >>  static int meson_nfc_read_oob(struct nand_chip *nand, int page)
> >>  {
> >> -	return meson_nfc_read_page_hwecc(nand, NULL, 1, page);
> >> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
> >> +}
> >> +
> >> +static int meson_nfc_write_oob_raw(struct nand_chip *nand, int page)
> >> +{
> >> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
> >> +}
> >> +
> >> +static int meson_nfc_write_oob(struct nand_chip *nand, int page)
> >> +{
> >> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);  
> > 
> > Do we really need these indirections?  
> 
> Right, I think I can use only one function for OOB write in both ECC and raw modes.
> 
> >   
> >>  }
> >>  
> >>  static bool meson_nfc_is_buffer_dma_safe(const void *buffer)
> >> @@ -982,7 +1086,7 @@ static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
> >>  	if (section >= nand->ecc.steps)
> >>  		return -ERANGE;
> >>  
> >> -	oobregion->offset =  2 + (section * (2 + nand->ecc.bytes));
> >> +	oobregion->offset = NFC_USER_BYTES + section * NFC_OOB_PER_ECC(nand);  
> > 
> > The () are still needed around the '*'
> >   
> >>  	oobregion->length = nand->ecc.bytes;
> >>  
> >>  	return 0;
> >> @@ -992,12 +1096,16 @@ static int meson_ooblayout_free(struct mtd_info *mtd, int section,
> >>  				struct mtd_oob_region *oobregion)
> >>  {
> >>  	struct nand_chip *nand = mtd_to_nand(mtd);
> >> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
> >>  
> >>  	if (section >= nand->ecc.steps)
> >>  		return -ERANGE;
> >>  
> >> -	oobregion->offset = section * (2 + nand->ecc.bytes);
> >> -	oobregion->length = 2;
> >> +	/* Split rest of OOB area (not covered by ECC engine) per each
> >> +	 * ECC section. This will be OOB data available to user.
> >> +	 */
> >> +	oobregion->offset = (section + nand->ecc.steps) * NFC_OOB_PER_ECC(nand);
> >> +	oobregion->length = oob_bytes / nand->ecc.steps;
> >>  
> >>  	return 0;
> >>  }
> >> @@ -1184,6 +1292,9 @@ static int meson_nand_bch_mode(struct nand_chip *nand)
> >>  
> >>  static void meson_nand_detach_chip(struct nand_chip *nand)
> >>  {
> >> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >> +
> >> +	kfree(meson_chip->oob_buf);
> >>  	meson_nfc_free_buffer(nand);
> >>  }
> >>  
> >> @@ -1225,9 +1336,9 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
> >>  	nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
> >>  	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
> >>  	nand->ecc.write_page = meson_nfc_write_page_hwecc;
> >> -	nand->ecc.write_oob_raw = nand_write_oob_std;
> >> -	nand->ecc.write_oob = nand_write_oob_std;
> >>  
> >> +	nand->ecc.write_oob_raw = meson_nfc_write_oob_raw;
> >> +	nand->ecc.write_oob = meson_nfc_write_oob;  
> > 
> > Actually if you define the right OOB layouts, are these really needed
> > ?? I would expect the right bytes to be picked up by the default
> > implementation. I see nothing specific in your current implementation?  
> 
> You mean to use 'nand_write_oob_std()'? If so, I think it won't work,
> because it tries to write OOB data right after 'mtd->writesize', but
> page layout for this controller is like this:
> [       1024 bytes of data        ]
> [ 14 bytes ECC + 2 bytes for user ] <- with new layout we don't touch these 2 bytes
> [       1024 bytes of data        ]
> [ 14 bytes ECC + 2 bytes for user ] <- with new layout we don't touch these 2 bytes
> [      32 bytes of user bytes     ] <- we use there 32 bytes as free(user) bytes in OOB

Right.

> 
> So with 'mtd->writesize' of 2048 we won't get into last 32 bytes in the picture above.
> I'm not goot in NAND/MTD area, but I think that why i need special OOB access callbacks.

Ok.

> Also in previous patches @Liang said, that default OOB read/write functions won't work
> on this controller and it is wrong to use them in currently merged driver.
> 
> >   
> >>  	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
> >>  	nand->ecc.read_page = meson_nfc_read_page_hwecc;
> >>  	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
> >> @@ -1237,9 +1348,16 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
> >>  		dev_err(nfc->dev, "16bits bus width not supported");
> >>  		return -EINVAL;
> >>  	}
> >> +
> >> +	meson_chip->oob_buf = kmalloc(nand->ecc.bytes, GFP_KERNEL);  
> > 
> > devm_kmalloc?  
> 
> Ack
> 
> >   
> >> +	if (!meson_chip->oob_buf)
> >> +		return -ENOMEM;
> >> +
> >>  	ret = meson_chip_buffer_init(nand);
> >> -	if (ret)
> >> +	if (ret) {
> >> +		kfree(meson_chip->oob_buf);
> >>  		return -ENOMEM;
> >> +	}
> >>  
> >>  	return ret;
> >>  }  
> > 
> > 
> > Thanks,
> > Miquèl  
> 
> Thanks, Arseniy


Thanks,
Miquèl

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-26 17:03         ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-26 17:03 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

avkrasnov@sberdevices.ru wrote on Tue, 23 May 2023 20:17:14 +0300:

> Hello Miquel! Thanks for detailed review!
> 
> On 22.05.2023 18:33, Miquel Raynal wrote:
> > Hi Arseniy,
> > 
> > AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:36 +0300:
> > 
> > The title should perhaps be "only expose unprotected user OOB bytes".
> >   
> >> This moves free bytes of OOB to non-protected ECC area. It is needed to
> >> make JFFS2 works correctly with this NAND controller. Problem fires when
> >> JFFS2 driver writes cleanmarker to some page and later it tries to write
> >> to this page - write will be done successfully, but after that such page
> >> becomes unreadable due to invalid ECC codes. This happens because second
> >> write needs to update ECC codes, but it is impossible to do it correctly
> >> without block erase. So idea of this patch is to  
> > 
> > "... is to use the unprotected OOB area to store the cleanmarkers, so
> > that they can be written by the filesystem without caring much about
> > the page being empty or not: the ECC codes will not be written anyway."
> > ?  
> 
> Ok
> 
> > 
> > JFFS2 is only useful on tiny NAND devices, where UBI does not fit,
> > which are usually true SLC flashes, with the capability of writing
> > a page with empty (0xFF) data, and still be able to write actual data
> > to it later in a second write.  
> 
> You mean to include text above to commit message also?

Yes, I believe this deserves to be in the commit message as well :)

> 
> >   
> >> split accesses to OOB
> >> free bytes and data on each page - now both of them does not depends on
> >> each other.
> >>
> >> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> >> ---
> >>  drivers/mtd/nand/raw/meson_nand.c | 192 ++++++++++++++++++++++++------
> >>  1 file changed, 155 insertions(+), 37 deletions(-)
> >>
> >> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> >> index 2f4d8c84186b..8526a6b87720 100644
> >> --- a/drivers/mtd/nand/raw/meson_nand.c
> >> +++ b/drivers/mtd/nand/raw/meson_nand.c
> >> @@ -108,6 +108,9 @@
> >>  
> >>  #define PER_INFO_BYTE		8
> >>  
> >> +#define NFC_USER_BYTES		2
> >> +#define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
> >> +
> >>  struct meson_nfc_nand_chip {
> >>  	struct list_head node;
> >>  	struct nand_chip nand;
> >> @@ -122,6 +125,7 @@ struct meson_nfc_nand_chip {
> >>  	u8 *data_buf;
> >>  	__le64 *info_buf;
> >>  	u32 nsels;
> >> +	u8 *oob_buf;
> >>  	u8 sels[];
> >>  };
> >>  
> >> @@ -338,7 +342,7 @@ static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
> >>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >>  	int len;
> >>  
> >> -	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
> >> +	len = nand->ecc.size * (i + 1) + NFC_OOB_PER_ECC(nand) * i;  
> > 
> > This...
> >   
> >>  
> >>  	return meson_chip->data_buf + len;
> >>  }
> >> @@ -349,7 +353,7 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
> >>  	int len, temp;
> >>  
> >>  	temp = nand->ecc.size + nand->ecc.bytes;
> >> -	len = (temp + 2) * i;
> >> +	len = (temp + NFC_USER_BYTES) * i;  
> > 
> > ... and this (same below)
> > 
> > are purely cosmetic, should be in a patch aside.
> >   
> 
> Ack, i'll move cosmetic updates to separate patch.
> 
> >>  
> >>  	return meson_chip->data_buf + len;
> >>  }
> >> @@ -357,29 +361,47 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
> >>  static void meson_nfc_get_data_oob(struct nand_chip *nand,
> >>  				   u8 *buf, u8 *oobbuf)
> >>  {
> >> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >> +	struct mtd_info *mtd = nand_to_mtd(nand);
> >>  	int i, oob_len = 0;
> >>  	u8 *dsrc, *osrc;
> >> +	u8 *oobtail;
> >>  
> >> -	oob_len = nand->ecc.bytes + 2;
> >> +	oob_len = NFC_OOB_PER_ECC(nand);
> >>  	for (i = 0; i < nand->ecc.steps; i++) {
> >>  		if (buf) {
> >>  			dsrc = meson_nfc_data_ptr(nand, i);
> >>  			memcpy(buf, dsrc, nand->ecc.size);
> >>  			buf += nand->ecc.size;
> >>  		}
> >> -		osrc = meson_nfc_oob_ptr(nand, i);
> >> -		memcpy(oobbuf, osrc, oob_len);
> >> -		oobbuf += oob_len;
> >> +
> >> +		if (oobbuf) {
> >> +			osrc = meson_nfc_oob_ptr(nand, i);
> >> +			memcpy(oobbuf, osrc, oob_len);
> >> +			oobbuf += oob_len;
> >> +		}
> >>  	}
> >> +
> >> +	if (!oobbuf)
> >> +		return;  
> > 
> > The whole "if (oobbuf)" logic is nice to have, but should as well be in
> > a dedicated patch.  
> 
> Sorry, You mean that this logic implements read of ECC codes? And not
> related to OOB layout update in this patch?

You need to make cosmetic changes in a first patch (or even in several
patches if they are not related), then in another patch you might make
additions like a better handling of the OOB, and any change in the
layout might come last. I just want to split the patch to make
understandable atomic changes (also easier to review).

1 atomic change == 1 patch

> >   
> >> +
> >> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
> >> +		  (nand->ecc.size + oob_len);
> >> +
> >> +	/* 'oobbuf' if already shifted to the start of unused area. */  
> > 
> > 		    is?
> > s/unused/user/? I'm not sure I get the comment.  
> 
> Yes, not clear comment.
> 
> >   
> >> +	memcpy(oobbuf, oobtail, mtd->oobsize - nand->ecc.steps * oob_len);
> >>  }
> >>  
> >>  static void meson_nfc_set_data_oob(struct nand_chip *nand,
> >>  				   const u8 *buf, u8 *oobbuf)
> >>  {
> >> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >> +	struct mtd_info *mtd = nand_to_mtd(nand);
> >>  	int i, oob_len = 0;
> >>  	u8 *dsrc, *osrc;
> >> +	u8 *oobtail;
> >>  
> >> -	oob_len = nand->ecc.bytes + 2;
> >> +	oob_len = NFC_OOB_PER_ECC(nand);
> >>  	for (i = 0; i < nand->ecc.steps; i++) {
> >>  		if (buf) {
> >>  			dsrc = meson_nfc_data_ptr(nand, i);
> >> @@ -390,6 +412,12 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
> >>  		memcpy(osrc, oobbuf, oob_len);
> >>  		oobbuf += oob_len;
> >>  	}
> >> +
> >> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
> >> +		  (nand->ecc.size + oob_len);
> >> +
> >> +	/* 'oobbuf' if already shifted to the start of unused area. */
> >> +	memcpy(oobtail, oobbuf, mtd->oobsize - nand->ecc.steps * oob_len);
> >>  }
> >>  
> >>  static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
> >> @@ -436,25 +464,12 @@ static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
> >>  {
> >>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >>  	__le64 *info;
> >> -	int i, count;
> >> -
> >> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> >> -		info = &meson_chip->info_buf[i];
> >> -		*info |= oob_buf[count];
> >> -		*info |= oob_buf[count + 1] << 8;
> >> -	}
> >> -}
> >> -
> >> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
> >> -{
> >> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >> -	__le64 *info;
> >> -	int i, count;
> >> +	int i;
> >>  
> >> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> >> +	for (i = 0; i < nand->ecc.steps; i++) {
> >>  		info = &meson_chip->info_buf[i];
> >> -		oob_buf[count] = *info;
> >> -		oob_buf[count + 1] = *info >> 8;
> >> +		/* Always ignore user bytes programming. */  
> > 
> > Why?  
> 
> I think comment message is wrong a little bit. Here "user bytes" are
> user bytes protected by ECC (e.g. location of these bytes differs from new
> OOB layout introduced by this patch). During page write this hardware
> always writes these bytes along with data. But, new OOB layout always ignores
> these 4 bytes, so set them to 0xFF always.

When performing page reads/writes, you need to take the data as it's
been provided. You may move the data around in the buffer provided to
the controller, so that it get the ECC data at the right location, and
you need of course to reorganize the data when reading as well, so that
the user sees XkiB of data + YB of OOB. That's all you need to do in
these helpers.

> > Also, maybe I should mention the helpers:
> > mtd_ooblayout_get_eccbytes and co
> > They are very useful to deal with OOB bytes. Everything seems extremely
> > hardcoded in this driver, while the user can tune read/write OOB
> > operations.  
> 
> Thanks for details, IIUC these helpers will work correctly with this driver.
> What means "tune" here?

The user can provide data for the OOB part and tell the mtd layer to
place the bytes in the ->oob_buf following different constraints:
- the user bytes are already when they need to be (MTD_OPS_PLACE_OOB
  and MTD_OPS_RAW)
- the user bytes are packed at the beginning of the buffer, please move
  them where they belong (MTD_OPS_AUTO_OOB).

So in the controller driver, what needs to be done is to make the
switch between the "data1+data2+oob1+oob2" layout into your
ECC controller's layout, eg "data1+oob1+data2+oob2".

Hence you should not need anything like that:
> >> +		*info |= 0xffff;

> >>  	}
> >>  }
> >>  
> >> @@ -698,18 +713,92 @@ static int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf,
> >>  	return meson_nfc_write_page_sub(nand, page, 1);
> >>  }
> >>  
> >> +static u32 meson_nfc_get_oob_bytes(struct nand_chip *nand)
> >> +{
> >> +	struct mtd_info *mtd = nand_to_mtd(nand);
> >> +
> >> +	return mtd->oobsize - nand->ecc.steps * NFC_OOB_PER_ECC(nand);
> >> +}
> >> +
> >> +static int __meson_nfc_write_oob(struct nand_chip *nand, int page, u8 *oob_buf)
> >> +{
> >> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >> +	struct mtd_info *mtd = nand_to_mtd(nand);
> >> +	u32 page_size = mtd->writesize + mtd->oobsize;
> >> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
> >> +	int ret;
> >> +
> >> +	if (!oob_bytes)
> >> +		return 0;
> >> +
> >> +	ret = nand_prog_page_begin_op(nand, page, 0, NULL, 0);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	memcpy(meson_chip->oob_buf, oob_buf + (mtd->oobsize - oob_bytes),
> >> +	       oob_bytes);
> >> +
> >> +	ret = nand_change_write_column_op(nand, page_size - oob_bytes,
> >> +					  meson_chip->oob_buf,
> >> +					  oob_bytes, false);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	return nand_prog_page_end_op(nand);
> >> +}
> >> +
> >> +static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
> >> +				u8 *oob_buf)
> >> +{
> >> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >> +	struct mtd_info *mtd = nand_to_mtd(nand);
> >> +	u32 oob_bytes;
> >> +	u32 page_size;
> >> +	int ret;
> >> +
> >> +	oob_bytes = meson_nfc_get_oob_bytes(nand);
> >> +
> >> +	if (!oob_bytes)
> >> +		return 0;
> >> +
> >> +	ret = nand_read_page_op(nand, page, 0, NULL, 0);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	page_size = mtd->writesize + mtd->oobsize;
> >> +
> >> +	ret = nand_change_read_column_op(nand, page_size - oob_bytes,
> >> +					 meson_chip->oob_buf,
> >> +					 oob_bytes, false);
> >> +
> >> +	if (!ret)
> >> +		memcpy(oob_buf + (mtd->oobsize - oob_bytes),
> >> +		       meson_chip->oob_buf,
> >> +		       oob_bytes);
> >> +
> >> +	return ret;
> >> +}
> >> +
> >>  static int meson_nfc_write_page_hwecc(struct nand_chip *nand,
> >>  				      const u8 *buf, int oob_required, int page)
> >>  {
> >>  	struct mtd_info *mtd = nand_to_mtd(nand);
> >>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >>  	u8 *oob_buf = nand->oob_poi;
> >> +	int ret;
> >>  
> >>  	memcpy(meson_chip->data_buf, buf, mtd->writesize);
> >>  	memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE);
> >>  	meson_nfc_set_user_byte(nand, oob_buf);
> >>  
> >> -	return meson_nfc_write_page_sub(nand, page, 0);
> >> +	ret = meson_nfc_write_page_sub(nand, page, 0);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	if (oob_required)
> >> +		ret = __meson_nfc_write_oob(nand, page, oob_buf);  
> > 
> > You should provide all the data including OOB bytes in a single write
> > call, otherwise you perform two writes on the same page, that's not
> > what this helper is expected to do.  
> 
> I see, so I need to check 'oob_required' here before programming page data?

Yes!

> If it is set -> place OOB data to DMA buffer and then call PAGEPROG once?

Exactly.

> 
> >   
> >> +
> >> +	return ret;
> >>  }
> >>  
> >>  static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
> >> @@ -783,7 +872,7 @@ static int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf,
> >>  	if (ret)
> >>  		return ret;
> >>  
> >> -	meson_nfc_get_data_oob(nand, buf, oob_buf);
> >> +	meson_nfc_get_data_oob(nand, buf, oob_required ? oob_buf : NULL);
> >>  
> >>  	return 0;
> >>  }
> >> @@ -803,12 +892,12 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
> >>  	if (ret)
> >>  		return ret;
> >>  
> >> -	meson_nfc_get_user_byte(nand, oob_buf);
> >>  	ret = meson_nfc_ecc_correct(nand, &bitflips, &correct_bitmap);
> >>  	if (ret == ECC_CHECK_RETURN_FF) {
> >>  		if (buf)
> >>  			memset(buf, 0xff, mtd->writesize);
> >>  		memset(oob_buf, 0xff, mtd->oobsize);
> >> +		return bitflips;  
> > 
> > That is something else => other fix => other patch?  
> 
> Idea of this 'return' is that when read fails, we return from this function
> without reading OOB below.

It seemed like the "return bitflips" thing was new, if it's just due to
the diff not being wide enough, then it's fine, otherwise if this is
something new, it should be in a dedicated patch.

> 
> >   
> >>  	} else if (ret < 0) {
> >>  		if ((nand->options & NAND_NEED_SCRAMBLING) || !buf) {
> >>  			mtd->ecc_stats.failed++;
> >> @@ -820,12 +909,14 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
> >>  
> >>  		for (i = 0; i < nand->ecc.steps ; i++) {
> >>  			u8 *data = buf + i * ecc->size;
> >> -			u8 *oob = nand->oob_poi + i * (ecc->bytes + 2);
> >> +			u8 *oob = nand->oob_poi + i * NFC_OOB_PER_ECC(nand);
> >>  
> >>  			if (correct_bitmap & BIT_ULL(i))
> >>  				continue;
> >> +
> >>  			ret = nand_check_erased_ecc_chunk(data,	ecc->size,
> >> -							  oob, ecc->bytes + 2,
> >> +							  oob,
> >> +							  NFC_OOB_PER_ECC(nand),
> >>  							  NULL, 0,
> >>  							  ecc->strength);
> >>  			if (ret < 0) {
> >> @@ -839,17 +930,30 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
> >>  		memcpy(buf, meson_chip->data_buf, mtd->writesize);
> >>  	}
> >>  
> >> +	if (oob_required)
> >> +		__meson_nfc_read_oob(nand, page, oob_buf);  
> > 
> > In the standalone "read_oob" hook, you have to send a READ0 command,
> > but not when you are in the read_page hook. It is a big waste of time.  
> 
> IIUC approach here must be exactly the same as in write? E.g. i need to
> send single READ0 and then fill provided OOB buffer if needed?

Yes.

For both reads and writes, the logic is:
- there are commands which are actually time consuming: there is
  something happening on the nand array which either reads or writes
  data to/from the internal sram.
- there are other commands which just change the "pointer" in the
  internal sram.

So you can basically say "I want to write into the sram at offset X,
then at offset Y" and when the sram has been filled you can commit the
write and that's the operation which should happen only once. In the
read path it's the opposite, you request a read from the NAND array into
the sram cache, and you can then request data randomly. Of course both
operations cannot be too random either, you need to follow the ECC
engine pattern which must be fed with X bytes of data and then will
produce the ECC bytes to write/compare.

> 
> >   
> >> +
> >>  	return bitflips;
> >>  }
> >>  
> >>  static int meson_nfc_read_oob_raw(struct nand_chip *nand, int page)
> >>  {
> >> -	return meson_nfc_read_page_raw(nand, NULL, 1, page);
> >> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
> >>  }
> >>  
> >>  static int meson_nfc_read_oob(struct nand_chip *nand, int page)
> >>  {
> >> -	return meson_nfc_read_page_hwecc(nand, NULL, 1, page);
> >> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
> >> +}
> >> +
> >> +static int meson_nfc_write_oob_raw(struct nand_chip *nand, int page)
> >> +{
> >> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
> >> +}
> >> +
> >> +static int meson_nfc_write_oob(struct nand_chip *nand, int page)
> >> +{
> >> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);  
> > 
> > Do we really need these indirections?  
> 
> Right, I think I can use only one function for OOB write in both ECC and raw modes.
> 
> >   
> >>  }
> >>  
> >>  static bool meson_nfc_is_buffer_dma_safe(const void *buffer)
> >> @@ -982,7 +1086,7 @@ static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
> >>  	if (section >= nand->ecc.steps)
> >>  		return -ERANGE;
> >>  
> >> -	oobregion->offset =  2 + (section * (2 + nand->ecc.bytes));
> >> +	oobregion->offset = NFC_USER_BYTES + section * NFC_OOB_PER_ECC(nand);  
> > 
> > The () are still needed around the '*'
> >   
> >>  	oobregion->length = nand->ecc.bytes;
> >>  
> >>  	return 0;
> >> @@ -992,12 +1096,16 @@ static int meson_ooblayout_free(struct mtd_info *mtd, int section,
> >>  				struct mtd_oob_region *oobregion)
> >>  {
> >>  	struct nand_chip *nand = mtd_to_nand(mtd);
> >> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
> >>  
> >>  	if (section >= nand->ecc.steps)
> >>  		return -ERANGE;
> >>  
> >> -	oobregion->offset = section * (2 + nand->ecc.bytes);
> >> -	oobregion->length = 2;
> >> +	/* Split rest of OOB area (not covered by ECC engine) per each
> >> +	 * ECC section. This will be OOB data available to user.
> >> +	 */
> >> +	oobregion->offset = (section + nand->ecc.steps) * NFC_OOB_PER_ECC(nand);
> >> +	oobregion->length = oob_bytes / nand->ecc.steps;
> >>  
> >>  	return 0;
> >>  }
> >> @@ -1184,6 +1292,9 @@ static int meson_nand_bch_mode(struct nand_chip *nand)
> >>  
> >>  static void meson_nand_detach_chip(struct nand_chip *nand)
> >>  {
> >> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >> +
> >> +	kfree(meson_chip->oob_buf);
> >>  	meson_nfc_free_buffer(nand);
> >>  }
> >>  
> >> @@ -1225,9 +1336,9 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
> >>  	nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
> >>  	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
> >>  	nand->ecc.write_page = meson_nfc_write_page_hwecc;
> >> -	nand->ecc.write_oob_raw = nand_write_oob_std;
> >> -	nand->ecc.write_oob = nand_write_oob_std;
> >>  
> >> +	nand->ecc.write_oob_raw = meson_nfc_write_oob_raw;
> >> +	nand->ecc.write_oob = meson_nfc_write_oob;  
> > 
> > Actually if you define the right OOB layouts, are these really needed
> > ?? I would expect the right bytes to be picked up by the default
> > implementation. I see nothing specific in your current implementation?  
> 
> You mean to use 'nand_write_oob_std()'? If so, I think it won't work,
> because it tries to write OOB data right after 'mtd->writesize', but
> page layout for this controller is like this:
> [       1024 bytes of data        ]
> [ 14 bytes ECC + 2 bytes for user ] <- with new layout we don't touch these 2 bytes
> [       1024 bytes of data        ]
> [ 14 bytes ECC + 2 bytes for user ] <- with new layout we don't touch these 2 bytes
> [      32 bytes of user bytes     ] <- we use there 32 bytes as free(user) bytes in OOB

Right.

> 
> So with 'mtd->writesize' of 2048 we won't get into last 32 bytes in the picture above.
> I'm not goot in NAND/MTD area, but I think that why i need special OOB access callbacks.

Ok.

> Also in previous patches @Liang said, that default OOB read/write functions won't work
> on this controller and it is wrong to use them in currently merged driver.
> 
> >   
> >>  	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
> >>  	nand->ecc.read_page = meson_nfc_read_page_hwecc;
> >>  	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
> >> @@ -1237,9 +1348,16 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
> >>  		dev_err(nfc->dev, "16bits bus width not supported");
> >>  		return -EINVAL;
> >>  	}
> >> +
> >> +	meson_chip->oob_buf = kmalloc(nand->ecc.bytes, GFP_KERNEL);  
> > 
> > devm_kmalloc?  
> 
> Ack
> 
> >   
> >> +	if (!meson_chip->oob_buf)
> >> +		return -ENOMEM;
> >> +
> >>  	ret = meson_chip_buffer_init(nand);
> >> -	if (ret)
> >> +	if (ret) {
> >> +		kfree(meson_chip->oob_buf);
> >>  		return -ENOMEM;
> >> +	}
> >>  
> >>  	return ret;
> >>  }  
> > 
> > 
> > Thanks,
> > Miquèl  
> 
> Thanks, Arseniy


Thanks,
Miquèl

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

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-26 17:03         ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-26 17:03 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

avkrasnov@sberdevices.ru wrote on Tue, 23 May 2023 20:17:14 +0300:

> Hello Miquel! Thanks for detailed review!
> 
> On 22.05.2023 18:33, Miquel Raynal wrote:
> > Hi Arseniy,
> > 
> > AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:36 +0300:
> > 
> > The title should perhaps be "only expose unprotected user OOB bytes".
> >   
> >> This moves free bytes of OOB to non-protected ECC area. It is needed to
> >> make JFFS2 works correctly with this NAND controller. Problem fires when
> >> JFFS2 driver writes cleanmarker to some page and later it tries to write
> >> to this page - write will be done successfully, but after that such page
> >> becomes unreadable due to invalid ECC codes. This happens because second
> >> write needs to update ECC codes, but it is impossible to do it correctly
> >> without block erase. So idea of this patch is to  
> > 
> > "... is to use the unprotected OOB area to store the cleanmarkers, so
> > that they can be written by the filesystem without caring much about
> > the page being empty or not: the ECC codes will not be written anyway."
> > ?  
> 
> Ok
> 
> > 
> > JFFS2 is only useful on tiny NAND devices, where UBI does not fit,
> > which are usually true SLC flashes, with the capability of writing
> > a page with empty (0xFF) data, and still be able to write actual data
> > to it later in a second write.  
> 
> You mean to include text above to commit message also?

Yes, I believe this deserves to be in the commit message as well :)

> 
> >   
> >> split accesses to OOB
> >> free bytes and data on each page - now both of them does not depends on
> >> each other.
> >>
> >> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> >> ---
> >>  drivers/mtd/nand/raw/meson_nand.c | 192 ++++++++++++++++++++++++------
> >>  1 file changed, 155 insertions(+), 37 deletions(-)
> >>
> >> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> >> index 2f4d8c84186b..8526a6b87720 100644
> >> --- a/drivers/mtd/nand/raw/meson_nand.c
> >> +++ b/drivers/mtd/nand/raw/meson_nand.c
> >> @@ -108,6 +108,9 @@
> >>  
> >>  #define PER_INFO_BYTE		8
> >>  
> >> +#define NFC_USER_BYTES		2
> >> +#define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
> >> +
> >>  struct meson_nfc_nand_chip {
> >>  	struct list_head node;
> >>  	struct nand_chip nand;
> >> @@ -122,6 +125,7 @@ struct meson_nfc_nand_chip {
> >>  	u8 *data_buf;
> >>  	__le64 *info_buf;
> >>  	u32 nsels;
> >> +	u8 *oob_buf;
> >>  	u8 sels[];
> >>  };
> >>  
> >> @@ -338,7 +342,7 @@ static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
> >>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >>  	int len;
> >>  
> >> -	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
> >> +	len = nand->ecc.size * (i + 1) + NFC_OOB_PER_ECC(nand) * i;  
> > 
> > This...
> >   
> >>  
> >>  	return meson_chip->data_buf + len;
> >>  }
> >> @@ -349,7 +353,7 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
> >>  	int len, temp;
> >>  
> >>  	temp = nand->ecc.size + nand->ecc.bytes;
> >> -	len = (temp + 2) * i;
> >> +	len = (temp + NFC_USER_BYTES) * i;  
> > 
> > ... and this (same below)
> > 
> > are purely cosmetic, should be in a patch aside.
> >   
> 
> Ack, i'll move cosmetic updates to separate patch.
> 
> >>  
> >>  	return meson_chip->data_buf + len;
> >>  }
> >> @@ -357,29 +361,47 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
> >>  static void meson_nfc_get_data_oob(struct nand_chip *nand,
> >>  				   u8 *buf, u8 *oobbuf)
> >>  {
> >> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >> +	struct mtd_info *mtd = nand_to_mtd(nand);
> >>  	int i, oob_len = 0;
> >>  	u8 *dsrc, *osrc;
> >> +	u8 *oobtail;
> >>  
> >> -	oob_len = nand->ecc.bytes + 2;
> >> +	oob_len = NFC_OOB_PER_ECC(nand);
> >>  	for (i = 0; i < nand->ecc.steps; i++) {
> >>  		if (buf) {
> >>  			dsrc = meson_nfc_data_ptr(nand, i);
> >>  			memcpy(buf, dsrc, nand->ecc.size);
> >>  			buf += nand->ecc.size;
> >>  		}
> >> -		osrc = meson_nfc_oob_ptr(nand, i);
> >> -		memcpy(oobbuf, osrc, oob_len);
> >> -		oobbuf += oob_len;
> >> +
> >> +		if (oobbuf) {
> >> +			osrc = meson_nfc_oob_ptr(nand, i);
> >> +			memcpy(oobbuf, osrc, oob_len);
> >> +			oobbuf += oob_len;
> >> +		}
> >>  	}
> >> +
> >> +	if (!oobbuf)
> >> +		return;  
> > 
> > The whole "if (oobbuf)" logic is nice to have, but should as well be in
> > a dedicated patch.  
> 
> Sorry, You mean that this logic implements read of ECC codes? And not
> related to OOB layout update in this patch?

You need to make cosmetic changes in a first patch (or even in several
patches if they are not related), then in another patch you might make
additions like a better handling of the OOB, and any change in the
layout might come last. I just want to split the patch to make
understandable atomic changes (also easier to review).

1 atomic change == 1 patch

> >   
> >> +
> >> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
> >> +		  (nand->ecc.size + oob_len);
> >> +
> >> +	/* 'oobbuf' if already shifted to the start of unused area. */  
> > 
> > 		    is?
> > s/unused/user/? I'm not sure I get the comment.  
> 
> Yes, not clear comment.
> 
> >   
> >> +	memcpy(oobbuf, oobtail, mtd->oobsize - nand->ecc.steps * oob_len);
> >>  }
> >>  
> >>  static void meson_nfc_set_data_oob(struct nand_chip *nand,
> >>  				   const u8 *buf, u8 *oobbuf)
> >>  {
> >> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >> +	struct mtd_info *mtd = nand_to_mtd(nand);
> >>  	int i, oob_len = 0;
> >>  	u8 *dsrc, *osrc;
> >> +	u8 *oobtail;
> >>  
> >> -	oob_len = nand->ecc.bytes + 2;
> >> +	oob_len = NFC_OOB_PER_ECC(nand);
> >>  	for (i = 0; i < nand->ecc.steps; i++) {
> >>  		if (buf) {
> >>  			dsrc = meson_nfc_data_ptr(nand, i);
> >> @@ -390,6 +412,12 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
> >>  		memcpy(osrc, oobbuf, oob_len);
> >>  		oobbuf += oob_len;
> >>  	}
> >> +
> >> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
> >> +		  (nand->ecc.size + oob_len);
> >> +
> >> +	/* 'oobbuf' if already shifted to the start of unused area. */
> >> +	memcpy(oobtail, oobbuf, mtd->oobsize - nand->ecc.steps * oob_len);
> >>  }
> >>  
> >>  static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
> >> @@ -436,25 +464,12 @@ static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
> >>  {
> >>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >>  	__le64 *info;
> >> -	int i, count;
> >> -
> >> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> >> -		info = &meson_chip->info_buf[i];
> >> -		*info |= oob_buf[count];
> >> -		*info |= oob_buf[count + 1] << 8;
> >> -	}
> >> -}
> >> -
> >> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
> >> -{
> >> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >> -	__le64 *info;
> >> -	int i, count;
> >> +	int i;
> >>  
> >> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> >> +	for (i = 0; i < nand->ecc.steps; i++) {
> >>  		info = &meson_chip->info_buf[i];
> >> -		oob_buf[count] = *info;
> >> -		oob_buf[count + 1] = *info >> 8;
> >> +		/* Always ignore user bytes programming. */  
> > 
> > Why?  
> 
> I think comment message is wrong a little bit. Here "user bytes" are
> user bytes protected by ECC (e.g. location of these bytes differs from new
> OOB layout introduced by this patch). During page write this hardware
> always writes these bytes along with data. But, new OOB layout always ignores
> these 4 bytes, so set them to 0xFF always.

When performing page reads/writes, you need to take the data as it's
been provided. You may move the data around in the buffer provided to
the controller, so that it get the ECC data at the right location, and
you need of course to reorganize the data when reading as well, so that
the user sees XkiB of data + YB of OOB. That's all you need to do in
these helpers.

> > Also, maybe I should mention the helpers:
> > mtd_ooblayout_get_eccbytes and co
> > They are very useful to deal with OOB bytes. Everything seems extremely
> > hardcoded in this driver, while the user can tune read/write OOB
> > operations.  
> 
> Thanks for details, IIUC these helpers will work correctly with this driver.
> What means "tune" here?

The user can provide data for the OOB part and tell the mtd layer to
place the bytes in the ->oob_buf following different constraints:
- the user bytes are already when they need to be (MTD_OPS_PLACE_OOB
  and MTD_OPS_RAW)
- the user bytes are packed at the beginning of the buffer, please move
  them where they belong (MTD_OPS_AUTO_OOB).

So in the controller driver, what needs to be done is to make the
switch between the "data1+data2+oob1+oob2" layout into your
ECC controller's layout, eg "data1+oob1+data2+oob2".

Hence you should not need anything like that:
> >> +		*info |= 0xffff;

> >>  	}
> >>  }
> >>  
> >> @@ -698,18 +713,92 @@ static int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf,
> >>  	return meson_nfc_write_page_sub(nand, page, 1);
> >>  }
> >>  
> >> +static u32 meson_nfc_get_oob_bytes(struct nand_chip *nand)
> >> +{
> >> +	struct mtd_info *mtd = nand_to_mtd(nand);
> >> +
> >> +	return mtd->oobsize - nand->ecc.steps * NFC_OOB_PER_ECC(nand);
> >> +}
> >> +
> >> +static int __meson_nfc_write_oob(struct nand_chip *nand, int page, u8 *oob_buf)
> >> +{
> >> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >> +	struct mtd_info *mtd = nand_to_mtd(nand);
> >> +	u32 page_size = mtd->writesize + mtd->oobsize;
> >> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
> >> +	int ret;
> >> +
> >> +	if (!oob_bytes)
> >> +		return 0;
> >> +
> >> +	ret = nand_prog_page_begin_op(nand, page, 0, NULL, 0);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	memcpy(meson_chip->oob_buf, oob_buf + (mtd->oobsize - oob_bytes),
> >> +	       oob_bytes);
> >> +
> >> +	ret = nand_change_write_column_op(nand, page_size - oob_bytes,
> >> +					  meson_chip->oob_buf,
> >> +					  oob_bytes, false);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	return nand_prog_page_end_op(nand);
> >> +}
> >> +
> >> +static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
> >> +				u8 *oob_buf)
> >> +{
> >> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >> +	struct mtd_info *mtd = nand_to_mtd(nand);
> >> +	u32 oob_bytes;
> >> +	u32 page_size;
> >> +	int ret;
> >> +
> >> +	oob_bytes = meson_nfc_get_oob_bytes(nand);
> >> +
> >> +	if (!oob_bytes)
> >> +		return 0;
> >> +
> >> +	ret = nand_read_page_op(nand, page, 0, NULL, 0);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	page_size = mtd->writesize + mtd->oobsize;
> >> +
> >> +	ret = nand_change_read_column_op(nand, page_size - oob_bytes,
> >> +					 meson_chip->oob_buf,
> >> +					 oob_bytes, false);
> >> +
> >> +	if (!ret)
> >> +		memcpy(oob_buf + (mtd->oobsize - oob_bytes),
> >> +		       meson_chip->oob_buf,
> >> +		       oob_bytes);
> >> +
> >> +	return ret;
> >> +}
> >> +
> >>  static int meson_nfc_write_page_hwecc(struct nand_chip *nand,
> >>  				      const u8 *buf, int oob_required, int page)
> >>  {
> >>  	struct mtd_info *mtd = nand_to_mtd(nand);
> >>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >>  	u8 *oob_buf = nand->oob_poi;
> >> +	int ret;
> >>  
> >>  	memcpy(meson_chip->data_buf, buf, mtd->writesize);
> >>  	memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE);
> >>  	meson_nfc_set_user_byte(nand, oob_buf);
> >>  
> >> -	return meson_nfc_write_page_sub(nand, page, 0);
> >> +	ret = meson_nfc_write_page_sub(nand, page, 0);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	if (oob_required)
> >> +		ret = __meson_nfc_write_oob(nand, page, oob_buf);  
> > 
> > You should provide all the data including OOB bytes in a single write
> > call, otherwise you perform two writes on the same page, that's not
> > what this helper is expected to do.  
> 
> I see, so I need to check 'oob_required' here before programming page data?

Yes!

> If it is set -> place OOB data to DMA buffer and then call PAGEPROG once?

Exactly.

> 
> >   
> >> +
> >> +	return ret;
> >>  }
> >>  
> >>  static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
> >> @@ -783,7 +872,7 @@ static int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf,
> >>  	if (ret)
> >>  		return ret;
> >>  
> >> -	meson_nfc_get_data_oob(nand, buf, oob_buf);
> >> +	meson_nfc_get_data_oob(nand, buf, oob_required ? oob_buf : NULL);
> >>  
> >>  	return 0;
> >>  }
> >> @@ -803,12 +892,12 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
> >>  	if (ret)
> >>  		return ret;
> >>  
> >> -	meson_nfc_get_user_byte(nand, oob_buf);
> >>  	ret = meson_nfc_ecc_correct(nand, &bitflips, &correct_bitmap);
> >>  	if (ret == ECC_CHECK_RETURN_FF) {
> >>  		if (buf)
> >>  			memset(buf, 0xff, mtd->writesize);
> >>  		memset(oob_buf, 0xff, mtd->oobsize);
> >> +		return bitflips;  
> > 
> > That is something else => other fix => other patch?  
> 
> Idea of this 'return' is that when read fails, we return from this function
> without reading OOB below.

It seemed like the "return bitflips" thing was new, if it's just due to
the diff not being wide enough, then it's fine, otherwise if this is
something new, it should be in a dedicated patch.

> 
> >   
> >>  	} else if (ret < 0) {
> >>  		if ((nand->options & NAND_NEED_SCRAMBLING) || !buf) {
> >>  			mtd->ecc_stats.failed++;
> >> @@ -820,12 +909,14 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
> >>  
> >>  		for (i = 0; i < nand->ecc.steps ; i++) {
> >>  			u8 *data = buf + i * ecc->size;
> >> -			u8 *oob = nand->oob_poi + i * (ecc->bytes + 2);
> >> +			u8 *oob = nand->oob_poi + i * NFC_OOB_PER_ECC(nand);
> >>  
> >>  			if (correct_bitmap & BIT_ULL(i))
> >>  				continue;
> >> +
> >>  			ret = nand_check_erased_ecc_chunk(data,	ecc->size,
> >> -							  oob, ecc->bytes + 2,
> >> +							  oob,
> >> +							  NFC_OOB_PER_ECC(nand),
> >>  							  NULL, 0,
> >>  							  ecc->strength);
> >>  			if (ret < 0) {
> >> @@ -839,17 +930,30 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
> >>  		memcpy(buf, meson_chip->data_buf, mtd->writesize);
> >>  	}
> >>  
> >> +	if (oob_required)
> >> +		__meson_nfc_read_oob(nand, page, oob_buf);  
> > 
> > In the standalone "read_oob" hook, you have to send a READ0 command,
> > but not when you are in the read_page hook. It is a big waste of time.  
> 
> IIUC approach here must be exactly the same as in write? E.g. i need to
> send single READ0 and then fill provided OOB buffer if needed?

Yes.

For both reads and writes, the logic is:
- there are commands which are actually time consuming: there is
  something happening on the nand array which either reads or writes
  data to/from the internal sram.
- there are other commands which just change the "pointer" in the
  internal sram.

So you can basically say "I want to write into the sram at offset X,
then at offset Y" and when the sram has been filled you can commit the
write and that's the operation which should happen only once. In the
read path it's the opposite, you request a read from the NAND array into
the sram cache, and you can then request data randomly. Of course both
operations cannot be too random either, you need to follow the ECC
engine pattern which must be fed with X bytes of data and then will
produce the ECC bytes to write/compare.

> 
> >   
> >> +
> >>  	return bitflips;
> >>  }
> >>  
> >>  static int meson_nfc_read_oob_raw(struct nand_chip *nand, int page)
> >>  {
> >> -	return meson_nfc_read_page_raw(nand, NULL, 1, page);
> >> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
> >>  }
> >>  
> >>  static int meson_nfc_read_oob(struct nand_chip *nand, int page)
> >>  {
> >> -	return meson_nfc_read_page_hwecc(nand, NULL, 1, page);
> >> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
> >> +}
> >> +
> >> +static int meson_nfc_write_oob_raw(struct nand_chip *nand, int page)
> >> +{
> >> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
> >> +}
> >> +
> >> +static int meson_nfc_write_oob(struct nand_chip *nand, int page)
> >> +{
> >> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);  
> > 
> > Do we really need these indirections?  
> 
> Right, I think I can use only one function for OOB write in both ECC and raw modes.
> 
> >   
> >>  }
> >>  
> >>  static bool meson_nfc_is_buffer_dma_safe(const void *buffer)
> >> @@ -982,7 +1086,7 @@ static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
> >>  	if (section >= nand->ecc.steps)
> >>  		return -ERANGE;
> >>  
> >> -	oobregion->offset =  2 + (section * (2 + nand->ecc.bytes));
> >> +	oobregion->offset = NFC_USER_BYTES + section * NFC_OOB_PER_ECC(nand);  
> > 
> > The () are still needed around the '*'
> >   
> >>  	oobregion->length = nand->ecc.bytes;
> >>  
> >>  	return 0;
> >> @@ -992,12 +1096,16 @@ static int meson_ooblayout_free(struct mtd_info *mtd, int section,
> >>  				struct mtd_oob_region *oobregion)
> >>  {
> >>  	struct nand_chip *nand = mtd_to_nand(mtd);
> >> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
> >>  
> >>  	if (section >= nand->ecc.steps)
> >>  		return -ERANGE;
> >>  
> >> -	oobregion->offset = section * (2 + nand->ecc.bytes);
> >> -	oobregion->length = 2;
> >> +	/* Split rest of OOB area (not covered by ECC engine) per each
> >> +	 * ECC section. This will be OOB data available to user.
> >> +	 */
> >> +	oobregion->offset = (section + nand->ecc.steps) * NFC_OOB_PER_ECC(nand);
> >> +	oobregion->length = oob_bytes / nand->ecc.steps;
> >>  
> >>  	return 0;
> >>  }
> >> @@ -1184,6 +1292,9 @@ static int meson_nand_bch_mode(struct nand_chip *nand)
> >>  
> >>  static void meson_nand_detach_chip(struct nand_chip *nand)
> >>  {
> >> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >> +
> >> +	kfree(meson_chip->oob_buf);
> >>  	meson_nfc_free_buffer(nand);
> >>  }
> >>  
> >> @@ -1225,9 +1336,9 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
> >>  	nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
> >>  	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
> >>  	nand->ecc.write_page = meson_nfc_write_page_hwecc;
> >> -	nand->ecc.write_oob_raw = nand_write_oob_std;
> >> -	nand->ecc.write_oob = nand_write_oob_std;
> >>  
> >> +	nand->ecc.write_oob_raw = meson_nfc_write_oob_raw;
> >> +	nand->ecc.write_oob = meson_nfc_write_oob;  
> > 
> > Actually if you define the right OOB layouts, are these really needed
> > ?? I would expect the right bytes to be picked up by the default
> > implementation. I see nothing specific in your current implementation?  
> 
> You mean to use 'nand_write_oob_std()'? If so, I think it won't work,
> because it tries to write OOB data right after 'mtd->writesize', but
> page layout for this controller is like this:
> [       1024 bytes of data        ]
> [ 14 bytes ECC + 2 bytes for user ] <- with new layout we don't touch these 2 bytes
> [       1024 bytes of data        ]
> [ 14 bytes ECC + 2 bytes for user ] <- with new layout we don't touch these 2 bytes
> [      32 bytes of user bytes     ] <- we use there 32 bytes as free(user) bytes in OOB

Right.

> 
> So with 'mtd->writesize' of 2048 we won't get into last 32 bytes in the picture above.
> I'm not goot in NAND/MTD area, but I think that why i need special OOB access callbacks.

Ok.

> Also in previous patches @Liang said, that default OOB read/write functions won't work
> on this controller and it is wrong to use them in currently merged driver.
> 
> >   
> >>  	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
> >>  	nand->ecc.read_page = meson_nfc_read_page_hwecc;
> >>  	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
> >> @@ -1237,9 +1348,16 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
> >>  		dev_err(nfc->dev, "16bits bus width not supported");
> >>  		return -EINVAL;
> >>  	}
> >> +
> >> +	meson_chip->oob_buf = kmalloc(nand->ecc.bytes, GFP_KERNEL);  
> > 
> > devm_kmalloc?  
> 
> Ack
> 
> >   
> >> +	if (!meson_chip->oob_buf)
> >> +		return -ENOMEM;
> >> +
> >>  	ret = meson_chip_buffer_init(nand);
> >> -	if (ret)
> >> +	if (ret) {
> >> +		kfree(meson_chip->oob_buf);
> >>  		return -ENOMEM;
> >> +	}
> >>  
> >>  	return ret;
> >>  }  
> > 
> > 
> > Thanks,
> > Miquèl  
> 
> Thanks, Arseniy


Thanks,
Miquèl

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-26 17:03         ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-26 17:03 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

avkrasnov@sberdevices.ru wrote on Tue, 23 May 2023 20:17:14 +0300:

> Hello Miquel! Thanks for detailed review!
> 
> On 22.05.2023 18:33, Miquel Raynal wrote:
> > Hi Arseniy,
> > 
> > AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:36 +0300:
> > 
> > The title should perhaps be "only expose unprotected user OOB bytes".
> >   
> >> This moves free bytes of OOB to non-protected ECC area. It is needed to
> >> make JFFS2 works correctly with this NAND controller. Problem fires when
> >> JFFS2 driver writes cleanmarker to some page and later it tries to write
> >> to this page - write will be done successfully, but after that such page
> >> becomes unreadable due to invalid ECC codes. This happens because second
> >> write needs to update ECC codes, but it is impossible to do it correctly
> >> without block erase. So idea of this patch is to  
> > 
> > "... is to use the unprotected OOB area to store the cleanmarkers, so
> > that they can be written by the filesystem without caring much about
> > the page being empty or not: the ECC codes will not be written anyway."
> > ?  
> 
> Ok
> 
> > 
> > JFFS2 is only useful on tiny NAND devices, where UBI does not fit,
> > which are usually true SLC flashes, with the capability of writing
> > a page with empty (0xFF) data, and still be able to write actual data
> > to it later in a second write.  
> 
> You mean to include text above to commit message also?

Yes, I believe this deserves to be in the commit message as well :)

> 
> >   
> >> split accesses to OOB
> >> free bytes and data on each page - now both of them does not depends on
> >> each other.
> >>
> >> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> >> ---
> >>  drivers/mtd/nand/raw/meson_nand.c | 192 ++++++++++++++++++++++++------
> >>  1 file changed, 155 insertions(+), 37 deletions(-)
> >>
> >> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> >> index 2f4d8c84186b..8526a6b87720 100644
> >> --- a/drivers/mtd/nand/raw/meson_nand.c
> >> +++ b/drivers/mtd/nand/raw/meson_nand.c
> >> @@ -108,6 +108,9 @@
> >>  
> >>  #define PER_INFO_BYTE		8
> >>  
> >> +#define NFC_USER_BYTES		2
> >> +#define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
> >> +
> >>  struct meson_nfc_nand_chip {
> >>  	struct list_head node;
> >>  	struct nand_chip nand;
> >> @@ -122,6 +125,7 @@ struct meson_nfc_nand_chip {
> >>  	u8 *data_buf;
> >>  	__le64 *info_buf;
> >>  	u32 nsels;
> >> +	u8 *oob_buf;
> >>  	u8 sels[];
> >>  };
> >>  
> >> @@ -338,7 +342,7 @@ static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
> >>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >>  	int len;
> >>  
> >> -	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
> >> +	len = nand->ecc.size * (i + 1) + NFC_OOB_PER_ECC(nand) * i;  
> > 
> > This...
> >   
> >>  
> >>  	return meson_chip->data_buf + len;
> >>  }
> >> @@ -349,7 +353,7 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
> >>  	int len, temp;
> >>  
> >>  	temp = nand->ecc.size + nand->ecc.bytes;
> >> -	len = (temp + 2) * i;
> >> +	len = (temp + NFC_USER_BYTES) * i;  
> > 
> > ... and this (same below)
> > 
> > are purely cosmetic, should be in a patch aside.
> >   
> 
> Ack, i'll move cosmetic updates to separate patch.
> 
> >>  
> >>  	return meson_chip->data_buf + len;
> >>  }
> >> @@ -357,29 +361,47 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
> >>  static void meson_nfc_get_data_oob(struct nand_chip *nand,
> >>  				   u8 *buf, u8 *oobbuf)
> >>  {
> >> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >> +	struct mtd_info *mtd = nand_to_mtd(nand);
> >>  	int i, oob_len = 0;
> >>  	u8 *dsrc, *osrc;
> >> +	u8 *oobtail;
> >>  
> >> -	oob_len = nand->ecc.bytes + 2;
> >> +	oob_len = NFC_OOB_PER_ECC(nand);
> >>  	for (i = 0; i < nand->ecc.steps; i++) {
> >>  		if (buf) {
> >>  			dsrc = meson_nfc_data_ptr(nand, i);
> >>  			memcpy(buf, dsrc, nand->ecc.size);
> >>  			buf += nand->ecc.size;
> >>  		}
> >> -		osrc = meson_nfc_oob_ptr(nand, i);
> >> -		memcpy(oobbuf, osrc, oob_len);
> >> -		oobbuf += oob_len;
> >> +
> >> +		if (oobbuf) {
> >> +			osrc = meson_nfc_oob_ptr(nand, i);
> >> +			memcpy(oobbuf, osrc, oob_len);
> >> +			oobbuf += oob_len;
> >> +		}
> >>  	}
> >> +
> >> +	if (!oobbuf)
> >> +		return;  
> > 
> > The whole "if (oobbuf)" logic is nice to have, but should as well be in
> > a dedicated patch.  
> 
> Sorry, You mean that this logic implements read of ECC codes? And not
> related to OOB layout update in this patch?

You need to make cosmetic changes in a first patch (or even in several
patches if they are not related), then in another patch you might make
additions like a better handling of the OOB, and any change in the
layout might come last. I just want to split the patch to make
understandable atomic changes (also easier to review).

1 atomic change == 1 patch

> >   
> >> +
> >> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
> >> +		  (nand->ecc.size + oob_len);
> >> +
> >> +	/* 'oobbuf' if already shifted to the start of unused area. */  
> > 
> > 		    is?
> > s/unused/user/? I'm not sure I get the comment.  
> 
> Yes, not clear comment.
> 
> >   
> >> +	memcpy(oobbuf, oobtail, mtd->oobsize - nand->ecc.steps * oob_len);
> >>  }
> >>  
> >>  static void meson_nfc_set_data_oob(struct nand_chip *nand,
> >>  				   const u8 *buf, u8 *oobbuf)
> >>  {
> >> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >> +	struct mtd_info *mtd = nand_to_mtd(nand);
> >>  	int i, oob_len = 0;
> >>  	u8 *dsrc, *osrc;
> >> +	u8 *oobtail;
> >>  
> >> -	oob_len = nand->ecc.bytes + 2;
> >> +	oob_len = NFC_OOB_PER_ECC(nand);
> >>  	for (i = 0; i < nand->ecc.steps; i++) {
> >>  		if (buf) {
> >>  			dsrc = meson_nfc_data_ptr(nand, i);
> >> @@ -390,6 +412,12 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
> >>  		memcpy(osrc, oobbuf, oob_len);
> >>  		oobbuf += oob_len;
> >>  	}
> >> +
> >> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
> >> +		  (nand->ecc.size + oob_len);
> >> +
> >> +	/* 'oobbuf' if already shifted to the start of unused area. */
> >> +	memcpy(oobtail, oobbuf, mtd->oobsize - nand->ecc.steps * oob_len);
> >>  }
> >>  
> >>  static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
> >> @@ -436,25 +464,12 @@ static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
> >>  {
> >>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >>  	__le64 *info;
> >> -	int i, count;
> >> -
> >> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> >> -		info = &meson_chip->info_buf[i];
> >> -		*info |= oob_buf[count];
> >> -		*info |= oob_buf[count + 1] << 8;
> >> -	}
> >> -}
> >> -
> >> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
> >> -{
> >> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >> -	__le64 *info;
> >> -	int i, count;
> >> +	int i;
> >>  
> >> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> >> +	for (i = 0; i < nand->ecc.steps; i++) {
> >>  		info = &meson_chip->info_buf[i];
> >> -		oob_buf[count] = *info;
> >> -		oob_buf[count + 1] = *info >> 8;
> >> +		/* Always ignore user bytes programming. */  
> > 
> > Why?  
> 
> I think comment message is wrong a little bit. Here "user bytes" are
> user bytes protected by ECC (e.g. location of these bytes differs from new
> OOB layout introduced by this patch). During page write this hardware
> always writes these bytes along with data. But, new OOB layout always ignores
> these 4 bytes, so set them to 0xFF always.

When performing page reads/writes, you need to take the data as it's
been provided. You may move the data around in the buffer provided to
the controller, so that it get the ECC data at the right location, and
you need of course to reorganize the data when reading as well, so that
the user sees XkiB of data + YB of OOB. That's all you need to do in
these helpers.

> > Also, maybe I should mention the helpers:
> > mtd_ooblayout_get_eccbytes and co
> > They are very useful to deal with OOB bytes. Everything seems extremely
> > hardcoded in this driver, while the user can tune read/write OOB
> > operations.  
> 
> Thanks for details, IIUC these helpers will work correctly with this driver.
> What means "tune" here?

The user can provide data for the OOB part and tell the mtd layer to
place the bytes in the ->oob_buf following different constraints:
- the user bytes are already when they need to be (MTD_OPS_PLACE_OOB
  and MTD_OPS_RAW)
- the user bytes are packed at the beginning of the buffer, please move
  them where they belong (MTD_OPS_AUTO_OOB).

So in the controller driver, what needs to be done is to make the
switch between the "data1+data2+oob1+oob2" layout into your
ECC controller's layout, eg "data1+oob1+data2+oob2".

Hence you should not need anything like that:
> >> +		*info |= 0xffff;

> >>  	}
> >>  }
> >>  
> >> @@ -698,18 +713,92 @@ static int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf,
> >>  	return meson_nfc_write_page_sub(nand, page, 1);
> >>  }
> >>  
> >> +static u32 meson_nfc_get_oob_bytes(struct nand_chip *nand)
> >> +{
> >> +	struct mtd_info *mtd = nand_to_mtd(nand);
> >> +
> >> +	return mtd->oobsize - nand->ecc.steps * NFC_OOB_PER_ECC(nand);
> >> +}
> >> +
> >> +static int __meson_nfc_write_oob(struct nand_chip *nand, int page, u8 *oob_buf)
> >> +{
> >> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >> +	struct mtd_info *mtd = nand_to_mtd(nand);
> >> +	u32 page_size = mtd->writesize + mtd->oobsize;
> >> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
> >> +	int ret;
> >> +
> >> +	if (!oob_bytes)
> >> +		return 0;
> >> +
> >> +	ret = nand_prog_page_begin_op(nand, page, 0, NULL, 0);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	memcpy(meson_chip->oob_buf, oob_buf + (mtd->oobsize - oob_bytes),
> >> +	       oob_bytes);
> >> +
> >> +	ret = nand_change_write_column_op(nand, page_size - oob_bytes,
> >> +					  meson_chip->oob_buf,
> >> +					  oob_bytes, false);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	return nand_prog_page_end_op(nand);
> >> +}
> >> +
> >> +static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
> >> +				u8 *oob_buf)
> >> +{
> >> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >> +	struct mtd_info *mtd = nand_to_mtd(nand);
> >> +	u32 oob_bytes;
> >> +	u32 page_size;
> >> +	int ret;
> >> +
> >> +	oob_bytes = meson_nfc_get_oob_bytes(nand);
> >> +
> >> +	if (!oob_bytes)
> >> +		return 0;
> >> +
> >> +	ret = nand_read_page_op(nand, page, 0, NULL, 0);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	page_size = mtd->writesize + mtd->oobsize;
> >> +
> >> +	ret = nand_change_read_column_op(nand, page_size - oob_bytes,
> >> +					 meson_chip->oob_buf,
> >> +					 oob_bytes, false);
> >> +
> >> +	if (!ret)
> >> +		memcpy(oob_buf + (mtd->oobsize - oob_bytes),
> >> +		       meson_chip->oob_buf,
> >> +		       oob_bytes);
> >> +
> >> +	return ret;
> >> +}
> >> +
> >>  static int meson_nfc_write_page_hwecc(struct nand_chip *nand,
> >>  				      const u8 *buf, int oob_required, int page)
> >>  {
> >>  	struct mtd_info *mtd = nand_to_mtd(nand);
> >>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >>  	u8 *oob_buf = nand->oob_poi;
> >> +	int ret;
> >>  
> >>  	memcpy(meson_chip->data_buf, buf, mtd->writesize);
> >>  	memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE);
> >>  	meson_nfc_set_user_byte(nand, oob_buf);
> >>  
> >> -	return meson_nfc_write_page_sub(nand, page, 0);
> >> +	ret = meson_nfc_write_page_sub(nand, page, 0);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	if (oob_required)
> >> +		ret = __meson_nfc_write_oob(nand, page, oob_buf);  
> > 
> > You should provide all the data including OOB bytes in a single write
> > call, otherwise you perform two writes on the same page, that's not
> > what this helper is expected to do.  
> 
> I see, so I need to check 'oob_required' here before programming page data?

Yes!

> If it is set -> place OOB data to DMA buffer and then call PAGEPROG once?

Exactly.

> 
> >   
> >> +
> >> +	return ret;
> >>  }
> >>  
> >>  static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
> >> @@ -783,7 +872,7 @@ static int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf,
> >>  	if (ret)
> >>  		return ret;
> >>  
> >> -	meson_nfc_get_data_oob(nand, buf, oob_buf);
> >> +	meson_nfc_get_data_oob(nand, buf, oob_required ? oob_buf : NULL);
> >>  
> >>  	return 0;
> >>  }
> >> @@ -803,12 +892,12 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
> >>  	if (ret)
> >>  		return ret;
> >>  
> >> -	meson_nfc_get_user_byte(nand, oob_buf);
> >>  	ret = meson_nfc_ecc_correct(nand, &bitflips, &correct_bitmap);
> >>  	if (ret == ECC_CHECK_RETURN_FF) {
> >>  		if (buf)
> >>  			memset(buf, 0xff, mtd->writesize);
> >>  		memset(oob_buf, 0xff, mtd->oobsize);
> >> +		return bitflips;  
> > 
> > That is something else => other fix => other patch?  
> 
> Idea of this 'return' is that when read fails, we return from this function
> without reading OOB below.

It seemed like the "return bitflips" thing was new, if it's just due to
the diff not being wide enough, then it's fine, otherwise if this is
something new, it should be in a dedicated patch.

> 
> >   
> >>  	} else if (ret < 0) {
> >>  		if ((nand->options & NAND_NEED_SCRAMBLING) || !buf) {
> >>  			mtd->ecc_stats.failed++;
> >> @@ -820,12 +909,14 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
> >>  
> >>  		for (i = 0; i < nand->ecc.steps ; i++) {
> >>  			u8 *data = buf + i * ecc->size;
> >> -			u8 *oob = nand->oob_poi + i * (ecc->bytes + 2);
> >> +			u8 *oob = nand->oob_poi + i * NFC_OOB_PER_ECC(nand);
> >>  
> >>  			if (correct_bitmap & BIT_ULL(i))
> >>  				continue;
> >> +
> >>  			ret = nand_check_erased_ecc_chunk(data,	ecc->size,
> >> -							  oob, ecc->bytes + 2,
> >> +							  oob,
> >> +							  NFC_OOB_PER_ECC(nand),
> >>  							  NULL, 0,
> >>  							  ecc->strength);
> >>  			if (ret < 0) {
> >> @@ -839,17 +930,30 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
> >>  		memcpy(buf, meson_chip->data_buf, mtd->writesize);
> >>  	}
> >>  
> >> +	if (oob_required)
> >> +		__meson_nfc_read_oob(nand, page, oob_buf);  
> > 
> > In the standalone "read_oob" hook, you have to send a READ0 command,
> > but not when you are in the read_page hook. It is a big waste of time.  
> 
> IIUC approach here must be exactly the same as in write? E.g. i need to
> send single READ0 and then fill provided OOB buffer if needed?

Yes.

For both reads and writes, the logic is:
- there are commands which are actually time consuming: there is
  something happening on the nand array which either reads or writes
  data to/from the internal sram.
- there are other commands which just change the "pointer" in the
  internal sram.

So you can basically say "I want to write into the sram at offset X,
then at offset Y" and when the sram has been filled you can commit the
write and that's the operation which should happen only once. In the
read path it's the opposite, you request a read from the NAND array into
the sram cache, and you can then request data randomly. Of course both
operations cannot be too random either, you need to follow the ECC
engine pattern which must be fed with X bytes of data and then will
produce the ECC bytes to write/compare.

> 
> >   
> >> +
> >>  	return bitflips;
> >>  }
> >>  
> >>  static int meson_nfc_read_oob_raw(struct nand_chip *nand, int page)
> >>  {
> >> -	return meson_nfc_read_page_raw(nand, NULL, 1, page);
> >> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
> >>  }
> >>  
> >>  static int meson_nfc_read_oob(struct nand_chip *nand, int page)
> >>  {
> >> -	return meson_nfc_read_page_hwecc(nand, NULL, 1, page);
> >> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
> >> +}
> >> +
> >> +static int meson_nfc_write_oob_raw(struct nand_chip *nand, int page)
> >> +{
> >> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
> >> +}
> >> +
> >> +static int meson_nfc_write_oob(struct nand_chip *nand, int page)
> >> +{
> >> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);  
> > 
> > Do we really need these indirections?  
> 
> Right, I think I can use only one function for OOB write in both ECC and raw modes.
> 
> >   
> >>  }
> >>  
> >>  static bool meson_nfc_is_buffer_dma_safe(const void *buffer)
> >> @@ -982,7 +1086,7 @@ static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
> >>  	if (section >= nand->ecc.steps)
> >>  		return -ERANGE;
> >>  
> >> -	oobregion->offset =  2 + (section * (2 + nand->ecc.bytes));
> >> +	oobregion->offset = NFC_USER_BYTES + section * NFC_OOB_PER_ECC(nand);  
> > 
> > The () are still needed around the '*'
> >   
> >>  	oobregion->length = nand->ecc.bytes;
> >>  
> >>  	return 0;
> >> @@ -992,12 +1096,16 @@ static int meson_ooblayout_free(struct mtd_info *mtd, int section,
> >>  				struct mtd_oob_region *oobregion)
> >>  {
> >>  	struct nand_chip *nand = mtd_to_nand(mtd);
> >> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
> >>  
> >>  	if (section >= nand->ecc.steps)
> >>  		return -ERANGE;
> >>  
> >> -	oobregion->offset = section * (2 + nand->ecc.bytes);
> >> -	oobregion->length = 2;
> >> +	/* Split rest of OOB area (not covered by ECC engine) per each
> >> +	 * ECC section. This will be OOB data available to user.
> >> +	 */
> >> +	oobregion->offset = (section + nand->ecc.steps) * NFC_OOB_PER_ECC(nand);
> >> +	oobregion->length = oob_bytes / nand->ecc.steps;
> >>  
> >>  	return 0;
> >>  }
> >> @@ -1184,6 +1292,9 @@ static int meson_nand_bch_mode(struct nand_chip *nand)
> >>  
> >>  static void meson_nand_detach_chip(struct nand_chip *nand)
> >>  {
> >> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >> +
> >> +	kfree(meson_chip->oob_buf);
> >>  	meson_nfc_free_buffer(nand);
> >>  }
> >>  
> >> @@ -1225,9 +1336,9 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
> >>  	nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
> >>  	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
> >>  	nand->ecc.write_page = meson_nfc_write_page_hwecc;
> >> -	nand->ecc.write_oob_raw = nand_write_oob_std;
> >> -	nand->ecc.write_oob = nand_write_oob_std;
> >>  
> >> +	nand->ecc.write_oob_raw = meson_nfc_write_oob_raw;
> >> +	nand->ecc.write_oob = meson_nfc_write_oob;  
> > 
> > Actually if you define the right OOB layouts, are these really needed
> > ?? I would expect the right bytes to be picked up by the default
> > implementation. I see nothing specific in your current implementation?  
> 
> You mean to use 'nand_write_oob_std()'? If so, I think it won't work,
> because it tries to write OOB data right after 'mtd->writesize', but
> page layout for this controller is like this:
> [       1024 bytes of data        ]
> [ 14 bytes ECC + 2 bytes for user ] <- with new layout we don't touch these 2 bytes
> [       1024 bytes of data        ]
> [ 14 bytes ECC + 2 bytes for user ] <- with new layout we don't touch these 2 bytes
> [      32 bytes of user bytes     ] <- we use there 32 bytes as free(user) bytes in OOB

Right.

> 
> So with 'mtd->writesize' of 2048 we won't get into last 32 bytes in the picture above.
> I'm not goot in NAND/MTD area, but I think that why i need special OOB access callbacks.

Ok.

> Also in previous patches @Liang said, that default OOB read/write functions won't work
> on this controller and it is wrong to use them in currently merged driver.
> 
> >   
> >>  	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
> >>  	nand->ecc.read_page = meson_nfc_read_page_hwecc;
> >>  	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
> >> @@ -1237,9 +1348,16 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
> >>  		dev_err(nfc->dev, "16bits bus width not supported");
> >>  		return -EINVAL;
> >>  	}
> >> +
> >> +	meson_chip->oob_buf = kmalloc(nand->ecc.bytes, GFP_KERNEL);  
> > 
> > devm_kmalloc?  
> 
> Ack
> 
> >   
> >> +	if (!meson_chip->oob_buf)
> >> +		return -ENOMEM;
> >> +
> >>  	ret = meson_chip_buffer_init(nand);
> >> -	if (ret)
> >> +	if (ret) {
> >> +		kfree(meson_chip->oob_buf);
> >>  		return -ENOMEM;
> >> +	}
> >>  
> >>  	return ret;
> >>  }  
> > 
> > 
> > Thanks,
> > Miquèl  
> 
> Thanks, Arseniy


Thanks,
Miquèl

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v4 3/5] mtd: rawnand: meson: always read whole OOB bytes
  2023-05-23 17:27       ` Arseniy Krasnov
  (?)
  (?)
@ 2023-05-26 17:09         ` Miquel Raynal
  -1 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-26 17:09 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Jianxin Pan, Yixun Lan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

avkrasnov@sberdevices.ru wrote on Tue, 23 May 2023 20:27:35 +0300:

> On 22.05.2023 18:38, Miquel Raynal wrote:
> > Hi Arseniy,
> > 
> > AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:37 +0300:
> >   
> >> This changes size of read access to OOB area by reading all bytes of
> >> OOB (free bytes + ECC engine bytes).  
> > 
> > This is normally up to the user (user in your case == jffs2). The
> > controller driver should expose a number of user accessible bytes and
> > then when users want the OOB area, they should access it entirely. On
> > top of that read, they can extract (or "write only") the user bytes.  
> 
> Sorry, I didn't get it. If driver exposes N bytes of user accessible bytes,
> I must always return whole OOB yes? E.g. N + rest of OOB

Yes. At the NAND controller level, you get asked for either a page of
data (sometimes a subpage, but whatever), and/or the oob area. You need
to provide what is requested, no more, no less. The upper layers will
trim down what's uneeded and extract the bytes they want.

> >> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> >> ---
> >>  drivers/mtd/nand/raw/meson_nand.c | 24 ++++++++++++++++++++++++
> >>  1 file changed, 24 insertions(+)
> >>
> >> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> >> index 8526a6b87720..a31106c943d7 100644
> >> --- a/drivers/mtd/nand/raw/meson_nand.c
> >> +++ b/drivers/mtd/nand/raw/meson_nand.c
> >> @@ -755,6 +755,30 @@ static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
> >>  	u32 oob_bytes;
> >>  	u32 page_size;
> >>  	int ret;
> >> +	int i;
> >> +
> >> +	/* Read ECC codes and user bytes. */
> >> +	for (i = 0; i < nand->ecc.steps; i++) {
> >> +		u32 ecc_offs = nand->ecc.size * (i + 1) +
> >> +			       NFC_OOB_PER_ECC(nand) * i;
> >> +
> >> +		ret = nand_read_page_op(nand, page, 0, NULL, 0);
> >> +		if (ret)
> >> +			return ret;
> >> +
> >> +		/* Use temporary buffer, because 'nand_change_read_column_op()'
> >> +		 * seems work with some alignment, so we can't read data to
> >> +		 * 'oob_buf' directly.  
> > 
> > DMA?  
> 
> Yes I guess, this address passed to exec_op code and used as DMA.

If your controller uses DMA on exec_op accesses, then yes. Exec_op
reads/writes are usually small enough (or not time sensitive at all if
they are bigger) so it's not required to use DMA there. Anyhow, oob_buf
is suitable for DMA purposes, so I'm a bit surprised you need a bounce
buffer, if that's the only reason. Maybe you need a bounce buffer to
reorganize the data. That would be a much better explanation.

> >> +		 */
> >> +		ret = nand_change_read_column_op(nand, ecc_offs, meson_chip->oob_buf,
> >> +						 NFC_OOB_PER_ECC(nand), false);
> >> +		if (ret)
> >> +			return ret;
> >> +
> >> +		memcpy(oob_buf + i * NFC_OOB_PER_ECC(nand),
> >> +		       meson_chip->oob_buf,
> >> +		       NFC_OOB_PER_ECC(nand));
> >> +	}
> >>  
> >>  	oob_bytes = meson_nfc_get_oob_bytes(nand);
> >>    
> > 
> > 
> > Thanks,
> > Miquèl  
> 
> Thanks, Arseniy


Thanks,
Miquèl

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

* Re: [PATCH v4 3/5] mtd: rawnand: meson: always read whole OOB bytes
@ 2023-05-26 17:09         ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-26 17:09 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Jianxin Pan, Yixun Lan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

avkrasnov@sberdevices.ru wrote on Tue, 23 May 2023 20:27:35 +0300:

> On 22.05.2023 18:38, Miquel Raynal wrote:
> > Hi Arseniy,
> > 
> > AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:37 +0300:
> >   
> >> This changes size of read access to OOB area by reading all bytes of
> >> OOB (free bytes + ECC engine bytes).  
> > 
> > This is normally up to the user (user in your case == jffs2). The
> > controller driver should expose a number of user accessible bytes and
> > then when users want the OOB area, they should access it entirely. On
> > top of that read, they can extract (or "write only") the user bytes.  
> 
> Sorry, I didn't get it. If driver exposes N bytes of user accessible bytes,
> I must always return whole OOB yes? E.g. N + rest of OOB

Yes. At the NAND controller level, you get asked for either a page of
data (sometimes a subpage, but whatever), and/or the oob area. You need
to provide what is requested, no more, no less. The upper layers will
trim down what's uneeded and extract the bytes they want.

> >> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> >> ---
> >>  drivers/mtd/nand/raw/meson_nand.c | 24 ++++++++++++++++++++++++
> >>  1 file changed, 24 insertions(+)
> >>
> >> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> >> index 8526a6b87720..a31106c943d7 100644
> >> --- a/drivers/mtd/nand/raw/meson_nand.c
> >> +++ b/drivers/mtd/nand/raw/meson_nand.c
> >> @@ -755,6 +755,30 @@ static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
> >>  	u32 oob_bytes;
> >>  	u32 page_size;
> >>  	int ret;
> >> +	int i;
> >> +
> >> +	/* Read ECC codes and user bytes. */
> >> +	for (i = 0; i < nand->ecc.steps; i++) {
> >> +		u32 ecc_offs = nand->ecc.size * (i + 1) +
> >> +			       NFC_OOB_PER_ECC(nand) * i;
> >> +
> >> +		ret = nand_read_page_op(nand, page, 0, NULL, 0);
> >> +		if (ret)
> >> +			return ret;
> >> +
> >> +		/* Use temporary buffer, because 'nand_change_read_column_op()'
> >> +		 * seems work with some alignment, so we can't read data to
> >> +		 * 'oob_buf' directly.  
> > 
> > DMA?  
> 
> Yes I guess, this address passed to exec_op code and used as DMA.

If your controller uses DMA on exec_op accesses, then yes. Exec_op
reads/writes are usually small enough (or not time sensitive at all if
they are bigger) so it's not required to use DMA there. Anyhow, oob_buf
is suitable for DMA purposes, so I'm a bit surprised you need a bounce
buffer, if that's the only reason. Maybe you need a bounce buffer to
reorganize the data. That would be a much better explanation.

> >> +		 */
> >> +		ret = nand_change_read_column_op(nand, ecc_offs, meson_chip->oob_buf,
> >> +						 NFC_OOB_PER_ECC(nand), false);
> >> +		if (ret)
> >> +			return ret;
> >> +
> >> +		memcpy(oob_buf + i * NFC_OOB_PER_ECC(nand),
> >> +		       meson_chip->oob_buf,
> >> +		       NFC_OOB_PER_ECC(nand));
> >> +	}
> >>  
> >>  	oob_bytes = meson_nfc_get_oob_bytes(nand);
> >>    
> > 
> > 
> > Thanks,
> > Miquèl  
> 
> Thanks, Arseniy


Thanks,
Miquèl

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

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

* Re: [PATCH v4 3/5] mtd: rawnand: meson: always read whole OOB bytes
@ 2023-05-26 17:09         ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-26 17:09 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Jianxin Pan, Yixun Lan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

avkrasnov@sberdevices.ru wrote on Tue, 23 May 2023 20:27:35 +0300:

> On 22.05.2023 18:38, Miquel Raynal wrote:
> > Hi Arseniy,
> > 
> > AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:37 +0300:
> >   
> >> This changes size of read access to OOB area by reading all bytes of
> >> OOB (free bytes + ECC engine bytes).  
> > 
> > This is normally up to the user (user in your case == jffs2). The
> > controller driver should expose a number of user accessible bytes and
> > then when users want the OOB area, they should access it entirely. On
> > top of that read, they can extract (or "write only") the user bytes.  
> 
> Sorry, I didn't get it. If driver exposes N bytes of user accessible bytes,
> I must always return whole OOB yes? E.g. N + rest of OOB

Yes. At the NAND controller level, you get asked for either a page of
data (sometimes a subpage, but whatever), and/or the oob area. You need
to provide what is requested, no more, no less. The upper layers will
trim down what's uneeded and extract the bytes they want.

> >> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> >> ---
> >>  drivers/mtd/nand/raw/meson_nand.c | 24 ++++++++++++++++++++++++
> >>  1 file changed, 24 insertions(+)
> >>
> >> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> >> index 8526a6b87720..a31106c943d7 100644
> >> --- a/drivers/mtd/nand/raw/meson_nand.c
> >> +++ b/drivers/mtd/nand/raw/meson_nand.c
> >> @@ -755,6 +755,30 @@ static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
> >>  	u32 oob_bytes;
> >>  	u32 page_size;
> >>  	int ret;
> >> +	int i;
> >> +
> >> +	/* Read ECC codes and user bytes. */
> >> +	for (i = 0; i < nand->ecc.steps; i++) {
> >> +		u32 ecc_offs = nand->ecc.size * (i + 1) +
> >> +			       NFC_OOB_PER_ECC(nand) * i;
> >> +
> >> +		ret = nand_read_page_op(nand, page, 0, NULL, 0);
> >> +		if (ret)
> >> +			return ret;
> >> +
> >> +		/* Use temporary buffer, because 'nand_change_read_column_op()'
> >> +		 * seems work with some alignment, so we can't read data to
> >> +		 * 'oob_buf' directly.  
> > 
> > DMA?  
> 
> Yes I guess, this address passed to exec_op code and used as DMA.

If your controller uses DMA on exec_op accesses, then yes. Exec_op
reads/writes are usually small enough (or not time sensitive at all if
they are bigger) so it's not required to use DMA there. Anyhow, oob_buf
is suitable for DMA purposes, so I'm a bit surprised you need a bounce
buffer, if that's the only reason. Maybe you need a bounce buffer to
reorganize the data. That would be a much better explanation.

> >> +		 */
> >> +		ret = nand_change_read_column_op(nand, ecc_offs, meson_chip->oob_buf,
> >> +						 NFC_OOB_PER_ECC(nand), false);
> >> +		if (ret)
> >> +			return ret;
> >> +
> >> +		memcpy(oob_buf + i * NFC_OOB_PER_ECC(nand),
> >> +		       meson_chip->oob_buf,
> >> +		       NFC_OOB_PER_ECC(nand));
> >> +	}
> >>  
> >>  	oob_bytes = meson_nfc_get_oob_bytes(nand);
> >>    
> > 
> > 
> > Thanks,
> > Miquèl  
> 
> Thanks, Arseniy


Thanks,
Miquèl

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* Re: [PATCH v4 3/5] mtd: rawnand: meson: always read whole OOB bytes
@ 2023-05-26 17:09         ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-26 17:09 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Jianxin Pan, Yixun Lan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

avkrasnov@sberdevices.ru wrote on Tue, 23 May 2023 20:27:35 +0300:

> On 22.05.2023 18:38, Miquel Raynal wrote:
> > Hi Arseniy,
> > 
> > AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:37 +0300:
> >   
> >> This changes size of read access to OOB area by reading all bytes of
> >> OOB (free bytes + ECC engine bytes).  
> > 
> > This is normally up to the user (user in your case == jffs2). The
> > controller driver should expose a number of user accessible bytes and
> > then when users want the OOB area, they should access it entirely. On
> > top of that read, they can extract (or "write only") the user bytes.  
> 
> Sorry, I didn't get it. If driver exposes N bytes of user accessible bytes,
> I must always return whole OOB yes? E.g. N + rest of OOB

Yes. At the NAND controller level, you get asked for either a page of
data (sometimes a subpage, but whatever), and/or the oob area. You need
to provide what is requested, no more, no less. The upper layers will
trim down what's uneeded and extract the bytes they want.

> >> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
> >> ---
> >>  drivers/mtd/nand/raw/meson_nand.c | 24 ++++++++++++++++++++++++
> >>  1 file changed, 24 insertions(+)
> >>
> >> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
> >> index 8526a6b87720..a31106c943d7 100644
> >> --- a/drivers/mtd/nand/raw/meson_nand.c
> >> +++ b/drivers/mtd/nand/raw/meson_nand.c
> >> @@ -755,6 +755,30 @@ static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
> >>  	u32 oob_bytes;
> >>  	u32 page_size;
> >>  	int ret;
> >> +	int i;
> >> +
> >> +	/* Read ECC codes and user bytes. */
> >> +	for (i = 0; i < nand->ecc.steps; i++) {
> >> +		u32 ecc_offs = nand->ecc.size * (i + 1) +
> >> +			       NFC_OOB_PER_ECC(nand) * i;
> >> +
> >> +		ret = nand_read_page_op(nand, page, 0, NULL, 0);
> >> +		if (ret)
> >> +			return ret;
> >> +
> >> +		/* Use temporary buffer, because 'nand_change_read_column_op()'
> >> +		 * seems work with some alignment, so we can't read data to
> >> +		 * 'oob_buf' directly.  
> > 
> > DMA?  
> 
> Yes I guess, this address passed to exec_op code and used as DMA.

If your controller uses DMA on exec_op accesses, then yes. Exec_op
reads/writes are usually small enough (or not time sensitive at all if
they are bigger) so it's not required to use DMA there. Anyhow, oob_buf
is suitable for DMA purposes, so I'm a bit surprised you need a bounce
buffer, if that's the only reason. Maybe you need a bounce buffer to
reorganize the data. That would be a much better explanation.

> >> +		 */
> >> +		ret = nand_change_read_column_op(nand, ecc_offs, meson_chip->oob_buf,
> >> +						 NFC_OOB_PER_ECC(nand), false);
> >> +		if (ret)
> >> +			return ret;
> >> +
> >> +		memcpy(oob_buf + i * NFC_OOB_PER_ECC(nand),
> >> +		       meson_chip->oob_buf,
> >> +		       NFC_OOB_PER_ECC(nand));
> >> +	}
> >>  
> >>  	oob_bytes = meson_nfc_get_oob_bytes(nand);
> >>    
> > 
> > 
> > Thanks,
> > Miquèl  
> 
> Thanks, Arseniy


Thanks,
Miquèl

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
  2023-05-24  9:05         ` Arseniy Krasnov
  (?)
  (?)
@ 2023-05-26 17:22           ` Miquel Raynal
  -1 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-26 17:22 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

avkrasnov@sberdevices.ru wrote on Wed, 24 May 2023 12:05:47 +0300:

> On 23.05.2023 12:12, Arseniy Krasnov wrote:
> > Hello Miquel, Liang
> > 
> > On 22.05.2023 18:05, Miquel Raynal wrote:  
> >> Hi Arseniy,
> >>
> >> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
> >>  
> >>> This fixes read/write functionality by:
> >>> 1) Changing NFC_CMD_RB_INT bit value.  
> >>
> >> I guess this is a separate fix
> >>  
> > 
> > Ok, I'll move it to separate patch
> >   
> >>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.  
> >>
> >> Is this really needed? Looks like you're delaying the next op only. Is
> >> using a delay enough? If yes, then it's probably the wrong approach.  
> 
> Hi Miquel, small update, I found some details from @Liang's message in v1 talks from the last month:
> 
> *
> After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send
> NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device.

That is true.

> but previous meson_nfc_queue_rb()
> only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
> i think there is something wrong with the Ready/Busy pin(please check the hardware whether this
> Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the
> nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the
> data bus and generate IRQ if ready.
> *
> 
> I guess, that sequence of commands from this patch is described in datasheet (unfortunately I don't have it and relied on the old driver).
> Yesterday I tried to remove sending of NAND_CMD_STATUS from this patch, but it broke current driver - i had ECC errors, so it looks like
> "shot in the dark" situation, to understand this logic.

When an operation on the NAND array happens (eg. read, prog, erase),
you need to wait "some time" before accessing the internal sram or even
the chip which is "busy" until it gets "ready" again. You can probe the
ready/busy pin (that's the hardware way, fast and reliable) or you can
poll a status with NAND_CMD_STATUS. The chips are designed so they can
actually process that command while they are doing time consuming tasks
to update the host. But IIRC every byte read will return the status
until you send READ0 again, which means "I'm done with the status
read" somehow.

Please see nand_soft_waitrdy() in order to understand how this is
supposed to work. You can even use that helper (which is exported)
instead of open-coding it in your driver. See atmel or sunxi
implementations for instance.

As using the native RB pin is better, you would need to identify
whether you have one or not at probe time and then either poll the
relevant bit of your controller if there is one, or fallback to the
soft read (which should fallback on exec_op in the end).

Thanks,
Miquèl

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-26 17:22           ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-26 17:22 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

avkrasnov@sberdevices.ru wrote on Wed, 24 May 2023 12:05:47 +0300:

> On 23.05.2023 12:12, Arseniy Krasnov wrote:
> > Hello Miquel, Liang
> > 
> > On 22.05.2023 18:05, Miquel Raynal wrote:  
> >> Hi Arseniy,
> >>
> >> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
> >>  
> >>> This fixes read/write functionality by:
> >>> 1) Changing NFC_CMD_RB_INT bit value.  
> >>
> >> I guess this is a separate fix
> >>  
> > 
> > Ok, I'll move it to separate patch
> >   
> >>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.  
> >>
> >> Is this really needed? Looks like you're delaying the next op only. Is
> >> using a delay enough? If yes, then it's probably the wrong approach.  
> 
> Hi Miquel, small update, I found some details from @Liang's message in v1 talks from the last month:
> 
> *
> After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send
> NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device.

That is true.

> but previous meson_nfc_queue_rb()
> only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
> i think there is something wrong with the Ready/Busy pin(please check the hardware whether this
> Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the
> nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the
> data bus and generate IRQ if ready.
> *
> 
> I guess, that sequence of commands from this patch is described in datasheet (unfortunately I don't have it and relied on the old driver).
> Yesterday I tried to remove sending of NAND_CMD_STATUS from this patch, but it broke current driver - i had ECC errors, so it looks like
> "shot in the dark" situation, to understand this logic.

When an operation on the NAND array happens (eg. read, prog, erase),
you need to wait "some time" before accessing the internal sram or even
the chip which is "busy" until it gets "ready" again. You can probe the
ready/busy pin (that's the hardware way, fast and reliable) or you can
poll a status with NAND_CMD_STATUS. The chips are designed so they can
actually process that command while they are doing time consuming tasks
to update the host. But IIRC every byte read will return the status
until you send READ0 again, which means "I'm done with the status
read" somehow.

Please see nand_soft_waitrdy() in order to understand how this is
supposed to work. You can even use that helper (which is exported)
instead of open-coding it in your driver. See atmel or sunxi
implementations for instance.

As using the native RB pin is better, you would need to identify
whether you have one or not at probe time and then either poll the
relevant bit of your controller if there is one, or fallback to the
soft read (which should fallback on exec_op in the end).

Thanks,
Miquèl

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

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-26 17:22           ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-26 17:22 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

avkrasnov@sberdevices.ru wrote on Wed, 24 May 2023 12:05:47 +0300:

> On 23.05.2023 12:12, Arseniy Krasnov wrote:
> > Hello Miquel, Liang
> > 
> > On 22.05.2023 18:05, Miquel Raynal wrote:  
> >> Hi Arseniy,
> >>
> >> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
> >>  
> >>> This fixes read/write functionality by:
> >>> 1) Changing NFC_CMD_RB_INT bit value.  
> >>
> >> I guess this is a separate fix
> >>  
> > 
> > Ok, I'll move it to separate patch
> >   
> >>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.  
> >>
> >> Is this really needed? Looks like you're delaying the next op only. Is
> >> using a delay enough? If yes, then it's probably the wrong approach.  
> 
> Hi Miquel, small update, I found some details from @Liang's message in v1 talks from the last month:
> 
> *
> After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send
> NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device.

That is true.

> but previous meson_nfc_queue_rb()
> only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
> i think there is something wrong with the Ready/Busy pin(please check the hardware whether this
> Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the
> nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the
> data bus and generate IRQ if ready.
> *
> 
> I guess, that sequence of commands from this patch is described in datasheet (unfortunately I don't have it and relied on the old driver).
> Yesterday I tried to remove sending of NAND_CMD_STATUS from this patch, but it broke current driver - i had ECC errors, so it looks like
> "shot in the dark" situation, to understand this logic.

When an operation on the NAND array happens (eg. read, prog, erase),
you need to wait "some time" before accessing the internal sram or even
the chip which is "busy" until it gets "ready" again. You can probe the
ready/busy pin (that's the hardware way, fast and reliable) or you can
poll a status with NAND_CMD_STATUS. The chips are designed so they can
actually process that command while they are doing time consuming tasks
to update the host. But IIRC every byte read will return the status
until you send READ0 again, which means "I'm done with the status
read" somehow.

Please see nand_soft_waitrdy() in order to understand how this is
supposed to work. You can even use that helper (which is exported)
instead of open-coding it in your driver. See atmel or sunxi
implementations for instance.

As using the native RB pin is better, you would need to identify
whether you have one or not at probe time and then either poll the
relevant bit of your controller if there is one, or fallback to the
soft read (which should fallback on exec_op in the end).

Thanks,
Miquèl

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-26 17:22           ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-26 17:22 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

avkrasnov@sberdevices.ru wrote on Wed, 24 May 2023 12:05:47 +0300:

> On 23.05.2023 12:12, Arseniy Krasnov wrote:
> > Hello Miquel, Liang
> > 
> > On 22.05.2023 18:05, Miquel Raynal wrote:  
> >> Hi Arseniy,
> >>
> >> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
> >>  
> >>> This fixes read/write functionality by:
> >>> 1) Changing NFC_CMD_RB_INT bit value.  
> >>
> >> I guess this is a separate fix
> >>  
> > 
> > Ok, I'll move it to separate patch
> >   
> >>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.  
> >>
> >> Is this really needed? Looks like you're delaying the next op only. Is
> >> using a delay enough? If yes, then it's probably the wrong approach.  
> 
> Hi Miquel, small update, I found some details from @Liang's message in v1 talks from the last month:
> 
> *
> After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send
> NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device.

That is true.

> but previous meson_nfc_queue_rb()
> only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
> i think there is something wrong with the Ready/Busy pin(please check the hardware whether this
> Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the
> nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the
> data bus and generate IRQ if ready.
> *
> 
> I guess, that sequence of commands from this patch is described in datasheet (unfortunately I don't have it and relied on the old driver).
> Yesterday I tried to remove sending of NAND_CMD_STATUS from this patch, but it broke current driver - i had ECC errors, so it looks like
> "shot in the dark" situation, to understand this logic.

When an operation on the NAND array happens (eg. read, prog, erase),
you need to wait "some time" before accessing the internal sram or even
the chip which is "busy" until it gets "ready" again. You can probe the
ready/busy pin (that's the hardware way, fast and reliable) or you can
poll a status with NAND_CMD_STATUS. The chips are designed so they can
actually process that command while they are doing time consuming tasks
to update the host. But IIRC every byte read will return the status
until you send READ0 again, which means "I'm done with the status
read" somehow.

Please see nand_soft_waitrdy() in order to understand how this is
supposed to work. You can even use that helper (which is exported)
instead of open-coding it in your driver. See atmel or sunxi
implementations for instance.

As using the native RB pin is better, you would need to identify
whether you have one or not at probe time and then either poll the
relevant bit of your controller if there is one, or fallback to the
soft read (which should fallback on exec_op in the end).

Thanks,
Miquèl

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
  2023-05-26 17:03         ` Miquel Raynal
  (?)
  (?)
@ 2023-05-29 19:43           ` Arseniy Krasnov
  -1 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-29 19:43 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hello Miquel! Thanks for details and explanations!

On 26.05.2023 20:03, Miquel Raynal wrote:
> Hi Arseniy,
> 
> avkrasnov@sberdevices.ru wrote on Tue, 23 May 2023 20:17:14 +0300:
> 
>> Hello Miquel! Thanks for detailed review!
>>
>> On 22.05.2023 18:33, Miquel Raynal wrote:
>>> Hi Arseniy,
>>>
>>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:36 +0300:
>>>
>>> The title should perhaps be "only expose unprotected user OOB bytes".
>>>   
>>>> This moves free bytes of OOB to non-protected ECC area. It is needed to
>>>> make JFFS2 works correctly with this NAND controller. Problem fires when
>>>> JFFS2 driver writes cleanmarker to some page and later it tries to write
>>>> to this page - write will be done successfully, but after that such page
>>>> becomes unreadable due to invalid ECC codes. This happens because second
>>>> write needs to update ECC codes, but it is impossible to do it correctly
>>>> without block erase. So idea of this patch is to  
>>>
>>> "... is to use the unprotected OOB area to store the cleanmarkers, so
>>> that they can be written by the filesystem without caring much about
>>> the page being empty or not: the ECC codes will not be written anyway."
>>> ?  
>>
>> Ok
>>
>>>
>>> JFFS2 is only useful on tiny NAND devices, where UBI does not fit,
>>> which are usually true SLC flashes, with the capability of writing
>>> a page with empty (0xFF) data, and still be able to write actual data
>>> to it later in a second write.  
>>
>> You mean to include text above to commit message also?
> 
> Yes, I believe this deserves to be in the commit message as well :)
> 

Ack

>>
>>>   
>>>> split accesses to OOB
>>>> free bytes and data on each page - now both of them does not depends on
>>>> each other.
>>>>
>>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>>> ---
>>>>  drivers/mtd/nand/raw/meson_nand.c | 192 ++++++++++++++++++++++++------
>>>>  1 file changed, 155 insertions(+), 37 deletions(-)
>>>>
>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>>> index 2f4d8c84186b..8526a6b87720 100644
>>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>> @@ -108,6 +108,9 @@
>>>>  
>>>>  #define PER_INFO_BYTE		8
>>>>  
>>>> +#define NFC_USER_BYTES		2
>>>> +#define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
>>>> +
>>>>  struct meson_nfc_nand_chip {
>>>>  	struct list_head node;
>>>>  	struct nand_chip nand;
>>>> @@ -122,6 +125,7 @@ struct meson_nfc_nand_chip {
>>>>  	u8 *data_buf;
>>>>  	__le64 *info_buf;
>>>>  	u32 nsels;
>>>> +	u8 *oob_buf;
>>>>  	u8 sels[];
>>>>  };
>>>>  
>>>> @@ -338,7 +342,7 @@ static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
>>>>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>  	int len;
>>>>  
>>>> -	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
>>>> +	len = nand->ecc.size * (i + 1) + NFC_OOB_PER_ECC(nand) * i;  
>>>
>>> This...
>>>   
>>>>  
>>>>  	return meson_chip->data_buf + len;
>>>>  }
>>>> @@ -349,7 +353,7 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>>>>  	int len, temp;
>>>>  
>>>>  	temp = nand->ecc.size + nand->ecc.bytes;
>>>> -	len = (temp + 2) * i;
>>>> +	len = (temp + NFC_USER_BYTES) * i;  
>>>
>>> ... and this (same below)
>>>
>>> are purely cosmetic, should be in a patch aside.
>>>   
>>
>> Ack, i'll move cosmetic updates to separate patch.
>>
>>>>  
>>>>  	return meson_chip->data_buf + len;
>>>>  }
>>>> @@ -357,29 +361,47 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>>>>  static void meson_nfc_get_data_oob(struct nand_chip *nand,
>>>>  				   u8 *buf, u8 *oobbuf)
>>>>  {
>>>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>>>  	int i, oob_len = 0;
>>>>  	u8 *dsrc, *osrc;
>>>> +	u8 *oobtail;
>>>>  
>>>> -	oob_len = nand->ecc.bytes + 2;
>>>> +	oob_len = NFC_OOB_PER_ECC(nand);
>>>>  	for (i = 0; i < nand->ecc.steps; i++) {
>>>>  		if (buf) {
>>>>  			dsrc = meson_nfc_data_ptr(nand, i);
>>>>  			memcpy(buf, dsrc, nand->ecc.size);
>>>>  			buf += nand->ecc.size;
>>>>  		}
>>>> -		osrc = meson_nfc_oob_ptr(nand, i);
>>>> -		memcpy(oobbuf, osrc, oob_len);
>>>> -		oobbuf += oob_len;
>>>> +
>>>> +		if (oobbuf) {
>>>> +			osrc = meson_nfc_oob_ptr(nand, i);
>>>> +			memcpy(oobbuf, osrc, oob_len);
>>>> +			oobbuf += oob_len;
>>>> +		}
>>>>  	}
>>>> +
>>>> +	if (!oobbuf)
>>>> +		return;  
>>>
>>> The whole "if (oobbuf)" logic is nice to have, but should as well be in
>>> a dedicated patch.  
>>
>> Sorry, You mean that this logic implements read of ECC codes? And not
>> related to OOB layout update in this patch?
> 
> You need to make cosmetic changes in a first patch (or even in several
> patches if they are not related), then in another patch you might make
> additions like a better handling of the OOB, and any change in the
> layout might come last. I just want to split the patch to make
> understandable atomic changes (also easier to review).
> 
> 1 atomic change == 1 patch
> 

Ok, I'll try to split it!

>>>   
>>>> +
>>>> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
>>>> +		  (nand->ecc.size + oob_len);
>>>> +
>>>> +	/* 'oobbuf' if already shifted to the start of unused area. */  
>>>
>>> 		    is?
>>> s/unused/user/? I'm not sure I get the comment.  
>>
>> Yes, not clear comment.
>>
>>>   
>>>> +	memcpy(oobbuf, oobtail, mtd->oobsize - nand->ecc.steps * oob_len);
>>>>  }
>>>>  
>>>>  static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>>>  				   const u8 *buf, u8 *oobbuf)
>>>>  {
>>>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>>>  	int i, oob_len = 0;
>>>>  	u8 *dsrc, *osrc;
>>>> +	u8 *oobtail;
>>>>  
>>>> -	oob_len = nand->ecc.bytes + 2;
>>>> +	oob_len = NFC_OOB_PER_ECC(nand);
>>>>  	for (i = 0; i < nand->ecc.steps; i++) {
>>>>  		if (buf) {
>>>>  			dsrc = meson_nfc_data_ptr(nand, i);
>>>> @@ -390,6 +412,12 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>>>  		memcpy(osrc, oobbuf, oob_len);
>>>>  		oobbuf += oob_len;
>>>>  	}
>>>> +
>>>> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
>>>> +		  (nand->ecc.size + oob_len);
>>>> +
>>>> +	/* 'oobbuf' if already shifted to the start of unused area. */
>>>> +	memcpy(oobtail, oobbuf, mtd->oobsize - nand->ecc.steps * oob_len);
>>>>  }
>>>>  
>>>>  static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
>>>> @@ -436,25 +464,12 @@ static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>>>  {
>>>>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>  	__le64 *info;
>>>> -	int i, count;
>>>> -
>>>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>>>> -		info = &meson_chip->info_buf[i];
>>>> -		*info |= oob_buf[count];
>>>> -		*info |= oob_buf[count + 1] << 8;
>>>> -	}
>>>> -}
>>>> -
>>>> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>>> -{
>>>> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>> -	__le64 *info;
>>>> -	int i, count;
>>>> +	int i;
>>>>  
>>>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>>>> +	for (i = 0; i < nand->ecc.steps; i++) {
>>>>  		info = &meson_chip->info_buf[i];
>>>> -		oob_buf[count] = *info;
>>>> -		oob_buf[count + 1] = *info >> 8;
>>>> +		/* Always ignore user bytes programming. */  
>>>
>>> Why?  
>>
>> I think comment message is wrong a little bit. Here "user bytes" are
>> user bytes protected by ECC (e.g. location of these bytes differs from new
>> OOB layout introduced by this patch). During page write this hardware
>> always writes these bytes along with data. But, new OOB layout always ignores
>> these 4 bytes, so set them to 0xFF always.
> 
> When performing page reads/writes, you need to take the data as it's
> been provided. You may move the data around in the buffer provided to
> the controller, so that it get the ECC data at the right location, and
> you need of course to reorganize the data when reading as well, so that
> the user sees XkiB of data + YB of OOB. That's all you need to do in
> these helpers.
> 

I think there is some misunderstanding about these "user bytes" above: there are 4
bytes which this NAND controller always writes to page in ECC mode - it was free OOB
bytes covered by ECC. Controller grabs values from DMA buffer (second DMA buffer which
doesn't contains page data) and writes it along with data and ECC codes. Idea of this
change is to always suppress this write by setting them to 0xFF (may be there is some
command option to not write it, but I don't have doc), because all of them (4 bytes)
become unavailable to reader/writer.

>>> Also, maybe I should mention the helpers:
>>> mtd_ooblayout_get_eccbytes and co
>>> They are very useful to deal with OOB bytes. Everything seems extremely
>>> hardcoded in this driver, while the user can tune read/write OOB
>>> operations.  
>>
>> Thanks for details, IIUC these helpers will work correctly with this driver.
>> What means "tune" here?
> 
> The user can provide data for the OOB part and tell the mtd layer to
> place the bytes in the ->oob_buf following different constraints:
> - the user bytes are already when they need to be (MTD_OPS_PLACE_OOB
>   and MTD_OPS_RAW)
> - the user bytes are packed at the beginning of the buffer, please move
>   them where they belong (MTD_OPS_AUTO_OOB).
> 
> So in the controller driver, what needs to be done is to make the
> switch between the "data1+data2+oob1+oob2" layout into your
> ECC controller's layout, eg "data1+oob1+data2+oob2".

I checked MTD_OPS_AUTO_OOB and MTD_OPS_PLACE_OOB by 'nandwrite' utility (it
has -a option). IIUC everything works as expected, because 'nand_fill_oob()'
performs this switch. 

> 
> Hence you should not need anything like that:
>>>> +		*info |= 0xffff;
> 
>>>>  	}
>>>>  }
>>>>  
>>>> @@ -698,18 +713,92 @@ static int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf,
>>>>  	return meson_nfc_write_page_sub(nand, page, 1);
>>>>  }
>>>>  
>>>> +static u32 meson_nfc_get_oob_bytes(struct nand_chip *nand)
>>>> +{
>>>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>>> +
>>>> +	return mtd->oobsize - nand->ecc.steps * NFC_OOB_PER_ECC(nand);
>>>> +}
>>>> +
>>>> +static int __meson_nfc_write_oob(struct nand_chip *nand, int page, u8 *oob_buf)
>>>> +{
>>>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>>> +	u32 page_size = mtd->writesize + mtd->oobsize;
>>>> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
>>>> +	int ret;
>>>> +
>>>> +	if (!oob_bytes)
>>>> +		return 0;
>>>> +
>>>> +	ret = nand_prog_page_begin_op(nand, page, 0, NULL, 0);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	memcpy(meson_chip->oob_buf, oob_buf + (mtd->oobsize - oob_bytes),
>>>> +	       oob_bytes);
>>>> +
>>>> +	ret = nand_change_write_column_op(nand, page_size - oob_bytes,
>>>> +					  meson_chip->oob_buf,
>>>> +					  oob_bytes, false);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	return nand_prog_page_end_op(nand);
>>>> +}
>>>> +
>>>> +static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
>>>> +				u8 *oob_buf)
>>>> +{
>>>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>>> +	u32 oob_bytes;
>>>> +	u32 page_size;
>>>> +	int ret;
>>>> +
>>>> +	oob_bytes = meson_nfc_get_oob_bytes(nand);
>>>> +
>>>> +	if (!oob_bytes)
>>>> +		return 0;
>>>> +
>>>> +	ret = nand_read_page_op(nand, page, 0, NULL, 0);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	page_size = mtd->writesize + mtd->oobsize;
>>>> +
>>>> +	ret = nand_change_read_column_op(nand, page_size - oob_bytes,
>>>> +					 meson_chip->oob_buf,
>>>> +					 oob_bytes, false);
>>>> +
>>>> +	if (!ret)
>>>> +		memcpy(oob_buf + (mtd->oobsize - oob_bytes),
>>>> +		       meson_chip->oob_buf,
>>>> +		       oob_bytes);
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>>  static int meson_nfc_write_page_hwecc(struct nand_chip *nand,
>>>>  				      const u8 *buf, int oob_required, int page)
>>>>  {
>>>>  	struct mtd_info *mtd = nand_to_mtd(nand);
>>>>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>  	u8 *oob_buf = nand->oob_poi;
>>>> +	int ret;
>>>>  
>>>>  	memcpy(meson_chip->data_buf, buf, mtd->writesize);
>>>>  	memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE);
>>>>  	meson_nfc_set_user_byte(nand, oob_buf);
>>>>  
>>>> -	return meson_nfc_write_page_sub(nand, page, 0);
>>>> +	ret = meson_nfc_write_page_sub(nand, page, 0);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	if (oob_required)
>>>> +		ret = __meson_nfc_write_oob(nand, page, oob_buf);  
>>>
>>> You should provide all the data including OOB bytes in a single write
>>> call, otherwise you perform two writes on the same page, that's not
>>> what this helper is expected to do.  
>>
>> I see, so I need to check 'oob_required' here before programming page data?
> 
> Yes!
> 
>> If it is set -> place OOB data to DMA buffer and then call PAGEPROG once?
> 
> Exactly.
> 

Commented about this below, but it works good!

>>
>>>   
>>>> +
>>>> +	return ret;
>>>>  }
>>>>  
>>>>  static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>>>> @@ -783,7 +872,7 @@ static int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf,
>>>>  	if (ret)
>>>>  		return ret;
>>>>  
>>>> -	meson_nfc_get_data_oob(nand, buf, oob_buf);
>>>> +	meson_nfc_get_data_oob(nand, buf, oob_required ? oob_buf : NULL);
>>>>  
>>>>  	return 0;
>>>>  }
>>>> @@ -803,12 +892,12 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>>>>  	if (ret)
>>>>  		return ret;
>>>>  
>>>> -	meson_nfc_get_user_byte(nand, oob_buf);
>>>>  	ret = meson_nfc_ecc_correct(nand, &bitflips, &correct_bitmap);
>>>>  	if (ret == ECC_CHECK_RETURN_FF) {
>>>>  		if (buf)
>>>>  			memset(buf, 0xff, mtd->writesize);
>>>>  		memset(oob_buf, 0xff, mtd->oobsize);
>>>> +		return bitflips;  
>>>
>>> That is something else => other fix => other patch?  
>>
>> Idea of this 'return' is that when read fails, we return from this function
>> without reading OOB below.
> 
> It seemed like the "return bitflips" thing was new, if it's just due to
> the diff not being wide enough, then it's fine, otherwise if this is
> something new, it should be in a dedicated patch.
> 

I think i can remove it, by checking 'ret' before OOB read below. In this case
when 'ret' has failed status, OOB won't be accessed.

>>
>>>   
>>>>  	} else if (ret < 0) {
>>>>  		if ((nand->options & NAND_NEED_SCRAMBLING) || !buf) {
>>>>  			mtd->ecc_stats.failed++;
>>>> @@ -820,12 +909,14 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>>>>  
>>>>  		for (i = 0; i < nand->ecc.steps ; i++) {
>>>>  			u8 *data = buf + i * ecc->size;
>>>> -			u8 *oob = nand->oob_poi + i * (ecc->bytes + 2);
>>>> +			u8 *oob = nand->oob_poi + i * NFC_OOB_PER_ECC(nand);
>>>>  
>>>>  			if (correct_bitmap & BIT_ULL(i))
>>>>  				continue;
>>>> +
>>>>  			ret = nand_check_erased_ecc_chunk(data,	ecc->size,
>>>> -							  oob, ecc->bytes + 2,
>>>> +							  oob,
>>>> +							  NFC_OOB_PER_ECC(nand),
>>>>  							  NULL, 0,
>>>>  							  ecc->strength);
>>>>  			if (ret < 0) {
>>>> @@ -839,17 +930,30 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>>>>  		memcpy(buf, meson_chip->data_buf, mtd->writesize);
>>>>  	}
>>>>  
>>>> +	if (oob_required)
>>>> +		__meson_nfc_read_oob(nand, page, oob_buf);  
>>>
>>> In the standalone "read_oob" hook, you have to send a READ0 command,
>>> but not when you are in the read_page hook. It is a big waste of time.  
>>
>> IIUC approach here must be exactly the same as in write? E.g. i need to
>> send single READ0 and then fill provided OOB buffer if needed?
> 
> Yes.
> 
> For both reads and writes, the logic is:
> - there are commands which are actually time consuming: there is
>   something happening on the nand array which either reads or writes
>   data to/from the internal sram.
> - there are other commands which just change the "pointer" in the
>   internal sram.
> 
> So you can basically say "I want to write into the sram at offset X,
> then at offset Y" and when the sram has been filled you can commit the
> write and that's the operation which should happen only once. In the
> read path it's the opposite, you request a read from the NAND array into
> the sram cache, and you can then request data randomly. Of course both
> operations cannot be too random either, you need to follow the ECC
> engine pattern which must be fed with X bytes of data and then will
> produce the ECC bytes to write/compare.

Thanks for this explanation! Now for example I tried to remove CMD_READ0 command
in OOB read in case when CMD_READ0 was already sent to read page data and it
works! I used 'nand_change_read_column_op()' and IIUC its internal command
CMD_RNDOUT is one of "other command which just change the pointer in the
internal sram" from above. OOB was read successfully without extra CMD_READ0!
The same thing i've implemented for write - 'nand_change_write_column_op()'
is used to send OOB data to internal SRAM, then i call PAGE_PROG and both page
data and OOB are written successfully.

Thanks for this!
 
> 
>>
>>>   
>>>> +
>>>>  	return bitflips;
>>>>  }
>>>>  
>>>>  static int meson_nfc_read_oob_raw(struct nand_chip *nand, int page)
>>>>  {
>>>> -	return meson_nfc_read_page_raw(nand, NULL, 1, page);
>>>> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
>>>>  }
>>>>  
>>>>  static int meson_nfc_read_oob(struct nand_chip *nand, int page)
>>>>  {
>>>> -	return meson_nfc_read_page_hwecc(nand, NULL, 1, page);
>>>> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
>>>> +}
>>>> +
>>>> +static int meson_nfc_write_oob_raw(struct nand_chip *nand, int page)
>>>> +{
>>>> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
>>>> +}
>>>> +
>>>> +static int meson_nfc_write_oob(struct nand_chip *nand, int page)
>>>> +{
>>>> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);  
>>>
>>> Do we really need these indirections?  
>>
>> Right, I think I can use only one function for OOB write in both ECC and raw modes.
>>
>>>   
>>>>  }
>>>>  
>>>>  static bool meson_nfc_is_buffer_dma_safe(const void *buffer)
>>>> @@ -982,7 +1086,7 @@ static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
>>>>  	if (section >= nand->ecc.steps)
>>>>  		return -ERANGE;
>>>>  
>>>> -	oobregion->offset =  2 + (section * (2 + nand->ecc.bytes));
>>>> +	oobregion->offset = NFC_USER_BYTES + section * NFC_OOB_PER_ECC(nand);  
>>>
>>> The () are still needed around the '*'
>>>   
>>>>  	oobregion->length = nand->ecc.bytes;
>>>>  
>>>>  	return 0;
>>>> @@ -992,12 +1096,16 @@ static int meson_ooblayout_free(struct mtd_info *mtd, int section,
>>>>  				struct mtd_oob_region *oobregion)
>>>>  {
>>>>  	struct nand_chip *nand = mtd_to_nand(mtd);
>>>> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
>>>>  
>>>>  	if (section >= nand->ecc.steps)
>>>>  		return -ERANGE;
>>>>  
>>>> -	oobregion->offset = section * (2 + nand->ecc.bytes);
>>>> -	oobregion->length = 2;
>>>> +	/* Split rest of OOB area (not covered by ECC engine) per each
>>>> +	 * ECC section. This will be OOB data available to user.
>>>> +	 */
>>>> +	oobregion->offset = (section + nand->ecc.steps) * NFC_OOB_PER_ECC(nand);
>>>> +	oobregion->length = oob_bytes / nand->ecc.steps;
>>>>  
>>>>  	return 0;
>>>>  }
>>>> @@ -1184,6 +1292,9 @@ static int meson_nand_bch_mode(struct nand_chip *nand)
>>>>  
>>>>  static void meson_nand_detach_chip(struct nand_chip *nand)
>>>>  {
>>>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>> +
>>>> +	kfree(meson_chip->oob_buf);
>>>>  	meson_nfc_free_buffer(nand);
>>>>  }
>>>>  
>>>> @@ -1225,9 +1336,9 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>>>>  	nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
>>>>  	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
>>>>  	nand->ecc.write_page = meson_nfc_write_page_hwecc;
>>>> -	nand->ecc.write_oob_raw = nand_write_oob_std;
>>>> -	nand->ecc.write_oob = nand_write_oob_std;
>>>>  
>>>> +	nand->ecc.write_oob_raw = meson_nfc_write_oob_raw;
>>>> +	nand->ecc.write_oob = meson_nfc_write_oob;  
>>>
>>> Actually if you define the right OOB layouts, are these really needed
>>> ?? I would expect the right bytes to be picked up by the default
>>> implementation. I see nothing specific in your current implementation?  
>>
>> You mean to use 'nand_write_oob_std()'? If so, I think it won't work,
>> because it tries to write OOB data right after 'mtd->writesize', but
>> page layout for this controller is like this:
>> [       1024 bytes of data        ]
>> [ 14 bytes ECC + 2 bytes for user ] <- with new layout we don't touch these 2 bytes
>> [       1024 bytes of data        ]
>> [ 14 bytes ECC + 2 bytes for user ] <- with new layout we don't touch these 2 bytes
>> [      32 bytes of user bytes     ] <- we use there 32 bytes as free(user) bytes in OOB
> 
> Right.
> 
>>
>> So with 'mtd->writesize' of 2048 we won't get into last 32 bytes in the picture above.
>> I'm not goot in NAND/MTD area, but I think that why i need special OOB access callbacks.
> 
> Ok.
> 

Ok, so I'll keep these callbacks in the next version.

>> Also in previous patches @Liang said, that default OOB read/write functions won't work
>> on this controller and it is wrong to use them in currently merged driver.
>>
>>>   
>>>>  	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
>>>>  	nand->ecc.read_page = meson_nfc_read_page_hwecc;
>>>>  	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
>>>> @@ -1237,9 +1348,16 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>>>>  		dev_err(nfc->dev, "16bits bus width not supported");
>>>>  		return -EINVAL;
>>>>  	}
>>>> +
>>>> +	meson_chip->oob_buf = kmalloc(nand->ecc.bytes, GFP_KERNEL);  
>>>
>>> devm_kmalloc?  
>>
>> Ack
>>
>>>   
>>>> +	if (!meson_chip->oob_buf)
>>>> +		return -ENOMEM;
>>>> +
>>>>  	ret = meson_chip_buffer_init(nand);
>>>> -	if (ret)
>>>> +	if (ret) {
>>>> +		kfree(meson_chip->oob_buf);
>>>>  		return -ENOMEM;
>>>> +	}
>>>>  
>>>>  	return ret;
>>>>  }  
>>>
>>>
>>> Thanks,
>>> Miquèl  
>>
>> Thanks, Arseniy
> 
> 
> Thanks,
> Miquèl

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-29 19:43           ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-29 19:43 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hello Miquel! Thanks for details and explanations!

On 26.05.2023 20:03, Miquel Raynal wrote:
> Hi Arseniy,
> 
> avkrasnov@sberdevices.ru wrote on Tue, 23 May 2023 20:17:14 +0300:
> 
>> Hello Miquel! Thanks for detailed review!
>>
>> On 22.05.2023 18:33, Miquel Raynal wrote:
>>> Hi Arseniy,
>>>
>>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:36 +0300:
>>>
>>> The title should perhaps be "only expose unprotected user OOB bytes".
>>>   
>>>> This moves free bytes of OOB to non-protected ECC area. It is needed to
>>>> make JFFS2 works correctly with this NAND controller. Problem fires when
>>>> JFFS2 driver writes cleanmarker to some page and later it tries to write
>>>> to this page - write will be done successfully, but after that such page
>>>> becomes unreadable due to invalid ECC codes. This happens because second
>>>> write needs to update ECC codes, but it is impossible to do it correctly
>>>> without block erase. So idea of this patch is to  
>>>
>>> "... is to use the unprotected OOB area to store the cleanmarkers, so
>>> that they can be written by the filesystem without caring much about
>>> the page being empty or not: the ECC codes will not be written anyway."
>>> ?  
>>
>> Ok
>>
>>>
>>> JFFS2 is only useful on tiny NAND devices, where UBI does not fit,
>>> which are usually true SLC flashes, with the capability of writing
>>> a page with empty (0xFF) data, and still be able to write actual data
>>> to it later in a second write.  
>>
>> You mean to include text above to commit message also?
> 
> Yes, I believe this deserves to be in the commit message as well :)
> 

Ack

>>
>>>   
>>>> split accesses to OOB
>>>> free bytes and data on each page - now both of them does not depends on
>>>> each other.
>>>>
>>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>>> ---
>>>>  drivers/mtd/nand/raw/meson_nand.c | 192 ++++++++++++++++++++++++------
>>>>  1 file changed, 155 insertions(+), 37 deletions(-)
>>>>
>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>>> index 2f4d8c84186b..8526a6b87720 100644
>>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>> @@ -108,6 +108,9 @@
>>>>  
>>>>  #define PER_INFO_BYTE		8
>>>>  
>>>> +#define NFC_USER_BYTES		2
>>>> +#define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
>>>> +
>>>>  struct meson_nfc_nand_chip {
>>>>  	struct list_head node;
>>>>  	struct nand_chip nand;
>>>> @@ -122,6 +125,7 @@ struct meson_nfc_nand_chip {
>>>>  	u8 *data_buf;
>>>>  	__le64 *info_buf;
>>>>  	u32 nsels;
>>>> +	u8 *oob_buf;
>>>>  	u8 sels[];
>>>>  };
>>>>  
>>>> @@ -338,7 +342,7 @@ static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
>>>>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>  	int len;
>>>>  
>>>> -	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
>>>> +	len = nand->ecc.size * (i + 1) + NFC_OOB_PER_ECC(nand) * i;  
>>>
>>> This...
>>>   
>>>>  
>>>>  	return meson_chip->data_buf + len;
>>>>  }
>>>> @@ -349,7 +353,7 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>>>>  	int len, temp;
>>>>  
>>>>  	temp = nand->ecc.size + nand->ecc.bytes;
>>>> -	len = (temp + 2) * i;
>>>> +	len = (temp + NFC_USER_BYTES) * i;  
>>>
>>> ... and this (same below)
>>>
>>> are purely cosmetic, should be in a patch aside.
>>>   
>>
>> Ack, i'll move cosmetic updates to separate patch.
>>
>>>>  
>>>>  	return meson_chip->data_buf + len;
>>>>  }
>>>> @@ -357,29 +361,47 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>>>>  static void meson_nfc_get_data_oob(struct nand_chip *nand,
>>>>  				   u8 *buf, u8 *oobbuf)
>>>>  {
>>>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>>>  	int i, oob_len = 0;
>>>>  	u8 *dsrc, *osrc;
>>>> +	u8 *oobtail;
>>>>  
>>>> -	oob_len = nand->ecc.bytes + 2;
>>>> +	oob_len = NFC_OOB_PER_ECC(nand);
>>>>  	for (i = 0; i < nand->ecc.steps; i++) {
>>>>  		if (buf) {
>>>>  			dsrc = meson_nfc_data_ptr(nand, i);
>>>>  			memcpy(buf, dsrc, nand->ecc.size);
>>>>  			buf += nand->ecc.size;
>>>>  		}
>>>> -		osrc = meson_nfc_oob_ptr(nand, i);
>>>> -		memcpy(oobbuf, osrc, oob_len);
>>>> -		oobbuf += oob_len;
>>>> +
>>>> +		if (oobbuf) {
>>>> +			osrc = meson_nfc_oob_ptr(nand, i);
>>>> +			memcpy(oobbuf, osrc, oob_len);
>>>> +			oobbuf += oob_len;
>>>> +		}
>>>>  	}
>>>> +
>>>> +	if (!oobbuf)
>>>> +		return;  
>>>
>>> The whole "if (oobbuf)" logic is nice to have, but should as well be in
>>> a dedicated patch.  
>>
>> Sorry, You mean that this logic implements read of ECC codes? And not
>> related to OOB layout update in this patch?
> 
> You need to make cosmetic changes in a first patch (or even in several
> patches if they are not related), then in another patch you might make
> additions like a better handling of the OOB, and any change in the
> layout might come last. I just want to split the patch to make
> understandable atomic changes (also easier to review).
> 
> 1 atomic change == 1 patch
> 

Ok, I'll try to split it!

>>>   
>>>> +
>>>> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
>>>> +		  (nand->ecc.size + oob_len);
>>>> +
>>>> +	/* 'oobbuf' if already shifted to the start of unused area. */  
>>>
>>> 		    is?
>>> s/unused/user/? I'm not sure I get the comment.  
>>
>> Yes, not clear comment.
>>
>>>   
>>>> +	memcpy(oobbuf, oobtail, mtd->oobsize - nand->ecc.steps * oob_len);
>>>>  }
>>>>  
>>>>  static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>>>  				   const u8 *buf, u8 *oobbuf)
>>>>  {
>>>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>>>  	int i, oob_len = 0;
>>>>  	u8 *dsrc, *osrc;
>>>> +	u8 *oobtail;
>>>>  
>>>> -	oob_len = nand->ecc.bytes + 2;
>>>> +	oob_len = NFC_OOB_PER_ECC(nand);
>>>>  	for (i = 0; i < nand->ecc.steps; i++) {
>>>>  		if (buf) {
>>>>  			dsrc = meson_nfc_data_ptr(nand, i);
>>>> @@ -390,6 +412,12 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>>>  		memcpy(osrc, oobbuf, oob_len);
>>>>  		oobbuf += oob_len;
>>>>  	}
>>>> +
>>>> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
>>>> +		  (nand->ecc.size + oob_len);
>>>> +
>>>> +	/* 'oobbuf' if already shifted to the start of unused area. */
>>>> +	memcpy(oobtail, oobbuf, mtd->oobsize - nand->ecc.steps * oob_len);
>>>>  }
>>>>  
>>>>  static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
>>>> @@ -436,25 +464,12 @@ static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>>>  {
>>>>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>  	__le64 *info;
>>>> -	int i, count;
>>>> -
>>>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>>>> -		info = &meson_chip->info_buf[i];
>>>> -		*info |= oob_buf[count];
>>>> -		*info |= oob_buf[count + 1] << 8;
>>>> -	}
>>>> -}
>>>> -
>>>> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>>> -{
>>>> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>> -	__le64 *info;
>>>> -	int i, count;
>>>> +	int i;
>>>>  
>>>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>>>> +	for (i = 0; i < nand->ecc.steps; i++) {
>>>>  		info = &meson_chip->info_buf[i];
>>>> -		oob_buf[count] = *info;
>>>> -		oob_buf[count + 1] = *info >> 8;
>>>> +		/* Always ignore user bytes programming. */  
>>>
>>> Why?  
>>
>> I think comment message is wrong a little bit. Here "user bytes" are
>> user bytes protected by ECC (e.g. location of these bytes differs from new
>> OOB layout introduced by this patch). During page write this hardware
>> always writes these bytes along with data. But, new OOB layout always ignores
>> these 4 bytes, so set them to 0xFF always.
> 
> When performing page reads/writes, you need to take the data as it's
> been provided. You may move the data around in the buffer provided to
> the controller, so that it get the ECC data at the right location, and
> you need of course to reorganize the data when reading as well, so that
> the user sees XkiB of data + YB of OOB. That's all you need to do in
> these helpers.
> 

I think there is some misunderstanding about these "user bytes" above: there are 4
bytes which this NAND controller always writes to page in ECC mode - it was free OOB
bytes covered by ECC. Controller grabs values from DMA buffer (second DMA buffer which
doesn't contains page data) and writes it along with data and ECC codes. Idea of this
change is to always suppress this write by setting them to 0xFF (may be there is some
command option to not write it, but I don't have doc), because all of them (4 bytes)
become unavailable to reader/writer.

>>> Also, maybe I should mention the helpers:
>>> mtd_ooblayout_get_eccbytes and co
>>> They are very useful to deal with OOB bytes. Everything seems extremely
>>> hardcoded in this driver, while the user can tune read/write OOB
>>> operations.  
>>
>> Thanks for details, IIUC these helpers will work correctly with this driver.
>> What means "tune" here?
> 
> The user can provide data for the OOB part and tell the mtd layer to
> place the bytes in the ->oob_buf following different constraints:
> - the user bytes are already when they need to be (MTD_OPS_PLACE_OOB
>   and MTD_OPS_RAW)
> - the user bytes are packed at the beginning of the buffer, please move
>   them where they belong (MTD_OPS_AUTO_OOB).
> 
> So in the controller driver, what needs to be done is to make the
> switch between the "data1+data2+oob1+oob2" layout into your
> ECC controller's layout, eg "data1+oob1+data2+oob2".

I checked MTD_OPS_AUTO_OOB and MTD_OPS_PLACE_OOB by 'nandwrite' utility (it
has -a option). IIUC everything works as expected, because 'nand_fill_oob()'
performs this switch. 

> 
> Hence you should not need anything like that:
>>>> +		*info |= 0xffff;
> 
>>>>  	}
>>>>  }
>>>>  
>>>> @@ -698,18 +713,92 @@ static int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf,
>>>>  	return meson_nfc_write_page_sub(nand, page, 1);
>>>>  }
>>>>  
>>>> +static u32 meson_nfc_get_oob_bytes(struct nand_chip *nand)
>>>> +{
>>>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>>> +
>>>> +	return mtd->oobsize - nand->ecc.steps * NFC_OOB_PER_ECC(nand);
>>>> +}
>>>> +
>>>> +static int __meson_nfc_write_oob(struct nand_chip *nand, int page, u8 *oob_buf)
>>>> +{
>>>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>>> +	u32 page_size = mtd->writesize + mtd->oobsize;
>>>> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
>>>> +	int ret;
>>>> +
>>>> +	if (!oob_bytes)
>>>> +		return 0;
>>>> +
>>>> +	ret = nand_prog_page_begin_op(nand, page, 0, NULL, 0);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	memcpy(meson_chip->oob_buf, oob_buf + (mtd->oobsize - oob_bytes),
>>>> +	       oob_bytes);
>>>> +
>>>> +	ret = nand_change_write_column_op(nand, page_size - oob_bytes,
>>>> +					  meson_chip->oob_buf,
>>>> +					  oob_bytes, false);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	return nand_prog_page_end_op(nand);
>>>> +}
>>>> +
>>>> +static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
>>>> +				u8 *oob_buf)
>>>> +{
>>>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>>> +	u32 oob_bytes;
>>>> +	u32 page_size;
>>>> +	int ret;
>>>> +
>>>> +	oob_bytes = meson_nfc_get_oob_bytes(nand);
>>>> +
>>>> +	if (!oob_bytes)
>>>> +		return 0;
>>>> +
>>>> +	ret = nand_read_page_op(nand, page, 0, NULL, 0);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	page_size = mtd->writesize + mtd->oobsize;
>>>> +
>>>> +	ret = nand_change_read_column_op(nand, page_size - oob_bytes,
>>>> +					 meson_chip->oob_buf,
>>>> +					 oob_bytes, false);
>>>> +
>>>> +	if (!ret)
>>>> +		memcpy(oob_buf + (mtd->oobsize - oob_bytes),
>>>> +		       meson_chip->oob_buf,
>>>> +		       oob_bytes);
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>>  static int meson_nfc_write_page_hwecc(struct nand_chip *nand,
>>>>  				      const u8 *buf, int oob_required, int page)
>>>>  {
>>>>  	struct mtd_info *mtd = nand_to_mtd(nand);
>>>>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>  	u8 *oob_buf = nand->oob_poi;
>>>> +	int ret;
>>>>  
>>>>  	memcpy(meson_chip->data_buf, buf, mtd->writesize);
>>>>  	memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE);
>>>>  	meson_nfc_set_user_byte(nand, oob_buf);
>>>>  
>>>> -	return meson_nfc_write_page_sub(nand, page, 0);
>>>> +	ret = meson_nfc_write_page_sub(nand, page, 0);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	if (oob_required)
>>>> +		ret = __meson_nfc_write_oob(nand, page, oob_buf);  
>>>
>>> You should provide all the data including OOB bytes in a single write
>>> call, otherwise you perform two writes on the same page, that's not
>>> what this helper is expected to do.  
>>
>> I see, so I need to check 'oob_required' here before programming page data?
> 
> Yes!
> 
>> If it is set -> place OOB data to DMA buffer and then call PAGEPROG once?
> 
> Exactly.
> 

Commented about this below, but it works good!

>>
>>>   
>>>> +
>>>> +	return ret;
>>>>  }
>>>>  
>>>>  static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>>>> @@ -783,7 +872,7 @@ static int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf,
>>>>  	if (ret)
>>>>  		return ret;
>>>>  
>>>> -	meson_nfc_get_data_oob(nand, buf, oob_buf);
>>>> +	meson_nfc_get_data_oob(nand, buf, oob_required ? oob_buf : NULL);
>>>>  
>>>>  	return 0;
>>>>  }
>>>> @@ -803,12 +892,12 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>>>>  	if (ret)
>>>>  		return ret;
>>>>  
>>>> -	meson_nfc_get_user_byte(nand, oob_buf);
>>>>  	ret = meson_nfc_ecc_correct(nand, &bitflips, &correct_bitmap);
>>>>  	if (ret == ECC_CHECK_RETURN_FF) {
>>>>  		if (buf)
>>>>  			memset(buf, 0xff, mtd->writesize);
>>>>  		memset(oob_buf, 0xff, mtd->oobsize);
>>>> +		return bitflips;  
>>>
>>> That is something else => other fix => other patch?  
>>
>> Idea of this 'return' is that when read fails, we return from this function
>> without reading OOB below.
> 
> It seemed like the "return bitflips" thing was new, if it's just due to
> the diff not being wide enough, then it's fine, otherwise if this is
> something new, it should be in a dedicated patch.
> 

I think i can remove it, by checking 'ret' before OOB read below. In this case
when 'ret' has failed status, OOB won't be accessed.

>>
>>>   
>>>>  	} else if (ret < 0) {
>>>>  		if ((nand->options & NAND_NEED_SCRAMBLING) || !buf) {
>>>>  			mtd->ecc_stats.failed++;
>>>> @@ -820,12 +909,14 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>>>>  
>>>>  		for (i = 0; i < nand->ecc.steps ; i++) {
>>>>  			u8 *data = buf + i * ecc->size;
>>>> -			u8 *oob = nand->oob_poi + i * (ecc->bytes + 2);
>>>> +			u8 *oob = nand->oob_poi + i * NFC_OOB_PER_ECC(nand);
>>>>  
>>>>  			if (correct_bitmap & BIT_ULL(i))
>>>>  				continue;
>>>> +
>>>>  			ret = nand_check_erased_ecc_chunk(data,	ecc->size,
>>>> -							  oob, ecc->bytes + 2,
>>>> +							  oob,
>>>> +							  NFC_OOB_PER_ECC(nand),
>>>>  							  NULL, 0,
>>>>  							  ecc->strength);
>>>>  			if (ret < 0) {
>>>> @@ -839,17 +930,30 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>>>>  		memcpy(buf, meson_chip->data_buf, mtd->writesize);
>>>>  	}
>>>>  
>>>> +	if (oob_required)
>>>> +		__meson_nfc_read_oob(nand, page, oob_buf);  
>>>
>>> In the standalone "read_oob" hook, you have to send a READ0 command,
>>> but not when you are in the read_page hook. It is a big waste of time.  
>>
>> IIUC approach here must be exactly the same as in write? E.g. i need to
>> send single READ0 and then fill provided OOB buffer if needed?
> 
> Yes.
> 
> For both reads and writes, the logic is:
> - there are commands which are actually time consuming: there is
>   something happening on the nand array which either reads or writes
>   data to/from the internal sram.
> - there are other commands which just change the "pointer" in the
>   internal sram.
> 
> So you can basically say "I want to write into the sram at offset X,
> then at offset Y" and when the sram has been filled you can commit the
> write and that's the operation which should happen only once. In the
> read path it's the opposite, you request a read from the NAND array into
> the sram cache, and you can then request data randomly. Of course both
> operations cannot be too random either, you need to follow the ECC
> engine pattern which must be fed with X bytes of data and then will
> produce the ECC bytes to write/compare.

Thanks for this explanation! Now for example I tried to remove CMD_READ0 command
in OOB read in case when CMD_READ0 was already sent to read page data and it
works! I used 'nand_change_read_column_op()' and IIUC its internal command
CMD_RNDOUT is one of "other command which just change the pointer in the
internal sram" from above. OOB was read successfully without extra CMD_READ0!
The same thing i've implemented for write - 'nand_change_write_column_op()'
is used to send OOB data to internal SRAM, then i call PAGE_PROG and both page
data and OOB are written successfully.

Thanks for this!
 
> 
>>
>>>   
>>>> +
>>>>  	return bitflips;
>>>>  }
>>>>  
>>>>  static int meson_nfc_read_oob_raw(struct nand_chip *nand, int page)
>>>>  {
>>>> -	return meson_nfc_read_page_raw(nand, NULL, 1, page);
>>>> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
>>>>  }
>>>>  
>>>>  static int meson_nfc_read_oob(struct nand_chip *nand, int page)
>>>>  {
>>>> -	return meson_nfc_read_page_hwecc(nand, NULL, 1, page);
>>>> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
>>>> +}
>>>> +
>>>> +static int meson_nfc_write_oob_raw(struct nand_chip *nand, int page)
>>>> +{
>>>> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
>>>> +}
>>>> +
>>>> +static int meson_nfc_write_oob(struct nand_chip *nand, int page)
>>>> +{
>>>> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);  
>>>
>>> Do we really need these indirections?  
>>
>> Right, I think I can use only one function for OOB write in both ECC and raw modes.
>>
>>>   
>>>>  }
>>>>  
>>>>  static bool meson_nfc_is_buffer_dma_safe(const void *buffer)
>>>> @@ -982,7 +1086,7 @@ static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
>>>>  	if (section >= nand->ecc.steps)
>>>>  		return -ERANGE;
>>>>  
>>>> -	oobregion->offset =  2 + (section * (2 + nand->ecc.bytes));
>>>> +	oobregion->offset = NFC_USER_BYTES + section * NFC_OOB_PER_ECC(nand);  
>>>
>>> The () are still needed around the '*'
>>>   
>>>>  	oobregion->length = nand->ecc.bytes;
>>>>  
>>>>  	return 0;
>>>> @@ -992,12 +1096,16 @@ static int meson_ooblayout_free(struct mtd_info *mtd, int section,
>>>>  				struct mtd_oob_region *oobregion)
>>>>  {
>>>>  	struct nand_chip *nand = mtd_to_nand(mtd);
>>>> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
>>>>  
>>>>  	if (section >= nand->ecc.steps)
>>>>  		return -ERANGE;
>>>>  
>>>> -	oobregion->offset = section * (2 + nand->ecc.bytes);
>>>> -	oobregion->length = 2;
>>>> +	/* Split rest of OOB area (not covered by ECC engine) per each
>>>> +	 * ECC section. This will be OOB data available to user.
>>>> +	 */
>>>> +	oobregion->offset = (section + nand->ecc.steps) * NFC_OOB_PER_ECC(nand);
>>>> +	oobregion->length = oob_bytes / nand->ecc.steps;
>>>>  
>>>>  	return 0;
>>>>  }
>>>> @@ -1184,6 +1292,9 @@ static int meson_nand_bch_mode(struct nand_chip *nand)
>>>>  
>>>>  static void meson_nand_detach_chip(struct nand_chip *nand)
>>>>  {
>>>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>> +
>>>> +	kfree(meson_chip->oob_buf);
>>>>  	meson_nfc_free_buffer(nand);
>>>>  }
>>>>  
>>>> @@ -1225,9 +1336,9 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>>>>  	nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
>>>>  	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
>>>>  	nand->ecc.write_page = meson_nfc_write_page_hwecc;
>>>> -	nand->ecc.write_oob_raw = nand_write_oob_std;
>>>> -	nand->ecc.write_oob = nand_write_oob_std;
>>>>  
>>>> +	nand->ecc.write_oob_raw = meson_nfc_write_oob_raw;
>>>> +	nand->ecc.write_oob = meson_nfc_write_oob;  
>>>
>>> Actually if you define the right OOB layouts, are these really needed
>>> ?? I would expect the right bytes to be picked up by the default
>>> implementation. I see nothing specific in your current implementation?  
>>
>> You mean to use 'nand_write_oob_std()'? If so, I think it won't work,
>> because it tries to write OOB data right after 'mtd->writesize', but
>> page layout for this controller is like this:
>> [       1024 bytes of data        ]
>> [ 14 bytes ECC + 2 bytes for user ] <- with new layout we don't touch these 2 bytes
>> [       1024 bytes of data        ]
>> [ 14 bytes ECC + 2 bytes for user ] <- with new layout we don't touch these 2 bytes
>> [      32 bytes of user bytes     ] <- we use there 32 bytes as free(user) bytes in OOB
> 
> Right.
> 
>>
>> So with 'mtd->writesize' of 2048 we won't get into last 32 bytes in the picture above.
>> I'm not goot in NAND/MTD area, but I think that why i need special OOB access callbacks.
> 
> Ok.
> 

Ok, so I'll keep these callbacks in the next version.

>> Also in previous patches @Liang said, that default OOB read/write functions won't work
>> on this controller and it is wrong to use them in currently merged driver.
>>
>>>   
>>>>  	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
>>>>  	nand->ecc.read_page = meson_nfc_read_page_hwecc;
>>>>  	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
>>>> @@ -1237,9 +1348,16 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>>>>  		dev_err(nfc->dev, "16bits bus width not supported");
>>>>  		return -EINVAL;
>>>>  	}
>>>> +
>>>> +	meson_chip->oob_buf = kmalloc(nand->ecc.bytes, GFP_KERNEL);  
>>>
>>> devm_kmalloc?  
>>
>> Ack
>>
>>>   
>>>> +	if (!meson_chip->oob_buf)
>>>> +		return -ENOMEM;
>>>> +
>>>>  	ret = meson_chip_buffer_init(nand);
>>>> -	if (ret)
>>>> +	if (ret) {
>>>> +		kfree(meson_chip->oob_buf);
>>>>  		return -ENOMEM;
>>>> +	}
>>>>  
>>>>  	return ret;
>>>>  }  
>>>
>>>
>>> Thanks,
>>> Miquèl  
>>
>> Thanks, Arseniy
> 
> 
> Thanks,
> Miquèl

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

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-29 19:43           ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-29 19:43 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hello Miquel! Thanks for details and explanations!

On 26.05.2023 20:03, Miquel Raynal wrote:
> Hi Arseniy,
> 
> avkrasnov@sberdevices.ru wrote on Tue, 23 May 2023 20:17:14 +0300:
> 
>> Hello Miquel! Thanks for detailed review!
>>
>> On 22.05.2023 18:33, Miquel Raynal wrote:
>>> Hi Arseniy,
>>>
>>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:36 +0300:
>>>
>>> The title should perhaps be "only expose unprotected user OOB bytes".
>>>   
>>>> This moves free bytes of OOB to non-protected ECC area. It is needed to
>>>> make JFFS2 works correctly with this NAND controller. Problem fires when
>>>> JFFS2 driver writes cleanmarker to some page and later it tries to write
>>>> to this page - write will be done successfully, but after that such page
>>>> becomes unreadable due to invalid ECC codes. This happens because second
>>>> write needs to update ECC codes, but it is impossible to do it correctly
>>>> without block erase. So idea of this patch is to  
>>>
>>> "... is to use the unprotected OOB area to store the cleanmarkers, so
>>> that they can be written by the filesystem without caring much about
>>> the page being empty or not: the ECC codes will not be written anyway."
>>> ?  
>>
>> Ok
>>
>>>
>>> JFFS2 is only useful on tiny NAND devices, where UBI does not fit,
>>> which are usually true SLC flashes, with the capability of writing
>>> a page with empty (0xFF) data, and still be able to write actual data
>>> to it later in a second write.  
>>
>> You mean to include text above to commit message also?
> 
> Yes, I believe this deserves to be in the commit message as well :)
> 

Ack

>>
>>>   
>>>> split accesses to OOB
>>>> free bytes and data on each page - now both of them does not depends on
>>>> each other.
>>>>
>>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>>> ---
>>>>  drivers/mtd/nand/raw/meson_nand.c | 192 ++++++++++++++++++++++++------
>>>>  1 file changed, 155 insertions(+), 37 deletions(-)
>>>>
>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>>> index 2f4d8c84186b..8526a6b87720 100644
>>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>> @@ -108,6 +108,9 @@
>>>>  
>>>>  #define PER_INFO_BYTE		8
>>>>  
>>>> +#define NFC_USER_BYTES		2
>>>> +#define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
>>>> +
>>>>  struct meson_nfc_nand_chip {
>>>>  	struct list_head node;
>>>>  	struct nand_chip nand;
>>>> @@ -122,6 +125,7 @@ struct meson_nfc_nand_chip {
>>>>  	u8 *data_buf;
>>>>  	__le64 *info_buf;
>>>>  	u32 nsels;
>>>> +	u8 *oob_buf;
>>>>  	u8 sels[];
>>>>  };
>>>>  
>>>> @@ -338,7 +342,7 @@ static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
>>>>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>  	int len;
>>>>  
>>>> -	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
>>>> +	len = nand->ecc.size * (i + 1) + NFC_OOB_PER_ECC(nand) * i;  
>>>
>>> This...
>>>   
>>>>  
>>>>  	return meson_chip->data_buf + len;
>>>>  }
>>>> @@ -349,7 +353,7 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>>>>  	int len, temp;
>>>>  
>>>>  	temp = nand->ecc.size + nand->ecc.bytes;
>>>> -	len = (temp + 2) * i;
>>>> +	len = (temp + NFC_USER_BYTES) * i;  
>>>
>>> ... and this (same below)
>>>
>>> are purely cosmetic, should be in a patch aside.
>>>   
>>
>> Ack, i'll move cosmetic updates to separate patch.
>>
>>>>  
>>>>  	return meson_chip->data_buf + len;
>>>>  }
>>>> @@ -357,29 +361,47 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>>>>  static void meson_nfc_get_data_oob(struct nand_chip *nand,
>>>>  				   u8 *buf, u8 *oobbuf)
>>>>  {
>>>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>>>  	int i, oob_len = 0;
>>>>  	u8 *dsrc, *osrc;
>>>> +	u8 *oobtail;
>>>>  
>>>> -	oob_len = nand->ecc.bytes + 2;
>>>> +	oob_len = NFC_OOB_PER_ECC(nand);
>>>>  	for (i = 0; i < nand->ecc.steps; i++) {
>>>>  		if (buf) {
>>>>  			dsrc = meson_nfc_data_ptr(nand, i);
>>>>  			memcpy(buf, dsrc, nand->ecc.size);
>>>>  			buf += nand->ecc.size;
>>>>  		}
>>>> -		osrc = meson_nfc_oob_ptr(nand, i);
>>>> -		memcpy(oobbuf, osrc, oob_len);
>>>> -		oobbuf += oob_len;
>>>> +
>>>> +		if (oobbuf) {
>>>> +			osrc = meson_nfc_oob_ptr(nand, i);
>>>> +			memcpy(oobbuf, osrc, oob_len);
>>>> +			oobbuf += oob_len;
>>>> +		}
>>>>  	}
>>>> +
>>>> +	if (!oobbuf)
>>>> +		return;  
>>>
>>> The whole "if (oobbuf)" logic is nice to have, but should as well be in
>>> a dedicated patch.  
>>
>> Sorry, You mean that this logic implements read of ECC codes? And not
>> related to OOB layout update in this patch?
> 
> You need to make cosmetic changes in a first patch (or even in several
> patches if they are not related), then in another patch you might make
> additions like a better handling of the OOB, and any change in the
> layout might come last. I just want to split the patch to make
> understandable atomic changes (also easier to review).
> 
> 1 atomic change == 1 patch
> 

Ok, I'll try to split it!

>>>   
>>>> +
>>>> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
>>>> +		  (nand->ecc.size + oob_len);
>>>> +
>>>> +	/* 'oobbuf' if already shifted to the start of unused area. */  
>>>
>>> 		    is?
>>> s/unused/user/? I'm not sure I get the comment.  
>>
>> Yes, not clear comment.
>>
>>>   
>>>> +	memcpy(oobbuf, oobtail, mtd->oobsize - nand->ecc.steps * oob_len);
>>>>  }
>>>>  
>>>>  static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>>>  				   const u8 *buf, u8 *oobbuf)
>>>>  {
>>>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>>>  	int i, oob_len = 0;
>>>>  	u8 *dsrc, *osrc;
>>>> +	u8 *oobtail;
>>>>  
>>>> -	oob_len = nand->ecc.bytes + 2;
>>>> +	oob_len = NFC_OOB_PER_ECC(nand);
>>>>  	for (i = 0; i < nand->ecc.steps; i++) {
>>>>  		if (buf) {
>>>>  			dsrc = meson_nfc_data_ptr(nand, i);
>>>> @@ -390,6 +412,12 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>>>  		memcpy(osrc, oobbuf, oob_len);
>>>>  		oobbuf += oob_len;
>>>>  	}
>>>> +
>>>> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
>>>> +		  (nand->ecc.size + oob_len);
>>>> +
>>>> +	/* 'oobbuf' if already shifted to the start of unused area. */
>>>> +	memcpy(oobtail, oobbuf, mtd->oobsize - nand->ecc.steps * oob_len);
>>>>  }
>>>>  
>>>>  static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
>>>> @@ -436,25 +464,12 @@ static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>>>  {
>>>>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>  	__le64 *info;
>>>> -	int i, count;
>>>> -
>>>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>>>> -		info = &meson_chip->info_buf[i];
>>>> -		*info |= oob_buf[count];
>>>> -		*info |= oob_buf[count + 1] << 8;
>>>> -	}
>>>> -}
>>>> -
>>>> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>>> -{
>>>> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>> -	__le64 *info;
>>>> -	int i, count;
>>>> +	int i;
>>>>  
>>>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>>>> +	for (i = 0; i < nand->ecc.steps; i++) {
>>>>  		info = &meson_chip->info_buf[i];
>>>> -		oob_buf[count] = *info;
>>>> -		oob_buf[count + 1] = *info >> 8;
>>>> +		/* Always ignore user bytes programming. */  
>>>
>>> Why?  
>>
>> I think comment message is wrong a little bit. Here "user bytes" are
>> user bytes protected by ECC (e.g. location of these bytes differs from new
>> OOB layout introduced by this patch). During page write this hardware
>> always writes these bytes along with data. But, new OOB layout always ignores
>> these 4 bytes, so set them to 0xFF always.
> 
> When performing page reads/writes, you need to take the data as it's
> been provided. You may move the data around in the buffer provided to
> the controller, so that it get the ECC data at the right location, and
> you need of course to reorganize the data when reading as well, so that
> the user sees XkiB of data + YB of OOB. That's all you need to do in
> these helpers.
> 

I think there is some misunderstanding about these "user bytes" above: there are 4
bytes which this NAND controller always writes to page in ECC mode - it was free OOB
bytes covered by ECC. Controller grabs values from DMA buffer (second DMA buffer which
doesn't contains page data) and writes it along with data and ECC codes. Idea of this
change is to always suppress this write by setting them to 0xFF (may be there is some
command option to not write it, but I don't have doc), because all of them (4 bytes)
become unavailable to reader/writer.

>>> Also, maybe I should mention the helpers:
>>> mtd_ooblayout_get_eccbytes and co
>>> They are very useful to deal with OOB bytes. Everything seems extremely
>>> hardcoded in this driver, while the user can tune read/write OOB
>>> operations.  
>>
>> Thanks for details, IIUC these helpers will work correctly with this driver.
>> What means "tune" here?
> 
> The user can provide data for the OOB part and tell the mtd layer to
> place the bytes in the ->oob_buf following different constraints:
> - the user bytes are already when they need to be (MTD_OPS_PLACE_OOB
>   and MTD_OPS_RAW)
> - the user bytes are packed at the beginning of the buffer, please move
>   them where they belong (MTD_OPS_AUTO_OOB).
> 
> So in the controller driver, what needs to be done is to make the
> switch between the "data1+data2+oob1+oob2" layout into your
> ECC controller's layout, eg "data1+oob1+data2+oob2".

I checked MTD_OPS_AUTO_OOB and MTD_OPS_PLACE_OOB by 'nandwrite' utility (it
has -a option). IIUC everything works as expected, because 'nand_fill_oob()'
performs this switch. 

> 
> Hence you should not need anything like that:
>>>> +		*info |= 0xffff;
> 
>>>>  	}
>>>>  }
>>>>  
>>>> @@ -698,18 +713,92 @@ static int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf,
>>>>  	return meson_nfc_write_page_sub(nand, page, 1);
>>>>  }
>>>>  
>>>> +static u32 meson_nfc_get_oob_bytes(struct nand_chip *nand)
>>>> +{
>>>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>>> +
>>>> +	return mtd->oobsize - nand->ecc.steps * NFC_OOB_PER_ECC(nand);
>>>> +}
>>>> +
>>>> +static int __meson_nfc_write_oob(struct nand_chip *nand, int page, u8 *oob_buf)
>>>> +{
>>>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>>> +	u32 page_size = mtd->writesize + mtd->oobsize;
>>>> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
>>>> +	int ret;
>>>> +
>>>> +	if (!oob_bytes)
>>>> +		return 0;
>>>> +
>>>> +	ret = nand_prog_page_begin_op(nand, page, 0, NULL, 0);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	memcpy(meson_chip->oob_buf, oob_buf + (mtd->oobsize - oob_bytes),
>>>> +	       oob_bytes);
>>>> +
>>>> +	ret = nand_change_write_column_op(nand, page_size - oob_bytes,
>>>> +					  meson_chip->oob_buf,
>>>> +					  oob_bytes, false);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	return nand_prog_page_end_op(nand);
>>>> +}
>>>> +
>>>> +static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
>>>> +				u8 *oob_buf)
>>>> +{
>>>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>>> +	u32 oob_bytes;
>>>> +	u32 page_size;
>>>> +	int ret;
>>>> +
>>>> +	oob_bytes = meson_nfc_get_oob_bytes(nand);
>>>> +
>>>> +	if (!oob_bytes)
>>>> +		return 0;
>>>> +
>>>> +	ret = nand_read_page_op(nand, page, 0, NULL, 0);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	page_size = mtd->writesize + mtd->oobsize;
>>>> +
>>>> +	ret = nand_change_read_column_op(nand, page_size - oob_bytes,
>>>> +					 meson_chip->oob_buf,
>>>> +					 oob_bytes, false);
>>>> +
>>>> +	if (!ret)
>>>> +		memcpy(oob_buf + (mtd->oobsize - oob_bytes),
>>>> +		       meson_chip->oob_buf,
>>>> +		       oob_bytes);
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>>  static int meson_nfc_write_page_hwecc(struct nand_chip *nand,
>>>>  				      const u8 *buf, int oob_required, int page)
>>>>  {
>>>>  	struct mtd_info *mtd = nand_to_mtd(nand);
>>>>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>  	u8 *oob_buf = nand->oob_poi;
>>>> +	int ret;
>>>>  
>>>>  	memcpy(meson_chip->data_buf, buf, mtd->writesize);
>>>>  	memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE);
>>>>  	meson_nfc_set_user_byte(nand, oob_buf);
>>>>  
>>>> -	return meson_nfc_write_page_sub(nand, page, 0);
>>>> +	ret = meson_nfc_write_page_sub(nand, page, 0);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	if (oob_required)
>>>> +		ret = __meson_nfc_write_oob(nand, page, oob_buf);  
>>>
>>> You should provide all the data including OOB bytes in a single write
>>> call, otherwise you perform two writes on the same page, that's not
>>> what this helper is expected to do.  
>>
>> I see, so I need to check 'oob_required' here before programming page data?
> 
> Yes!
> 
>> If it is set -> place OOB data to DMA buffer and then call PAGEPROG once?
> 
> Exactly.
> 

Commented about this below, but it works good!

>>
>>>   
>>>> +
>>>> +	return ret;
>>>>  }
>>>>  
>>>>  static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>>>> @@ -783,7 +872,7 @@ static int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf,
>>>>  	if (ret)
>>>>  		return ret;
>>>>  
>>>> -	meson_nfc_get_data_oob(nand, buf, oob_buf);
>>>> +	meson_nfc_get_data_oob(nand, buf, oob_required ? oob_buf : NULL);
>>>>  
>>>>  	return 0;
>>>>  }
>>>> @@ -803,12 +892,12 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>>>>  	if (ret)
>>>>  		return ret;
>>>>  
>>>> -	meson_nfc_get_user_byte(nand, oob_buf);
>>>>  	ret = meson_nfc_ecc_correct(nand, &bitflips, &correct_bitmap);
>>>>  	if (ret == ECC_CHECK_RETURN_FF) {
>>>>  		if (buf)
>>>>  			memset(buf, 0xff, mtd->writesize);
>>>>  		memset(oob_buf, 0xff, mtd->oobsize);
>>>> +		return bitflips;  
>>>
>>> That is something else => other fix => other patch?  
>>
>> Idea of this 'return' is that when read fails, we return from this function
>> without reading OOB below.
> 
> It seemed like the "return bitflips" thing was new, if it's just due to
> the diff not being wide enough, then it's fine, otherwise if this is
> something new, it should be in a dedicated patch.
> 

I think i can remove it, by checking 'ret' before OOB read below. In this case
when 'ret' has failed status, OOB won't be accessed.

>>
>>>   
>>>>  	} else if (ret < 0) {
>>>>  		if ((nand->options & NAND_NEED_SCRAMBLING) || !buf) {
>>>>  			mtd->ecc_stats.failed++;
>>>> @@ -820,12 +909,14 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>>>>  
>>>>  		for (i = 0; i < nand->ecc.steps ; i++) {
>>>>  			u8 *data = buf + i * ecc->size;
>>>> -			u8 *oob = nand->oob_poi + i * (ecc->bytes + 2);
>>>> +			u8 *oob = nand->oob_poi + i * NFC_OOB_PER_ECC(nand);
>>>>  
>>>>  			if (correct_bitmap & BIT_ULL(i))
>>>>  				continue;
>>>> +
>>>>  			ret = nand_check_erased_ecc_chunk(data,	ecc->size,
>>>> -							  oob, ecc->bytes + 2,
>>>> +							  oob,
>>>> +							  NFC_OOB_PER_ECC(nand),
>>>>  							  NULL, 0,
>>>>  							  ecc->strength);
>>>>  			if (ret < 0) {
>>>> @@ -839,17 +930,30 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>>>>  		memcpy(buf, meson_chip->data_buf, mtd->writesize);
>>>>  	}
>>>>  
>>>> +	if (oob_required)
>>>> +		__meson_nfc_read_oob(nand, page, oob_buf);  
>>>
>>> In the standalone "read_oob" hook, you have to send a READ0 command,
>>> but not when you are in the read_page hook. It is a big waste of time.  
>>
>> IIUC approach here must be exactly the same as in write? E.g. i need to
>> send single READ0 and then fill provided OOB buffer if needed?
> 
> Yes.
> 
> For both reads and writes, the logic is:
> - there are commands which are actually time consuming: there is
>   something happening on the nand array which either reads or writes
>   data to/from the internal sram.
> - there are other commands which just change the "pointer" in the
>   internal sram.
> 
> So you can basically say "I want to write into the sram at offset X,
> then at offset Y" and when the sram has been filled you can commit the
> write and that's the operation which should happen only once. In the
> read path it's the opposite, you request a read from the NAND array into
> the sram cache, and you can then request data randomly. Of course both
> operations cannot be too random either, you need to follow the ECC
> engine pattern which must be fed with X bytes of data and then will
> produce the ECC bytes to write/compare.

Thanks for this explanation! Now for example I tried to remove CMD_READ0 command
in OOB read in case when CMD_READ0 was already sent to read page data and it
works! I used 'nand_change_read_column_op()' and IIUC its internal command
CMD_RNDOUT is one of "other command which just change the pointer in the
internal sram" from above. OOB was read successfully without extra CMD_READ0!
The same thing i've implemented for write - 'nand_change_write_column_op()'
is used to send OOB data to internal SRAM, then i call PAGE_PROG and both page
data and OOB are written successfully.

Thanks for this!
 
> 
>>
>>>   
>>>> +
>>>>  	return bitflips;
>>>>  }
>>>>  
>>>>  static int meson_nfc_read_oob_raw(struct nand_chip *nand, int page)
>>>>  {
>>>> -	return meson_nfc_read_page_raw(nand, NULL, 1, page);
>>>> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
>>>>  }
>>>>  
>>>>  static int meson_nfc_read_oob(struct nand_chip *nand, int page)
>>>>  {
>>>> -	return meson_nfc_read_page_hwecc(nand, NULL, 1, page);
>>>> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
>>>> +}
>>>> +
>>>> +static int meson_nfc_write_oob_raw(struct nand_chip *nand, int page)
>>>> +{
>>>> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
>>>> +}
>>>> +
>>>> +static int meson_nfc_write_oob(struct nand_chip *nand, int page)
>>>> +{
>>>> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);  
>>>
>>> Do we really need these indirections?  
>>
>> Right, I think I can use only one function for OOB write in both ECC and raw modes.
>>
>>>   
>>>>  }
>>>>  
>>>>  static bool meson_nfc_is_buffer_dma_safe(const void *buffer)
>>>> @@ -982,7 +1086,7 @@ static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
>>>>  	if (section >= nand->ecc.steps)
>>>>  		return -ERANGE;
>>>>  
>>>> -	oobregion->offset =  2 + (section * (2 + nand->ecc.bytes));
>>>> +	oobregion->offset = NFC_USER_BYTES + section * NFC_OOB_PER_ECC(nand);  
>>>
>>> The () are still needed around the '*'
>>>   
>>>>  	oobregion->length = nand->ecc.bytes;
>>>>  
>>>>  	return 0;
>>>> @@ -992,12 +1096,16 @@ static int meson_ooblayout_free(struct mtd_info *mtd, int section,
>>>>  				struct mtd_oob_region *oobregion)
>>>>  {
>>>>  	struct nand_chip *nand = mtd_to_nand(mtd);
>>>> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
>>>>  
>>>>  	if (section >= nand->ecc.steps)
>>>>  		return -ERANGE;
>>>>  
>>>> -	oobregion->offset = section * (2 + nand->ecc.bytes);
>>>> -	oobregion->length = 2;
>>>> +	/* Split rest of OOB area (not covered by ECC engine) per each
>>>> +	 * ECC section. This will be OOB data available to user.
>>>> +	 */
>>>> +	oobregion->offset = (section + nand->ecc.steps) * NFC_OOB_PER_ECC(nand);
>>>> +	oobregion->length = oob_bytes / nand->ecc.steps;
>>>>  
>>>>  	return 0;
>>>>  }
>>>> @@ -1184,6 +1292,9 @@ static int meson_nand_bch_mode(struct nand_chip *nand)
>>>>  
>>>>  static void meson_nand_detach_chip(struct nand_chip *nand)
>>>>  {
>>>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>> +
>>>> +	kfree(meson_chip->oob_buf);
>>>>  	meson_nfc_free_buffer(nand);
>>>>  }
>>>>  
>>>> @@ -1225,9 +1336,9 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>>>>  	nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
>>>>  	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
>>>>  	nand->ecc.write_page = meson_nfc_write_page_hwecc;
>>>> -	nand->ecc.write_oob_raw = nand_write_oob_std;
>>>> -	nand->ecc.write_oob = nand_write_oob_std;
>>>>  
>>>> +	nand->ecc.write_oob_raw = meson_nfc_write_oob_raw;
>>>> +	nand->ecc.write_oob = meson_nfc_write_oob;  
>>>
>>> Actually if you define the right OOB layouts, are these really needed
>>> ?? I would expect the right bytes to be picked up by the default
>>> implementation. I see nothing specific in your current implementation?  
>>
>> You mean to use 'nand_write_oob_std()'? If so, I think it won't work,
>> because it tries to write OOB data right after 'mtd->writesize', but
>> page layout for this controller is like this:
>> [       1024 bytes of data        ]
>> [ 14 bytes ECC + 2 bytes for user ] <- with new layout we don't touch these 2 bytes
>> [       1024 bytes of data        ]
>> [ 14 bytes ECC + 2 bytes for user ] <- with new layout we don't touch these 2 bytes
>> [      32 bytes of user bytes     ] <- we use there 32 bytes as free(user) bytes in OOB
> 
> Right.
> 
>>
>> So with 'mtd->writesize' of 2048 we won't get into last 32 bytes in the picture above.
>> I'm not goot in NAND/MTD area, but I think that why i need special OOB access callbacks.
> 
> Ok.
> 

Ok, so I'll keep these callbacks in the next version.

>> Also in previous patches @Liang said, that default OOB read/write functions won't work
>> on this controller and it is wrong to use them in currently merged driver.
>>
>>>   
>>>>  	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
>>>>  	nand->ecc.read_page = meson_nfc_read_page_hwecc;
>>>>  	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
>>>> @@ -1237,9 +1348,16 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>>>>  		dev_err(nfc->dev, "16bits bus width not supported");
>>>>  		return -EINVAL;
>>>>  	}
>>>> +
>>>> +	meson_chip->oob_buf = kmalloc(nand->ecc.bytes, GFP_KERNEL);  
>>>
>>> devm_kmalloc?  
>>
>> Ack
>>
>>>   
>>>> +	if (!meson_chip->oob_buf)
>>>> +		return -ENOMEM;
>>>> +
>>>>  	ret = meson_chip_buffer_init(nand);
>>>> -	if (ret)
>>>> +	if (ret) {
>>>> +		kfree(meson_chip->oob_buf);
>>>>  		return -ENOMEM;
>>>> +	}
>>>>  
>>>>  	return ret;
>>>>  }  
>>>
>>>
>>> Thanks,
>>> Miquèl  
>>
>> Thanks, Arseniy
> 
> 
> Thanks,
> Miquèl

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-29 19:43           ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-29 19:43 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hello Miquel! Thanks for details and explanations!

On 26.05.2023 20:03, Miquel Raynal wrote:
> Hi Arseniy,
> 
> avkrasnov@sberdevices.ru wrote on Tue, 23 May 2023 20:17:14 +0300:
> 
>> Hello Miquel! Thanks for detailed review!
>>
>> On 22.05.2023 18:33, Miquel Raynal wrote:
>>> Hi Arseniy,
>>>
>>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:36 +0300:
>>>
>>> The title should perhaps be "only expose unprotected user OOB bytes".
>>>   
>>>> This moves free bytes of OOB to non-protected ECC area. It is needed to
>>>> make JFFS2 works correctly with this NAND controller. Problem fires when
>>>> JFFS2 driver writes cleanmarker to some page and later it tries to write
>>>> to this page - write will be done successfully, but after that such page
>>>> becomes unreadable due to invalid ECC codes. This happens because second
>>>> write needs to update ECC codes, but it is impossible to do it correctly
>>>> without block erase. So idea of this patch is to  
>>>
>>> "... is to use the unprotected OOB area to store the cleanmarkers, so
>>> that they can be written by the filesystem without caring much about
>>> the page being empty or not: the ECC codes will not be written anyway."
>>> ?  
>>
>> Ok
>>
>>>
>>> JFFS2 is only useful on tiny NAND devices, where UBI does not fit,
>>> which are usually true SLC flashes, with the capability of writing
>>> a page with empty (0xFF) data, and still be able to write actual data
>>> to it later in a second write.  
>>
>> You mean to include text above to commit message also?
> 
> Yes, I believe this deserves to be in the commit message as well :)
> 

Ack

>>
>>>   
>>>> split accesses to OOB
>>>> free bytes and data on each page - now both of them does not depends on
>>>> each other.
>>>>
>>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>>> ---
>>>>  drivers/mtd/nand/raw/meson_nand.c | 192 ++++++++++++++++++++++++------
>>>>  1 file changed, 155 insertions(+), 37 deletions(-)
>>>>
>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>>> index 2f4d8c84186b..8526a6b87720 100644
>>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>> @@ -108,6 +108,9 @@
>>>>  
>>>>  #define PER_INFO_BYTE		8
>>>>  
>>>> +#define NFC_USER_BYTES		2
>>>> +#define NFC_OOB_PER_ECC(nand)	((nand)->ecc.bytes + NFC_USER_BYTES)
>>>> +
>>>>  struct meson_nfc_nand_chip {
>>>>  	struct list_head node;
>>>>  	struct nand_chip nand;
>>>> @@ -122,6 +125,7 @@ struct meson_nfc_nand_chip {
>>>>  	u8 *data_buf;
>>>>  	__le64 *info_buf;
>>>>  	u32 nsels;
>>>> +	u8 *oob_buf;
>>>>  	u8 sels[];
>>>>  };
>>>>  
>>>> @@ -338,7 +342,7 @@ static u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
>>>>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>  	int len;
>>>>  
>>>> -	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
>>>> +	len = nand->ecc.size * (i + 1) + NFC_OOB_PER_ECC(nand) * i;  
>>>
>>> This...
>>>   
>>>>  
>>>>  	return meson_chip->data_buf + len;
>>>>  }
>>>> @@ -349,7 +353,7 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>>>>  	int len, temp;
>>>>  
>>>>  	temp = nand->ecc.size + nand->ecc.bytes;
>>>> -	len = (temp + 2) * i;
>>>> +	len = (temp + NFC_USER_BYTES) * i;  
>>>
>>> ... and this (same below)
>>>
>>> are purely cosmetic, should be in a patch aside.
>>>   
>>
>> Ack, i'll move cosmetic updates to separate patch.
>>
>>>>  
>>>>  	return meson_chip->data_buf + len;
>>>>  }
>>>> @@ -357,29 +361,47 @@ static u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
>>>>  static void meson_nfc_get_data_oob(struct nand_chip *nand,
>>>>  				   u8 *buf, u8 *oobbuf)
>>>>  {
>>>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>>>  	int i, oob_len = 0;
>>>>  	u8 *dsrc, *osrc;
>>>> +	u8 *oobtail;
>>>>  
>>>> -	oob_len = nand->ecc.bytes + 2;
>>>> +	oob_len = NFC_OOB_PER_ECC(nand);
>>>>  	for (i = 0; i < nand->ecc.steps; i++) {
>>>>  		if (buf) {
>>>>  			dsrc = meson_nfc_data_ptr(nand, i);
>>>>  			memcpy(buf, dsrc, nand->ecc.size);
>>>>  			buf += nand->ecc.size;
>>>>  		}
>>>> -		osrc = meson_nfc_oob_ptr(nand, i);
>>>> -		memcpy(oobbuf, osrc, oob_len);
>>>> -		oobbuf += oob_len;
>>>> +
>>>> +		if (oobbuf) {
>>>> +			osrc = meson_nfc_oob_ptr(nand, i);
>>>> +			memcpy(oobbuf, osrc, oob_len);
>>>> +			oobbuf += oob_len;
>>>> +		}
>>>>  	}
>>>> +
>>>> +	if (!oobbuf)
>>>> +		return;  
>>>
>>> The whole "if (oobbuf)" logic is nice to have, but should as well be in
>>> a dedicated patch.  
>>
>> Sorry, You mean that this logic implements read of ECC codes? And not
>> related to OOB layout update in this patch?
> 
> You need to make cosmetic changes in a first patch (or even in several
> patches if they are not related), then in another patch you might make
> additions like a better handling of the OOB, and any change in the
> layout might come last. I just want to split the patch to make
> understandable atomic changes (also easier to review).
> 
> 1 atomic change == 1 patch
> 

Ok, I'll try to split it!

>>>   
>>>> +
>>>> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
>>>> +		  (nand->ecc.size + oob_len);
>>>> +
>>>> +	/* 'oobbuf' if already shifted to the start of unused area. */  
>>>
>>> 		    is?
>>> s/unused/user/? I'm not sure I get the comment.  
>>
>> Yes, not clear comment.
>>
>>>   
>>>> +	memcpy(oobbuf, oobtail, mtd->oobsize - nand->ecc.steps * oob_len);
>>>>  }
>>>>  
>>>>  static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>>>  				   const u8 *buf, u8 *oobbuf)
>>>>  {
>>>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>>>  	int i, oob_len = 0;
>>>>  	u8 *dsrc, *osrc;
>>>> +	u8 *oobtail;
>>>>  
>>>> -	oob_len = nand->ecc.bytes + 2;
>>>> +	oob_len = NFC_OOB_PER_ECC(nand);
>>>>  	for (i = 0; i < nand->ecc.steps; i++) {
>>>>  		if (buf) {
>>>>  			dsrc = meson_nfc_data_ptr(nand, i);
>>>> @@ -390,6 +412,12 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand,
>>>>  		memcpy(osrc, oobbuf, oob_len);
>>>>  		oobbuf += oob_len;
>>>>  	}
>>>> +
>>>> +	oobtail = meson_chip->data_buf + nand->ecc.steps *
>>>> +		  (nand->ecc.size + oob_len);
>>>> +
>>>> +	/* 'oobbuf' if already shifted to the start of unused area. */
>>>> +	memcpy(oobtail, oobbuf, mtd->oobsize - nand->ecc.steps * oob_len);
>>>>  }
>>>>  
>>>>  static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, int cmd_read0)
>>>> @@ -436,25 +464,12 @@ static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>>>  {
>>>>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>  	__le64 *info;
>>>> -	int i, count;
>>>> -
>>>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>>>> -		info = &meson_chip->info_buf[i];
>>>> -		*info |= oob_buf[count];
>>>> -		*info |= oob_buf[count + 1] << 8;
>>>> -	}
>>>> -}
>>>> -
>>>> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>>> -{
>>>> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>> -	__le64 *info;
>>>> -	int i, count;
>>>> +	int i;
>>>>  
>>>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>>>> +	for (i = 0; i < nand->ecc.steps; i++) {
>>>>  		info = &meson_chip->info_buf[i];
>>>> -		oob_buf[count] = *info;
>>>> -		oob_buf[count + 1] = *info >> 8;
>>>> +		/* Always ignore user bytes programming. */  
>>>
>>> Why?  
>>
>> I think comment message is wrong a little bit. Here "user bytes" are
>> user bytes protected by ECC (e.g. location of these bytes differs from new
>> OOB layout introduced by this patch). During page write this hardware
>> always writes these bytes along with data. But, new OOB layout always ignores
>> these 4 bytes, so set them to 0xFF always.
> 
> When performing page reads/writes, you need to take the data as it's
> been provided. You may move the data around in the buffer provided to
> the controller, so that it get the ECC data at the right location, and
> you need of course to reorganize the data when reading as well, so that
> the user sees XkiB of data + YB of OOB. That's all you need to do in
> these helpers.
> 

I think there is some misunderstanding about these "user bytes" above: there are 4
bytes which this NAND controller always writes to page in ECC mode - it was free OOB
bytes covered by ECC. Controller grabs values from DMA buffer (second DMA buffer which
doesn't contains page data) and writes it along with data and ECC codes. Idea of this
change is to always suppress this write by setting them to 0xFF (may be there is some
command option to not write it, but I don't have doc), because all of them (4 bytes)
become unavailable to reader/writer.

>>> Also, maybe I should mention the helpers:
>>> mtd_ooblayout_get_eccbytes and co
>>> They are very useful to deal with OOB bytes. Everything seems extremely
>>> hardcoded in this driver, while the user can tune read/write OOB
>>> operations.  
>>
>> Thanks for details, IIUC these helpers will work correctly with this driver.
>> What means "tune" here?
> 
> The user can provide data for the OOB part and tell the mtd layer to
> place the bytes in the ->oob_buf following different constraints:
> - the user bytes are already when they need to be (MTD_OPS_PLACE_OOB
>   and MTD_OPS_RAW)
> - the user bytes are packed at the beginning of the buffer, please move
>   them where they belong (MTD_OPS_AUTO_OOB).
> 
> So in the controller driver, what needs to be done is to make the
> switch between the "data1+data2+oob1+oob2" layout into your
> ECC controller's layout, eg "data1+oob1+data2+oob2".

I checked MTD_OPS_AUTO_OOB and MTD_OPS_PLACE_OOB by 'nandwrite' utility (it
has -a option). IIUC everything works as expected, because 'nand_fill_oob()'
performs this switch. 

> 
> Hence you should not need anything like that:
>>>> +		*info |= 0xffff;
> 
>>>>  	}
>>>>  }
>>>>  
>>>> @@ -698,18 +713,92 @@ static int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf,
>>>>  	return meson_nfc_write_page_sub(nand, page, 1);
>>>>  }
>>>>  
>>>> +static u32 meson_nfc_get_oob_bytes(struct nand_chip *nand)
>>>> +{
>>>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>>> +
>>>> +	return mtd->oobsize - nand->ecc.steps * NFC_OOB_PER_ECC(nand);
>>>> +}
>>>> +
>>>> +static int __meson_nfc_write_oob(struct nand_chip *nand, int page, u8 *oob_buf)
>>>> +{
>>>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>>> +	u32 page_size = mtd->writesize + mtd->oobsize;
>>>> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
>>>> +	int ret;
>>>> +
>>>> +	if (!oob_bytes)
>>>> +		return 0;
>>>> +
>>>> +	ret = nand_prog_page_begin_op(nand, page, 0, NULL, 0);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	memcpy(meson_chip->oob_buf, oob_buf + (mtd->oobsize - oob_bytes),
>>>> +	       oob_bytes);
>>>> +
>>>> +	ret = nand_change_write_column_op(nand, page_size - oob_bytes,
>>>> +					  meson_chip->oob_buf,
>>>> +					  oob_bytes, false);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	return nand_prog_page_end_op(nand);
>>>> +}
>>>> +
>>>> +static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
>>>> +				u8 *oob_buf)
>>>> +{
>>>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>> +	struct mtd_info *mtd = nand_to_mtd(nand);
>>>> +	u32 oob_bytes;
>>>> +	u32 page_size;
>>>> +	int ret;
>>>> +
>>>> +	oob_bytes = meson_nfc_get_oob_bytes(nand);
>>>> +
>>>> +	if (!oob_bytes)
>>>> +		return 0;
>>>> +
>>>> +	ret = nand_read_page_op(nand, page, 0, NULL, 0);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	page_size = mtd->writesize + mtd->oobsize;
>>>> +
>>>> +	ret = nand_change_read_column_op(nand, page_size - oob_bytes,
>>>> +					 meson_chip->oob_buf,
>>>> +					 oob_bytes, false);
>>>> +
>>>> +	if (!ret)
>>>> +		memcpy(oob_buf + (mtd->oobsize - oob_bytes),
>>>> +		       meson_chip->oob_buf,
>>>> +		       oob_bytes);
>>>> +
>>>> +	return ret;
>>>> +}
>>>> +
>>>>  static int meson_nfc_write_page_hwecc(struct nand_chip *nand,
>>>>  				      const u8 *buf, int oob_required, int page)
>>>>  {
>>>>  	struct mtd_info *mtd = nand_to_mtd(nand);
>>>>  	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>  	u8 *oob_buf = nand->oob_poi;
>>>> +	int ret;
>>>>  
>>>>  	memcpy(meson_chip->data_buf, buf, mtd->writesize);
>>>>  	memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE);
>>>>  	meson_nfc_set_user_byte(nand, oob_buf);
>>>>  
>>>> -	return meson_nfc_write_page_sub(nand, page, 0);
>>>> +	ret = meson_nfc_write_page_sub(nand, page, 0);
>>>> +	if (ret)
>>>> +		return ret;
>>>> +
>>>> +	if (oob_required)
>>>> +		ret = __meson_nfc_write_oob(nand, page, oob_buf);  
>>>
>>> You should provide all the data including OOB bytes in a single write
>>> call, otherwise you perform two writes on the same page, that's not
>>> what this helper is expected to do.  
>>
>> I see, so I need to check 'oob_required' here before programming page data?
> 
> Yes!
> 
>> If it is set -> place OOB data to DMA buffer and then call PAGEPROG once?
> 
> Exactly.
> 

Commented about this below, but it works good!

>>
>>>   
>>>> +
>>>> +	return ret;
>>>>  }
>>>>  
>>>>  static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
>>>> @@ -783,7 +872,7 @@ static int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf,
>>>>  	if (ret)
>>>>  		return ret;
>>>>  
>>>> -	meson_nfc_get_data_oob(nand, buf, oob_buf);
>>>> +	meson_nfc_get_data_oob(nand, buf, oob_required ? oob_buf : NULL);
>>>>  
>>>>  	return 0;
>>>>  }
>>>> @@ -803,12 +892,12 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>>>>  	if (ret)
>>>>  		return ret;
>>>>  
>>>> -	meson_nfc_get_user_byte(nand, oob_buf);
>>>>  	ret = meson_nfc_ecc_correct(nand, &bitflips, &correct_bitmap);
>>>>  	if (ret == ECC_CHECK_RETURN_FF) {
>>>>  		if (buf)
>>>>  			memset(buf, 0xff, mtd->writesize);
>>>>  		memset(oob_buf, 0xff, mtd->oobsize);
>>>> +		return bitflips;  
>>>
>>> That is something else => other fix => other patch?  
>>
>> Idea of this 'return' is that when read fails, we return from this function
>> without reading OOB below.
> 
> It seemed like the "return bitflips" thing was new, if it's just due to
> the diff not being wide enough, then it's fine, otherwise if this is
> something new, it should be in a dedicated patch.
> 

I think i can remove it, by checking 'ret' before OOB read below. In this case
when 'ret' has failed status, OOB won't be accessed.

>>
>>>   
>>>>  	} else if (ret < 0) {
>>>>  		if ((nand->options & NAND_NEED_SCRAMBLING) || !buf) {
>>>>  			mtd->ecc_stats.failed++;
>>>> @@ -820,12 +909,14 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>>>>  
>>>>  		for (i = 0; i < nand->ecc.steps ; i++) {
>>>>  			u8 *data = buf + i * ecc->size;
>>>> -			u8 *oob = nand->oob_poi + i * (ecc->bytes + 2);
>>>> +			u8 *oob = nand->oob_poi + i * NFC_OOB_PER_ECC(nand);
>>>>  
>>>>  			if (correct_bitmap & BIT_ULL(i))
>>>>  				continue;
>>>> +
>>>>  			ret = nand_check_erased_ecc_chunk(data,	ecc->size,
>>>> -							  oob, ecc->bytes + 2,
>>>> +							  oob,
>>>> +							  NFC_OOB_PER_ECC(nand),
>>>>  							  NULL, 0,
>>>>  							  ecc->strength);
>>>>  			if (ret < 0) {
>>>> @@ -839,17 +930,30 @@ static int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
>>>>  		memcpy(buf, meson_chip->data_buf, mtd->writesize);
>>>>  	}
>>>>  
>>>> +	if (oob_required)
>>>> +		__meson_nfc_read_oob(nand, page, oob_buf);  
>>>
>>> In the standalone "read_oob" hook, you have to send a READ0 command,
>>> but not when you are in the read_page hook. It is a big waste of time.  
>>
>> IIUC approach here must be exactly the same as in write? E.g. i need to
>> send single READ0 and then fill provided OOB buffer if needed?
> 
> Yes.
> 
> For both reads and writes, the logic is:
> - there are commands which are actually time consuming: there is
>   something happening on the nand array which either reads or writes
>   data to/from the internal sram.
> - there are other commands which just change the "pointer" in the
>   internal sram.
> 
> So you can basically say "I want to write into the sram at offset X,
> then at offset Y" and when the sram has been filled you can commit the
> write and that's the operation which should happen only once. In the
> read path it's the opposite, you request a read from the NAND array into
> the sram cache, and you can then request data randomly. Of course both
> operations cannot be too random either, you need to follow the ECC
> engine pattern which must be fed with X bytes of data and then will
> produce the ECC bytes to write/compare.

Thanks for this explanation! Now for example I tried to remove CMD_READ0 command
in OOB read in case when CMD_READ0 was already sent to read page data and it
works! I used 'nand_change_read_column_op()' and IIUC its internal command
CMD_RNDOUT is one of "other command which just change the pointer in the
internal sram" from above. OOB was read successfully without extra CMD_READ0!
The same thing i've implemented for write - 'nand_change_write_column_op()'
is used to send OOB data to internal SRAM, then i call PAGE_PROG and both page
data and OOB are written successfully.

Thanks for this!
 
> 
>>
>>>   
>>>> +
>>>>  	return bitflips;
>>>>  }
>>>>  
>>>>  static int meson_nfc_read_oob_raw(struct nand_chip *nand, int page)
>>>>  {
>>>> -	return meson_nfc_read_page_raw(nand, NULL, 1, page);
>>>> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
>>>>  }
>>>>  
>>>>  static int meson_nfc_read_oob(struct nand_chip *nand, int page)
>>>>  {
>>>> -	return meson_nfc_read_page_hwecc(nand, NULL, 1, page);
>>>> +	return __meson_nfc_read_oob(nand, page, nand->oob_poi);
>>>> +}
>>>> +
>>>> +static int meson_nfc_write_oob_raw(struct nand_chip *nand, int page)
>>>> +{
>>>> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);
>>>> +}
>>>> +
>>>> +static int meson_nfc_write_oob(struct nand_chip *nand, int page)
>>>> +{
>>>> +	return __meson_nfc_write_oob(nand, page, nand->oob_poi);  
>>>
>>> Do we really need these indirections?  
>>
>> Right, I think I can use only one function for OOB write in both ECC and raw modes.
>>
>>>   
>>>>  }
>>>>  
>>>>  static bool meson_nfc_is_buffer_dma_safe(const void *buffer)
>>>> @@ -982,7 +1086,7 @@ static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
>>>>  	if (section >= nand->ecc.steps)
>>>>  		return -ERANGE;
>>>>  
>>>> -	oobregion->offset =  2 + (section * (2 + nand->ecc.bytes));
>>>> +	oobregion->offset = NFC_USER_BYTES + section * NFC_OOB_PER_ECC(nand);  
>>>
>>> The () are still needed around the '*'
>>>   
>>>>  	oobregion->length = nand->ecc.bytes;
>>>>  
>>>>  	return 0;
>>>> @@ -992,12 +1096,16 @@ static int meson_ooblayout_free(struct mtd_info *mtd, int section,
>>>>  				struct mtd_oob_region *oobregion)
>>>>  {
>>>>  	struct nand_chip *nand = mtd_to_nand(mtd);
>>>> +	u32 oob_bytes = meson_nfc_get_oob_bytes(nand);
>>>>  
>>>>  	if (section >= nand->ecc.steps)
>>>>  		return -ERANGE;
>>>>  
>>>> -	oobregion->offset = section * (2 + nand->ecc.bytes);
>>>> -	oobregion->length = 2;
>>>> +	/* Split rest of OOB area (not covered by ECC engine) per each
>>>> +	 * ECC section. This will be OOB data available to user.
>>>> +	 */
>>>> +	oobregion->offset = (section + nand->ecc.steps) * NFC_OOB_PER_ECC(nand);
>>>> +	oobregion->length = oob_bytes / nand->ecc.steps;
>>>>  
>>>>  	return 0;
>>>>  }
>>>> @@ -1184,6 +1292,9 @@ static int meson_nand_bch_mode(struct nand_chip *nand)
>>>>  
>>>>  static void meson_nand_detach_chip(struct nand_chip *nand)
>>>>  {
>>>> +	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>> +
>>>> +	kfree(meson_chip->oob_buf);
>>>>  	meson_nfc_free_buffer(nand);
>>>>  }
>>>>  
>>>> @@ -1225,9 +1336,9 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>>>>  	nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
>>>>  	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
>>>>  	nand->ecc.write_page = meson_nfc_write_page_hwecc;
>>>> -	nand->ecc.write_oob_raw = nand_write_oob_std;
>>>> -	nand->ecc.write_oob = nand_write_oob_std;
>>>>  
>>>> +	nand->ecc.write_oob_raw = meson_nfc_write_oob_raw;
>>>> +	nand->ecc.write_oob = meson_nfc_write_oob;  
>>>
>>> Actually if you define the right OOB layouts, are these really needed
>>> ?? I would expect the right bytes to be picked up by the default
>>> implementation. I see nothing specific in your current implementation?  
>>
>> You mean to use 'nand_write_oob_std()'? If so, I think it won't work,
>> because it tries to write OOB data right after 'mtd->writesize', but
>> page layout for this controller is like this:
>> [       1024 bytes of data        ]
>> [ 14 bytes ECC + 2 bytes for user ] <- with new layout we don't touch these 2 bytes
>> [       1024 bytes of data        ]
>> [ 14 bytes ECC + 2 bytes for user ] <- with new layout we don't touch these 2 bytes
>> [      32 bytes of user bytes     ] <- we use there 32 bytes as free(user) bytes in OOB
> 
> Right.
> 
>>
>> So with 'mtd->writesize' of 2048 we won't get into last 32 bytes in the picture above.
>> I'm not goot in NAND/MTD area, but I think that why i need special OOB access callbacks.
> 
> Ok.
> 

Ok, so I'll keep these callbacks in the next version.

>> Also in previous patches @Liang said, that default OOB read/write functions won't work
>> on this controller and it is wrong to use them in currently merged driver.
>>
>>>   
>>>>  	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
>>>>  	nand->ecc.read_page = meson_nfc_read_page_hwecc;
>>>>  	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
>>>> @@ -1237,9 +1348,16 @@ static int meson_nand_attach_chip(struct nand_chip *nand)
>>>>  		dev_err(nfc->dev, "16bits bus width not supported");
>>>>  		return -EINVAL;
>>>>  	}
>>>> +
>>>> +	meson_chip->oob_buf = kmalloc(nand->ecc.bytes, GFP_KERNEL);  
>>>
>>> devm_kmalloc?  
>>
>> Ack
>>
>>>   
>>>> +	if (!meson_chip->oob_buf)
>>>> +		return -ENOMEM;
>>>> +
>>>>  	ret = meson_chip_buffer_init(nand);
>>>> -	if (ret)
>>>> +	if (ret) {
>>>> +		kfree(meson_chip->oob_buf);
>>>>  		return -ENOMEM;
>>>> +	}
>>>>  
>>>>  	return ret;
>>>>  }  
>>>
>>>
>>> Thanks,
>>> Miquèl  
>>
>> Thanks, Arseniy
> 
> 
> Thanks,
> Miquèl

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* Re: [PATCH v4 3/5] mtd: rawnand: meson: always read whole OOB bytes
  2023-05-26 17:09         ` Miquel Raynal
  (?)
  (?)
@ 2023-05-29 19:46           ` Arseniy Krasnov
  -1 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-29 19:46 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Jianxin Pan, Yixun Lan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel



On 26.05.2023 20:09, Miquel Raynal wrote:
> Hi Arseniy,
> 
> avkrasnov@sberdevices.ru wrote on Tue, 23 May 2023 20:27:35 +0300:
> 
>> On 22.05.2023 18:38, Miquel Raynal wrote:
>>> Hi Arseniy,
>>>
>>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:37 +0300:
>>>   
>>>> This changes size of read access to OOB area by reading all bytes of
>>>> OOB (free bytes + ECC engine bytes).  
>>>
>>> This is normally up to the user (user in your case == jffs2). The
>>> controller driver should expose a number of user accessible bytes and
>>> then when users want the OOB area, they should access it entirely. On
>>> top of that read, they can extract (or "write only") the user bytes.  
>>
>> Sorry, I didn't get it. If driver exposes N bytes of user accessible bytes,
>> I must always return whole OOB yes? E.g. N + rest of OOB
> 
> Yes. At the NAND controller level, you get asked for either a page of
> data (sometimes a subpage, but whatever), and/or the oob area. You need
> to provide what is requested, no more, no less. The upper layers will
> trim down what's uneeded and extract the bytes they want.

I see, so in this case I think this patch could be merged to the patch which
changes OOB layout be moving it out of ECC area? Because driver MUST return all
bytes of OOB area.

> 
>>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>>> ---
>>>>  drivers/mtd/nand/raw/meson_nand.c | 24 ++++++++++++++++++++++++
>>>>  1 file changed, 24 insertions(+)
>>>>
>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>>> index 8526a6b87720..a31106c943d7 100644
>>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>> @@ -755,6 +755,30 @@ static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
>>>>  	u32 oob_bytes;
>>>>  	u32 page_size;
>>>>  	int ret;
>>>> +	int i;
>>>> +
>>>> +	/* Read ECC codes and user bytes. */
>>>> +	for (i = 0; i < nand->ecc.steps; i++) {
>>>> +		u32 ecc_offs = nand->ecc.size * (i + 1) +
>>>> +			       NFC_OOB_PER_ECC(nand) * i;
>>>> +
>>>> +		ret = nand_read_page_op(nand, page, 0, NULL, 0);
>>>> +		if (ret)
>>>> +			return ret;
>>>> +
>>>> +		/* Use temporary buffer, because 'nand_change_read_column_op()'
>>>> +		 * seems work with some alignment, so we can't read data to
>>>> +		 * 'oob_buf' directly.  
>>>
>>> DMA?  
>>
>> Yes I guess, this address passed to exec_op code and used as DMA.
> 
> If your controller uses DMA on exec_op accesses, then yes. Exec_op
> reads/writes are usually small enough (or not time sensitive at all if
> they are bigger) so it's not required to use DMA there. Anyhow, oob_buf
> is suitable for DMA purposes, so I'm a bit surprised you need a bounce
> buffer, if that's the only reason. Maybe you need a bounce buffer to
> reorganize the data. That would be a much better explanation.

Yes! I remove this temporary buffer, seems my mistake! Without it everything works
good, I'll remove it from the next version!

Thanks, Arseniy

> 
>>>> +		 */
>>>> +		ret = nand_change_read_column_op(nand, ecc_offs, meson_chip->oob_buf,
>>>> +						 NFC_OOB_PER_ECC(nand), false);
>>>> +		if (ret)
>>>> +			return ret;
>>>> +
>>>> +		memcpy(oob_buf + i * NFC_OOB_PER_ECC(nand),
>>>> +		       meson_chip->oob_buf,
>>>> +		       NFC_OOB_PER_ECC(nand));
>>>> +	}
>>>>  
>>>>  	oob_bytes = meson_nfc_get_oob_bytes(nand);
>>>>    
>>>
>>>
>>> Thanks,
>>> Miquèl  
>>
>> Thanks, Arseniy
> 
> 
> Thanks,
> Miquèl

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

* Re: [PATCH v4 3/5] mtd: rawnand: meson: always read whole OOB bytes
@ 2023-05-29 19:46           ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-29 19:46 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Jianxin Pan, Yixun Lan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel



On 26.05.2023 20:09, Miquel Raynal wrote:
> Hi Arseniy,
> 
> avkrasnov@sberdevices.ru wrote on Tue, 23 May 2023 20:27:35 +0300:
> 
>> On 22.05.2023 18:38, Miquel Raynal wrote:
>>> Hi Arseniy,
>>>
>>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:37 +0300:
>>>   
>>>> This changes size of read access to OOB area by reading all bytes of
>>>> OOB (free bytes + ECC engine bytes).  
>>>
>>> This is normally up to the user (user in your case == jffs2). The
>>> controller driver should expose a number of user accessible bytes and
>>> then when users want the OOB area, they should access it entirely. On
>>> top of that read, they can extract (or "write only") the user bytes.  
>>
>> Sorry, I didn't get it. If driver exposes N bytes of user accessible bytes,
>> I must always return whole OOB yes? E.g. N + rest of OOB
> 
> Yes. At the NAND controller level, you get asked for either a page of
> data (sometimes a subpage, but whatever), and/or the oob area. You need
> to provide what is requested, no more, no less. The upper layers will
> trim down what's uneeded and extract the bytes they want.

I see, so in this case I think this patch could be merged to the patch which
changes OOB layout be moving it out of ECC area? Because driver MUST return all
bytes of OOB area.

> 
>>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>>> ---
>>>>  drivers/mtd/nand/raw/meson_nand.c | 24 ++++++++++++++++++++++++
>>>>  1 file changed, 24 insertions(+)
>>>>
>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>>> index 8526a6b87720..a31106c943d7 100644
>>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>> @@ -755,6 +755,30 @@ static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
>>>>  	u32 oob_bytes;
>>>>  	u32 page_size;
>>>>  	int ret;
>>>> +	int i;
>>>> +
>>>> +	/* Read ECC codes and user bytes. */
>>>> +	for (i = 0; i < nand->ecc.steps; i++) {
>>>> +		u32 ecc_offs = nand->ecc.size * (i + 1) +
>>>> +			       NFC_OOB_PER_ECC(nand) * i;
>>>> +
>>>> +		ret = nand_read_page_op(nand, page, 0, NULL, 0);
>>>> +		if (ret)
>>>> +			return ret;
>>>> +
>>>> +		/* Use temporary buffer, because 'nand_change_read_column_op()'
>>>> +		 * seems work with some alignment, so we can't read data to
>>>> +		 * 'oob_buf' directly.  
>>>
>>> DMA?  
>>
>> Yes I guess, this address passed to exec_op code and used as DMA.
> 
> If your controller uses DMA on exec_op accesses, then yes. Exec_op
> reads/writes are usually small enough (or not time sensitive at all if
> they are bigger) so it's not required to use DMA there. Anyhow, oob_buf
> is suitable for DMA purposes, so I'm a bit surprised you need a bounce
> buffer, if that's the only reason. Maybe you need a bounce buffer to
> reorganize the data. That would be a much better explanation.

Yes! I remove this temporary buffer, seems my mistake! Without it everything works
good, I'll remove it from the next version!

Thanks, Arseniy

> 
>>>> +		 */
>>>> +		ret = nand_change_read_column_op(nand, ecc_offs, meson_chip->oob_buf,
>>>> +						 NFC_OOB_PER_ECC(nand), false);
>>>> +		if (ret)
>>>> +			return ret;
>>>> +
>>>> +		memcpy(oob_buf + i * NFC_OOB_PER_ECC(nand),
>>>> +		       meson_chip->oob_buf,
>>>> +		       NFC_OOB_PER_ECC(nand));
>>>> +	}
>>>>  
>>>>  	oob_bytes = meson_nfc_get_oob_bytes(nand);
>>>>    
>>>
>>>
>>> Thanks,
>>> Miquèl  
>>
>> Thanks, Arseniy
> 
> 
> Thanks,
> Miquèl

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

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

* Re: [PATCH v4 3/5] mtd: rawnand: meson: always read whole OOB bytes
@ 2023-05-29 19:46           ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-29 19:46 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Jianxin Pan, Yixun Lan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel



On 26.05.2023 20:09, Miquel Raynal wrote:
> Hi Arseniy,
> 
> avkrasnov@sberdevices.ru wrote on Tue, 23 May 2023 20:27:35 +0300:
> 
>> On 22.05.2023 18:38, Miquel Raynal wrote:
>>> Hi Arseniy,
>>>
>>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:37 +0300:
>>>   
>>>> This changes size of read access to OOB area by reading all bytes of
>>>> OOB (free bytes + ECC engine bytes).  
>>>
>>> This is normally up to the user (user in your case == jffs2). The
>>> controller driver should expose a number of user accessible bytes and
>>> then when users want the OOB area, they should access it entirely. On
>>> top of that read, they can extract (or "write only") the user bytes.  
>>
>> Sorry, I didn't get it. If driver exposes N bytes of user accessible bytes,
>> I must always return whole OOB yes? E.g. N + rest of OOB
> 
> Yes. At the NAND controller level, you get asked for either a page of
> data (sometimes a subpage, but whatever), and/or the oob area. You need
> to provide what is requested, no more, no less. The upper layers will
> trim down what's uneeded and extract the bytes they want.

I see, so in this case I think this patch could be merged to the patch which
changes OOB layout be moving it out of ECC area? Because driver MUST return all
bytes of OOB area.

> 
>>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>>> ---
>>>>  drivers/mtd/nand/raw/meson_nand.c | 24 ++++++++++++++++++++++++
>>>>  1 file changed, 24 insertions(+)
>>>>
>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>>> index 8526a6b87720..a31106c943d7 100644
>>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>> @@ -755,6 +755,30 @@ static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
>>>>  	u32 oob_bytes;
>>>>  	u32 page_size;
>>>>  	int ret;
>>>> +	int i;
>>>> +
>>>> +	/* Read ECC codes and user bytes. */
>>>> +	for (i = 0; i < nand->ecc.steps; i++) {
>>>> +		u32 ecc_offs = nand->ecc.size * (i + 1) +
>>>> +			       NFC_OOB_PER_ECC(nand) * i;
>>>> +
>>>> +		ret = nand_read_page_op(nand, page, 0, NULL, 0);
>>>> +		if (ret)
>>>> +			return ret;
>>>> +
>>>> +		/* Use temporary buffer, because 'nand_change_read_column_op()'
>>>> +		 * seems work with some alignment, so we can't read data to
>>>> +		 * 'oob_buf' directly.  
>>>
>>> DMA?  
>>
>> Yes I guess, this address passed to exec_op code and used as DMA.
> 
> If your controller uses DMA on exec_op accesses, then yes. Exec_op
> reads/writes are usually small enough (or not time sensitive at all if
> they are bigger) so it's not required to use DMA there. Anyhow, oob_buf
> is suitable for DMA purposes, so I'm a bit surprised you need a bounce
> buffer, if that's the only reason. Maybe you need a bounce buffer to
> reorganize the data. That would be a much better explanation.

Yes! I remove this temporary buffer, seems my mistake! Without it everything works
good, I'll remove it from the next version!

Thanks, Arseniy

> 
>>>> +		 */
>>>> +		ret = nand_change_read_column_op(nand, ecc_offs, meson_chip->oob_buf,
>>>> +						 NFC_OOB_PER_ECC(nand), false);
>>>> +		if (ret)
>>>> +			return ret;
>>>> +
>>>> +		memcpy(oob_buf + i * NFC_OOB_PER_ECC(nand),
>>>> +		       meson_chip->oob_buf,
>>>> +		       NFC_OOB_PER_ECC(nand));
>>>> +	}
>>>>  
>>>>  	oob_bytes = meson_nfc_get_oob_bytes(nand);
>>>>    
>>>
>>>
>>> Thanks,
>>> Miquèl  
>>
>> Thanks, Arseniy
> 
> 
> Thanks,
> Miquèl

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v4 3/5] mtd: rawnand: meson: always read whole OOB bytes
@ 2023-05-29 19:46           ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-29 19:46 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Jianxin Pan, Yixun Lan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel



On 26.05.2023 20:09, Miquel Raynal wrote:
> Hi Arseniy,
> 
> avkrasnov@sberdevices.ru wrote on Tue, 23 May 2023 20:27:35 +0300:
> 
>> On 22.05.2023 18:38, Miquel Raynal wrote:
>>> Hi Arseniy,
>>>
>>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:37 +0300:
>>>   
>>>> This changes size of read access to OOB area by reading all bytes of
>>>> OOB (free bytes + ECC engine bytes).  
>>>
>>> This is normally up to the user (user in your case == jffs2). The
>>> controller driver should expose a number of user accessible bytes and
>>> then when users want the OOB area, they should access it entirely. On
>>> top of that read, they can extract (or "write only") the user bytes.  
>>
>> Sorry, I didn't get it. If driver exposes N bytes of user accessible bytes,
>> I must always return whole OOB yes? E.g. N + rest of OOB
> 
> Yes. At the NAND controller level, you get asked for either a page of
> data (sometimes a subpage, but whatever), and/or the oob area. You need
> to provide what is requested, no more, no less. The upper layers will
> trim down what's uneeded and extract the bytes they want.

I see, so in this case I think this patch could be merged to the patch which
changes OOB layout be moving it out of ECC area? Because driver MUST return all
bytes of OOB area.

> 
>>>> Signed-off-by: Arseniy Krasnov <AVKrasnov@sberdevices.ru>
>>>> ---
>>>>  drivers/mtd/nand/raw/meson_nand.c | 24 ++++++++++++++++++++++++
>>>>  1 file changed, 24 insertions(+)
>>>>
>>>> diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
>>>> index 8526a6b87720..a31106c943d7 100644
>>>> --- a/drivers/mtd/nand/raw/meson_nand.c
>>>> +++ b/drivers/mtd/nand/raw/meson_nand.c
>>>> @@ -755,6 +755,30 @@ static int __meson_nfc_read_oob(struct nand_chip *nand, int page,
>>>>  	u32 oob_bytes;
>>>>  	u32 page_size;
>>>>  	int ret;
>>>> +	int i;
>>>> +
>>>> +	/* Read ECC codes and user bytes. */
>>>> +	for (i = 0; i < nand->ecc.steps; i++) {
>>>> +		u32 ecc_offs = nand->ecc.size * (i + 1) +
>>>> +			       NFC_OOB_PER_ECC(nand) * i;
>>>> +
>>>> +		ret = nand_read_page_op(nand, page, 0, NULL, 0);
>>>> +		if (ret)
>>>> +			return ret;
>>>> +
>>>> +		/* Use temporary buffer, because 'nand_change_read_column_op()'
>>>> +		 * seems work with some alignment, so we can't read data to
>>>> +		 * 'oob_buf' directly.  
>>>
>>> DMA?  
>>
>> Yes I guess, this address passed to exec_op code and used as DMA.
> 
> If your controller uses DMA on exec_op accesses, then yes. Exec_op
> reads/writes are usually small enough (or not time sensitive at all if
> they are bigger) so it's not required to use DMA there. Anyhow, oob_buf
> is suitable for DMA purposes, so I'm a bit surprised you need a bounce
> buffer, if that's the only reason. Maybe you need a bounce buffer to
> reorganize the data. That would be a much better explanation.

Yes! I remove this temporary buffer, seems my mistake! Without it everything works
good, I'll remove it from the next version!

Thanks, Arseniy

> 
>>>> +		 */
>>>> +		ret = nand_change_read_column_op(nand, ecc_offs, meson_chip->oob_buf,
>>>> +						 NFC_OOB_PER_ECC(nand), false);
>>>> +		if (ret)
>>>> +			return ret;
>>>> +
>>>> +		memcpy(oob_buf + i * NFC_OOB_PER_ECC(nand),
>>>> +		       meson_chip->oob_buf,
>>>> +		       NFC_OOB_PER_ECC(nand));
>>>> +	}
>>>>  
>>>>  	oob_bytes = meson_nfc_get_oob_bytes(nand);
>>>>    
>>>
>>>
>>> Thanks,
>>> Miquèl  
>>
>> Thanks, Arseniy
> 
> 
> Thanks,
> Miquèl

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
  2023-05-29 19:43           ` Arseniy Krasnov
  (?)
  (?)
@ 2023-05-30  7:44             ` Miquel Raynal
  -1 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-30  7:44 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

> >>>> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
> >>>> -{
> >>>> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >>>> -	__le64 *info;
> >>>> -	int i, count;
> >>>> +	int i;
> >>>>  
> >>>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> >>>> +	for (i = 0; i < nand->ecc.steps; i++) {
> >>>>  		info = &meson_chip->info_buf[i];
> >>>> -		oob_buf[count] = *info;
> >>>> -		oob_buf[count + 1] = *info >> 8;
> >>>> +		/* Always ignore user bytes programming. */    
> >>>
> >>> Why?    
> >>
> >> I think comment message is wrong a little bit. Here "user bytes" are
> >> user bytes protected by ECC (e.g. location of these bytes differs from new
> >> OOB layout introduced by this patch). During page write this hardware
> >> always writes these bytes along with data. But, new OOB layout always ignores
> >> these 4 bytes, so set them to 0xFF always.  
> > 
> > When performing page reads/writes, you need to take the data as it's
> > been provided. You may move the data around in the buffer provided to
> > the controller, so that it get the ECC data at the right location, and
> > you need of course to reorganize the data when reading as well, so that
> > the user sees XkiB of data + YB of OOB. That's all you need to do in
> > these helpers.
> >   
> 
> I think there is some misunderstanding about these "user bytes" above: there are 4
> bytes which this NAND controller always writes to page in ECC mode - it was free OOB
> bytes covered by ECC. Controller grabs values from DMA buffer (second DMA buffer which
> doesn't contains page data) and writes it along with data and ECC codes. Idea of this
> change is to always suppress this write by setting them to 0xFF (may be there is some
> command option to not write it, but I don't have doc), because all of them (4 bytes)
> become unavailable to reader/writer.

At the NAND controller level, I would rather avoid doing things like
that.

I believe you can just update the ooblayout so that protected OOB bytes
are not exposed to the user as free bytes. Then your buffers should
already contain 0xffffff at the problematic location.

Thanks,
Miquèl

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

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-30  7:44             ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-30  7:44 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

> >>>> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
> >>>> -{
> >>>> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >>>> -	__le64 *info;
> >>>> -	int i, count;
> >>>> +	int i;
> >>>>  
> >>>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> >>>> +	for (i = 0; i < nand->ecc.steps; i++) {
> >>>>  		info = &meson_chip->info_buf[i];
> >>>> -		oob_buf[count] = *info;
> >>>> -		oob_buf[count + 1] = *info >> 8;
> >>>> +		/* Always ignore user bytes programming. */    
> >>>
> >>> Why?    
> >>
> >> I think comment message is wrong a little bit. Here "user bytes" are
> >> user bytes protected by ECC (e.g. location of these bytes differs from new
> >> OOB layout introduced by this patch). During page write this hardware
> >> always writes these bytes along with data. But, new OOB layout always ignores
> >> these 4 bytes, so set them to 0xFF always.  
> > 
> > When performing page reads/writes, you need to take the data as it's
> > been provided. You may move the data around in the buffer provided to
> > the controller, so that it get the ECC data at the right location, and
> > you need of course to reorganize the data when reading as well, so that
> > the user sees XkiB of data + YB of OOB. That's all you need to do in
> > these helpers.
> >   
> 
> I think there is some misunderstanding about these "user bytes" above: there are 4
> bytes which this NAND controller always writes to page in ECC mode - it was free OOB
> bytes covered by ECC. Controller grabs values from DMA buffer (second DMA buffer which
> doesn't contains page data) and writes it along with data and ECC codes. Idea of this
> change is to always suppress this write by setting them to 0xFF (may be there is some
> command option to not write it, but I don't have doc), because all of them (4 bytes)
> become unavailable to reader/writer.

At the NAND controller level, I would rather avoid doing things like
that.

I believe you can just update the ooblayout so that protected OOB bytes
are not exposed to the user as free bytes. Then your buffers should
already contain 0xffffff at the problematic location.

Thanks,
Miquèl

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-30  7:44             ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-30  7:44 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

> >>>> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
> >>>> -{
> >>>> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >>>> -	__le64 *info;
> >>>> -	int i, count;
> >>>> +	int i;
> >>>>  
> >>>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> >>>> +	for (i = 0; i < nand->ecc.steps; i++) {
> >>>>  		info = &meson_chip->info_buf[i];
> >>>> -		oob_buf[count] = *info;
> >>>> -		oob_buf[count + 1] = *info >> 8;
> >>>> +		/* Always ignore user bytes programming. */    
> >>>
> >>> Why?    
> >>
> >> I think comment message is wrong a little bit. Here "user bytes" are
> >> user bytes protected by ECC (e.g. location of these bytes differs from new
> >> OOB layout introduced by this patch). During page write this hardware
> >> always writes these bytes along with data. But, new OOB layout always ignores
> >> these 4 bytes, so set them to 0xFF always.  
> > 
> > When performing page reads/writes, you need to take the data as it's
> > been provided. You may move the data around in the buffer provided to
> > the controller, so that it get the ECC data at the right location, and
> > you need of course to reorganize the data when reading as well, so that
> > the user sees XkiB of data + YB of OOB. That's all you need to do in
> > these helpers.
> >   
> 
> I think there is some misunderstanding about these "user bytes" above: there are 4
> bytes which this NAND controller always writes to page in ECC mode - it was free OOB
> bytes covered by ECC. Controller grabs values from DMA buffer (second DMA buffer which
> doesn't contains page data) and writes it along with data and ECC codes. Idea of this
> change is to always suppress this write by setting them to 0xFF (may be there is some
> command option to not write it, but I don't have doc), because all of them (4 bytes)
> become unavailable to reader/writer.

At the NAND controller level, I would rather avoid doing things like
that.

I believe you can just update the ooblayout so that protected OOB bytes
are not exposed to the user as free bytes. Then your buffers should
already contain 0xffffff at the problematic location.

Thanks,
Miquèl

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-30  7:44             ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-30  7:44 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

> >>>> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
> >>>> -{
> >>>> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >>>> -	__le64 *info;
> >>>> -	int i, count;
> >>>> +	int i;
> >>>>  
> >>>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> >>>> +	for (i = 0; i < nand->ecc.steps; i++) {
> >>>>  		info = &meson_chip->info_buf[i];
> >>>> -		oob_buf[count] = *info;
> >>>> -		oob_buf[count + 1] = *info >> 8;
> >>>> +		/* Always ignore user bytes programming. */    
> >>>
> >>> Why?    
> >>
> >> I think comment message is wrong a little bit. Here "user bytes" are
> >> user bytes protected by ECC (e.g. location of these bytes differs from new
> >> OOB layout introduced by this patch). During page write this hardware
> >> always writes these bytes along with data. But, new OOB layout always ignores
> >> these 4 bytes, so set them to 0xFF always.  
> > 
> > When performing page reads/writes, you need to take the data as it's
> > been provided. You may move the data around in the buffer provided to
> > the controller, so that it get the ECC data at the right location, and
> > you need of course to reorganize the data when reading as well, so that
> > the user sees XkiB of data + YB of OOB. That's all you need to do in
> > these helpers.
> >   
> 
> I think there is some misunderstanding about these "user bytes" above: there are 4
> bytes which this NAND controller always writes to page in ECC mode - it was free OOB
> bytes covered by ECC. Controller grabs values from DMA buffer (second DMA buffer which
> doesn't contains page data) and writes it along with data and ECC codes. Idea of this
> change is to always suppress this write by setting them to 0xFF (may be there is some
> command option to not write it, but I don't have doc), because all of them (4 bytes)
> become unavailable to reader/writer.

At the NAND controller level, I would rather avoid doing things like
that.

I believe you can just update the ooblayout so that protected OOB bytes
are not exposed to the user as free bytes. Then your buffers should
already contain 0xffffff at the problematic location.

Thanks,
Miquèl

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
  2023-05-30  7:44             ` Miquel Raynal
  (?)
  (?)
@ 2023-05-30  8:09               ` Arseniy Krasnov
  -1 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-30  8:09 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Miquel,

On 30.05.2023 10:44, Miquel Raynal wrote:
> Hi Arseniy,
> 
>>>>>> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>>>>> -{
>>>>>> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>>> -	__le64 *info;
>>>>>> -	int i, count;
>>>>>> +	int i;
>>>>>>  
>>>>>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>>>>>> +	for (i = 0; i < nand->ecc.steps; i++) {
>>>>>>  		info = &meson_chip->info_buf[i];
>>>>>> -		oob_buf[count] = *info;
>>>>>> -		oob_buf[count + 1] = *info >> 8;
>>>>>> +		/* Always ignore user bytes programming. */    
>>>>>
>>>>> Why?    
>>>>
>>>> I think comment message is wrong a little bit. Here "user bytes" are
>>>> user bytes protected by ECC (e.g. location of these bytes differs from new
>>>> OOB layout introduced by this patch). During page write this hardware
>>>> always writes these bytes along with data. But, new OOB layout always ignores
>>>> these 4 bytes, so set them to 0xFF always.  
>>>
>>> When performing page reads/writes, you need to take the data as it's
>>> been provided. You may move the data around in the buffer provided to
>>> the controller, so that it get the ECC data at the right location, and
>>> you need of course to reorganize the data when reading as well, so that
>>> the user sees XkiB of data + YB of OOB. That's all you need to do in
>>> these helpers.
>>>   
>>
>> I think there is some misunderstanding about these "user bytes" above: there are 4
>> bytes which this NAND controller always writes to page in ECC mode - it was free OOB
>> bytes covered by ECC. Controller grabs values from DMA buffer (second DMA buffer which
>> doesn't contains page data) and writes it along with data and ECC codes. Idea of this
>> change is to always suppress this write by setting them to 0xFF (may be there is some
>> command option to not write it, but I don't have doc), because all of them (4 bytes)
>> become unavailable to reader/writer.
> 
> At the NAND controller level, I would rather avoid doing things like
> that.
> 
> I believe you can just update the ooblayout so that protected OOB bytes
> are not exposed to the user as free bytes. Then your buffers should
> already contain 0xffffff at the problematic location.

So Your idea is to continue fill DMA buffer (for these 4 bytes) from provided OOB buffer,
relying on that as these bytes are unused, they will be 0xFF in OOB buffer so we get the same result?

Thanks, Arseniy

> 
> Thanks,
> Miquèl

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-30  8:09               ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-30  8:09 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Miquel,

On 30.05.2023 10:44, Miquel Raynal wrote:
> Hi Arseniy,
> 
>>>>>> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>>>>> -{
>>>>>> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>>> -	__le64 *info;
>>>>>> -	int i, count;
>>>>>> +	int i;
>>>>>>  
>>>>>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>>>>>> +	for (i = 0; i < nand->ecc.steps; i++) {
>>>>>>  		info = &meson_chip->info_buf[i];
>>>>>> -		oob_buf[count] = *info;
>>>>>> -		oob_buf[count + 1] = *info >> 8;
>>>>>> +		/* Always ignore user bytes programming. */    
>>>>>
>>>>> Why?    
>>>>
>>>> I think comment message is wrong a little bit. Here "user bytes" are
>>>> user bytes protected by ECC (e.g. location of these bytes differs from new
>>>> OOB layout introduced by this patch). During page write this hardware
>>>> always writes these bytes along with data. But, new OOB layout always ignores
>>>> these 4 bytes, so set them to 0xFF always.  
>>>
>>> When performing page reads/writes, you need to take the data as it's
>>> been provided. You may move the data around in the buffer provided to
>>> the controller, so that it get the ECC data at the right location, and
>>> you need of course to reorganize the data when reading as well, so that
>>> the user sees XkiB of data + YB of OOB. That's all you need to do in
>>> these helpers.
>>>   
>>
>> I think there is some misunderstanding about these "user bytes" above: there are 4
>> bytes which this NAND controller always writes to page in ECC mode - it was free OOB
>> bytes covered by ECC. Controller grabs values from DMA buffer (second DMA buffer which
>> doesn't contains page data) and writes it along with data and ECC codes. Idea of this
>> change is to always suppress this write by setting them to 0xFF (may be there is some
>> command option to not write it, but I don't have doc), because all of them (4 bytes)
>> become unavailable to reader/writer.
> 
> At the NAND controller level, I would rather avoid doing things like
> that.
> 
> I believe you can just update the ooblayout so that protected OOB bytes
> are not exposed to the user as free bytes. Then your buffers should
> already contain 0xffffff at the problematic location.

So Your idea is to continue fill DMA buffer (for these 4 bytes) from provided OOB buffer,
relying on that as these bytes are unused, they will be 0xFF in OOB buffer so we get the same result?

Thanks, Arseniy

> 
> Thanks,
> Miquèl

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

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-30  8:09               ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-30  8:09 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Miquel,

On 30.05.2023 10:44, Miquel Raynal wrote:
> Hi Arseniy,
> 
>>>>>> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>>>>> -{
>>>>>> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>>> -	__le64 *info;
>>>>>> -	int i, count;
>>>>>> +	int i;
>>>>>>  
>>>>>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>>>>>> +	for (i = 0; i < nand->ecc.steps; i++) {
>>>>>>  		info = &meson_chip->info_buf[i];
>>>>>> -		oob_buf[count] = *info;
>>>>>> -		oob_buf[count + 1] = *info >> 8;
>>>>>> +		/* Always ignore user bytes programming. */    
>>>>>
>>>>> Why?    
>>>>
>>>> I think comment message is wrong a little bit. Here "user bytes" are
>>>> user bytes protected by ECC (e.g. location of these bytes differs from new
>>>> OOB layout introduced by this patch). During page write this hardware
>>>> always writes these bytes along with data. But, new OOB layout always ignores
>>>> these 4 bytes, so set them to 0xFF always.  
>>>
>>> When performing page reads/writes, you need to take the data as it's
>>> been provided. You may move the data around in the buffer provided to
>>> the controller, so that it get the ECC data at the right location, and
>>> you need of course to reorganize the data when reading as well, so that
>>> the user sees XkiB of data + YB of OOB. That's all you need to do in
>>> these helpers.
>>>   
>>
>> I think there is some misunderstanding about these "user bytes" above: there are 4
>> bytes which this NAND controller always writes to page in ECC mode - it was free OOB
>> bytes covered by ECC. Controller grabs values from DMA buffer (second DMA buffer which
>> doesn't contains page data) and writes it along with data and ECC codes. Idea of this
>> change is to always suppress this write by setting them to 0xFF (may be there is some
>> command option to not write it, but I don't have doc), because all of them (4 bytes)
>> become unavailable to reader/writer.
> 
> At the NAND controller level, I would rather avoid doing things like
> that.
> 
> I believe you can just update the ooblayout so that protected OOB bytes
> are not exposed to the user as free bytes. Then your buffers should
> already contain 0xffffff at the problematic location.

So Your idea is to continue fill DMA buffer (for these 4 bytes) from provided OOB buffer,
relying on that as these bytes are unused, they will be 0xFF in OOB buffer so we get the same result?

Thanks, Arseniy

> 
> Thanks,
> Miquèl

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-30  8:09               ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-30  8:09 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Miquel,

On 30.05.2023 10:44, Miquel Raynal wrote:
> Hi Arseniy,
> 
>>>>>> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>>>>> -{
>>>>>> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>>> -	__le64 *info;
>>>>>> -	int i, count;
>>>>>> +	int i;
>>>>>>  
>>>>>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>>>>>> +	for (i = 0; i < nand->ecc.steps; i++) {
>>>>>>  		info = &meson_chip->info_buf[i];
>>>>>> -		oob_buf[count] = *info;
>>>>>> -		oob_buf[count + 1] = *info >> 8;
>>>>>> +		/* Always ignore user bytes programming. */    
>>>>>
>>>>> Why?    
>>>>
>>>> I think comment message is wrong a little bit. Here "user bytes" are
>>>> user bytes protected by ECC (e.g. location of these bytes differs from new
>>>> OOB layout introduced by this patch). During page write this hardware
>>>> always writes these bytes along with data. But, new OOB layout always ignores
>>>> these 4 bytes, so set them to 0xFF always.  
>>>
>>> When performing page reads/writes, you need to take the data as it's
>>> been provided. You may move the data around in the buffer provided to
>>> the controller, so that it get the ECC data at the right location, and
>>> you need of course to reorganize the data when reading as well, so that
>>> the user sees XkiB of data + YB of OOB. That's all you need to do in
>>> these helpers.
>>>   
>>
>> I think there is some misunderstanding about these "user bytes" above: there are 4
>> bytes which this NAND controller always writes to page in ECC mode - it was free OOB
>> bytes covered by ECC. Controller grabs values from DMA buffer (second DMA buffer which
>> doesn't contains page data) and writes it along with data and ECC codes. Idea of this
>> change is to always suppress this write by setting them to 0xFF (may be there is some
>> command option to not write it, but I don't have doc), because all of them (4 bytes)
>> become unavailable to reader/writer.
> 
> At the NAND controller level, I would rather avoid doing things like
> that.
> 
> I believe you can just update the ooblayout so that protected OOB bytes
> are not exposed to the user as free bytes. Then your buffers should
> already contain 0xffffff at the problematic location.

So Your idea is to continue fill DMA buffer (for these 4 bytes) from provided OOB buffer,
relying on that as these bytes are unused, they will be 0xFF in OOB buffer so we get the same result?

Thanks, Arseniy

> 
> Thanks,
> Miquèl

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
  2023-05-30  8:09               ` Arseniy Krasnov
  (?)
  (?)
@ 2023-05-30  8:21                 ` Miquel Raynal
  -1 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-30  8:21 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

avkrasnov@sberdevices.ru wrote on Tue, 30 May 2023 11:09:10 +0300:

> Hi Miquel,
> 
> On 30.05.2023 10:44, Miquel Raynal wrote:
> > Hi Arseniy,
> >   
> >>>>>> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
> >>>>>> -{
> >>>>>> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >>>>>> -	__le64 *info;
> >>>>>> -	int i, count;
> >>>>>> +	int i;
> >>>>>>  
> >>>>>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> >>>>>> +	for (i = 0; i < nand->ecc.steps; i++) {
> >>>>>>  		info = &meson_chip->info_buf[i];
> >>>>>> -		oob_buf[count] = *info;
> >>>>>> -		oob_buf[count + 1] = *info >> 8;
> >>>>>> +		/* Always ignore user bytes programming. */      
> >>>>>
> >>>>> Why?      
> >>>>
> >>>> I think comment message is wrong a little bit. Here "user bytes" are
> >>>> user bytes protected by ECC (e.g. location of these bytes differs from new
> >>>> OOB layout introduced by this patch). During page write this hardware
> >>>> always writes these bytes along with data. But, new OOB layout always ignores
> >>>> these 4 bytes, so set them to 0xFF always.    
> >>>
> >>> When performing page reads/writes, you need to take the data as it's
> >>> been provided. You may move the data around in the buffer provided to
> >>> the controller, so that it get the ECC data at the right location, and
> >>> you need of course to reorganize the data when reading as well, so that
> >>> the user sees XkiB of data + YB of OOB. That's all you need to do in
> >>> these helpers.
> >>>     
> >>
> >> I think there is some misunderstanding about these "user bytes" above: there are 4
> >> bytes which this NAND controller always writes to page in ECC mode - it was free OOB
> >> bytes covered by ECC. Controller grabs values from DMA buffer (second DMA buffer which
> >> doesn't contains page data) and writes it along with data and ECC codes. Idea of this
> >> change is to always suppress this write by setting them to 0xFF (may be there is some
> >> command option to not write it, but I don't have doc), because all of them (4 bytes)
> >> become unavailable to reader/writer.  
> > 
> > At the NAND controller level, I would rather avoid doing things like
> > that.
> > 
> > I believe you can just update the ooblayout so that protected OOB bytes
> > are not exposed to the user as free bytes. Then your buffers should
> > already contain 0xffffff at the problematic location.  
> 
> So Your idea is to continue fill DMA buffer (for these 4 bytes) from provided OOB buffer,
> relying on that as these bytes are unused, they will be 0xFF in OOB buffer so we get the same result?

Yes.

The problem you face is due to jffs2 using free OOB bytes to store some
data. If this data is in the protected area -> BOOM.

If another application wants to use all the bytes and writes them all
in the same PROGRAM operation it's fine.

Jffs2 accesses the free area through the OOB layouts only, so just
tweaking the OOB layouts should work.

Thanks,
Miquèl

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-30  8:21                 ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-30  8:21 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

avkrasnov@sberdevices.ru wrote on Tue, 30 May 2023 11:09:10 +0300:

> Hi Miquel,
> 
> On 30.05.2023 10:44, Miquel Raynal wrote:
> > Hi Arseniy,
> >   
> >>>>>> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
> >>>>>> -{
> >>>>>> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >>>>>> -	__le64 *info;
> >>>>>> -	int i, count;
> >>>>>> +	int i;
> >>>>>>  
> >>>>>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> >>>>>> +	for (i = 0; i < nand->ecc.steps; i++) {
> >>>>>>  		info = &meson_chip->info_buf[i];
> >>>>>> -		oob_buf[count] = *info;
> >>>>>> -		oob_buf[count + 1] = *info >> 8;
> >>>>>> +		/* Always ignore user bytes programming. */      
> >>>>>
> >>>>> Why?      
> >>>>
> >>>> I think comment message is wrong a little bit. Here "user bytes" are
> >>>> user bytes protected by ECC (e.g. location of these bytes differs from new
> >>>> OOB layout introduced by this patch). During page write this hardware
> >>>> always writes these bytes along with data. But, new OOB layout always ignores
> >>>> these 4 bytes, so set them to 0xFF always.    
> >>>
> >>> When performing page reads/writes, you need to take the data as it's
> >>> been provided. You may move the data around in the buffer provided to
> >>> the controller, so that it get the ECC data at the right location, and
> >>> you need of course to reorganize the data when reading as well, so that
> >>> the user sees XkiB of data + YB of OOB. That's all you need to do in
> >>> these helpers.
> >>>     
> >>
> >> I think there is some misunderstanding about these "user bytes" above: there are 4
> >> bytes which this NAND controller always writes to page in ECC mode - it was free OOB
> >> bytes covered by ECC. Controller grabs values from DMA buffer (second DMA buffer which
> >> doesn't contains page data) and writes it along with data and ECC codes. Idea of this
> >> change is to always suppress this write by setting them to 0xFF (may be there is some
> >> command option to not write it, but I don't have doc), because all of them (4 bytes)
> >> become unavailable to reader/writer.  
> > 
> > At the NAND controller level, I would rather avoid doing things like
> > that.
> > 
> > I believe you can just update the ooblayout so that protected OOB bytes
> > are not exposed to the user as free bytes. Then your buffers should
> > already contain 0xffffff at the problematic location.  
> 
> So Your idea is to continue fill DMA buffer (for these 4 bytes) from provided OOB buffer,
> relying on that as these bytes are unused, they will be 0xFF in OOB buffer so we get the same result?

Yes.

The problem you face is due to jffs2 using free OOB bytes to store some
data. If this data is in the protected area -> BOOM.

If another application wants to use all the bytes and writes them all
in the same PROGRAM operation it's fine.

Jffs2 accesses the free area through the OOB layouts only, so just
tweaking the OOB layouts should work.

Thanks,
Miquèl

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

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-30  8:21                 ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-30  8:21 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

avkrasnov@sberdevices.ru wrote on Tue, 30 May 2023 11:09:10 +0300:

> Hi Miquel,
> 
> On 30.05.2023 10:44, Miquel Raynal wrote:
> > Hi Arseniy,
> >   
> >>>>>> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
> >>>>>> -{
> >>>>>> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >>>>>> -	__le64 *info;
> >>>>>> -	int i, count;
> >>>>>> +	int i;
> >>>>>>  
> >>>>>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> >>>>>> +	for (i = 0; i < nand->ecc.steps; i++) {
> >>>>>>  		info = &meson_chip->info_buf[i];
> >>>>>> -		oob_buf[count] = *info;
> >>>>>> -		oob_buf[count + 1] = *info >> 8;
> >>>>>> +		/* Always ignore user bytes programming. */      
> >>>>>
> >>>>> Why?      
> >>>>
> >>>> I think comment message is wrong a little bit. Here "user bytes" are
> >>>> user bytes protected by ECC (e.g. location of these bytes differs from new
> >>>> OOB layout introduced by this patch). During page write this hardware
> >>>> always writes these bytes along with data. But, new OOB layout always ignores
> >>>> these 4 bytes, so set them to 0xFF always.    
> >>>
> >>> When performing page reads/writes, you need to take the data as it's
> >>> been provided. You may move the data around in the buffer provided to
> >>> the controller, so that it get the ECC data at the right location, and
> >>> you need of course to reorganize the data when reading as well, so that
> >>> the user sees XkiB of data + YB of OOB. That's all you need to do in
> >>> these helpers.
> >>>     
> >>
> >> I think there is some misunderstanding about these "user bytes" above: there are 4
> >> bytes which this NAND controller always writes to page in ECC mode - it was free OOB
> >> bytes covered by ECC. Controller grabs values from DMA buffer (second DMA buffer which
> >> doesn't contains page data) and writes it along with data and ECC codes. Idea of this
> >> change is to always suppress this write by setting them to 0xFF (may be there is some
> >> command option to not write it, but I don't have doc), because all of them (4 bytes)
> >> become unavailable to reader/writer.  
> > 
> > At the NAND controller level, I would rather avoid doing things like
> > that.
> > 
> > I believe you can just update the ooblayout so that protected OOB bytes
> > are not exposed to the user as free bytes. Then your buffers should
> > already contain 0xffffff at the problematic location.  
> 
> So Your idea is to continue fill DMA buffer (for these 4 bytes) from provided OOB buffer,
> relying on that as these bytes are unused, they will be 0xFF in OOB buffer so we get the same result?

Yes.

The problem you face is due to jffs2 using free OOB bytes to store some
data. If this data is in the protected area -> BOOM.

If another application wants to use all the bytes and writes them all
in the same PROGRAM operation it's fine.

Jffs2 accesses the free area through the OOB layouts only, so just
tweaking the OOB layouts should work.

Thanks,
Miquèl

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-30  8:21                 ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-30  8:21 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

avkrasnov@sberdevices.ru wrote on Tue, 30 May 2023 11:09:10 +0300:

> Hi Miquel,
> 
> On 30.05.2023 10:44, Miquel Raynal wrote:
> > Hi Arseniy,
> >   
> >>>>>> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
> >>>>>> -{
> >>>>>> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
> >>>>>> -	__le64 *info;
> >>>>>> -	int i, count;
> >>>>>> +	int i;
> >>>>>>  
> >>>>>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
> >>>>>> +	for (i = 0; i < nand->ecc.steps; i++) {
> >>>>>>  		info = &meson_chip->info_buf[i];
> >>>>>> -		oob_buf[count] = *info;
> >>>>>> -		oob_buf[count + 1] = *info >> 8;
> >>>>>> +		/* Always ignore user bytes programming. */      
> >>>>>
> >>>>> Why?      
> >>>>
> >>>> I think comment message is wrong a little bit. Here "user bytes" are
> >>>> user bytes protected by ECC (e.g. location of these bytes differs from new
> >>>> OOB layout introduced by this patch). During page write this hardware
> >>>> always writes these bytes along with data. But, new OOB layout always ignores
> >>>> these 4 bytes, so set them to 0xFF always.    
> >>>
> >>> When performing page reads/writes, you need to take the data as it's
> >>> been provided. You may move the data around in the buffer provided to
> >>> the controller, so that it get the ECC data at the right location, and
> >>> you need of course to reorganize the data when reading as well, so that
> >>> the user sees XkiB of data + YB of OOB. That's all you need to do in
> >>> these helpers.
> >>>     
> >>
> >> I think there is some misunderstanding about these "user bytes" above: there are 4
> >> bytes which this NAND controller always writes to page in ECC mode - it was free OOB
> >> bytes covered by ECC. Controller grabs values from DMA buffer (second DMA buffer which
> >> doesn't contains page data) and writes it along with data and ECC codes. Idea of this
> >> change is to always suppress this write by setting them to 0xFF (may be there is some
> >> command option to not write it, but I don't have doc), because all of them (4 bytes)
> >> become unavailable to reader/writer.  
> > 
> > At the NAND controller level, I would rather avoid doing things like
> > that.
> > 
> > I believe you can just update the ooblayout so that protected OOB bytes
> > are not exposed to the user as free bytes. Then your buffers should
> > already contain 0xffffff at the problematic location.  
> 
> So Your idea is to continue fill DMA buffer (for these 4 bytes) from provided OOB buffer,
> relying on that as these bytes are unused, they will be 0xFF in OOB buffer so we get the same result?

Yes.

The problem you face is due to jffs2 using free OOB bytes to store some
data. If this data is in the protected area -> BOOM.

If another application wants to use all the bytes and writes them all
in the same PROGRAM operation it's fine.

Jffs2 accesses the free area through the OOB layouts only, so just
tweaking the OOB layouts should work.

Thanks,
Miquèl

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
  2023-05-30  8:21                 ` Miquel Raynal
  (?)
  (?)
@ 2023-05-30  8:28                   ` Arseniy Krasnov
  -1 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-30  8:28 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel



On 30.05.2023 11:21, Miquel Raynal wrote:
> Hi Arseniy,
> 
> avkrasnov@sberdevices.ru wrote on Tue, 30 May 2023 11:09:10 +0300:
> 
>> Hi Miquel,
>>
>> On 30.05.2023 10:44, Miquel Raynal wrote:
>>> Hi Arseniy,
>>>   
>>>>>>>> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>>>>>>> -{
>>>>>>>> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>>>>> -	__le64 *info;
>>>>>>>> -	int i, count;
>>>>>>>> +	int i;
>>>>>>>>  
>>>>>>>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>>>>>>>> +	for (i = 0; i < nand->ecc.steps; i++) {
>>>>>>>>  		info = &meson_chip->info_buf[i];
>>>>>>>> -		oob_buf[count] = *info;
>>>>>>>> -		oob_buf[count + 1] = *info >> 8;
>>>>>>>> +		/* Always ignore user bytes programming. */      
>>>>>>>
>>>>>>> Why?      
>>>>>>
>>>>>> I think comment message is wrong a little bit. Here "user bytes" are
>>>>>> user bytes protected by ECC (e.g. location of these bytes differs from new
>>>>>> OOB layout introduced by this patch). During page write this hardware
>>>>>> always writes these bytes along with data. But, new OOB layout always ignores
>>>>>> these 4 bytes, so set them to 0xFF always.    
>>>>>
>>>>> When performing page reads/writes, you need to take the data as it's
>>>>> been provided. You may move the data around in the buffer provided to
>>>>> the controller, so that it get the ECC data at the right location, and
>>>>> you need of course to reorganize the data when reading as well, so that
>>>>> the user sees XkiB of data + YB of OOB. That's all you need to do in
>>>>> these helpers.
>>>>>     
>>>>
>>>> I think there is some misunderstanding about these "user bytes" above: there are 4
>>>> bytes which this NAND controller always writes to page in ECC mode - it was free OOB
>>>> bytes covered by ECC. Controller grabs values from DMA buffer (second DMA buffer which
>>>> doesn't contains page data) and writes it along with data and ECC codes. Idea of this
>>>> change is to always suppress this write by setting them to 0xFF (may be there is some
>>>> command option to not write it, but I don't have doc), because all of them (4 bytes)
>>>> become unavailable to reader/writer.  
>>>
>>> At the NAND controller level, I would rather avoid doing things like
>>> that.
>>>
>>> I believe you can just update the ooblayout so that protected OOB bytes
>>> are not exposed to the user as free bytes. Then your buffers should
>>> already contain 0xffffff at the problematic location.  
>>
>> So Your idea is to continue fill DMA buffer (for these 4 bytes) from provided OOB buffer,
>> relying on that as these bytes are unused, they will be 0xFF in OOB buffer so we get the same result?
> 
> Yes.
> 
> The problem you face is due to jffs2 using free OOB bytes to store some
> data. If this data is in the protected area -> BOOM.
> 
> If another application wants to use all the bytes and writes them all
> in the same PROGRAM operation it's fine.
> 
> Jffs2 accesses the free area through the OOB layouts only, so just
> tweaking the OOB layouts should work.

I see, I'll try this.

Thanks,
Arseniy

> 
> Thanks,
> Miquèl

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-30  8:28                   ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-30  8:28 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel



On 30.05.2023 11:21, Miquel Raynal wrote:
> Hi Arseniy,
> 
> avkrasnov@sberdevices.ru wrote on Tue, 30 May 2023 11:09:10 +0300:
> 
>> Hi Miquel,
>>
>> On 30.05.2023 10:44, Miquel Raynal wrote:
>>> Hi Arseniy,
>>>   
>>>>>>>> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>>>>>>> -{
>>>>>>>> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>>>>> -	__le64 *info;
>>>>>>>> -	int i, count;
>>>>>>>> +	int i;
>>>>>>>>  
>>>>>>>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>>>>>>>> +	for (i = 0; i < nand->ecc.steps; i++) {
>>>>>>>>  		info = &meson_chip->info_buf[i];
>>>>>>>> -		oob_buf[count] = *info;
>>>>>>>> -		oob_buf[count + 1] = *info >> 8;
>>>>>>>> +		/* Always ignore user bytes programming. */      
>>>>>>>
>>>>>>> Why?      
>>>>>>
>>>>>> I think comment message is wrong a little bit. Here "user bytes" are
>>>>>> user bytes protected by ECC (e.g. location of these bytes differs from new
>>>>>> OOB layout introduced by this patch). During page write this hardware
>>>>>> always writes these bytes along with data. But, new OOB layout always ignores
>>>>>> these 4 bytes, so set them to 0xFF always.    
>>>>>
>>>>> When performing page reads/writes, you need to take the data as it's
>>>>> been provided. You may move the data around in the buffer provided to
>>>>> the controller, so that it get the ECC data at the right location, and
>>>>> you need of course to reorganize the data when reading as well, so that
>>>>> the user sees XkiB of data + YB of OOB. That's all you need to do in
>>>>> these helpers.
>>>>>     
>>>>
>>>> I think there is some misunderstanding about these "user bytes" above: there are 4
>>>> bytes which this NAND controller always writes to page in ECC mode - it was free OOB
>>>> bytes covered by ECC. Controller grabs values from DMA buffer (second DMA buffer which
>>>> doesn't contains page data) and writes it along with data and ECC codes. Idea of this
>>>> change is to always suppress this write by setting them to 0xFF (may be there is some
>>>> command option to not write it, but I don't have doc), because all of them (4 bytes)
>>>> become unavailable to reader/writer.  
>>>
>>> At the NAND controller level, I would rather avoid doing things like
>>> that.
>>>
>>> I believe you can just update the ooblayout so that protected OOB bytes
>>> are not exposed to the user as free bytes. Then your buffers should
>>> already contain 0xffffff at the problematic location.  
>>
>> So Your idea is to continue fill DMA buffer (for these 4 bytes) from provided OOB buffer,
>> relying on that as these bytes are unused, they will be 0xFF in OOB buffer so we get the same result?
> 
> Yes.
> 
> The problem you face is due to jffs2 using free OOB bytes to store some
> data. If this data is in the protected area -> BOOM.
> 
> If another application wants to use all the bytes and writes them all
> in the same PROGRAM operation it's fine.
> 
> Jffs2 accesses the free area through the OOB layouts only, so just
> tweaking the OOB layouts should work.

I see, I'll try this.

Thanks,
Arseniy

> 
> Thanks,
> Miquèl

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

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-30  8:28                   ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-30  8:28 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel



On 30.05.2023 11:21, Miquel Raynal wrote:
> Hi Arseniy,
> 
> avkrasnov@sberdevices.ru wrote on Tue, 30 May 2023 11:09:10 +0300:
> 
>> Hi Miquel,
>>
>> On 30.05.2023 10:44, Miquel Raynal wrote:
>>> Hi Arseniy,
>>>   
>>>>>>>> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>>>>>>> -{
>>>>>>>> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>>>>> -	__le64 *info;
>>>>>>>> -	int i, count;
>>>>>>>> +	int i;
>>>>>>>>  
>>>>>>>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>>>>>>>> +	for (i = 0; i < nand->ecc.steps; i++) {
>>>>>>>>  		info = &meson_chip->info_buf[i];
>>>>>>>> -		oob_buf[count] = *info;
>>>>>>>> -		oob_buf[count + 1] = *info >> 8;
>>>>>>>> +		/* Always ignore user bytes programming. */      
>>>>>>>
>>>>>>> Why?      
>>>>>>
>>>>>> I think comment message is wrong a little bit. Here "user bytes" are
>>>>>> user bytes protected by ECC (e.g. location of these bytes differs from new
>>>>>> OOB layout introduced by this patch). During page write this hardware
>>>>>> always writes these bytes along with data. But, new OOB layout always ignores
>>>>>> these 4 bytes, so set them to 0xFF always.    
>>>>>
>>>>> When performing page reads/writes, you need to take the data as it's
>>>>> been provided. You may move the data around in the buffer provided to
>>>>> the controller, so that it get the ECC data at the right location, and
>>>>> you need of course to reorganize the data when reading as well, so that
>>>>> the user sees XkiB of data + YB of OOB. That's all you need to do in
>>>>> these helpers.
>>>>>     
>>>>
>>>> I think there is some misunderstanding about these "user bytes" above: there are 4
>>>> bytes which this NAND controller always writes to page in ECC mode - it was free OOB
>>>> bytes covered by ECC. Controller grabs values from DMA buffer (second DMA buffer which
>>>> doesn't contains page data) and writes it along with data and ECC codes. Idea of this
>>>> change is to always suppress this write by setting them to 0xFF (may be there is some
>>>> command option to not write it, but I don't have doc), because all of them (4 bytes)
>>>> become unavailable to reader/writer.  
>>>
>>> At the NAND controller level, I would rather avoid doing things like
>>> that.
>>>
>>> I believe you can just update the ooblayout so that protected OOB bytes
>>> are not exposed to the user as free bytes. Then your buffers should
>>> already contain 0xffffff at the problematic location.  
>>
>> So Your idea is to continue fill DMA buffer (for these 4 bytes) from provided OOB buffer,
>> relying on that as these bytes are unused, they will be 0xFF in OOB buffer so we get the same result?
> 
> Yes.
> 
> The problem you face is due to jffs2 using free OOB bytes to store some
> data. If this data is in the protected area -> BOOM.
> 
> If another application wants to use all the bytes and writes them all
> in the same PROGRAM operation it's fine.
> 
> Jffs2 accesses the free area through the OOB layouts only, so just
> tweaking the OOB layouts should work.

I see, I'll try this.

Thanks,
Arseniy

> 
> Thanks,
> Miquèl

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area
@ 2023-05-30  8:28                   ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-30  8:28 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel



On 30.05.2023 11:21, Miquel Raynal wrote:
> Hi Arseniy,
> 
> avkrasnov@sberdevices.ru wrote on Tue, 30 May 2023 11:09:10 +0300:
> 
>> Hi Miquel,
>>
>> On 30.05.2023 10:44, Miquel Raynal wrote:
>>> Hi Arseniy,
>>>   
>>>>>>>> -static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
>>>>>>>> -{
>>>>>>>> -	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
>>>>>>>> -	__le64 *info;
>>>>>>>> -	int i, count;
>>>>>>>> +	int i;
>>>>>>>>  
>>>>>>>> -	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
>>>>>>>> +	for (i = 0; i < nand->ecc.steps; i++) {
>>>>>>>>  		info = &meson_chip->info_buf[i];
>>>>>>>> -		oob_buf[count] = *info;
>>>>>>>> -		oob_buf[count + 1] = *info >> 8;
>>>>>>>> +		/* Always ignore user bytes programming. */      
>>>>>>>
>>>>>>> Why?      
>>>>>>
>>>>>> I think comment message is wrong a little bit. Here "user bytes" are
>>>>>> user bytes protected by ECC (e.g. location of these bytes differs from new
>>>>>> OOB layout introduced by this patch). During page write this hardware
>>>>>> always writes these bytes along with data. But, new OOB layout always ignores
>>>>>> these 4 bytes, so set them to 0xFF always.    
>>>>>
>>>>> When performing page reads/writes, you need to take the data as it's
>>>>> been provided. You may move the data around in the buffer provided to
>>>>> the controller, so that it get the ECC data at the right location, and
>>>>> you need of course to reorganize the data when reading as well, so that
>>>>> the user sees XkiB of data + YB of OOB. That's all you need to do in
>>>>> these helpers.
>>>>>     
>>>>
>>>> I think there is some misunderstanding about these "user bytes" above: there are 4
>>>> bytes which this NAND controller always writes to page in ECC mode - it was free OOB
>>>> bytes covered by ECC. Controller grabs values from DMA buffer (second DMA buffer which
>>>> doesn't contains page data) and writes it along with data and ECC codes. Idea of this
>>>> change is to always suppress this write by setting them to 0xFF (may be there is some
>>>> command option to not write it, but I don't have doc), because all of them (4 bytes)
>>>> become unavailable to reader/writer.  
>>>
>>> At the NAND controller level, I would rather avoid doing things like
>>> that.
>>>
>>> I believe you can just update the ooblayout so that protected OOB bytes
>>> are not exposed to the user as free bytes. Then your buffers should
>>> already contain 0xffffff at the problematic location.  
>>
>> So Your idea is to continue fill DMA buffer (for these 4 bytes) from provided OOB buffer,
>> relying on that as these bytes are unused, they will be 0xFF in OOB buffer so we get the same result?
> 
> Yes.
> 
> The problem you face is due to jffs2 using free OOB bytes to store some
> data. If this data is in the protected area -> BOOM.
> 
> If another application wants to use all the bytes and writes them all
> in the same PROGRAM operation it's fine.
> 
> Jffs2 accesses the free area through the OOB layouts only, so just
> tweaking the OOB layouts should work.

I see, I'll try this.

Thanks,
Arseniy

> 
> Thanks,
> Miquèl

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
  2023-05-26 17:22           ` Miquel Raynal
  (?)
  (?)
@ 2023-05-30 11:19             ` Arseniy Krasnov
  -1 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-30 11:19 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel



On 26.05.2023 20:22, Miquel Raynal wrote:
> Hi Arseniy,
> 
> avkrasnov@sberdevices.ru wrote on Wed, 24 May 2023 12:05:47 +0300:
> 
>> On 23.05.2023 12:12, Arseniy Krasnov wrote:
>>> Hello Miquel, Liang
>>>
>>> On 22.05.2023 18:05, Miquel Raynal wrote:  
>>>> Hi Arseniy,
>>>>
>>>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
>>>>  
>>>>> This fixes read/write functionality by:
>>>>> 1) Changing NFC_CMD_RB_INT bit value.  
>>>>
>>>> I guess this is a separate fix
>>>>  
>>>
>>> Ok, I'll move it to separate patch
>>>   
>>>>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.  
>>>>
>>>> Is this really needed? Looks like you're delaying the next op only. Is
>>>> using a delay enough? If yes, then it's probably the wrong approach.  
>>
>> Hi Miquel, small update, I found some details from @Liang's message in v1 talks from the last month:
>>
>> *
>> After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send
>> NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device.
> 
> That is true.
> 
>> but previous meson_nfc_queue_rb()
>> only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
>> i think there is something wrong with the Ready/Busy pin(please check the hardware whether this
>> Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the
>> nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the
>> data bus and generate IRQ if ready.
>> *
>>
>> I guess, that sequence of commands from this patch is described in datasheet (unfortunately I don't have it and relied on the old driver).
>> Yesterday I tried to remove sending of NAND_CMD_STATUS from this patch, but it broke current driver - i had ECC errors, so it looks like
>> "shot in the dark" situation, to understand this logic.
> 
> When an operation on the NAND array happens (eg. read, prog, erase),
> you need to wait "some time" before accessing the internal sram or even
> the chip which is "busy" until it gets "ready" again. You can probe the
> ready/busy pin (that's the hardware way, fast and reliable) or you can
> poll a status with NAND_CMD_STATUS. The chips are designed so they can
> actually process that command while they are doing time consuming tasks
> to update the host. But IIRC every byte read will return the status
> until you send READ0 again, which means "I'm done with the status
> read" somehow.
> 
> Please see nand_soft_waitrdy() in order to understand how this is
> supposed to work. You can even use that helper (which is exported)
> instead of open-coding it in your driver. See atmel or sunxi
> implementations for instance.
> 
> As using the native RB pin is better, you would need to identify
> whether you have one or not at probe time and then either poll the
> relevant bit of your controller if there is one, or fallback to the
> soft read (which should fallback on exec_op in the end).

Thanks for this information! I'll use 'nand_soft_waitrdy()' at least, because i guess that
there is no RB pin on my device.

Thanks, Arseniy

> 
> Thanks,
> Miquèl

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-30 11:19             ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-30 11:19 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel



On 26.05.2023 20:22, Miquel Raynal wrote:
> Hi Arseniy,
> 
> avkrasnov@sberdevices.ru wrote on Wed, 24 May 2023 12:05:47 +0300:
> 
>> On 23.05.2023 12:12, Arseniy Krasnov wrote:
>>> Hello Miquel, Liang
>>>
>>> On 22.05.2023 18:05, Miquel Raynal wrote:  
>>>> Hi Arseniy,
>>>>
>>>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
>>>>  
>>>>> This fixes read/write functionality by:
>>>>> 1) Changing NFC_CMD_RB_INT bit value.  
>>>>
>>>> I guess this is a separate fix
>>>>  
>>>
>>> Ok, I'll move it to separate patch
>>>   
>>>>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.  
>>>>
>>>> Is this really needed? Looks like you're delaying the next op only. Is
>>>> using a delay enough? If yes, then it's probably the wrong approach.  
>>
>> Hi Miquel, small update, I found some details from @Liang's message in v1 talks from the last month:
>>
>> *
>> After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send
>> NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device.
> 
> That is true.
> 
>> but previous meson_nfc_queue_rb()
>> only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
>> i think there is something wrong with the Ready/Busy pin(please check the hardware whether this
>> Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the
>> nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the
>> data bus and generate IRQ if ready.
>> *
>>
>> I guess, that sequence of commands from this patch is described in datasheet (unfortunately I don't have it and relied on the old driver).
>> Yesterday I tried to remove sending of NAND_CMD_STATUS from this patch, but it broke current driver - i had ECC errors, so it looks like
>> "shot in the dark" situation, to understand this logic.
> 
> When an operation on the NAND array happens (eg. read, prog, erase),
> you need to wait "some time" before accessing the internal sram or even
> the chip which is "busy" until it gets "ready" again. You can probe the
> ready/busy pin (that's the hardware way, fast and reliable) or you can
> poll a status with NAND_CMD_STATUS. The chips are designed so they can
> actually process that command while they are doing time consuming tasks
> to update the host. But IIRC every byte read will return the status
> until you send READ0 again, which means "I'm done with the status
> read" somehow.
> 
> Please see nand_soft_waitrdy() in order to understand how this is
> supposed to work. You can even use that helper (which is exported)
> instead of open-coding it in your driver. See atmel or sunxi
> implementations for instance.
> 
> As using the native RB pin is better, you would need to identify
> whether you have one or not at probe time and then either poll the
> relevant bit of your controller if there is one, or fallback to the
> soft read (which should fallback on exec_op in the end).

Thanks for this information! I'll use 'nand_soft_waitrdy()' at least, because i guess that
there is no RB pin on my device.

Thanks, Arseniy

> 
> Thanks,
> Miquèl

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-30 11:19             ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-30 11:19 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel



On 26.05.2023 20:22, Miquel Raynal wrote:
> Hi Arseniy,
> 
> avkrasnov@sberdevices.ru wrote on Wed, 24 May 2023 12:05:47 +0300:
> 
>> On 23.05.2023 12:12, Arseniy Krasnov wrote:
>>> Hello Miquel, Liang
>>>
>>> On 22.05.2023 18:05, Miquel Raynal wrote:  
>>>> Hi Arseniy,
>>>>
>>>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
>>>>  
>>>>> This fixes read/write functionality by:
>>>>> 1) Changing NFC_CMD_RB_INT bit value.  
>>>>
>>>> I guess this is a separate fix
>>>>  
>>>
>>> Ok, I'll move it to separate patch
>>>   
>>>>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.  
>>>>
>>>> Is this really needed? Looks like you're delaying the next op only. Is
>>>> using a delay enough? If yes, then it's probably the wrong approach.  
>>
>> Hi Miquel, small update, I found some details from @Liang's message in v1 talks from the last month:
>>
>> *
>> After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send
>> NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device.
> 
> That is true.
> 
>> but previous meson_nfc_queue_rb()
>> only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
>> i think there is something wrong with the Ready/Busy pin(please check the hardware whether this
>> Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the
>> nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the
>> data bus and generate IRQ if ready.
>> *
>>
>> I guess, that sequence of commands from this patch is described in datasheet (unfortunately I don't have it and relied on the old driver).
>> Yesterday I tried to remove sending of NAND_CMD_STATUS from this patch, but it broke current driver - i had ECC errors, so it looks like
>> "shot in the dark" situation, to understand this logic.
> 
> When an operation on the NAND array happens (eg. read, prog, erase),
> you need to wait "some time" before accessing the internal sram or even
> the chip which is "busy" until it gets "ready" again. You can probe the
> ready/busy pin (that's the hardware way, fast and reliable) or you can
> poll a status with NAND_CMD_STATUS. The chips are designed so they can
> actually process that command while they are doing time consuming tasks
> to update the host. But IIRC every byte read will return the status
> until you send READ0 again, which means "I'm done with the status
> read" somehow.
> 
> Please see nand_soft_waitrdy() in order to understand how this is
> supposed to work. You can even use that helper (which is exported)
> instead of open-coding it in your driver. See atmel or sunxi
> implementations for instance.
> 
> As using the native RB pin is better, you would need to identify
> whether you have one or not at probe time and then either poll the
> relevant bit of your controller if there is one, or fallback to the
> soft read (which should fallback on exec_op in the end).

Thanks for this information! I'll use 'nand_soft_waitrdy()' at least, because i guess that
there is no RB pin on my device.

Thanks, Arseniy

> 
> Thanks,
> Miquèl

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-30 11:19             ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-30 11:19 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel



On 26.05.2023 20:22, Miquel Raynal wrote:
> Hi Arseniy,
> 
> avkrasnov@sberdevices.ru wrote on Wed, 24 May 2023 12:05:47 +0300:
> 
>> On 23.05.2023 12:12, Arseniy Krasnov wrote:
>>> Hello Miquel, Liang
>>>
>>> On 22.05.2023 18:05, Miquel Raynal wrote:  
>>>> Hi Arseniy,
>>>>
>>>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
>>>>  
>>>>> This fixes read/write functionality by:
>>>>> 1) Changing NFC_CMD_RB_INT bit value.  
>>>>
>>>> I guess this is a separate fix
>>>>  
>>>
>>> Ok, I'll move it to separate patch
>>>   
>>>>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.  
>>>>
>>>> Is this really needed? Looks like you're delaying the next op only. Is
>>>> using a delay enough? If yes, then it's probably the wrong approach.  
>>
>> Hi Miquel, small update, I found some details from @Liang's message in v1 talks from the last month:
>>
>> *
>> After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send
>> NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device.
> 
> That is true.
> 
>> but previous meson_nfc_queue_rb()
>> only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
>> i think there is something wrong with the Ready/Busy pin(please check the hardware whether this
>> Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the
>> nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the
>> data bus and generate IRQ if ready.
>> *
>>
>> I guess, that sequence of commands from this patch is described in datasheet (unfortunately I don't have it and relied on the old driver).
>> Yesterday I tried to remove sending of NAND_CMD_STATUS from this patch, but it broke current driver - i had ECC errors, so it looks like
>> "shot in the dark" situation, to understand this logic.
> 
> When an operation on the NAND array happens (eg. read, prog, erase),
> you need to wait "some time" before accessing the internal sram or even
> the chip which is "busy" until it gets "ready" again. You can probe the
> ready/busy pin (that's the hardware way, fast and reliable) or you can
> poll a status with NAND_CMD_STATUS. The chips are designed so they can
> actually process that command while they are doing time consuming tasks
> to update the host. But IIRC every byte read will return the status
> until you send READ0 again, which means "I'm done with the status
> read" somehow.
> 
> Please see nand_soft_waitrdy() in order to understand how this is
> supposed to work. You can even use that helper (which is exported)
> instead of open-coding it in your driver. See atmel or sunxi
> implementations for instance.
> 
> As using the native RB pin is better, you would need to identify
> whether you have one or not at probe time and then either poll the
> relevant bit of your controller if there is one, or fallback to the
> soft read (which should fallback on exec_op in the end).

Thanks for this information! I'll use 'nand_soft_waitrdy()' at least, because i guess that
there is no RB pin on my device.

Thanks, Arseniy

> 
> Thanks,
> Miquèl

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

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
  2023-05-30 11:19             ` Arseniy Krasnov
  (?)
  (?)
@ 2023-05-30 13:05               ` Miquel Raynal
  -1 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-30 13:05 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

avkrasnov@sberdevices.ru wrote on Tue, 30 May 2023 14:19:08 +0300:

> On 26.05.2023 20:22, Miquel Raynal wrote:
> > Hi Arseniy,
> > 
> > avkrasnov@sberdevices.ru wrote on Wed, 24 May 2023 12:05:47 +0300:
> >   
> >> On 23.05.2023 12:12, Arseniy Krasnov wrote:  
> >>> Hello Miquel, Liang
> >>>
> >>> On 22.05.2023 18:05, Miquel Raynal wrote:    
> >>>> Hi Arseniy,
> >>>>
> >>>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
> >>>>    
> >>>>> This fixes read/write functionality by:
> >>>>> 1) Changing NFC_CMD_RB_INT bit value.    
> >>>>
> >>>> I guess this is a separate fix
> >>>>    
> >>>
> >>> Ok, I'll move it to separate patch
> >>>     
> >>>>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.    
> >>>>
> >>>> Is this really needed? Looks like you're delaying the next op only. Is
> >>>> using a delay enough? If yes, then it's probably the wrong approach.    
> >>
> >> Hi Miquel, small update, I found some details from @Liang's message in v1 talks from the last month:
> >>
> >> *
> >> After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send
> >> NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device.  
> > 
> > That is true.
> >   
> >> but previous meson_nfc_queue_rb()
> >> only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
> >> i think there is something wrong with the Ready/Busy pin(please check the hardware whether this
> >> Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the
> >> nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the
> >> data bus and generate IRQ if ready.
> >> *
> >>
> >> I guess, that sequence of commands from this patch is described in datasheet (unfortunately I don't have it and relied on the old driver).
> >> Yesterday I tried to remove sending of NAND_CMD_STATUS from this patch, but it broke current driver - i had ECC errors, so it looks like
> >> "shot in the dark" situation, to understand this logic.  
> > 
> > When an operation on the NAND array happens (eg. read, prog, erase),
> > you need to wait "some time" before accessing the internal sram or even
> > the chip which is "busy" until it gets "ready" again. You can probe the
> > ready/busy pin (that's the hardware way, fast and reliable) or you can
> > poll a status with NAND_CMD_STATUS. The chips are designed so they can
> > actually process that command while they are doing time consuming tasks
> > to update the host. But IIRC every byte read will return the status
> > until you send READ0 again, which means "I'm done with the status
> > read" somehow.
> > 
> > Please see nand_soft_waitrdy() in order to understand how this is
> > supposed to work. You can even use that helper (which is exported)
> > instead of open-coding it in your driver. See atmel or sunxi
> > implementations for instance.
> > 
> > As using the native RB pin is better, you would need to identify
> > whether you have one or not at probe time and then either poll the
> > relevant bit of your controller if there is one, or fallback to the
> > soft read (which should fallback on exec_op in the end).  
> 
> Thanks for this information! I'll use 'nand_soft_waitrdy()' at least, because i guess that
> there is no RB pin on my device.

Currently there is only support for the physical pin IIRC. This means
you cannot just drop it. You need to support both.

Thanks,
Miquèl

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-30 13:05               ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-30 13:05 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

avkrasnov@sberdevices.ru wrote on Tue, 30 May 2023 14:19:08 +0300:

> On 26.05.2023 20:22, Miquel Raynal wrote:
> > Hi Arseniy,
> > 
> > avkrasnov@sberdevices.ru wrote on Wed, 24 May 2023 12:05:47 +0300:
> >   
> >> On 23.05.2023 12:12, Arseniy Krasnov wrote:  
> >>> Hello Miquel, Liang
> >>>
> >>> On 22.05.2023 18:05, Miquel Raynal wrote:    
> >>>> Hi Arseniy,
> >>>>
> >>>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
> >>>>    
> >>>>> This fixes read/write functionality by:
> >>>>> 1) Changing NFC_CMD_RB_INT bit value.    
> >>>>
> >>>> I guess this is a separate fix
> >>>>    
> >>>
> >>> Ok, I'll move it to separate patch
> >>>     
> >>>>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.    
> >>>>
> >>>> Is this really needed? Looks like you're delaying the next op only. Is
> >>>> using a delay enough? If yes, then it's probably the wrong approach.    
> >>
> >> Hi Miquel, small update, I found some details from @Liang's message in v1 talks from the last month:
> >>
> >> *
> >> After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send
> >> NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device.  
> > 
> > That is true.
> >   
> >> but previous meson_nfc_queue_rb()
> >> only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
> >> i think there is something wrong with the Ready/Busy pin(please check the hardware whether this
> >> Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the
> >> nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the
> >> data bus and generate IRQ if ready.
> >> *
> >>
> >> I guess, that sequence of commands from this patch is described in datasheet (unfortunately I don't have it and relied on the old driver).
> >> Yesterday I tried to remove sending of NAND_CMD_STATUS from this patch, but it broke current driver - i had ECC errors, so it looks like
> >> "shot in the dark" situation, to understand this logic.  
> > 
> > When an operation on the NAND array happens (eg. read, prog, erase),
> > you need to wait "some time" before accessing the internal sram or even
> > the chip which is "busy" until it gets "ready" again. You can probe the
> > ready/busy pin (that's the hardware way, fast and reliable) or you can
> > poll a status with NAND_CMD_STATUS. The chips are designed so they can
> > actually process that command while they are doing time consuming tasks
> > to update the host. But IIRC every byte read will return the status
> > until you send READ0 again, which means "I'm done with the status
> > read" somehow.
> > 
> > Please see nand_soft_waitrdy() in order to understand how this is
> > supposed to work. You can even use that helper (which is exported)
> > instead of open-coding it in your driver. See atmel or sunxi
> > implementations for instance.
> > 
> > As using the native RB pin is better, you would need to identify
> > whether you have one or not at probe time and then either poll the
> > relevant bit of your controller if there is one, or fallback to the
> > soft read (which should fallback on exec_op in the end).  
> 
> Thanks for this information! I'll use 'nand_soft_waitrdy()' at least, because i guess that
> there is no RB pin on my device.

Currently there is only support for the physical pin IIRC. This means
you cannot just drop it. You need to support both.

Thanks,
Miquèl

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

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-30 13:05               ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-30 13:05 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

avkrasnov@sberdevices.ru wrote on Tue, 30 May 2023 14:19:08 +0300:

> On 26.05.2023 20:22, Miquel Raynal wrote:
> > Hi Arseniy,
> > 
> > avkrasnov@sberdevices.ru wrote on Wed, 24 May 2023 12:05:47 +0300:
> >   
> >> On 23.05.2023 12:12, Arseniy Krasnov wrote:  
> >>> Hello Miquel, Liang
> >>>
> >>> On 22.05.2023 18:05, Miquel Raynal wrote:    
> >>>> Hi Arseniy,
> >>>>
> >>>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
> >>>>    
> >>>>> This fixes read/write functionality by:
> >>>>> 1) Changing NFC_CMD_RB_INT bit value.    
> >>>>
> >>>> I guess this is a separate fix
> >>>>    
> >>>
> >>> Ok, I'll move it to separate patch
> >>>     
> >>>>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.    
> >>>>
> >>>> Is this really needed? Looks like you're delaying the next op only. Is
> >>>> using a delay enough? If yes, then it's probably the wrong approach.    
> >>
> >> Hi Miquel, small update, I found some details from @Liang's message in v1 talks from the last month:
> >>
> >> *
> >> After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send
> >> NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device.  
> > 
> > That is true.
> >   
> >> but previous meson_nfc_queue_rb()
> >> only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
> >> i think there is something wrong with the Ready/Busy pin(please check the hardware whether this
> >> Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the
> >> nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the
> >> data bus and generate IRQ if ready.
> >> *
> >>
> >> I guess, that sequence of commands from this patch is described in datasheet (unfortunately I don't have it and relied on the old driver).
> >> Yesterday I tried to remove sending of NAND_CMD_STATUS from this patch, but it broke current driver - i had ECC errors, so it looks like
> >> "shot in the dark" situation, to understand this logic.  
> > 
> > When an operation on the NAND array happens (eg. read, prog, erase),
> > you need to wait "some time" before accessing the internal sram or even
> > the chip which is "busy" until it gets "ready" again. You can probe the
> > ready/busy pin (that's the hardware way, fast and reliable) or you can
> > poll a status with NAND_CMD_STATUS. The chips are designed so they can
> > actually process that command while they are doing time consuming tasks
> > to update the host. But IIRC every byte read will return the status
> > until you send READ0 again, which means "I'm done with the status
> > read" somehow.
> > 
> > Please see nand_soft_waitrdy() in order to understand how this is
> > supposed to work. You can even use that helper (which is exported)
> > instead of open-coding it in your driver. See atmel or sunxi
> > implementations for instance.
> > 
> > As using the native RB pin is better, you would need to identify
> > whether you have one or not at probe time and then either poll the
> > relevant bit of your controller if there is one, or fallback to the
> > soft read (which should fallback on exec_op in the end).  
> 
> Thanks for this information! I'll use 'nand_soft_waitrdy()' at least, because i guess that
> there is no RB pin on my device.

Currently there is only support for the physical pin IIRC. This means
you cannot just drop it. You need to support both.

Thanks,
Miquèl

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-30 13:05               ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-30 13:05 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

avkrasnov@sberdevices.ru wrote on Tue, 30 May 2023 14:19:08 +0300:

> On 26.05.2023 20:22, Miquel Raynal wrote:
> > Hi Arseniy,
> > 
> > avkrasnov@sberdevices.ru wrote on Wed, 24 May 2023 12:05:47 +0300:
> >   
> >> On 23.05.2023 12:12, Arseniy Krasnov wrote:  
> >>> Hello Miquel, Liang
> >>>
> >>> On 22.05.2023 18:05, Miquel Raynal wrote:    
> >>>> Hi Arseniy,
> >>>>
> >>>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
> >>>>    
> >>>>> This fixes read/write functionality by:
> >>>>> 1) Changing NFC_CMD_RB_INT bit value.    
> >>>>
> >>>> I guess this is a separate fix
> >>>>    
> >>>
> >>> Ok, I'll move it to separate patch
> >>>     
> >>>>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.    
> >>>>
> >>>> Is this really needed? Looks like you're delaying the next op only. Is
> >>>> using a delay enough? If yes, then it's probably the wrong approach.    
> >>
> >> Hi Miquel, small update, I found some details from @Liang's message in v1 talks from the last month:
> >>
> >> *
> >> After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send
> >> NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device.  
> > 
> > That is true.
> >   
> >> but previous meson_nfc_queue_rb()
> >> only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
> >> i think there is something wrong with the Ready/Busy pin(please check the hardware whether this
> >> Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the
> >> nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the
> >> data bus and generate IRQ if ready.
> >> *
> >>
> >> I guess, that sequence of commands from this patch is described in datasheet (unfortunately I don't have it and relied on the old driver).
> >> Yesterday I tried to remove sending of NAND_CMD_STATUS from this patch, but it broke current driver - i had ECC errors, so it looks like
> >> "shot in the dark" situation, to understand this logic.  
> > 
> > When an operation on the NAND array happens (eg. read, prog, erase),
> > you need to wait "some time" before accessing the internal sram or even
> > the chip which is "busy" until it gets "ready" again. You can probe the
> > ready/busy pin (that's the hardware way, fast and reliable) or you can
> > poll a status with NAND_CMD_STATUS. The chips are designed so they can
> > actually process that command while they are doing time consuming tasks
> > to update the host. But IIRC every byte read will return the status
> > until you send READ0 again, which means "I'm done with the status
> > read" somehow.
> > 
> > Please see nand_soft_waitrdy() in order to understand how this is
> > supposed to work. You can even use that helper (which is exported)
> > instead of open-coding it in your driver. See atmel or sunxi
> > implementations for instance.
> > 
> > As using the native RB pin is better, you would need to identify
> > whether you have one or not at probe time and then either poll the
> > relevant bit of your controller if there is one, or fallback to the
> > soft read (which should fallback on exec_op in the end).  
> 
> Thanks for this information! I'll use 'nand_soft_waitrdy()' at least, because i guess that
> there is no RB pin on my device.

Currently there is only support for the physical pin IIRC. This means
you cannot just drop it. You need to support both.

Thanks,
Miquèl

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
  2023-05-30 13:05               ` Miquel Raynal
  (?)
  (?)
@ 2023-05-30 13:35                 ` Arseniy Krasnov
  -1 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-30 13:35 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, JeromeBrunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel



On 30.05.2023 16:05, Miquel Raynal wrote:
> Hi Arseniy,
> 
> avkrasnov@sberdevices.ru wrote on Tue, 30 May 2023 14:19:08 +0300:
> 
>> On 26.05.2023 20:22, Miquel Raynal wrote:
>>> Hi Arseniy,
>>>
>>> avkrasnov@sberdevices.ru wrote on Wed, 24 May 2023 12:05:47 +0300:
>>>   
>>>> On 23.05.2023 12:12, Arseniy Krasnov wrote:  
>>>>> Hello Miquel, Liang
>>>>>
>>>>> On 22.05.2023 18:05, Miquel Raynal wrote:    
>>>>>> Hi Arseniy,
>>>>>>
>>>>>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
>>>>>>    
>>>>>>> This fixes read/write functionality by:
>>>>>>> 1) Changing NFC_CMD_RB_INT bit value.    
>>>>>>
>>>>>> I guess this is a separate fix
>>>>>>    
>>>>>
>>>>> Ok, I'll move it to separate patch
>>>>>     
>>>>>>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.    
>>>>>>
>>>>>> Is this really needed? Looks like you're delaying the next op only. Is
>>>>>> using a delay enough? If yes, then it's probably the wrong approach.    
>>>>
>>>> Hi Miquel, small update, I found some details from @Liang's message in v1 talks from the last month:
>>>>
>>>> *
>>>> After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send
>>>> NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device.  
>>>
>>> That is true.
>>>   
>>>> but previous meson_nfc_queue_rb()
>>>> only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
>>>> i think there is something wrong with the Ready/Busy pin(please check the hardware whether this
>>>> Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the
>>>> nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the
>>>> data bus and generate IRQ if ready.
>>>> *
>>>>
>>>> I guess, that sequence of commands from this patch is described in datasheet (unfortunately I don't have it and relied on the old driver).
>>>> Yesterday I tried to remove sending of NAND_CMD_STATUS from this patch, but it broke current driver - i had ECC errors, so it looks like
>>>> "shot in the dark" situation, to understand this logic.  
>>>
>>> When an operation on the NAND array happens (eg. read, prog, erase),
>>> you need to wait "some time" before accessing the internal sram or even
>>> the chip which is "busy" until it gets "ready" again. You can probe the
>>> ready/busy pin (that's the hardware way, fast and reliable) or you can
>>> poll a status with NAND_CMD_STATUS. The chips are designed so they can
>>> actually process that command while they are doing time consuming tasks
>>> to update the host. But IIRC every byte read will return the status
>>> until you send READ0 again, which means "I'm done with the status
>>> read" somehow.
>>>
>>> Please see nand_soft_waitrdy() in order to understand how this is
>>> supposed to work. You can even use that helper (which is exported)
>>> instead of open-coding it in your driver. See atmel or sunxi
>>> implementations for instance.
>>>
>>> As using the native RB pin is better, you would need to identify
>>> whether you have one or not at probe time and then either poll the
>>> relevant bit of your controller if there is one, or fallback to the
>>> soft read (which should fallback on exec_op in the end).  
>>
>> Thanks for this information! I'll use 'nand_soft_waitrdy()' at least, because i guess that
>> there is no RB pin on my device.
> 
> Currently there is only support for the physical pin IIRC. This means
> you cannot just drop it. You need to support both.

Yes, i'm not going to drop RB pin support, but as I don't have device to test it(i guess), i'll add
'nand_sort_waitrdy()' anyway.

Thanks, Arseniy

> 
> Thanks,
> Miquèl

УВЕДОМЛЕНИЕ О КОНФИДЕНЦИАЛЬНОСТИ: Это электронное сообщение и любые документы, приложенные к нему, содержат конфиденциальную информацию. Настоящим уведомляем Вас о том, что если это сообщение не предназначено Вам, использование, копирование, распространение информации, содержащейся в настоящем сообщении, а также осуществление любых действий на основе этой информации, строго запрещено. Если Вы получили это сообщение по ошибке, пожалуйста, сообщите об этом отправителю по электронной почте и удалите это сообщение.
CONFIDENTIALITY NOTICE: This email and any files attached to it are confidential. If you are not the intended recipient you are notified that using, copying, distributing or taking any action in reliance on the contents of this information is strictly prohibited. If you have received this email in error please notify the sender and delete this email.

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-30 13:35                 ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-30 13:35 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, JeromeBrunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel



On 30.05.2023 16:05, Miquel Raynal wrote:
> Hi Arseniy,
> 
> avkrasnov@sberdevices.ru wrote on Tue, 30 May 2023 14:19:08 +0300:
> 
>> On 26.05.2023 20:22, Miquel Raynal wrote:
>>> Hi Arseniy,
>>>
>>> avkrasnov@sberdevices.ru wrote on Wed, 24 May 2023 12:05:47 +0300:
>>>   
>>>> On 23.05.2023 12:12, Arseniy Krasnov wrote:  
>>>>> Hello Miquel, Liang
>>>>>
>>>>> On 22.05.2023 18:05, Miquel Raynal wrote:    
>>>>>> Hi Arseniy,
>>>>>>
>>>>>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
>>>>>>    
>>>>>>> This fixes read/write functionality by:
>>>>>>> 1) Changing NFC_CMD_RB_INT bit value.    
>>>>>>
>>>>>> I guess this is a separate fix
>>>>>>    
>>>>>
>>>>> Ok, I'll move it to separate patch
>>>>>     
>>>>>>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.    
>>>>>>
>>>>>> Is this really needed? Looks like you're delaying the next op only. Is
>>>>>> using a delay enough? If yes, then it's probably the wrong approach.    
>>>>
>>>> Hi Miquel, small update, I found some details from @Liang's message in v1 talks from the last month:
>>>>
>>>> *
>>>> After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send
>>>> NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device.  
>>>
>>> That is true.
>>>   
>>>> but previous meson_nfc_queue_rb()
>>>> only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
>>>> i think there is something wrong with the Ready/Busy pin(please check the hardware whether this
>>>> Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the
>>>> nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the
>>>> data bus and generate IRQ if ready.
>>>> *
>>>>
>>>> I guess, that sequence of commands from this patch is described in datasheet (unfortunately I don't have it and relied on the old driver).
>>>> Yesterday I tried to remove sending of NAND_CMD_STATUS from this patch, but it broke current driver - i had ECC errors, so it looks like
>>>> "shot in the dark" situation, to understand this logic.  
>>>
>>> When an operation on the NAND array happens (eg. read, prog, erase),
>>> you need to wait "some time" before accessing the internal sram or even
>>> the chip which is "busy" until it gets "ready" again. You can probe the
>>> ready/busy pin (that's the hardware way, fast and reliable) or you can
>>> poll a status with NAND_CMD_STATUS. The chips are designed so they can
>>> actually process that command while they are doing time consuming tasks
>>> to update the host. But IIRC every byte read will return the status
>>> until you send READ0 again, which means "I'm done with the status
>>> read" somehow.
>>>
>>> Please see nand_soft_waitrdy() in order to understand how this is
>>> supposed to work. You can even use that helper (which is exported)
>>> instead of open-coding it in your driver. See atmel or sunxi
>>> implementations for instance.
>>>
>>> As using the native RB pin is better, you would need to identify
>>> whether you have one or not at probe time and then either poll the
>>> relevant bit of your controller if there is one, or fallback to the
>>> soft read (which should fallback on exec_op in the end).  
>>
>> Thanks for this information! I'll use 'nand_soft_waitrdy()' at least, because i guess that
>> there is no RB pin on my device.
> 
> Currently there is only support for the physical pin IIRC. This means
> you cannot just drop it. You need to support both.

Yes, i'm not going to drop RB pin support, but as I don't have device to test it(i guess), i'll add
'nand_sort_waitrdy()' anyway.

Thanks, Arseniy

> 
> Thanks,
> Miquèl

УВЕДОМЛЕНИЕ О КОНФИДЕНЦИАЛЬНОСТИ: Это электронное сообщение и любые документы, приложенные к нему, содержат конфиденциальную информацию. Настоящим уведомляем Вас о том, что если это сообщение не предназначено Вам, использование, копирование, распространение информации, содержащейся в настоящем сообщении, а также осуществление любых действий на основе этой информации, строго запрещено. Если Вы получили это сообщение по ошибке, пожалуйста, сообщите об этом отправителю по электронной почте и удалите это сообщение.
CONFIDENTIALITY NOTICE: This email and any files attached to it are confidential. If you are not the intended recipient you are notified that using, copying, distributing or taking any action in reliance on the contents of this information is strictly prohibited. If you have received this email in error please notify the sender and delete this email.

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

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-30 13:35                 ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-30 13:35 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, JeromeBrunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel



On 30.05.2023 16:05, Miquel Raynal wrote:
> Hi Arseniy,
> 
> avkrasnov@sberdevices.ru wrote on Tue, 30 May 2023 14:19:08 +0300:
> 
>> On 26.05.2023 20:22, Miquel Raynal wrote:
>>> Hi Arseniy,
>>>
>>> avkrasnov@sberdevices.ru wrote on Wed, 24 May 2023 12:05:47 +0300:
>>>   
>>>> On 23.05.2023 12:12, Arseniy Krasnov wrote:  
>>>>> Hello Miquel, Liang
>>>>>
>>>>> On 22.05.2023 18:05, Miquel Raynal wrote:    
>>>>>> Hi Arseniy,
>>>>>>
>>>>>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
>>>>>>    
>>>>>>> This fixes read/write functionality by:
>>>>>>> 1) Changing NFC_CMD_RB_INT bit value.    
>>>>>>
>>>>>> I guess this is a separate fix
>>>>>>    
>>>>>
>>>>> Ok, I'll move it to separate patch
>>>>>     
>>>>>>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.    
>>>>>>
>>>>>> Is this really needed? Looks like you're delaying the next op only. Is
>>>>>> using a delay enough? If yes, then it's probably the wrong approach.    
>>>>
>>>> Hi Miquel, small update, I found some details from @Liang's message in v1 talks from the last month:
>>>>
>>>> *
>>>> After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send
>>>> NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device.  
>>>
>>> That is true.
>>>   
>>>> but previous meson_nfc_queue_rb()
>>>> only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
>>>> i think there is something wrong with the Ready/Busy pin(please check the hardware whether this
>>>> Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the
>>>> nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the
>>>> data bus and generate IRQ if ready.
>>>> *
>>>>
>>>> I guess, that sequence of commands from this patch is described in datasheet (unfortunately I don't have it and relied on the old driver).
>>>> Yesterday I tried to remove sending of NAND_CMD_STATUS from this patch, but it broke current driver - i had ECC errors, so it looks like
>>>> "shot in the dark" situation, to understand this logic.  
>>>
>>> When an operation on the NAND array happens (eg. read, prog, erase),
>>> you need to wait "some time" before accessing the internal sram or even
>>> the chip which is "busy" until it gets "ready" again. You can probe the
>>> ready/busy pin (that's the hardware way, fast and reliable) or you can
>>> poll a status with NAND_CMD_STATUS. The chips are designed so they can
>>> actually process that command while they are doing time consuming tasks
>>> to update the host. But IIRC every byte read will return the status
>>> until you send READ0 again, which means "I'm done with the status
>>> read" somehow.
>>>
>>> Please see nand_soft_waitrdy() in order to understand how this is
>>> supposed to work. You can even use that helper (which is exported)
>>> instead of open-coding it in your driver. See atmel or sunxi
>>> implementations for instance.
>>>
>>> As using the native RB pin is better, you would need to identify
>>> whether you have one or not at probe time and then either poll the
>>> relevant bit of your controller if there is one, or fallback to the
>>> soft read (which should fallback on exec_op in the end).  
>>
>> Thanks for this information! I'll use 'nand_soft_waitrdy()' at least, because i guess that
>> there is no RB pin on my device.
> 
> Currently there is only support for the physical pin IIRC. This means
> you cannot just drop it. You need to support both.

Yes, i'm not going to drop RB pin support, but as I don't have device to test it(i guess), i'll add
'nand_sort_waitrdy()' anyway.

Thanks, Arseniy

> 
> Thanks,
> Miquèl

УВЕДОМЛЕНИЕ О КОНФИДЕНЦИАЛЬНОСТИ: Это электронное сообщение и любые документы, приложенные к нему, содержат конфиденциальную информацию. Настоящим уведомляем Вас о том, что если это сообщение не предназначено Вам, использование, копирование, распространение информации, содержащейся в настоящем сообщении, а также осуществление любых действий на основе этой информации, строго запрещено. Если Вы получили это сообщение по ошибке, пожалуйста, сообщите об этом отправителю по электронной почте и удалите это сообщение.
CONFIDENTIALITY NOTICE: This email and any files attached to it are confidential. If you are not the intended recipient you are notified that using, copying, distributing or taking any action in reliance on the contents of this information is strictly prohibited. If you have received this email in error please notify the sender and delete this email.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-30 13:35                 ` Arseniy Krasnov
  0 siblings, 0 replies; 108+ messages in thread
From: Arseniy Krasnov @ 2023-05-30 13:35 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, JeromeBrunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel



On 30.05.2023 16:05, Miquel Raynal wrote:
> Hi Arseniy,
> 
> avkrasnov@sberdevices.ru wrote on Tue, 30 May 2023 14:19:08 +0300:
> 
>> On 26.05.2023 20:22, Miquel Raynal wrote:
>>> Hi Arseniy,
>>>
>>> avkrasnov@sberdevices.ru wrote on Wed, 24 May 2023 12:05:47 +0300:
>>>   
>>>> On 23.05.2023 12:12, Arseniy Krasnov wrote:  
>>>>> Hello Miquel, Liang
>>>>>
>>>>> On 22.05.2023 18:05, Miquel Raynal wrote:    
>>>>>> Hi Arseniy,
>>>>>>
>>>>>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
>>>>>>    
>>>>>>> This fixes read/write functionality by:
>>>>>>> 1) Changing NFC_CMD_RB_INT bit value.    
>>>>>>
>>>>>> I guess this is a separate fix
>>>>>>    
>>>>>
>>>>> Ok, I'll move it to separate patch
>>>>>     
>>>>>>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.    
>>>>>>
>>>>>> Is this really needed? Looks like you're delaying the next op only. Is
>>>>>> using a delay enough? If yes, then it's probably the wrong approach.    
>>>>
>>>> Hi Miquel, small update, I found some details from @Liang's message in v1 talks from the last month:
>>>>
>>>> *
>>>> After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send
>>>> NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device.  
>>>
>>> That is true.
>>>   
>>>> but previous meson_nfc_queue_rb()
>>>> only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
>>>> i think there is something wrong with the Ready/Busy pin(please check the hardware whether this
>>>> Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the
>>>> nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the
>>>> data bus and generate IRQ if ready.
>>>> *
>>>>
>>>> I guess, that sequence of commands from this patch is described in datasheet (unfortunately I don't have it and relied on the old driver).
>>>> Yesterday I tried to remove sending of NAND_CMD_STATUS from this patch, but it broke current driver - i had ECC errors, so it looks like
>>>> "shot in the dark" situation, to understand this logic.  
>>>
>>> When an operation on the NAND array happens (eg. read, prog, erase),
>>> you need to wait "some time" before accessing the internal sram or even
>>> the chip which is "busy" until it gets "ready" again. You can probe the
>>> ready/busy pin (that's the hardware way, fast and reliable) or you can
>>> poll a status with NAND_CMD_STATUS. The chips are designed so they can
>>> actually process that command while they are doing time consuming tasks
>>> to update the host. But IIRC every byte read will return the status
>>> until you send READ0 again, which means "I'm done with the status
>>> read" somehow.
>>>
>>> Please see nand_soft_waitrdy() in order to understand how this is
>>> supposed to work. You can even use that helper (which is exported)
>>> instead of open-coding it in your driver. See atmel or sunxi
>>> implementations for instance.
>>>
>>> As using the native RB pin is better, you would need to identify
>>> whether you have one or not at probe time and then either poll the
>>> relevant bit of your controller if there is one, or fallback to the
>>> soft read (which should fallback on exec_op in the end).  
>>
>> Thanks for this information! I'll use 'nand_soft_waitrdy()' at least, because i guess that
>> there is no RB pin on my device.
> 
> Currently there is only support for the physical pin IIRC. This means
> you cannot just drop it. You need to support both.

Yes, i'm not going to drop RB pin support, but as I don't have device to test it(i guess), i'll add
'nand_sort_waitrdy()' anyway.

Thanks, Arseniy

> 
> Thanks,
> Miquèl

УВЕДОМЛЕНИЕ О КОНФИДЕНЦИАЛЬНОСТИ: Это электронное сообщение и любые документы, приложенные к нему, содержат конфиденциальную информацию. Настоящим уведомляем Вас о том, что если это сообщение не предназначено Вам, использование, копирование, распространение информации, содержащейся в настоящем сообщении, а также осуществление любых действий на основе этой информации, строго запрещено. Если Вы получили это сообщение по ошибке, пожалуйста, сообщите об этом отправителю по электронной почте и удалите это сообщение.
CONFIDENTIALITY NOTICE: This email and any files attached to it are confidential. If you are not the intended recipient you are notified that using, copying, distributing or taking any action in reliance on the contents of this information is strictly prohibited. If you have received this email in error please notify the sender and delete this email.

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
  2023-05-30 13:35                 ` Arseniy Krasnov
  (?)
  (?)
@ 2023-05-30 13:58                   ` Miquel Raynal
  -1 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-30 13:58 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, JeromeBrunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

avkrasnov@sberdevices.ru wrote on Tue, 30 May 2023 16:35:59 +0300:

> On 30.05.2023 16:05, Miquel Raynal wrote:
> > Hi Arseniy,
> > 
> > avkrasnov@sberdevices.ru wrote on Tue, 30 May 2023 14:19:08 +0300:
> >   
> >> On 26.05.2023 20:22, Miquel Raynal wrote:  
> >>> Hi Arseniy,
> >>>
> >>> avkrasnov@sberdevices.ru wrote on Wed, 24 May 2023 12:05:47 +0300:
> >>>     
> >>>> On 23.05.2023 12:12, Arseniy Krasnov wrote:    
> >>>>> Hello Miquel, Liang
> >>>>>
> >>>>> On 22.05.2023 18:05, Miquel Raynal wrote:      
> >>>>>> Hi Arseniy,
> >>>>>>
> >>>>>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
> >>>>>>      
> >>>>>>> This fixes read/write functionality by:
> >>>>>>> 1) Changing NFC_CMD_RB_INT bit value.      
> >>>>>>
> >>>>>> I guess this is a separate fix
> >>>>>>      
> >>>>>
> >>>>> Ok, I'll move it to separate patch
> >>>>>       
> >>>>>>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.      
> >>>>>>
> >>>>>> Is this really needed? Looks like you're delaying the next op only. Is
> >>>>>> using a delay enough? If yes, then it's probably the wrong approach.      
> >>>>
> >>>> Hi Miquel, small update, I found some details from @Liang's message in v1 talks from the last month:
> >>>>
> >>>> *
> >>>> After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send
> >>>> NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device.    
> >>>
> >>> That is true.
> >>>     
> >>>> but previous meson_nfc_queue_rb()
> >>>> only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
> >>>> i think there is something wrong with the Ready/Busy pin(please check the hardware whether this
> >>>> Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the
> >>>> nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the
> >>>> data bus and generate IRQ if ready.
> >>>> *
> >>>>
> >>>> I guess, that sequence of commands from this patch is described in datasheet (unfortunately I don't have it and relied on the old driver).
> >>>> Yesterday I tried to remove sending of NAND_CMD_STATUS from this patch, but it broke current driver - i had ECC errors, so it looks like
> >>>> "shot in the dark" situation, to understand this logic.    
> >>>
> >>> When an operation on the NAND array happens (eg. read, prog, erase),
> >>> you need to wait "some time" before accessing the internal sram or even
> >>> the chip which is "busy" until it gets "ready" again. You can probe the
> >>> ready/busy pin (that's the hardware way, fast and reliable) or you can
> >>> poll a status with NAND_CMD_STATUS. The chips are designed so they can
> >>> actually process that command while they are doing time consuming tasks
> >>> to update the host. But IIRC every byte read will return the status
> >>> until you send READ0 again, which means "I'm done with the status
> >>> read" somehow.
> >>>
> >>> Please see nand_soft_waitrdy() in order to understand how this is
> >>> supposed to work. You can even use that helper (which is exported)
> >>> instead of open-coding it in your driver. See atmel or sunxi
> >>> implementations for instance.
> >>>
> >>> As using the native RB pin is better, you would need to identify
> >>> whether you have one or not at probe time and then either poll the
> >>> relevant bit of your controller if there is one, or fallback to the
> >>> soft read (which should fallback on exec_op in the end).    
> >>
> >> Thanks for this information! I'll use 'nand_soft_waitrdy()' at least, because i guess that
> >> there is no RB pin on my device.  
> > 
> > Currently there is only support for the physical pin IIRC. This means
> > you cannot just drop it. You need to support both.  
> 
> Yes, i'm not going to drop RB pin support, but as I don't have device to test it(i guess), i'll add
> 'nand_sort_waitrdy()' anyway.

Clear. Then go for it :)

Thanks,
Miquèl

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-30 13:58                   ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-30 13:58 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, JeromeBrunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

avkrasnov@sberdevices.ru wrote on Tue, 30 May 2023 16:35:59 +0300:

> On 30.05.2023 16:05, Miquel Raynal wrote:
> > Hi Arseniy,
> > 
> > avkrasnov@sberdevices.ru wrote on Tue, 30 May 2023 14:19:08 +0300:
> >   
> >> On 26.05.2023 20:22, Miquel Raynal wrote:  
> >>> Hi Arseniy,
> >>>
> >>> avkrasnov@sberdevices.ru wrote on Wed, 24 May 2023 12:05:47 +0300:
> >>>     
> >>>> On 23.05.2023 12:12, Arseniy Krasnov wrote:    
> >>>>> Hello Miquel, Liang
> >>>>>
> >>>>> On 22.05.2023 18:05, Miquel Raynal wrote:      
> >>>>>> Hi Arseniy,
> >>>>>>
> >>>>>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
> >>>>>>      
> >>>>>>> This fixes read/write functionality by:
> >>>>>>> 1) Changing NFC_CMD_RB_INT bit value.      
> >>>>>>
> >>>>>> I guess this is a separate fix
> >>>>>>      
> >>>>>
> >>>>> Ok, I'll move it to separate patch
> >>>>>       
> >>>>>>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.      
> >>>>>>
> >>>>>> Is this really needed? Looks like you're delaying the next op only. Is
> >>>>>> using a delay enough? If yes, then it's probably the wrong approach.      
> >>>>
> >>>> Hi Miquel, small update, I found some details from @Liang's message in v1 talks from the last month:
> >>>>
> >>>> *
> >>>> After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send
> >>>> NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device.    
> >>>
> >>> That is true.
> >>>     
> >>>> but previous meson_nfc_queue_rb()
> >>>> only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
> >>>> i think there is something wrong with the Ready/Busy pin(please check the hardware whether this
> >>>> Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the
> >>>> nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the
> >>>> data bus and generate IRQ if ready.
> >>>> *
> >>>>
> >>>> I guess, that sequence of commands from this patch is described in datasheet (unfortunately I don't have it and relied on the old driver).
> >>>> Yesterday I tried to remove sending of NAND_CMD_STATUS from this patch, but it broke current driver - i had ECC errors, so it looks like
> >>>> "shot in the dark" situation, to understand this logic.    
> >>>
> >>> When an operation on the NAND array happens (eg. read, prog, erase),
> >>> you need to wait "some time" before accessing the internal sram or even
> >>> the chip which is "busy" until it gets "ready" again. You can probe the
> >>> ready/busy pin (that's the hardware way, fast and reliable) or you can
> >>> poll a status with NAND_CMD_STATUS. The chips are designed so they can
> >>> actually process that command while they are doing time consuming tasks
> >>> to update the host. But IIRC every byte read will return the status
> >>> until you send READ0 again, which means "I'm done with the status
> >>> read" somehow.
> >>>
> >>> Please see nand_soft_waitrdy() in order to understand how this is
> >>> supposed to work. You can even use that helper (which is exported)
> >>> instead of open-coding it in your driver. See atmel or sunxi
> >>> implementations for instance.
> >>>
> >>> As using the native RB pin is better, you would need to identify
> >>> whether you have one or not at probe time and then either poll the
> >>> relevant bit of your controller if there is one, or fallback to the
> >>> soft read (which should fallback on exec_op in the end).    
> >>
> >> Thanks for this information! I'll use 'nand_soft_waitrdy()' at least, because i guess that
> >> there is no RB pin on my device.  
> > 
> > Currently there is only support for the physical pin IIRC. This means
> > you cannot just drop it. You need to support both.  
> 
> Yes, i'm not going to drop RB pin support, but as I don't have device to test it(i guess), i'll add
> 'nand_sort_waitrdy()' anyway.

Clear. Then go for it :)

Thanks,
Miquèl

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

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-30 13:58                   ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-30 13:58 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, JeromeBrunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

avkrasnov@sberdevices.ru wrote on Tue, 30 May 2023 16:35:59 +0300:

> On 30.05.2023 16:05, Miquel Raynal wrote:
> > Hi Arseniy,
> > 
> > avkrasnov@sberdevices.ru wrote on Tue, 30 May 2023 14:19:08 +0300:
> >   
> >> On 26.05.2023 20:22, Miquel Raynal wrote:  
> >>> Hi Arseniy,
> >>>
> >>> avkrasnov@sberdevices.ru wrote on Wed, 24 May 2023 12:05:47 +0300:
> >>>     
> >>>> On 23.05.2023 12:12, Arseniy Krasnov wrote:    
> >>>>> Hello Miquel, Liang
> >>>>>
> >>>>> On 22.05.2023 18:05, Miquel Raynal wrote:      
> >>>>>> Hi Arseniy,
> >>>>>>
> >>>>>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
> >>>>>>      
> >>>>>>> This fixes read/write functionality by:
> >>>>>>> 1) Changing NFC_CMD_RB_INT bit value.      
> >>>>>>
> >>>>>> I guess this is a separate fix
> >>>>>>      
> >>>>>
> >>>>> Ok, I'll move it to separate patch
> >>>>>       
> >>>>>>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.      
> >>>>>>
> >>>>>> Is this really needed? Looks like you're delaying the next op only. Is
> >>>>>> using a delay enough? If yes, then it's probably the wrong approach.      
> >>>>
> >>>> Hi Miquel, small update, I found some details from @Liang's message in v1 talks from the last month:
> >>>>
> >>>> *
> >>>> After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send
> >>>> NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device.    
> >>>
> >>> That is true.
> >>>     
> >>>> but previous meson_nfc_queue_rb()
> >>>> only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
> >>>> i think there is something wrong with the Ready/Busy pin(please check the hardware whether this
> >>>> Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the
> >>>> nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the
> >>>> data bus and generate IRQ if ready.
> >>>> *
> >>>>
> >>>> I guess, that sequence of commands from this patch is described in datasheet (unfortunately I don't have it and relied on the old driver).
> >>>> Yesterday I tried to remove sending of NAND_CMD_STATUS from this patch, but it broke current driver - i had ECC errors, so it looks like
> >>>> "shot in the dark" situation, to understand this logic.    
> >>>
> >>> When an operation on the NAND array happens (eg. read, prog, erase),
> >>> you need to wait "some time" before accessing the internal sram or even
> >>> the chip which is "busy" until it gets "ready" again. You can probe the
> >>> ready/busy pin (that's the hardware way, fast and reliable) or you can
> >>> poll a status with NAND_CMD_STATUS. The chips are designed so they can
> >>> actually process that command while they are doing time consuming tasks
> >>> to update the host. But IIRC every byte read will return the status
> >>> until you send READ0 again, which means "I'm done with the status
> >>> read" somehow.
> >>>
> >>> Please see nand_soft_waitrdy() in order to understand how this is
> >>> supposed to work. You can even use that helper (which is exported)
> >>> instead of open-coding it in your driver. See atmel or sunxi
> >>> implementations for instance.
> >>>
> >>> As using the native RB pin is better, you would need to identify
> >>> whether you have one or not at probe time and then either poll the
> >>> relevant bit of your controller if there is one, or fallback to the
> >>> soft read (which should fallback on exec_op in the end).    
> >>
> >> Thanks for this information! I'll use 'nand_soft_waitrdy()' at least, because i guess that
> >> there is no RB pin on my device.  
> > 
> > Currently there is only support for the physical pin IIRC. This means
> > you cannot just drop it. You need to support both.  
> 
> Yes, i'm not going to drop RB pin support, but as I don't have device to test it(i guess), i'll add
> 'nand_sort_waitrdy()' anyway.

Clear. Then go for it :)

Thanks,
Miquèl

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write
@ 2023-05-30 13:58                   ` Miquel Raynal
  0 siblings, 0 replies; 108+ messages in thread
From: Miquel Raynal @ 2023-05-30 13:58 UTC (permalink / raw)
  To: Arseniy Krasnov
  Cc: Liang Yang, Richard Weinberger, Vignesh Raghavendra,
	Neil Armstrong, Kevin Hilman, JeromeBrunet, Martin Blumenstingl,
	Yixun Lan, Jianxin Pan, oxffffaa, kernel, linux-mtd,
	linux-arm-kernel, linux-amlogic, linux-kernel

Hi Arseniy,

avkrasnov@sberdevices.ru wrote on Tue, 30 May 2023 16:35:59 +0300:

> On 30.05.2023 16:05, Miquel Raynal wrote:
> > Hi Arseniy,
> > 
> > avkrasnov@sberdevices.ru wrote on Tue, 30 May 2023 14:19:08 +0300:
> >   
> >> On 26.05.2023 20:22, Miquel Raynal wrote:  
> >>> Hi Arseniy,
> >>>
> >>> avkrasnov@sberdevices.ru wrote on Wed, 24 May 2023 12:05:47 +0300:
> >>>     
> >>>> On 23.05.2023 12:12, Arseniy Krasnov wrote:    
> >>>>> Hello Miquel, Liang
> >>>>>
> >>>>> On 22.05.2023 18:05, Miquel Raynal wrote:      
> >>>>>> Hi Arseniy,
> >>>>>>
> >>>>>> AVKrasnov@sberdevices.ru wrote on Mon, 15 May 2023 12:44:35 +0300:
> >>>>>>      
> >>>>>>> This fixes read/write functionality by:
> >>>>>>> 1) Changing NFC_CMD_RB_INT bit value.      
> >>>>>>
> >>>>>> I guess this is a separate fix
> >>>>>>      
> >>>>>
> >>>>> Ok, I'll move it to separate patch
> >>>>>       
> >>>>>>> 2) Adding extra NAND_CMD_STATUS command on each r/w request.      
> >>>>>>
> >>>>>> Is this really needed? Looks like you're delaying the next op only. Is
> >>>>>> using a delay enough? If yes, then it's probably the wrong approach.      
> >>>>
> >>>> Hi Miquel, small update, I found some details from @Liang's message in v1 talks from the last month:
> >>>>
> >>>> *
> >>>> After sending NAND_CMD_READ0, address, NAND_CMD_READSTART and read status(NAND_CMD_STATUS = 0x70) commands, it should send
> >>>> NAND_CMD_READ0 command for exiting the read status mode from the datasheet from NAND device.    
> >>>
> >>> That is true.
> >>>     
> >>>> but previous meson_nfc_queue_rb()
> >>>> only checks the Ready/Busy pin and it doesn't send read status(NAND_CMD_STATUS = 0x70) command.
> >>>> i think there is something wrong with the Ready/Busy pin(please check the hardware whether this
> >>>> Ready/Busy pin is connected with SOC) or the source code. i have the board without Ready/Busy pin and prefer to use the
> >>>> nfc command called RB_IO6. it sends NAND_CMD_STATUS command and checks bit6 of the status register of NAND device from the
> >>>> data bus and generate IRQ if ready.
> >>>> *
> >>>>
> >>>> I guess, that sequence of commands from this patch is described in datasheet (unfortunately I don't have it and relied on the old driver).
> >>>> Yesterday I tried to remove sending of NAND_CMD_STATUS from this patch, but it broke current driver - i had ECC errors, so it looks like
> >>>> "shot in the dark" situation, to understand this logic.    
> >>>
> >>> When an operation on the NAND array happens (eg. read, prog, erase),
> >>> you need to wait "some time" before accessing the internal sram or even
> >>> the chip which is "busy" until it gets "ready" again. You can probe the
> >>> ready/busy pin (that's the hardware way, fast and reliable) or you can
> >>> poll a status with NAND_CMD_STATUS. The chips are designed so they can
> >>> actually process that command while they are doing time consuming tasks
> >>> to update the host. But IIRC every byte read will return the status
> >>> until you send READ0 again, which means "I'm done with the status
> >>> read" somehow.
> >>>
> >>> Please see nand_soft_waitrdy() in order to understand how this is
> >>> supposed to work. You can even use that helper (which is exported)
> >>> instead of open-coding it in your driver. See atmel or sunxi
> >>> implementations for instance.
> >>>
> >>> As using the native RB pin is better, you would need to identify
> >>> whether you have one or not at probe time and then either poll the
> >>> relevant bit of your controller if there is one, or fallback to the
> >>> soft read (which should fallback on exec_op in the end).    
> >>
> >> Thanks for this information! I'll use 'nand_soft_waitrdy()' at least, because i guess that
> >> there is no RB pin on my device.  
> > 
> > Currently there is only support for the physical pin IIRC. This means
> > you cannot just drop it. You need to support both.  
> 
> Yes, i'm not going to drop RB pin support, but as I don't have device to test it(i guess), i'll add
> 'nand_sort_waitrdy()' anyway.

Clear. Then go for it :)

Thanks,
Miquèl

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

end of thread, other threads:[~2023-05-30 13:59 UTC | newest]

Thread overview: 108+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-05-15  9:44 [PATCH v4 0/5] refactoring and fix for Meson NAND Arseniy Krasnov
2023-05-15  9:44 ` Arseniy Krasnov
2023-05-15  9:44 ` Arseniy Krasnov
2023-05-15  9:44 ` Arseniy Krasnov
2023-05-15  9:44 ` [PATCH v4 1/5] mtd: rawnand: meson: fix command sequence for read/write Arseniy Krasnov
2023-05-15  9:44   ` Arseniy Krasnov
2023-05-15  9:44   ` Arseniy Krasnov
2023-05-15  9:44   ` Arseniy Krasnov
2023-05-22 15:05   ` Miquel Raynal
2023-05-22 15:05     ` Miquel Raynal
2023-05-22 15:05     ` Miquel Raynal
2023-05-22 15:05     ` Miquel Raynal
2023-05-23  9:12     ` Arseniy Krasnov
2023-05-23  9:12       ` Arseniy Krasnov
2023-05-23  9:12       ` Arseniy Krasnov
2023-05-23  9:12       ` Arseniy Krasnov
2023-05-24  9:05       ` Arseniy Krasnov
2023-05-24  9:05         ` Arseniy Krasnov
2023-05-24  9:05         ` Arseniy Krasnov
2023-05-24  9:05         ` Arseniy Krasnov
2023-05-26 17:22         ` Miquel Raynal
2023-05-26 17:22           ` Miquel Raynal
2023-05-26 17:22           ` Miquel Raynal
2023-05-26 17:22           ` Miquel Raynal
2023-05-30 11:19           ` Arseniy Krasnov
2023-05-30 11:19             ` Arseniy Krasnov
2023-05-30 11:19             ` Arseniy Krasnov
2023-05-30 11:19             ` Arseniy Krasnov
2023-05-30 13:05             ` Miquel Raynal
2023-05-30 13:05               ` Miquel Raynal
2023-05-30 13:05               ` Miquel Raynal
2023-05-30 13:05               ` Miquel Raynal
2023-05-30 13:35               ` Arseniy Krasnov
2023-05-30 13:35                 ` Arseniy Krasnov
2023-05-30 13:35                 ` Arseniy Krasnov
2023-05-30 13:35                 ` Arseniy Krasnov
2023-05-30 13:58                 ` Miquel Raynal
2023-05-30 13:58                   ` Miquel Raynal
2023-05-30 13:58                   ` Miquel Raynal
2023-05-30 13:58                   ` Miquel Raynal
2023-05-15  9:44 ` [PATCH v4 2/5] mtd: rawnand: meson: move OOB to non-protected ECC area Arseniy Krasnov
2023-05-15  9:44   ` Arseniy Krasnov
2023-05-15  9:44   ` Arseniy Krasnov
2023-05-15  9:44   ` Arseniy Krasnov
2023-05-22 15:33   ` Miquel Raynal
2023-05-22 15:33     ` Miquel Raynal
2023-05-22 15:33     ` Miquel Raynal
2023-05-22 15:33     ` Miquel Raynal
2023-05-23 17:17     ` Arseniy Krasnov
2023-05-23 17:17       ` Arseniy Krasnov
2023-05-23 17:17       ` Arseniy Krasnov
2023-05-23 17:17       ` Arseniy Krasnov
2023-05-26 17:03       ` Miquel Raynal
2023-05-26 17:03         ` Miquel Raynal
2023-05-26 17:03         ` Miquel Raynal
2023-05-26 17:03         ` Miquel Raynal
2023-05-29 19:43         ` Arseniy Krasnov
2023-05-29 19:43           ` Arseniy Krasnov
2023-05-29 19:43           ` Arseniy Krasnov
2023-05-29 19:43           ` Arseniy Krasnov
2023-05-30  7:44           ` Miquel Raynal
2023-05-30  7:44             ` Miquel Raynal
2023-05-30  7:44             ` Miquel Raynal
2023-05-30  7:44             ` Miquel Raynal
2023-05-30  8:09             ` Arseniy Krasnov
2023-05-30  8:09               ` Arseniy Krasnov
2023-05-30  8:09               ` Arseniy Krasnov
2023-05-30  8:09               ` Arseniy Krasnov
2023-05-30  8:21               ` Miquel Raynal
2023-05-30  8:21                 ` Miquel Raynal
2023-05-30  8:21                 ` Miquel Raynal
2023-05-30  8:21                 ` Miquel Raynal
2023-05-30  8:28                 ` Arseniy Krasnov
2023-05-30  8:28                   ` Arseniy Krasnov
2023-05-30  8:28                   ` Arseniy Krasnov
2023-05-30  8:28                   ` Arseniy Krasnov
2023-05-15  9:44 ` [PATCH v4 3/5] mtd: rawnand: meson: always read whole OOB bytes Arseniy Krasnov
2023-05-15  9:44   ` Arseniy Krasnov
2023-05-15  9:44   ` Arseniy Krasnov
2023-05-15  9:44   ` Arseniy Krasnov
2023-05-22 15:38   ` Miquel Raynal
2023-05-22 15:38     ` Miquel Raynal
2023-05-22 15:38     ` Miquel Raynal
2023-05-22 15:38     ` Miquel Raynal
2023-05-23 17:27     ` Arseniy Krasnov
2023-05-23 17:27       ` Arseniy Krasnov
2023-05-23 17:27       ` Arseniy Krasnov
2023-05-23 17:27       ` Arseniy Krasnov
2023-05-26 17:09       ` Miquel Raynal
2023-05-26 17:09         ` Miquel Raynal
2023-05-26 17:09         ` Miquel Raynal
2023-05-26 17:09         ` Miquel Raynal
2023-05-29 19:46         ` Arseniy Krasnov
2023-05-29 19:46           ` Arseniy Krasnov
2023-05-29 19:46           ` Arseniy Krasnov
2023-05-29 19:46           ` Arseniy Krasnov
2023-05-15  9:44 ` [PATCH v4 4/5] mtd: rawnand: meson: check buffer length Arseniy Krasnov
2023-05-15  9:44   ` Arseniy Krasnov
2023-05-15  9:44   ` Arseniy Krasnov
2023-05-15  9:44   ` Arseniy Krasnov
2023-05-22 15:43   ` Miquel Raynal
2023-05-22 15:43     ` Miquel Raynal
2023-05-22 15:43     ` Miquel Raynal
2023-05-22 15:43     ` Miquel Raynal
2023-05-15  9:44 ` [PATCH v4 5/5] mtd: rawnand: meson: remove unneeded bitwise OR with zeroes Arseniy Krasnov
2023-05-15  9:44   ` Arseniy Krasnov
2023-05-15  9:44   ` Arseniy Krasnov
2023-05-15  9:44   ` Arseniy Krasnov

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