[3/3] mtd: rawnand: marvell: add support for AC5 SoC

Message ID 20221019082046.30160-4-vadym.kochan@plvision.eu
State New
Headers
Series mtd: rawnand: marvell: add support for AC5 SoC |

Commit Message

Vadym Kochan Oct. 19, 2022, 8:20 a.m. UTC
  From: Aviram Dali <aviramd@marvell.com>

The following changes were made to add AC5 support:

   1) Modify Marvell nand NFC timing set for mode 0

   2) fix validation in AC5 Nand driver for ONFI timings values modes 1 and 3

   3) remove unnecessary nand timing-mode in device tree of ac5.dtsi

   4) add nand missing AC5X layouts , add option to use ndtr predefined values

   5) Zero steps and total fields of ecc in ecc controller initialization so
      nand_scan_tail() will calculate these two fields, otherwise
      NAND initialization will fail with kernel 5.15 and above.

Signed-off-by: Aviram Dali <aviramd@marvell.com>
Signed-off-by: Vadym Kochan <vadym.kochan@plvision.eu>
---
 drivers/mtd/nand/raw/Kconfig        |   2 +-
 drivers/mtd/nand/raw/marvell_nand.c | 277 ++++++++++++++++++++++++----
 2 files changed, 246 insertions(+), 33 deletions(-)
  

Comments

Miquel Raynal Oct. 19, 2022, 8:45 a.m. UTC | #1
Hi Vadym,

vadym.kochan@plvision.eu wrote on Wed, 19 Oct 2022 11:20:40 +0300:

