From 4b513a618d7d7edeb5ed9c5073e838b46e2f4676 Mon Sep 17 00:00:00 2001 From: Damien Zammit Date: Thu, 20 Aug 2015 00:37:05 +1000 Subject: northbridge/intel/x4x: Native raminit Passes memtest86+ with either one or two sticks of 2GB ram but memory map needs a hole at 0xa0000000 to 0xc0000000 Change-Id: Ib34d862cb48b49c054a505fffcba1c17aeb39436 Signed-off-by: Damien Zammit Reviewed-on: https://review.coreboot.org/11307 Tested-by: build bot (Jenkins) Reviewed-by: Martin Roth --- src/northbridge/intel/x4x/raminit.c | 500 ++++++++++++++++++++++++++++++++++++ 1 file changed, 500 insertions(+) create mode 100644 src/northbridge/intel/x4x/raminit.c (limited to 'src/northbridge/intel/x4x/raminit.c') diff --git a/src/northbridge/intel/x4x/raminit.c b/src/northbridge/intel/x4x/raminit.c new file mode 100644 index 0000000000..613011fa98 --- /dev/null +++ b/src/northbridge/intel/x4x/raminit.c @@ -0,0 +1,500 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2015 Damien Zammit + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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) +{ + u8 i, j, chan; + int status = 0; + FOR_EACH_DIMM(i) { + if (s->spd_map[i] == 0) { + /* Non-existant SPD address */ + s->dimms[i].card_type = 0; + 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 = 0; + break; + } + s->dimms[i].spd_data[j] = (u8) status; + if (j == 62) + s->dimms[i].card_type = ((u8) status) & 0x1f; + } + if (status >= 0) { + 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"); + } + break; + } + } + 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 = 0x78; + s->dimms[i].cas_latencies &= s->dimms[i].spd_data[18]; + if (s->dimms[i].cas_latencies == 0) + s->dimms[i].cas_latencies = 7; + 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) { + if (s->dimms[chan>>1].sides == 0) { + // NC + if (s->dimms[(chan>>1) + 1].sides == 0) { + // NC/NC + s->dimm_config[chan] = 0; + } else if (s->dimms[(chan>>1) + 1].sides == 1) { + // NC/SS + if (s->dimms[(chan>>1) + 1].width == 0) { + // NC/8SS + s->dimm_config[chan] = 4; + } else { + // NC/16SS + s->dimm_config[chan] = 12; + } + } else { + // NC/DS + if (s->dimms[(chan>>1) + 1].width == 0) { + // NC/8DS + s->dimm_config[chan] = 8; + } else { + // NOT SUPPORTED + die("16DS Not supported\n"); + } + } + } else if (s->dimms[chan>>1].sides == 1) { + // SS + if (s->dimms[(chan>>1) + 1].sides == 0) { + // SS/NC + if (s->dimms[chan>>1].width == 0) { + // 8SS/NC + s->dimm_config[chan] = 1; + } else { + // 16SS/NC + s->dimm_config[chan] = 3; + } + } else if (s->dimms[(chan>>1) + 1].sides == 1) { + // SS/SS + if (s->dimms[chan>>1].width == 0) { + if (s->dimms[(chan>>1) + 1].width == 0) { + // 8SS/8SS + s->dimm_config[chan] = 5; + } else { + // 8SS/16SS + s->dimm_config[chan] = 13; + } + } else { + if (s->dimms[(chan>>1) + 1].width == 0) { + // 16SS/8SS + s->dimm_config[chan] = 7; + } else { + // 16SS/16SS + s->dimm_config[chan] = 15; + } + } + } else { + // SS/DS + if (s->dimms[chan>>1].width == 0) { + if (s->dimms[(chan>>1) + 1].width == 0) { + // 8SS/8DS + s->dimm_config[chan] = 9; + } else { + die("16DS not supported\n"); + } + } else { + if (s->dimms[(chan>>1) + 1].width == 0) { + // 16SS/8DS + s->dimm_config[chan] = 11; + } else { + die("16DS not supported\n"); + } + } + } + } else { + // DS + if (s->dimms[(chan>>1) + 1].sides == 0) { + // DS/NC + if (s->dimms[chan>>1].width == 0) { + // 8DS/NC + s->dimm_config[chan] = 2; + } else { + die("16DS not supported\n"); + } + } else if (s->dimms[(chan>>1) + 1].sides == 1) { + // DS/SS + if (s->dimms[chan>>1].width == 0) { + if (s->dimms[(chan>>1) + 1].width == 0) { + // 8DS/8SS + s->dimm_config[chan] = 6; + } else { + // 8DS/16SS + s->dimm_config[chan] = 14; + } + } else { + die("16DS not supported\n"); + } + } else { + // DS/DS + if (s->dimms[chan>>1].width == 0 && s->dimms[(chan>>1)+1].width == 0) { + // 8DS/8DS + s->dimm_config[chan] = 10; + } + } + } + printk(BIOS_DEBUG, " Config[CH%d] : %d\n", chan, s->dimm_config[chan]); + } +} + +static u8 lsbpos(u8 val) //Forward +{ + u8 i; + for (i = 0; (i < 8) && ((val & (1 << i)) == 0); i++); + return i; +} + +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; + s->cores = ((eax >> 26) & 0x3f) + 1; + printk(BIOS_WARNING, "%d CPU cores\n", s->cores); + + u32 capid = pci_read_config16(PCI_DEV(0,0,0), 0xe8); + if (!(capid & (1<<(79-64)))) { + printk(BIOS_WARNING, "iTPM enabled\n"); + } + + capid = pci_read_config32(PCI_DEV(0, 0, 0), 0xe4); + if (!(capid & (1<<(57-32)))) { + printk(BIOS_WARNING, "ME enabled\n"); + } + + if (!(capid & (1<<(56-32)))) { + printk(BIOS_WARNING, "AMT enabled\n"); + } + + s->max_ddr2_mhz = (capid & (1<<(53-32)))?667:800; + printk(BIOS_WARNING, "Capable of DDR2 of %d MHz or lower\n", s->max_ddr2_mhz); + + if (!(capid & (1<<(48-32)))) { + printk(BIOS_WARNING, "VT-d enabled\n"); + } +} + +static void sdram_detect_ram_speed(struct sysinfo *s) +{ + u8 i; + u8 commoncas = 0; + u8 cas; + u8 lowcas; + u8 highcas; + u8 maxfreq; + u8 freq = 0; + + // Find max FSB speed + switch (MCHBAR32(0xc00) & 0x7) { + case 0x0: + s->max_fsb = FSB_CLOCK_1066MHz; + break; + case 0x2: + s->max_fsb = FSB_CLOCK_800MHz; + break; + case 0x4: + s->max_fsb = FSB_CLOCK_1333MHz; + break; + default: + s->max_fsb = FSB_CLOCK_800MHz; + printk(BIOS_WARNING, "Can't detect FSB, setting 800MHz\n"); + break; + } + + // Find RAM speed + maxfreq = (u8) ((pci_read_config16(PCI_DEV(0,0,0), 0xea) >> 4) & 0x3f); + if (s->spd_type == DDR2) { + + // Limit frequency for MCH + maxfreq &= 0x7; + freq = MEM_CLOCK_800MHz; + if (maxfreq) { + freq = maxfreq; + } + if (freq > MEM_CLOCK_800MHz) { + freq = MEM_CLOCK_800MHz; + } + + // Detect a common CAS latency + commoncas = 0xff; + FOR_EACH_POPULATED_DIMM(s->dimms, i) { + commoncas &= s->dimms[i].spd_data[18]; + } + if (commoncas == 0) { + die("No common CAS among dimms\n"); + } + + // Start with fastest common CAS + cas = 0; + highcas = msbpos(commoncas); + lowcas = lsbpos(commoncas); + + while (cas == 0 && highcas >= lowcas) { + FOR_EACH_POPULATED_DIMM(s->dimms, i) { + switch (freq) { + case MEM_CLOCK_800MHz: + if ((s->dimms[i].spd_data[9] > 0x25) || + (s->dimms[i].spd_data[10] > 0x40)) { + // CAS too fast, lower it + highcas--; + break; + } else { + cas = highcas; + } + break; + case MEM_CLOCK_667MHz: + default: + if ((s->dimms[i].spd_data[9] > 0x30) || + (s->dimms[i].spd_data[10] > 0x45)) { + // CAS too fast, lower it + highcas--; + break; + } else { + cas = highcas; + } + break; + } + } + } + if (highcas < lowcas) { + // Timings not supported by MCH, lower the frequency + freq--; + cas = 0; + highcas = msbpos(commoncas); + lowcas = lsbpos(commoncas); + while (cas == 0 && highcas >= lowcas) { + FOR_EACH_POPULATED_DIMM(s->dimms, i) { + switch (freq) { + case MEM_CLOCK_800MHz: + if ((s->dimms[i].spd_data[23] > 0x25) || + (s->dimms[i].spd_data[24] > 0x40)) { + // CAS too fast, lower it + highcas--; + break; + } else { + cas = highcas; + } + break; + case MEM_CLOCK_667MHz: + default: + if ((s->dimms[i].spd_data[23] > 0x30) || + (s->dimms[i].spd_data[24] > 0x45)) { + // CAS too fast, lower it + highcas--; + break; + } else { + cas = highcas; + } + break; + } + } + } + } + s->selected_timings.mem_clk = freq; + s->selected_timings.CAS = cas; + + } else { // DDR3 + // Limit frequency for MCH + 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; + } + + // TODO: CAS detection for DDR3 + } +} + +/** + * @param boot_path: 0 = normal, 1 = reset, 2 = resume from s3 + */ +void sdram_initialize(int boot_path, const u8 *spd_map) +{ + struct sysinfo s; + u8 reg8; + + printk(BIOS_DEBUG, "Setting up RAM controller.\n"); + + pci_write_config8(PCI_DEV(0,0,0), 0xdf, 0xff); + + memset(&s, 0, sizeof(struct sysinfo)); + + s.boot_path = boot_path; + s.spd_map[0] = spd_map[0]; + s.spd_map[1] = spd_map[1]; + s.spd_map[2] = spd_map[2]; + s.spd_map[3] = spd_map[3]; + + /* 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); + + mchinfo_ddr2(&s); + + sdram_read_spds(&s); + + /* Choose Common Frequency */ + sdram_detect_ram_speed(&s); + + switch (s.spd_type) { + case DDR2: + raminit_ddr2(&s); + break; + case DDR3: + // FIXME Add: raminit_ddr3(&s); + break; + default: + die("Unknown DDR type\n"); + break; + } + + reg8 = pci_read_config8(PCI_DEV(0, 0x1f, 0), 0xa2); + pci_write_config8(PCI_DEV(0, 0x1f, 0), 0xa2, reg8 & ~0x80); + + reg8 = pci_read_config8(PCI_DEV(0,0,0), 0xf4); + pci_write_config8(PCI_DEV(0,0,0), 0xf4, reg8 | 1); + printk(BIOS_DEBUG, "RAM initialization finished.\n"); +} -- cgit v1.2.3