nb/intel/gm45: Wedge DDR2 SPD support in
Add initial support for DDR2. This also changes GM45 raminit to internally work in units of 1/256 ns for both DDR2 and DDR3 instead of the 1/8 ns MTB assumed for DDR3, which simplifies the handling of time values. DDR3 time values are thus scaled by a factor of 32 accordingly. TODO: - DDR2 JEDEC init - Memory IO init - Register programming TEST: DDR2 systems boot (with the rest of the patch train) - Tested on a Dell Latitude E6400 - Tested on a Compal JHL90 TEST: Ensure DDR3 systems still boot - Tested on a Thinkpad X200 Change-Id: I265938d58c30264fd5d4f7b89da7b689058b8cf8 Signed-off-by: Nico Huber <nico.h@gmx.de> Reviewed-on: https://review.coreboot.org/c/coreboot/+/34826 Reviewed-by: Angel Pons <th3fanbus@gmail.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
parent
df4fa45ce4
commit
0c314f9c7e
|
@ -111,7 +111,7 @@ typedef struct {
|
||||||
int txt_enabled;
|
int txt_enabled;
|
||||||
int cores;
|
int cores;
|
||||||
gmch_gfx_t gfx_type;
|
gmch_gfx_t gfx_type;
|
||||||
int max_ddr2_mhz;
|
int max_ddr2_mt;
|
||||||
int max_ddr3_mt;
|
int max_ddr3_mt;
|
||||||
fsb_clock_t max_fsb;
|
fsb_clock_t max_fsb;
|
||||||
int max_fsb_mhz;
|
int max_fsb_mhz;
|
||||||
|
|
|
@ -57,8 +57,8 @@ void get_gmch_info(sysinfo_t *sysinfo)
|
||||||
printk(BIOS_SPEW, "AMT enabled\n");
|
printk(BIOS_SPEW, "AMT enabled\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
sysinfo->max_ddr2_mhz = (capid & (1<<(53-32)))?667:800;
|
sysinfo->max_ddr2_mt = (capid & (1<<(53-32)))?667:800;
|
||||||
printk(BIOS_SPEW, "capable of DDR2 of %d MHz or lower\n", sysinfo->max_ddr2_mhz);
|
printk(BIOS_SPEW, "capable of DDR2 of %d MHz or lower\n", sysinfo->max_ddr2_mt);
|
||||||
|
|
||||||
if (!(capid & (1<<(48-32)))) {
|
if (!(capid & (1<<(48-32)))) {
|
||||||
printk(BIOS_SPEW, "VT-d enabled\n");
|
printk(BIOS_SPEW, "VT-d enabled\n");
|
||||||
|
@ -223,6 +223,58 @@ static int test_dimm(sysinfo_t *const sysinfo,
|
||||||
return (smbus_read_byte(sysinfo->spd_map[dimm], addr) & bitmask) == expected;
|
return (smbus_read_byte(sysinfo->spd_map[dimm], addr) & bitmask) == expected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This function dies if dimm is unsuitable for the chipset. */
|
||||||
|
static void verify_ddr2_dimm(sysinfo_t *const sysinfo, int dimm)
|
||||||
|
{
|
||||||
|
if (!test_dimm(sysinfo, dimm, 20, 0x04, 0x04))
|
||||||
|
die("Chipset only supports SO-DIMM\n");
|
||||||
|
|
||||||
|
if (!test_dimm(sysinfo, dimm, 6, 0xff, 0x40) ||
|
||||||
|
!test_dimm(sysinfo, dimm, 11, 0xff, 0x00))
|
||||||
|
die("Chipset doesn't support ECC RAM\n");
|
||||||
|
|
||||||
|
if (!test_dimm(sysinfo, dimm, 5, 0x07, 0) &&
|
||||||
|
!test_dimm(sysinfo, dimm, 5, 0x07, 1))
|
||||||
|
die("Chipset wants single or dual ranked DIMMs\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generally supports:
|
||||||
|
* x8/x16
|
||||||
|
* 4 or 8 banks
|
||||||
|
* 10 column address bits
|
||||||
|
* 13, 14 or 15 (x8 only) row address bits
|
||||||
|
*
|
||||||
|
* FIXME: There seems to be an exception for 256Gb x16 chips. Not
|
||||||
|
* covered by the numbers above (9 column address bits?).
|
||||||
|
*/
|
||||||
|
if (!test_dimm(sysinfo, dimm, 13, 0xff, 8) &&
|
||||||
|
!test_dimm(sysinfo, dimm, 13, 0xff, 16))
|
||||||
|
die("Chipset requires x8 or x16 width\n");
|
||||||
|
|
||||||
|
if (!test_dimm(sysinfo, dimm, 17, 0xff, 4) &&
|
||||||
|
!test_dimm(sysinfo, dimm, 17, 0xff, 8))
|
||||||
|
die("Chipset requires 4 or 8 banks\n");
|
||||||
|
|
||||||
|
if (!test_dimm(sysinfo, dimm, 4, 0xff, 10))
|
||||||
|
die("Chipset requires 10 column address bits\n");
|
||||||
|
|
||||||
|
if (!test_dimm(sysinfo, dimm, 3, 0xff, 13) &&
|
||||||
|
!test_dimm(sysinfo, dimm, 3, 0xff, 14) &&
|
||||||
|
!(test_dimm(sysinfo, dimm, 3, 0xff, 15) &&
|
||||||
|
test_dimm(sysinfo, dimm, 13, 0xff, 8)))
|
||||||
|
die("Chipset requires 13, 14 or 15 (with x8) row address bits");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For every detected DIMM, test if it's suitable for the chipset. */
|
||||||
|
static void verify_ddr2(sysinfo_t *const sysinfo, int mask)
|
||||||
|
{
|
||||||
|
int cur;
|
||||||
|
for (cur = 0; mask; mask >>= 1, ++cur) {
|
||||||
|
if (mask & 1)
|
||||||
|
verify_ddr2_dimm(sysinfo, cur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* This function dies if dimm is unsuitable for the chipset. */
|
/* This function dies if dimm is unsuitable for the chipset. */
|
||||||
static void verify_ddr3_dimm(sysinfo_t *const sysinfo, int dimm)
|
static void verify_ddr3_dimm(sysinfo_t *const sysinfo, int dimm)
|
||||||
{
|
{
|
||||||
|
@ -281,7 +333,7 @@ static void verify_ddr3(sysinfo_t *const sysinfo, int mask)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int dimm_mask;
|
int dimm_mask;
|
||||||
struct {
|
struct spd_dimminfo {
|
||||||
unsigned int rows;
|
unsigned int rows;
|
||||||
unsigned int cols;
|
unsigned int cols;
|
||||||
unsigned int chip_capacity;
|
unsigned int chip_capacity;
|
||||||
|
@ -299,6 +351,105 @@ typedef struct {
|
||||||
unsigned int raw_card;
|
unsigned int raw_card;
|
||||||
} channel[2];
|
} channel[2];
|
||||||
} spdinfo_t;
|
} spdinfo_t;
|
||||||
|
/**
|
||||||
|
* \brief Decode SPD tck cycle time
|
||||||
|
*
|
||||||
|
* Decodes a raw SPD data from a DDR2 DIMM.
|
||||||
|
* Returns cycle time in 1/256th ns.
|
||||||
|
*/
|
||||||
|
static unsigned int spd_decode_tck_time(u8 c)
|
||||||
|
{
|
||||||
|
u8 high, low;
|
||||||
|
|
||||||
|
high = c >> 4;
|
||||||
|
|
||||||
|
switch (c & 0xf) {
|
||||||
|
case 0xa:
|
||||||
|
low = 25;
|
||||||
|
break;
|
||||||
|
case 0xb:
|
||||||
|
low = 33;
|
||||||
|
break;
|
||||||
|
case 0xc:
|
||||||
|
low = 66;
|
||||||
|
break;
|
||||||
|
case 0xd:
|
||||||
|
low = 75;
|
||||||
|
break;
|
||||||
|
case 0xe:
|
||||||
|
case 0xf:
|
||||||
|
die("Invalid tck setting. lower nibble is 0x%x\n", c & 0xf);
|
||||||
|
default:
|
||||||
|
low = (c & 0xf) * 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((high * 100 + low) << 8) / 100;
|
||||||
|
}
|
||||||
|
static void collect_ddr2_dimm(struct spd_dimminfo *const di, const int smb_addr)
|
||||||
|
{
|
||||||
|
static const int tCK_offsets[] = { 9, 23, 25 };
|
||||||
|
|
||||||
|
di->rows = smbus_read_byte(smb_addr, 3);
|
||||||
|
di->cols = smbus_read_byte(smb_addr, 4);
|
||||||
|
di->banks = smbus_read_byte(smb_addr, 17);
|
||||||
|
di->width = smbus_read_byte(smb_addr, 13) / 8; /* in bytes */
|
||||||
|
|
||||||
|
/* 0: 256Mb .. 3: 2Gb */
|
||||||
|
di->chip_capacity =
|
||||||
|
di->rows + di->cols
|
||||||
|
+ (di->width == 1 ? 3 : 4) /* 1B: 2^3 bits, 2B: 2^4 bits */
|
||||||
|
+ (di->banks == 4 ? 2 : 3) /* 4 banks: 2^2, 8 banks: 2^3 */
|
||||||
|
- 28;
|
||||||
|
|
||||||
|
di->page_size = di->width * (1 << di->cols); /* in bytes */
|
||||||
|
|
||||||
|
di->ranks = (smbus_read_byte(smb_addr, 5) & 7) + 1;
|
||||||
|
|
||||||
|
di->cas_latencies = smbus_read_byte(smb_addr, 18);
|
||||||
|
/* assuming tCKmin for the highest CAS is the absolute minimum */
|
||||||
|
di->tCKmin = spd_decode_tck_time(smbus_read_byte(smb_addr, 9));
|
||||||
|
|
||||||
|
/* try to reconstruct tAAmin from available data (I hate DDR2 SPDs) */
|
||||||
|
unsigned int i;
|
||||||
|
unsigned int cas = 7;
|
||||||
|
di->tAAmin = UINT32_MAX; /* we don't have UINT_MAX? */
|
||||||
|
for (i = 0; i < ARRAY_SIZE(tCK_offsets); ++i, --cas) {
|
||||||
|
for (; cas > 1; --cas)
|
||||||
|
if (di->cas_latencies & (1 << cas))
|
||||||
|
break;
|
||||||
|
if (cas <= 1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
const unsigned int tCK_enc =
|
||||||
|
smbus_read_byte(smb_addr, tCK_offsets[i]);
|
||||||
|
const unsigned int tAA = spd_decode_tck_time(tCK_enc) * cas;
|
||||||
|
if (tAA < di->tAAmin)
|
||||||
|
di->tAAmin = tAA;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* convert to 1/256ns */
|
||||||
|
di->tRAS = smbus_read_byte(smb_addr, 30) << 8; /* given in ns */
|
||||||
|
di->tRP = smbus_read_byte(smb_addr, 27) << 6; /* given in 1/4ns */
|
||||||
|
di->tRCD = smbus_read_byte(smb_addr, 29) << 6; /* given in 1/4ns */
|
||||||
|
di->tWR = smbus_read_byte(smb_addr, 36) << 6; /* given in 1/4ns */
|
||||||
|
|
||||||
|
di->raw_card = 0; /* Use same path as for DDR3 type A. */
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* This function collects RAM characteristics from SPD, assuming that RAM
|
||||||
|
* is generally within chipset's requirements, since verify_ddr2() passed.
|
||||||
|
*/
|
||||||
|
static void collect_ddr2(sysinfo_t *const sysinfo, spdinfo_t *const config)
|
||||||
|
{
|
||||||
|
int cur;
|
||||||
|
for (cur = 0; cur < 2; ++cur) {
|
||||||
|
if (config->dimm_mask & (1 << (2 * cur))) {
|
||||||
|
collect_ddr2_dimm(&config->channel[cur],
|
||||||
|
sysinfo->spd_map[2 * cur]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function collects RAM characteristics from SPD, assuming that RAM
|
* This function collects RAM characteristics from SPD, assuming that RAM
|
||||||
* is generally within chipset's requirements, since verify_ddr3() passed.
|
* is generally within chipset's requirements, since verify_ddr3() passed.
|
||||||
|
@ -325,18 +476,18 @@ static void collect_ddr3(sysinfo_t *const sysinfo, spdinfo_t *const config)
|
||||||
config->channel[cur].cas_latencies =
|
config->channel[cur].cas_latencies =
|
||||||
((smbus_read_byte(smb_addr, 15) << 8) | smbus_read_byte(smb_addr, 14))
|
((smbus_read_byte(smb_addr, 15) << 8) | smbus_read_byte(smb_addr, 14))
|
||||||
<< 4; /* so bit x is CAS x */
|
<< 4; /* so bit x is CAS x */
|
||||||
config->channel[cur].tAAmin = smbus_read_byte(smb_addr, 16); /* in MTB */
|
config->channel[cur].tAAmin = smbus_read_byte(smb_addr, 16) * 32; /* convert from MTB to 1/256 ns */
|
||||||
config->channel[cur].tCKmin = smbus_read_byte(smb_addr, 12); /* in MTB */
|
config->channel[cur].tCKmin = smbus_read_byte(smb_addr, 12) * 32; /* convert from MTB to 1/256 ns */
|
||||||
|
|
||||||
config->channel[cur].width = smbus_read_byte(smb_addr, 7) & 7;
|
config->channel[cur].width = smbus_read_byte(smb_addr, 7) & 7;
|
||||||
config->channel[cur].page_size = config->channel[cur].width *
|
config->channel[cur].page_size = config->channel[cur].width *
|
||||||
(1 << config->channel[cur].cols); /* in Bytes */
|
(1 << config->channel[cur].cols); /* in Bytes */
|
||||||
|
|
||||||
tmp = smbus_read_byte(smb_addr, 21);
|
tmp = smbus_read_byte(smb_addr, 21);
|
||||||
config->channel[cur].tRAS = smbus_read_byte(smb_addr, 22) | ((tmp & 0xf) << 8);
|
config->channel[cur].tRAS = (smbus_read_byte(smb_addr, 22) | ((tmp & 0xf) << 8)) * 32;
|
||||||
config->channel[cur].tRP = smbus_read_byte(smb_addr, 20);
|
config->channel[cur].tRP = smbus_read_byte(smb_addr, 20) * 32;
|
||||||
config->channel[cur].tRCD = smbus_read_byte(smb_addr, 18);
|
config->channel[cur].tRCD = smbus_read_byte(smb_addr, 18) * 32;
|
||||||
config->channel[cur].tWR = smbus_read_byte(smb_addr, 17);
|
config->channel[cur].tWR = smbus_read_byte(smb_addr, 17) * 32;
|
||||||
|
|
||||||
config->channel[cur].raw_card = smbus_read_byte(smb_addr, 62) & 0x1f;
|
config->channel[cur].raw_card = smbus_read_byte(smb_addr, 62) & 0x1f;
|
||||||
}
|
}
|
||||||
|
@ -418,10 +569,13 @@ static unsigned int find_common_clock_cas(sysinfo_t *const sysinfo,
|
||||||
case FSB_CLOCK_667MHz: fsb_mhz = 667; break;
|
case FSB_CLOCK_667MHz: fsb_mhz = 667; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int clock = 8000 / tCKmin;
|
unsigned int clock = 256000 / tCKmin;
|
||||||
if ((clock > sysinfo->max_ddr3_mt / 2) || (clock > fsb_mhz / 2)) {
|
const unsigned int max_ddr_clock = (sysinfo->spd_type == DDR2)
|
||||||
int new_clock = MIN(sysinfo->max_ddr3_mt / 2, fsb_mhz / 2);
|
? sysinfo->max_ddr2_mt / 2
|
||||||
printk(BIOS_SPEW, "DIMMs support %d MHz, but chipset only runs at up to %d. Limiting...\n",
|
: sysinfo->max_ddr3_mt / 2;
|
||||||
|
if ((clock > max_ddr_clock) || (clock > fsb_mhz / 2)) {
|
||||||
|
int new_clock = MIN(max_ddr_clock, fsb_mhz / 2);
|
||||||
|
printk(BIOS_INFO, "DIMMs support %d MHz, but chipset only runs at up to %d. Limiting...\n",
|
||||||
clock, new_clock);
|
clock, new_clock);
|
||||||
clock = new_clock;
|
clock = new_clock;
|
||||||
}
|
}
|
||||||
|
@ -433,13 +587,13 @@ static unsigned int find_common_clock_cas(sysinfo_t *const sysinfo,
|
||||||
while (1) {
|
while (1) {
|
||||||
if (!clock)
|
if (!clock)
|
||||||
die("Couldn't find compatible clock / CAS settings.\n");
|
die("Couldn't find compatible clock / CAS settings.\n");
|
||||||
tCKproposed = 8000 / clock;
|
tCKproposed = 256000 / clock;
|
||||||
CAS = DIV_ROUND_UP(tAAmin, tCKproposed);
|
CAS = DIV_ROUND_UP(tAAmin, tCKproposed);
|
||||||
printk(BIOS_SPEW, "Trying CAS %u, tCK %u.\n", CAS, tCKproposed);
|
printk(BIOS_SPEW, "Trying CAS %u, tCK %u.\n", CAS, tCKproposed);
|
||||||
for (; CAS <= DDR3_MAX_CAS; ++CAS)
|
for (; CAS <= DDR3_MAX_CAS; ++CAS)
|
||||||
if (cas_latencies & (1 << CAS))
|
if (cas_latencies & (1 << CAS))
|
||||||
break;
|
break;
|
||||||
if ((CAS <= DDR3_MAX_CAS) && (CAS * tCKproposed < 160)) {
|
if ((CAS <= DDR3_MAX_CAS) && (CAS * tCKproposed < 32 * 160)) {
|
||||||
/* Found good CAS. */
|
/* Found good CAS. */
|
||||||
printk(BIOS_SPEW, "Found compatible clock / CAS pair: %u / %u.\n", clock, CAS);
|
printk(BIOS_SPEW, "Found compatible clock / CAS pair: %u / %u.\n", clock, CAS);
|
||||||
break;
|
break;
|
||||||
|
@ -533,7 +687,9 @@ static void calculate_derived_timings(sysinfo_t *const sysinfo,
|
||||||
|
|
||||||
/* Refresh rate is fixed. */
|
/* Refresh rate is fixed. */
|
||||||
unsigned int tWL;
|
unsigned int tWL;
|
||||||
if (sysinfo->selected_timings.mem_clock == MEM_CLOCK_1067MT) {
|
if (sysinfo->spd_type == DDR2) {
|
||||||
|
tWL = sysinfo->selected_timings.CAS - 1;
|
||||||
|
} else if (sysinfo->selected_timings.mem_clock == MEM_CLOCK_1067MT) {
|
||||||
tWL = 6;
|
tWL = 6;
|
||||||
} else {
|
} else {
|
||||||
tWL = 5;
|
tWL = 5;
|
||||||
|
@ -594,7 +750,8 @@ static void collect_dimm_config(sysinfo_t *const sysinfo)
|
||||||
printk(BIOS_SPEW, "DDR mask %x, DDR %d\n", spdinfo.dimm_mask, sysinfo->spd_type);
|
printk(BIOS_SPEW, "DDR mask %x, DDR %d\n", spdinfo.dimm_mask, sysinfo->spd_type);
|
||||||
|
|
||||||
if (sysinfo->spd_type == DDR2) {
|
if (sysinfo->spd_type == DDR2) {
|
||||||
die("DDR2 not supported at this time.\n");
|
verify_ddr2(sysinfo, spdinfo.dimm_mask);
|
||||||
|
collect_ddr2(sysinfo, &spdinfo);
|
||||||
} else if (sysinfo->spd_type == DDR3) {
|
} else if (sysinfo->spd_type == DDR3) {
|
||||||
verify_ddr3(sysinfo, spdinfo.dimm_mask);
|
verify_ddr3(sysinfo, spdinfo.dimm_mask);
|
||||||
collect_ddr3(sysinfo, &spdinfo);
|
collect_ddr3(sysinfo, &spdinfo);
|
||||||
|
@ -621,7 +778,7 @@ static void collect_dimm_config(sysinfo_t *const sysinfo)
|
||||||
spdinfo.channel[i].width, spdinfo.channel[i].page_size,
|
spdinfo.channel[i].width, spdinfo.channel[i].page_size,
|
||||||
spdinfo.channel[i].banks, spdinfo.channel[i].ranks,
|
spdinfo.channel[i].banks, spdinfo.channel[i].ranks,
|
||||||
spdinfo.channel[i].tAAmin, spdinfo.channel[i].tCKmin,
|
spdinfo.channel[i].tAAmin, spdinfo.channel[i].tCKmin,
|
||||||
8000 / spdinfo.channel[i].tCKmin, spdinfo.channel[i].cas_latencies);
|
256000 / spdinfo.channel[i].tCKmin, spdinfo.channel[i].cas_latencies);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -660,6 +817,9 @@ static void collect_dimm_config(sysinfo_t *const sysinfo)
|
||||||
sysinfo->selected_timings.channel_mode = CHANNEL_MODE_DUAL_INTERLEAVED;
|
sysinfo->selected_timings.channel_mode = CHANNEL_MODE_DUAL_INTERLEAVED;
|
||||||
else
|
else
|
||||||
sysinfo->selected_timings.channel_mode = CHANNEL_MODE_SINGLE;
|
sysinfo->selected_timings.channel_mode = CHANNEL_MODE_SINGLE;
|
||||||
|
|
||||||
|
if (sysinfo->spd_type == DDR2)
|
||||||
|
die("DDR2 support not complete yet\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void reset_on_bad_warmboot(void)
|
static void reset_on_bad_warmboot(void)
|
||||||
|
@ -825,6 +985,7 @@ static void configure_dram_control_mode(const timings_t *const timings, const di
|
||||||
cxdrc |= CxDRC0_RANKEN(r);
|
cxdrc |= CxDRC0_RANKEN(r);
|
||||||
cxdrc = (cxdrc & ~CxDRC0_RMS_MASK) |
|
cxdrc = (cxdrc & ~CxDRC0_RMS_MASK) |
|
||||||
/* Always 7.8us for DDR3: */
|
/* Always 7.8us for DDR3: */
|
||||||
|
/* FIXME DDR2: SPD+12? */
|
||||||
CxDRC0_RMS_78US;
|
CxDRC0_RMS_78US;
|
||||||
mchbar_write32(mchbar, cxdrc);
|
mchbar_write32(mchbar, cxdrc);
|
||||||
|
|
||||||
|
@ -992,6 +1153,7 @@ static void dram_program_timings(const timings_t *const timings)
|
||||||
mchbar_write32(CxDRT5_MCHBAR(i), reg);
|
mchbar_write32(CxDRT5_MCHBAR(i), reg);
|
||||||
|
|
||||||
reg = mchbar_read32(CxDRT6_MCHBAR(i));
|
reg = mchbar_read32(CxDRT6_MCHBAR(i));
|
||||||
|
/* FIXME DDR2: SPD+12? */
|
||||||
reg = (reg & ~(0xffff << 16)) | (0x066a << 16); /* always 7.8us refresh rate for DDR3 */
|
reg = (reg & ~(0xffff << 16)) | (0x066a << 16); /* always 7.8us refresh rate for DDR3 */
|
||||||
reg |= (1 << 2);
|
reg |= (1 << 2);
|
||||||
mchbar_write32(CxDRT6_MCHBAR(i), reg);
|
mchbar_write32(CxDRT6_MCHBAR(i), reg);
|
||||||
|
|
Loading…
Reference in New Issue