Subject: [PATCH v4 1/2] mtd: rawnand: fsl_ifc: fix FSL NAND driver to read all ONFI parameter pages

Per ONFI specification (Rev. 4.0), if the CRC of the first parameter page
read is not valid, the host should read redundant parameter page copies.
Fix FSL NAND driver to read the two redundant copies which are mandatory
in the specification.

Signed-off-by: Jane Wan <[email protected]>
---
drivers/mtd/nand/raw/fsl_ifc_nand.c | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c
index 61aae02..98aac1f 100644
--- a/drivers/mtd/nand/raw/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c
@@ -342,9 +342,16 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,

case NAND_CMD_READID:
case NAND_CMD_PARAM: {
+ /*
+ * For READID, read 8 bytes that are currently used.
+ * For PARAM, read all 3 copies of 256-bytes pages.
+ */
+ int len = 8;
int timing = IFC_FIR_OP_RB;
- if (command == NAND_CMD_PARAM)
+ if (command == NAND_CMD_PARAM) {
timing = IFC_FIR_OP_RBCD;
+ len = 256 * 3;
+ }

ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
(IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) |
@@ -354,12 +361,8 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
&ifc->ifc_nand.nand_fcr0);
ifc_out32(column, &ifc->ifc_nand.row3);

- /*
- * although currently it's 8 bytes for READID, we always read
- * the maximum 256 bytes(for PARAM)
- */
- ifc_out32(256, &ifc->ifc_nand.nand_fbcr);
- ifc_nand_ctrl->read_bytes = 256;
+ ifc_out32(len, &ifc->ifc_nand.nand_fbcr);
+ ifc_nand_ctrl->read_bytes = len;

set_addr(mtd, 0, 0, 0);
fsl_ifc_run_command(mtd);
--
1.7.9.5



Subject: [PATCH v4 2/2] mtd: rawnand: use bit-wise majority to recover the contents of ONFI parameter

Per ONFI specification (Rev. 4.0), if all parameter pages have invalid
CRC values, the bit-wise majority may be used to recover the contents of
the parameter pages from the parameter page copies present.

Signed-off-by: Jane Wan <[email protected]>
---
drivers/mtd/nand/raw/nand_base.c | 49 ++++++++++++++++++++++++++++++++++----
1 file changed, 44 insertions(+), 5 deletions(-)

diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 72f3a89..dfc341c 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -5086,6 +5086,38 @@ static int nand_flash_detect_ext_param_page(struct nand_chip *chip,
return ret;
}

+#define GET_BIT(bit, val) (((val) >> (bit)) & 0x01)
+
+/*
+ * Recover NAND parameter page with bit-wise majority
+ */
+static int onfi_recover_param(struct nand_onfi_params *p, int pages)
+{
+ int i, j, k;
+ u8 v, m;
+ u8 *buf;
+
+ buf = (u8 *)p;
+ for (i = 0; i < sizeof(*p); i++) {
+ v = 0;
+ for (j = 0; j < 8; j++) {
+ m = 0;
+ for (k = 0; k < pages; k++)
+ m += GET_BIT(j, buf[k*sizeof(*p) + i]);
+ if (m > pages/2)
+ v |= BIT(j);
+ }
+ ((u8 *)p)[i] = v;
+ }
+
+ if (onfi_crc16(ONFI_CRC_BASE, (u8 *)p, 254) ==
+ le16_to_cpu(p->crc)) {
+ return 1;
+ }
+
+ return 0;
+}
+
/*
* Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
*/
@@ -5102,7 +5134,7 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
return 0;

/* ONFI chip: allocate a buffer to hold its parameter page */
- p = kzalloc(sizeof(*p), GFP_KERNEL);
+ p = kzalloc((sizeof(*p) * 3), GFP_KERNEL);
if (!p)
return -ENOMEM;

@@ -5113,21 +5145,28 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
}

for (i = 0; i < 3; i++) {
- ret = nand_read_data_op(chip, p, sizeof(*p), true);
+ ret = nand_read_data_op(chip, &p[i], sizeof(*p), true);
if (ret) {
ret = 0;
goto free_onfi_param_page;
}

- if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
+ if (onfi_crc16(ONFI_CRC_BASE, (u8 *)&p[i], 254) ==
le16_to_cpu(p->crc)) {
+ if (i)
+ memcpy(p, &p[i], sizeof(*p));
break;
}
}

