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

Vadym Kochan vadym.kochan at plvision.eu
Wed Oct 19 01:20:40 PDT 2022


From: Aviram Dali <aviramd at 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 at marvell.com>
Signed-off-by: Vadym Kochan <vadym.kochan at 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),
 };
 
 /**
@@ -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,
-- 
2.17.1




More information about the linux-mtd mailing list