diff mbox

[v3,1/2] mtd: nand: pxa3xx: Fix PIO FIFO draining

Message ID 1424091072-7738-2-git-send-email-maxime.ripard@free-electrons.com
State Superseded
Headers show

Commit Message

Maxime Ripard Feb. 16, 2015, 12:51 p.m. UTC
The NDDB register holds the data that are needed by the read and write
commands.

However, during a read PIO access, the datasheet specifies that after each 32
bits read in that register, when BCH is enabled, we have to make sure that the
RDDREQ bit is set in the NDSR register.

This fixes an issue that was seen on the Armada 385, and presumably other mvebu
SoCs, when a read on a newly erased page would end up in the driver reporting a
timeout from the NAND.

Cc: <stable@vger.kernel.org> # v3.14
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/mtd/nand/pxa3xx_nand.c | 47 ++++++++++++++++++++++++++++++++++++------
 1 file changed, 41 insertions(+), 6 deletions(-)

Comments

Boris Brezillon Feb. 16, 2015, 1:34 p.m. UTC | #1
Hi Maxime,

On Mon, 16 Feb 2015 13:51:11 +0100
Maxime Ripard <maxime.ripard@free-electrons.com> wrote:

> The NDDB register holds the data that are needed by the read and write
> commands.
> 
> However, during a read PIO access, the datasheet specifies that after each 32
> bits read in that register, when BCH is enabled, we have to make sure that the
> RDDREQ bit is set in the NDSR register.
> 
> This fixes an issue that was seen on the Armada 385, and presumably other mvebu
> SoCs, when a read on a newly erased page would end up in the driver reporting a
> timeout from the NAND.
> 
> Cc: <stable@vger.kernel.org> # v3.14
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  drivers/mtd/nand/pxa3xx_nand.c | 47 ++++++++++++++++++++++++++++++++++++------
>  1 file changed, 41 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
> index 96b0b1d27df1..b2d8d6960765 100644
> --- a/drivers/mtd/nand/pxa3xx_nand.c
> +++ b/drivers/mtd/nand/pxa3xx_nand.c
> @@ -480,6 +480,41 @@ static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
>  	nand_writel(info, NDCR, ndcr | int_mask);
>  }
>  
> +static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
> +{
> +	if (info->ecc_bch) {
> +		int index = 0;
> +
> +		while (index < (len * 4)) {
> +			u32 timeout;
> +
> +			__raw_readsl(info->mmio_base + NDDB, data + index, 8);
> +

Shouldn't you break here if you've read all the data you were
expecting ?
As I said in my previous review, I don't know what's happening if you
wait for RDDREQ when the FIFO has been fully drained.

> +			/*
> +			 * According to the datasheet, when reading
> +			 * from NDDB with BCH enabled, after each 32
> +			 * bytes reads, we have to make sure that the
> +			 * NDSR.RDDREQ bit is set
> +			 */
> +			for (timeout = 0;
> +			     !(nand_readl(info, NDSR) & NDSR_RDDREQ);
> +			     timeout++) {
> +				if (timeout >= 5) {
> +					dev_err(&info->pdev->dev,
> +						"Timeout on RDDREQ while draining the FIFO\n");
> +					return;
> +				}
> +
> +				mdelay(1);
> +			}
> +
> +			index += 32;
> +		}
> +	} else {
> +		__raw_readsl(info->mmio_base + NDDB, data, len);
> +	}
> +}

Best Regards,

Boris
Ezequiel Garcia Feb. 16, 2015, 1:35 p.m. UTC | #2
On 02/16/2015 09:51 AM, Maxime Ripard wrote:
> The NDDB register holds the data that are needed by the read and write
> commands.
> 
> However, during a read PIO access, the datasheet specifies that after each 32
> bits read in that register, when BCH is enabled, we have to make sure that the
> RDDREQ bit is set in the NDSR register.
> 

Typo s/32 bits/32 bytes
Thomas Petazzoni Feb. 16, 2015, 4:27 p.m. UTC | #3
Dear Maxime Ripard,

On Mon, 16 Feb 2015 13:51:11 +0100, Maxime Ripard wrote:

