diff options
author | Alexandru Gagniuc <mr.nuke.me@gmail.com> | 2013-06-03 13:58:10 -0500 |
---|---|---|
committer | Ronald G. Minnich <rminnich@gmail.com> | 2013-06-04 18:37:53 +0200 |
commit | 78706fd61f44f2765f54d00bdb5cfc2144bb0de4 (patch) | |
tree | 9d55f05d7065abccf8b84258b167876d6d1881bd /src/device | |
parent | 775551c69e1f2d0ab69ea35ef6cbccfddec415d6 (diff) |
DDR3: Add utilities for creating MRS commands
MRS commands are used to tell the DRAM chip what timing and what
termination and drive strength to use, along with other parameters.
The MRS commands are defined by the DDR3 specification [1]. This
makes MRS commands hardware-independent.
MRS command creation is duplicated in various shapes and forms in any
chipset that does DDR3. This is an effort to create a generic MRS API
that can be used with any chipset.
This is used in the VX900 branch.
[1] www.jedec.org/sites/default/files/docs/JESD79-3E.pdf
Change-Id: Ia8bb593e3e28a5923a866042327243d798c3b793
Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Reviewed-on: http://review.coreboot.org/3354
Tested-by: build bot (Jenkins)
Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
Diffstat (limited to 'src/device')
-rw-r--r-- | src/device/dram/ddr3.c | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/src/device/dram/ddr3.c b/src/device/dram/ddr3.c index c745bd74b7..6e3fc2a58d 100644 --- a/src/device/dram/ddr3.c +++ b/src/device/dram/ddr3.c @@ -27,6 +27,10 @@ #include <device/device.h> #include <device/dram/ddr3.h> +/*============================================================================== + * = DDR3 SPD decoding helpers + *----------------------------------------------------------------------------*/ + /** * \brief Checks if the DIMM is Registered based on byte[3] of the SPD * @@ -368,5 +372,218 @@ void dram_print_spd_ddr3(const dimm_attr * dimm) print_ns(" tWTRmin : ", dimm->tWTR); print_ns(" tRTPmin : ", dimm->tRTP); print_ns(" tFAWmin : ", dimm->tFAW); +} + +/*============================================================================== + *= DDR3 MRS helpers + *----------------------------------------------------------------------------*/ + +/* + * MRS command structure: + * cmd[15:0] = Address pins MA[15:0] + * cmd[18:16] = Bank address BA[2:0] + */ + +/* Map tWR value to a bitmask of the MR0 cycle */ +static u16 ddr3_twr_to_mr0_map(u8 twr) +{ + if ((twr >= 5) && (twr <= 8)) + return (twr - 4) << 9; + + /* + * From 8T onwards, we can only use even values. Round up if we are + * given an odd value. + */ + if ((twr >= 9) && (twr <= 14)) + return ((twr + 1) >> 1) << 9; + + /* tWR == 16T is [000] */ + return 0; +} + +/* Map the CAS latency to a bitmask for the MR0 cycle */ +static u16 ddr3_cas_to_mr0_map(u8 cas) +{ + u16 mask = 0; + /* A[6:4] are bits [2:0] of (CAS - 4) */ + mask = ((cas - 4) & 0x07) << 4; + + /* A2 is the MSB of (CAS - 4) */ + if ((cas - 4) & (1 << 3)) + mask |= (1 << 2); + + return mask; +} + +/** + * \brief Get command address for a DDR3 MR0 command + * + * The DDR3 specification only covers odd write_recovery up to 7T. If an odd + * write_recovery greater than 7 is specified, it will be rounded up. If a tWR + * greater than 8 is specified, it is recommended to explicitly round it up or + * down before calling this function. + * + * write_recovery and cas are given in clock cycles. For example, a CAS of 7T + * should be given as 7. + * + * @param write_recovery Write recovery latency, tWR in clock cycles. + * @param cas CAS latency in clock cycles. + */ +mrs_cmd_t ddr3_get_mr0(enum ddr3_mr0_precharge precharge_pd, + u8 write_recovery, + enum ddr3_mr0_dll_reset dll_reset, + enum ddr3_mr0_mode mode, + u8 cas, + enum ddr3_mr0_burst_type burst_type, + enum ddr3_mr0_burst_length burst_length) +{ + mrs_cmd_t cmd = 0 << 16; + + if (precharge_pd == DDR3_MR0_PRECHARGE_FAST) + cmd |= (1 << 12); + + cmd |= ddr3_twr_to_mr0_map(write_recovery); + + if (dll_reset == DDR3_MR0_DLL_RESET_YES) + cmd |= (1 << 8); + + if (mode == DDR3_MR0_MODE_TEST) + cmd |= (1 << 7); + + cmd |= ddr3_cas_to_mr0_map(cas); + + if (burst_type == DDR3_MR0_BURST_TYPE_INTERLEAVED) + cmd |= (1 << 3); + + cmd |= (burst_length & 0x03) << 0; + + return cmd; +} + +static u16 ddr3_rtt_nom_to_mr1_map(enum ddr3_mr1_rtt_nom rtt_nom) +{ + u16 mask = 0; + /* A9 <-> rtt_nom[2] */ + if (rtt_nom & (1 << 2)) + mask |= (1 << 9); + /* A6 <-> rtt_nom[1] */ + if (rtt_nom & (1 << 1)) + mask |= (1 << 6); + /* A2 <-> rtt_nom[0] */ + if (rtt_nom & (1 << 0)) + mask |= (1 << 2); + + return mask; +} + +static u16 ddr3_ods_to_mr1_map(enum ddr3_mr1_ods ods) +{ + u16 mask = 0; + /* A5 <-> ods[1] */ + if (ods & (1 << 1)) + mask |= (1 << 5); + /* A1 <-> ods[0] */ + if (ods & (1 << 0)) + mask |= (1 << 1); + + return mask; +} + +/** + * \brief Get command address for a DDR3 MR1 command + */ +mrs_cmd_t ddr3_get_mr1(enum ddr3_mr1_qoff qoff, + enum ddr3_mr1_tqds tqds, + enum ddr3_mr1_rtt_nom rtt_nom, + enum ddr3_mr1_write_leveling write_leveling, + enum ddr3_mr1_ods ods, + enum ddr3_mr1_additive_latency additive_latency, + enum ddr3_mr1_dll dll_disable) +{ + mrs_cmd_t cmd = 1 << 16; + + if (qoff == DDR3_MR1_QOFF_DISABLE) + cmd |= (1 << 12); + + if (tqds == DDR3_MR1_TQDS_ENABLE) + cmd |= (1 << 11); + + cmd |= ddr3_rtt_nom_to_mr1_map(rtt_nom); + + if (write_leveling == DDR3_MR1_WRLVL_ENABLE) + cmd |= (1 << 7); + + cmd |= ddr3_ods_to_mr1_map(ods); + + cmd |= (additive_latency & 0x03) << 3; + + if (dll_disable == DDR3_MR1_DLL_DISABLE) + cmd |= (1 << 0); + + return cmd; +} + +/** + * \brief Get command address for a DDR3 MR2 command + * + * cas_cwl is given in clock cycles. For example, a cas_cwl of 7T should be + * given as 7. + * + * @param cas_cwl CAS write latency in clock cycles. + */ +mrs_cmd_t ddr3_get_mr2(enum ddr3_mr2_rttwr rtt_wr, + enum ddr3_mr2_srt_range extended_temp, + enum ddr3_mr2_asr self_refresh, u8 cas_cwl) +{ + mrs_cmd_t cmd = 2 << 16; + + cmd |= (rtt_wr & 0x03) << 9; + + if (extended_temp == DDR3_MR2_SRT_EXTENDED) + cmd |= (1 << 7); + + if (self_refresh == DDR3_MR2_ASR_AUTO) + cmd |= (1 << 6); + + cmd |= ((cas_cwl - 5) & 0x07) << 3; + return cmd; +} + +/** + * \brief Get command address for a DDR3 MR3 command + * + * @param dataflow_from_mpr Specify a non-zero value to put DRAM in read + * leveling mode. Zero for normal operation. + */ +mrs_cmd_t ddr3_get_mr3(char dataflow_from_mpr) +{ + mrs_cmd_t cmd = 3 << 16; + + if (dataflow_from_mpr) + cmd |= (1 << 2); + + return cmd; +} + +/** + * \brief Mirror the address bits for this MRS command + * + * Swap the following bits in the MRS command: + * - MA3 <-> MA4 + * - MA5 <-> MA6 + * - MA7 <-> MA8 + * - BA0 <-> BA1 + */ +mrs_cmd_t ddr3_mrs_mirror_pins(mrs_cmd_t cmd) +{ + u32 downshift, upshift; + /* High bits= A4 | A6 | A8 | BA1 */ + /* Low bits = A3 | A5 | A7 | BA0 */ + u32 lowbits = (1 << 3) | (1 << 5) | (1 << 7) | (1 << 16); + downshift = (cmd & (lowbits << 1)); + upshift = (cmd & lowbits); + cmd &= ~(lowbits | (lowbits << 1)); + cmd |= (downshift >> 1) | (upshift << 1); + return cmd; } |