aboutsummaryrefslogtreecommitdiff
path: root/src/northbridge/intel/x4x/raminit.c
diff options
context:
space:
mode:
authorArthur Heymans <arthur@aheymans.xyz>2017-04-05 16:17:26 +0200
committerArthur Heymans <arthur@aheymans.xyz>2017-12-16 10:00:10 +0000
commit3cf94032bced9345f4b9d71b7d2becab4dfcd530 (patch)
tree49546878d13dc164f18db96fe6cc1ddc914d3026 /src/northbridge/intel/x4x/raminit.c
parentcb0c40d3503badee7939393360903ebdc904609a (diff)
nb/x4x/raminit: Rewrite SPD decode and timing selection
This is mostly written from scratch and uses common spd ddr2 decode functions. This improves the following: * This fixes incorrect CAS/Freq detection on DDR2; * Fixes tRFC computation; tRFC == 78 is a valid timing which is excluded and 0 ends up being used; (TESTED) * Timings selection does not use loops; * Removes ddr3 spd decode and is re-added in follow-up patches using common ddr3 spd functions; * Raminit would bail out if a dimm was unsupported, now in some cases it just marks the dimm slot as empty; * It dramatically reduces stack usage since it does not allocate 4 times 256 bytes to store full SPDs, amongs other unused things that were stored in sysinfo; * Reports when no dimms are present; * Uses i2c block read to read SPD which is about 5 times faster than bytewise read, with a fallback to smbus mode in case of failure, which does seem to happen when the system is forcefully powered off. Change-Id: I760eeaa3bd4f2bc25a517ddb1b9533c971454071 Signed-off-by: Arthur Heymans <arthur@aheymans.xyz> Reviewed-on: https://review.coreboot.org/19143 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Martin Roth <martinroth@google.com>
Diffstat (limited to 'src/northbridge/intel/x4x/raminit.c')
-rw-r--r--src/northbridge/intel/x4x/raminit.c478
1 files changed, 255 insertions, 223 deletions
diff --git a/src/northbridge/intel/x4x/raminit.c b/src/northbridge/intel/x4x/raminit.c
index dc64c51ffc..cd786bb266 100644
--- a/src/northbridge/intel/x4x/raminit.c
+++ b/src/northbridge/intel/x4x/raminit.c
@@ -32,150 +32,71 @@
#include <pc80/mc146818rtc.h>
#include <spd.h>
#include <string.h>
+#include <device/dram/ddr2.h>
static inline int spd_read_byte(unsigned int device, unsigned int address)
{
return smbus_read_byte(device, address);
}
-static void sdram_read_spds(struct sysinfo *s)
+struct abs_timings {
+ u32 min_tclk;
+ u32 min_tRAS;
+ u32 min_tRP;
+ u32 min_tRCD;
+ u32 min_tWR;
+ u32 min_tRFC;
+ u32 min_tWTR;
+ u32 min_tRRD;
+ u32 min_tRTP;
+ u32 min_tCLK_cas[8];
+ u32 cas_supported;
+};
+
+#define CTRL_MIN_TCLK_DDR2 TCK_400MHZ
+
+static void select_cas_dramfreq_ddr2(struct sysinfo *s,
+ const struct abs_timings *saved_timings)
{
- u8 i, j, chan;
- int status = 0;
- FOR_EACH_DIMM(i) {
- if (s->spd_map[i] == 0) {
- /* Non-existent SPD address */
- s->dimms[i].card_type = RAW_CARD_UNPOPULATED;
- continue;
- }
- for (j = 0; j < 64; j++) {
- status = spd_read_byte(s->spd_map[i], j);
- if (status < 0) {
- /* No SPD here */
- s->dimms[i].card_type = RAW_CARD_UNPOPULATED;
- break;
- }
- s->dimms[i].spd_data[j] = (u8) status;
- if (j == 62)
- s->dimms[i].card_type = ((u8) status) & 0x1f;
- }
-
- if (status >= 0) {
- if (IS_ENABLED(CONFIG_DEBUG_RAM_SETUP))
- hexdump(s->dimms[i].spd_data, 64);
- }
- }
-
- s->spd_type = 0;
- int fail = 1;
- FOR_EACH_POPULATED_DIMM(s->dimms, i) {
- switch ((enum ddrxspd) s->dimms[i].spd_data[2]) {
- case DDR2SPD:
- if (s->spd_type == 0)
- s->spd_type = DDR2;
- else if (s->spd_type == DDR3)
- die("DIMM type mismatch\n");
- break;
- case DDR3SPD:
- default:
- if (s->spd_type == 0)
- s->spd_type = DDR3;
- else if (s->spd_type == DDR2)
- die("DIMM type mismatch\n");
+ u8 try_cas;
+ /* Currently only these CAS are supported */
+ u8 cas_mask = SPD_CAS_LATENCY_DDR2_5 | SPD_CAS_LATENCY_DDR2_6;
+
+ cas_mask &= saved_timings->cas_supported;
+ try_cas = spd_get_msbs(cas_mask);
+
+ while (cas_mask & (1 << try_cas) && try_cas > 0) {
+ s->selected_timings.CAS = try_cas;
+ s->selected_timings.tclk = saved_timings->min_tCLK_cas[try_cas];
+ if (s->selected_timings.tclk >= CTRL_MIN_TCLK_DDR2 &&
+ saved_timings->min_tCLK_cas[try_cas] !=
+ saved_timings->min_tCLK_cas[try_cas - 1])
break;
- }
+ try_cas--;
}
- if (s->spd_type == DDR3) {
- FOR_EACH_POPULATED_DIMM(s->dimms, i) {
- s->dimms[i].sides = (s->dimms[i].spd_data[5] & 0x0f) + 1;
- s->dimms[i].ranks = ((s->dimms[i].spd_data[7] >> 3) & 0x7) + 1;
- s->dimms[i].chip_capacity = (s->dimms[i].spd_data[4] & 0xf);
- s->dimms[i].banks = 8;
- s->dimms[i].rows = ((s->dimms[i].spd_data[5] >> 3) & 0x7) + 12;
- s->dimms[i].cols = (s->dimms[i].spd_data[5] & 0x7) + 9;
- s->dimms[i].cas_latencies = 0xfe;
- s->dimms[i].cas_latencies &= (s->dimms[i].spd_data[14] << 1);
- if (s->dimms[i].cas_latencies == 0)
- s->dimms[i].cas_latencies = 0x40;
- s->dimms[i].tAAmin = s->dimms[i].spd_data[16];
- s->dimms[i].tCKmin = s->dimms[i].spd_data[12];
- s->dimms[i].width = s->dimms[i].spd_data[7] & 0x7;
- s->dimms[i].page_size = s->dimms[i].width * (1 << s->dimms[i].cols); // Bytes
- s->dimms[i].tRAS = ((s->dimms[i].spd_data[21] & 0xf) << 8) |
- s->dimms[i].spd_data[22];
- s->dimms[i].tRP = s->dimms[i].spd_data[20];
- s->dimms[i].tRCD = s->dimms[i].spd_data[18];
- s->dimms[i].tWR = s->dimms[i].spd_data[17];
- fail = 0;
- }
- } else if (s->spd_type == DDR2) {
- FOR_EACH_POPULATED_DIMM(s->dimms, i) {
- s->dimms[i].sides = (s->dimms[i].spd_data[5] & 0x7) + 1;
- s->dimms[i].banks = (s->dimms[i].spd_data[17] >> 2) - 1;
- s->dimms[i].chip_capacity = s->dimms[i].banks;
- s->dimms[i].rows = s->dimms[i].spd_data[3];// - 12;
- s->dimms[i].cols = s->dimms[i].spd_data[4];// - 9;
- s->dimms[i].cas_latencies = s->dimms[i].spd_data[18];
- if (s->dimms[i].cas_latencies == 0)
- s->dimms[i].cas_latencies = 0x60; // 6,5 CL
- s->dimms[i].tAAmin = s->dimms[i].spd_data[26];
- s->dimms[i].tCKmin = s->dimms[i].spd_data[25];
- s->dimms[i].width = (s->dimms[i].spd_data[13] >> 3) - 1;
- s->dimms[i].page_size = (s->dimms[i].width+1) * (1 << s->dimms[i].cols); // Bytes
- s->dimms[i].tRAS = s->dimms[i].spd_data[30];
- s->dimms[i].tRP = s->dimms[i].spd_data[27];
- s->dimms[i].tRCD = s->dimms[i].spd_data[29];
- s->dimms[i].tWR = s->dimms[i].spd_data[36];
- s->dimms[i].ranks = s->dimms[i].sides; // XXX
-
- printk(BIOS_DEBUG, "DIMM %d\n", i);
- printk(BIOS_DEBUG, " Sides : %d\n", s->dimms[i].sides);
- printk(BIOS_DEBUG, " Banks : %d\n", s->dimms[i].banks);
- printk(BIOS_DEBUG, " Ranks : %d\n", s->dimms[i].ranks);
- printk(BIOS_DEBUG, " Rows : %d\n", s->dimms[i].rows);
- printk(BIOS_DEBUG, " Cols : %d\n", s->dimms[i].cols);
- printk(BIOS_DEBUG, " Page size : %d\n", s->dimms[i].page_size);
- printk(BIOS_DEBUG, " Width : %d\n", (s->dimms[i].width+1)*8);
- fail = 0;
- }
- }
- if (fail)
- die("No memory dimms, halt\n");
- FOR_EACH_POPULATED_CHANNEL(s->dimms, chan) {
- FOR_EACH_POPULATED_DIMM_IN_CHANNEL(s->dimms, chan, i) {
- int dimm_config;
- if (s->dimms[i].ranks == 1) {
- if (s->dimms[i].width == 0) /* x8 */
- dimm_config = 1;
- else /* x16 */
- dimm_config = 3;
- } else {
- if (s->dimms[i].width == 0) /* x8 */
- dimm_config = 2;
- else
- die("Dual-rank x16 not supported\n");
- }
- s->dimm_config[chan] |=
- dimm_config << (i % DIMMS_PER_CHANNEL) * 2;
- }
- printk(BIOS_DEBUG, " Config[CH%d] : %d\n", chan, s->dimm_config[chan]);
- }
-}
-static u8 msbpos(u8 val) //Reverse
-{
- u8 i;
- for (i = 7; (i >= 0) && ((val & (1 << i)) == 0); i--)
- ;
- return i;
+ if ((s->selected_timings.CAS < 3) || (s->selected_timings.tclk == 0))
+ die("Could not find common memory frequency and CAS\n");
+
+ switch (s->selected_timings.tclk) {
+ case TCK_200MHZ:
+ case TCK_266MHZ:
+ /* FIXME: this works on vendor BIOS */
+ die("Selected dram frequency not supported\n");
+ case TCK_333MHZ:
+ s->selected_timings.mem_clk = MEM_CLOCK_667MHz;
+ break;
+ case TCK_400MHZ:
+ s->selected_timings.mem_clk = MEM_CLOCK_800MHz;
+ break;
+ }
}
static void mchinfo_ddr2(struct sysinfo *s)
{
const u32 eax = cpuid_ext(0x04, 0).eax;
- s->cores = ((eax >> 26) & 0x3f) + 1;
- printk(BIOS_WARNING, "%d CPU cores\n", s->cores);
+ printk(BIOS_WARNING, "%d CPU cores\n", ((eax >> 26) & 0x3f) + 1);
u32 capid = pci_read_config16(PCI_DEV(0, 0, 0), 0xe8);
if (!(capid & (1<<(79-64))))
@@ -195,21 +116,126 @@ static void mchinfo_ddr2(struct sysinfo *s)
printk(BIOS_WARNING, "VT-d enabled\n");
}
-static void sdram_detect_ram_speed(struct sysinfo *s)
+static int ddr2_save_dimminfo(u8 dimm_idx, u8 *raw_spd,
+ struct abs_timings *saved_timings, struct sysinfo *s)
+{
+ struct dimm_attr_st decoded_dimm;
+ int i;
+
+ if (spd_decode_ddr2(&decoded_dimm, raw_spd) != SPD_STATUS_OK) {
+ printk(BIOS_DEBUG, "Problems decoding SPD\n");
+ return CB_ERR;
+ }
+
+ if (IS_ENABLED(CONFIG_DEBUG_RAM_SETUP))
+ dram_print_spd_ddr2(&decoded_dimm);
+
+ if (!(decoded_dimm.width & (0x08 | 0x10))) {
+
+ printk(BIOS_ERR,
+ "DIMM%d Unsupported width: x%d. Disabling dimm\n",
+ dimm_idx, s->dimms[dimm_idx].width);
+ return CB_ERR;
+ }
+ s->dimms[dimm_idx].width = (decoded_dimm.width >> 3) - 1;
+ /*
+ * This boils down to:
+ * "Except for the x16 configuration, all DDR2 devices have a
+ * 1KB page size. For the x16 configuration, the page size is 2KB
+ * for all densities except the 256Mb device, which has a 1KB page
+ * size." Micron, 'TN-47-16 Designing for High-Density DDR2 Memory'
+ */
+ s->dimms[dimm_idx].page_size = s->dimms[dimm_idx].width *
+ (1 << decoded_dimm.col_bits);
+
+ switch (decoded_dimm.banks) {
+ case 4:
+ s->dimms[dimm_idx].n_banks = N_BANKS_4;
+ break;
+ case 8:
+ s->dimms[dimm_idx].n_banks = N_BANKS_8;
+ break;
+ default:
+ printk(BIOS_ERR,
+ "DIMM%d Unsupported #banks: x%d. Disabling dimm\n",
+ dimm_idx, decoded_dimm.banks);
+ return CB_ERR;
+ }
+
+ s->dimms[dimm_idx].ranks = decoded_dimm.ranks;
+ s->dimms[dimm_idx].rows = decoded_dimm.row_bits;
+ s->dimms[dimm_idx].cols = decoded_dimm.col_bits;
+
+ saved_timings->cas_supported &= decoded_dimm.cas_supported;
+
+ saved_timings->min_tRAS =
+ MAX(saved_timings->min_tRAS, decoded_dimm.tRAS);
+ saved_timings->min_tRP =
+ MAX(saved_timings->min_tRP, decoded_dimm.tRP);
+ saved_timings->min_tRCD =
+ MAX(saved_timings->min_tRCD, decoded_dimm.tRCD);
+ saved_timings->min_tWR =
+ MAX(saved_timings->min_tWR, decoded_dimm.tWR);
+ saved_timings->min_tRFC =
+ MAX(saved_timings->min_tRFC, decoded_dimm.tRFC);
+ saved_timings->min_tWTR =
+ MAX(saved_timings->min_tWTR, decoded_dimm.tWTR);
+ saved_timings->min_tRRD =
+ MAX(saved_timings->min_tRRD, decoded_dimm.tRRD);
+ saved_timings->min_tRTP =
+ MAX(saved_timings->min_tRTP, decoded_dimm.tRTP);
+ for (i = 0; i < 8; i++) {
+ if (!(saved_timings->cas_supported & (1 << i)))
+ saved_timings->min_tCLK_cas[i] = 0;
+ else
+ saved_timings->min_tCLK_cas[i] =
+ MAX(saved_timings->min_tCLK_cas[i],
+ decoded_dimm.cycle_time[i]);
+ }
+ return CB_SUCCESS;
+}
+
+static void select_discrete_timings(struct sysinfo *s,
+ const struct abs_timings *timings)
+{
+ s->selected_timings.tRAS = DIV_ROUND_UP(timings->min_tRAS,
+ s->selected_timings.tclk);
+ s->selected_timings.tRP = DIV_ROUND_UP(timings->min_tRP,
+ s->selected_timings.tclk);
+ s->selected_timings.tRCD = DIV_ROUND_UP(timings->min_tRCD,
+ s->selected_timings.tclk);
+ s->selected_timings.tWR = DIV_ROUND_UP(timings->min_tWR,
+ s->selected_timings.tclk);
+ s->selected_timings.tRFC = DIV_ROUND_UP(timings->min_tRFC,
+ s->selected_timings.tclk);
+ s->selected_timings.tWTR = DIV_ROUND_UP(timings->min_tWTR,
+ s->selected_timings.tclk);
+ s->selected_timings.tRRD = DIV_ROUND_UP(timings->min_tRRD,
+ s->selected_timings.tclk);
+ s->selected_timings.tRTP = DIV_ROUND_UP(timings->min_tRTP,
+ s->selected_timings.tclk);
+}
+static void print_selected_timings(struct sysinfo *s)
+{
+ printk(BIOS_DEBUG, "Selected timings:\n");
+ printk(BIOS_DEBUG, "\tFSB: %dMHz\n",
+ fsb2mhz(s->selected_timings.fsb_clk));
+ printk(BIOS_DEBUG, "\tDDR: %dMHz\n",
+ ddr2mhz(s->selected_timings.mem_clk));
+
+ printk(BIOS_DEBUG, "\tCAS: %d\n", s->selected_timings.CAS);
+ printk(BIOS_DEBUG, "\ttRAS: %d\n", s->selected_timings.tRAS);
+ printk(BIOS_DEBUG, "\ttRP: %d\n", s->selected_timings.tRP);
+ printk(BIOS_DEBUG, "\ttRCD: %d\n", s->selected_timings.tRCD);
+ printk(BIOS_DEBUG, "\ttWR: %d\n", s->selected_timings.tWR);
+ printk(BIOS_DEBUG, "\ttRFC: %d\n", s->selected_timings.tRFC);
+ printk(BIOS_DEBUG, "\ttWTR: %d\n", s->selected_timings.tWTR);
+ printk(BIOS_DEBUG, "\ttRRD: %d\n", s->selected_timings.tRRD);
+ printk(BIOS_DEBUG, "\ttRTP: %d\n", s->selected_timings.tRTP);
+}
+
+static void find_fsb_speed(struct sysinfo *s)
{
- u8 i;
- u8 commoncas = 0;
- u8 currcas;
- u8 currfreq;
- u8 maxfreq;
- u8 freq = 0;
-
- // 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
switch (MCHBAR32(0xc00) & 0x7) {
case 0x0:
s->max_fsb = FSB_CLOCK_1066MHz;
@@ -225,88 +251,98 @@ static void sdram_detect_ram_speed(struct sysinfo *s)
printk(BIOS_WARNING, "Can't detect FSB, setting 800MHz\n");
break;
}
+ s->selected_timings.fsb_clk = s->max_fsb;
+}
- // 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;
+static void decode_spd_select_timings(struct sysinfo *s)
+{
+ unsigned int device;
+ u8 dram_type_mask = (1 << DDR2) | (1 << DDR3);
+ u8 dimm_mask = 0;
+ u8 raw_spd[256];
+ int i, j;
+ struct abs_timings saved_timings;
+ memset(&saved_timings, 0, sizeof(saved_timings));
+ saved_timings.cas_supported = UINT32_MAX;
- FOR_EACH_POPULATED_DIMM(s->dimms, i) {
- commoncas &= s->dimms[i].cas_latencies;
+ FOR_EACH_DIMM(i) {
+ s->dimms[i].card_type = RAW_CARD_POPULATED;
+ device = s->spd_map[i];
+ if (!device) {
+ s->dimms[i].card_type = RAW_CARD_UNPOPULATED;
+ continue;
}
- if (commoncas == 0)
- die("No common CAS among dimms\n");
-
- // 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) {
- 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
- found = 0;
- }
- }
- if (found)
- break;
- }
+ switch (spd_read_byte(s->spd_map[i], SPD_MEMORY_TYPE)) {
+ case DDR2SPD:
+ dram_type_mask &= 1 << DDR2;
+ s->spd_type = DDR2;
+ break;
+ case DDR3SPD:
+ dram_type_mask &= 1 << DDR3;
+ s->spd_type = DDR3;
+ break;
+ default:
+ s->dimms[i].card_type = RAW_CARD_UNPOPULATED;
+ continue;
}
+ if (!dram_type_mask)
+ die("Mixing up dimm types is not supported!\n");
- 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;
- }
+ printk(BIOS_DEBUG, "Decoding dimm %d\n", i);
+ if (s->spd_type == DDR2){
+ printk(BIOS_DEBUG,
+ "Reading SPD using i2c block operation.\n");
+ if (i2c_block_read(device, 0, 64, raw_spd) != 64) {
+ printk(BIOS_DEBUG, "i2c block operation failed,"
+ " trying smbus byte operation.\n");
+ for (j = 0; j < 64; j++)
+ raw_spd[j] = spd_read_byte(device, j);
+ }
+ if (ddr2_save_dimminfo(i, raw_spd, &saved_timings, s)) {
+ printk(BIOS_WARNING,
+ "Encountered problems with SPD, "
+ "skipping this DIMM.\n");
+ s->dimms[i].card_type = RAW_CARD_UNPOPULATED;
+ continue;
}
+ } else { /* DDR3: not implemented so don't decode */
+ die("DDR3 support is not implemented\n");
}
+ dimm_mask |= (1 << i);
+ }
+ if (!dimm_mask)
+ die("No memory installed.\n");
- 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;
- maxfreq >>= 3;
- freq = MEM_CLOCK_1333MHz;
- if (maxfreq)
- freq = maxfreq + 2;
- if (freq > MEM_CLOCK_1333MHz)
- freq = MEM_CLOCK_1333MHz;
-
- // Limit DDR speed to FSB speed
- switch (s->max_fsb) {
- case FSB_CLOCK_800MHz:
- if (freq > MEM_CLOCK_800MHz)
- freq = MEM_CLOCK_800MHz;
- break;
- case FSB_CLOCK_1066MHz:
- if (freq > MEM_CLOCK_1066MHz)
- freq = MEM_CLOCK_1066MHz;
- break;
- case FSB_CLOCK_1333MHz:
- if (freq > MEM_CLOCK_1333MHz)
- freq = MEM_CLOCK_1333MHz;
- break;
- default:
- die("Invalid FSB\n");
- break;
- }
+ if (s->spd_type == DDR2)
+ select_cas_dramfreq_ddr2(s, &saved_timings);
+ select_discrete_timings(s, &saved_timings);
+}
+
+static void find_dimm_config(struct sysinfo *s)
+{
+ int chan, i;
- // TODO: CAS detection for DDR3
+ FOR_EACH_POPULATED_CHANNEL(s->dimms, chan) {
+ FOR_EACH_POPULATED_DIMM_IN_CHANNEL(s->dimms, chan, i) {
+ int dimm_config;
+ if (s->dimms[i].ranks == 1) {
+ if (s->dimms[i].width == 0) /* x8 */
+ dimm_config = 1;
+ else /* x16 */
+ dimm_config = 3;
+ } else {
+ if (s->dimms[i].width == 0) /* x8 */
+ dimm_config = 2;
+ else
+ die("Dual-rank x16 not supported\n");
+ }
+ s->dimm_config[chan] |=
+ dimm_config << (i % DIMMS_PER_CHANNEL) * 2;
+ }
+ printk(BIOS_DEBUG, " Config[CH%d] : %d\n", chan,
+ s->dimm_config[chan]);
}
+
}
static void checkreset_ddr2(int boot_path)
@@ -367,19 +403,15 @@ void sdram_initialize(int boot_path, const u8 *spd_map)
checkreset_ddr2(s.boot_path);
/* Detect dimms per channel */
- s.dimms_per_ch = 2;
reg8 = pci_read_config8(PCI_DEV(0, 0, 0), 0xe9);
- if (reg8 & 0x10)
- s.dimms_per_ch = 1;
-
- printk(BIOS_DEBUG, "Dimms per channel: %d\n", s.dimms_per_ch);
+ printk(BIOS_DEBUG, "Dimms per channel: %d\n", (reg8 & 0x10) ? 1 : 2);
mchinfo_ddr2(&s);
- sdram_read_spds(&s);
-
- /* Choose Common Frequency */
- sdram_detect_ram_speed(&s);
+ find_fsb_speed(&s);
+ decode_spd_select_timings(&s);
+ print_selected_timings(&s);
+ find_dimm_config(&s);
switch (s.spd_type) {
case DDR2: