diff mbox series

[1/2] mtd: spi-nand: Add fixups for read retry

Message ID 20240905055333.2363358-2-linchengming884@gmail.com
State New
Headers show
Series mtd: spi-nand: Add support for read retry | expand

Commit Message

Cheng Ming Lin Sept. 5, 2024, 5:53 a.m. UTC
From: Cheng Ming Lin <chengminglin@mxic.com.tw>

Add fixups for support read retry:
- Initialize the NAND device maximum retry mode.
- Set feature on Special Read for Data Recovery register.

The Special Read for Data Recovery operation is enabled by Set Feature
function.

There are 5 modes for the user to recover the lost data.

Signed-off-by: Cheng Ming Lin <chengminglin@mxic.com.tw>
---
 drivers/mtd/nand/spi/macronix.c | 79 ++++++++++++++++++++++++++-------
 include/linux/mtd/spinand.h     | 17 +++++++
 2 files changed, 81 insertions(+), 15 deletions(-)

Comments

Miquel Raynal Oct. 1, 2024, 9:40 a.m. UTC | #1
Hi Cheng Ming,

linchengming884@gmail.com wrote on Thu,  5 Sep 2024 13:53:32 +0800:

> From: Cheng Ming Lin <chengminglin@mxic.com.tw>
> 
> Add fixups for support read retry:
> - Initialize the NAND device maximum retry mode.
> - Set feature on Special Read for Data Recovery register.
> 
> The Special Read for Data Recovery operation is enabled by Set Feature
> function.
> 
> There are 5 modes for the user to recover the lost data.
> 
> Signed-off-by: Cheng Ming Lin <chengminglin@mxic.com.tw>
> ---
>  drivers/mtd/nand/spi/macronix.c | 79 ++++++++++++++++++++++++++-------
>  include/linux/mtd/spinand.h     | 17 +++++++
>  2 files changed, 81 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c
> index 3f9e9c572854..baca67ff1cd6 100644
> --- a/drivers/mtd/nand/spi/macronix.c
> +++ b/drivers/mtd/nand/spi/macronix.c
> @@ -9,6 +9,8 @@
>  #include <linux/kernel.h>
>  #include <linux/mtd/spinand.h>
>  
> +#define MACRONIX_NUM_READ_RETRY_MODES 6

You said 5 in the cover letter?

> +#define MACRONIX_FEATURE_ADDR_READ_RETRY 0x70

Both definitions should probably come...

>  #define SPINAND_MFR_MACRONIX		0xC2
>  #define MACRONIX_ECCSR_MASK		0x0F

...here

>  
> @@ -100,6 +102,38 @@ static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand,
>  	return -EINVAL;
>  }
>  
> +/**
> + * macronix_spinand_init_read_retry - Initialize read_retries
> + * @spinand: SPI NAND device
> + *
> + * Return: the number of read retry modes
> + */
> +static int macronix_spinand_init_read_retry(struct spinand_device *spinand)
> +{
> +	return MACRONIX_NUM_READ_RETRY_MODES;

Does not sound very useful as a function?

> +}
> +
> +/**
> + * macronix_spinand_setup_read_retry - Set the retry mode
> + * @spinand: SPI NAND device
> + * @retry_mode: Specify which retry mode to set
> + *
> + * Return: 0 on success, -error otherwise

			, a negative error code otherwise.

> + */
> +static int macronix_spinand_setup_read_retry(struct spinand_device *spinand, u8 retry_mode)
> +{
> +	struct spi_mem_op op = SPINAND_SET_FEATURE_OP(MACRONIX_FEATURE_ADDR_READ_RETRY,
> +						      spinand->scratchbuf);
> +
> +	*spinand->scratchbuf = retry_mode;
> +	return spi_mem_exec_op(spinand->spimem, &op);
> +}
> +
> +static const struct spi_nand_fixups read_retry_fixups = {
> +	.init_read_retry = macronix_spinand_init_read_retry,
> +	.setup_read_retry = macronix_spinand_setup_read_retry,
> +};
> +

...

