All the mail mirrored from lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4] mtd: nand: sunxi: fix OOB handling in ->write_xxx() functions
@ 2015-09-14  8:41 ` Boris Brezillon
  0 siblings, 0 replies; 14+ messages in thread
From: Boris Brezillon @ 2015-09-14  8:41 UTC (permalink / raw)
  To: David Woodhouse, Brian Norris, linux-mtd
  Cc: Maxime Ripard, linux-sunxi, linux-arm-kernel, Boris Brezillon,
	stable

The USER_DATA register cannot be accessed using byte accessors on A13
SoCs, thus triggering a bug when using memcpy_toio on this register.
Declare an helper macros to convert an OOB buffer into a suitable
USER_DATA value and vice-versa.

This patch also fixes an error in the oob_required logic (some OOB data
are not written even if the user required it) by removing the
oob_required condition, which is perfectly valid since the core already
fill ->oob_poi with FFs when oob_required is false.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Cc: <stable@vger.kernel.org> # 3.19+
Fixes: 1fef62c1423b ("mtd: nand: add sunxi NAND flash controller support")

---
Changes since v3:
- drop the NFC_USER_DATA_TO_BUF() macro

Changes since v2:
- add the NFC_USER_DATA_TO_BUF() macro and rename NFC_USER_DATA() into
  NFC_BUF_TO_USER_DATA()
- rework the commit message

Changes since v1:
- drop the !oob_required conditional path
- replace endianness conversions by a macro relying on byte shifting
  operations
---
 drivers/mtd/nand/sunxi_nand.c | 26 +++++++++-----------------
 1 file changed, 9 insertions(+), 17 deletions(-)

diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
index f97a58d..279cafd 100644
--- a/drivers/mtd/nand/sunxi_nand.c
+++ b/drivers/mtd/nand/sunxi_nand.c
@@ -147,6 +147,10 @@
 #define NFC_ECC_MODE		GENMASK(15, 12)
 #define NFC_RANDOM_SEED		GENMASK(30, 16)
 
+/* NFC_USER_DATA helper macros */
+#define NFC_BUF_TO_USER_DATA(buf)	((buf)[0] | ((buf)[1] << 8) | \
+					((buf)[2] << 16) | ((buf)[3] << 24))
+
 #define NFC_DEFAULT_TIMEOUT_MS	1000
 
 #define NFC_SRAM_SIZE		1024
@@ -646,15 +650,9 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
 		offset = layout->eccpos[i * ecc->bytes] - 4 + mtd->writesize;
 
 		/* Fill OOB data in */
-		if (oob_required) {
-			tmp = 0xffffffff;
-			memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp,
-				    4);
-		} else {
-			memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE,
-				    chip->oob_poi + offset - mtd->writesize,
-				    4);
-		}
+		writel(NFC_BUF_TO_USER_DATA(chip->oob_poi +
+					    layout->oobfree[i].offset),
+		       nfc->regs + NFC_REG_USER_DATA_BASE);
 
 		chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
 
@@ -784,14 +782,8 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
 		offset += ecc->size;
 
 		/* Fill OOB data in */
-		if (oob_required) {
-			tmp = 0xffffffff;
-			memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp,
-				    4);
-		} else {
-			memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, oob,
-				    4);
-		}
+		writel(NFC_BUF_TO_USER_DATA(oob),
+		       nfc->regs + NFC_REG_USER_DATA_BASE);
 
 		tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR |
 		      (1 << 30);
-- 
1.9.1

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

* [PATCH v4] mtd: nand: sunxi: fix OOB handling in ->write_xxx() functions
@ 2015-09-14  8:41 ` Boris Brezillon
  0 siblings, 0 replies; 14+ messages in thread
From: Boris Brezillon @ 2015-09-14  8:41 UTC (permalink / raw)
  To: linux-arm-kernel

The USER_DATA register cannot be accessed using byte accessors on A13
SoCs, thus triggering a bug when using memcpy_toio on this register.
Declare an helper macros to convert an OOB buffer into a suitable
USER_DATA value and vice-versa.

This patch also fixes an error in the oob_required logic (some OOB data
are not written even if the user required it) by removing the
oob_required condition, which is perfectly valid since the core already
fill ->oob_poi with FFs when oob_required is false.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Cc: <stable@vger.kernel.org> # 3.19+
Fixes: 1fef62c1423b ("mtd: nand: add sunxi NAND flash controller support")

---
Changes since v3:
- drop the NFC_USER_DATA_TO_BUF() macro

Changes since v2:
- add the NFC_USER_DATA_TO_BUF() macro and rename NFC_USER_DATA() into
  NFC_BUF_TO_USER_DATA()
- rework the commit message

Changes since v1:
- drop the !oob_required conditional path
- replace endianness conversions by a macro relying on byte shifting
  operations
---
 drivers/mtd/nand/sunxi_nand.c | 26 +++++++++-----------------
 1 file changed, 9 insertions(+), 17 deletions(-)

diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
index f97a58d..279cafd 100644
--- a/drivers/mtd/nand/sunxi_nand.c
+++ b/drivers/mtd/nand/sunxi_nand.c
@@ -147,6 +147,10 @@
 #define NFC_ECC_MODE		GENMASK(15, 12)
 #define NFC_RANDOM_SEED		GENMASK(30, 16)
 
+/* NFC_USER_DATA helper macros */
+#define NFC_BUF_TO_USER_DATA(buf)	((buf)[0] | ((buf)[1] << 8) | \
+					((buf)[2] << 16) | ((buf)[3] << 24))
+
 #define NFC_DEFAULT_TIMEOUT_MS	1000
 
 #define NFC_SRAM_SIZE		1024
@@ -646,15 +650,9 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
 		offset = layout->eccpos[i * ecc->bytes] - 4 + mtd->writesize;
 
 		/* Fill OOB data in */
-		if (oob_required) {
-			tmp = 0xffffffff;
-			memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp,
-				    4);
-		} else {
-			memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE,
-				    chip->oob_poi + offset - mtd->writesize,
-				    4);
-		}
+		writel(NFC_BUF_TO_USER_DATA(chip->oob_poi +
+					    layout->oobfree[i].offset),
+		       nfc->regs + NFC_REG_USER_DATA_BASE);
 
 		chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
 
@@ -784,14 +782,8 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
 		offset += ecc->size;
 
 		/* Fill OOB data in */
-		if (oob_required) {
-			tmp = 0xffffffff;
-			memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp,
-				    4);
-		} else {
-			memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, oob,
-				    4);
-		}
+		writel(NFC_BUF_TO_USER_DATA(oob),
+		       nfc->regs + NFC_REG_USER_DATA_BASE);
 
 		tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR |
 		      (1 << 30);
-- 
1.9.1

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

* Re: [PATCH v4] mtd: nand: sunxi: fix OOB handling in ->write_xxx() functions
  2015-09-14  8:41 ` Boris Brezillon
@ 2015-09-14  8:59   ` Arnd Bergmann
  -1 siblings, 0 replies; 14+ messages in thread
From: Arnd Bergmann @ 2015-09-14  8:59 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Boris Brezillon, David Woodhouse, Brian Norris, linux-mtd,
	Maxime Ripard, stable, linux-sunxi

On Monday 14 September 2015 10:41:03 Boris Brezillon wrote:
>                 /* Fill OOB data in */
> -               if (oob_required) {
> -                       tmp = 0xffffffff;
> -                       memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp,
> -                                   4);
> -               } else {
> -                       memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE,
> -                                   chip->oob_poi + offset - mtd->writesize,
> -                                   4);
> -               }
> +               writel(NFC_BUF_TO_USER_DATA(chip->oob_poi +
> +                                           layout->oobfree[i].offset),
> +                      nfc->regs + NFC_REG_USER_DATA_BASE);

This looks like you are changing the endianess of the data that gets written.
Is that intentional?

memcpy_toio() uses the same endianess for source and destination, while writel()
assumes that the destination is a little-endian register, and that could break
if the kernel is built to run as big-endian. I also see that sunxi_nfc_write_buf()
uses memcpy_toio() for writing the actual data, and you are not changing that.

If all hardware can do 32-bit accesses here and the size is guaranteed to be a
multiple of four bytes, you can probably improve performance by using a
__raw_writel() loop there. Using __raw_writel() in general is almost always
a bug, but here it actually makes sense. See also the powerpc implementation
of _memcpy_toio().

	Arnd

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

* [PATCH v4] mtd: nand: sunxi: fix OOB handling in ->write_xxx() functions
@ 2015-09-14  8:59   ` Arnd Bergmann
  0 siblings, 0 replies; 14+ messages in thread
From: Arnd Bergmann @ 2015-09-14  8:59 UTC (permalink / raw)
  To: linux-arm-kernel

On Monday 14 September 2015 10:41:03 Boris Brezillon wrote:
>                 /* Fill OOB data in */
> -               if (oob_required) {
> -                       tmp = 0xffffffff;
> -                       memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp,
> -                                   4);
> -               } else {
> -                       memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE,
> -                                   chip->oob_poi + offset - mtd->writesize,
> -                                   4);
> -               }
> +               writel(NFC_BUF_TO_USER_DATA(chip->oob_poi +
> +                                           layout->oobfree[i].offset),
> +                      nfc->regs + NFC_REG_USER_DATA_BASE);

This looks like you are changing the endianess of the data that gets written.
Is that intentional?

memcpy_toio() uses the same endianess for source and destination, while writel()
assumes that the destination is a little-endian register, and that could break
if the kernel is built to run as big-endian. I also see that sunxi_nfc_write_buf()
uses memcpy_toio() for writing the actual data, and you are not changing that.

If all hardware can do 32-bit accesses here and the size is guaranteed to be a
multiple of four bytes, you can probably improve performance by using a
__raw_writel() loop there. Using __raw_writel() in general is almost always
a bug, but here it actually makes sense. See also the powerpc implementation
of _memcpy_toio().

	Arnd

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

* Re: [PATCH v4] mtd: nand: sunxi: fix OOB handling in ->write_xxx() functions
  2015-09-14  8:59   ` Arnd Bergmann