if (i == 3) {
- pr_err("Could not find valid ONFI parameter page; aborting\n");
- goto free_onfi_param_page;
+ pr_err("Could not find valid ONFI parameter page\n");
+ pr_info("Recover ONFI params with bit-wise majority\n");
+ ret = onfi_recover_param(p, 3);
+ if (ret == 0) {
+ pr_err("ONFI parameter recovery failed, aborting\n");
+ goto free_onfi_param_page;
+ }
}

/* Check version */
--
1.7.9.5


2018-05-09 14:13:21

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v4 1/2] mtd: rawnand: fsl_ifc: fix FSL NAND driver to read all ONFI parameter pages

On Tue, 8 May 2018 14:19:53 -0700
Jane Wan <[email protected]> wrote:

> Per ONFI specification (Rev. 4.0), if the CRC of the first parameter page
> read is not valid, the host should read redundant parameter page copies.
> Fix FSL NAND driver to read the two redundant copies which are mandatory
> in the specification.
>
> Signed-off-by: Jane Wan <[email protected]>

I'm gonna take this patch, but I'd like to make it clear: this is the
last time I accept fixes touching fsl_ifc_cmdfunc() for bugs that could
have been addressed by switching to ->exec_op() (note that I had a look
at a freescale datasheet, and I'm now 100% sure this driver can be
converted to ->exec_op()).

> ---
> drivers/mtd/nand/raw/fsl_ifc_nand.c | 17 ++++++++++-------
> 1 file changed, 10 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c
> index 61aae02..98aac1f 100644
> --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c
> +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c
> @@ -342,9 +342,16 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
>
> case NAND_CMD_READID:
> case NAND_CMD_PARAM: {
> + /*
> + * For READID, read 8 bytes that are currently used.
> + * For PARAM, read all 3 copies of 256-bytes pages.
> + */
> + int len = 8;
> int timing = IFC_FIR_OP_RB;
> - if (command == NAND_CMD_PARAM)
> + if (command == NAND_CMD_PARAM) {
> timing = IFC_FIR_OP_RBCD;
> + len = 256 * 3;
> + }
>
> ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
> (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) |
> @@ -354,12 +361,8 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
> &ifc->ifc_nand.nand_fcr0);
> ifc_out32(column, &ifc->ifc_nand.row3);
>
> - /*
> - * although currently it's 8 bytes for READID, we always read
> - * the maximum 256 bytes(for PARAM)
> - */
> - ifc_out32(256, &ifc->ifc_nand.nand_fbcr);
> - ifc_nand_ctrl->read_bytes = 256;
> + ifc_out32(len, &ifc->ifc_nand.nand_fbcr);
> + ifc_nand_ctrl->read_bytes = len;
>
> set_addr(mtd, 0, 0, 0);
> fsl_ifc_run_command(mtd);


2018-05-09 14:20:09

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v4 2/2] mtd: rawnand: use bit-wise majority to recover the contents of ONFI parameter

On Tue, 8 May 2018 14:19:54 -0700
Jane Wan <[email protected]> wrote:

> Per ONFI specification (Rev. 4.0), if all parameter pages have invalid
> CRC values, the bit-wise majority may be used to recover the contents of
> the parameter pages from the parameter page copies present.
>
> Signed-off-by: Jane Wan <[email protected]>
> ---
> drivers/mtd/nand/raw/nand_base.c | 49 ++++++++++++++++++++++++++++++++++----
> 1 file changed, 44 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
> index 72f3a89..dfc341c 100644
> --- a/drivers/mtd/nand/raw/nand_base.c
> +++ b/drivers/mtd/nand/raw/nand_base.c
> @@ -5086,6 +5086,38 @@ static int nand_flash_detect_ext_param_page(struct nand_chip *chip,
> return ret;
> }
>
> +#define GET_BIT(bit, val) (((val) >> (bit)) & 0x01)
> +
> +/*
> + * Recover NAND parameter page with bit-wise majority
> + */
> +static int onfi_recover_param(struct nand_onfi_params *p, int pages)

I had something more generic in mind:

static void nand_bit_wise_majority(const void **srcbufs,
void *dstbuf,
unsigned int nbufs,
unsigned int bufsize)
{
...
}

And then you do the crc check in nand_flash_detect_onfi().

The reason I'm asking that is because I'm almost sure we'll re-use this
functions for extended param pages, and also for vendor specific data
(we already have a byte-wise majority check in the hynix driver, so I
wouldn't be surprised if other vendors decided to use a bit-wise
approach for some of their OTP area).