> @@ -325,7 +373,8 @@ static const struct spinand_info macronix_spinand_table[] = {
>  					      &update_cache_variants),
>  		     SPINAND_HAS_QE_BIT,
>  		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
> -				     mx35lf1ge4ab_ecc_get_status)),
> +				     mx35lf1ge4ab_ecc_get_status),
> +		     SPINAND_FIXUPS(&read_retry_fixups)),
>  	SPINAND_INFO("MX35UF1GE4AC",
>  		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92, 0x01),
>  		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),

I expect a patch targeting the core first, and then the changes in the
Macronix driver.

> diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
> index 5c19ead60499..e567d00a2805 100644
> --- a/include/linux/mtd/spinand.h
> +++ b/include/linux/mtd/spinand.h
> @@ -354,6 +354,7 @@ struct spinand_info {
>  	} op_variants;
>  	int (*select_target)(struct spinand_device *spinand,
>  			     unsigned int target);
> +	const struct spi_nand_fixups *fixups;
>  };
>  
>  #define SPINAND_ID(__method, ...)					\
> @@ -379,6 +380,9 @@ struct spinand_info {
>  #define SPINAND_SELECT_TARGET(__func)					\
>  	.select_target = __func,
>  
> +#define SPINAND_FIXUPS(__func)					\
> +	.fixups = __func,

I don't like the fixups naming. It feels like something is inherently
wrong.

> +
>  #define SPINAND_INFO(__model, __id, __memorg, __eccreq, __op_variants,	\
>  		     __flags, ...)					\
>  	{								\
> @@ -398,6 +402,16 @@ struct spinand_dirmap {
>  	struct spi_mem_dirmap_desc *rdesc_ecc;
>  };
>  
> +/**
> + * struct spi_nand_fixups - SPI NAND fixup hooks
> + * @init_read_retry: initialize spinand->read_retries
> + * @setup_read_retry: set the retry mode
> + */
> +struct spi_nand_fixups {
> +	int (*init_read_retry)(struct spinand_device *spinand);
> +	int (*setup_read_retry)(struct spinand_device *spinand, u8 retry_mode);
> +};
> +
>  /**
>   * struct spinand_device - SPI NAND device instance
>   * @base: NAND device instance
> @@ -423,6 +437,7 @@ struct spinand_dirmap {
>   *		the stack
>   * @manufacturer: SPI NAND manufacturer information
>   * @priv: manufacturer private data
> + * @read_retries: the number of read retry modes supported
>   */
>  struct spinand_device {
>  	struct nand_device base;
> @@ -449,8 +464,10 @@ struct spinand_device {
>  	u8 *databuf;
>  	u8 *oobbuf;
>  	u8 *scratchbuf;
> +	const struct spinand_info *info;

This looks like a leftover.

>  	const struct spinand_manufacturer *manufacturer;
>  	void *priv;
> +	int read_retries;

Any reason to keep this variable signed?

>  };
>  
>  /**


Thanks,
Miquèl
Cheng Ming Lin Oct. 7, 2024, 5:49 a.m. UTC | #2
Hi Miquel,

Miquel Raynal <miquel.raynal@bootlin.com> 於 2024年10月1日 週二 下午5:40寫道:
>
> Hi Cheng Ming,
>
> linchengming884@gmail.com wrote on Thu,  5 Sep 2024 13:53:32 +0800:
>
> > From: Cheng Ming Lin <chengminglin@mxic.com.tw>
> >
> > Add fixups for support read retry:
> > - Initialize the NAND device maximum retry mode.
> > - Set feature on Special Read for Data Recovery register.
> >
> > The Special Read for Data Recovery operation is enabled by Set Feature
> > function.
> >
> > There are 5 modes for the user to recover the lost data.
> >
> > Signed-off-by: Cheng Ming Lin <chengminglin@mxic.com.tw>
> > ---
> >  drivers/mtd/nand/spi/macronix.c | 79 ++++++++++++++++++++++++++-------
> >  include/linux/mtd/spinand.h     | 17 +++++++
> >  2 files changed, 81 insertions(+), 15 deletions(-)
> >
> > diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c
> > index 3f9e9c572854..baca67ff1cd6 100644
> > --- a/drivers/mtd/nand/spi/macronix.c
> > +++ b/drivers/mtd/nand/spi/macronix.c
> > @@ -9,6 +9,8 @@
> >  #include <linux/kernel.h>
> >  #include <linux/mtd/spinand.h>
> >
> > +#define MACRONIX_NUM_READ_RETRY_MODES 6
>
> You said 5 in the cover letter?

Since the original mode is labeled as default in our datasheet,
there are a total of six modes, including five additional modes
numbered from Mode 1 to Mode 5.

>
> > +#define MACRONIX_FEATURE_ADDR_READ_RETRY 0x70
>
> Both definitions should probably come...
>
> >  #define SPINAND_MFR_MACRONIX         0xC2
> >  #define MACRONIX_ECCSR_MASK          0x0F
>
> ...here

Sure, thanks for your advice.

>
> >
> > @@ -100,6 +102,38 @@ static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand,
> >       return -EINVAL;
> >  }
> >
> > +/**
> > + * macronix_spinand_init_read_retry - Initialize read_retries
> > + * @spinand: SPI NAND device
> > + *
> > + * Return: the number of read retry modes
> > + */
> > +static int macronix_spinand_init_read_retry(struct spinand_device *spinand)
> > +{
> > +     return MACRONIX_NUM_READ_RETRY_MODES;
>
> Does not sound very useful as a function?
>
> > +}
> > +
> > +/**
> > + * macronix_spinand_setup_read_retry - Set the retry mode
> > + * @spinand: SPI NAND device
> > + * @retry_mode: Specify which retry mode to set
> > + *
> > + * Return: 0 on success, -error otherwise
>
>                         , a negative error code otherwise.
>
> > + */
> > +static int macronix_spinand_setup_read_retry(struct spinand_device *spinand, u8 retry_mode)
> > +{
> > +     struct spi_mem_op op = SPINAND_SET_FEATURE_OP(MACRONIX_FEATURE_ADDR_READ_RETRY,
> > +                                                   spinand->scratchbuf);
> > +
> > +     *spinand->scratchbuf = retry_mode;
> > +     return spi_mem_exec_op(spinand->spimem, &op);
> > +}
> > +
> > +static const struct spi_nand_fixups read_retry_fixups = {
> > +     .init_read_retry = macronix_spinand_init_read_retry,
> > +     .setup_read_retry = macronix_spinand_setup_read_retry,
> > +};
> > +
>
> ...
>
> > @@ -325,7 +373,8 @@ static const struct spinand_info macronix_spinand_table[] = {
> >                                             &update_cache_variants),
> >                    SPINAND_HAS_QE_BIT,
> >                    SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
> > -                                  mx35lf1ge4ab_ecc_get_status)),
> > +                                  mx35lf1ge4ab_ecc_get_status),
> > +                  SPINAND_FIXUPS(&read_retry_fixups)),
> >       SPINAND_INFO("MX35UF1GE4AC",
> >                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92, 0x01),
> >                    NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
>
> I expect a patch targeting the core first, and then the changes in the
> Macronix driver.

