diff mbox

[U-Boot,v2,09/16] mtd/nand/tegra: alignment workaround

Message ID a948adcd9699f934734603c8ffc25ff6b4c9997d.1437426110.git.marcel.ziswiler@toradex.com
State Superseded
Delegated to: Tom Warren
Headers show

Commit Message

Marcel Ziswiler July 20, 2015, 10:35 p.m. UTC
From: Marcel Ziswiler <marcel.ziswiler@toradex.com>

Integrate cache alignment bounce buffer to workaround issues as follows:

Loading file '/boot/zImage' to addr 0x01000000 with size 4499152 (0x0044a6d0)...
ERROR: v7_dcache_inval_range - start address is not aligned - 0x1f7f0108
ERROR: v7_dcache_inval_range - stop address is not aligned - 0x1f7f1108
Done
Kernel image @ 0x1000000 [ 0x000000 - 0x44a6d0 ]

Starting kernel ...

undefined instruction
pc : [<005ff03c>]          lr : [<0000800c>]
sp : 0144b6e8  ip : 01000188     fp : 0144a6c8
r10: 00000000  r9 : 411fc090     r8 : 00000100
r7 : 00000cfb  r6 : 0144a6d0     r5 : 00000000  r4 : 00008000
r3 : 0000000c  r2 : 00000100     r1 : 00000cfb  r0 : 00000000
Flags: nZCv  IRQs off  FIQs off  Mode SVC_32
Resetting CPU ...

Signed-off-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
---
Changes in v2:
Migrated to using generic bounce buffer implementation as suggested by Simon.

 drivers/mtd/nand/tegra_nand.c | 83 +++++++++++++++++--------------------------
 1 file changed, 32 insertions(+), 51 deletions(-)

Comments

Scott Wood July 27, 2015, 7:57 p.m. UTC | #1
On Tue, 2015-07-21 at 00:35 +0200, Marcel Ziswiler wrote:
> From: Marcel Ziswiler <marcel.ziswiler@toradex.com>
> 
> Integrate cache alignment bounce buffer to workaround issues as follows:
> 
> Loading file '/boot/zImage' to addr 0x01000000 with size 4499152 
> (0x0044a6d0)...
> ERROR: v7_dcache_inval_range - start address is not aligned - 0x1f7f0108
> ERROR: v7_dcache_inval_range - stop address is not aligned - 0x1f7f1108
> Done
> Kernel image @ 0x1000000 [ 0x000000 - 0x44a6d0 ]
> 
> Starting kernel ...
> 
> undefined instruction
> pc : [<005ff03c>]          lr : [<0000800c>]
> sp : 0144b6e8  ip : 01000188     fp : 0144a6c8
> r10: 00000000  r9 : 411fc090     r8 : 00000100
> r7 : 00000cfb  r6 : 0144a6d0     r5 : 00000000  r4 : 00008000
> r3 : 0000000c  r2 : 00000100     r1 : 00000cfb  r0 : 00000000
> Flags: nZCv  IRQs off  FIQs off  Mode SVC_32
> Resetting CPU ...
> 
> Signed-off-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
> ---
> Changes in v2:
> Migrated to using generic bounce buffer implementation as suggested by 
> Simon.

I didn't see Simon's suggestion, but what is the general policy in U-Boot 
supposed to be for I/O alignment?  Responsibility of the API user, or of the 
driver?  Are there other drivers with the same problem?

> -     writel(virt_to_phys(buf), &info->reg->data_block_ptr);
> +     writel((u32)bbstate.bounce_buffer, &info->reg->data_block_ptr);

Why are you converting usage of virt_to_phys() into a u32 cast?

-Scott
Simon Glass July 27, 2015, 11:28 p.m. UTC | #2
Hi Scott,

