Arthur Heymans (arthur@aheymans.xyz) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/18156
-gerrit
commit d021ebead81c608d2c7cb6734c6488181765c477 Author: Arthur Heymans arthur@aheymans.xyz Date: Mon Jan 16 23:32:02 2017 +0100
nb/intel/x4x: Refactor dram freq and cas selection
The code did not check for tAC and for tCLK potentially selected the wrong spd offset for minimum cycle time.
Adds some debugging output for freq and cas selection.
The code is also written to be easily extendable for officially unsported dram frequencies like 533MHz and 400MHz which some vendor bios seem to support.
Change-Id: Ie0d8326af50d3125bf35918ba645a6df27dd2ee4 Signed-off-by: Arthur Heymans arthur@aheymans.xyz --- src/northbridge/intel/x4x/raminit.c | 135 ++++++++++++++++++++++++------------ 1 file changed, 91 insertions(+), 44 deletions(-)
diff --git a/src/northbridge/intel/x4x/raminit.c b/src/northbridge/intel/x4x/raminit.c index 86f63f1..77e427c 100644 --- a/src/northbridge/intel/x4x/raminit.c +++ b/src/northbridge/intel/x4x/raminit.c @@ -160,13 +160,6 @@ static void sdram_read_spds(struct sysinfo *s) } }
-static u8 msbpos(u8 val) //Reverse -{ - u8 i; - for (i = 7; (i >= 0) && ((val & (1 << i)) == 0); i--); - return i; -} - static void mchinfo_ddr2(struct sysinfo *s) { const u32 eax = cpuid_ext(0x04, 0).eax; @@ -195,17 +188,46 @@ static void mchinfo_ddr2(struct sysinfo *s) } }
+/* Decode tclk to picoseconds */ +static int decode_tclk(u8 spd_data) +{ + int high_nibble = (spd_data >> 4) * 1000; + int low_nibble; + if ((spd_data & 0xf) > 0x9) /* Should be < 0xd */ + low_nibble = ((spd_data & 0xf) - 0x9) * 250; + else + low_nibble = spd_data * 100; + return high_nibble + low_nibble; +} + static void sdram_detect_ram_speed(struct sysinfo *s) { - u8 i; + u8 i, j; u8 commoncas = 0; + u8 highest_commoncas; u8 currcas; u8 currfreq; u8 maxfreq; u8 freq = 0; + u8 high_cas; + u8 spd_tclk_lat[TOTAL_DIMMS][8] = { 0 }; + u8 spd_tac_lat[TOTAL_DIMMS][8] = { 0 }; + u8 tclk, tclk_target; + u8 tac, tac_target; + + const u8 ddr2_speeds_table[] = { + 0x50, 0x60, /* DDR2 400: tCLK = 5.0ns tAC = 0.6ns */ + 0x3d, 0x50, /* DDR2 533: tCLK = 3.75ns tAC = 0.5ns */ + 0x30, 0x45, /* DDR2 667: tCLK = 3.0ns tAC = 0.45ns */ + 0x25, 0x40, /* DDR2 800: tCLK = 2.5ns tAC = 0.40ns */ + }; + + const u8 spd_lookup_table[] = { + SPD_MIN_CYCLE_TIME_AT_CAS_MAX, SPD_ACCESS_TIME_FROM_CLOCK, + SPD_SDRAM_CYCLE_TIME_2ND, SPD_ACCESS_TIME_FROM_CLOCK_2ND, + SPD_SDRAM_CYCLE_TIME_3RD, SPD_ACCESS_TIME_FROM_CLOCK_3RD, + };
- // spdidx,cycletime @CAS 5 6 - u8 idx800[7][2] = {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {23,0x30}, {9,0x25}}; int found = 0;
// Find max FSB speed @@ -225,57 +247,82 @@ static void sdram_detect_ram_speed(struct sysinfo *s) break; }
- // Max RAM speed if (s->spd_type == DDR2) { - - maxfreq = MEM_CLOCK_800MHz; - - // Choose common CAS latency from {6,5}, 4 does not work - commoncas = 0x60; - + /* Only CAS 5 and 6 are supported */ + commoncas = SPD_CAS_LATENCY_DDR2_6 | SPD_CAS_LATENCY_DDR2_5; FOR_EACH_POPULATED_DIMM(s->dimms, i) { + if (s->dimms[i].cas_latencies & SPD_CAS_LATENCY_DDR2_7) { + high_cas = 7; + } else if (s->dimms[i].cas_latencies & SPD_CAS_LATENCY_DDR2_6) { + high_cas = 6; + } else if (s->dimms[i].cas_latencies & SPD_CAS_LATENCY_DDR2_5) { + high_cas = 5; + } /* Don't check for lower CAS since not supported */ commoncas &= s->dimms[i].cas_latencies; + for (j = 0; j < 3; i++) { + if (s->dimms[i].cas_latencies & (1 << (high_cas - j))) { + spd_tclk_lat[i][high_cas - j] = + s->dimms[i].spd_data[spd_lookup_table[2 * j]]; + spd_tac_lat[i][high_cas - j] = + s->dimms[i].spd_data[spd_lookup_table[2 * j + 1]]; + } + } } - if (commoncas == 0) { + if (commoncas == 0) die("No common CAS among dimms\n"); - } + else if (commoncas & SPD_CAS_LATENCY_DDR2_6) + highest_commoncas = 6; + else + highest_commoncas = 5; + + /* + * FIXME: datasheets say only 667MHz and 800MHz are supported + * but vendor bios also supports 533MHz and possibly 400MHz + */ + /* Test from higher to lower frequency, from lower to higher CAS latency */ + for (currfreq = MEM_CLOCK_800MHz; currfreq >= MEM_CLOCK_667MHz; currfreq--) { + printk(BIOS_DEBUG, "Trying DDR2: "); + switch (currfreq) { + case MEM_CLOCK_400MHz: + printk(BIOS_DEBUG, "%dMHz\n", 400); + case MEM_CLOCK_533MHz: + printk(BIOS_DEBUG, "%dMHz\n", 533); + case MEM_CLOCK_667MHz: + printk(BIOS_DEBUG, "%dMHz\n", 667); + case MEM_CLOCK_800MHz: + printk(BIOS_DEBUG, "%dMHz\n", 800); + }
- // Working from fastest to slowest, - // fast->slow 5@800 6@800 5@667 - found = 0; - for (currcas = 5; currcas <= msbpos(commoncas); currcas++) { - currfreq = maxfreq; - if (currfreq == MEM_CLOCK_800MHz) { + for (currcas = 5; currcas <= highest_commoncas; currcas++) { + printk(BIOS_DEBUG, " Trying CAS: %d\n", currcas); found = 1; FOR_EACH_POPULATED_DIMM(s->dimms, i) { - if (s->dimms[i].spd_data[idx800[currcas][0]] > idx800[currcas][1]) { - // this is too fast + tclk = decode_tclk(spd_tclk_lat[i][currcas]); + if (tclk == 0) + /* this timing is not supprted */ found = 0; + break; + tclk_target = ddr2_speeds_table[2 * currfreq]; + tac = spd_tac_lat[i][currcas]; + tac_target = ddr2_speeds_table[2 * currfreq + 1]; + printk(BIOS_DEBUG, " tCLK: %dps , tCLK_target: %dps\n", tclk, tclk_target); + printk(BIOS_DEBUG, " tAC: 0.%xns , tAC_target: 0.%xns\n", tac, tac_target); + if ((tclk > tclk_target) && (tac > tac_target)) { + printk(BIOS_DEBUG, " Timing too fast\n"); + found = 0; + break; /* No need to check other dimms for these timings */ } } - if (found) - break; - } - } - - if (!found) { - currcas = 5; - currfreq = MEM_CLOCK_667MHz; - found = 1; - FOR_EACH_POPULATED_DIMM(s->dimms, i) { - if (s->dimms[i].spd_data[9] > 0x30) { - // this is too fast - found = 0; + if (found) { + s->selected_timings.mem_clk = currfreq; + s->selected_timings.CAS = currcas; + return; } } } - if (!found) die("No valid CAS/frequencies detected\n");
- s->selected_timings.mem_clk = currfreq; - s->selected_timings.CAS = currcas; - } else { // DDR3 // Limit frequency for MCH maxfreq = (s->max_ddr2_mhz == 800) ? MEM_CLOCK_800MHz : MEM_CLOCK_667MHz;