Got it, so do you prefer that we switch to using flags instead?

>
> > diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
> > index 5c19ead60499..e567d00a2805 100644
> > --- a/include/linux/mtd/spinand.h
> > +++ b/include/linux/mtd/spinand.h
> > @@ -354,6 +354,7 @@ struct spinand_info {
> >       } op_variants;
> >       int (*select_target)(struct spinand_device *spinand,
> >                            unsigned int target);
> > +     const struct spi_nand_fixups *fixups;
> >  };
> >
> >  #define SPINAND_ID(__method, ...)                                    \
> > @@ -379,6 +380,9 @@ struct spinand_info {
> >  #define SPINAND_SELECT_TARGET(__func)                                        \
> >       .select_target = __func,
> >
> > +#define SPINAND_FIXUPS(__func)                                       \
> > +     .fixups = __func,
>
> I don't like the fixups naming. It feels like something is inherently
> wrong.
>
> > +
> >  #define SPINAND_INFO(__model, __id, __memorg, __eccreq, __op_variants,       \
> >                    __flags, ...)                                      \
> >       {                                                               \
> > @@ -398,6 +402,16 @@ struct spinand_dirmap {
> >       struct spi_mem_dirmap_desc *rdesc_ecc;
> >  };
> >
> > +/**
> > + * struct spi_nand_fixups - SPI NAND fixup hooks
> > + * @init_read_retry: initialize spinand->read_retries
> > + * @setup_read_retry: set the retry mode
> > + */
> > +struct spi_nand_fixups {
> > +     int (*init_read_retry)(struct spinand_device *spinand);
> > +     int (*setup_read_retry)(struct spinand_device *spinand, u8 retry_mode);
> > +};
> > +
> >  /**
> >   * struct spinand_device - SPI NAND device instance
> >   * @base: NAND device instance
> > @@ -423,6 +437,7 @@ struct spinand_dirmap {
> >   *           the stack
> >   * @manufacturer: SPI NAND manufacturer information
> >   * @priv: manufacturer private data
> > + * @read_retries: the number of read retry modes supported
> >   */
> >  struct spinand_device {
> >       struct nand_device base;
> > @@ -449,8 +464,10 @@ struct spinand_device {
> >       u8 *databuf;
> >       u8 *oobbuf;
> >       u8 *scratchbuf;
> > +     const struct spinand_info *info;
>
> This looks like a leftover.
>
> >       const struct spinand_manufacturer *manufacturer;
> >       void *priv;
> > +     int read_retries;
>
> Any reason to keep this variable signed?

No, we can simply change from int to u8.

>
> >  };
> >
> >  /**
>
>
> Thanks,
> Miquèl

Thanks,
Cheng Ming Lin
Miquel Raynal Oct. 7, 2024, 8:33 a.m. UTC | #3
Hi Cheng Ming,

> > > @@ -325,7 +373,8 @@ static const struct spinand_info macronix_spinand_table[] = {
> > >                                             &update_cache_variants),
> > >                    SPINAND_HAS_QE_BIT,
> > >                    SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
> > > -                                  mx35lf1ge4ab_ecc_get_status)),
> > > +                                  mx35lf1ge4ab_ecc_get_status),
> > > +                  SPINAND_FIXUPS(&read_retry_fixups)),
> > >       SPINAND_INFO("MX35UF1GE4AC",
> > >                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92, 0x01),
> > >                    NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),  
> >
> > I expect a patch targeting the core first, and then the changes in the
> > Macronix driver.  
> 
> Got it, so do you prefer that we switch to using flags instead?

Not necessarily, did I?

...

> > >       const struct spinand_manufacturer *manufacturer;
> > >       void *priv;
> > > +     int read_retries;  
> >
> > Any reason to keep this variable signed?  
> 
> No, we can simply change from int to u8.

Just unsigned int is fine.

Thanks,
Miquèl
Cheng Ming Lin Oct. 8, 2024, 6:25 a.m. UTC | #4
Hi Miquel,

Miquel Raynal <miquel.raynal@bootlin.com> 於 2024年10月7日 週一 下午4:33寫道:
>
> Hi Cheng Ming,
>
> > > > @@ -325,7 +373,8 @@ static const struct spinand_info macronix_spinand_table[] = {
> > > >                                             &update_cache_variants),
> > > >                    SPINAND_HAS_QE_BIT,
> > > >                    SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
> > > > -                                  mx35lf1ge4ab_ecc_get_status)),
> > > > +                                  mx35lf1ge4ab_ecc_get_status),
> > > > +                  SPINAND_FIXUPS(&read_retry_fixups)),
> > > >       SPINAND_INFO("MX35UF1GE4AC",
> > > >                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92, 0x01),
> > > >                    NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
> > >
> > > I expect a patch targeting the core first, and then the changes in the
> > > Macronix driver.
> >
> > Got it, so do you prefer that we switch to using flags instead?
>
> Not necessarily, did I?
>
> ...
>

Using a flag instead of fixups allows this patch to target the core first,
and reduces changes in the Macronix driver.

> > > >       const struct spinand_manufacturer *manufacturer;
> > > >       void *priv;
> > > > +     int read_retries;
> > >
> > > Any reason to keep this variable signed?
> >
> > No, we can simply change from int to u8.
>
> Just unsigned int is fine.
>

Sure, thanks!

> Thanks,
> Miquèl

Thanks,
Cheng Ming Lin
Miquel Raynal Oct. 8, 2024, 8:55 a.m. UTC | #5
Hi,

linchengming884@gmail.com wrote on Tue, 8 Oct 2024 14:25:25 +0800:

> Hi Miquel,
> 
> Miquel Raynal <miquel.raynal@bootlin.com> 於 2024年10月7日 週一 下午4:33寫道:
> >
> > Hi Cheng Ming,
> >  
> > > > > @@ -325,7 +373,8 @@ static const struct spinand_info macronix_spinand_table[] = {
> > > > >                                             &update_cache_variants),
> > > > >                    SPINAND_HAS_QE_BIT,
> > > > >                    SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
> > > > > -                                  mx35lf1ge4ab_ecc_get_status)),
> > > > > +                                  mx35lf1ge4ab_ecc_get_status),
> > > > > +                  SPINAND_FIXUPS(&read_retry_fixups)),
> > > > >       SPINAND_INFO("MX35UF1GE4AC",
> > > > >                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92, 0x01),
> > > > >                    NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),  
> > > >
> > > > I expect a patch targeting the core first, and then the changes in the
> > > > Macronix driver.  
> > >
> > > Got it, so do you prefer that we switch to using flags instead?  
> >
> > Not necessarily, did I?
> >
> > ...
> >  
> 
> Using a flag instead of fixups allows this patch to target the core first,
> and reduces changes in the Macronix driver.

Propose what ever you think is best. You can also look at how it is
done in raw NAND. But always include the core changes first, please.
It is not related to how you implement it.

> 
> > > > >       const struct spinand_manufacturer *manufacturer;
> > > > >       void *priv;
> > > > > +     int read_retries;  
> > > >
> > > > Any reason to keep this variable signed?  
> > >
> > > No, we can simply change from int to u8.  
> >
> > Just unsigned int is fine.
> >  
> 
> Sure, thanks!
> 
> > Thanks,
> > Miquèl  
> 
> Thanks,
> Cheng Ming Lin


Thanks,
Miquèl
Cheng Ming Lin Oct. 8, 2024, 9:19 a.m. UTC | #6
Hi Miquel,

Miquel Raynal <miquel.raynal@bootlin.com> 於 2024年10月8日 週二 下午4:55寫道:
>
> Hi,
>
> linchengming884@gmail.com wrote on Tue, 8 Oct 2024 14:25:25 +0800:
>
> > Hi Miquel,
> >
> > Miquel Raynal <miquel.raynal@bootlin.com> 於 2024年10月7日 週一 下午4:33寫道:
> > >
> > > Hi Cheng Ming,
> > >
> > > > > > @@ -325,7 +373,8 @@ static const struct spinand_info macronix_spinand_table[] = {
> > > > > >                                             &update_cache_variants),
> > > > > >                    SPINAND_HAS_QE_BIT,
> > > > > >                    SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
> > > > > > -                                  mx35lf1ge4ab_ecc_get_status)),
> > > > > > +                                  mx35lf1ge4ab_ecc_get_status),
> > > > > > +                  SPINAND_FIXUPS(&read_retry_fixups)),
> > > > > >       SPINAND_INFO("MX35UF1GE4AC",
> > > > > >                    SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92, 0x01),
> > > > > >                    NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
> > > > >
> > > > > I expect a patch targeting the core first, and then the changes in the
> > > > > Macronix driver.
> > > >
> > > > Got it, so do you prefer that we switch to using flags instead?
> > >
> > > Not necessarily, did I?
> > >
> > > ...
> > >
> >
> > Using a flag instead of fixups allows this patch to target the core first,
> > and reduces changes in the Macronix driver.
>
> Propose what ever you think is best. You can also look at how it is
> done in raw NAND. But always include the core changes first, please.
> It is not related to how you implement it.
>

Thank you so much for your suggestion. I will ensure the core changes are
addressed first, as you mentioned. Also, I did refer to how it's done in raw
NAND when working on this patch. I really appreciate your guidance and will
definitely keep your words in mind.

> >
> > > > > >       const struct spinand_manufacturer *manufacturer;
> > > > > >       void *priv;
> > > > > > +     int read_retries;
> > > > >
> > > > > Any reason to keep this variable signed?
> > > >
> > > > No, we can simply change from int to u8.
> > >
> > > Just unsigned int is fine.
> > >
> >
> > Sure, thanks!
> >
> > > Thanks,
> > > Miquèl
> >
> > Thanks,
> > Cheng Ming Lin
>
>
> Thanks,
> Miquèl

Thanks,
Cheng Ming Lin
diff mbox series

Patch

diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c
index 3f9e9c572854..baca67ff1cd6 100644
--- a/drivers/mtd/nand/spi/macronix.c
+++ b/drivers/mtd/nand/spi/macronix.c
@@ -9,6 +9,8 @@ 
 #include <linux/kernel.h>
 #include <linux/mtd/spinand.h>
 
+#define MACRONIX_NUM_READ_RETRY_MODES 6
+#define MACRONIX_FEATURE_ADDR_READ_RETRY 0x70
 #define SPINAND_MFR_MACRONIX		0xC2
 #define MACRONIX_ECCSR_MASK		0x0F
 
@@ -100,6 +102,38 @@  static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand,
 	return -EINVAL;
 }
 
+/**
+ * macronix_spinand_init_read_retry - Initialize read_retries
+ * @spinand: SPI NAND device
+ *
+ * Return: the number of read retry modes
+ */
+static int macronix_spinand_init_read_retry(struct spinand_device *spinand)
+{
+	return MACRONIX_NUM_READ_RETRY_MODES;
+}
+
+/**
+ * macronix_spinand_setup_read_retry - Set the retry mode
+ * @spinand: SPI NAND device
+ * @retry_mode: Specify which retry mode to set
+ *
+ * Return: 0 on success, -error otherwise
+ */
+static int macronix_spinand_setup_read_retry(struct spinand_device *spinand, u8 retry_mode)
+{
+	struct spi_mem_op op = SPINAND_SET_FEATURE_OP(MACRONIX_FEATURE_ADDR_READ_RETRY,
+						      spinand->scratchbuf);
+
+	*spinand->scratchbuf = retry_mode;
+	return spi_mem_exec_op(spinand->spimem, &op);
+}
+
+static const struct spi_nand_fixups read_retry_fixups = {
+	.init_read_retry = macronix_spinand_init_read_retry,
+	.setup_read_retry = macronix_spinand_setup_read_retry,
+};
+
 static const struct spinand_info macronix_spinand_table[] = {
 	SPINAND_INFO("MX35LF1GE4AB",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x12),
@@ -129,7 +163,8 @@  static const struct spinand_info macronix_spinand_table[] = {
 					      &update_cache_variants),
 		     SPINAND_HAS_QE_BIT,
 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
-				     mx35lf1ge4ab_ecc_get_status)),
+				     mx35lf1ge4ab_ecc_get_status),
+		     SPINAND_FIXUPS(&read_retry_fixups)),
 	SPINAND_INFO("MX35LF4GE4AD",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x37, 0x03),
 		     NAND_MEMORG(1, 4096, 128, 64, 2048, 40, 1, 1, 1),