On 27 July 2015 at 13:57, Scott Wood <scottwood@freescale.com> wrote:
> On Tue, 2015-07-21 at 00:35 +0200, Marcel Ziswiler wrote:
>> From: Marcel Ziswiler <marcel.ziswiler@toradex.com>
>>
>> Integrate cache alignment bounce buffer to workaround issues as follows:
>>
>> Loading file '/boot/zImage' to addr 0x01000000 with size 4499152
>> (0x0044a6d0)...
>> ERROR: v7_dcache_inval_range - start address is not aligned - 0x1f7f0108
>> ERROR: v7_dcache_inval_range - stop address is not aligned - 0x1f7f1108
>> Done
>> Kernel image @ 0x1000000 [ 0x000000 - 0x44a6d0 ]
>>
>> Starting kernel ...
>>
>> undefined instruction
>> pc : [<005ff03c>]          lr : [<0000800c>]
>> sp : 0144b6e8  ip : 01000188     fp : 0144a6c8
>> r10: 00000000  r9 : 411fc090     r8 : 00000100
>> r7 : 00000cfb  r6 : 0144a6d0     r5 : 00000000  r4 : 00008000
>> r3 : 0000000c  r2 : 00000100     r1 : 00000cfb  r0 : 00000000
>> Flags: nZCv  IRQs off  FIQs off  Mode SVC_32
>> Resetting CPU ...
>>
>> Signed-off-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
>> ---
>> Changes in v2:
>> Migrated to using generic bounce buffer implementation as suggested by
>> Simon.
>
> I didn't see Simon's suggestion, but what is the general policy in U-Boot
> supposed to be for I/O alignment?  Responsibility of the API user, or of the
> driver?  Are there other drivers with the same problem?

MMC has it. I feel that in general we should be able to ask the
higher-level systems to do the right thing and align their data.
Otherwise it introduces inefficiencies into the stack. But at least if
we do introduce this, we should use common infrastructure, hence my
request.

>
>> -     writel(virt_to_phys(buf), &info->reg->data_block_ptr);
>> +     writel((u32)bbstate.bounce_buffer, &info->reg->data_block_ptr);
>
> Why are you converting usage of virt_to_phys() into a u32 cast?
>
> -Scott
>

Regards,
Simon
Scott Wood July 27, 2015, 11:32 p.m. UTC | #3
On Mon, 2015-07-27 at 17:28 -0600, Simon Glass wrote:
> Hi Scott,
> 
> On 27 July 2015 at 13:57, Scott Wood <scottwood@freescale.com> wrote:
> > On Tue, 2015-07-21 at 00:35 +0200, Marcel Ziswiler wrote:
> > > From: Marcel Ziswiler <marcel.ziswiler@toradex.com>
> > > 
> > > Integrate cache alignment bounce buffer to workaround issues as follows:
> > > 
> > > Loading file '/boot/zImage' to addr 0x01000000 with size 4499152
> > > (0x0044a6d0)...
> > > ERROR: v7_dcache_inval_range - start address is not aligned - 0x1f7f0108
> > > ERROR: v7_dcache_inval_range - stop address is not aligned - 0x1f7f1108
> > > Done
> > > Kernel image @ 0x1000000 [ 0x000000 - 0x44a6d0 ]
> > > 
> > > Starting kernel ...
> > > 
> > > undefined instruction
> > > pc : [<005ff03c>]          lr : [<0000800c>]
> > > sp : 0144b6e8  ip : 01000188     fp : 0144a6c8
> > > r10: 00000000  r9 : 411fc090     r8 : 00000100
> > > r7 : 00000cfb  r6 : 0144a6d0     r5 : 00000000  r4 : 00008000
> > > r3 : 0000000c  r2 : 00000100     r1 : 00000cfb  r0 : 00000000
> > > Flags: nZCv  IRQs off  FIQs off  Mode SVC_32
> > > Resetting CPU ...
> > > 
> > > Signed-off-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
> > > ---
> > > Changes in v2:
> > > Migrated to using generic bounce buffer implementation as suggested by
> > > Simon.
> > 
> > I didn't see Simon's suggestion, but what is the general policy in U-Boot
> > supposed to be for I/O alignment?  Responsibility of the API user, or of 
> > the
> > driver?  Are there other drivers with the same problem?
> 
> MMC has it. I feel that in general we should be able to ask the
> higher-level systems to do the right thing and align their data.
> Otherwise it introduces inefficiencies into the stack. But at least if
> we do introduce this, we should use common infrastructure, hence my
> request.

OK, so your request wasn't to move this into the driver, just about how to 
implement it if it's done in the driver?  Marcel, why did you change to 
handling this in the driver?