@ 2015-09-14  9:41     ` Boris Brezillon
  -1 siblings, 0 replies; 14+ messages in thread
From: Boris Brezillon @ 2015-09-14  9:41 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-arm-kernel, David Woodhouse, Brian Norris, linux-mtd,
	Maxime Ripard, stable, linux-sunxi

Hi Arnd,

On Mon, 14 Sep 2015 10:59:02 +0200
Arnd Bergmann <arnd@arndb.de> wrote:

> On Monday 14 September 2015 10:41:03 Boris Brezillon wrote:
> >                 /* Fill OOB data in */
> > -               if (oob_required) {
> > -                       tmp = 0xffffffff;
> > -                       memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp,
> > -                                   4);
> > -               } else {
> > -                       memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE,
> > -                                   chip->oob_poi + offset - mtd->writesize,
> > -                                   4);
> > -               }
> > +               writel(NFC_BUF_TO_USER_DATA(chip->oob_poi +
> > +                                           layout->oobfree[i].offset),
> > +                      nfc->regs + NFC_REG_USER_DATA_BASE);
> 
> This looks like you are changing the endianess of the data that gets written.
> Is that intentional?

Hm, the real goal of this patch was to avoid accessing the
NFC_REG_USER_DATA_BASE register using byte accessors (writeb()).
The first version of this series was directly copying data from the
buffer into a temporary u32 variable, thus forcing the data to be stored
in little endian (tell me if I'm wrong), and then changing endianness
using le32_to_cpu().
Brian suggested to use __raw_writel() (as you seem to suggest too), but
I was worried about the missing mem barrier in this function.
That's why I made my own macro doing the little endian to CPU conversion
manually, but still using the standard writel() accessor (which will
do the conversion in reverse order).
Maybe I should use __raw_writel() and add an explicit memory barrier.

> 
> memcpy_toio() uses the same endianess for source and destination, while writel()
> assumes that the destination is a little-endian register, and that could break
> if the kernel is built to run as big-endian. I also see that sunxi_nfc_write_buf()
> uses memcpy_toio() for writing the actual data, and you are not changing that.

AFAIU the peripheral is always in little endian, and only the CPU can
be switched to big endian, right?
Are you saying that memcpy_toio() uses writel? Because according to
this implementation [2] it uses writeb, which should be safe (accessing
the internal SRAM using byte accessors is authorized).

> 
> If all hardware can do 32-bit accesses here and the size is guaranteed to be a
> multiple of four bytes, you can probably improve performance by using a
> __raw_writel() loop there. Using __raw_writel() in general is almost always
> a bug, but here it actually makes sense. See also the powerpc implementation
> of _memcpy_toio().

AFAICT, buffer passed to ->write_bu() are not necessarily aligned on
32bits, so using writel here might require copying data in temporary
buffers :-/.

Don't hesitate to point where I'm wrong ;-).

Best Regards,

Boris

[1]https://patchwork.ozlabs.org/patch/502041/
[2]http://lxr.free-electrons.com/source/arch/arm/kernel/io.c#L56


-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

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

* [PATCH v4] mtd: nand: sunxi: fix OOB handling in ->write_xxx() functions
@ 2015-09-14  9:41     ` Boris Brezillon
  0 siblings, 0 replies; 14+ messages in thread
From: Boris Brezillon @ 2015-09-14  9:41 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Arnd,

On Mon, 14 Sep 2015 10:59:02 +0200
Arnd Bergmann <arnd@arndb.de> wrote:

> On Monday 14 September 2015 10:41:03 Boris Brezillon wrote:
> >                 /* Fill OOB data in */
> > -               if (oob_required) {
> > -                       tmp = 0xffffffff;
> > -                       memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp,
> > -                                   4);
> > -               } else {
> > -                       memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE,
> > -                                   chip->oob_poi + offset - mtd->writesize,
> > -                                   4);
> > -               }
> > +               writel(NFC_BUF_TO_USER_DATA(chip->oob_poi +
> > +                                           layout->oobfree[i].offset),
> > +                      nfc->regs + NFC_REG_USER_DATA_BASE);
> 
> This looks like you are changing the endianess of the data that gets written.
> Is that intentional?