@@ -139,7 +174,8 @@  static const struct spinand_info macronix_spinand_table[] = {
 					      &update_cache_variants),
 		     SPINAND_HAS_QE_BIT,
 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
-				     mx35lf1ge4ab_ecc_get_status)),
+				     mx35lf1ge4ab_ecc_get_status),
+		     SPINAND_FIXUPS(&read_retry_fixups)),
 	SPINAND_INFO("MX35LF1G24AD",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14, 0x03),
 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
@@ -148,7 +184,8 @@  static const struct spinand_info macronix_spinand_table[] = {
 					      &write_cache_variants,
 					      &update_cache_variants),
 		     SPINAND_HAS_QE_BIT,
-		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
+		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL),
+		     SPINAND_FIXUPS(&read_retry_fixups)),
 	SPINAND_INFO("MX35LF2G24AD",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24, 0x03),
 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1),
@@ -157,7 +194,8 @@  static const struct spinand_info macronix_spinand_table[] = {
 					      &write_cache_variants,
 					      &update_cache_variants),
 		     SPINAND_HAS_QE_BIT,
-		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
+		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL),
+		     SPINAND_FIXUPS(&read_retry_fixups)),
 	SPINAND_INFO("MX35LF2G24AD-Z4I8",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x64, 0x03),
 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