> +{
> + int i, j, k;
> + u8 v, m;
> + u8 *buf;
> +
> + buf = (u8 *)p;
> + for (i = 0; i < sizeof(*p); i++) {
> + v = 0;
> + for (j = 0; j < 8; j++) {
> + m = 0;
> + for (k = 0; k < pages; k++)
> + m += GET_BIT(j, buf[k*sizeof(*p) + i]);
> + if (m > pages/2)
> + v |= BIT(j);
> + }
> + ((u8 *)p)[i] = v;
> + }
> +
> + if (onfi_crc16(ONFI_CRC_BASE, (u8 *)p, 254) ==
> + le16_to_cpu(p->crc)) {
> + return 1;
> + }
> +
> + return 0;
> +}
> +
> /*
> * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
> */
> @@ -5102,7 +5134,7 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
> return 0;
>
> /* ONFI chip: allocate a buffer to hold its parameter page */
> - p = kzalloc(sizeof(*p), GFP_KERNEL);
> + p = kzalloc((sizeof(*p) * 3), GFP_KERNEL);
> if (!p)
> return -ENOMEM;
>
> @@ -5113,21 +5145,28 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
> }
>
> for (i = 0; i < 3; i++) {
> - ret = nand_read_data_op(chip, p, sizeof(*p), true);
> + ret = nand_read_data_op(chip, &p[i], sizeof(*p), true);
> if (ret) {
> ret = 0;
> goto free_onfi_param_page;
> }
>
> - if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
> + if (onfi_crc16(ONFI_CRC_BASE, (u8 *)&p[i], 254) ==
> le16_to_cpu(p->crc)) {
> + if (i)
> + memcpy(p, &p[i], sizeof(*p));
> break;
> }
> }
>
> if (i == 3) {
> - pr_err("Could not find valid ONFI parameter page; aborting\n");
> - goto free_onfi_param_page;
> + pr_err("Could not find valid ONFI parameter page\n");
> + pr_info("Recover ONFI params with bit-wise majority\n");
> + ret = onfi_recover_param(p, 3);
> + if (ret == 0) {
> + pr_err("ONFI parameter recovery failed, aborting\n");
> + goto free_onfi_param_page;
> + }
> }
>
> /* Check version */


2018-05-09 15:56:58

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v4 1/2] mtd: rawnand: fsl_ifc: fix FSL NAND driver to read all ONFI parameter pages

On Tue, 8 May 2018 14:19:53 -0700
Jane Wan <[email protected]> wrote:

> Per ONFI specification (Rev. 4.0), if the CRC of the first parameter page
> read is not valid, the host should read redundant parameter page copies.
> Fix FSL NAND driver to read the two redundant copies which are mandatory
> in the specification.
>
> Signed-off-by: Jane Wan <[email protected]>

Applied.

Thanks,

Boris

> ---
> drivers/mtd/nand/raw/fsl_ifc_nand.c | 17 ++++++++++-------
> 1 file changed, 10 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c
> index 61aae02..98aac1f 100644
> --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c
> +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c
> @@ -342,9 +342,16 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
>
> case NAND_CMD_READID:
> case NAND_CMD_PARAM: {
> + /*
> + * For READID, read 8 bytes that are currently used.
> + * For PARAM, read all 3 copies of 256-bytes pages.
> + */
> + int len = 8;
> int timing = IFC_FIR_OP_RB;
> - if (command == NAND_CMD_PARAM)
> + if (command == NAND_CMD_PARAM) {
> timing = IFC_FIR_OP_RBCD;
> + len = 256 * 3;
> + }
>
> ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |
> (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) |
> @@ -354,12 +361,8 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,
> &ifc->ifc_nand.nand_fcr0);
> ifc_out32(column, &ifc->ifc_nand.row3);
>
> - /*
> - * although currently it's 8 bytes for READID, we always read
> - * the maximum 256 bytes(for PARAM)
> - */
> - ifc_out32(256, &ifc->ifc_nand.nand_fbcr);
> - ifc_nand_ctrl->read_bytes = 256;
> + ifc_out32(len, &ifc->ifc_nand.nand_fbcr);
> + ifc_nand_ctrl->read_bytes = len;
>
> set_addr(mtd, 0, 0, 0);
> fsl_ifc_run_command(mtd);


Subject: RE: [PATCH v4 2/2] mtd: rawnand: use bit-wise majority to recover the contents of ONFI parameter

Hi Boris,

I've sent v5 of the patch based on your comment.

Thanks.
Jane