Hm, the real goal of this patch was to avoid accessing the
NFC_REG_USER_DATA_BASE register using byte accessors (writeb()).
The first version of this series was directly copying data from the
buffer into a temporary u32 variable, thus forcing the data to be stored
in little endian (tell me if I'm wrong), and then changing endianness
using le32_to_cpu().
Brian suggested to use __raw_writel() (as you seem to suggest too), but
I was worried about the missing mem barrier in this function.
That's why I made my own macro doing the little endian to CPU conversion
manually, but still using the standard writel() accessor (which will
do the conversion in reverse order).
Maybe I should use __raw_writel() and add an explicit memory barrier.

> 
> memcpy_toio() uses the same endianess for source and destination, while writel()
> assumes that the destination is a little-endian register, and that could break
> if the kernel is built to run as big-endian. I also see that sunxi_nfc_write_buf()
> uses memcpy_toio() for writing the actual data, and you are not changing that.

AFAIU the peripheral is always in little endian, and only the CPU can
be switched to big endian, right?
Are you saying that memcpy_toio() uses writel? Because according to
this implementation [2] it uses writeb, which should be safe (accessing
the internal SRAM using byte accessors is authorized).

> 
> If all hardware can do 32-bit accesses here and the size is guaranteed to be a
> multiple of four bytes, you can probably improve performance by using a
> __raw_writel() loop there. Using __raw_writel() in general is almost always
> a bug, but here it actually makes sense. See also the powerpc implementation
> of _memcpy_toio().

AFAICT, buffer passed to ->write_bu() are not necessarily aligned on
32bits, so using writel here might require copying data in temporary
buffers :-/.

Don't hesitate to point where I'm wrong ;-).

Best Regards,

Boris

[1]https://patchwork.ozlabs.org/patch/502041/
[2]http://lxr.free-electrons.com/source/arch/arm/kernel/io.c#L56


-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

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

* Re: [PATCH v4] mtd: nand: sunxi: fix OOB handling in ->write_xxx() functions
  2015-09-14  9:41     ` Boris Brezillon
@ 2015-09-14 11:49       ` Arnd Bergmann
  -1 siblings, 0 replies; 14+ messages in thread
From: Arnd Bergmann @ 2015-09-14 11:49 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: linux-arm-kernel, David Woodhouse, Brian Norris, linux-mtd,
	Maxime Ripard, stable, linux-sunxi

On Monday 14 September 2015 11:41:13 Boris Brezillon wrote:
> Hi Arnd,
> 
> On Mon, 14 Sep 2015 10:59:02 +0200
> Arnd Bergmann <arnd@arndb.de> wrote:
> 
> > On Monday 14 September 2015 10:41:03 Boris Brezillon wrote:
> > >                 /* Fill OOB data in */
> > > -               if (oob_required) {
> > > -                       tmp = 0xffffffff;
> > > -                       memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp,
> > > -                                   4);
> > > -               } else {
> > > -                       memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE,
> > > -                                   chip->oob_poi + offset - mtd->writesize,
> > > -                                   4);
> > > -               }
> > > +               writel(NFC_BUF_TO_USER_DATA(chip->oob_poi +
> > > +                                           layout->oobfree[i].offset),
> > > +                      nfc->regs + NFC_REG_USER_DATA_BASE);
> > 
> > This looks like you are changing the endianess of the data that gets written.
> > Is that intentional?
> 
> Hm, the real goal of this patch was to avoid accessing the
> NFC_REG_USER_DATA_BASE register using byte accessors (writeb()).
> The first version of this series was directly copying data from the
> buffer into a temporary u32 variable, thus forcing the data to be stored
> in little endian (tell me if I'm wrong), and then changing endianness
> using le32_to_cpu().
> Brian suggested to use __raw_writel() (as you seem to suggest too), but
> I was worried about the missing mem barrier in this function.
> That's why I made my own macro doing the little endian to CPU conversion
> manually, but still using the standard writel() accessor (which will
> do the conversion in reverse order).

D'oh, I totally missed your open-coded le32_to_cpu macro.
So your code does look correct to me, it's just a little inefficient
on big-endian machines because you end up swapping twice.

> Maybe I should use __raw_writel() and add an explicit memory barrier.

That would work, and avoid the double swapping, yes. Or you could
use writesl() with a length of one, which should have all the
necessary barriers but no byteswap.

I don't think you need a barrier on ARM here (no DMA that can interfere),
but it's better write the code architecture independent (as you did
above).

The memcpy_toio() is definitely overkill as it has a barrier after every
single byte, where you need at most one for this kind of driver.

> > memcpy_toio() uses the same endianess for source and destination, while writel()
> > assumes that the destination is a little-endian register, and that could break
> > if the kernel is built to run as big-endian. I also see that sunxi_nfc_write_buf()
> > uses memcpy_toio() for writing the actual data, and you are not changing that.
> 
> AFAIU the peripheral is always in little endian, and only the CPU can
> be switched to big endian, right?

Correct.

> Are you saying that memcpy_toio() uses writel? Because according to
> this implementation [2] it uses writeb, which should be safe (accessing
> the internal SRAM using byte accessors is authorized).

No, what I meant is that you are replacing some memcpy_toio() with
writel(), but don't replace some of the others that should/could also
be replaced.

As mentioned, I was under the impression that you changed the endianess
for the OOB data but did not change the endianess for the user data,
which would be inconsistent.

> > If all hardware can do 32-bit accesses here and the size is guaranteed to be a
> > multiple of four bytes, you can probably improve performance by using a
> > __raw_writel() loop there. Using __raw_writel() in general is almost always
> > a bug, but here it actually makes sense. See also the powerpc implementation
> > of _memcpy_toio().
> 
> AFAICT, buffer passed to ->write_bu() are not necessarily aligned on
> 32bits, so using writel here might require copying data in temporary
> buffers :-/.
> 
> Don't hesitate to point where I'm wrong ;-).