> From: Aviram Dali <aviramd@marvell.com>
> 
> The following changes were made to add AC5 support:
> 
>    1) Modify Marvell nand NFC timing set for mode 0
> 
>    2) fix validation in AC5 Nand driver for ONFI timings values modes 1 and 3
> 
>    3) remove unnecessary nand timing-mode in device tree of ac5.dtsi
> 
>    4) add nand missing AC5X layouts , add option to use ndtr predefined values
> 
>    5) Zero steps and total fields of ecc in ecc controller initialization so
>       nand_scan_tail() will calculate these two fields, otherwise
>       NAND initialization will fail with kernel 5.15 and above.
> 
> Signed-off-by: Aviram Dali <aviramd@marvell.com>
> Signed-off-by: Vadym Kochan <vadym.kochan@plvision.eu>
> ---
>  drivers/mtd/nand/raw/Kconfig        |   2 +-
>  drivers/mtd/nand/raw/marvell_nand.c | 277 ++++++++++++++++++++++++----
>  2 files changed, 246 insertions(+), 33 deletions(-)
> 
> diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
> index 4cd40af362de..b7bb62f27e02 100644
> --- a/drivers/mtd/nand/raw/Kconfig
> +++ b/drivers/mtd/nand/raw/Kconfig
> @@ -160,7 +160,7 @@ config MTD_NAND_MARVELL
>  	  including:
>  	  - PXA3xx processors (NFCv1)
>  	  - 32-bit Armada platforms (XP, 37x, 38x, 39x) (NFCv2)
> -	  - 64-bit Aramda platforms (7k, 8k) (NFCv2)
> +	  - 64-bit Aramda platforms (7k, 8k, ac5) (NFCv2)
>  
>  config MTD_NAND_SLC_LPC32XX
>  	tristate "NXP LPC32xx SLC NAND controller"
> diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c
> index b9d1e96e3334..940a89115782 100644
> --- a/drivers/mtd/nand/raw/marvell_nand.c
> +++ b/drivers/mtd/nand/raw/marvell_nand.c
> @@ -226,6 +226,20 @@
>  #define XTYPE_COMMAND_DISPATCH	6
>  #define XTYPE_MASK		7
>  
> +/* use tRP_min, tWC_min and tWP_min to distinct across timings modes */
> +#define IS_TIMINGS_EQUAL(t1, t2) \
> +		((t1->tRP_min == t2->tRP_min &&\
> +		t1->tWC_min == t2->tWC_min &&\
> +		t1->tWP_min == t2->tWP_min) ? true : false)
> +
> +/*  ndtr0,1 set , each set has few modes level */
> +typedef enum marvell_nfc_timing_mode_set {
> +	MARVELL_NFC_NDTR_SET_0,		/* tested with ac5 */
> +
> +	MARVELL_NFC_NDTR_NUM_OF_SET,
> +	MARVELL_NFC_NDTR_SET_NON = MARVELL_NFC_NDTR_NUM_OF_SET
> +} marvell_nfc_timing_mode_set_t;
> +
>  /**
>   * struct marvell_hw_ecc_layout - layout of Marvell ECC
>   *
> @@ -283,14 +297,21 @@ struct marvell_hw_ecc_layout {
>  
>  /* Layouts explained in AN-379_Marvell_SoC_NFC_ECC */
>  static const struct marvell_hw_ecc_layout marvell_nfc_layouts[] = {
> -	MARVELL_LAYOUT(  512,   512,  1,  1,  1,  512,  8,  8,  0,  0,  0),
> -	MARVELL_LAYOUT( 2048,   512,  1,  1,  1, 2048, 40, 24,  0,  0,  0),
> -	MARVELL_LAYOUT( 2048,   512,  4,  1,  1, 2048, 32, 30,  0,  0,  0),
> -	MARVELL_LAYOUT( 2048,   512,  8,  2,  1, 1024,  0, 30,1024,32, 30),
> -	MARVELL_LAYOUT( 4096,   512,  4,  2,  2, 2048, 32, 30,  0,  0,  0),
> -	MARVELL_LAYOUT( 4096,   512,  8,  5,  4, 1024,  0, 30,  0, 64, 30),
> -	MARVELL_LAYOUT( 8192,   512,  4,  4,  4, 2048,  0, 30,  0,  0,  0),
> -	MARVELL_LAYOUT( 8192,   512,  8,  9,  8, 1024,  0, 30,  0, 160, 30),
> +	MARVELL_LAYOUT(512,   512,  1,  1,  1,  512,  8,  8,  0,   0,  0),
> +	MARVELL_LAYOUT(2048,   512,  1,  1,  1, 2048, 40, 24,  0,   0,  0),
> +	MARVELL_LAYOUT(2048,   512,  4,  1,  1, 2048, 32, 30,  0,   0,  0),
> +	MARVELL_LAYOUT(2048,   512,  8,  2,  1, 1024,  0, 30,  1024, 32, 30),
> +	MARVELL_LAYOUT(2048,   512,  8,  2,  1, 1024,  0, 30,  1024, 64, 30),
> +	MARVELL_LAYOUT(2048,   512,  12, 3,  2, 704,   0, 30,  640, 0,  30),
> +	MARVELL_LAYOUT(2048,   512,  16, 5,  4, 512,   0, 30,  0,   32, 30),
> +	MARVELL_LAYOUT(4096,   512,  4,  2,  2, 2048, 32, 30,  0,   0,  0),
> +	MARVELL_LAYOUT(4096,   512,  8,  5,  4, 1024,  0, 30,  0,   64, 30),
> +	MARVELL_LAYOUT(4096,   512,  12, 6,  5, 704,   0, 30,  576, 32, 30),
> +	MARVELL_LAYOUT(4096,   512,  16, 9,  8, 512,   0, 30,  0,   32, 30),
> +	MARVELL_LAYOUT(8192,   512,  4,  4,  4, 2048,  0, 30,  0,   0,  0),
> +	MARVELL_LAYOUT(8192,   512,  8,  9,  8, 1024,  0, 30,  0,  160, 30),
> +	MARVELL_LAYOUT(8192,   512,  12, 12, 11, 704,  0, 30,  448, 64, 30),
> +	MARVELL_LAYOUT(8192,   512,  16, 17, 16, 512,  0, 30,  0,   32, 30),
>  };

If you don't like the layout, it's fine, but you must do it in a
separate commit.

[...]

