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

From: Vadym Kochan
Date: Wed Oct 19 2022 - 04:22:23 EST


From: Aviram Dali <aviramd@xxxxxxxxxxx>

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@xxxxxxxxxxx>
Signed-off-by: Vadym Kochan <vadym.kochan@xxxxxxxxxxx>
---
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