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