-Scott
Marcel Ziswiler July 28, 2015, 1:55 a.m. UTC | #4
On 27 July 2015 21:57:14 CEST, Scott Wood <scottwood@freescale.com> wrote:
>On Tue, 2015-07-21 at 00:35 +0200, Marcel Ziswiler wrote:
>> From: Marcel Ziswiler <marcel.ziswiler@toradex.com>
>> 
>> Integrate cache alignment bounce buffer to workaround issues as
>follows:
>> 
>> Loading file '/boot/zImage' to addr 0x01000000 with size 4499152 
>> (0x0044a6d0)...
>> ERROR: v7_dcache_inval_range - start address is not aligned -
>0x1f7f0108
>> ERROR: v7_dcache_inval_range - stop address is not aligned -
>0x1f7f1108
>> Done
>> Kernel image @ 0x1000000 [ 0x000000 - 0x44a6d0 ]
>> 
>> Starting kernel ...
>> 
>> undefined instruction
>> pc : [<005ff03c>]          lr : [<0000800c>]
>> sp : 0144b6e8  ip : 01000188     fp : 0144a6c8
>> r10: 00000000  r9 : 411fc090     r8 : 00000100
>> r7 : 00000cfb  r6 : 0144a6d0     r5 : 00000000  r4 : 00008000
>> r3 : 0000000c  r2 : 00000100     r1 : 00000cfb  r0 : 00000000
>> Flags: nZCv  IRQs off  FIQs off  Mode SVC_32
>> Resetting CPU ...
>> 
>> Signed-off-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
>> ---
>> Changes in v2:
>> Migrated to using generic bounce buffer implementation as suggested
>by 
>> Simon.
>
>I didn't see Simon's suggestion, but what is the general policy in
>U-Boot 
>supposed to be for I/O alignment?  Responsibility of the API user, or
>of the 
>driver?  Are there other drivers with the same problem?

