[coreboot] [commit] r6176 - trunk/src/northbridge/amd/amdk8

repository service svn at coreboot.org
Mon Dec 13 21:43:34 CET 2010


Author: ruik
Date: Mon Dec 13 21:43:33 2010
New Revision: 6176
URL: https://tracker.coreboot.org/trac/coreboot/changeset/6176

Log:
Attached patch implements the memory speed reductions (and 2T/1T clock logic) for DDR1 memory (939 sockets). The details can be found in BKDG chapter 4.1.3.3.

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 at assembler.cz> 
Acked-by: Peter Stuge <peter at stuge.se>

Modified:
   trunk/src/northbridge/amd/amdk8/raminit.c

Modified: trunk/src/northbridge/amd/amdk8/raminit.c
==============================================================================
--- trunk/src/northbridge/amd/amdk8/raminit.c	Mon Dec 13 21:04:25 2010	(r6175)
+++ trunk/src/northbridge/amd/amdk8/raminit.c	Mon Dec 13 21:43:33 2010	(r6176)
@@ -1164,14 +1164,13 @@
 	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 @@
 	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 @@
 	}
 #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);
 




More information about the coreboot mailing list