@@ -166,7 +204,8 @@  static const struct spinand_info macronix_spinand_table[] = {
 					      &write_cache_variants,
 					      &update_cache_variants),
 		     SPINAND_HAS_QE_BIT,
-		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
+		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL),
+		     SPINAND_FIXUPS(&read_retry_fixups)),
 	SPINAND_INFO("MX35LF4G24AD",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35, 0x03),
 		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 2, 1, 1),
@@ -175,7 +214,8 @@  static const struct spinand_info macronix_spinand_table[] = {
 					      &write_cache_variants,
 					      &update_cache_variants),
 		     SPINAND_HAS_QE_BIT,
-		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
+		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL),
+		     SPINAND_FIXUPS(&read_retry_fixups)),
 	SPINAND_INFO("MX35LF4G24AD-Z4I8",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x75, 0x03),
 		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
@@ -184,7 +224,8 @@  static const struct spinand_info macronix_spinand_table[] = {
 					      &write_cache_variants,
 					      &update_cache_variants),
 		     SPINAND_HAS_QE_BIT,
-		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
+		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL),
+		     SPINAND_FIXUPS(&read_retry_fixups)),
 	SPINAND_INFO("MX31LF1GE4BC",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x1e),
 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
@@ -225,7 +266,8 @@  static const struct spinand_info macronix_spinand_table[] = {
 					      &update_cache_variants),
 		     SPINAND_HAS_QE_BIT,
 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