Brian or Dwmw2 should be able to know for sure. I think it's definitely
worth trying as the potential performance gains could be huge, if you
replace

	for (p = start; p < start + length; data++, p++) {
		writeb(*data, p);
		wmb();
	}

with

	for (p = start; p < start + length; data++, p+=4) {
		writel(*data, p);
	};
	wmb();

	Arnd

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

* [PATCH v4] mtd: nand: sunxi: fix OOB handling in ->write_xxx() functions
@ 2015-09-14 11:49       ` Arnd Bergmann
  0 siblings, 0 replies; 14+ messages in thread
From: Arnd Bergmann @ 2015-09-14 11:49 UTC (permalink / raw)
  To: linux-arm-kernel

On Monday 14 September 2015 11:41:13 Boris Brezillon wrote:
> Hi Arnd,
> 
> On Mon, 14 Sep 2015 10:59:02 +0200
> Arnd Bergmann <arnd@arndb.de> wrote:
> 
> > On Monday 14 September 2015 10:41:03 Boris Brezillon wrote:
> > >                 /* Fill OOB data in */
> > > -               if (oob_required) {
> > > -                       tmp = 0xffffffff;
> > > -                       memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp,
> > > -                                   4);
> > > -               } else {
> > > -                       memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE,
> > > -                                   chip->oob_poi + offset - mtd->writesize,
> > > -                                   4);
> > > -               }
> > > +               writel(NFC_BUF_TO_USER_DATA(chip->oob_poi +
> > > +                                           layout->oobfree[i].offset),
> > > +                      nfc->regs + NFC_REG_USER_DATA_BASE);
> > 
> > This looks like you are changing the endianess of the data that gets written.
> > Is that intentional?
> 
> Hm, the real goal of this patch was to avoid accessing the
> NFC_REG_USER_DATA_BASE register using byte accessors (writeb()).
> The first version of this series was directly copying data from the
> buffer into a temporary u32 variable, thus forcing the data to be stored
> in little endian (tell me if I'm wrong), and then changing endianness
> using le32_to_cpu().
> Brian suggested to use __raw_writel() (as you seem to suggest too), but
> I was worried about the missing mem barrier in this function.
> That's why I made my own macro doing the little endian to CPU conversion
> manually, but still using the standard writel() accessor (which will
> do the conversion in reverse order).

D'oh, I totally missed your open-coded le32_to_cpu macro.
So your code does look correct to me, it's just a little inefficient
on big-endian machines because you end up swapping twice.

> Maybe I should use __raw_writel() and add an explicit memory barrier.

That would work, and avoid the double swapping, yes. Or you could
use writesl() with a length of one, which should have all the
necessary barriers but no byteswap.

I don't think you need a barrier on ARM here (no DMA that can interfere),
but it's better write the code architecture independent (as you did
above).

The memcpy_toio() is definitely overkill as it has a barrier after every
single byte, where you need at most one for this kind of driver.

> > memcpy_toio() uses the same endianess for source and destination, while writel()
> > assumes that the destination is a little-endian register, and that could break
> > if the kernel is built to run as big-endian. I also see that sunxi_nfc_write_buf()
> > uses memcpy_toio() for writing the actual data, and you are not changing that.
> 
> AFAIU the peripheral is always in little endian, and only the CPU can
> be switched to big endian, right?

Correct.

> Are you saying that memcpy_toio() uses writel? Because according to
> this implementation [2] it uses writeb, which should be safe (accessing
> the internal SRAM using byte accessors is authorized).

No, what I meant is that you are replacing some memcpy_toio() with
writel(), but don't replace some of the others that should/could also
be replaced.

As mentioned, I was under the impression that you changed the endianess
for the OOB data but did not change the endianess for the user data,
which would be inconsistent.

> > If all hardware can do 32-bit accesses here and the size is guaranteed to be a
> > multiple of four bytes, you can probably improve performance by using a
> > __raw_writel() loop there. Using __raw_writel() in general is almost always
> > a bug, but here it actually makes sense. See also the powerpc implementation
> > of _memcpy_toio().
> 
> AFAICT, buffer passed to ->write_bu() are not necessarily aligned on
> 32bits, so using writel here might require copying data in temporary
> buffers :-/.
> 
> Don't hesitate to point where I'm wrong ;-).

Brian or Dwmw2 should be able to know for sure. I think it's definitely
worth trying as the potential performance gains could be huge, if you
replace

	for (p = start; p < start + length; data++, p++) {
		writeb(*data, p);
		wmb();
	}

with

	for (p = start; p < start + length; data++, p+=4) {
		writel(*data, p);
	};
	wmb();

	Arnd

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

* Re: [PATCH v4] mtd: nand: sunxi: fix OOB handling in ->write_xxx() functions
  2015-09-14 11:49       ` Arnd Bergmann
