diff mbox series

[RFC,v4,4/5] mtd: spinand: Move set/get OOB databytes to each ECC engines

Message ID 20211130083202.14228-5-xiangsheng.hou@mediatek.com (mailing list archive)
State New, archived
Headers show
Series Add Mediatek SPI Nand controller and convert ECC driver | expand

Commit Message

Xiangsheng Hou Nov. 30, 2021, 8:32 a.m. UTC
Move set/get OOB databytes to each ECC engines when in AUTO mode.
For read/write in AUTO mode, the OOB bytes include not only free
date bytes but also ECC data bytes. And for some special ECC engine,
the data bytes in OOB may be mixed with main data. For example,
mediatek ECC engine will be one more main data byte swap with BBM.
So, just put these operation in each ECC engine to distinguish
the differentiation.

Signed-off-by: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
---
 drivers/mtd/nand/ecc-sw-bch.c           | 71 ++++++++++++++++---
 drivers/mtd/nand/ecc-sw-hamming.c       | 71 ++++++++++++++++---
 drivers/mtd/nand/spi/core.c             | 93 +++++++++++++++++--------
 include/linux/mtd/nand-ecc-sw-bch.h     |  4 ++
 include/linux/mtd/nand-ecc-sw-hamming.h |  4 ++
 include/linux/mtd/spinand.h             |  4 ++
 6 files changed, 198 insertions(+), 49 deletions(-)

Comments

Miquel Raynal Dec. 14, 2021, 11:41 a.m. UTC | #1
Hi Xiangsheng,

xiangsheng.hou@mediatek.com wrote on Tue, 30 Nov 2021 16:32:01 +0800:

> Move set/get OOB databytes to each ECC engines when in AUTO mode.
> For read/write in AUTO mode, the OOB bytes include not only free
> date bytes but also ECC data bytes.

This is more or less ok, I would rephrase it to give more details about
the issue:

Move the data bytes handling when in AUTO mode to the ECC drivers, only
them have the knowledge of what's in the OOB buffer and we should be
sure that the ECC bytes do not smash the data provided by the user in
this mode. So the ECC drivers must take care of any data that could be
present at the beginning of the buffer before generating the ECC bytes.

> And for some special ECC engine,
> the data bytes in OOB may be mixed with main data. For example,
> mediatek ECC engine will be one more main data byte swap with BBM.
> So, just put these operation in each ECC engine to distinguish
> the differentiation.

But his is not related to your change, please drop it.

Needs a Fixes here I believe.

> Signed-off-by: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
> ---
>  drivers/mtd/nand/ecc-sw-bch.c           | 71 ++++++++++++++++---
>  drivers/mtd/nand/ecc-sw-hamming.c       | 71 ++++++++++++++++---
>  drivers/mtd/nand/spi/core.c             | 93 +++++++++++++++++--------
>  include/linux/mtd/nand-ecc-sw-bch.h     |  4 ++
>  include/linux/mtd/nand-ecc-sw-hamming.h |  4 ++
>  include/linux/mtd/spinand.h             |  4 ++
>  6 files changed, 198 insertions(+), 49 deletions(-)
> 
> diff --git a/drivers/mtd/nand/ecc-sw-bch.c b/drivers/mtd/nand/ecc-sw-bch.c
> index 405552d014a8..bda31ef8f0b8 100644
> --- a/drivers/mtd/nand/ecc-sw-bch.c
> +++ b/drivers/mtd/nand/ecc-sw-bch.c
> @@ -238,7 +238,9 @@ int nand_ecc_sw_bch_init_ctx(struct nand_device *nand)
>  	engine_conf->code_size = code_size;
>  	engine_conf->calc_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
>  	engine_conf->code_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
> -	if (!engine_conf->calc_buf || !engine_conf->code_buf) {
> +	engine_conf->oob_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
> +	if (!engine_conf->calc_buf || !engine_conf->code_buf ||
> +	    !engine_conf->oob_buf) {
>  		ret = -ENOMEM;
>  		goto free_bufs;
>  	}
> @@ -267,6 +269,7 @@ int nand_ecc_sw_bch_init_ctx(struct nand_device *nand)
>  	nand_ecc_cleanup_req_tweaking(&engine_conf->req_ctx);
>  	kfree(engine_conf->calc_buf);
>  	kfree(engine_conf->code_buf);
> +	kfree(engine_conf->oob_buf);
>  free_engine_conf:
>  	kfree(engine_conf);
>  
> @@ -283,6 +286,7 @@ void nand_ecc_sw_bch_cleanup_ctx(struct nand_device *nand)
>  		nand_ecc_cleanup_req_tweaking(&engine_conf->req_ctx);
>  		kfree(engine_conf->calc_buf);
>  		kfree(engine_conf->code_buf);
> +		kfree(engine_conf->oob_buf);
>  		kfree(engine_conf);
>  	}
>  }
> @@ -299,22 +303,42 @@ static int nand_ecc_sw_bch_prepare_io_req(struct nand_device *nand,
>  	int total = nand->ecc.ctx.total;
>  	u8 *ecccalc = engine_conf->calc_buf;
>  	const u8 *data;
> -	int i;
> +	int i, ret = 0;

int i, ret;

>  
>  	/* Nothing to do for a raw operation */
>  	if (req->mode == MTD_OPS_RAW)
>  		return 0;
>  
> -	/* This engine does not provide BBM/free OOB bytes protection */
> -	if (!req->datalen)
> -		return 0;
> -

Why? Please drop this removal here and below, it has nothing to do with
the current fix, right?

>  	nand_ecc_tweak_req(&engine_conf->req_ctx, req);
>  
>  	/* No more preparation for page read */
>  	if (req->type == NAND_PAGE_READ)
>  		return 0;
>  
> +	if (req->ooblen) {
> +		memset(engine_conf->oob_buf, 0xff,
> +		       nanddev_per_page_oobsize(nand));

I think this is only needed in the AUTO case.

> +
> +		if (req->mode == MTD_OPS_AUTO_OOB) {
> +			ret = mtd_ooblayout_set_databytes(mtd, req->oobbuf.out,
> +							  engine_conf->oob_buf,
> +							  req->ooboffs,
> +							  mtd->oobavail);
> +			if (ret)
> +				return ret;
> +		} else {
> +			memcpy(engine_conf->oob_buf + req->ooboffs,
> +			       req->oobbuf.out, req->ooblen);
> +		}
> +
> +		engine_conf->src_oob_buf = (void *)req->oobbuf.out;
> +		req->oobbuf.out = engine_conf->oob_buf;
> +	}
> +
> +	/* This engine does not provide BBM/free OOB bytes protection */
> +	if (!req->datalen)
> +		return 0;
> +

I believe we can do something simpler:

if (req->ooblen && req->mode == AUTO) {
	memcpy(oobbuf, req->oobbuf.out...
	mtd_ooblayout_set_databytes(using oobbuf)
}

But I believe this should be done before tweaking the request so that
you can avoid messing with src_oob_buf, right?

>  	/* Preparation for page write: derive the ECC bytes and place them */
>  	for (i = 0, data = req->databuf.out;
>  	     eccsteps;
> @@ -344,12 +368,36 @@ static int nand_ecc_sw_bch_finish_io_req(struct nand_device *nand,
>  	if (req->mode == MTD_OPS_RAW)
>  		return 0;
>  
> -	/* This engine does not provide BBM/free OOB bytes protection */
> -	if (!req->datalen)
> -		return 0;
> -
>  	/* No more preparation for page write */
>  	if (req->type == NAND_PAGE_WRITE) {
> +		if (req->ooblen)
> +			req->oobbuf.out = engine_conf->src_oob_buf;
> +
> +		nand_ecc_restore_req(&engine_conf->req_ctx, req);
> +		return 0;
> +	}
> +
> +	if (req->ooblen) {
> +		memset(engine_conf->oob_buf, 0xff,
> +		       nanddev_per_page_oobsize(nand));
> +
> +		if (req->mode == MTD_OPS_AUTO_OOB) {
> +			ret = mtd_ooblayout_get_databytes(mtd,
> +							  engine_conf->oob_buf,
> +							  req->oobbuf.in,
> +							  req->ooboffs,
> +							  mtd->oobavail);
> +			if (ret)
> +				return ret;
> +		} else {
> +			memcpy(engine_conf->oob_buf,
> +			       req->oobbuf.in + req->ooboffs, req->ooblen);
> +		}
> +	}
> +
> +	/* This engine does not provide BBM/free OOB bytes protection */
> +	if (!req->datalen) {
> +		req->oobbuf.in = engine_conf->oob_buf;
>  		nand_ecc_restore_req(&engine_conf->req_ctx, req);
>  		return 0;
>  	}
> @@ -379,6 +427,9 @@ static int nand_ecc_sw_bch_finish_io_req(struct nand_device *nand,
>  		}
>  	}
>  
> +	if (req->ooblen)
> +		req->oobbuf.in = engine_conf->oob_buf;
> +
>  	nand_ecc_restore_req(&engine_conf->req_ctx, req);
>  
>  	return max_bitflips;

Same applies to the two other engines.

Thanks,
Miquèl
Xiangsheng Hou Dec. 20, 2021, 7:37 a.m. UTC | #2
Hi Miquel,

On Tue, 2021-12-14 at 12:41 +0100, Miquel Raynal wrote:
> 
> > 
> > @@ -299,22 +303,42 @@ static int
> > nand_ecc_sw_bch_prepare_io_req(struct nand_device *nand,
> >  	int total = nand->ecc.ctx.total;
> >  	u8 *ecccalc = engine_conf->calc_buf;
> >  	const u8 *data;
> > -	int i;
> > +	int i, ret = 0;
> 
> int i, ret;
> 
> >  
> >  	/* Nothing to do for a raw operation */
> >  	if (req->mode == MTD_OPS_RAW)
> >  		return 0;
> >  
> > -	/* This engine does not provide BBM/free OOB bytes protection
> > */
> > -	if (!req->datalen)
> > -		return 0;
> > -
> 
> Why? Please drop this removal here and below, it has nothing to do
> with
> the current fix, right?
> 

For req->datalen == 0 and req->ooblen !=0 when write operation in
OPS_AUTO mode, it`s also need to set OOB databytes. So I remove this to
the back.

However, for OPS_RAW and OPS_PLACE_OOB mode, it`s unnecessary.

I will try to fix this.

> >  	nand_ecc_tweak_req(&engine_conf->req_ctx, req);
> >  
> >  	/* No more preparation for page read */
> >  	if (req->type == NAND_PAGE_READ)
> >  		return 0;
> >  
> > +	if (req->ooblen) {
> > +		memset(engine_conf->oob_buf, 0xff,
> > +		       nanddev_per_page_oobsize(nand));
> 
> I think this is only needed in the AUTO case.
> 
> > +
> > +		if (req->mode == MTD_OPS_AUTO_OOB) {
> > +			ret = mtd_ooblayout_set_databytes(mtd, req-
> > >oobbuf.out,
> > +							  engine_conf-
> > >oob_buf,
> > +							  req->ooboffs,
> > +							  mtd-
> > >oobavail);
> > +			if (ret)
> > +				return ret;
> > +		} else {
> > +			memcpy(engine_conf->oob_buf + req->ooboffs,
> > +			       req->oobbuf.out, req->ooblen);
> > +		}
> > +
> > +		engine_conf->src_oob_buf = (void *)req->oobbuf.out;
> > +		req->oobbuf.out = engine_conf->oob_buf;
> > +	}
> > +
> > +	/* This engine does not provide BBM/free OOB bytes protection
> > */
> > +	if (!req->datalen)
> > +		return 0;
> > +
> 
> I believe we can do something simpler:
> 
> if (req->ooblen && req->mode == AUTO) {
> 	memcpy(oobbuf, req->oobbuf.out...
> 	mtd_ooblayout_set_databytes(using oobbuf)
> }
> 
> But I believe this should be done before tweaking the request so that
> you can avoid messing with src_oob_buf, right?

Yes, you are right. For OPS_AUTO mode, the maximal request OOB len is
mtd->oobavail which smaller than nand->memorg.oobsize.

Therefore, this will use bounce oob buffer when tweaking the request.

> 
> Same applies to the two other engines.

I will.

Thanks
Xiangsheng Hou
diff mbox series

Patch

diff --git a/drivers/mtd/nand/ecc-sw-bch.c b/drivers/mtd/nand/ecc-sw-bch.c
index 405552d014a8..bda31ef8f0b8 100644
--- a/drivers/mtd/nand/ecc-sw-bch.c
+++ b/drivers/mtd/nand/ecc-sw-bch.c
@@ -238,7 +238,9 @@  int nand_ecc_sw_bch_init_ctx(struct nand_device *nand)
 	engine_conf->code_size = code_size;
 	engine_conf->calc_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
 	engine_conf->code_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
-	if (!engine_conf->calc_buf || !engine_conf->code_buf) {
+	engine_conf->oob_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
+	if (!engine_conf->calc_buf || !engine_conf->code_buf ||
+	    !engine_conf->oob_buf) {
 		ret = -ENOMEM;
 		goto free_bufs;
 	}
@@ -267,6 +269,7 @@  int nand_ecc_sw_bch_init_ctx(struct nand_device *nand)
 	nand_ecc_cleanup_req_tweaking(&engine_conf->req_ctx);
 	kfree(engine_conf->calc_buf);
 	kfree(engine_conf->code_buf);
+	kfree(engine_conf->oob_buf);
 free_engine_conf:
 	kfree(engine_conf);
 
@@ -283,6 +286,7 @@  void nand_ecc_sw_bch_cleanup_ctx(struct nand_device *nand)
 		nand_ecc_cleanup_req_tweaking(&engine_conf->req_ctx);
 		kfree(engine_conf->calc_buf);
 		kfree(engine_conf->code_buf);
+		kfree(engine_conf->oob_buf);
 		kfree(engine_conf);
 	}
 }
@@ -299,22 +303,42 @@  static int nand_ecc_sw_bch_prepare_io_req(struct nand_device *nand,
 	int total = nand->ecc.ctx.total;
 	u8 *ecccalc = engine_conf->calc_buf;
 	const u8 *data;
-	int i;
+	int i, ret = 0;
 
 	/* Nothing to do for a raw operation */
 	if (req->mode == MTD_OPS_RAW)
 		return 0;
 
-	/* This engine does not provide BBM/free OOB bytes protection */
-	if (!req->datalen)
-		return 0;
-
 	nand_ecc_tweak_req(&engine_conf->req_ctx, req);
 
 	/* No more preparation for page read */
 	if (req->type == NAND_PAGE_READ)
 		return 0;
 
+	if (req->ooblen) {
+		memset(engine_conf->oob_buf, 0xff,
+		       nanddev_per_page_oobsize(nand));
+
+		if (req->mode == MTD_OPS_AUTO_OOB) {
+			ret = mtd_ooblayout_set_databytes(mtd, req->oobbuf.out,
+							  engine_conf->oob_buf,
+							  req->ooboffs,
+							  mtd->oobavail);
+			if (ret)
+				return ret;
+		} else {
+			memcpy(engine_conf->oob_buf + req->ooboffs,
+			       req->oobbuf.out, req->ooblen);
+		}
+
+		engine_conf->src_oob_buf = (void *)req->oobbuf.out;
+		req->oobbuf.out = engine_conf->oob_buf;
+	}
+
+	/* This engine does not provide BBM/free OOB bytes protection */
+	if (!req->datalen)
+		return 0;
+
 	/* Preparation for page write: derive the ECC bytes and place them */
 	for (i = 0, data = req->databuf.out;
 	     eccsteps;
@@ -344,12 +368,36 @@  static int nand_ecc_sw_bch_finish_io_req(struct nand_device *nand,
 	if (req->mode == MTD_OPS_RAW)
 		return 0;
 
-	/* This engine does not provide BBM/free OOB bytes protection */
-	if (!req->datalen)
-		return 0;
-
 	/* No more preparation for page write */
 	if (req->type == NAND_PAGE_WRITE) {
+		if (req->ooblen)
+			req->oobbuf.out = engine_conf->src_oob_buf;
+
+		nand_ecc_restore_req(&engine_conf->req_ctx, req);
+		return 0;
+	}
+
+	if (req->ooblen) {
+		memset(engine_conf->oob_buf, 0xff,
+		       nanddev_per_page_oobsize(nand));
+
+		if (req->mode == MTD_OPS_AUTO_OOB) {
+			ret = mtd_ooblayout_get_databytes(mtd,
+							  engine_conf->oob_buf,
+							  req->oobbuf.in,
+							  req->ooboffs,
+							  mtd->oobavail);
+			if (ret)
+				return ret;
+		} else {
+			memcpy(engine_conf->oob_buf,
+			       req->oobbuf.in + req->ooboffs, req->ooblen);
+		}
+	}
+
+	/* This engine does not provide BBM/free OOB bytes protection */
+	if (!req->datalen) {
+		req->oobbuf.in = engine_conf->oob_buf;
 		nand_ecc_restore_req(&engine_conf->req_ctx, req);
 		return 0;
 	}
@@ -379,6 +427,9 @@  static int nand_ecc_sw_bch_finish_io_req(struct nand_device *nand,
 		}
 	}
 
+	if (req->ooblen)
+		req->oobbuf.in = engine_conf->oob_buf;
+
 	nand_ecc_restore_req(&engine_conf->req_ctx, req);
 
 	return max_bitflips;
diff --git a/drivers/mtd/nand/ecc-sw-hamming.c b/drivers/mtd/nand/ecc-sw-hamming.c
index 254db2e7f8bb..c90ff31e9656 100644
--- a/drivers/mtd/nand/ecc-sw-hamming.c
+++ b/drivers/mtd/nand/ecc-sw-hamming.c
@@ -507,7 +507,9 @@  int nand_ecc_sw_hamming_init_ctx(struct nand_device *nand)
 	engine_conf->code_size = 3;
 	engine_conf->calc_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
 	engine_conf->code_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
-	if (!engine_conf->calc_buf || !engine_conf->code_buf) {
+	engine_conf->oob_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
+	if (!engine_conf->calc_buf || !engine_conf->code_buf ||
+	    !engine_conf->oob_buf) {
 		ret = -ENOMEM;
 		goto free_bufs;
 	}
@@ -522,6 +524,7 @@  int nand_ecc_sw_hamming_init_ctx(struct nand_device *nand)
 	nand_ecc_cleanup_req_tweaking(&engine_conf->req_ctx);
 	kfree(engine_conf->calc_buf);
 	kfree(engine_conf->code_buf);
+	kfree(engine_conf->oob_buf);
 free_engine_conf:
 	kfree(engine_conf);
 
@@ -537,6 +540,7 @@  void nand_ecc_sw_hamming_cleanup_ctx(struct nand_device *nand)
 		nand_ecc_cleanup_req_tweaking(&engine_conf->req_ctx);
 		kfree(engine_conf->calc_buf);
 		kfree(engine_conf->code_buf);
+		kfree(engine_conf->oob_buf);
 		kfree(engine_conf);
 	}
 }
@@ -553,22 +557,42 @@  static int nand_ecc_sw_hamming_prepare_io_req(struct nand_device *nand,
 	int total = nand->ecc.ctx.total;
 	u8 *ecccalc = engine_conf->calc_buf;
 	const u8 *data;
-	int i;
+	int i, ret;
 
 	/* Nothing to do for a raw operation */
 	if (req->mode == MTD_OPS_RAW)
 		return 0;
 
-	/* This engine does not provide BBM/free OOB bytes protection */
-	if (!req->datalen)
-		return 0;
-
 	nand_ecc_tweak_req(&engine_conf->req_ctx, req);
 
 	/* No more preparation for page read */
 	if (req->type == NAND_PAGE_READ)
 		return 0;
 
+	if (req->ooblen) {
+		memset(engine_conf->oob_buf, 0xff,
+		       nanddev_per_page_oobsize(nand));
+
+		if (req->mode == MTD_OPS_AUTO_OOB) {
+			ret = mtd_ooblayout_set_databytes(mtd, req->oobbuf.out,
+							  engine_conf->oob_buf,
+							  req->ooboffs,
+							  mtd->oobavail);
+			if (ret)
+				return ret;
+		} else {
+			memcpy(engine_conf->oob_buf + req->ooboffs,
+			       req->oobbuf.out, req->ooblen);
+		}
+
+		engine_conf->src_oob_buf = (void *)req->oobbuf.out;
+		req->oobbuf.out = engine_conf->oob_buf;
+	}
+
+	/* This engine does not provide BBM/free OOB bytes protection */
+	if (!req->datalen)
+		return 0;
+
 	/* Preparation for page write: derive the ECC bytes and place them */
 	for (i = 0, data = req->databuf.out;
 	     eccsteps;
@@ -598,12 +622,36 @@  static int nand_ecc_sw_hamming_finish_io_req(struct nand_device *nand,
 	if (req->mode == MTD_OPS_RAW)
 		return 0;
 
-	/* This engine does not provide BBM/free OOB bytes protection */
-	if (!req->datalen)
-		return 0;
-
 	/* No more preparation for page write */
 	if (req->type == NAND_PAGE_WRITE) {
+		if (req->ooblen)
+			req->oobbuf.out = engine_conf->src_oob_buf;
+
+		nand_ecc_restore_req(&engine_conf->req_ctx, req);
+		return 0;
+	}
+
+	if (req->ooblen) {
+		memset(engine_conf->oob_buf, 0xff,
+		       nanddev_per_page_oobsize(nand));
+
+		if (req->mode == MTD_OPS_AUTO_OOB) {
+			ret = mtd_ooblayout_get_databytes(mtd,
+							  engine_conf->oob_buf,
+							  req->oobbuf.in,
+							  req->ooboffs,
+							  mtd->oobavail);
+			if (ret)
+				return ret;
+		} else {
+			memcpy(engine_conf->oob_buf,
+			       req->oobbuf.in + req->ooboffs, req->ooblen);
+		}
+	}
+
+	/* This engine does not provide BBM/free OOB bytes protection */
+	if (!req->datalen) {
+		req->oobbuf.in = engine_conf->oob_buf;
 		nand_ecc_restore_req(&engine_conf->req_ctx, req);
 		return 0;
 	}
@@ -633,6 +681,9 @@  static int nand_ecc_sw_hamming_finish_io_req(struct nand_device *nand,
 		}
 	}
 
+	if (req->ooblen)
+		req->oobbuf.in = engine_conf->oob_buf;
+
 	nand_ecc_restore_req(&engine_conf->req_ctx, req);
 
 	return max_bitflips;
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index c58f558302c4..9033036086f2 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -267,6 +267,12 @@  static int spinand_ondie_ecc_init_ctx(struct nand_device *nand)
 	if (!engine_conf)
 		return -ENOMEM;
 
+	engine_conf->oob_buf = kzalloc(nand->memorg.oobsize, GFP_KERNEL);
+	if (!engine_conf->oob_buf) {
+		kfree(engine_conf);
+		return -ENOMEM;
+	}
+
 	nand->ecc.ctx.priv = engine_conf;
 
 	if (spinand->eccinfo.ooblayout)
@@ -279,16 +285,40 @@  static int spinand_ondie_ecc_init_ctx(struct nand_device *nand)
 
 static void spinand_ondie_ecc_cleanup_ctx(struct nand_device *nand)
 {
-	kfree(nand->ecc.ctx.priv);
+	struct spinand_ondie_ecc_conf *engine_conf = nand->ecc.ctx.priv;
+
+	kfree(engine_conf->oob_buf);
+	kfree(engine_conf);
 }
 
 static int spinand_ondie_ecc_prepare_io_req(struct nand_device *nand,
 					    struct nand_page_io_req *req)
 {
+	struct spinand_ondie_ecc_conf *engine_conf = nand->ecc.ctx.priv;
 	struct spinand_device *spinand = nand_to_spinand(nand);
+	struct mtd_info *mtd = spinand_to_mtd(spinand);
 	bool enable = (req->mode != MTD_OPS_RAW);
+	int ret;
 
-	memset(spinand->oobbuf, 0xff, nanddev_per_page_oobsize(nand));
+	if (req->ooblen && req->type == NAND_PAGE_WRITE) {
+		memset(engine_conf->oob_buf, 0xff,
+		       nanddev_per_page_oobsize(nand));
+
+		if (req->mode == MTD_OPS_AUTO_OOB) {
+			ret = mtd_ooblayout_set_databytes(mtd, req->oobbuf.out,
+							  engine_conf->oob_buf,
+							  req->ooboffs,
+							  mtd->oobavail);
+			if (ret)
+				return ret;
+		} else {
+			memcpy(engine_conf->oob_buf + req->ooboffs,
+			       req->oobbuf.out, req->ooblen);
+		}
+
+		engine_conf->src_oob_buf = (void *)req->oobbuf.out;
+		req->oobbuf.out = engine_conf->oob_buf;
+	}
 
 	/* Only enable or disable the engine */
 	return spinand_ecc_enable(spinand, enable);
@@ -306,8 +336,32 @@  static int spinand_ondie_ecc_finish_io_req(struct nand_device *nand,
 		return 0;
 
 	/* Nothing to do when finishing a page write */
-	if (req->type == NAND_PAGE_WRITE)
+	if (req->type == NAND_PAGE_WRITE) {
+		if (req->ooblen)
+			req->oobbuf.out = engine_conf->src_oob_buf;
+
 		return 0;
+	}
+
+	if (req->ooblen) {
+		memset(engine_conf->oob_buf, 0xff,
+		       nanddev_per_page_oobsize(nand));
+
+		if (req->mode == MTD_OPS_AUTO_OOB) {
+			ret = mtd_ooblayout_get_databytes(mtd,
+							  engine_conf->oob_buf,
+							  req->oobbuf.in,
+							  req->ooboffs,
+							  mtd->oobavail);
+			if (ret)
+				return ret;
+		} else {
+			memcpy(engine_conf->oob_buf,
+			       req->oobbuf.in + req->ooboffs, req->ooblen);
+		}
+
+		req->oobbuf.in = engine_conf->oob_buf;
+	}
 
 	/* Finish a page read: check the status, report errors/bitflips */
 	ret = spinand_check_ecc_status(spinand, engine_conf->status);
@@ -360,7 +414,6 @@  static int spinand_read_from_cache_op(struct spinand_device *spinand,
 				      const struct nand_page_io_req *req)
 {
 	struct nand_device *nand = spinand_to_nand(spinand);
-	struct mtd_info *mtd = spinand_to_mtd(spinand);
 	struct spi_mem_dirmap_desc *rdesc;
 	unsigned int nbytes = 0;
 	void *buf = NULL;
@@ -403,16 +456,9 @@  static int spinand_read_from_cache_op(struct spinand_device *spinand,
 		memcpy(req->databuf.in, spinand->databuf + req->dataoffs,
 		       req->datalen);
 
-	if (req->ooblen) {
-		if (req->mode == MTD_OPS_AUTO_OOB)
-			mtd_ooblayout_get_databytes(mtd, req->oobbuf.in,
-						    spinand->oobbuf,
-						    req->ooboffs,
-						    req->ooblen);
-		else
-			memcpy(req->oobbuf.in, spinand->oobbuf + req->ooboffs,
-			       req->ooblen);
-	}
+	if (req->ooblen)
+		memcpy(req->oobbuf.in, spinand->oobbuf + req->ooboffs,
+		       req->ooblen);
 
 	return 0;
 }
@@ -421,7 +467,6 @@  static int spinand_write_to_cache_op(struct spinand_device *spinand,
 				     const struct nand_page_io_req *req)
 {
 	struct nand_device *nand = spinand_to_nand(spinand);
-	struct mtd_info *mtd = spinand_to_mtd(spinand);
 	struct spi_mem_dirmap_desc *wdesc;
 	unsigned int nbytes, column = 0;
 	void *buf = spinand->databuf;
@@ -433,27 +478,17 @@  static int spinand_write_to_cache_op(struct spinand_device *spinand,
 	 * must fill the page cache entirely even if we only want to program
 	 * the data portion of the page, otherwise we might corrupt the BBM or
 	 * user data previously programmed in OOB area.
-	 *
-	 * Only reset the data buffer manually, the OOB buffer is prepared by
-	 * ECC engines ->prepare_io_req() callback.
 	 */
 	nbytes = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
-	memset(spinand->databuf, 0xff, nanddev_page_size(nand));
+	memset(spinand->databuf, 0xff, nbytes);
 
 	if (req->datalen)
 		memcpy(spinand->databuf + req->dataoffs, req->databuf.out,
 		       req->datalen);
 
-	if (req->ooblen) {
-		if (req->mode == MTD_OPS_AUTO_OOB)
-			mtd_ooblayout_set_databytes(mtd, req->oobbuf.out,
-						    spinand->oobbuf,
-						    req->ooboffs,
-						    req->ooblen);
-		else
-			memcpy(spinand->oobbuf + req->ooboffs, req->oobbuf.out,
-			       req->ooblen);
-	}
+	if (req->ooblen)
+		memcpy(spinand->oobbuf + req->ooboffs, req->oobbuf.out,
+		       req->ooblen);
 
 	if (req->mode == MTD_OPS_RAW)
 		wdesc = spinand->dirmaps[req->pos.plane].wdesc;
diff --git a/include/linux/mtd/nand-ecc-sw-bch.h b/include/linux/mtd/nand-ecc-sw-bch.h
index 9da9969505a8..c4730badb77b 100644
--- a/include/linux/mtd/nand-ecc-sw-bch.h
+++ b/include/linux/mtd/nand-ecc-sw-bch.h
@@ -18,6 +18,8 @@ 
  * @code_size: Number of bytes needed to store a code (one code per step)
  * @calc_buf: Buffer to use when calculating ECC bytes
  * @code_buf: Buffer to use when reading (raw) ECC bytes from the chip
+ * @oob_buf: Buffer to use when handle the data in OOB area.
+ * @src_oob_buf: Pointer be used to store source OOB buffer pointer when write
  * @bch: BCH control structure
  * @errloc: error location array
  * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid
@@ -27,6 +29,8 @@  struct nand_ecc_sw_bch_conf {
 	unsigned int code_size;
 	u8 *calc_buf;
 	u8 *code_buf;
+	u8 *oob_buf;
+	void *src_oob_buf;
 	struct bch_control *bch;
 	unsigned int *errloc;
 	unsigned char *eccmask;
diff --git a/include/linux/mtd/nand-ecc-sw-hamming.h b/include/linux/mtd/nand-ecc-sw-hamming.h
index c6c71894c575..88788d53b911 100644
--- a/include/linux/mtd/nand-ecc-sw-hamming.h
+++ b/include/linux/mtd/nand-ecc-sw-hamming.h
@@ -19,6 +19,8 @@ 
  * @code_size: Number of bytes needed to store a code (one code per step)
  * @calc_buf: Buffer to use when calculating ECC bytes
  * @code_buf: Buffer to use when reading (raw) ECC bytes from the chip
+ * @oob_buf: Buffer to use when handle the data in OOB area.
+ * @src_oob_buf: Pointer be used to store source OOB buffer pointer when write
  * @sm_order: Smart Media special ordering
  */
 struct nand_ecc_sw_hamming_conf {
@@ -26,6 +28,8 @@  struct nand_ecc_sw_hamming_conf {
 	unsigned int code_size;
 	u8 *calc_buf;
 	u8 *code_buf;
+	u8 *oob_buf;
+	void *src_oob_buf;
 	unsigned int sm_order;
 };
 
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index 3aa28240a77f..23b86941fbf6 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -312,9 +312,13 @@  struct spinand_ecc_info {
  * struct spinand_ondie_ecc_conf - private SPI-NAND on-die ECC engine structure
  * @status: status of the last wait operation that will be used in case
  *          ->get_status() is not populated by the spinand device.
+ * @oob_buf: Buffer to use when handle the data in OOB area.
+ * @src_oob_buf: Pointer be used to store source OOB buffer pointer when write
  */
 struct spinand_ondie_ecc_conf {
 	u8 status;
+	u8 *oob_buf;
+	void *src_oob_buf;
 };
 
 /**