[U-Boot] [PATCH v3 1/3] drivers/ddr/fsl: Fix DDR4 RDIMM support

For DDR4, command/address delay in mode registers and parity latency in timing config register are only needed for UDIMMs, but not RDIMMs. Add additional register rcw_3 for DDR4 RDIMM. Fix mirrored bit for dual rank RDIMMs. Set sdram_cfg_3[DIS_MRS_PAR] for RDIMMs. Use hexadecimal format for printing RCW (register control word) registers.
Signed-off-by: York Sun york.sun@nxp.com ---
Changes in v3: None Changes in v2: None
drivers/ddr/fsl/ctrl_regs.c | 32 +++++++++++++++++++++++++------- drivers/ddr/fsl/ddr4_dimm_params.c | 2 ++ drivers/ddr/fsl/interactive.c | 9 +++++++-- include/fsl_ddr_sdram.h | 1 + 4 files changed, 35 insertions(+), 9 deletions(-)
diff --git a/drivers/ddr/fsl/ctrl_regs.c b/drivers/ddr/fsl/ctrl_regs.c index c0ee858..4a1f397 100644 --- a/drivers/ddr/fsl/ctrl_regs.c +++ b/drivers/ddr/fsl/ctrl_regs.c @@ -732,6 +732,7 @@ static void set_ddr_sdram_rcw(fsl_ddr_cfg_regs_t *ddr, if (popts->rcw_override) { ddr->ddr_sdram_rcw_1 = popts->rcw_1; ddr->ddr_sdram_rcw_2 = popts->rcw_2; + ddr->ddr_sdram_rcw_3 = popts->rcw_3; } else { ddr->ddr_sdram_rcw_1 = common_dimm->rcw[0] << 28 | \ @@ -752,8 +753,12 @@ static void set_ddr_sdram_rcw(fsl_ddr_cfg_regs_t *ddr, common_dimm->rcw[14] << 4 | \ common_dimm->rcw[15]; } - debug("FSLDDR: ddr_sdram_rcw_1 = 0x%08x\n", ddr->ddr_sdram_rcw_1); - debug("FSLDDR: ddr_sdram_rcw_2 = 0x%08x\n", ddr->ddr_sdram_rcw_2); + debug("FSLDDR: ddr_sdram_rcw_1 = 0x%08x\n", + ddr->ddr_sdram_rcw_1); + debug("FSLDDR: ddr_sdram_rcw_2 = 0x%08x\n", + ddr->ddr_sdram_rcw_2); + debug("FSLDDR: ddr_sdram_rcw_3 = 0x%08x\n", + ddr->ddr_sdram_rcw_3); } }
@@ -1159,8 +1164,14 @@ static void set_ddr_sdram_mode_9(fsl_ddr_cfg_regs_t *ddr, esdmode5 = 0x00000400; /* Data mask enabled */ }
- /* set command/address parity latency */ - if (ddr->ddr_sdram_cfg_2 & SDRAM_CFG2_AP_EN) { + /* + * For DDR3, set C/A latency if address parity is enabled. + * For DDR4, set C/A latency for UDIMM only. For RDIMM the delay is + * handled by register chip and RCW settings. + */ + if ((ddr->ddr_sdram_cfg_2 & SDRAM_CFG2_AP_EN) && + ((CONFIG_FSL_SDRAM_TYPE != SDRAM_TYPE_DDR4) || + !popts->registered_dimm_en)) { if (mclk_ps >= 935) { /* for DDR4-1600/1866/2133 */ esdmode5 |= DDR_MR5_CA_PARITY_LAT_4_CLK; @@ -1193,7 +1204,9 @@ static void set_ddr_sdram_mode_9(fsl_ddr_cfg_regs_t *ddr, esdmode5 = 0x00000400; }
- if (ddr->ddr_sdram_cfg_2 & SDRAM_CFG2_AP_EN) { + if ((ddr->ddr_sdram_cfg_2 & SDRAM_CFG2_AP_EN) && + ((CONFIG_FSL_SDRAM_TYPE != SDRAM_TYPE_DDR4) || + !popts->registered_dimm_en)) { if (mclk_ps >= 935) { /* for DDR4-1600/1866/2133 */ esdmode5 |= DDR_MR5_CA_PARITY_LAT_4_CLK; @@ -1965,6 +1978,7 @@ static void set_timing_cfg_6(fsl_ddr_cfg_regs_t *ddr)
static void set_timing_cfg_7(const unsigned int ctrl_num, fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts, const common_timing_params_t *common_dimm) { unsigned int txpr, tcksre, tcksrx; @@ -1975,7 +1989,9 @@ static void set_timing_cfg_7(const unsigned int ctrl_num, tcksre = max(5U, picos_to_mclk(ctrl_num, 10000)); tcksrx = max(5U, picos_to_mclk(ctrl_num, 10000));
- if (ddr->ddr_sdram_cfg_2 & SDRAM_CFG2_AP_EN) { + if ((ddr->ddr_sdram_cfg_2 & SDRAM_CFG2_AP_EN) && + ((CONFIG_FSL_SDRAM_TYPE != SDRAM_TYPE_DDR4) || + !popts->registered_dimm_en)) { if (mclk_ps >= 935) { /* parity latency 4 clocks in case of 1600/1866/2133 */ par_lat = 4; @@ -2130,6 +2146,8 @@ static void set_ddr_sdram_cfg_3(fsl_ddr_cfg_regs_t *ddr, rd_pre = popts->quad_rank_present ? 1 : 0;
ddr->ddr_sdram_cfg_3 = (rd_pre & 0x1) << 16; + /* Disable MRS on parity error for RDIMMs */ + ddr->ddr_sdram_cfg_3 |= popts->registered_dimm_en ? 1 : 0;
debug("FSLDDR: ddr_sdram_cfg_3 = 0x%08x\n", ddr->ddr_sdram_cfg_3); } @@ -2535,7 +2553,7 @@ compute_fsl_memctl_config_regs(const unsigned int ctrl_num, #ifdef CONFIG_SYS_FSL_DDR4 set_ddr_sdram_cfg_3(ddr, popts); set_timing_cfg_6(ddr); - set_timing_cfg_7(ctrl_num, ddr, common_dimm); + set_timing_cfg_7(ctrl_num, ddr, popts, common_dimm); set_timing_cfg_8(ctrl_num, ddr, popts, common_dimm, cas_latency); set_timing_cfg_9(ddr); set_ddr_dq_mapping(ddr, dimm_params); diff --git a/drivers/ddr/fsl/ddr4_dimm_params.c b/drivers/ddr/fsl/ddr4_dimm_params.c index 42834ca..6e26ba8 100644 --- a/drivers/ddr/fsl/ddr4_dimm_params.c +++ b/drivers/ddr/fsl/ddr4_dimm_params.c @@ -179,6 +179,8 @@ unsigned int ddr_compute_dimm_parameters(const unsigned int ctrl_num, case DDR4_SPD_MODULETYPE_RDIMM: /* Registered/buffered DIMMs */ pdimm->registered_dimm = 1; + if (spd->mod_section.registered.reg_map & 0x1) + pdimm->mirrored_dimm = 1; break;
case DDR4_SPD_MODULETYPE_UDIMM: diff --git a/drivers/ddr/fsl/interactive.c b/drivers/ddr/fsl/interactive.c index c99bd2f..660060d 100644 --- a/drivers/ddr/fsl/interactive.c +++ b/drivers/ddr/fsl/interactive.c @@ -558,6 +558,7 @@ static void fsl_ddr_options_edit(fsl_ddr_info_t *pinfo, */ CTRL_OPTIONS(twot_en), CTRL_OPTIONS(threet_en), + CTRL_OPTIONS(mirrored_dimm), CTRL_OPTIONS(ap_en), CTRL_OPTIONS(x4_en), CTRL_OPTIONS(bstopre), @@ -568,6 +569,7 @@ static void fsl_ddr_options_edit(fsl_ddr_info_t *pinfo, CTRL_OPTIONS(rcw_override), CTRL_OPTIONS(rcw_1), CTRL_OPTIONS(rcw_2), + CTRL_OPTIONS(rcw_3), CTRL_OPTIONS(ddr_cdr1), CTRL_OPTIONS(ddr_cdr2), CTRL_OPTIONS(tfaw_window_four_activates_ps), @@ -660,6 +662,7 @@ static void print_fsl_memctl_config_regs(const fsl_ddr_cfg_regs_t *ddr) CFG_REGS(ddr_sr_cntr), CFG_REGS(ddr_sdram_rcw_1), CFG_REGS(ddr_sdram_rcw_2), + CFG_REGS(ddr_sdram_rcw_3), CFG_REGS(ddr_cdr1), CFG_REGS(ddr_cdr2), CFG_REGS(dq_map_0), @@ -750,6 +753,7 @@ static void fsl_ddr_regs_edit(fsl_ddr_info_t *pinfo, CFG_REGS(ddr_sr_cntr), CFG_REGS(ddr_sdram_rcw_1), CFG_REGS(ddr_sdram_rcw_2), + CFG_REGS(ddr_sdram_rcw_3), CFG_REGS(ddr_cdr1), CFG_REGS(ddr_cdr2), CFG_REGS(dq_map_0), @@ -857,8 +861,9 @@ static void print_memctl_options(const memctl_options_t *popts) CTRL_OPTIONS(wrlvl_start), CTRL_OPTIONS_HEX(cswl_override), CTRL_OPTIONS(rcw_override), - CTRL_OPTIONS(rcw_1), - CTRL_OPTIONS(rcw_2), + CTRL_OPTIONS_HEX(rcw_1), + CTRL_OPTIONS_HEX(rcw_2), + CTRL_OPTIONS_HEX(rcw_3), CTRL_OPTIONS_HEX(ddr_cdr1), CTRL_OPTIONS_HEX(ddr_cdr2), CTRL_OPTIONS(tfaw_window_four_activates_ps), diff --git a/include/fsl_ddr_sdram.h b/include/fsl_ddr_sdram.h index 6a1f04b..de7ef9b 100644 --- a/include/fsl_ddr_sdram.h +++ b/include/fsl_ddr_sdram.h @@ -408,6 +408,7 @@ typedef struct memctl_options_s { unsigned int rcw_override; unsigned int rcw_1; unsigned int rcw_2; + unsigned int rcw_3; /* control register 1 */ unsigned int ddr_cdr1; unsigned int ddr_cdr2;

Wrong field was masked in this workaround due to wrong endianness. The impacted SoCs have big-endian.
Signed-off-by: York Sun york.sun@nxp.com ---
Changes in v3: None Changes in v2: None
drivers/ddr/fsl/fsl_ddr_gen4.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/ddr/fsl/fsl_ddr_gen4.c b/drivers/ddr/fsl/fsl_ddr_gen4.c index b3a27ec..7df9178 100644 --- a/drivers/ddr/fsl/fsl_ddr_gen4.c +++ b/drivers/ddr/fsl/fsl_ddr_gen4.c @@ -210,7 +210,7 @@ void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, if (regs->ddr_sdram_cfg_2 & SDRAM_CFG2_AP_EN) { if (regs->ddr_sdram_cfg & SDRAM_CFG_RD_EN) { /* for RDIMM */ ddr_out32(&ddr->ddr_sdram_rcw_2, - regs->ddr_sdram_rcw_2 & ~0x0f000000); + regs->ddr_sdram_rcw_2 & ~0xf0); } ddr_out32(&ddr->err_disable, regs->err_disable | DDR_ERR_DISABLE_APED);

This adds 2Rx8 RDIMM on LS1046ARDB board. Tested with address parity enabled and disabled with RDIMM MTA18ASF2G72PDZ.
Signed-off-by: York Sun york.sun@nxp.com
---
Changes in v3: Update register control words for different speeds.
Changes in v2: Update timing table for lower speeds
board/freescale/ls1046ardb/ddr.c | 27 ++++++++++++++++++++++++--- board/freescale/ls1046ardb/ddr.h | 16 ++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-)
diff --git a/board/freescale/ls1046ardb/ddr.c b/board/freescale/ls1046ardb/ddr.c index fb4f6ab..ca48f28 100644 --- a/board/freescale/ls1046ardb/ddr.c +++ b/board/freescale/ls1046ardb/ddr.c @@ -29,7 +29,10 @@ void fsl_ddr_board_options(memctl_options_t *popts, if (!pdimm->n_ranks) return;
- pbsp = udimms[0]; + if (popts->registered_dimm_en) + pbsp = rdimms[0]; + else + pbsp = udimms[0];
/* Get clk_adjust, wrlvl_start, wrlvl_ctl, according to the board ddr * freqency and n_banks specified in board_specific_parameters table. @@ -66,8 +69,6 @@ found: pbsp->n_ranks, pbsp->datarate_mhz_high, pbsp->rank_gb);
popts->data_bus_width = 0; /* 64-bit data bus */ - popts->otf_burst_chop_en = 0; - popts->burst_length = DDR_BL8; popts->bstopre = 0; /* enable auto precharge */
/* @@ -93,6 +94,26 @@ found: popts->ddr_cdr2 = DDR_CDR2_ODT(DDR_CDR_ODT_80ohm) | DDR_CDR2_VREF_TRAIN_EN | DDR_CDR2_VREF_RANGE_2;
+ if (popts->registered_dimm_en) { + popts->rcw_1 = 0x000655f0; + if (ddr_freq > 3200) + popts->rcw_2 = 0xb0700c03; + else if (ddr_freq > 2933) + popts->rcw_2 = 0xb0600c02; + else if (ddr_freq > 2666) + popts->rcw_2 = 0xb0500c02; + else if (ddr_freq > 2400) + popts->rcw_2 = 0xb0400c02; + else if (ddr_freq > 2133) + popts->rcw_2 = 0xb0300c01; + else if (ddr_freq > 1866) + popts->rcw_2 = 0xb0100c00; + else + popts->rcw_2 = 0xb0000c00; + + popts->rcw_3 = ((ddr_freq - 1260 + 19) / 20) << 8; + } + /* optimize cpo for erratum A-009942 */ popts->cpo_sample = 0x70; } diff --git a/board/freescale/ls1046ardb/ddr.h b/board/freescale/ls1046ardb/ddr.h index 9e440f6..ffc2ef9 100644 --- a/board/freescale/ls1046ardb/ddr.h +++ b/board/freescale/ls1046ardb/ddr.h @@ -41,4 +41,20 @@ static const struct board_specific_parameters *udimms[] = { udimm0, };
+static const struct board_specific_parameters rdimm0[] = { + /* + * memory controller 0 + * num| hi| rank| clk| wrlvl | wrlvl | wrlvl + * ranks| mhz| GB |adjst| start | ctl2 | ctl3 + */ + {2, 1666, 0, 0x8, 0x15, 0x14131110, 0x12131410,}, + {2, 1900, 0, 0x8, 0x16, 0x15141311, 0x13141511,}, + {2, 2300, 0, 0xa, 0x1A, 0x18171513, 0x15161814,}, + {} +}; + +static const struct board_specific_parameters *rdimms[] = { + rdimm0, +}; + #endif

Hi York,
On 24 January 2018 at 12:04, York Sun york.sun@nxp.com wrote:
For DDR4, command/address delay in mode registers and parity latency in timing config register are only needed for UDIMMs, but not RDIMMs. Add additional register rcw_3 for DDR4 RDIMM. Fix mirrored bit for dual rank RDIMMs. Set sdram_cfg_3[DIS_MRS_PAR] for RDIMMs. Use hexadecimal format for printing RCW (register control word) registers.
Signed-off-by: York Sun york.sun@nxp.com
Changes in v3: None Changes in v2: None
drivers/ddr/fsl/ctrl_regs.c | 32 +++++++++++++++++++++++++------- drivers/ddr/fsl/ddr4_dimm_params.c | 2 ++ drivers/ddr/fsl/interactive.c | 9 +++++++-- include/fsl_ddr_sdram.h | 1 + 4 files changed, 35 insertions(+), 9 deletions(-)
What do you think about moving this into drivers/ram (separate from this patch)? Do you think it could use the uclass?
Regards, Simon

On 02/04/2018 05:40 AM, Simon Glass wrote:
Hi York,
On 24 January 2018 at 12:04, York Sun york.sun@nxp.com wrote:
For DDR4, command/address delay in mode registers and parity latency in timing config register are only needed for UDIMMs, but not RDIMMs. Add additional register rcw_3 for DDR4 RDIMM. Fix mirrored bit for dual rank RDIMMs. Set sdram_cfg_3[DIS_MRS_PAR] for RDIMMs. Use hexadecimal format for printing RCW (register control word) registers.
Signed-off-by: York Sun york.sun@nxp.com
Changes in v3: None Changes in v2: None
drivers/ddr/fsl/ctrl_regs.c | 32 +++++++++++++++++++++++++------- drivers/ddr/fsl/ddr4_dimm_params.c | 2 ++ drivers/ddr/fsl/interactive.c | 9 +++++++-- include/fsl_ddr_sdram.h | 1 + 4 files changed, 35 insertions(+), 9 deletions(-)
What do you think about moving this into drivers/ram (separate from this patch)? Do you think it could use the uclass?
Simon,
Moving to drivers/ram is not a problem. My concern is it is worth the effort. Legacy PowerPC-based SoCs are still alive but I don't see any new products coming. ARM-based SoCs will move to ATF (or alike) boots first model. Once ATF boots first, U-Boot will no longer need this driver (PowerPC SoCs still use it though). Let's discuss this again a few months later when I may have a better vision.
York
participants (2)
-
Simon Glass
-
York Sun