[U-Boot] [PATCH 1/2] driver/ddr/fsl: add command/address parity for DDR4 UDIMM

Add support of command/address parity for DDR4 UDIMM or discrete memory. It requires to configurate corresponding MR5[2:0] and TIMING_CFG_7[PAR_LAT]. Parity can be turned on/off by hwconfig, e.g. hwconfig=fsl_ddr:parity=on.
Signed-off-by: Shengzhou Liu Shengzhou.Liu@nxp.com --- doc/README.fsl-ddr | 8 +++++++ drivers/ddr/fsl/ctrl_regs.c | 52 +++++++++++++++++++++++++++++++++++++++------ drivers/ddr/fsl/options.c | 12 +++++++++-- include/fsl_ddr_sdram.h | 11 +++++++++- 4 files changed, 73 insertions(+), 10 deletions(-)
diff --git a/doc/README.fsl-ddr b/doc/README.fsl-ddr index cd71ec8..80f91d3 100644 --- a/doc/README.fsl-ddr +++ b/doc/README.fsl-ddr @@ -143,6 +143,14 @@ platform
hwconfig=fsl_ddr:addr_hash=true,ctlr_intlv=cacheline,bank_intlv=cs0_cs1_cs2_cs3,ecc=on
+ +Memory address parity on/off +============================ +address parity can be turned on/off by hwconfig, it's disabled by default. +Syntax is: +hwconfig=fsl_ddr:parity=on + + Table for dynamic ODT for DDR3 ============================== For single-slot system with quad-rank DIMM and dual-slot system, dynamic ODT may diff --git a/drivers/ddr/fsl/ctrl_regs.c b/drivers/ddr/fsl/ctrl_regs.c index 0bfcd34..1f5b9fc 100644 --- a/drivers/ddr/fsl/ctrl_regs.c +++ b/drivers/ddr/fsl/ctrl_regs.c @@ -895,12 +895,10 @@ static void set_ddr_sdram_cfg_2(const unsigned int ctrl_num, slow = get_ddr_freq(ctrl_num) < 1249000000; #endif
- if (popts->registered_dimm_en) { + ap_en = popts->ap_en; + + if (popts->registered_dimm_en) rcw_en = 1; - ap_en = popts->ap_en; - } else { - ap_en = 0; - }
x4_en = popts->x4_en ? 1 : 0;
@@ -1135,6 +1133,7 @@ static void set_ddr_sdram_mode_9(fsl_ddr_cfg_regs_t *ddr, unsigned short esdmode5; /* Extended SDRAM mode 5 */ int rtt_park = 0; bool four_cs = false; + const unsigned int mclk_ps = get_memory_clk_period_ps(0);
#if CONFIG_CHIP_SELECTS_PER_CTRL == 4 if ((ddr->cs[0].config & SDRAM_CS_CONFIG_EN) && @@ -1150,6 +1149,19 @@ 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) { + if (mclk_ps >= 935) { + /* for DDR4-1600/1866/2133 */ + esdmode5 |= DDR_MR5_CA_PARITY_LAT_4_CLK; + } else if (mclk_ps >= 833) { + /* for DDR4-2400 */ + esdmode5 |= DDR_MR5_CA_PARITY_LAT_5_CLK; + } else { + printf("parity: mclk_ps = %d not supported\n", mclk_ps); + } + } + ddr->ddr_sdram_mode_9 = (0 | ((esdmode4 & 0xffff) << 16) | ((esdmode5 & 0xffff) << 0) @@ -1170,6 +1182,19 @@ static void set_ddr_sdram_mode_9(fsl_ddr_cfg_regs_t *ddr, } else { esdmode5 = 0x00000400; } + + if (ddr->ddr_sdram_cfg_2 & SDRAM_CFG2_AP_EN) { + if (mclk_ps >= 935) { + /* for DDR4-1600/1866/2133 */ + esdmode5 |= DDR_MR5_CA_PARITY_LAT_4_CLK; + } else if (mclk_ps >= 833) { + /* for DDR4-2400 */ + esdmode5 |= DDR_MR5_CA_PARITY_LAT_5_CLK; + } else { + printf("parity: the speed reserved\n"); + } + } + switch (i) { case 1: ddr->ddr_sdram_mode_11 = (0 @@ -1925,12 +1950,25 @@ static void set_timing_cfg_7(const unsigned int ctrl_num, const common_timing_params_t *common_dimm) { unsigned int txpr, tcksre, tcksrx; - unsigned int cke_rst, cksre, cksrx, par_lat, cs_to_cmd; + unsigned int cke_rst, cksre, cksrx, par_lat = 0, cs_to_cmd; + const unsigned int mclk_ps = get_memory_clk_period_ps(ctrl_num);
txpr = max(5U, picos_to_mclk(ctrl_num, common_dimm->trfc1_ps + 10000)); tcksre = max(5U, picos_to_mclk(ctrl_num, 10000)); tcksrx = max(5U, picos_to_mclk(ctrl_num, 10000)); - par_lat = 0; + + if (ddr->ddr_sdram_cfg_2 & SDRAM_CFG2_AP_EN) { + if (mclk_ps >= 935) { + /* parity latency 4 clocks in case of 1600/1866/2133 */ + par_lat = 4; + } else if (mclk_ps >= 833) { + /* parity latency 5 clocks for DDR4-2400 */ + par_lat = 5; + } else { + printf("parity: mclk_ps = %d not supported\n", mclk_ps); + } + } + cs_to_cmd = 0;
if (txpr <= 200) diff --git a/drivers/ddr/fsl/options.c b/drivers/ddr/fsl/options.c index 791d644..9ea80af 100644 --- a/drivers/ddr/fsl/options.c +++ b/drivers/ddr/fsl/options.c @@ -1002,8 +1002,16 @@ unsigned int populate_memctl_options(const common_timing_params_t *common_dimm, popts->twot_en = 0; popts->threet_en = 0;
- /* for RDIMM, address parity enable */ - popts->ap_en = 1; + /* for RDIMM and DDR4 UDIMM/discrete memory, address parity enable */ + popts->ap_en = 0; /* 0 = disable, 1 = enable */ + + if (hwconfig_sub_f("fsl_ddr", "parity", buf)) { + if (hwconfig_subarg_cmp_f("fsl_ddr", "parity", "on", buf)) { + if (popts->registered_dimm_en || + (CONFIG_FSL_SDRAM_TYPE == SDRAM_TYPE_DDR4)) + popts->ap_en = 1; + } + }
/* * BSTTOPRE precharge interval diff --git a/include/fsl_ddr_sdram.h b/include/fsl_ddr_sdram.h index 3699c04..cf316a4 100644 --- a/include/fsl_ddr_sdram.h +++ b/include/fsl_ddr_sdram.h @@ -123,6 +123,7 @@ typedef struct ddr4_spd_eeprom_s generic_spd_eeprom_t;
#define SDRAM_CFG2_FRC_SR 0x80000000 #define SDRAM_CFG2_D_INIT 0x00000010 +#define SDRAM_CFG2_AP_EN 0x00000020 #define SDRAM_CFG2_ODT_CFG_MASK 0x00600000 #define SDRAM_CFG2_ODT_NEVER 0 #define SDRAM_CFG2_ODT_ONLY_WRITE 1 @@ -177,6 +178,14 @@ typedef struct ddr4_spd_eeprom_s generic_spd_eeprom_t; #define DDR_CDR2_VREF_TRAIN_EN 0x00000080 #define DDR_CDR2_VREF_RANGE_2 0x00000040
+/* DDR ERR_DISABLE */ +#define DDR_ERR_DISABLE_APED (1 << 8) /* Address parity error disable */ + +/* Mode Registers */ +#define DDR_MR5_CA_PARITY_LAT_4_CLK 0x1 /* for DDR4-1600/1866/2133 */ +#define DDR_MR5_CA_PARITY_LAT_5_CLK 0x2 /* for DDR4-2400 */ + + #if (defined(CONFIG_SYS_FSL_DDR_VER) && \ (CONFIG_SYS_FSL_DDR_VER >= FSL_DDR_VER_4_7)) #ifdef CONFIG_SYS_FSL_DDR3L @@ -343,7 +352,7 @@ typedef struct memctl_options_s { /* mirrior DIMMs for DDR3 */ unsigned int mirrored_dimm; unsigned int quad_rank_present; - unsigned int ap_en; /* address parity enable for RDIMM */ + unsigned int ap_en; /* address parity enable for RDIMM/DDR4-UDIMM */ unsigned int x4_en; /* enable x4 devices */
/* Global Timing Parameters */

During the initial DDR training, false parity errors may be detected. This can lead to the ERR_DETECT[APE], ERR_DETECT[MME] bits getting set during DDR training and the DDR could enter a bad state to try and recover since it thinks a parity error has occurred. This patch adds workaround to fix the erratum.
Signed-off-by: Shengzhou Liu Shengzhou.Liu@nxp.com --- arch/arm/include/asm/arch-fsl-layerscape/config.h | 1 + drivers/ddr/fsl/fsl_ddr_gen4.c | 44 ++++++++++++++++++++--- 2 files changed, 40 insertions(+), 5 deletions(-)
diff --git a/arch/arm/include/asm/arch-fsl-layerscape/config.h b/arch/arm/include/asm/arch-fsl-layerscape/config.h index 0ef7c9d..b0c112b 100644 --- a/arch/arm/include/asm/arch-fsl-layerscape/config.h +++ b/arch/arm/include/asm/arch-fsl-layerscape/config.h @@ -119,6 +119,7 @@ #define CONFIG_SYS_FSL_ERRATUM_A008751 #define CONFIG_SYS_FSL_ERRATUM_A009635 #define CONFIG_SYS_FSL_ERRATUM_A009663 +#define CONFIG_SYS_FSL_ERRATUM_A009803 #define CONFIG_SYS_FSL_ERRATUM_A009942
/* ARM A57 CORE ERRATA */ diff --git a/drivers/ddr/fsl/fsl_ddr_gen4.c b/drivers/ddr/fsl/fsl_ddr_gen4.c index 6f76980..608810d 100644 --- a/drivers/ddr/fsl/fsl_ddr_gen4.c +++ b/drivers/ddr/fsl/fsl_ddr_gen4.c @@ -12,7 +12,8 @@ #include <fsl_ddr.h> #include <fsl_errata.h>
-#ifdef CONFIG_SYS_FSL_ERRATUM_A008511 +#if defined(CONFIG_SYS_FSL_ERRATUM_A008511) | \ + defined(CONFIG_SYS_FSL_ERRATUM_A009803) static void set_wait_for_bits_clear(void *ptr, u32 value, u32 bits) { int timeout = 1000; @@ -24,9 +25,9 @@ static void set_wait_for_bits_clear(void *ptr, u32 value, u32 bits) timeout--; } if (timeout <= 0) - puts("Error: A007865 wait for clear timeout.\n"); + puts("Error: wait for clear timeout.\n"); } -#endif /* CONFIG_SYS_FSL_ERRATUM_A008511 */ +#endif
#if (CONFIG_CHIP_SELECTS_PER_CTRL > 4) #error Invalid setting for CONFIG_CHIP_SELECTS_PER_CTRL @@ -201,7 +202,18 @@ void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, ddr_out32(&ddr->init_ext_addr, regs->ddr_init_ext_addr); ddr_out32(&ddr->ddr_cdr2, regs->ddr_cdr2); } + +#ifdef CONFIG_SYS_FSL_ERRATUM_A009803 + /* part 1 of 2 */ + if (regs->ddr_sdram_cfg & SDRAM_CFG_RD_EN) { /* for RDIMM */ + ddr_out32(&ddr->ddr_sdram_rcw_2, + regs->ddr_sdram_rcw_2 & ~0x0f000000); + } + + ddr_out32(&ddr->err_disable, regs->err_disable | DDR_ERR_DISABLE_APED); +#else ddr_out32(&ddr->err_disable, regs->err_disable); +#endif ddr_out32(&ddr->err_int_en, regs->err_int_en); for (i = 0; i < 32; i++) { if (regs->debug[i]) { @@ -297,7 +309,8 @@ step2: mb(); isb();
-#ifdef CONFIG_SYS_FSL_ERRATUM_A008511 +#if defined(CONFIG_SYS_FSL_ERRATUM_A008511) || \ + defined(CONFIG_SYS_FSL_ERRATUM_A009803) /* Part 2 of 2 */ /* This erraum only applies to verion 5.2.0 */ if (fsl_ddr_get_version(ctrl_num) == 0x50200) { @@ -313,6 +326,7 @@ step2: ctrl_num, ddr_in32(&ddr->debug[1])); }
+#ifdef CONFIG_SYS_FSL_ERRATUM_A008511 /* The vref setting sequence is different for range 2 */ if (regs->ddr_cdr2 & DDR_CDR2_VREF_RANGE_2) vref_seq = vref_seq2; @@ -359,9 +373,29 @@ step2: } /* Restore D_INIT */ ddr_out32(&ddr->sdram_cfg_2, regs->ddr_sdram_cfg_2); - } #endif /* CONFIG_SYS_FSL_ERRATUM_A008511 */
+#ifdef CONFIG_SYS_FSL_ERRATUM_A009803 + /* if it's RDIMM */ + if (regs->ddr_sdram_cfg & SDRAM_CFG_RD_EN) { + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + if (!(regs->cs[i].config & SDRAM_CS_CONFIG_EN)) + continue; + set_wait_for_bits_clear(&ddr->sdram_md_cntl, + MD_CNTL_MD_EN | + MD_CNTL_CS_SEL(i) | + 0x070000ed, + MD_CNTL_MD_EN); + udelay(1); + } + } + + ddr_out32(&ddr->err_disable, + regs->err_disable & ~DDR_ERR_DISABLE_APED); +#endif + } +#endif + total_gb_size_per_controller = 0; for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { if (!(regs->cs[i].config & 0x80000000))

Shengzhou,
On 03/07/2016 10:01 PM, Shengzhou Liu wrote:
During the initial DDR training, false parity errors may be detected. This can lead to the ERR_DETECT[APE], ERR_DETECT[MME] bits getting set during DDR training and the DDR could enter a bad state to try and recover since it thinks a parity error has occurred.
Please rephrase this sentence. It is too long and confusing. It would be helpful to list the boards tested.
York

Shengzhou,
Subject is inaccurate. Not only UDIMM for DDR4.
On 03/07/2016 10:01 PM, Shengzhou Liu wrote:
Add support of command/address parity for DDR4 UDIMM or discrete memory. It requires to configurate corresponding MR5[2:0] and TIMING_CFG_7[PAR_LAT]. Parity can be turned on/off by hwconfig, e.g. hwconfig=fsl_ddr:parity=on.
Signed-off-by: Shengzhou Liu Shengzhou.Liu@nxp.com
doc/README.fsl-ddr | 8 +++++++ drivers/ddr/fsl/ctrl_regs.c | 52 +++++++++++++++++++++++++++++++++++++++------ drivers/ddr/fsl/options.c | 12 +++++++++-- include/fsl_ddr_sdram.h | 11 +++++++++- 4 files changed, 73 insertions(+), 10 deletions(-)
diff --git a/doc/README.fsl-ddr b/doc/README.fsl-ddr index cd71ec8..80f91d3 100644 --- a/doc/README.fsl-ddr +++ b/doc/README.fsl-ddr @@ -143,6 +143,14 @@ platform
hwconfig=fsl_ddr:addr_hash=true,ctlr_intlv=cacheline,bank_intlv=cs0_cs1_cs2_cs3,ecc=on
+Memory address parity on/off +============================ +address parity can be turned on/off by hwconfig, it's disabled by default. +Syntax is: +hwconfig=fsl_ddr:parity=on
Table for dynamic ODT for DDR3
For single-slot system with quad-rank DIMM and dual-slot system, dynamic ODT may diff --git a/drivers/ddr/fsl/ctrl_regs.c b/drivers/ddr/fsl/ctrl_regs.c index 0bfcd34..1f5b9fc 100644 --- a/drivers/ddr/fsl/ctrl_regs.c +++ b/drivers/ddr/fsl/ctrl_regs.c @@ -895,12 +895,10 @@ static void set_ddr_sdram_cfg_2(const unsigned int ctrl_num, slow = get_ddr_freq(ctrl_num) < 1249000000; #endif
- if (popts->registered_dimm_en) {
- ap_en = popts->ap_en;
- if (popts->registered_dimm_en) rcw_en = 1;
ap_en = popts->ap_en;
- } else {
ap_en = 0;
- }
This change removes the protection. popts->ap_en can be set by board ddr setting. Without checking popts->registered_dimm_en, this could set ap_en incorrectly. This was written for DDR3 RDIMM. Since DDR4 allows address parity for UDIMM, I think it is better to check for DDR3.
x4_en = popts->x4_en ? 1 : 0;
@@ -1135,6 +1133,7 @@ static void set_ddr_sdram_mode_9(fsl_ddr_cfg_regs_t *ddr, unsigned short esdmode5; /* Extended SDRAM mode 5 */ int rtt_park = 0; bool four_cs = false;
- const unsigned int mclk_ps = get_memory_clk_period_ps(0);
#if CONFIG_CHIP_SELECTS_PER_CTRL == 4 if ((ddr->cs[0].config & SDRAM_CS_CONFIG_EN) && @@ -1150,6 +1149,19 @@ 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) {
if (mclk_ps >= 935) {
/* for DDR4-1600/1866/2133 */
esdmode5 |= DDR_MR5_CA_PARITY_LAT_4_CLK;
} else if (mclk_ps >= 833) {
/* for DDR4-2400 */
esdmode5 |= DDR_MR5_CA_PARITY_LAT_5_CLK;
} else {
printf("parity: mclk_ps = %d not supported\n", mclk_ps);
}
- }
- ddr->ddr_sdram_mode_9 = (0 | ((esdmode4 & 0xffff) << 16) | ((esdmode5 & 0xffff) << 0)
@@ -1170,6 +1182,19 @@ static void set_ddr_sdram_mode_9(fsl_ddr_cfg_regs_t *ddr, } else { esdmode5 = 0x00000400; }
if (ddr->ddr_sdram_cfg_2 & SDRAM_CFG2_AP_EN) {
if (mclk_ps >= 935) {
/* for DDR4-1600/1866/2133 */
esdmode5 |= DDR_MR5_CA_PARITY_LAT_4_CLK;
} else if (mclk_ps >= 833) {
/* for DDR4-2400 */
esdmode5 |= DDR_MR5_CA_PARITY_LAT_5_CLK;
} else {
printf("parity: the speed reserved\n");
}
}
switch (i) { case 1: ddr->ddr_sdram_mode_11 = (0
@@ -1925,12 +1950,25 @@ static void set_timing_cfg_7(const unsigned int ctrl_num, const common_timing_params_t *common_dimm) { unsigned int txpr, tcksre, tcksrx;
- unsigned int cke_rst, cksre, cksrx, par_lat, cs_to_cmd;
unsigned int cke_rst, cksre, cksrx, par_lat = 0, cs_to_cmd;
const unsigned int mclk_ps = get_memory_clk_period_ps(ctrl_num);
txpr = max(5U, picos_to_mclk(ctrl_num, common_dimm->trfc1_ps + 10000)); tcksre = max(5U, picos_to_mclk(ctrl_num, 10000)); tcksrx = max(5U, picos_to_mclk(ctrl_num, 10000));
- par_lat = 0;
if (ddr->ddr_sdram_cfg_2 & SDRAM_CFG2_AP_EN) {
if (mclk_ps >= 935) {
/* parity latency 4 clocks in case of 1600/1866/2133 */
par_lat = 4;
} else if (mclk_ps >= 833) {
/* parity latency 5 clocks for DDR4-2400 */
par_lat = 5;
} else {
printf("parity: mclk_ps = %d not supported\n", mclk_ps);
}
}
cs_to_cmd = 0;
if (txpr <= 200)
diff --git a/drivers/ddr/fsl/options.c b/drivers/ddr/fsl/options.c index 791d644..9ea80af 100644 --- a/drivers/ddr/fsl/options.c +++ b/drivers/ddr/fsl/options.c @@ -1002,8 +1002,16 @@ unsigned int populate_memctl_options(const common_timing_params_t *common_dimm, popts->twot_en = 0; popts->threet_en = 0;
- /* for RDIMM, address parity enable */
- popts->ap_en = 1;
- /* for RDIMM and DDR4 UDIMM/discrete memory, address parity enable */
- popts->ap_en = 0; /* 0 = disable, 1 = enable */
- if (hwconfig_sub_f("fsl_ddr", "parity", buf)) {
if (hwconfig_subarg_cmp_f("fsl_ddr", "parity", "on", buf)) {
if (popts->registered_dimm_en ||
(CONFIG_FSL_SDRAM_TYPE == SDRAM_TYPE_DDR4))
popts->ap_en = 1;
}
- }
I don't remember why I enabled AP for all DDR3 RDIMM. Now you are making it optional. Please double check this.
York
participants (2)
-
Shengzhou Liu
-
york sun