@ 2015-09-14 12:36         ` Arnd Bergmann
  -1 siblings, 0 replies; 14+ messages in thread
From: Arnd Bergmann @ 2015-09-14 12:36 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Boris Brezillon, stable, linux-sunxi, linux-mtd, Maxime Ripard,
	Brian Norris, David Woodhouse

On Monday 14 September 2015 13:49:12 Arnd Bergmann wrote:
> > > If all hardware can do 32-bit accesses here and the size is guaranteed to be a
> > > multiple of four bytes, you can probably improve performance by using a
> > > __raw_writel() loop there. Using __raw_writel() in general is almost always
> > > a bug, but here it actually makes sense. See also the powerpc implementation
> > > of _memcpy_toio().
> > 
> > AFAICT, buffer passed to ->write_bu() are not necessarily aligned on
> > 32bits, so using writel here might require copying data in temporary
> > buffers :-/.
> > 
> > Don't hesitate to point where I'm wrong ;-).
> 
> Brian or Dwmw2 should be able to know for sure. I think it's definitely
> worth trying as the potential performance gains could be huge, if you
> replace
> 
>         for (p = start; p < start + length; data++, p++) {
>                 writeb(*data, p);
>                 wmb();
>         }
> 
> with
> 
>         for (p = start; p < start + length; data++, p+=4) {
>                 writel(*data, p);
>         };
>         wmb();
> 

As Boris pointed out on IRC, we have an optimized version of
memcpy_toio on little-endian, which already does this. I'm not completely
sure why we don't use it for big-endian architectures as well.

Powerpc uses the same method on big-endian, but it's possible that
it does not do the right thing on one of the older platforms using
BE32 mode, or one that has a weird bus mode.

	Arnd
 

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

* [PATCH v4] mtd: nand: sunxi: fix OOB handling in ->write_xxx() functions
@ 2015-09-14 12:36         ` Arnd Bergmann
  0 siblings, 0 replies; 14+ messages in thread
From: Arnd Bergmann @ 2015-09-14 12:36 UTC (permalink / raw)
  To: linux-arm-kernel

On Monday 14 September 2015 13:49:12 Arnd Bergmann wrote:
> > > If all hardware can do 32-bit accesses here and the size is guaranteed to be a
> > > multiple of four bytes, you can probably improve performance by using a
> > > __raw_writel() loop there. Using __raw_writel() in general is almost always
> > > a bug, but here it actually makes sense. See also the powerpc implementation
> > > of _memcpy_toio().
> > 
> > AFAICT, buffer passed to ->write_bu() are not necessarily aligned on
> > 32bits, so using writel here might require copying data in temporary
> > buffers :-/.
> > 
> > Don't hesitate to point where I'm wrong ;-).
> 
> Brian or Dwmw2 should be able to know for sure. I think it's definitely
> worth trying as the potential performance gains could be huge, if you
> replace
> 
>         for (p = start; p < start + length; data++, p++) {
>                 writeb(*data, p);
>                 wmb();
>         }
> 
> with
> 
>         for (p = start; p < start + length; data++, p+=4) {
>                 writel(*data, p);
>         };
>         wmb();
> 

As Boris pointed out on IRC, we have an optimized version of
memcpy_toio on little-endian, which already does this. I'm not completely
sure why we don't use it for big-endian architectures as well.

Powerpc uses the same method on big-endian, but it's possible that
it does not do the right thing on one of the older platforms using
BE32 mode, or one that has a weird bus mode.

	Arnd
 

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

* Re: [PATCH v4] mtd: nand: sunxi: fix OOB handling in ->write_xxx() functions
  2015-09-14  8:41 ` Boris Brezillon
@ 2015-09-14 17:02   ` Brian Norris
  -1 siblings, 0 replies; 14+ messages in thread
From: Brian Norris @ 2015-09-14 17:02 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: David Woodhouse, linux-mtd, Maxime Ripard, linux-sunxi,
	linux-arm-kernel, stable

On Mon, Sep 14, 2015 at 10:41:03AM +0200, Boris Brezillon wrote:
> The USER_DATA register cannot be accessed using byte accessors on A13
> SoCs, thus triggering a bug when using memcpy_toio on this register.
> Declare an helper macros to convert an OOB buffer into a suitable
> USER_DATA value and vice-versa.
> 
> This patch also fixes an error in the oob_required logic (some OOB data
> are not written even if the user required it) by removing the
> oob_required condition, which is perfectly valid since the core already
> fill ->oob_poi with FFs when oob_required is false.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> Cc: <stable@vger.kernel.org> # 3.19+
> Fixes: 1fef62c1423b ("mtd: nand: add sunxi NAND flash controller support")
> 
> ---
> Changes since v3:
> - drop the NFC_USER_DATA_TO_BUF() macro

I don't have any real objections to this version, and I think some IRC
discussion helped clear up a few questions. I'll take this for 4.3 soon,
unless I see any objections.