-				     mx35lf1ge4ab_ecc_get_status)),
+				     mx35lf1ge4ab_ecc_get_status),
+		     SPINAND_FIXUPS(&read_retry_fixups)),
 	SPINAND_INFO("MX35UF4G24AD-Z4I8",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xf5, 0x03),
 		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
@@ -235,7 +277,8 @@  static const struct spinand_info macronix_spinand_table[] = {
 					      &update_cache_variants),
 		     SPINAND_HAS_QE_BIT,
 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
-				     mx35lf1ge4ab_ecc_get_status)),
+				     mx35lf1ge4ab_ecc_get_status),
+		     SPINAND_FIXUPS(&read_retry_fixups)),
 	SPINAND_INFO("MX35UF4GE4AD",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb7, 0x03),
 		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
@@ -245,7 +288,8 @@  static const struct spinand_info macronix_spinand_table[] = {
 					      &update_cache_variants),
 		     SPINAND_HAS_QE_BIT,
 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
-				     mx35lf1ge4ab_ecc_get_status)),
+				     mx35lf1ge4ab_ecc_get_status),
+		     SPINAND_FIXUPS(&read_retry_fixups)),
 	SPINAND_INFO("MX35UF2G14AC",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa0),
 		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1),
@@ -265,7 +309,8 @@  static const struct spinand_info macronix_spinand_table[] = {
 					      &update_cache_variants),
 		     SPINAND_HAS_QE_BIT,
 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