Well, well. You got us into some ruff terrain right there. Remember I have some other alignment patches awaiting acceptance (e.g U-Boot's MTD and most possibly other subsystems are deeply broken in that respect) but even with those I failed booting a kernel loaded from UBIFS so I started having my doubts whether or not this is truly possible without bouncing. At least Tegra's MMC driver just bounces as well.

>> -     writel(virt_to_phys(buf), &info->reg->data_block_ptr);
>> +     writel((u32)bbstate.bounce_buffer, &info->reg->data_block_ptr);
>
>Why are you converting usage of virt_to_phys() into a u32 cast?

Good question. I believe this I took straight from the MMC driver but will double check it again for a v3.
Marcel Ziswiler July 28, 2015, 2:09 a.m. UTC | #5
On 28 July 2015 01:32:10 CEST, Scott Wood <scottwood@freescale.com> wrote:
>On Mon, 2015-07-27 at 17:28 -0600, Simon Glass wrote:
>> Hi Scott,
>> 
>> On 27 July 2015 at 13:57, Scott Wood <scottwood@freescale.com> wrote:
>> > On Tue, 2015-07-21 at 00:35 +0200, Marcel Ziswiler wrote:
>> > > From: Marcel Ziswiler <marcel.ziswiler@toradex.com>
>> > > 
>> > > Integrate cache alignment bounce buffer to workaround issues as
>follows:
>> > > 
>> > > Loading file '/boot/zImage' to addr 0x01000000 with size 4499152
>> > > (0x0044a6d0)...
>> > > ERROR: v7_dcache_inval_range - start address is not aligned -
>0x1f7f0108
>> > > ERROR: v7_dcache_inval_range - stop address is not aligned -
>0x1f7f1108
>> > > Done
>> > > Kernel image @ 0x1000000 [ 0x000000 - 0x44a6d0 ]
>> > > 
>> > > Starting kernel ...
>> > > 
>> > > undefined instruction
>> > > pc : [<005ff03c>]          lr : [<0000800c>]
>> > > sp : 0144b6e8  ip : 01000188     fp : 0144a6c8
>> > > r10: 00000000  r9 : 411fc090     r8 : 00000100
>> > > r7 : 00000cfb  r6 : 0144a6d0     r5 : 00000000  r4 : 00008000
>> > > r3 : 0000000c  r2 : 00000100     r1 : 00000cfb  r0 : 00000000
>> > > Flags: nZCv  IRQs off  FIQs off  Mode SVC_32
>> > > Resetting CPU ...
>> > > 
>> > > Signed-off-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
>> > > ---
>> > > Changes in v2:
>> > > Migrated to using generic bounce buffer implementation as
>suggested by
>> > > Simon.
>> > 
>> > I didn't see Simon's suggestion, but what is the general policy in
>U-Boot
>> > supposed to be for I/O alignment?  Responsibility of the API user,
>or of 
>> > the
>> > driver?  Are there other drivers with the same problem?
>> 
>> MMC has it. I feel that in general we should be able to ask the
>> higher-level systems to do the right thing and align their data.
>> Otherwise it introduces inefficiencies into the stack. But at least
>if
>> we do introduce this, we should use common infrastructure, hence my
>> request.
>
>OK, so your request wasn't to move this into the driver, just about how
>to 
>implement it if it's done in the driver?  Marcel, why did you change to
>
>handling this in the driver?

Maybe because U-Boot is deeply broken in that respect. Please also see my comment in my previous post.
Scott Wood July 28, 2015, 2:15 a.m. UTC | #6
On Tue, 2015-07-28 at 03:55 +0200, Marcel Ziswiler wrote:
> On 27 July 2015 21:57:14 CEST, Scott Wood <scottwood@freescale.com> wrote:
> > On Tue, 2015-07-21 at 00:35 +0200, Marcel Ziswiler wrote:
> > > From: Marcel Ziswiler <marcel.ziswiler@toradex.com>
> > > 
> > > Integrate cache alignment bounce buffer to workaround issues as
> > follows:
> > > 
> > > Loading file '/boot/zImage' to addr 0x01000000 with size 4499152 
> > > (0x0044a6d0)...
> > > ERROR: v7_dcache_inval_range - start address is not aligned -
> > 0x1f7f0108
> > > ERROR: v7_dcache_inval_range - stop address is not aligned -
> > 0x1f7f1108
> > > Done
> > > Kernel image @ 0x1000000 [ 0x000000 - 0x44a6d0 ]
> > > 
> > > Starting kernel ...
> > > 
> > > undefined instruction
> > > pc : [<005ff03c>]          lr : [<0000800c>]
> > > sp : 0144b6e8  ip : 01000188     fp : 0144a6c8
> > > r10: 00000000  r9 : 411fc090     r8 : 00000100
> > > r7 : 00000cfb  r6 : 0144a6d0     r5 : 00000000  r4 : 00008000
> > > r3 : 0000000c  r2 : 00000100     r1 : 00000cfb  r0 : 00000000
> > > Flags: nZCv  IRQs off  FIQs off  Mode SVC_32
> > > Resetting CPU ...
> > > 
> > > Signed-off-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
> > > ---
> > > Changes in v2:
> > > Migrated to using generic bounce buffer implementation as suggested
> > by 
> > > Simon.
> > 
> > I didn't see Simon's suggestion, but what is the general policy in
> > U-Boot 
> > supposed to be for I/O alignment?  Responsibility of the API user, or
> > of the 
> > driver?  Are there other drivers with the same problem?
> 
> Well, well. You got us into some ruff terrain right there. Remember I have 
> some other alignment patches awaiting acceptance (e.g U-Boot's MTD and most 
> possibly other subsystems are deeply broken in that respect) but even with 
> those I failed booting a kernel loaded from UBIFS so I started having my 
> doubts whether or not this is truly possible without bouncing. At least 
> Tegra's MMC driver just bounces as well.

OK.  With users such as UBI that are shared with kernel code, it might be 
difficult to enforce a caller-aligns model.

-Scott
Marcel Ziswiler Aug. 5, 2015, 10:12 p.m. UTC | #7
On Tue, 2015-07-28 at 03:55 +0200, Marcel Ziswiler wrote:
> 
> On 27 July 2015 21:57:14 CEST, Scott Wood <scottwood@freescale.com> 
> wrote:
> > > -     writel(virt_to_phys(buf), &info->reg->data_block_ptr);
> > > +     writel((u32)bbstate.bounce_buffer, &info->reg
> > > ->data_block_ptr);
> > 
> > Why are you converting usage of virt_to_phys() into a u32 cast?
> 
> Good question. I believe this I took straight from the MMC driver but 
> will double check it again for a v3.

Yes, the MMC driver actually even does a double cast. Don't knowexactly what that should bring but that's how it looks there:
writel((u32)(unsigned long)bbstate->bounce_buffer, &host->reg->sysad);
http://git.denx.de/?p=u-boot.git;a=blob;f=drivers/mmc/tegra_mmc.c;hb=HEAD#l70
Nonetheless I changed it back to using virt_to_phys() for the v3 to beposted soon.
Scott Wood Aug. 5, 2015, 10:16 p.m. UTC | #8
On Wed, 2015-08-05 at 22:12 +0000, Marcel Ziswiler wrote:
> On Tue, 2015-07-28 at 03:55 +0200, Marcel Ziswiler wrote:
> > 
> > On 27 July 2015 21:57:14 CEST, Scott Wood <scottwood@freescale.com> 
> > wrote:
> > > > -     writel(virt_to_phys(buf), &info->reg->data_block_ptr);
> > > > +     writel((u32)bbstate.bounce_buffer, &info->reg
> > > > ->data_block_ptr);
> > > 
> > > Why are you converting usage of virt_to_phys() into a u32 cast?
> > 
> > Good question. I believe this I took straight from the MMC driver but 
> > will double check it again for a v3.
> 
> Yes, the MMC driver actually even does a double cast. Don't knowexactly 
> what that should bring but that's how it looks there:
> writel((u32)(unsigned long)bbstate->bounce_buffer, &host->reg->sysad);
> http://git.denx.de/?p=u-boot.git;a=blob;f=drivers/mmc/tegra_mmc.c;hb=HEAD#l70
> Nonetheless I changed it back to using virt_to_phys() for the v3 to 
> beposted soon.

The double cast suppresses a warning in 64-bit builds (converting directly 
from a pointer to an integer of a different size), but ideally virt_to_phys() 
should be used.

-Scott
diff mbox

Patch

diff --git a/drivers/mtd/nand/tegra_nand.c b/drivers/mtd/nand/tegra_nand.c
index 9f90683..02d8aed 100644
--- a/drivers/mtd/nand/tegra_nand.c
+++ b/drivers/mtd/nand/tegra_nand.c
@@ -16,6 +16,7 @@ 
 #include <asm/errno.h>
 #include <asm/gpio.h>
 #include <fdtdec.h>
+#include <bouncebuf.h>
 #include "tegra_nand.h"
 
 DECLARE_GLOBAL_DATA_PTR;
@@ -93,35 +94,6 @@  static struct nand_drv nand_ctrl;
 static struct mtd_info *our_mtd;
 static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE];
 
