From e1fd16b6f5e80d932c73f5a36141adfb35126e83 Mon Sep 17 00:00:00 2001 From: York Sun Date: Mon, 10 Jan 2011 12:03:00 +0000 Subject: [PATCH] mpc85xx: Enable unique mode registers and dynamic ODT for DDR3 Added fsl_ddr_get_version() function to for DDR3 to poll DDRC IP version (major, minor, errata) to determine if unique mode registers are available. If true, always use unique mode registers. Dynamic ODT is enabled if needed. The table is documented in doc/README.fsl-ddr. This function may also need to be extend for future other platforms if such a feature exists. Enable address parity and RCW by default for RDIMMs. Change default output driver impedance from 34 ohm to 40ohm. Make it 34ohm for quad-rank RDIMMs. Use a formula to calculate rodt_on for timing_cfg_5. Signed-off-by: York Sun Signed-off-by: Kumar Gala --- arch/powerpc/cpu/mpc85xx/ddr-gen3.c | 6 + arch/powerpc/cpu/mpc8xxx/ddr/ctrl_regs.c | 252 +++++++++++++++---- arch/powerpc/cpu/mpc8xxx/ddr/options.c | 308 ++++++++++++++++++++++- arch/powerpc/include/asm/fsl_ddr_sdram.h | 18 ++ doc/README.fsl-ddr | 67 ++++- 5 files changed, 596 insertions(+), 55 deletions(-) diff --git a/arch/powerpc/cpu/mpc85xx/ddr-gen3.c b/arch/powerpc/cpu/mpc85xx/ddr-gen3.c index 4e768d39ba..9bc36f3211 100644 --- a/arch/powerpc/cpu/mpc85xx/ddr-gen3.c +++ b/arch/powerpc/cpu/mpc85xx/ddr-gen3.c @@ -66,6 +66,12 @@ void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, out_be32(&ddr->sdram_cfg_2, regs->ddr_sdram_cfg_2); out_be32(&ddr->sdram_mode, regs->ddr_sdram_mode); out_be32(&ddr->sdram_mode_2, regs->ddr_sdram_mode_2); + out_be32(&ddr->sdram_mode_3, regs->ddr_sdram_mode_3); + out_be32(&ddr->sdram_mode_4, regs->ddr_sdram_mode_4); + out_be32(&ddr->sdram_mode_5, regs->ddr_sdram_mode_5); + out_be32(&ddr->sdram_mode_6, regs->ddr_sdram_mode_6); + out_be32(&ddr->sdram_mode_7, regs->ddr_sdram_mode_7); + out_be32(&ddr->sdram_mode_8, regs->ddr_sdram_mode_8); out_be32(&ddr->sdram_md_cntl, regs->ddr_sdram_md_cntl); out_be32(&ddr->sdram_interval, regs->ddr_sdram_interval); out_be32(&ddr->sdram_data_init, regs->ddr_data_init); diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/ctrl_regs.c b/arch/powerpc/cpu/mpc8xxx/ddr/ctrl_regs.c index 8fdafdb906..2271071ad2 100644 --- a/arch/powerpc/cpu/mpc8xxx/ddr/ctrl_regs.c +++ b/arch/powerpc/cpu/mpc8xxx/ddr/ctrl_regs.c @@ -1,5 +1,5 @@ /* - * Copyright 2008-2010 Freescale Semiconductor, Inc. + * Copyright 2008-2011 Freescale Semiconductor, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -18,7 +18,28 @@ #include "ddr.h" -extern unsigned int picos_to_mclk(unsigned int picos); +#ifdef CONFIG_MPC85xx + #define _DDR_ADDR CONFIG_SYS_MPC85xx_DDR_ADDR +#elif defined(CONFIG_MPC86xx) + #define _DDR_ADDR CONFIG_SYS_MPC86xx_DDR_ADDR +#else + #error "Undefined _DDR_ADDR" +#endif + +u32 fsl_ddr_get_version(void) +{ + ccsr_ddr_t *ddr; + u32 ver_major_minor_errata; + + ddr = (void *)_DDR_ADDR; + ver_major_minor_errata = (in_be32(&ddr->ip_rev1) & 0xFFFF) << 8; + ver_major_minor_errata |= (in_be32(&ddr->ip_rev2) & 0xFF00) >> 8; + + return ver_major_minor_errata; +} + +unsigned int picos_to_mclk(unsigned int picos); + /* * Determine Rtt value. * @@ -187,7 +208,8 @@ static void set_csn_config_2(int i, fsl_ddr_cfg_regs_t *ddr) * Avoid writing for DDR I. The new PQ38 DDR controller * dreams up non-zero default values to be backwards compatible. */ -static void set_timing_cfg_0(fsl_ddr_cfg_regs_t *ddr) +static void set_timing_cfg_0(fsl_ddr_cfg_regs_t *ddr, + const memctl_options_t *popts) { unsigned char trwt_mclk = 0; /* Read-to-write turnaround */ unsigned char twrt_mclk = 0; /* Write-to-read turnaround */ @@ -204,7 +226,7 @@ static void set_timing_cfg_0(fsl_ddr_cfg_regs_t *ddr) /* Mode register set cycle time (tMRD). */ unsigned char tmrd_mclk; -#if defined(CONFIG_FSL_DDR3) +#ifdef CONFIG_FSL_DDR3 /* * (tXARD and tXARDS). Empirical? * The DDR3 spec has not tXARD, @@ -214,13 +236,21 @@ static void set_timing_cfg_0(fsl_ddr_cfg_regs_t *ddr) * tAXPD=1, need design to confirm. */ int tXP = max((get_memory_clk_period_ps() * 3), 7500); /* unit=ps */ - act_pd_exit_mclk = picos_to_mclk(tXP); - /* Mode register MR0[A12] is '1' - fast exit */ - pre_pd_exit_mclk = act_pd_exit_mclk; - taxpd_mclk = 1; tmrd_mclk = 4; /* set the turnaround time */ trwt_mclk = 1; + + if (popts->dynamic_power == 0) { /* powerdown is not used */ + act_pd_exit_mclk = 1; + pre_pd_exit_mclk = 1; + taxpd_mclk = 1; + } else { + /* act_pd_exit_mclk = tXARD, see above */ + act_pd_exit_mclk = picos_to_mclk(tXP); + /* Mode register MR0[A12] is '1' - fast exit */ + pre_pd_exit_mclk = act_pd_exit_mclk; + taxpd_mclk = 1; + } #else /* CONFIG_FSL_DDR2 */ /* * (tXARD and tXARDS). Empirical? @@ -450,28 +480,34 @@ static void set_timing_cfg_2(fsl_ddr_cfg_regs_t *ddr, /* 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) { if (common_dimm->all_DIMMs_registered && !common_dimm->all_DIMMs_unbuffered) { - ddr->ddr_sdram_rcw_1 = - common_dimm->rcw[0] << 28 | \ - common_dimm->rcw[1] << 24 | \ - common_dimm->rcw[2] << 20 | \ - common_dimm->rcw[3] << 16 | \ - common_dimm->rcw[4] << 12 | \ - common_dimm->rcw[5] << 8 | \ - common_dimm->rcw[6] << 4 | \ - common_dimm->rcw[7]; - ddr->ddr_sdram_rcw_2 = - common_dimm->rcw[8] << 28 | \ - common_dimm->rcw[9] << 24 | \ - common_dimm->rcw[10] << 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]; + if (popts->rcw_override) { + ddr->ddr_sdram_rcw_1 = popts->rcw_1; + ddr->ddr_sdram_rcw_2 = popts->rcw_2; + } else { + ddr->ddr_sdram_rcw_1 = + common_dimm->rcw[0] << 28 | \ + common_dimm->rcw[1] << 24 | \ + common_dimm->rcw[2] << 20 | \ + common_dimm->rcw[3] << 16 | \ + common_dimm->rcw[4] << 12 | \ + common_dimm->rcw[5] << 8 | \ + common_dimm->rcw[6] << 4 | \ + common_dimm->rcw[7]; + ddr->ddr_sdram_rcw_2 = + common_dimm->rcw[8] << 28 | \ + common_dimm->rcw[9] << 24 | \ + common_dimm->rcw[10] << 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]; + } 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); } @@ -509,8 +545,14 @@ static void set_ddr_sdram_cfg(fsl_ddr_cfg_regs_t *ddr, ecc_en = 0; } - rd_en = (common_dimm->all_DIMMs_registered - && !common_dimm->all_DIMMs_unbuffered); + if (common_dimm->all_DIMMs_registered + && !common_dimm->all_DIMMs_unbuffered) { + rd_en = 1; + twoT_en = 0; + } else { + rd_en = 0; + twoT_en = popts->twoT_en; + } sdram_type = CONFIG_FSL_SDRAM_TYPE; @@ -530,7 +572,6 @@ static void set_ddr_sdram_cfg(fsl_ddr_cfg_regs_t *ddr, } threeT_en = popts->threeT_en; - twoT_en = popts->twoT_en; ba_intlv_ctl = popts->ba_intlv_ctl; hse = popts->half_strength_driver_enable; @@ -558,7 +599,8 @@ static void set_ddr_sdram_cfg(fsl_ddr_cfg_regs_t *ddr, /* DDR SDRAM control configuration 2 (DDR_SDRAM_CFG_2) */ static void set_ddr_sdram_cfg_2(fsl_ddr_cfg_regs_t *ddr, - const memctl_options_t *popts) + const memctl_options_t *popts, + const unsigned int unq_mrs_en) { unsigned int frc_sr = 0; /* Force self refresh */ unsigned int sr_ie = 0; /* Self-refresh interrupt enable */ @@ -598,11 +640,17 @@ static void set_ddr_sdram_cfg_2(fsl_ddr_cfg_regs_t *ddr, obc_cfg = 0; #endif - ap_en = 0; /* Make this configurable? */ + if (popts->registered_dimm_en) { + rcw_en = 1; + ap_en = popts->ap_en; + } else { + rcw_en = 0; + ap_en = 0; + } #if defined(CONFIG_ECC_INIT_VIA_DDRCONTROLLER) /* Use the DDR controller to auto initialize memory. */ - d_init = 1; + d_init = popts->ECC_init_using_memctl; ddr->ddr_data_init = CONFIG_MEM_INIT_VALUE; debug("DDR: ddr_data_init = 0x%08x\n", ddr->ddr_data_init); #else @@ -613,7 +661,6 @@ static void set_ddr_sdram_cfg_2(fsl_ddr_cfg_regs_t *ddr, #if defined(CONFIG_FSL_DDR3) md_en = popts->mirrored_dimm; #endif - rcw_en = popts->registered_dimm_en; qd_en = popts->quad_rank_present ? 1 : 0; ddr->ddr_sdram_cfg_2 = (0 | ((frc_sr & 0x1) << 31) @@ -623,6 +670,7 @@ static void set_ddr_sdram_cfg_2(fsl_ddr_cfg_regs_t *ddr, | ((odt_cfg & 0x3) << 21) | ((num_pr & 0xf) << 12) | (qd_en << 9) + | (unq_mrs_en << 8) | ((obc_cfg & 0x1) << 6) | ((ap_en & 0x1) << 5) | ((d_init & 0x1) << 4) @@ -634,10 +682,12 @@ static void set_ddr_sdram_cfg_2(fsl_ddr_cfg_regs_t *ddr, /* DDR SDRAM Mode configuration 2 (DDR_SDRAM_MODE_2) */ static void set_ddr_sdram_mode_2(fsl_ddr_cfg_regs_t *ddr, - const memctl_options_t *popts) + const memctl_options_t *popts, + const unsigned int unq_mrs_en) { unsigned short esdmode2 = 0; /* Extended SDRAM mode 2 */ unsigned short esdmode3 = 0; /* Extended SDRAM mode 3 */ + int i; #if defined(CONFIG_FSL_DDR3) unsigned int rtt_wr = 0; /* Rtt_WR - dynamic ODT off */ @@ -648,7 +698,8 @@ static void set_ddr_sdram_mode_2(fsl_ddr_cfg_regs_t *ddr, if (popts->rtt_override) rtt_wr = popts->rtt_wr_override_value; - + else + rtt_wr = popts->cs_local_opts[0].odt_rtt_wr; esdmode2 = (0 | ((rtt_wr & 0x3) << 9) | ((srt & 0x1) << 7) @@ -661,6 +712,46 @@ static void set_ddr_sdram_mode_2(fsl_ddr_cfg_regs_t *ddr, | ((esdmode3 & 0xFFFF) << 0) ); debug("FSLDDR: ddr_sdram_mode_2 = 0x%08x\n", ddr->ddr_sdram_mode_2); + +#ifdef CONFIG_FSL_DDR3 + if (unq_mrs_en) { /* unique mode registers are supported */ + for (i = 1; i < 4; i++) { + if (popts->rtt_override) + rtt_wr = popts->rtt_wr_override_value; + else + rtt_wr = popts->cs_local_opts[i].odt_rtt_wr; + + esdmode2 &= 0xF9FF; /* clear bit 10, 9 */ + esdmode2 |= (rtt_wr & 0x3) << 9; + switch (i) { + case 1: + ddr->ddr_sdram_mode_4 = (0 + | ((esdmode2 & 0xFFFF) << 16) + | ((esdmode3 & 0xFFFF) << 0) + ); + break; + case 2: + ddr->ddr_sdram_mode_6 = (0 + | ((esdmode2 & 0xFFFF) << 16) + | ((esdmode3 & 0xFFFF) << 0) + ); + break; + case 3: + ddr->ddr_sdram_mode_8 = (0 + | ((esdmode2 & 0xFFFF) << 16) + | ((esdmode3 & 0xFFFF) << 0) + ); + break; + } + } + debug("FSLDDR: ddr_sdram_mode_4 = 0x%08x\n", + ddr->ddr_sdram_mode_4); + debug("FSLDDR: ddr_sdram_mode_6 = 0x%08x\n", + ddr->ddr_sdram_mode_6); + debug("FSLDDR: ddr_sdram_mode_8 = 0x%08x\n", + ddr->ddr_sdram_mode_8); + } +#endif } /* DDR SDRAM Interval Configuration (DDR_SDRAM_INTERVAL) */ @@ -689,7 +780,8 @@ static void set_ddr_sdram_mode(fsl_ddr_cfg_regs_t *ddr, const memctl_options_t *popts, const common_timing_params_t *common_dimm, unsigned int cas_latency, - unsigned int additive_latency) + unsigned int additive_latency, + const unsigned int unq_mrs_en) { unsigned short esdmode; /* Extended SDRAM mode */ unsigned short sdmode; /* SDRAM mode */ @@ -700,7 +792,7 @@ static void set_ddr_sdram_mode(fsl_ddr_cfg_regs_t *ddr, unsigned int rtt; unsigned int wrlvl_en = 0; /* Write level enable: 0=no, 1=yes */ unsigned int al = 0; /* Posted CAS# additive latency (AL) */ - unsigned int dic = 1; /* Output driver impedance, 34ohm */ + unsigned int dic = 0; /* Output driver impedance, 40ohm */ unsigned int dll_en = 0; /* DLL Enable 0=Enable (Normal), 1=Disable (Test/Debug) */ @@ -717,16 +809,21 @@ static void set_ddr_sdram_mode(fsl_ddr_cfg_regs_t *ddr, unsigned int wr_mclk; const unsigned int mclk_ps = get_memory_clk_period_ps(); + int i; - rtt = fsl_ddr_get_rtt(); if (popts->rtt_override) rtt = popts->rtt_override_value; + else + rtt = popts->cs_local_opts[0].odt_rtt_norm; if (additive_latency == (cas_latency - 1)) al = 1; if (additive_latency == (cas_latency - 2)) al = 2; + if (popts->quad_rank_present) + dic = 1; /* output driver impedance 240/7 ohm */ + /* * The esdmode value will also be used for writing * MR1 during write leveling for DDR3, although the @@ -812,6 +909,48 @@ static void set_ddr_sdram_mode(fsl_ddr_cfg_regs_t *ddr, ); debug("FSLDDR: ddr_sdram_mode = 0x%08x\n", ddr->ddr_sdram_mode); + + if (unq_mrs_en) { /* unique mode registers are supported */ + for (i = 1; i < 4; i++) { + if (popts->rtt_override) + rtt = popts->rtt_override_value; + else + rtt = popts->cs_local_opts[i].odt_rtt_norm; + + esdmode &= 0xFDBB; /* clear bit 9,6,2 */ + esdmode |= (0 + | ((rtt & 0x4) << 7) /* rtt field is split */ + | ((rtt & 0x2) << 5) /* rtt field is split */ + | ((rtt & 0x1) << 2) /* rtt field is split */ + ); + switch (i) { + case 1: + ddr->ddr_sdram_mode_3 = (0 + | ((esdmode & 0xFFFF) << 16) + | ((sdmode & 0xFFFF) << 0) + ); + break; + case 2: + ddr->ddr_sdram_mode_5 = (0 + | ((esdmode & 0xFFFF) << 16) + | ((sdmode & 0xFFFF) << 0) + ); + break; + case 3: + ddr->ddr_sdram_mode_7 = (0 + | ((esdmode & 0xFFFF) << 16) + | ((sdmode & 0xFFFF) << 0) + ); + break; + } + } + debug("FSLDDR: ddr_sdram_mode_3 = 0x%08x\n", + ddr->ddr_sdram_mode_3); + debug("FSLDDR: ddr_sdram_mode_5 = 0x%08x\n", + ddr->ddr_sdram_mode_5); + debug("FSLDDR: ddr_sdram_mode_5 = 0x%08x\n", + ddr->ddr_sdram_mode_5); + } } #else /* !CONFIG_FSL_DDR3 */ @@ -821,7 +960,8 @@ static void set_ddr_sdram_mode(fsl_ddr_cfg_regs_t *ddr, const memctl_options_t *popts, const common_timing_params_t *common_dimm, unsigned int cas_latency, - unsigned int additive_latency) + unsigned int additive_latency, + const unsigned int unq_mrs_en) { unsigned short esdmode; /* Extended SDRAM mode */ unsigned short sdmode; /* SDRAM mode */ @@ -1024,7 +1164,7 @@ static void set_timing_cfg_4(fsl_ddr_cfg_regs_t *ddr, } /* DDR SDRAM Timing Configuration 5 (TIMING_CFG_5) */ -static void set_timing_cfg_5(fsl_ddr_cfg_regs_t *ddr) +static void set_timing_cfg_5(fsl_ddr_cfg_regs_t *ddr, unsigned int cas_latency) { unsigned int rodt_on = 0; /* Read to ODT on */ unsigned int rodt_off = 0; /* Read to ODT off */ @@ -1032,7 +1172,8 @@ static void set_timing_cfg_5(fsl_ddr_cfg_regs_t *ddr) unsigned int wodt_off = 0; /* Write to ODT off */ #if defined(CONFIG_FSL_DDR3) - rodt_on = 2; /* 2 clocks */ + /* rodt_on = timing_cfg_1[caslat] - timing_cfg_2[wrlat] + 1 */ + rodt_on = cas_latency - ((ddr->timing_cfg_2 & 0x00780000) >> 19) + 1; rodt_off = 4; /* 4 clocks */ wodt_on = 1; /* 1 clocks */ wodt_off = 4; /* 4 clocks */ @@ -1068,6 +1209,7 @@ static void set_ddr_zq_cntl(fsl_ddr_cfg_regs_t *ddr, unsigned int zq_en) | ((zqoper & 0xF) << 16) | ((zqcs & 0xF) << 8) ); + debug("FSLDDR: zq_cntl = 0x%08x\n", ddr->ddr_zq_cntl); } /* DDR Write Leveling Control (DDR_WRLVL_CNTL) */ @@ -1113,7 +1255,8 @@ static void set_ddr_wrlvl_cntl(fsl_ddr_cfg_regs_t *ddr, unsigned int wrlvl_en, /* * Write leveling start time * The value use for the DQS_ADJUST for the first sample - * when write leveling is enabled. + * when write leveling is enabled. It probably needs to be + * overriden per platform. */ wrlvl_start = 0x8; /* @@ -1135,6 +1278,7 @@ static void set_ddr_wrlvl_cntl(fsl_ddr_cfg_regs_t *ddr, unsigned int wrlvl_en, | ((wrlvl_wlr & 0x7) << 8) | ((wrlvl_start & 0x1F) << 0) ); + debug("FSLDDR: wrlvl_cntl = 0x%08x\n", ddr->ddr_wrlvl_cntl); } /* DDR Self Refresh Counter (DDR_SR_CNTR) */ @@ -1152,6 +1296,12 @@ static void set_ddr_eor(fsl_ddr_cfg_regs_t *ddr, const memctl_options_t *popts) } } +static void set_ddr_cdr1(fsl_ddr_cfg_regs_t *ddr, const memctl_options_t *popts) +{ + ddr->ddr_cdr1 = popts->ddr_cdr1; + debug("FSLDDR: ddr_cdr1 = 0x%08x\n", ddr->ddr_cdr1); +} + unsigned int check_fsl_memctl_config_regs(const fsl_ddr_cfg_regs_t *ddr) { @@ -1185,6 +1335,8 @@ compute_fsl_memctl_config_regs(const memctl_options_t *popts, unsigned int sr_it; unsigned int zq_en; unsigned int wrlvl_en; + unsigned int ip_rev = 0; + unsigned int unq_mrs_en = 0; int cs_en = 1; memset(ddr, 0, sizeof(fsl_ddr_cfg_regs_t)); @@ -1405,7 +1557,7 @@ compute_fsl_memctl_config_regs(const memctl_options_t *popts, set_ddr_eor(ddr, popts); #if !defined(CONFIG_FSL_DDR1) - set_timing_cfg_0(ddr); + set_timing_cfg_0(ddr, popts); #endif set_timing_cfg_3(ddr, common_dimm, cas_latency); @@ -1413,26 +1565,30 @@ compute_fsl_memctl_config_regs(const memctl_options_t *popts, set_timing_cfg_2(ddr, popts, common_dimm, cas_latency, additive_latency); + set_ddr_cdr1(ddr, popts); set_ddr_sdram_cfg(ddr, popts, common_dimm); + ip_rev = fsl_ddr_get_version(); + if (ip_rev > 0x40400) + unq_mrs_en = 1; - set_ddr_sdram_cfg_2(ddr, popts); + set_ddr_sdram_cfg_2(ddr, popts, unq_mrs_en); set_ddr_sdram_mode(ddr, popts, common_dimm, - cas_latency, additive_latency); - set_ddr_sdram_mode_2(ddr, popts); + cas_latency, additive_latency, unq_mrs_en); + set_ddr_sdram_mode_2(ddr, popts, unq_mrs_en); set_ddr_sdram_interval(ddr, popts, common_dimm); set_ddr_data_init(ddr); set_ddr_sdram_clk_cntl(ddr, popts); set_ddr_init_addr(ddr); set_ddr_init_ext_addr(ddr); set_timing_cfg_4(ddr, popts); - set_timing_cfg_5(ddr); + set_timing_cfg_5(ddr, cas_latency); set_ddr_zq_cntl(ddr, zq_en); set_ddr_wrlvl_cntl(ddr, wrlvl_en, popts); set_ddr_sr_cntr(ddr, sr_it); - set_ddr_sdram_rcw(ddr, common_dimm); + set_ddr_sdram_rcw(ddr, popts, common_dimm); return check_fsl_memctl_config_regs(ddr); } diff --git a/arch/powerpc/cpu/mpc8xxx/ddr/options.c b/arch/powerpc/cpu/mpc8xxx/ddr/options.c index 55dff43947..6ccc3b0c70 100644 --- a/arch/powerpc/cpu/mpc8xxx/ddr/options.c +++ b/arch/powerpc/cpu/mpc8xxx/ddr/options.c @@ -26,6 +26,243 @@ extern void fsl_ddr_board_options(memctl_options_t *popts, dimm_params_t *pdimm, unsigned int ctrl_num); +typedef struct { + unsigned int odt_rd_cfg; + unsigned int odt_wr_cfg; + unsigned int odt_rtt_norm; + unsigned int odt_rtt_wr; +} dynamic_odt_t; + +static const dynamic_odt_t single_Q[4] = { + { /* cs0 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_CS_AND_OTHER_DIMM, + DDR3_RTT_20_OHM, + DDR3_RTT_120_OHM + }, + { /* cs1 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_NEVER, /* tied high */ + DDR3_RTT_OFF, + DDR3_RTT_120_OHM + }, + { /* cs2 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_CS_AND_OTHER_DIMM, + DDR3_RTT_20_OHM, + DDR3_RTT_120_OHM + }, + { /* cs3 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_NEVER, /* tied high */ + DDR3_RTT_OFF, + DDR3_RTT_120_OHM + } +}; + +static const dynamic_odt_t single_D[4] = { + { /* cs0 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_ALL, + DDR3_RTT_40_OHM, + DDR3_RTT_OFF + }, + { /* cs1 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_NEVER, + DDR3_RTT_OFF, + DDR3_RTT_OFF + }, + {0, 0, 0, 0}, + {0, 0, 0, 0} +}; + +static const dynamic_odt_t single_S[4] = { + { /* cs0 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_ALL, + DDR3_RTT_40_OHM, + DDR3_RTT_OFF + }, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, +}; + +static const dynamic_odt_t dual_DD[4] = { + { /* cs0 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_SAME_DIMM, + DDR3_RTT_120_OHM, + DDR3_RTT_OFF + }, + { /* cs1 */ + FSL_DDR_ODT_OTHER_DIMM, + FSL_DDR_ODT_OTHER_DIMM, + DDR3_RTT_30_OHM, + DDR3_RTT_OFF + }, + { /* cs2 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_SAME_DIMM, + DDR3_RTT_120_OHM, + DDR3_RTT_OFF + }, + { /* cs3 */ + FSL_DDR_ODT_OTHER_DIMM, + FSL_DDR_ODT_OTHER_DIMM, + DDR3_RTT_30_OHM, + DDR3_RTT_OFF + } +}; + +static const dynamic_odt_t dual_DS[4] = { + { /* cs0 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_SAME_DIMM, + DDR3_RTT_120_OHM, + DDR3_RTT_OFF + }, + { /* cs1 */ + FSL_DDR_ODT_OTHER_DIMM, + FSL_DDR_ODT_OTHER_DIMM, + DDR3_RTT_30_OHM, + DDR3_RTT_OFF + }, + { /* cs2 */ + FSL_DDR_ODT_OTHER_DIMM, + FSL_DDR_ODT_ALL, + DDR3_RTT_20_OHM, + DDR3_RTT_120_OHM + }, + {0, 0, 0, 0} +}; +static const dynamic_odt_t dual_SD[4] = { + { /* cs0 */ + FSL_DDR_ODT_OTHER_DIMM, + FSL_DDR_ODT_ALL, + DDR3_RTT_20_OHM, + DDR3_RTT_120_OHM + }, + {0, 0, 0, 0}, + { /* cs2 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_SAME_DIMM, + DDR3_RTT_120_OHM, + DDR3_RTT_OFF + }, + { /* cs3 */ + FSL_DDR_ODT_OTHER_DIMM, + FSL_DDR_ODT_OTHER_DIMM, + DDR3_RTT_20_OHM, + DDR3_RTT_OFF + } +}; + +static const dynamic_odt_t dual_SS[4] = { + { /* cs0 */ + FSL_DDR_ODT_OTHER_DIMM, + FSL_DDR_ODT_ALL, + DDR3_RTT_30_OHM, + DDR3_RTT_120_OHM + }, + {0, 0, 0, 0}, + { /* cs2 */ + FSL_DDR_ODT_OTHER_DIMM, + FSL_DDR_ODT_ALL, + DDR3_RTT_30_OHM, + DDR3_RTT_120_OHM + }, + {0, 0, 0, 0} +}; + +static const dynamic_odt_t dual_D0[4] = { + { /* cs0 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_SAME_DIMM, + DDR3_RTT_40_OHM, + DDR3_RTT_OFF + }, + { /* cs1 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_NEVER, + DDR3_RTT_OFF, + DDR3_RTT_OFF + }, + {0, 0, 0, 0}, + {0, 0, 0, 0} +}; + +static const dynamic_odt_t dual_0D[4] = { + {0, 0, 0, 0}, + {0, 0, 0, 0}, + { /* cs2 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_SAME_DIMM, + DDR3_RTT_40_OHM, + DDR3_RTT_OFF + }, + { /* cs3 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_NEVER, + DDR3_RTT_OFF, + DDR3_RTT_OFF + } +}; + +static const dynamic_odt_t dual_S0[4] = { + { /* cs0 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_CS, + DDR3_RTT_40_OHM, + DDR3_RTT_OFF + }, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0} + +}; + +static const dynamic_odt_t dual_0S[4] = { + {0, 0, 0, 0}, + {0, 0, 0, 0}, + { /* cs2 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_CS, + DDR3_RTT_40_OHM, + DDR3_RTT_OFF + }, + {0, 0, 0, 0} + +}; + +static const dynamic_odt_t odt_unknown[4] = { + { /* cs0 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_CS, + DDR3_RTT_120_OHM, + DDR3_RTT_OFF + }, + { /* cs1 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_CS, + DDR3_RTT_120_OHM, + DDR3_RTT_OFF + }, + { /* cs2 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_CS, + DDR3_RTT_120_OHM, + DDR3_RTT_OFF + }, + { /* cs3 */ + FSL_DDR_ODT_NEVER, + FSL_DDR_ODT_CS, + DDR3_RTT_120_OHM, + DDR3_RTT_OFF + } +}; + unsigned int populate_memctl_options(int all_DIMMs_registered, memctl_options_t *popts, dimm_params_t *pdimm, @@ -34,6 +271,7 @@ unsigned int populate_memctl_options(int all_DIMMs_registered, unsigned int i; char buffer[HWCONFIG_BUFFER_SIZE]; char *buf = NULL; + const dynamic_odt_t *pdodt = odt_unknown; /* * Extract hwconfig from environment since we have not properly setup @@ -43,15 +281,70 @@ unsigned int populate_memctl_options(int all_DIMMs_registered, buf = buffer; /* Chip select options. */ + if (CONFIG_DIMM_SLOTS_PER_CTLR == 1) { + switch (pdimm[0].n_ranks) { + case 1: + pdodt = single_S; + break; + case 2: + pdodt = single_D; + break; + case 4: + pdodt = single_Q; + break; + } + } else if (CONFIG_DIMM_SLOTS_PER_CTLR == 2) { + switch (pdimm[0].n_ranks) { + case 2: + switch (pdimm[1].n_ranks) { + case 2: + pdodt = dual_DD; + break; + case 1: + pdodt = dual_DS; + break; + case 0: + pdodt = dual_D0; + break; + } + break; + case 1: + switch (pdimm[1].n_ranks) { + case 2: + pdodt = dual_SD; + break; + case 1: + pdodt = dual_SS; + break; + case 0: + pdodt = dual_S0; + break; + } + break; + case 0: + switch (pdimm[1].n_ranks) { + case 2: + pdodt = dual_0D; + break; + case 1: + pdodt = dual_0S; + break; + } + break; + } + } /* Pick chip-select local options. */ for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { - /* If not DDR2, odt_rd_cfg and odt_wr_cfg need to be 0. */ - - /* only for single CS? */ - popts->cs_local_opts[i].odt_rd_cfg = 0; - - popts->cs_local_opts[i].odt_wr_cfg = 1; +#if defined(CONFIG_FSL_DDR3) + popts->cs_local_opts[i].odt_rd_cfg = pdodt[i].odt_rd_cfg; + popts->cs_local_opts[i].odt_wr_cfg = pdodt[i].odt_wr_cfg; + popts->cs_local_opts[i].odt_rtt_norm = pdodt[i].odt_rtt_norm; + popts->cs_local_opts[i].odt_rtt_wr = pdodt[i].odt_rtt_wr; +#else + popts->cs_local_opts[i].odt_rd_cfg = FSL_DDR_ODT_NEVER; + popts->cs_local_opts[i].odt_wr_cfg = FSL_DDR_ODT_CS; +#endif popts->cs_local_opts[i].auto_precharge = 0; } @@ -179,6 +472,9 @@ unsigned int populate_memctl_options(int all_DIMMs_registered, popts->twoT_en = 0; popts->threeT_en = 0; + /* for RDIMM, address parity enable */ + popts->ap_en = 1; + /* * BSTTOPRE precharge interval * diff --git a/arch/powerpc/include/asm/fsl_ddr_sdram.h b/arch/powerpc/include/asm/fsl_ddr_sdram.h index 04aeb40753..989c915839 100644 --- a/arch/powerpc/include/asm/fsl_ddr_sdram.h +++ b/arch/powerpc/include/asm/fsl_ddr_sdram.h @@ -24,6 +24,7 @@ #define DDR_OTF 6 /* on-the-fly BC4 and BL8 */ #define DDR_BL8 8 /* burst length 8 */ +#define DDR3_RTT_OFF 0 #define DDR3_RTT_60_OHM 1 /* RTT_Nom = RZQ/4 */ #define DDR3_RTT_120_OHM 2 /* RTT_Nom = RZQ/2 */ #define DDR3_RTT_40_OHM 3 /* RTT_Nom = RZQ/6 */ @@ -50,6 +51,15 @@ typedef ddr3_spd_eeprom_t generic_spd_eeprom_t; #endif #endif /* #if defined(CONFIG_FSL_DDR1) */ +#define FSL_DDR_ODT_NEVER 0x0 +#define FSL_DDR_ODT_CS 0x1 +#define FSL_DDR_ODT_ALL_OTHER_CS 0x2 +#define FSL_DDR_ODT_OTHER_DIMM 0x3 +#define FSL_DDR_ODT_ALL 0x4 +#define FSL_DDR_ODT_SAME_DIMM 0x5 +#define FSL_DDR_ODT_CS_AND_OTHER_DIMM 0x6 +#define FSL_DDR_ODT_OTHER_CS_ONSAMEDIMM 0x7 + /* define bank(chip select) interleaving mode */ #define FSL_DDR_CS0_CS1 0x40 #define FSL_DDR_CS2_CS3 0x20 @@ -106,6 +116,12 @@ typedef struct fsl_ddr_cfg_regs_s { unsigned int ddr_sdram_cfg_2; unsigned int ddr_sdram_mode; unsigned int ddr_sdram_mode_2; + unsigned int ddr_sdram_mode_3; + unsigned int ddr_sdram_mode_4; + unsigned int ddr_sdram_mode_5; + unsigned int ddr_sdram_mode_6; + unsigned int ddr_sdram_mode_7; + unsigned int ddr_sdram_mode_8; unsigned int ddr_sdram_md_cntl; unsigned int ddr_sdram_interval; unsigned int ddr_data_init; @@ -156,6 +172,8 @@ typedef struct memctl_options_s { unsigned int auto_precharge; unsigned int odt_rd_cfg; unsigned int odt_wr_cfg; + unsigned int odt_rtt_norm; + unsigned int odt_rtt_wr; } cs_local_opts[CONFIG_CHIP_SELECTS_PER_CTRL]; /* Special configurations for chip select */ diff --git a/doc/README.fsl-ddr b/doc/README.fsl-ddr index 9e3c5390de..a7ba193f30 100644 --- a/doc/README.fsl-ddr +++ b/doc/README.fsl-ddr @@ -104,4 +104,69 @@ Combination of hwconfig Hwconfig can be combined with multiple parameters, for example, on a supported platform -hwconfig=fsl_ddr:addr_hash=true,ctlr_intlv=cacheline,bank_intlv=cs0_cs1_cs2_cs3 +hwconfig=fsl_ddr:addr_hash=true,ctlr_intlv=cacheline,bank_intlv=cs0_cs1_cs2_cs3,ecc=on + +Table for dynamic ODT for DDR3 +============================== +For single-slot system with quad-rank DIMM and dual-slot system, dynamic ODT may +be needed, depending on the configuration. The numbers in the following tables are +in Ohms. + +* denotes dynamic ODT + +Two slots system ++-----------------------+----------+---------------+-----------------------------+-----------------------------+ +| Configuration | |DRAM controller| Slot 1 | Slot 2 | ++-----------+-----------+----------+-------+-------+--------------+--------------+--------------+--------------+ +| | | | | | Rank 1 | Rank 2 | Rank 1 | Rank 2 | ++ Slot 1 | Slot 2 |Write/Read| Write | Read |-------+------+-------+------+-------+------+-------+------+ +| | | | | | Write | Read | Write | Read | Write | Read | Write | Read | ++-----------+-----------+----------+-------+-------+-------+------+-------+------+-------+------+-------+------+ +| | | Slot 1 | off | 75 | 120 | off | off | off | off | off | 30 | 30 | +| Dual Rank | Dual Rank |----------+-------+-------+-------+------+-------+------+-------+------+-------+------+ +| | | Slot 2 | off | 75 | off | off | 30 | 30 | 120 | off | off | off | ++-----------+-----------+----------+-------+-------+-------+------+-------+------+-------+------+-------+------+ +| | | Slot 1 | off | 75 | 120 | off | off | off | 20 | 20 | | | +| Dual Rank |Single Rank|----------+-------+-------+-------+------+-------+------+-------+------+-------+------+ +| | | Slot 2 | off | 75 | off | off | 20 | 20 | 120 *| off | | | ++-----------+-----------+----------+-------+-------+-------+------+-------+------+-------+------+-------+------+ +| | | Slot 1 | off | 75 | 120 *| off | | | off | off | 20 | 20 | +|Single Rank| Dual Rank |----------+-------+-------+-------+------+-------+------+-------+------+-------+------+ +| | | Slot 2 | off | 75 | 20 | 20 | | | 120 | off | off | off | ++-----------+-----------+----------+-------+-------+-------+------+-------+------+-------+------+-------+------+ +| | | Slot 1 | off | 75 | 120 *| off | | | 30 | 30 | | | +|Single Rank|Single Rank|----------+-------+-------+-------+------+-------+------+-------+------+-------+------+ +| | | Slot 2 | off | 75 | 30 | 30 | | | 120 *| off | | | ++-----------+-----------+----------+-------+-------+-------+------+-------+------+-------+------+-------+------+ +| Dual Rank | Empty | Slot 1 | off | 75 | 40 | off | off | off | | | | | ++-----------+-----------+----------+-------+-------+-------+------+-------+------+-------+------+-------+------+ +| Empty | Dual Rank | Slot 2 | off | 75 | | | | | 40 | off | off | off | ++-----------+-----------+----------+-------+-------+-------+------+-------+------+-------+------+-------+------+ +|Single Rank| Empty | Slot 1 | off | 75 | 40 | off | | | | | | | ++-----------+-----------+----------+-------+-------+-------+------+-------+------+-------+------+-------+------+ +| Empty |Single Rank| Slot 2 | off | 75 | | | | | 40 | off | | | ++-----------+-----------+----------+-------+-------+-------+------+-------+------+-------+------+-------+------+ + +Single slot system ++-------------+------------+---------------+-----------------------------+-----------------------------+ +| | |DRAM controller| Rank 1 | Rank 2 | Rank 3 | Rank 4 | +|Configuration| Write/Read |-------+-------+-------+------+-------+------+-------+------+-------+------+ +| | | Write | Read | Write | Read | Write | Read | Write | Read | Write | Read | ++-------------+------------+-------+-------+-------+------+-------+------+-------+------+-------+------+ +| | R1 | off | 75 | 120 *| off | off | off | 20 | 20 | off | off | +| |------------+-------+-------+-------+------+-------+------+-------+------+-------+------+ +| | R2 | off | 75 | off | 20 | 120 | off | 20 | 20 | off | off | +| Quad Rank |------------+-------+-------+-------+------+-------+------+-------+------+-------+------+ +| | R3 | off | 75 | 20 | 20 | off | off | 120 *| off | off | off | +| |------------+-------+-------+-------+------+-------+------+-------+------+-------+------+ +| | R4 | off | 75 | 20 | 20 | off | off | off | 20 | 120 | off | ++-------------+------------+-------+-------+-------+------+-------+------+-------+------+-------+------+ +| | R1 | off | 75 | 40 | off | off | off | +| Dual Rank |------------+-------+-------+-------+------+-------+------+ +| | R2 | off | 75 | 40 | off | off | off | ++-------------+------------+-------+-------+-------+------+-------+------+ +| Single Rank | R1 | off | 75 | 40 | off | ++-------------+------------+-------+-------+-------+------+ + +Reference http://www.xrosstalkmag.com/mag_issues/xrosstalk_oct08_final.pdf + http://download.micron.com/pdf/technotes/ddr3/tn4108_ddr3_design_guide.pdf -- 2.30.2