-				     mx35lf1ge4ab_ecc_get_status)),
+				     mx35lf1ge4ab_ecc_get_status),
+		     SPINAND_FIXUPS(&read_retry_fixups)),
 	SPINAND_INFO("MX35UF2G24AD-Z4I8",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xe4, 0x03),
 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
@@ -275,7 +320,8 @@  static const struct spinand_info macronix_spinand_table[] = {
 					      &update_cache_variants),
 		     SPINAND_HAS_QE_BIT,
 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
-				     mx35lf1ge4ab_ecc_get_status)),
+				     mx35lf1ge4ab_ecc_get_status),
+		     SPINAND_FIXUPS(&read_retry_fixups)),
 	SPINAND_INFO("MX35UF2GE4AD",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa6, 0x03),
 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
@@ -285,7 +331,8 @@  static const struct spinand_info macronix_spinand_table[] = {
 					      &update_cache_variants),
 		     SPINAND_HAS_QE_BIT,
 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
-				     mx35lf1ge4ab_ecc_get_status)),
+				     mx35lf1ge4ab_ecc_get_status),
+		     SPINAND_FIXUPS(&read_retry_fixups)),
 	SPINAND_INFO("MX35UF2GE4AC",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa2, 0x01),
 		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
@@ -315,7 +362,8 @@  static const struct spinand_info macronix_spinand_table[] = {
 					      &update_cache_variants),
 		     SPINAND_HAS_QE_BIT,
 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
