Riku Viitanen has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/85793?usp=email )
Change subject: nb/sandybridge: Implement automatic DRAM voltage setting ......................................................................
nb/sandybridge: Implement automatic DRAM voltage setting
This change enables using higher performance XMP profiles that request more than the standard 1.5V on boards that can adjust their DRAM voltage from firmware.
Precautions are taken to not run any modules outside their specifications. Furthermore, voltages higher than 1.65V are not enabled by default for safety.
TEST=ASRock Z77 Extreme4. Tested various combinations of XMP and non-XMP modules.
Change-Id: I1a8857deee85fd635429afd3cbf93cad7a7d589b Signed-off-by: Riku Viitanen riku.viitanen@protonmail.com --- M src/device/Kconfig M src/northbridge/intel/sandybridge/raminit.c M src/northbridge/intel/sandybridge/raminit_common.h M src/northbridge/intel/sandybridge/raminit_native.c M src/northbridge/intel/sandybridge/sandybridge.h 5 files changed, 78 insertions(+), 3 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/93/85793/1
diff --git a/src/device/Kconfig b/src/device/Kconfig index bcff6fd..516e840 100644 --- a/src/device/Kconfig +++ b/src/device/Kconfig @@ -1035,6 +1035,12 @@ Please note that enabling D3Cold support may break system suspend-to-RAM (S3) functionality.
+config MAINBOARD_HAS_ADJUSTABLE_DRAM_VOLTAGE + def_bool n + help + Selected by mainboards that can adjust their RAM operating + voltage from firmware. + source "src/device/dram/Kconfig"
endmenu diff --git a/src/northbridge/intel/sandybridge/raminit.c b/src/northbridge/intel/sandybridge/raminit.c index 9ec8fb6f..a230429 100644 --- a/src/northbridge/intel/sandybridge/raminit.c +++ b/src/northbridge/intel/sandybridge/raminit.c @@ -193,6 +193,55 @@ } /* CONFIG(HAVE_SPD_IN_CBFS) */ }
+#ifdef CONFIG_MAINBOARD_HAS_ADJUSTABLE_DRAM_VOLTAGE +#define VOLTAGE_MIN 1500 +#define VOLTAGE_MAX 1650 +#define VOLTAGE_DEFAULT 1500 +static int dram_find_highest_common_voltage(spd_ddr3_raw_data *spd) +{ + printram("Begin DRAM voltage search.\n"); + int channel, spd_slot, voltage = 0; + struct dimm_attr_ddr3_st dimm; + + FOR_ALL_CHANNELS { + for (int slot = 0; slot < NUM_SLOTS; slot++) { + spd_slot = 2 * channel + slot; + printram("channel=%i, slot=%i\n", channel, slot); + + if (spd[spd_slot][SPD_MEMORY_TYPE] == SPD_MEMORY_TYPE_SDRAM_DDR3) { + spd_xmp_decode_ddr3(&dimm, spd[spd_slot], + DDR3_XMP_PROFILE_1); + if (dimm.dram_type != SPD_MEMORY_TYPE_SDRAM_DDR3) { + /* Non-XMP DIMM. Use default voltage since + * every DIMM is supposed to work with it */ + printram("Non-XMP DIMM detected. Defaulting to %imV.", + VOLTAGE_DEFAULT); + return VOLTAGE_DEFAULT; + + } else if (dimm.voltage > VOLTAGE_MAX) { + printram("XMP requested voltage too high!\n"); + if (voltage != 0) + voltage = MIN(voltage, VOLTAGE_MAX); + else + voltage = VOLTAGE_MAX; + + } else if (dimm.voltage < VOLTAGE_MIN) { + printram("XMP requested voltage too low!?\n"); + return VOLTAGE_MIN; + + } else { + printram("Set temporary voltage to %imV\n", dimm.voltage); + voltage = MAX(voltage, dimm.voltage); + } + } + printram("voltage is %imV\n", voltage); + } + } + + return voltage; +} +#endif + static void dram_find_spds_ddr3(spd_ddr3_raw_data *spd, ramctr_timing *ctrl) { int dimms = 0, ch_dimms; @@ -204,6 +253,16 @@ ctrl->extended_temperature_range = 1; ctrl->auto_self_refresh = 1;
+ if (CONFIG(MAINBOARD_HAS_ADJUSTABLE_DRAM_VOLTAGE)) { + printk(BIOS_DEBUG, "Mainboard has adjustable DRAM voltage. " + "Searching for a suitable voltage.\n"); + ctrl->voltage = dram_find_highest_common_voltage(spd); + printk(BIOS_DEBUG, "Setting DRAM voltage to %imV\n", ctrl->voltage); + mainboard_set_dram_voltage(ctrl->voltage); + } else { + ctrl->voltage = 1500; + } + FOR_ALL_CHANNELS { ctrl->channel_size_mb[channel] = 0;
@@ -241,8 +300,7 @@ else spd_decode_ddr3(dimm, spd[spd_slot]);
- } else if (dimm->voltage != 1500) { - /* TODO: Support DDR3 voltages other than 1500mV */ + } else if (dimm->voltage != ctrl->voltage) { printram("XMP profile's requested %u mV is unsupported.\n", dimm->voltage);
diff --git a/src/northbridge/intel/sandybridge/raminit_common.h b/src/northbridge/intel/sandybridge/raminit_common.h index ee7c525..4aeacc5 100644 --- a/src/northbridge/intel/sandybridge/raminit_common.h +++ b/src/northbridge/intel/sandybridge/raminit_common.h @@ -305,7 +305,7 @@ /* * WARNING: Do not forget to increase MRC_CACHE_VERSION when the saved data is changed! */ -#define MRC_CACHE_VERSION 5 +#define MRC_CACHE_VERSION 6
enum power_down_mode { PDM_NONE = 0, @@ -417,6 +417,8 @@
struct ram_rank_timings timings[NUM_CHANNELS][NUM_SLOTRANKS];
+ u16 voltage; /* operating voltage in millivolts */ + dimm_info info; } ramctr_timing;
diff --git a/src/northbridge/intel/sandybridge/raminit_native.c b/src/northbridge/intel/sandybridge/raminit_native.c index ed11945..555cf5f 100644 --- a/src/northbridge/intel/sandybridge/raminit_native.c +++ b/src/northbridge/intel/sandybridge/raminit_native.c @@ -645,6 +645,12 @@ dram_dimm_mapping(ctrl); }
+ if (CONFIG(MAINBOARD_HAS_ADJUSTABLE_DRAM_VOLTAGE)) { + printk(BIOS_DEBUG, "Setting DRAM voltage to %imV\n", + ctrl->voltage); + mainboard_set_dram_voltage(ctrl->voltage); + } + /* Set MPLL frequency */ dram_freq(ctrl);
diff --git a/src/northbridge/intel/sandybridge/sandybridge.h b/src/northbridge/intel/sandybridge/sandybridge.h index 0eaa4ec..1d6214b 100644 --- a/src/northbridge/intel/sandybridge/sandybridge.h +++ b/src/northbridge/intel/sandybridge/sandybridge.h @@ -63,6 +63,9 @@ void northbridge_romstage_finalize(void); void early_init_dmi(void);
+/* optional callback for setting the DRAM voltage */ +void mainboard_set_dram_voltage(int voltage); + /* mainboard_early_init: Optional callback, run after console init but before raminit. */ void mainboard_early_init(int s3resume); void perform_raminit(int s3resume);