> Changes since v2:
> - add the NFC_USER_DATA_TO_BUF() macro and rename NFC_USER_DATA() into
>   NFC_BUF_TO_USER_DATA()
> - rework the commit message
> 
> Changes since v1:
> - drop the !oob_required conditional path
> - replace endianness conversions by a macro relying on byte shifting
>   operations
> ---
>  drivers/mtd/nand/sunxi_nand.c | 26 +++++++++-----------------
>  1 file changed, 9 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
> index f97a58d..279cafd 100644
> --- a/drivers/mtd/nand/sunxi_nand.c
> +++ b/drivers/mtd/nand/sunxi_nand.c
> @@ -147,6 +147,10 @@
>  #define NFC_ECC_MODE		GENMASK(15, 12)
>  #define NFC_RANDOM_SEED		GENMASK(30, 16)
>  
> +/* NFC_USER_DATA helper macros */
> +#define NFC_BUF_TO_USER_DATA(buf)	((buf)[0] | ((buf)[1] << 8) | \
> +					((buf)[2] << 16) | ((buf)[3] << 24))
> +
>  #define NFC_DEFAULT_TIMEOUT_MS	1000
>  
>  #define NFC_SRAM_SIZE		1024
> @@ -646,15 +650,9 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
>  		offset = layout->eccpos[i * ecc->bytes] - 4 + mtd->writesize;
>  
>  		/* Fill OOB data in */
> -		if (oob_required) {
> -			tmp = 0xffffffff;
> -			memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp,
> -				    4);
> -		} else {
> -			memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE,
> -				    chip->oob_poi + offset - mtd->writesize,
> -				    4);
> -		}
> +		writel(NFC_BUF_TO_USER_DATA(chip->oob_poi +
> +					    layout->oobfree[i].offset),
> +		       nfc->regs + NFC_REG_USER_DATA_BASE);

I believe the few remaining questionable points were:

 * you don't actually need to add the barrier, thus __raw_writel() would
   suffice
 * you're still doing a double swap for the (likely theoretical) big
   endian case

Neither point matters much to me, so as noted above, LGTM.

>  
>  		chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
>  
> @@ -784,14 +782,8 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
>  		offset += ecc->size;
>  
>  		/* Fill OOB data in */
> -		if (oob_required) {
> -			tmp = 0xffffffff;
> -			memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp,
> -				    4);
> -		} else {
> -			memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, oob,
> -				    4);
> -		}
> +		writel(NFC_BUF_TO_USER_DATA(oob),
> +		       nfc->regs + NFC_REG_USER_DATA_BASE);

Same here.

>  
>  		tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR |
>  		      (1 << 30);

Brian

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

* [PATCH v4] mtd: nand: sunxi: fix OOB handling in ->write_xxx() functions
@ 2015-09-14 17:02   ` Brian Norris
  0 siblings, 0 replies; 14+ messages in thread
From: Brian Norris @ 2015-09-14 17:02 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Sep 14, 2015 at 10:41:03AM +0200, Boris Brezillon wrote:
> The USER_DATA register cannot be accessed using byte accessors on A13
> SoCs, thus triggering a bug when using memcpy_toio on this register.
> Declare an helper macros to convert an OOB buffer into a suitable
> USER_DATA value and vice-versa.
> 
> This patch also fixes an error in the oob_required logic (some OOB data
> are not written even if the user required it) by removing the
> oob_required condition, which is perfectly valid since the core already
> fill ->oob_poi with FFs when oob_required is false.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> Cc: <stable@vger.kernel.org> # 3.19+
> Fixes: 1fef62c1423b ("mtd: nand: add sunxi NAND flash controller support")
> 
> ---
> Changes since v3:
> - drop the NFC_USER_DATA_TO_BUF() macro

I don't have any real objections to this version, and I think some IRC
discussion helped clear up a few questions. I'll take this for 4.3 soon,
unless I see any objections.

> Changes since v2:
> - add the NFC_USER_DATA_TO_BUF() macro and rename NFC_USER_DATA() into
>   NFC_BUF_TO_USER_DATA()
> - rework the commit message
> 
> Changes since v1:
> - drop the !oob_required conditional path
> - replace endianness conversions by a macro relying on byte shifting
>   operations
> ---
>  drivers/mtd/nand/sunxi_nand.c | 26 +++++++++-----------------
>  1 file changed, 9 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
> index f97a58d..279cafd 100644
> --- a/drivers/mtd/nand/sunxi_nand.c
> +++ b/drivers/mtd/nand/sunxi_nand.c
> @@ -147,6 +147,10 @@
>  #define NFC_ECC_MODE		GENMASK(15, 12)
>  #define NFC_RANDOM_SEED		GENMASK(30, 16)
>  
> +/* NFC_USER_DATA helper macros */
> +#define NFC_BUF_TO_USER_DATA(buf)	((buf)[0] | ((buf)[1] << 8) | \
> +					((buf)[2] << 16) | ((buf)[3] << 24))
> +
>  #define NFC_DEFAULT_TIMEOUT_MS	1000
>  
>  #define NFC_SRAM_SIZE		1024
> @@ -646,15 +650,9 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
>  		offset = layout->eccpos[i * ecc->bytes] - 4 + mtd->writesize;
>  
>  		/* Fill OOB data in */
> -		if (oob_required) {
> -			tmp = 0xffffffff;
> -			memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp,
> -				    4);
> -		} else {
> -			memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE,
> -				    chip->oob_poi + offset - mtd->writesize,
> -				    4);
> -		}
> +		writel(NFC_BUF_TO_USER_DATA(chip->oob_poi +
> +					    layout->oobfree[i].offset),
> +		       nfc->regs + NFC_REG_USER_DATA_BASE);

I believe the few remaining questionable points were:

 * you don't actually need to add the barrier, thus __raw_writel() would
   suffice
 * you're still doing a double swap for the (likely theoretical) big
   endian case

Neither point matters much to me, so as noted above, LGTM.

>  
>  		chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
>  
> @@ -784,14 +782,8 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
>  		offset += ecc->size;
>  
>  		/* Fill OOB data in */
> -		if (oob_required) {
> -			tmp = 0xffffffff;
> -			memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp,
> -				    4);
> -		} else {
> -			memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, oob,
> -				    4);
> -		}
> +		writel(NFC_BUF_TO_USER_DATA(oob),
> +		       nfc->regs + NFC_REG_USER_DATA_BASE);

Same here.

>  
>  		tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR |
>  		      (1 << 30);

Brian

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

* Re: [PATCH v4] mtd: nand: sunxi: fix OOB handling in ->write_xxx() functions
  2015-09-14 17:02   ` Brian Norris
