+// SPDX-License-Identifier: GPL-2.0+
/*
- * Copyright 2008-2014 Freescale Semiconductor, Inc.
- *
- * SPDX-License-Identifier: GPL-2.0+
+ * Copyright 2008-2016 Freescale Semiconductor, Inc.
+ * Copyright 2017-2020 NXP Semiconductor
*/
/*
- * Generic driver for Freescale DDR/DDR2/DDR3 memory controller.
+ * Generic driver for Freescale DDR/DDR2/DDR3/DDR4 memory controller.
* Based on code from spd_sdram.c
* Author: James Yang [at freescale.com]
*/
#include <common.h>
#include <fsl_ddr_sdram.h>
-
+#include <fsl_errata.h>
#include <fsl_ddr.h>
#include <fsl_immap.h>
+#include <log.h>
+#include <asm/bitops.h>
#include <asm/io.h>
+#if defined(CONFIG_FSL_LSCH2) || defined(CONFIG_FSL_LSCH3) || \
+ defined(CONFIG_ARM)
+#include <asm/arch/clock.h>
+#endif
/*
* Determine Rtt value.
| ((ext_pretoact & 0x1) << 28)
| ((ext_acttopre & 0x3) << 24)
| ((ext_acttorw & 0x1) << 22)
- | ((ext_refrec & 0x1F) << 16)
+ | ((ext_refrec & 0x3F) << 16)
| ((ext_caslat & 0x3) << 12)
| ((ext_add_lat & 0x1) << 10)
| ((ext_wrrec & 0x1) << 8)
| ((add_lat_mclk & 0xf) << 28)
| ((cpo & 0x1f) << 23)
| ((wr_lat & 0xf) << 19)
- | ((wr_lat & 0x10) << 18)
+ | (((wr_lat & 0x10) >> 4) << 18)
| ((rd_to_pre & RD_TO_PRE_MASK) << RD_TO_PRE_SHIFT)
| ((wr_data_delay & WR_DATA_DELAY_MASK) << WR_DATA_DELAY_SHIFT)
| ((cke_pls & 0x7) << 6)
}
/* DDR SDRAM Register Control Word */
-static void set_ddr_sdram_rcw(fsl_ddr_cfg_regs_t *ddr,
- const memctl_options_t *popts,
- const common_timing_params_t *common_dimm)
+static void set_ddr_sdram_rcw(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 ddr_freq = get_ddr_freq(ctrl_num) / 1000000;
+ unsigned int rc0a, rc0f;
+
if (common_dimm->all_dimms_registered &&
!common_dimm->all_dimms_unbuffered) {
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 {
+ rc0a = ddr_freq > 3200 ? 0x7 :
+ (ddr_freq > 2933 ? 0x6 :
+ (ddr_freq > 2666 ? 0x5 :
+ (ddr_freq > 2400 ? 0x4 :
+ (ddr_freq > 2133 ? 0x3 :
+ (ddr_freq > 1866 ? 0x2 :
+ (ddr_freq > 1600 ? 1 : 0))))));
+ rc0f = ddr_freq > 3200 ? 0x3 :
+ (ddr_freq > 2400 ? 0x2 :
+ (ddr_freq > 2133 ? 0x1 : 0));
ddr->ddr_sdram_rcw_1 =
common_dimm->rcw[0] << 28 | \
common_dimm->rcw[1] << 24 | \
ddr->ddr_sdram_rcw_2 =
common_dimm->rcw[8] << 28 | \
common_dimm->rcw[9] << 24 | \
- common_dimm->rcw[10] << 20 | \
+ rc0a << 20 | \
common_dimm->rcw[11] << 16 | \
common_dimm->rcw[12] << 12 | \
common_dimm->rcw[13] << 8 | \
common_dimm->rcw[14] << 4 | \
- common_dimm->rcw[15];
+ rc0f;
+ ddr->ddr_sdram_rcw_3 =
+ ((ddr_freq - 1260 + 19) / 20) << 8;
}
- 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);
}
}
}
}
sr_ie = popts->self_refresh_interrupt_en;
- num_pr = 1; /* Make this configurable */
+ num_pr = popts->package_3ds + 1;
/*
* 8572 manual says
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;
* need 0x500 to park.
*/
- debug("FSLDDR: ddr_sdram_mode_9) = 0x%08x\n", ddr->ddr_sdram_mode_9);
+ debug("FSLDDR: ddr_sdram_mode_9 = 0x%08x\n", ddr->ddr_sdram_mode_9);
if (unq_mrs_en) { /* unique mode registers are supported */
for (i = 1; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) {
if (!rtt_park &&
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;
| ((esdmode6 & 0xffff) << 16)
| ((esdmode7 & 0xffff) << 0)
);
- debug("FSLDDR: ddr_sdram_mode_10) = 0x%08x\n", ddr->ddr_sdram_mode_10);
+ debug("FSLDDR: ddr_sdram_mode_10 = 0x%08x\n", ddr->ddr_sdram_mode_10);
if (unq_mrs_en) { /* unique mode registers are supported */
for (i = 1; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) {
switch (i) {
unsigned int clk_adjust; /* Clock adjust */
unsigned int ss_en = 0; /* Source synchronous enable */
-#if defined(CONFIG_MPC8541) || defined(CONFIG_MPC8555)
+#if defined(CONFIG_ARCH_MPC8555)
/* Per FSL Application Note: AN2805 */
ss_en = 1;
#endif
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;
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 (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);
- }
+ if (ddr->ddr_sdram_cfg_2 & SDRAM_CFG2_AP_EN &&
+ CONFIG_FSL_SDRAM_TYPE == SDRAM_TYPE_DDR4) {
+ /* for DDR4 only */
+ par_lat = (ddr->ddr_sdram_rcw_2 & 0xf) + 1;
+ debug("PAR_LAT = %u for mclk_ps = %d\n", par_lat, mclk_ps);
}
cs_to_cmd = 0;
const common_timing_params_t *common_dimm,
unsigned int cas_latency)
{
- unsigned int rwt_bg, wrt_bg, rrt_bg, wwt_bg;
+ int rwt_bg, wrt_bg, rrt_bg, wwt_bg;
unsigned int acttoact_bg, wrtord_bg, pre_all_rec;
- unsigned int tccdl = picos_to_mclk(ctrl_num, common_dimm->tccdl_ps);
- unsigned int wr_lat = ((ddr->timing_cfg_2 & 0x00780000) >> 19) +
- ((ddr->timing_cfg_2 & 0x00040000) >> 14);
+ int tccdl = picos_to_mclk(ctrl_num, common_dimm->tccdl_ps);
+ int wr_lat = ((ddr->timing_cfg_2 & 0x00780000) >> 19) +
+ ((ddr->timing_cfg_2 & 0x00040000) >> 14);
rwt_bg = cas_latency + 2 + 4 - wr_lat;
if (rwt_bg < tccdl)
debug("FSLDDR: timing_cfg_8 = 0x%08x\n", ddr->timing_cfg_8);
}
-static void set_timing_cfg_9(fsl_ddr_cfg_regs_t *ddr)
+static void set_timing_cfg_9(const unsigned int ctrl_num,
+ fsl_ddr_cfg_regs_t *ddr,
+ const memctl_options_t *popts,
+ const common_timing_params_t *common_dimm)
{
- ddr->timing_cfg_9 = 0;
+ unsigned int refrec_cid_mclk = 0;
+ unsigned int acttoact_cid_mclk = 0;
+
+ if (popts->package_3ds) {
+ refrec_cid_mclk =
+ picos_to_mclk(ctrl_num, common_dimm->trfc_slr_ps);
+ acttoact_cid_mclk = 4U; /* tRRDS_slr */
+ }
+
+ ddr->timing_cfg_9 = (refrec_cid_mclk & 0x3ff) << 16 |
+ (acttoact_cid_mclk & 0xf) << 8;
+
debug("FSLDDR: timing_cfg_9 = 0x%08x\n", ddr->timing_cfg_9);
}
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;
+
+ if (popts->package_3ds) { /* only 2,4,8 are supported */
+ if ((popts->package_3ds + 1) & 0x1) {
+ printf("Error: Unsupported 3DS DIMM with %d die\n",
+ popts->package_3ds + 1);
+ } else {
+ ddr->ddr_sdram_cfg_3 |= ((popts->package_3ds + 1) >> 1)
+ << 4;
+ }
+ }
debug("FSLDDR: ddr_sdram_cfg_3 = 0x%08x\n", ddr->ddr_sdram_cfg_3);
}
* Write leveling start time
* The value use for the DQS_ADJUST for the first sample
* when write leveling is enabled. It probably needs to be
- * overriden per platform.
+ * overridden per platform.
*/
wrlvl_start = 0x8;
/*
set_ddr_sdram_mode_9(ddr, popts, common_dimm, unq_mrs_en);
set_ddr_sdram_mode_10(ctrl_num, ddr, popts, common_dimm, unq_mrs_en);
#endif
+ set_ddr_sdram_rcw(ctrl_num, ddr, popts, common_dimm);
+
set_ddr_sdram_interval(ctrl_num, ddr, popts, common_dimm);
set_ddr_data_init(ddr);
set_ddr_sdram_clk_cntl(ddr, popts);
#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_timing_cfg_9(ctrl_num, ddr, popts, common_dimm);
set_ddr_dq_mapping(ddr, dimm_params);
#endif
set_ddr_sr_cntr(ddr, sr_it);
- set_ddr_sdram_rcw(ddr, popts, common_dimm);
-
#ifdef CONFIG_SYS_FSL_DDR_EMU
/* disble DDR training for emulator */
ddr->debug[2] = 0x00000400;
ddr->debug[2] |= 0x00000200; /* set bit 22 */
#endif
+#ifdef CONFIG_SYS_FSL_ERRATUM_A009942
+ if (popts->cpo_sample)
+ ddr->debug[28] = (ddr->debug[28] & 0xffffff00) |
+ popts->cpo_sample;
+#endif
+
return check_fsl_memctl_config_regs(ddr);
}
+
+#ifdef CONFIG_SYS_FSL_ERRATUM_A009942
+/*
+ * This additional workaround of A009942 checks the condition to determine if
+ * the CPO value set by the existing A009942 workaround needs to be updated.
+ * If need, print a warning to prompt user reconfigure DDR debug_29[24:31] with
+ * expected optimal value, the optimal value is highly board dependent.
+ */
+void erratum_a009942_check_cpo(void)
+{
+ struct ccsr_ddr __iomem *ddr =
+ (struct ccsr_ddr __iomem *)(CONFIG_SYS_FSL_DDR_ADDR);
+ u32 cpo, cpo_e, cpo_o, cpo_target, cpo_optimal;
+ u32 cpo_min = ddr_in32(&ddr->debug[9]) >> 24;
+ u32 cpo_max = cpo_min;
+ u32 sdram_cfg, i, tmp, lanes, ddr_type;
+ bool update_cpo = false, has_ecc = false;
+
+ sdram_cfg = ddr_in32(&ddr->sdram_cfg);
+ if (sdram_cfg & SDRAM_CFG_32_BE)
+ lanes = 4;
+ else if (sdram_cfg & SDRAM_CFG_16_BE)
+ lanes = 2;
+ else
+ lanes = 8;
+
+ if (sdram_cfg & SDRAM_CFG_ECC_EN)
+ has_ecc = true;
+
+ /* determine the maximum and minimum CPO values */
+ for (i = 9; i < 9 + lanes / 2; i++) {
+ cpo = ddr_in32(&ddr->debug[i]);
+ cpo_e = cpo >> 24;
+ cpo_o = (cpo >> 8) & 0xff;
+ tmp = min(cpo_e, cpo_o);
+ if (tmp < cpo_min)
+ cpo_min = tmp;
+ tmp = max(cpo_e, cpo_o);
+ if (tmp > cpo_max)
+ cpo_max = tmp;
+ }
+
+ if (has_ecc) {
+ cpo = ddr_in32(&ddr->debug[13]);
+ cpo = cpo >> 24;
+ if (cpo < cpo_min)
+ cpo_min = cpo;
+ if (cpo > cpo_max)
+ cpo_max = cpo;
+ }
+
+ cpo_target = ddr_in32(&ddr->debug[28]) & 0xff;
+ cpo_optimal = ((cpo_max + cpo_min) >> 1) + 0x27;
+ debug("cpo_optimal = 0x%x, cpo_target = 0x%x\n", cpo_optimal,
+ cpo_target);
+ debug("cpo_max = 0x%x, cpo_min = 0x%x\n", cpo_max, cpo_min);
+
+ ddr_type = (sdram_cfg & SDRAM_CFG_SDRAM_TYPE_MASK) >>
+ SDRAM_CFG_SDRAM_TYPE_SHIFT;
+ if (ddr_type == SDRAM_TYPE_DDR4)
+ update_cpo = (cpo_min + 0x3b) < cpo_target ? true : false;
+ else if (ddr_type == SDRAM_TYPE_DDR3)
+ update_cpo = (cpo_min + 0x3f) < cpo_target ? true : false;
+
+ if (update_cpo) {
+ printf("WARN: pls set popts->cpo_sample = 0x%x ", cpo_optimal);
+ printf("in <board>/ddr.c to optimize cpo\n");
+ }
+}
+#endif