> -----Original Message-----
> From: Boris Brezillon [mailto:[email protected]]
> Sent: Wednesday, May 09, 2018 7:19 AM
> To: Wan, Jane (Nokia - US/Sunnyvale) <[email protected]>
> Cc: [email protected]; [email protected];
> [email protected]; [email protected]; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected]; linux-
> [email protected]; Bos, Ties (Nokia - US/Sunnyvale) <[email protected]>
> Subject: Re: [PATCH v4 2/2] mtd: rawnand: use bit-wise majority to recover the
> contents of ONFI parameter
>
> On Tue, 8 May 2018 14:19:54 -0700
> Jane Wan <[email protected]> wrote:
>
> > Per ONFI specification (Rev. 4.0), if all parameter pages have invalid
> > CRC values, the bit-wise majority may be used to recover the contents
> > of the parameter pages from the parameter page copies present.
> >
> > Signed-off-by: Jane Wan <[email protected]>
> > ---
> > drivers/mtd/nand/raw/nand_base.c | 49
> ++++++++++++++++++++++++++++++++++----
> > 1 file changed, 44 insertions(+), 5 deletions(-)
> >
> > diff --git a/drivers/mtd/nand/raw/nand_base.c
> > b/drivers/mtd/nand/raw/nand_base.c
> > index 72f3a89..dfc341c 100644
> > --- a/drivers/mtd/nand/raw/nand_base.c
> > +++ b/drivers/mtd/nand/raw/nand_base.c
> > @@ -5086,6 +5086,38 @@ static int
> nand_flash_detect_ext_param_page(struct nand_chip *chip,
> > return ret;
> > }
> >
> > +#define GET_BIT(bit, val) (((val) >> (bit)) & 0x01)
> > +
> > +/*
> > + * Recover NAND parameter page with bit-wise majority */ static int
> > +onfi_recover_param(struct nand_onfi_params *p, int pages)
>
> I had something more generic in mind:
>
> static void nand_bit_wise_majority(const void **srcbufs,
> void *dstbuf,
> unsigned int nbufs,
> unsigned int bufsize)
> {
> ...
> }
>
> And then you do the crc check in nand_flash_detect_onfi().
>
> The reason I'm asking that is because I'm almost sure we'll re-use this functions
> for extended param pages, and also for vendor specific data (we already have a
> byte-wise majority check in the hynix driver, so I wouldn't be surprised if other
> vendors decided to use a bit-wise approach for some of their OTP area).

[Jane] Modified the function as suggested in v5.

>
> > +{
> > + int i, j, k;
> > + u8 v, m;
> > + u8 *buf;
> > +
> > + buf = (u8 *)p;
> > + for (i = 0; i < sizeof(*p); i++) {
> > + v = 0;
> > + for (j = 0; j < 8; j++) {
> > + m = 0;
> > + for (k = 0; k < pages; k++)
> > + m += GET_BIT(j, buf[k*sizeof(*p) + i]);
> > + if (m > pages/2)
> > + v |= BIT(j);
> > + }
> > + ((u8 *)p)[i] = v;
> > + }
> > +
> > + if (onfi_crc16(ONFI_CRC_BASE, (u8 *)p, 254) ==
> > + le16_to_cpu(p->crc)) {
> > + return 1;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > /*
> > * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
> > */
> > @@ -5102,7 +5134,7 @@ static int nand_flash_detect_onfi(struct nand_chip
> *chip)
> > return 0;
> >
> > /* ONFI chip: allocate a buffer to hold its parameter page */
> > - p = kzalloc(sizeof(*p), GFP_KERNEL);
> > + p = kzalloc((sizeof(*p) * 3), GFP_KERNEL);
> > if (!p)
> > return -ENOMEM;
> >
> > @@ -5113,21 +5145,28 @@ static int nand_flash_detect_onfi(struct
> nand_chip *chip)
> > }
> >
> > for (i = 0; i < 3; i++) {
> > - ret = nand_read_data_op(chip, p, sizeof(*p), true);
> > + ret = nand_read_data_op(chip, &p[i], sizeof(*p), true);
> > if (ret) {
> > ret = 0;
> > goto free_onfi_param_page;
> > }
> >
> > - if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
> > + if (onfi_crc16(ONFI_CRC_BASE, (u8 *)&p[i], 254) ==
> > le16_to_cpu(p->crc)) {
> > + if (i)
> > + memcpy(p, &p[i], sizeof(*p));
> > break;
> > }
> > }
> >
> > if (i == 3) {
> > - pr_err("Could not find valid ONFI parameter page; aborting\n");
> > - goto free_onfi_param_page;
> > + pr_err("Could not find valid ONFI parameter page\n");
> > + pr_info("Recover ONFI params with bit-wise majority\n");
> > + ret = onfi_recover_param(p, 3);
> > + if (ret == 0) {
> > + pr_err("ONFI parameter recovery failed, aborting\n");
> > + goto free_onfi_param_page;
> > + }
> > }
> >
> > /* Check version */