> +/*
> + * get nand timing-mode from device tree
> + */
> +static int get_nand_timing_mode(struct device_node *np)
> +{
> +	int ret;
> +	u32 val;
> +
> +	ret = of_property_read_u32(np, "nand-timing-mode", &val);
> +	return ret ? ret : val;
> +}
> +
>  /*
>   * Internal helper to conditionnally apply a delay (from the above structure,
>   * most of the time).
> @@ -2257,9 +2399,21 @@ static int marvell_nand_hw_ecc_controller_init(struct mtd_info *mtd,
>  	}
>  
>  	mtd_set_ooblayout(mtd, &marvell_nand_ooblayout_ops);
> -	ecc->steps = l->nchunks;
>  	ecc->size = l->data_bytes;
>  
> +	/* nand_scan_tail func perform  validity tests for ECC strength, and it
> +	 * assumes that all chunks are with same size. in our case when ecc is 12
> +	 * the chunk size is 704 but the last chunk is with different size so
> +	 * we cheat it nand_scan_tail validity tests by set info->ecc_size value to
> +	 * 512.

The driver already supports this, trying to cheat the core is bad. See
the last members of struct marvell_hw_ecc_layout.

> +	 */
> +	if (ecc->strength == 12)
> +		ecc->size = 512;
> +
> +	/* let nand_scan_tail() calculate these two fields */
> +	ecc->steps = 0;
> +	ecc->total = 0;
> +
>  	if (ecc->strength == 1) {
>  		chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
>  		ecc->read_page_raw = marvell_nfc_hw_ecc_hmg_read_page_raw;
> @@ -2360,9 +2514,11 @@ static int marvell_nfc_setup_interface(struct nand_chip *chip, int chipnr,
>  	struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
>  	struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
>  	unsigned int period_ns = 1000000000 / clk_get_rate(nfc->core_clk) * 2;
> -	const struct nand_sdr_timings *sdr;
> +	const struct nand_sdr_timings *sdr, *timings;
>  	struct marvell_nfc_timings nfc_tmg;
>  	int read_delay;
> +	marvell_nfc_timing_mode_set_t modes_set;
> +	int mode = 0;
>  
>  	sdr = nand_get_sdr_timings(conf);
>  	if (IS_ERR(sdr))
> @@ -2421,32 +2577,71 @@ static int marvell_nfc_setup_interface(struct nand_chip *chip, int chipnr,
>  			nfc_tmg.tR = 0;
>  	}
>  
> -	if (chipnr < 0)
> -		return 0;
>  
> -	marvell_nand->ndtr0 =
> -		NDTR0_TRP(nfc_tmg.tRP) |
> -		NDTR0_TRH(nfc_tmg.tRH) |
> -		NDTR0_ETRP(nfc_tmg.tRP) |
> -		NDTR0_TWP(nfc_tmg.tWP) |
> -		NDTR0_TWH(nfc_tmg.tWH) |
> -		NDTR0_TCS(nfc_tmg.tCS) |
> -		NDTR0_TCH(nfc_tmg.tCH);
> +	/* get the timing modes from predefined values according to its compatibility */
> +	if (nfc->caps->is_marvell_timing_modes) {
> +		/* get the mode set */
> +		modes_set = nfc->caps->timing_mode_set;
> +		if (modes_set >= MARVELL_NFC_NDTR_SET_NON) {
> +			dev_warn(nfc->dev,
> +				"Warning: not supported timing registers set,use set number 0 by default\n");
>  
> -	marvell_nand->ndtr1 =
> -		NDTR1_TAR(nfc_tmg.tAR) |
> -		NDTR1_TWHR(nfc_tmg.tWHR) |
> -		NDTR1_TR(nfc_tmg.tR);
> +			modes_set = MARVELL_NFC_NDTR_SET_0;
> +		}
>  
> -	if (nfc->caps->is_nfcv2) {
> -		marvell_nand->ndtr0 |=
> -			NDTR0_RD_CNT_DEL(read_delay) |
> -			NDTR0_SELCNTR |
> -			NDTR0_TADL(nfc_tmg.tADL);
> +		/* find the caller mode according to timings values */
> +		/* if exit on error it means no more modes; not suppose to happen */
> +		do {
> +			timings = onfi_async_timing_mode_to_sdr_timings(mode);

The timing negotiation is handled by the core, I don't see why you
would need this.

> +			if (IS_TIMINGS_EQUAL(timings, sdr))
> +				break;
> +			mode++;
> +		} while (!IS_ERR(timings));
> +
> +		/* if mode is not supported by NFC, return false or if nand-timing-mode that
> +		 * exists in device tree greater then caller mode also return false and wait
> +		 * for caller to try with next mode (mode-1). we want the nand feature to be
> +		 * configured with nand-timing-mode value.
> +		 */
> +		if (mode > nfc->caps->max_mode_number ||
> +			 ((marvell_nand->nand_timing_mode) >= 0 &&
> +			 (mode > marvell_nand->nand_timing_mode)))
> +			return -EOPNOTSUPP;

[...]

> @@ -2681,6 +2877,10 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
>  	if (of_property_read_bool(np, "marvell,nand-keep-config"))
>  		chip->options |= NAND_KEEP_TIMINGS;
>  
> +	/* read the mode from device tree */
> +	dn = nand_get_flash_node(chip);
> +	marvell_nand->nand_timing_mode = get_nand_timing_mode(dn);

I'm sorry but unless you give me a really convincing explanation, this
is not gonna work. The timing mode should be negotiated between the
controller and the chip, then the controller needs to configure its own
registers to fit these timings. There is no way the timing mode should
be hardcoded in the device tree.
 
> +
>  	mtd = nand_to_mtd(chip);
>  	mtd->dev.parent = dev;
>  

Thanks,
Miquèl
  

Patch

diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index 4cd40af362de..b7bb62f27e02 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -160,7 +160,7 @@  config MTD_NAND_MARVELL
 	  including:
 	  - PXA3xx processors (NFCv1)
 	  - 32-bit Armada platforms (XP, 37x, 38x, 39x) (NFCv2)
-	  - 64-bit Aramda platforms (7k, 8k) (NFCv2)
+	  - 64-bit Aramda platforms (7k, 8k, ac5) (NFCv2)
 
 config MTD_NAND_SLC_LPC32XX
 	tristate "NXP LPC32xx SLC NAND controller"
diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c
index b9d1e96e3334..940a89115782 100644
--- a/drivers/mtd/nand/raw/marvell_nand.c
+++ b/drivers/mtd/nand/raw/marvell_nand.c
@@ -226,6 +226,20 @@ 
 #define XTYPE_COMMAND_DISPATCH	6
 #define XTYPE_MASK		7
 
+/* use tRP_min, tWC_min and tWP_min to distinct across timings modes */
+#define IS_TIMINGS_EQUAL(t1, t2) \
+		((t1->tRP_min == t2->tRP_min &&\
+		t1->tWC_min == t2->tWC_min &&\
+		t1->tWP_min == t2->tWP_min) ? true : false)
+
+/*  ndtr0,1 set , each set has few modes level */
+typedef enum marvell_nfc_timing_mode_set {
+	MARVELL_NFC_NDTR_SET_0,		/* tested with ac5 */
+
+	MARVELL_NFC_NDTR_NUM_OF_SET,
+	MARVELL_NFC_NDTR_SET_NON = MARVELL_NFC_NDTR_NUM_OF_SET
+} marvell_nfc_timing_mode_set_t;
+
 /**
  * struct marvell_hw_ecc_layout - layout of Marvell ECC
  *
@@ -283,14 +297,21 @@  struct marvell_hw_ecc_layout {
 
 /* Layouts explained in AN-379_Marvell_SoC_NFC_ECC */
 static const struct marvell_hw_ecc_layout marvell_nfc_layouts[] = {
-	MARVELL_LAYOUT(  512,   512,  1,  1,  1,  512,  8,  8,  0,  0,  0),
-	MARVELL_LAYOUT( 2048,   512,  1,  1,  1, 2048, 40, 24,  0,  0,  0),
-	MARVELL_LAYOUT( 2048,   512,  4,  1,  1, 2048, 32, 30,  0,  0,  0),
-	MARVELL_LAYOUT( 2048,   512,  8,  2,  1, 1024,  0, 30,1024,32, 30),
-	MARVELL_LAYOUT( 4096,   512,  4,  2,  2, 2048, 32, 30,  0,  0,  0),
-	MARVELL_LAYOUT( 4096,   512,  8,  5,  4, 1024,  0, 30,  0, 64, 30),
-	MARVELL_LAYOUT( 8192,   512,  4,  4,  4, 2048,  0, 30,  0,  0,  0),
-	MARVELL_LAYOUT( 8192,   512,  8,  9,  8, 1024,  0, 30,  0, 160, 30),
+	MARVELL_LAYOUT(512,   512,  1,  1,  1,  512,  8,  8,  0,   0,  0),
+	MARVELL_LAYOUT(2048,   512,  1,  1,  1, 2048, 40, 24,  0,   0,  0),
+	MARVELL_LAYOUT(2048,   512,  4,  1,  1, 2048, 32, 30,  0,   0,  0),
+	MARVELL_LAYOUT(2048,   512,  8,  2,  1, 1024,  0, 30,  1024, 32, 30),
+	MARVELL_LAYOUT(2048,   512,  8,  2,  1, 1024,  0, 30,  1024, 64, 30),
+	MARVELL_LAYOUT(2048,   512,  12, 3,  2, 704,   0, 30,  640, 0,  30),
+	MARVELL_LAYOUT(2048,   512,  16, 5,  4, 512,   0, 30,  0,   32, 30),
+	MARVELL_LAYOUT(4096,   512,  4,  2,  2, 2048, 32, 30,  0,   0,  0),
+	MARVELL_LAYOUT(4096,   512,  8,  5,  4, 1024,  0, 30,  0,   64, 30),
+	MARVELL_LAYOUT(4096,   512,  12, 6,  5, 704,   0, 30,  576, 32, 30),
+	MARVELL_LAYOUT(4096,   512,  16, 9,  8, 512,   0, 30,  0,   32, 30),
+	MARVELL_LAYOUT(8192,   512,  4,  4,  4, 2048,  0, 30,  0,   0,  0),
+	MARVELL_LAYOUT(8192,   512,  8,  9,  8, 1024,  0, 30,  0,  160, 30),
+	MARVELL_LAYOUT(8192,   512,  12, 12, 11, 704,  0, 30,  448, 64, 30),
+	MARVELL_LAYOUT(8192,   512,  16, 17, 16, 512,  0, 30,  0,   32, 30),
 };
 
 /**
@@ -328,6 +349,7 @@  struct marvell_nand_chip_sel {
  * @selected_die:	Current active CS
  * @nsels:		Number of CS lines required by the NAND chip
  * @sels:		Array of CS lines descriptions
+ * @nand_timing_mode:	nand-timing-mode from dts
  */
 struct marvell_nand_chip {
 	struct nand_chip chip;
@@ -339,6 +361,7 @@  struct marvell_nand_chip {
 	int addr_cyc;
 	int selected_die;
 	unsigned int nsels;
+	int nand_timing_mode;
 	struct marvell_nand_chip_sel sels[];
 };
 
@@ -367,6 +390,10 @@  static inline struct marvell_nand_chip_sel *to_nand_sel(struct marvell_nand_chip
  *			BCH error detection and correction algorithm,
  *			NDCB3 register has been added
  * @use_dma:		Use dma for data transfers
+ * @is_marvell_timing_modes: use marvell predefined register values per mode
+ * @max_mode_number: supported mode by NFC (max mode that supported)
+ * @timing_mode_set: which set to use from predefined array of sets
+					 each set has few modes
  */
 struct marvell_nfc_caps {
 	unsigned int max_cs_nb;
@@ -375,6 +402,9 @@  struct marvell_nfc_caps {
 	bool legacy_of_bindings;
 	bool is_nfcv2;
 	bool use_dma;
+	bool is_marvell_timing_modes;
+	unsigned int max_mode_number;
+	marvell_nfc_timing_mode_set_t timing_mode_set;
 };
 
 /**
@@ -485,6 +515,118 @@  struct marvell_nfc_op {
 	const struct nand_op_instr *data_instr;
 };
 
+/* NFC ndtr0 */
+typedef union  marvell_nand_ndtr0 {
+	struct {
+		unsigned  int tRP                 :3;  /* 0-2   */
+		unsigned  int tRH                 :3;  /* 3-5   */
+		unsigned  int tRPE                :1;  /* 6     */
+		unsigned  int tRE_edge            :1;  /* 7     */
+		unsigned  int tWP                 :3;  /* 8-10  */
+		unsigned  int tWH                 :3;  /* 11-13 */
+		unsigned  int reserved            :2;  /* 14-15 */
+		unsigned  int tCS                 :3;  /* 16-18 */
+		unsigned  int tCH                 :3;  /* 19-21 */
+		unsigned  int Rd_Cnt_Del          :4;  /* 22-25 */
+		unsigned  int selCnrl             :1;  /* 26    */
+		unsigned  int tADL                :5;  /* 27-31 */
+	} fields;
+	unsigned  int  regValue;
+} marvell_nfc_ndtr0_t;
+
+/* NFC ndtr1 */
+typedef union  marvell_nand_ndtr1 {
+	struct {
+		unsigned  int tAR                 :4;  /* 0-3   */
+		unsigned  int tWHR                :4;  /* 4-7   */
+		unsigned  int tRHW                :2;  /* 8-9   */
+		unsigned  int reserved            :4;  /* 10-13 */
+		unsigned  int Prescale            :1;  /* 14    */
+		unsigned  int wait_mode           :1;  /* 15    */
+		unsigned  int tR                  :16; /* 16-31 */
+	} fields;
+	unsigned  int  regValue;
+} marvell_nfc_ndtr1_t;
+
+#define NUM_OF_TIMING_MODES	6
+
+/* arrays of NFC timings modes */
+typedef marvell_nfc_ndtr0_t marvell_nfc_ndtr0_arr[NUM_OF_TIMING_MODES];
+typedef marvell_nfc_ndtr1_t marvell_nfc_ndtr1_arr[NUM_OF_TIMING_MODES];
+
+#define MARVELL_NTDR0(trp, trh, trpe, tre_edge, twp, twh, resrv, tcs, tch, rd_cnt_del, selcnrl, tadl)	\
+		{\
+			.fields = {\
+				.tRP = trp,                 /* 0-2   */\
+				.tRH = trh,                 /* 3-5   */\
+				.tRPE = trpe,               /* 6     */\
+				.tRE_edge = tre_edge,       /* 7     */\
+				.tWP = twp,                 /* 8-10  */\
+				.tWH = twh,                 /* 11-13 */\
+				.reserved = resrv,          /* 14-15 */\
+				.tCS = tcs,                 /* 16-18 */\
+				.tCH = tch,                 /* 19-21 */\
+				.Rd_Cnt_Del = rd_cnt_del,   /* 22-25 */\
+				.selCnrl = selcnrl,         /* 26    */\
+				.tADL = tadl,               /* 27-31 */\
+			} \
+		}
+
+#define MARVELL_NTDR1(tar, twhr, trhw, resrv, prescale, waiting_mode, tr)	\
+		{\
+			.fields = {\
+				.tAR = tar,                 /* 0-3   */\
+				.tWHR = twhr,               /* 4-7   */\
+				.tRHW = trhw,               /* 8-9   */\
+				.reserved = resrv,          /* 10-13 */\
+				.Prescale = prescale,       /* 14    */\
+				.wait_mode = waiting_mode,  /* 15    */\
+				.tR = tr,                   /* 16-31 */\
+			} \
+		}
+
+/* ndtr0_modes and ndtr1_modes are arrays of modes with optimal values
+ * that were tested with Marvell NFC with correlation to ONFI timings mode
+ * each entry in the array presents different set of modes , for example ac5
+ * is entry 0.
+ */
+/* todo: add more modes ASAP */
+
+/* Layouts explained in AN-379_Marvell_SoC_NFC_ECC */
+marvell_nfc_ndtr0_arr ndtr0_modes[MARVELL_NFC_NDTR_NUM_OF_SET] = {
+
+	/* value tested with AC5 */
+	{
+		MARVELL_NTDR0(7, 7, 1, 1, 7, 7, 0, 7, 7, 12, 1, 31),
+		MARVELL_NTDR0(6, 3, 0, 0, 5, 4, 0, 7, 7, 1, 1, 15),
+		MARVELL_NTDR0(4, 3, 0, 0, 3, 3, 0, 7, 7, 2, 1, 15),
+		MARVELL_NTDR0(3, 2, 0, 0, 3, 2, 0, 1, 0, 2, 1, 15)
+	}
+};
+
+marvell_nfc_ndtr1_arr ndtr1_modes[MARVELL_NFC_NDTR_NUM_OF_SET] = {
+
+	/* value tested with AC5 */
+	{
+		MARVELL_NTDR1(15, 15, 3, 0, 0, 1, 50),
+		MARVELL_NTDR1(15, 15, 3, 0, 0, 1, 25),
+		MARVELL_NTDR1(15, 15, 3, 0, 0, 1, 25),
+		MARVELL_NTDR1(11, 11, 2, 0, 0, 1, 25)
+	}
+};
+
+/*
+ * get nand timing-mode from device tree
+ */
+static int get_nand_timing_mode(struct device_node *np)
+{
+	int ret;
+	u32 val;
+
+	ret = of_property_read_u32(np, "nand-timing-mode", &val);
+	return ret ? ret : val;
+}
+
 /*
  * Internal helper to conditionnally apply a delay (from the above structure,
  * most of the time).
@@ -2257,9 +2399,21 @@  static int marvell_nand_hw_ecc_controller_init(struct mtd_info *mtd,
 	}
 
 	mtd_set_ooblayout(mtd, &marvell_nand_ooblayout_ops);
-	ecc->steps = l->nchunks;
 	ecc->size = l->data_bytes;
 
+	/* nand_scan_tail func perform  validity tests for ECC strength, and it
+	 * assumes that all chunks are with same size. in our case when ecc is 12
+	 * the chunk size is 704 but the last chunk is with different size so
+	 * we cheat it nand_scan_tail validity tests by set info->ecc_size value to
+	 * 512.
+	 */
+	if (ecc->strength == 12)
+		ecc->size = 512;
+
+	/* let nand_scan_tail() calculate these two fields */
+	ecc->steps = 0;
+	ecc->total = 0;
+
 	if (ecc->strength == 1) {
 		chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
 		ecc->read_page_raw = marvell_nfc_hw_ecc_hmg_read_page_raw;
@@ -2360,9 +2514,11 @@  static int marvell_nfc_setup_interface(struct nand_chip *chip, int chipnr,
 	struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
 	struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
 	unsigned int period_ns = 1000000000 / clk_get_rate(nfc->core_clk) * 2;
-	const struct nand_sdr_timings *sdr;
+	const struct nand_sdr_timings *sdr, *timings;
 	struct marvell_nfc_timings nfc_tmg;
 	int read_delay;
+	marvell_nfc_timing_mode_set_t modes_set;
+	int mode = 0;
 
 	sdr = nand_get_sdr_timings(conf);
 	if (IS_ERR(sdr))
@@ -2421,32 +2577,71 @@  static int marvell_nfc_setup_interface(struct nand_chip *chip, int chipnr,
 			nfc_tmg.tR = 0;
 	}
 
-	if (chipnr < 0)
-		return 0;
 
-	marvell_nand->ndtr0 =
-		NDTR0_TRP(nfc_tmg.tRP) |
-		NDTR0_TRH(nfc_tmg.tRH) |
-		NDTR0_ETRP(nfc_tmg.tRP) |
-		NDTR0_TWP(nfc_tmg.tWP) |
-		NDTR0_TWH(nfc_tmg.tWH) |
-		NDTR0_TCS(nfc_tmg.tCS) |
-		NDTR0_TCH(nfc_tmg.tCH);
+	/* get the timing modes from predefined values according to its compatibility */
+	if (nfc->caps->is_marvell_timing_modes) {
+		/* get the mode set */
+		modes_set = nfc->caps->timing_mode_set;
+		if (modes_set >= MARVELL_NFC_NDTR_SET_NON) {
+			dev_warn(nfc->dev,
+				"Warning: not supported timing registers set,use set number 0 by default\n");
 
-	marvell_nand->ndtr1 =
-		NDTR1_TAR(nfc_tmg.tAR) |
-		NDTR1_TWHR(nfc_tmg.tWHR) |
-		NDTR1_TR(nfc_tmg.tR);
+			modes_set = MARVELL_NFC_NDTR_SET_0;
+		}
 
-	if (nfc->caps->is_nfcv2) {
-		marvell_nand->ndtr0 |=
-			NDTR0_RD_CNT_DEL(read_delay) |
-			NDTR0_SELCNTR |
-			NDTR0_TADL(nfc_tmg.tADL);
+		/* find the caller mode according to timings values */
+		/* if exit on error it means no more modes; not suppose to happen */
+		do {
+			timings = onfi_async_timing_mode_to_sdr_timings(mode);
+			if (IS_TIMINGS_EQUAL(timings, sdr))
+				break;
+			mode++;
+		} while (!IS_ERR(timings));
+
+		/* if mode is not supported by NFC, return false or if nand-timing-mode that
+		 * exists in device tree greater then caller mode also return false and wait
+		 * for caller to try with next mode (mode-1). we want the nand feature to be
+		 * configured with nand-timing-mode value.
+		 */
+		if (mode > nfc->caps->max_mode_number ||
+			 ((marvell_nand->nand_timing_mode) >= 0 &&
+			 (mode > marvell_nand->nand_timing_mode)))
+			return -EOPNOTSUPP;
+
+		/* just checking NFC capabilities no need to set the registers */
+		if (chipnr < 0)
+			return 0;
 
-		marvell_nand->ndtr1 |=
-			NDTR1_TRHW(nfc_tmg.tRHW) |
-			NDTR1_WAIT_MODE;
+		marvell_nand->ndtr0 = ndtr0_modes[modes_set][mode].regValue;
+		marvell_nand->ndtr1 = ndtr1_modes[modes_set][mode].regValue;
+	} else {
+		if (chipnr < 0)
+			return 0;
+
+		marvell_nand->ndtr0 =
+			NDTR0_TRP(nfc_tmg.tRP) |
+			NDTR0_TRH(nfc_tmg.tRH) |
+			NDTR0_ETRP(nfc_tmg.tRP) |
+			NDTR0_TWP(nfc_tmg.tWP) |
+			NDTR0_TWH(nfc_tmg.tWH) |
+			NDTR0_TCS(nfc_tmg.tCS) |
+			NDTR0_TCH(nfc_tmg.tCH);
+
+		marvell_nand->ndtr1 =
+			NDTR1_TAR(nfc_tmg.tAR) |
+			NDTR1_TWHR(nfc_tmg.tWHR) |
+			NDTR1_TR(nfc_tmg.tR);
+
+		if (nfc->caps->is_nfcv2) {
+			marvell_nand->ndtr0 |=
+				NDTR0_RD_CNT_DEL(read_delay) |
+				NDTR0_SELCNTR |
+				NDTR0_TADL(nfc_tmg.tADL);
+
+			marvell_nand->ndtr1 |=
+				NDTR1_TRHW(nfc_tmg.tRHW) |
+				NDTR1_WAIT_MODE;
+		}
 	}
 
 	return 0;
@@ -2568,6 +2763,7 @@  static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
 	struct nand_chip *chip;
 	int nsels, ret, i;
 	u32 cs, rb;
+	struct device_node *dn;
 
 	/*
 	 * The legacy "num-cs" property indicates the number of CS on the only
@@ -2681,6 +2877,10 @@  static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
 	if (of_property_read_bool(np, "marvell,nand-keep-config"))
 		chip->options |= NAND_KEEP_TIMINGS;
 
+	/* read the mode from device tree */
+	dn = nand_get_flash_node(chip);
+	marvell_nand->nand_timing_mode = get_nand_timing_mode(dn);
+
 	mtd = nand_to_mtd(chip);
 	mtd->dev.parent = dev;
 
@@ -3064,6 +3264,15 @@  static const struct marvell_nfc_caps marvell_armada_8k_nfc_caps = {
 	.is_nfcv2 = true,
 };
 
+static const struct marvell_nfc_caps marvell_ac5_caps = {
+	.max_cs_nb = 2,
+	.max_rb_nb = 1,
+	.is_nfcv2 = true,
+	.is_marvell_timing_modes = true,
+	.max_mode_number = 3,
+	.timing_mode_set = MARVELL_NFC_NDTR_SET_0,
+};
+
 static const struct marvell_nfc_caps marvell_armada370_nfc_caps = {
 	.max_cs_nb = 4,
 	.max_rb_nb = 2,
@@ -3112,6 +3321,10 @@  static const struct of_device_id marvell_nfc_of_ids[] = {
 		.compatible = "marvell,armada-8k-nand-controller",
 		.data = &marvell_armada_8k_nfc_caps,
 	},
+	{
+		.compatible = "marvell,ac5-nand-controller",
+		.data = &marvell_ac5_caps,
+	},
 	{
 		.compatible = "marvell,armada370-nand-controller",
 		.data = &marvell_armada370_nfc_caps,