diff options
Diffstat (limited to 'src/northbridge/amd')
-rw-r--r-- | src/northbridge/amd/amdk8/raminit.c | 158 |
1 files changed, 148 insertions, 10 deletions
diff --git a/src/northbridge/amd/amdk8/raminit.c b/src/northbridge/amd/amdk8/raminit.c index 563064b13f..c4dc3f408e 100644 --- a/src/northbridge/amd/amdk8/raminit.c +++ b/src/northbridge/amd/amdk8/raminit.c @@ -1164,14 +1164,13 @@ static long spd_handle_unbuffered_dimms(const struct mem_controller *ctrl, dcl &= ~DCL_UnBuffDimm; if (unbuffered) { if ((has_dualch) && (!is_cpu_pre_d0())) { - dcl |= DCL_UnBuffDimm; /* set DCL_DualDIMMen too? */ - - /* set DCL_En2T if you have non-equal DDR mem types! */ - + dcl |= DCL_UnBuffDimm; +#if defined(CONFIG_CPU_AMD_SOCKET_939) && CONFIG_CPU_AMD_SOCKET_939 if ((cpuid_eax(1) & 0x30) == 0x30) { /* CS[7:4] is copy of CS[3:0], should be set for 939 socket */ dcl |= DCL_UpperCSMap; } +#endif } else { dcl |= DCL_UnBuffDimm; } @@ -1382,21 +1381,150 @@ struct spd_set_memclk_result { const struct mem_param *param; long dimm_mask; }; + +static const unsigned char min_cycle_times[] = { + [NBCAP_MEMCLK_200MHZ] = 0x50, /* 5ns */ + [NBCAP_MEMCLK_166MHZ] = 0x60, /* 6ns */ + [NBCAP_MEMCLK_133MHZ] = 0x75, /* 7.5ns */ + [NBCAP_MEMCLK_100MHZ] = 0xa0, /* 10ns */ +}; + +#if defined(CONFIG_CPU_AMD_SOCKET_939) && CONFIG_CPU_AMD_SOCKET_939 + +/* return the minimum cycle time and set 2T accordingly */ +static unsigned int spd_dimm_loading_socket939(const struct mem_controller *ctrl, long dimm_mask) { + +/* + 1 raise so we detect 0 as bad field */ +#define DDR200 (NBCAP_MEMCLK_100MHZ + 1) +#define DDR333 (NBCAP_MEMCLK_166MHZ + 1) +#define DDR400 (NBCAP_MEMCLK_200MHZ + 1) +#define DDR_2T 0x80 +#define DDR_MASK 0x3 + +#define DDR200_2T (DDR_2T | DDR200) +#define DDR333_2T (DDR_2T | DDR333) +#define DDR400_2T (DDR_2T | DDR400) + +/* + Following table comes directly from BKDG (unbuffered DIMM support) + [Y][X] Y = ch0_0, ch1_0, ch0_1, ch1_1 1=present 0=empty + X uses same layout but 1 means double rank 0 is single rank/empty + + Following tables come from BKDG the ch{0_0,1_0,0_1,1_1} maps to + MEMCS_{1L,1H,2L,2H} in i the PDF. PreE is table 45, and revE table 46. +*/ + + static const unsigned char dimm_loading_config_preE[16][16] = { + [0x8] = {[0x0] = DDR400,[0x8] = DDR400}, + [0x2] = {[0x0] = DDR333,[0x2] = DDR400}, + [0xa] = {[0x0] = DDR400_2T,[0x2] = DDR400_2T, + [0x8] = DDR400_2T,[0xa] = DDR333_2T}, + [0xc] = {[0x0] = DDR400,[0xc] = DDR400}, + [0x3] = {[0x0] = DDR333,[0x3] = DDR400}, + [0xf] = {[0x0] = DDR400_2T,[0x3] = DDR400_2T, + [0xc] = DDR400_2T,[0xf] = DDR333_2T}, + }; + + static const unsigned char dimm_loading_config_revE[16][16] = { + [0x8] = {[0x0] = DDR400, [0x8] = DDR400}, + [0x2] = {[0x0] = DDR333, [0x2] = DDR400}, + [0x4] = {[0x0] = DDR400, [0x4] = DDR400}, + [0x1] = {[0x0] = DDR333, [0x1] = DDR400}, + [0xa] = {[0x0] = DDR400_2T, [0x2] = DDR400_2T, + [0x8] = DDR400_2T, [0xa] = DDR333_2T}, + [0x5] = {[0x0] = DDR400_2T, [0x1] = DDR400_2T, + [0x4] = DDR400_2T, [0x5] = DDR333_2T}, + [0xc] = {[0x0] = DDR400, [0xc] = DDR400, [0x4] = DDR400, [0x8] = DDR400}, + [0x3] = {[0x0] = DDR333, [0x1] = DDR333, [0x2] = DDR333, [0x3] = DDR400}, + [0xe] = {[0x0] = DDR400_2T, [0x4] = DDR400_2T, [0x2] = DDR400_2T, + [0x6] = DDR400_2T, [0x8] = DDR400_2T, [0xc] = DDR400_2T, + [0xa] = DDR333_2T, [0xe] = DDR333_2T}, + [0xb] = {[0x0] = DDR333, [0x1] = DDR400_2T, [0x2] = DDR333_2T, + [0x3] = DDR400_2T, [0x8] = DDR333_2T, [0x9] = DDR400_2T, + [0xa] = DDR333_2T, [0xb] = DDR333_2T}, + [0xd] = {[0x0] = DDR400_2T, [0x8] = DDR400_2T, [0x1] = DDR400_2T, + [0x9] = DDR333_2T, [0x4] = DDR400_2T, [0xc] = DDR400_2T, + [0x5] = DDR333_2T, [0xd] = DDR333_2T}, + [0x7] = {[0x0] = DDR333, [0x2] = DDR400_2T, [0x1] = DDR333_2T, + [0x3] = DDR400_2T, [0x4] = DDR333_2T, [0x6] = DDR400_2T, + [0x5] = DDR333_2T, [0x7] = DDR333_2T}, + [0xf] = {[0x0] = DDR400_2T, [0x1] = DDR400_2T, [0x4] = DDR400_2T, + [0x5] = DDR333_2T, [0x2] = DDR400_2T, [0x3] = DDR400_2T, + [0x6] = DDR400_2T, [0x7] = DDR333_2T, [0x8] = DDR400_2T, + [0x9] = DDR400_2T, [0xc] = DDR400_2T, [0xd] = DDR333_2T, + [0xa] = DDR333_2T, [0xb] = DDR333_2T, [0xe] = DDR333_2T, + [0xf] = DDR333_2T}, + }; + /*The dpos matches channel positions defined in BKDG and above arrays + The rpos is bitmask of dual rank dimms in same order as dpos */ + unsigned int dloading = 0, dloading_cycle_time, i, rpos = 0, dpos =0; + const unsigned char (*dimm_loading_config)[16] = dimm_loading_config_revE; + int rank; + uint32_t dcl; + + if (is_cpu_pre_e0()) { + dimm_loading_config = dimm_loading_config_preE; + } + + /* only DIMMS two per channel */ + for (i = 0; i < 2; i++) { + if ((dimm_mask & (1 << i))) { + /* read rank channel 0 */ + rank = spd_read_byte(ctrl->channel0[i], 5); + if (rank < 0) goto hw_error; + rpos |= (rank == 2) ? (1 << (3 - (i * 2))) : 0; + dpos |= (1 << (3 - (i * 2))); + } + + if ((dimm_mask & (1 << (i+DIMM_SOCKETS)))) { + /* read rank channel 1*/ + rank = spd_read_byte(ctrl->channel1[i], 5); + if (rank < 0) goto hw_error; + rpos |= (rank == 2) ? (1 << (2 - (i * 2))) : 0; + dpos |= (1 << (2 - (i * 2))); + } + } + /* now the lookup, decode the max speed DDR400_2T etc */ + dloading = dimm_loading_config[dpos][rpos] & DDR_MASK; +#if 0 + printk(BIOS_DEBUG, "XXX %x %x dload %x 2T %x\n", dpos,rpos, dloading, dimm_loading_config[dpos][rpos] & DDR_2T); +#endif +hw_error: + if (dloading != 0) { + /* map it back to cycle load times */ + dloading_cycle_time = min_cycle_times[dloading - 1]; + /* we have valid combination check the restrictions */ + dcl = pci_read_config32(ctrl->f2, DRAM_CONFIG_LOW); + dcl |= (dimm_loading_config[dpos][rpos] & DDR_2T) ? (DCL_En2T) : 0; + /* Set DuallDimm is second channel is completely empty (revD+) */ + if (((cpuid_eax(1) & 0xfff0f) >= 0x10f00) && ((dpos & 0x5) == 0)) { + printk(BIOS_DEBUG, "Setting DualDIMMen\n"); + dcl |= DCL_DualDIMMen; + } + pci_write_config32(ctrl->f2, DRAM_CONFIG_LOW, dcl); + } else { + /* if we don't find it we se it to DDR400 */ + printk(BIOS_WARNING, "Detected strange DIMM configuration, may not work! (or bug)\n"); + dloading_cycle_time = min_cycle_times[NBCAP_MEMCLK_200MHZ]; + } + + return dloading_cycle_time; +} + +#endif /* #if defined(CONFIG_CPU_AMD_SOCKET_939) */ + static struct spd_set_memclk_result spd_set_memclk(const struct mem_controller *ctrl, long dimm_mask) { /* Compute the minimum cycle time for these dimms */ struct spd_set_memclk_result result; unsigned min_cycle_time, min_latency, bios_cycle_time; +#if defined(CONFIG_CPU_AMD_SOCKET_939) + unsigned dloading_cycle_time; +#endif int i; uint32_t value; static const uint8_t latency_indicies[] = { 26, 23, 9 }; - static const unsigned char min_cycle_times[] = { - [NBCAP_MEMCLK_200MHZ] = 0x50, /* 5ns */ - [NBCAP_MEMCLK_166MHZ] = 0x60, /* 6ns */ - [NBCAP_MEMCLK_133MHZ] = 0x75, /* 7.5ns */ - [NBCAP_MEMCLK_100MHZ] = 0xa0, /* 10ns */ - }; value = pci_read_config32(ctrl->f3, NORTHBRIDGE_CAP); @@ -1545,6 +1673,16 @@ static struct spd_set_memclk_result spd_set_memclk(const struct mem_controller * } #endif #endif + +#if defined(CONFIG_CPU_AMD_SOCKET_939) && CONFIG_CPU_AMD_SOCKET_939 + dloading_cycle_time = spd_dimm_loading_socket939(ctrl, dimm_mask); + if (dloading_cycle_time > min_cycle_time) { + min_cycle_time = dloading_cycle_time; + printk(BIOS_WARNING, "Memory speed reduced due to signal loading conditions\n"); + } +#endif + + /* Now that I know the minimum cycle time lookup the memory parameters */ result.param = get_mem_param(min_cycle_time); |