Attached patch implements the memory speed reductions (and 2T/1T clock logic) for...
authorRudolf Marek <r.marek@assembler.cz>
Mon, 13 Dec 2010 20:43:33 +0000 (20:43 +0000)
committerRudolf Marek <r.marek@assembler.cz>
Mon, 13 Dec 2010 20:43:33 +0000 (20:43 +0000)
The patch looks at certain DDR configurations (dual rank/single rank) and lowers the clocks to 2T or frequency as guide suggest. It sets the DualDIMMen bit which I believe should be set for non-dual channel configs.

The patch does not implement support for three dimm configurations supported from revE.
On the other hand it should improve greatly memory stability across the 939 platform.

Signed-off-by: Rudolf Marek <r.marek@assembler.cz>
Acked-by: Peter Stuge <peter@stuge.se>
git-svn-id: svn://svn.coreboot.org/coreboot/trunk@6176 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1

src/northbridge/amd/amdk8/raminit.c

index 563064b13f667229130c81fabb9e4cc1b793805c..c4dc3f408e8b5c0b583fb3fbb7ccc742316a8858 100644 (file)
@@ -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);