[coreboot-gerrit] New patch to review for coreboot: 59c72d4 DDR3: Add utilities for creating MRS commands

Alexandru Gagniuc (mr.nuke.me@gmail.com) gerrit at coreboot.org
Mon Jun 3 21:12:04 CEST 2013


Alexandru Gagniuc (mr.nuke.me at gmail.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/3354

-gerrit

commit 59c72d4663a483700080529f7f9f4a0491beeb82
Author: Alexandru Gagniuc <mr.nuke.me at gmail.com>
Date:   Mon Jun 3 13:58:10 2013 -0500

    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 at gmail.com>
---
 src/device/dram/ddr3.c         | 195 +++++++++++++++++++++++++++++++++++++++++
 src/include/device/dram/ddr3.h | 119 +++++++++++++++++++++++++
 2 files changed, 314 insertions(+)

diff --git a/src/device/dram/ddr3.c b/src/device/dram/ddr3.c
index c745bd7..5318d64 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,196 @@ 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]
+ */
+
+static u16 ddr3_twr_to_mr0_map(u8 twr)
+{
+	if ((twr >= 5) && (twr <= 8))
+		return (twr - 4) << 9;
+
+	if ((twr >= 9) && (twr <= 14))
+		return (twr >> 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 << 4))
+		mask |= (1 << 2);
+
+	return mask;
+}
+
+/**
+ * \brief Get command address for a DDR3 MR0 command
+ */
+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_lenght)
+{
+	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_lenght & 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
+ */
+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
+ */
+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;
+}
+
+mrs_cmd_t ddr3_mrs_swap_pins(mrs_cmd_t 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;
 }
diff --git a/src/include/device/dram/ddr3.h b/src/include/device/dram/ddr3.h
index 926f7b9..fb50808 100644
--- a/src/include/device/dram/ddr3.h
+++ b/src/include/device/dram/ddr3.h
@@ -186,4 +186,123 @@ static inline u32 volatile_read(volatile u32 addr)
 	return result;
 }
 
+/**
+ * \brief Representation of an MRS command
+ *
+ * This represents an MRS command as seen by the DIMM. This is not a memory
+ * address that can be read to generate an MRS command. The mapping of CPU
+ * to memory pins is hardware-dependent.
+ * \n
+ * The idea is to generalize the MRS code, and only need a hardware-specific
+ * function to map the MRS bits to CPU address bits. An MRS command can be
+ * sent like:
+ * @code{.c}
+ *	u32 addr;
+ *	mrs_cmd_t mrs;
+ *	chipset_enable_mrs_command_mode();
+ *	mrs = ddr3_get_mr2(rtt_wr, srt, asr, cwl)
+ *	if (rank_has_mirrorred_pins)
+ *		mrs = ddr3_mrs_mirror_pins(mrs);
+ *	addr = chipset_specific_get_mrs_addr(mrs);
+ *	volatile_read(addr);
+ * @endcode
+ *
+ * The MRS representation has the following structure:
+ *	- cmd[15:0] = Address pins MA[15:0]
+ *	- cmd[18:16] = Bank address BA[2:0]
+ */
+typedef u32 mrs_cmd_t;
+
+enum ddr3_mr0_precharge {
+	DDR3_MR0_PRECHARGE_SLOW = 0,
+	DDR3_MR0_PRECHARGE_FAST = 1,
+};
+enum ddr3_mr0_mode {
+	DDR3_MR0_MODE_NORMAL = 0,
+	DDR3_MR0_MODE_TEST = 1,
+};
+enum ddr3_mr0_dll_reset {
+	DDR3_MR0_DLL_RESET_NO = 0,
+	DDR3_MR0_DLL_RESET_YES = 1,
+};
+enum ddr3_mr0_burst_type {
+	DDR3_MR0_BURST_TYPE_SEQUENTIAL = 0,
+	DDR3_MR0_BURST_TYPE_INTERLEAVED = 1,
+};
+enum ddr3_mr0_burst_length {
+	DDR3_MR0_BURST_LENGTH_8 = 0,
+	DDR3_MR0_BURST_LENGTH_CHOP = 1,
+	DDR3_MR0_BURST_LENGTH_4 = 2,
+};
+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 interleaved_burst,
+		       enum ddr3_mr0_burst_length burst_lenght);
+
+enum ddr3_mr1_qoff {
+	DDR3_MR1_QOFF_ENABLE = 0,
+	DDR3_MR1_QOFF_DISABLE = 1,
+};
+enum ddr3_mr1_tqds {
+	DDR3_MR1_TQDS_DISABLE = 0,
+	DDR3_MR1_TQDS_ENABLE = 1,
+};
+enum ddr3_mr1_write_leveling {
+	DDR3_MR1_WRLVL_DISABLE = 0,
+	DDR3_MR1_WRLVL_ENABLE = 1,
+};
+enum ddr3_mr1_rtt_nom {
+	DDR3_MR1_RTT_NOM_OFF = 0,
+	DDR3_MR1_RTT_NOM_RZQ4 = 1,
+	DDR3_MR1_RTT_NOM_RZQ2 = 2,
+	DDR3_MR1_RTT_NOM_RZQ6 = 3,
+	DDR3_MR1_RTT_NOM_RZQ12 = 4,
+	DDR3_MR1_RTT_NOM_RZQ8 = 5,
+};
+enum ddr3_mr1_additive_latency {
+	DDR3_MR1_AL_DISABLE = 0,
+	DDR3_MR1_AL_CL_MINUS_1 = 1,
+	DDR3_MR1_AL_CL_MINUS_2 = 2,
+};
+enum ddr3_mr1_ods {
+	DDR3_MR1_ODS_RZQ6 = 0,
+	DDR3_MR1_ODS_RZQ7 = 1,
+};
+enum ddr3_mr1_dll {
+	DDR3_MR1_DLL_ENABLE = 0,
+	DDR3_MR1_DLL_DISABLE = 1,
+};
+
+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 output_drive_strenght,
+		       enum ddr3_mr1_additive_latency additive_latency,
+		       enum ddr3_mr1_dll dll_disable);
+
+enum ddr3_mr2_rttwr {
+	DDR3_MR2_RTTWR_OFF = 0,
+	DDR3_MR2_RTTWR_RZQ4 = 1,
+	DDR3_MR2_RTTWR_RZQ2 = 2,
+};
+enum ddr3_mr2_srt_range {
+	DDR3_MR2_SRT_NORMAL = 0,
+	DDR3_MR2_SRT_EXTENDED = 1,
+};
+enum ddr3_mr2_asr {
+	DDR3_MR2_ASR_MANUAL = 0,
+	DDR3_MR2_ASR_AUTO = 1,
+};
+
+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 ddr3_get_mr3(char dataflow_from_mpr);
+mrs_cmd_t ddr3_mrs_mirror_pins(mrs_cmd_t cmd);
+
 #endif				/* DEVICE_DRAM_DDR3_H */



More information about the coreboot-gerrit mailing list