Arthur Heymans has uploaded a new change for review. ( https://review.coreboot.org/19869 )
Change subject: nb/x4x/raminit: Decode ddr3 dimms ......................................................................
nb/x4x/raminit: Decode ddr3 dimms
The raw_spd array needs to be increased since the ddr3 variant of spd_decode assumes an array of that size.
Change-Id: I8dba19ca1e6e6b0a03b56c8de9633f9c1a2eb7d7 Signed-off-by: Arthur Heymans arthur@aheymans.xyz --- M src/northbridge/intel/x4x/Makefile.inc M src/northbridge/intel/x4x/raminit.c M src/northbridge/intel/x4x/raminit_ddr2.c M src/northbridge/intel/x4x/spd_ddr2_decode.c A src/northbridge/intel/x4x/spd_ddr3_decode.c M src/northbridge/intel/x4x/x4x.h 6 files changed, 197 insertions(+), 8 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/69/19869/1
diff --git a/src/northbridge/intel/x4x/Makefile.inc b/src/northbridge/intel/x4x/Makefile.inc index b433810..86ecf7b 100644 --- a/src/northbridge/intel/x4x/Makefile.inc +++ b/src/northbridge/intel/x4x/Makefile.inc @@ -21,6 +21,7 @@ romstage-y += raminit_ddr2.c romstage-y += ram_calc.c romstage-y += spd_ddr2_decode.c +romstage-y += spd_ddr3_decode.c
ramstage-y += acpi.c ramstage-y += ram_calc.c diff --git a/src/northbridge/intel/x4x/raminit.c b/src/northbridge/intel/x4x/raminit.c index 7e69b4c..b2bc280 100644 --- a/src/northbridge/intel/x4x/raminit.c +++ b/src/northbridge/intel/x4x/raminit.c @@ -116,7 +116,7 @@ unsigned int device; u8 dram_type_mask = (1 << DDR2) | (1 << DDR3); u8 dimm_mask = 0; - u8 raw_spd[128]; + u8 raw_spd[256]; int i; struct abs_timings saved_timings = { }; saved_timings.cas_supported = (u32)-1; @@ -151,7 +151,12 @@ continue; } } else { /* DDR3: not implemented so don't decode */ - die("DDR3 support is not implemented\n"); + i2c_block_read(device, 0, 128, raw_spd); + if (ddr3_save_dimminfo(i, raw_spd, &saved_timings, s)) { + /* something in decoded SPD was unsupported */ + s->dimms[i].card_type = RAW_CARD_UNPOPULATED; + continue; + } } dimm_mask = (1 << i); } @@ -159,6 +164,8 @@ die("No memory installed.\n"); if (s->spd_type == DDR2) select_cas_dramfreq_ddr2(s, &saved_timings); + else + select_cas_dramfreq_ddr3(s, &saved_timings); select_discrete_timings(s, &saved_timings); }
diff --git a/src/northbridge/intel/x4x/raminit_ddr2.c b/src/northbridge/intel/x4x/raminit_ddr2.c index 8984fad..e663564 100644 --- a/src/northbridge/intel/x4x/raminit_ddr2.c +++ b/src/northbridge/intel/x4x/raminit_ddr2.c @@ -1469,7 +1469,6 @@ };
u8 drbtab[10] = {0x04, 0x02, 0x08, 0x04, 0x08, 0x04, 0x10, 0x08, 0x20, 0x10}; - u8 tab_width;
// DRA rankpop0 = 0; @@ -1480,9 +1479,8 @@ i = ch << 1; else i = (ch << 1) + 1; - tab_width = (s->dimms[i].width >> 3) - 1; /* 16->1, 8->0 */ dra = dratab[s->dimms[i].n_banks] - [tab_width] + [s->dimms[i].width] [s->dimms[i].cols-9] [s->dimms[i].rows-12]; if (s->dimms[i].n_banks == 1) diff --git a/src/northbridge/intel/x4x/spd_ddr2_decode.c b/src/northbridge/intel/x4x/spd_ddr2_decode.c index 938aab7..f8f4403 100644 --- a/src/northbridge/intel/x4x/spd_ddr2_decode.c +++ b/src/northbridge/intel/x4x/spd_ddr2_decode.c @@ -82,12 +82,14 @@ * Used to be content of spd byte 62 which does not make * that much sense. */ - s->dimms[dimm_idx].width = decoded_dimm.width; - if (!(s->dimms[dimm_idx].width & (0x8 | 0x10))) { + + if (!(decoded_dimm.width & (0x8 | 0x10))) { printk(BIOS_ERR, "DIMM%d Unsupported width: x%d. Disabling dimm\n", dimm_idx, s->dimms[dimm_idx].width); return 1; } + + s->dimms[dimm_idx].width = (decoded_dimm.width >> 3) - 1; /* * This boils down to: * "Except for the x16 configuration, all DDR2 devices have a @@ -96,7 +98,7 @@ * page size." * Micron, 'TN-47-16 Designing for High-Density DDR2 Memory' */ - s->dimms[dimm_idx].page_size = s->dimms[dimm_idx].width * + s->dimms[dimm_idx].page_size = decoded_dimm.width * (1 << decoded_dimm.col_bits);
switch (decoded_dimm.banks) { diff --git a/src/northbridge/intel/x4x/spd_ddr3_decode.c b/src/northbridge/intel/x4x/spd_ddr3_decode.c new file mode 100644 index 0000000..fd69fd4 --- /dev/null +++ b/src/northbridge/intel/x4x/spd_ddr3_decode.c @@ -0,0 +1,175 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2015 Damien Zammit damien@zamaudio.com + * Copyright (C) 2017 Arthur Heymans arthur@aheymans.xyz + * + * 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 <arch/io.h> +#include <console/console.h> +#include <device/dram/ddr3.h> +#include "x4x.h" + +static void normalize_tCLK(u32 *tCLK) +{ + if (*tCLK <= TCK_666MHZ) + *tCLK = TCK_666MHZ; + else if (*tCLK <= TCK_533MHZ) + *tCLK = TCK_533MHZ; + else if (*tCLK <= TCK_400MHZ) + *tCLK = TCK_400MHZ; + else + *tCLK = 0; +} + +static void increase_tCLK(u32 *tCLK) +{ + ++*tCLK; + normalize_tCLK(tCLK); +} + +void select_cas_dramfreq_ddr3(struct sysinfo *s, + struct abs_timings *saved_timings) +{ + /* various constraints must be fulfilled: + CAS * tCK < 20ns == 160MTB + tCK_max >= tCK >= tCK_min + CAS >= roundup(tAA_min/tCK) + CAS supported + AND BTW: Clock(MT) = 2000 / tCK(ns) - intel uses MTs but calls them MHz + */ + + u32 min_tCLK; + u8 try_CAS; + u16 capid = (pci_read_config16(PCI_DEV(0, 0, 0), 0xea) >> 4) & 0x3f; + + switch (s->max_fsb) { + default: + case FSB_CLOCK_800MHz: + min_tCLK = TCK_400MHZ; + break; + case FSB_CLOCK_1066MHz: + min_tCLK = TCK_533MHZ; + break; + case FSB_CLOCK_1333MHz: + min_tCLK = TCK_666MHZ; + break; + } + + switch (capid >> 3) { + default: /* Should not happen */ + min_tCLK = TCK_400MHZ; + break; + case 1: + min_tCLK = MAX(min_tCLK, TCK_400MHZ); + break; + case 2: + min_tCLK = MAX(min_tCLK, TCK_533MHZ); + break; + case 3: /* Only on P45 */ + min_tCLK = MAX(min_tCLK, TCK_666MHZ); + break; + } + + min_tCLK = MAX(min_tCLK, saved_timings->min_tclk); + normalize_tCLK(&min_tCLK); + if (min_tCLK == 0) { + printk(BIOS_ERR, "DRAM frequency is under lowest supported " + "frequency (400 MHz). Increasing to 400 MHz as last resort"); + min_tCLK = TCK_400MHZ; + } + + while (1) { + if (min_tCLK == 0) + die("Couldn't find compatible clock / CAS settings.\n"); + try_CAS = DIV_ROUND_UP(saved_timings->min_tAA, min_tCLK); + printk(BIOS_SPEW, "Trying CAS %u, tCK %u.\n", try_CAS, min_tCLK); + for (; try_CAS <= DDR3_MAX_CAS; ++try_CAS) { + if ((saved_timings->cas_supported << 4) & (1 << try_CAS)) + break; + } + if ((try_CAS <= DDR3_MAX_CAS) && (try_CAS * min_tCLK < 20 * 256)) { + /* Found good CAS. */ + printk(BIOS_SPEW, "Found compatible tCLK / CAS pair: %u / %u.\n", + min_tCLK, try_CAS); + break; + } + increase_tCLK(&min_tCLK); + } + s->selected_timings.tclk = min_tCLK; + s->selected_timings.CAS = try_CAS; + + switch (s->selected_timings.tclk) { + case TCK_400MHZ: + s->selected_timings.mem_clk = MEM_CLOCK_800MHz; break; + case TCK_533MHZ: + s->selected_timings.mem_clk = MEM_CLOCK_1066MHz; break; + case TCK_666MHZ: + s->selected_timings.mem_clk = MEM_CLOCK_1333MHz; break; + } +} + +int ddr3_save_dimminfo(u8 dimm_idx, u8 *raw_spd, + struct abs_timings *saved_timings, struct sysinfo *s) +{ + struct dimm_attr_st decoded_dimm; + + if (spd_decode_ddr3(&decoded_dimm, raw_spd) != SPD_STATUS_OK) + return 1; + + if (IS_ENABLED(CONFIG_DEBUG_RAM_SETUP)) + dram_print_spd_ddr3(&decoded_dimm); + + if (!(decoded_dimm.width & (0x8 | 0x10))) { + printk(BIOS_ERR, "DIMM%d Unsupported width: x%d. Disabling dimm\n", + dimm_idx, s->dimms[dimm_idx].width); + return 1; + } + 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); + + s->dimms[dimm_idx].n_banks = 1; /* Always 8 banks on ddr3?? */ + + 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->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); + saved_timings->min_tAA = + MAX(saved_timings->min_tAA, decoded_dimm.tAA); + saved_timings->cas_supported &= decoded_dimm.cas_supported; + return 0; +} diff --git a/src/northbridge/intel/x4x/x4x.h b/src/northbridge/intel/x4x/x4x.h index fc7fbfe..43e37ef 100644 --- a/src/northbridge/intel/x4x/x4x.h +++ b/src/northbridge/intel/x4x/x4x.h @@ -323,6 +323,7 @@
struct abs_timings { u32 min_tclk; + u32 min_tAA; u32 min_tRAS; u32 min_tRP; u32 min_tRCD; @@ -349,6 +350,11 @@ struct abs_timings *saved_timings); int ddr2_save_dimminfo(u8 dimm_idx, u8 *raw_spd, struct abs_timings *saved_timings, struct sysinfo *s); +void select_cas_dramfreq_ddr3(struct sysinfo *s, + struct abs_timings *saved_timings); +int ddr3_save_dimminfo(u8 dimm_idx, u8 *raw_spd, + struct abs_timings *saved_timings, struct sysinfo *s); +
struct acpi_rsdp; #ifndef __SIMPLE_DEVICE__