> +		while (index < (len * 4)) {
> +			u32 timeout;
> +
> +			__raw_readsl(info->mmio_base + NDDB, data + index, 8);

Are you guaranteed that 'len' is a multiple of 32 bytes?

Thomas
Maxime Ripard Feb. 16, 2015, 4:41 p.m. UTC | #4
On Mon, Feb 16, 2015 at 05:27:53PM +0100, Thomas Petazzoni wrote:
> Dear Maxime Ripard,
> 
> On Mon, 16 Feb 2015 13:51:11 +0100, Maxime Ripard wrote:
> 
> > +		while (index < (len * 4)) {
> > +			u32 timeout;
> > +
> > +			__raw_readsl(info->mmio_base + NDDB, data + index, 8);
> 
> Are you guaranteed that 'len' is a multiple of 32 bytes?

I don't know if you're guaranteed of anything, but the controller
supports only 512, 2k, 4k and 8k pages, which are all mutiples of 32
bytes.

Maxime
Maxime Ripard Feb. 16, 2015, 4:49 p.m. UTC | #5
On Mon, Feb 16, 2015 at 10:35:50AM -0300, Ezequiel Garcia wrote:
> On 02/16/2015 09:51 AM, Maxime Ripard wrote:
> > The NDDB register holds the data that are needed by the read and write
> > commands.
> > 
> > However, during a read PIO access, the datasheet specifies that after each 32
> > bits read in that register, when BCH is enabled, we have to make sure that the
> > RDDREQ bit is set in the NDSR register.
> > 
> 
> Typo s/32 bits/32 bytes

Good catch, thanks!
Maxime
Ezequiel Garcia Feb. 16, 2015, 4:57 p.m. UTC | #6
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

On 02/16/2015 01:41 PM, Maxime Ripard wrote:
> On Mon, Feb 16, 2015 at 05:27:53PM +0100, Thomas Petazzoni wrote:
>> Dear Maxime Ripard,
>>
>> On Mon, 16 Feb 2015 13:51:11 +0100, Maxime Ripard wrote:
>>
>>> +		while (index < (len * 4)) {
>>> +			u32 timeout;
>>> +
>>> +			__raw_readsl(info->mmio_base + NDDB, data + index, 8);
>>
>> Are you guaranteed that 'len' is a multiple of 32 bytes?
> 
> I don't know if you're guaranteed of anything, but the controller
> supports only 512, 2k, 4k and 8k pages, which are all mutiples of 32
> bytes.
> 

'len' here comes from:

do_bytes = min(info->data_size, info->chunk_size);

and

DIV_ROUND_UP(do_bytes, 4)

Where chunk_size is the size we want to read/write in each command step
(keep in mind that with extended commands we issue multiple commands, and
read/write data in chunks for each page).

And data_size is initialized at mtd->writesize (i.e. the size of a page).

Given all the flash pages I'm aware of are multiples of 32-bytes, and
given a chunk is either a quarter or half a page... I'd say it's
guaranteed to be 32-byte multiple, but perhaps it's a good idea to enforce it.
- -- 
Ezequiel GarcĂ­a, Free Electrons
Embedded Linux, Kernel and Android Engineering
http://free-electrons.com
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQIcBAEBCAAGBQJU4iFfAAoJEIOKbhOEIHKiOuMQAK0yiPyjBKRcqX8qrpG9Ljcq
JVhJTjn7VdiWhoh0n9BOt5bV3K0wAvcbt3LZvpGwf1EOifBaZB+f2QskZNLDUXyC
JXPaonbdUqabEU0n9frduc9xBgbPhrwL4X0RzbJ0xZ+A2FrPt/80qUe8lsDmykH9
dyl3FOL3EQQiQ83D1VefkYbeDjaunvhA7Lfi7CcdPSFRv1FE47NQUW/8OjvZVczx
uPcvdNj4818aXtFyOJQbR9xWOhVh7nxPlU8flHZPHuJ5WVCGWBbt++/4vmK+LZkv
aZQ8W6dGiKI3ayT+PQ7nsETmoXZcjWTihq+nW+Ie2vs5PZf1iME5RYarLSKsc0Ac
4GjLnd4+0H3jeInvJ0MLw0dhkYM4PLkzp4CPo4vrH8z5F3cLXxaRkZYuv7gChden
C2VITr9C8p1OSQJ2mF8m9gWdExkEuuy7q6vURx74C4KaeQA2R4ARAROm85o6JtmN
dhozZIFrJQGwGuB5+7MI3yJj4OpFsBkxoq6U1JNDTwYnu3SnMOdwvq9kwqGXgR2I
yQlu6MO6DYHkMtmw//kkqX+vnyhGexrFoesOyG4d40mOgyGYqyk+oadV7pNh2g5Y
nXGr21Li80N65Sk+RaOFlvmIPaQ45Xn6gS7ckHcVVCZI9HAu87n5n15HEtfaj4Dc
r9FkTUgw9cqcio5EVfEs
=nkDF
-----END PGP SIGNATURE-----
Robert Jarzmik Feb. 16, 2015, 8:11 p.m. UTC | #7
Maxime Ripard <maxime.ripard@free-electrons.com> writes:

>  drivers/mtd/nand/pxa3xx_nand.c | 47 ++++++++++++++++++++++++++++++++++++------
>  1 file changed, 41 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
> index 96b0b1d27df1..b2d8d6960765 100644
> --- a/drivers/mtd/nand/pxa3xx_nand.c
> +++ b/drivers/mtd/nand/pxa3xx_nand.c
> @@ -480,6 +480,41 @@ static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
>  	nand_writel(info, NDCR, ndcr | int_mask);
>  }
>  
> +static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
> +{
> +	if (info->ecc_bch) {
> +		int index = 0;
> +
> +		while (index < (len * 4)) {
> +			u32 timeout;
> +
> +			__raw_readsl(info->mmio_base + NDDB, data + index, 8);
> +
> +			/*
> +			 * According to the datasheet, when reading
> +			 * from NDDB with BCH enabled, after each 32
> +			 * bytes reads, we have to make sure that the
> +			 * NDSR.RDDREQ bit is set
> +			 */
> +			for (timeout = 0;
> +			     !(nand_readl(info, NDSR) & NDSR_RDDREQ);
> +			     timeout++) {
> +				if (timeout >= 5) {
> +					dev_err(&info->pdev->dev,
> +						"Timeout on RDDREQ while draining the FIFO\n");
> +					return;
> +				}
> +
> +				mdelay(1);
So in worst case, we'll end up with 4 times mdelay(1) times len / 32.
For a 2048 page, it is : 256ms where everything is stuck (mdelay and not
msleep).

I know you had no choice because this is called from interrupt handler (top
half). But having a irq handler and a irq thread handler would solve that issue,
and you'll end up with msleep(1) in this code.

I don't think an mdelay(256) is acceptable.

Cheers.

--
Robert
Maxime Ripard Feb. 16, 2015, 8:58 p.m. UTC | #8
Hi Robert,

On Mon, Feb 16, 2015 at 09:11:24PM +0100, Robert Jarzmik wrote:
> Maxime Ripard <maxime.ripard@free-electrons.com> writes:
> 
> >  drivers/mtd/nand/pxa3xx_nand.c | 47 ++++++++++++++++++++++++++++++++++++------
> >  1 file changed, 41 insertions(+), 6 deletions(-)
> >
> > diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
> > index 96b0b1d27df1..b2d8d6960765 100644
> > --- a/drivers/mtd/nand/pxa3xx_nand.c
> > +++ b/drivers/mtd/nand/pxa3xx_nand.c
> > @@ -480,6 +480,41 @@ static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
> >  	nand_writel(info, NDCR, ndcr | int_mask);
> >  }
> >  
> > +static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
> > +{
> > +	if (info->ecc_bch) {
> > +		int index = 0;
> > +
> > +		while (index < (len * 4)) {
> > +			u32 timeout;
> > +
> > +			__raw_readsl(info->mmio_base + NDDB, data + index, 8);
> > +
> > +			/*
> > +			 * According to the datasheet, when reading
> > +			 * from NDDB with BCH enabled, after each 32
> > +			 * bytes reads, we have to make sure that the
> > +			 * NDSR.RDDREQ bit is set
> > +			 */
> > +			for (timeout = 0;
> > +			     !(nand_readl(info, NDSR) & NDSR_RDDREQ);
> > +			     timeout++) {
> > +				if (timeout >= 5) {
> > +					dev_err(&info->pdev->dev,
> > +						"Timeout on RDDREQ while draining the FIFO\n");
> > +					return;
> > +				}
> > +
> > +				mdelay(1);
> So in worst case, we'll end up with 4 times mdelay(1) times len / 32.
> For a 2048 page, it is : 256ms where everything is stuck (mdelay and not
> msleep).
> 
> I know you had no choice because this is called from interrupt handler (top
> half). But having a irq handler and a irq thread handler would solve that issue,
> and you'll end up with msleep(1) in this code.
> 
> I don't think an mdelay(256) is acceptable.

That's very true that this driver would need some love, but
valentine's day was last week.

I'm sorry, but this is a patch targeted for stable. This is a pure
bugfix. I won't rewrite the whole driver solely to make the driver
better, especially since that would make such a patch (or more likely
a whole serie) unsuitable for stable.

Maxime
Maxime Ripard Feb. 17, 2015, 10:29 a.m. UTC | #9
On Mon, Feb 16, 2015 at 01:57:12PM -0300, Ezequiel Garcia wrote:
> On 02/16/2015 01:41 PM, Maxime Ripard wrote:
> > On Mon, Feb 16, 2015 at 05:27:53PM +0100, Thomas Petazzoni wrote:
> >> Dear Maxime Ripard,
> >>
> >> On Mon, 16 Feb 2015 13:51:11 +0100, Maxime Ripard wrote:
> >>
> >>> +		while (index < (len * 4)) {
> >>> +			u32 timeout;
> >>> +
> >>> +			__raw_readsl(info->mmio_base + NDDB, data + index, 8);
> >>
> >> Are you guaranteed that 'len' is a multiple of 32 bytes?
> > 
> > I don't know if you're guaranteed of anything, but the controller
> > supports only 512, 2k, 4k and 8k pages, which are all mutiples of 32
> > bytes.
> > 
> 
> 'len' here comes from:
> 
> do_bytes = min(info->data_size, info->chunk_size);
> 
> and
> 
> DIV_ROUND_UP(do_bytes, 4)
> 
> Where chunk_size is the size we want to read/write in each command step
> (keep in mind that with extended commands we issue multiple commands, and
> read/write data in chunks for each page).
> 
> And data_size is initialized at mtd->writesize (i.e. the size of a page).
> 
> Given all the flash pages I'm aware of are multiples of 32-bytes, and
> given a chunk is either a quarter or half a page... I'd say it's
> guaranteed to be 32-byte multiple, but perhaps it's a good idea to enforce it.

I've fixed the function to both support non-aligned reading, just in
case, and to not poll on the last chunk, as Boris suggested.

Thanks!
Maxime
diff mbox

Patch

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 96b0b1d27df1..b2d8d6960765 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -480,6 +480,41 @@  static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
 	nand_writel(info, NDCR, ndcr | int_mask);
 }
 
+static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
+{
+	if (info->ecc_bch) {
+		int index = 0;
+
+		while (index < (len * 4)) {
+			u32 timeout;
+
+			__raw_readsl(info->mmio_base + NDDB, data + index, 8);
+
+			/*
+			 * According to the datasheet, when reading
+			 * from NDDB with BCH enabled, after each 32
+			 * bytes reads, we have to make sure that the
+			 * NDSR.RDDREQ bit is set
+			 */
+			for (timeout = 0;
+			     !(nand_readl(info, NDSR) & NDSR_RDDREQ);
+			     timeout++) {
+				if (timeout >= 5) {
+					dev_err(&info->pdev->dev,
+						"Timeout on RDDREQ while draining the FIFO\n");
+					return;
+				}
+
+				mdelay(1);
+			}
+
+			index += 32;
+		}
+	} else {
+		__raw_readsl(info->mmio_base + NDDB, data, len);
+	}
+}
+
 static void handle_data_pio(struct pxa3xx_nand_info *info)
 {
 	unsigned int do_bytes = min(info->data_size, info->chunk_size);
@@ -496,14 +531,14 @@  static void handle_data_pio(struct pxa3xx_nand_info *info)
 				      DIV_ROUND_UP(info->oob_size, 4));
 		break;
 	case STATE_PIO_READING:
-		__raw_readsl(info->mmio_base + NDDB,
-			     info->data_buff + info->data_buff_pos,
-			     DIV_ROUND_UP(do_bytes, 4));
+		drain_fifo(info,
+			   info->data_buff + info->data_buff_pos,
+			   DIV_ROUND_UP(do_bytes, 4));
 
 		if (info->oob_size > 0)
-			__raw_readsl(info->mmio_base + NDDB,
-				     info->oob_buff + info->oob_buff_pos,
-				     DIV_ROUND_UP(info->oob_size, 4));
+			drain_fifo(info,
+				   info->oob_buff + info->oob_buff_pos,
+				   DIV_ROUND_UP(info->oob_size, 4));
 		break;
 	default:
 		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,