-#ifdef CONFIG_SYS_DCACHE_OFF
-static inline void dma_prepare(void *start, unsigned long length,
-			       int is_writing)
-{
-}
-#else
-/**
- * Prepare for a DMA transaction
- *
- * For a write we flush out our data. For a read we invalidate, since we
- * need to do this before we read from the buffer after the DMA has
- * completed, so may as well do it now.
- *
- * @param start		Start address for DMA buffer (should be cache-aligned)
- * @param length	Length of DMA buffer in bytes
- * @param is_writing	0 if reading, non-zero if writing
- */
-static void dma_prepare(void *start, unsigned long length, int is_writing)
-{
-	unsigned long addr = (unsigned long)start;
-
-	length = ALIGN(length, ARCH_DMA_MINALIGN);
-	if (is_writing)
-		flush_dcache_range(addr, addr + length);
-	else
-		invalidate_dcache_range(addr, addr + length);
-}
-#endif
-
 /**
  * Wait for command completion
  *
@@ -534,6 +506,8 @@  static int nand_rw_page(struct mtd_info *mtd, struct nand_chip *chip,
 	char *tag_ptr;
 	struct nand_drv *info;
 	struct fdt_nand *config;
+	unsigned int bbflags;
+	struct bounce_buffer bbstate, bbstate_oob;
 
 	if ((uintptr_t)buf & 0x03) {
 		printf("buf %p has to be 4-byte aligned\n", buf);
@@ -550,21 +524,21 @@  static int nand_rw_page(struct mtd_info *mtd, struct nand_chip *chip,
 
 	stop_command(info->reg);
 
+	if (is_writing)
+		bbflags = GEN_BB_READ;
+	else
+		bbflags = GEN_BB_WRITE;
+
+	bounce_buffer_start(&bbstate, (void *)buf, 1 << chip->page_shift,
+			    bbflags);
 	writel((1 << chip->page_shift) - 1, &info->reg->dma_cfg_a);
-	writel(virt_to_phys(buf), &info->reg->data_block_ptr);
+	writel((u32)bbstate.bounce_buffer, &info->reg->data_block_ptr);
 
+	/* Set ECC selection, configure ECC settings */
 	if (with_ecc) {
-		writel(virt_to_phys(tag_ptr), &info->reg->tag_ptr);
 		if (is_writing)
 			memcpy(tag_ptr, chip->oob_poi + free->offset,
-				chip->ecc.layout->oobavail +
-				TAG_ECC_BYTES);
-	} else {
-		writel(virt_to_phys(chip->oob_poi), &info->reg->tag_ptr);
-	}
-
-	/* Set ECC selection, configure ECC settings */
-	if (with_ecc) {
+			       chip->ecc.layout->oobavail + TAG_ECC_BYTES);
 		tag_size = chip->ecc.layout->oobavail + TAG_ECC_BYTES;
 		reg_val |= (CFG_SKIP_SPARE_SEL_4
 			| CFG_SKIP_SPARE_ENABLE
@@ -577,7 +551,8 @@  static int nand_rw_page(struct mtd_info *mtd, struct nand_chip *chip,
 
 		if (!is_writing)
 			tag_size += SKIPPED_SPARE_BYTES;
-		dma_prepare(tag_ptr, tag_size, is_writing);
+		bounce_buffer_start(&bbstate_oob, (void *)tag_ptr, tag_size,
+				    bbflags);
 	} else {
 		tag_size = mtd->oobsize;
 		reg_val |= (CFG_SKIP_SPARE_DISABLE
@@ -585,14 +560,12 @@  static int nand_rw_page(struct mtd_info *mtd, struct nand_chip *chip,
 			| CFG_ECC_EN_TAG_DISABLE
 			| CFG_HW_ECC_DISABLE
 			| (tag_size - 1));
-		dma_prepare(chip->oob_poi, tag_size, is_writing);
+		bounce_buffer_start(&bbstate_oob, (void *)chip->oob_poi,
+				    tag_size, bbflags);
 	}
 	writel(reg_val, &info->reg->config);
-
-	dma_prepare(buf, 1 << chip->page_shift, is_writing);
-
+	writel((u32)bbstate_oob.bounce_buffer, &info->reg->tag_ptr);
 	writel(BCH_CONFIG_BCH_ECC_DISABLE, &info->reg->bch_config);
-
 	writel(tag_size - 1, &info->reg->dma_cfg_b);
 
 	nand_clear_interrupt_status(info->reg);
@@ -638,6 +611,9 @@  static int nand_rw_page(struct mtd_info *mtd, struct nand_chip *chip,
 		return -EIO;
 	}
 
+	bounce_buffer_stop(&bbstate_oob);
+	bounce_buffer_stop(&bbstate);
+
 	if (with_ecc && !is_writing) {
 		memcpy(chip->oob_poi, tag_ptr,
 			SKIPPED_SPARE_BYTES);
@@ -755,6 +731,8 @@  static int nand_rw_oob(struct mtd_info *mtd, struct nand_chip *chip,
 	int tag_size;
 	struct nand_oobfree *free = chip->ecc.layout->oobfree;
 	struct nand_drv *info;
+	unsigned int bbflags;
+	struct bounce_buffer bbstate_oob;
 
 	if (((int)chip->oob_poi) & 0x03)
 		return -EINVAL;
@@ -764,8 +742,6 @@  static int nand_rw_oob(struct mtd_info *mtd, struct nand_chip *chip,
 
 	stop_command(info->reg);
 
-	writel(virt_to_phys(chip->oob_poi), &info->reg->tag_ptr);
-
 	/* Set ECC selection */
 	tag_size = mtd->oobsize;
 	if (with_ecc)
@@ -779,13 +755,20 @@  static int nand_rw_oob(struct mtd_info *mtd, struct nand_chip *chip,
 		CFG_HW_ECC_DISABLE);
 	writel(reg_val, &info->reg->config);
 
-	dma_prepare(chip->oob_poi, tag_size, is_writing);
-
-	writel(BCH_CONFIG_BCH_ECC_DISABLE, &info->reg->bch_config);
-
 	if (is_writing && with_ecc)
 		tag_size -= TAG_ECC_BYTES;
 
+	if (is_writing)
+		bbflags = GEN_BB_READ;
+	else
+		bbflags = GEN_BB_WRITE;
+
+	bounce_buffer_start(&bbstate_oob, (void *)chip->oob_poi, tag_size,
+			    bbflags);
+	writel((u32)bbstate_oob.bounce_buffer, &info->reg->tag_ptr);
+
+	writel(BCH_CONFIG_BCH_ECC_DISABLE, &info->reg->bch_config);
+
 	writel(tag_size - 1, &info->reg->dma_cfg_b);
 
 	nand_clear_interrupt_status(info->reg);
@@ -822,6 +805,8 @@  static int nand_rw_oob(struct mtd_info *mtd, struct nand_chip *chip,
 		return -EIO;
 	}
 
+	bounce_buffer_stop(&bbstate_oob);
+
 	if (with_ecc && !is_writing) {
 		reg_val = (u32)check_ecc_error(info->reg, 0, 0,
 			(u8 *)(chip->oob_poi + free->offset),