@ 2015-09-21 20:43     ` Brian Norris
  -1 siblings, 0 replies; 14+ messages in thread
From: Brian Norris @ 2015-09-21 20:43 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: David Woodhouse, linux-mtd, Maxime Ripard, linux-sunxi,
	linux-arm-kernel, stable

On Mon, Sep 14, 2015 at 10:02:04AM -0700, Brian Norris wrote:
> On Mon, Sep 14, 2015 at 10:41:03AM +0200, Boris Brezillon wrote:
> > The USER_DATA register cannot be accessed using byte accessors on A13
> > SoCs, thus triggering a bug when using memcpy_toio on this register.
> > Declare an helper macros to convert an OOB buffer into a suitable
> > USER_DATA value and vice-versa.
> > 
> > This patch also fixes an error in the oob_required logic (some OOB data
> > are not written even if the user required it) by removing the
> > oob_required condition, which is perfectly valid since the core already
> > fill ->oob_poi with FFs when oob_required is false.
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> > Cc: <stable@vger.kernel.org> # 3.19+
> > Fixes: 1fef62c1423b ("mtd: nand: add sunxi NAND flash controller support")
> > 
> > ---
> > Changes since v3:
> > - drop the NFC_USER_DATA_TO_BUF() macro
> 
> I don't have any real objections to this version, and I think some IRC
> discussion helped clear up a few questions. I'll take this for 4.3 soon,
> unless I see any objections.

Pushed to linux-mtd.git.

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

* [PATCH v4] mtd: nand: sunxi: fix OOB handling in ->write_xxx() functions
@ 2015-09-21 20:43     ` Brian Norris
  0 siblings, 0 replies; 14+ messages in thread
From: Brian Norris @ 2015-09-21 20:43 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Sep 14, 2015 at 10:02:04AM -0700, Brian Norris wrote:
> On Mon, Sep 14, 2015 at 10:41:03AM +0200, Boris Brezillon wrote:
> > The USER_DATA register cannot be accessed using byte accessors on A13
> > SoCs, thus triggering a bug when using memcpy_toio on this register.
> > Declare an helper macros to convert an OOB buffer into a suitable
> > USER_DATA value and vice-versa.
> > 
> > This patch also fixes an error in the oob_required logic (some OOB data
> > are not written even if the user required it) by removing the
> > oob_required condition, which is perfectly valid since the core already
> > fill ->oob_poi with FFs when oob_required is false.
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> > Cc: <stable@vger.kernel.org> # 3.19+
> > Fixes: 1fef62c1423b ("mtd: nand: add sunxi NAND flash controller support")
> > 
> > ---
> > Changes since v3:
> > - drop the NFC_USER_DATA_TO_BUF() macro
> 
> I don't have any real objections to this version, and I think some IRC
> discussion helped clear up a few questions. I'll take this for 4.3 soon,
> unless I see any objections.

Pushed to linux-mtd.git.

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

end of thread, other threads:[~2015-09-21 20:43 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-09-14  8:41 [PATCH v4] mtd: nand: sunxi: fix OOB handling in ->write_xxx() functions Boris Brezillon
2015-09-14  8:41 ` Boris Brezillon
2015-09-14  8:59 ` Arnd Bergmann
2015-09-14  8:59   ` Arnd Bergmann
2015-09-14  9:41   ` Boris Brezillon
2015-09-14  9:41     ` Boris Brezillon
2015-09-14 11:49     ` Arnd Bergmann
2015-09-14 11:49       ` Arnd Bergmann
2015-09-14 12:36       ` Arnd Bergmann
2015-09-14 12:36         ` Arnd Bergmann
2015-09-14 17:02 ` Brian Norris
2015-09-14 17:02   ` Brian Norris
2015-09-21 20:43   ` Brian Norris
2015-09-21 20:43     ` Brian Norris

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.