-				     mx35lf1ge4ab_ecc_get_status)),
+				     mx35lf1ge4ab_ecc_get_status),
+		     SPINAND_FIXUPS(&read_retry_fixups)),
 	SPINAND_INFO("MX35UF1GE4AD",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x96, 0x03),
 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
@@ -325,7 +373,8 @@  static const struct spinand_info macronix_spinand_table[] = {
 					      &update_cache_variants),
 		     SPINAND_HAS_QE_BIT,
 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
-				     mx35lf1ge4ab_ecc_get_status)),
+				     mx35lf1ge4ab_ecc_get_status),
+		     SPINAND_FIXUPS(&read_retry_fixups)),
 	SPINAND_INFO("MX35UF1GE4AC",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92, 0x01),
 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index 5c19ead60499..e567d00a2805 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -354,6 +354,7 @@  struct spinand_info {
 	} op_variants;
 	int (*select_target)(struct spinand_device *spinand,
 			     unsigned int target);
+	const struct spi_nand_fixups *fixups;
 };
 
 #define SPINAND_ID(__method, ...)					\
@@ -379,6 +380,9 @@  struct spinand_info {
 #define SPINAND_SELECT_TARGET(__func)					\
 	.select_target = __func,
 
+#define SPINAND_FIXUPS(__func)					\
+	.fixups = __func,
+
 #define SPINAND_INFO(__model, __id, __memorg, __eccreq, __op_variants,	\
 		     __flags, ...)					\
 	{								\
@@ -398,6 +402,16 @@  struct spinand_dirmap {
 	struct spi_mem_dirmap_desc *rdesc_ecc;
 };
 
+/**
+ * struct spi_nand_fixups - SPI NAND fixup hooks
+ * @init_read_retry: initialize spinand->read_retries
+ * @setup_read_retry: set the retry mode
+ */
+struct spi_nand_fixups {
+	int (*init_read_retry)(struct spinand_device *spinand);
+	int (*setup_read_retry)(struct spinand_device *spinand, u8 retry_mode);
+};
+
 /**
  * struct spinand_device - SPI NAND device instance
  * @base: NAND device instance
@@ -423,6 +437,7 @@  struct spinand_dirmap {
  *		the stack
  * @manufacturer: SPI NAND manufacturer information
  * @priv: manufacturer private data
+ * @read_retries: the number of read retry modes supported
  */
 struct spinand_device {
 	struct nand_device base;
@@ -449,8 +464,10 @@  struct spinand_device {
 	u8 *databuf;
 	u8 *oobbuf;
 	u8 *scratchbuf;
+	const struct spinand_info *info;
 	const struct spinand_manufacturer *manufacturer;
 	void *priv;
+	int read_retries;
 };
 
 /**