Patchwork Implement DIMM loading for DDR on K8

login
register
about
Submitter Rudolf Marek
Date 2010-12-12 00:10:33
Message ID <4D0412F9.5000600@assembler.cz>
Download mbox | patch
Permalink /patch/2424/
State Accepted
Headers show

Comments

Rudolf Marek - 2010-12-12 00:10:33
Hi,

Yes it is tested on real HW. Attaching refreshed patch because

#if CONFIG_ ... SOCKET939 == 1

#endif

Is undefined, so I changed that to

#if defined(....)

(After abuild test ;)


Thanks,
Rudolf
Stefan Reinauer - 2010-12-12 00:59:51
* Rudolf Marek <r.marek@assembler.cz> [101212 01:10]:
> Hi,
> 
> Yes it is tested on real HW. Attaching refreshed patch because
> 
> #if CONFIG_ ... SOCKET939 == 1
> 
> #endif
> 
> Is undefined, so I changed that to
> 
> #if defined(....)


that's not good enough i think. It might be 0, then defined would hit
even though it should not.


Stefan
Peter Stuge - 2010-12-12 01:15:04
Rudolf Marek wrote:
> +/*
> +	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
> +*/
> +

Maybe clarify that ch{0_0,1_0,0_1,1_1} maps to MEMCS_{1L,1H,2L,2H} in
the PDF. Maybe also mention that 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},
> +	};

This matches the PDF.


> +	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_2T, [0x2] = DDR400_2T, [0x1] = DDR333_2T,

7,0 above could be DDR333, ie use 1T.


> +			 [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 26094 BKDF PDF refers to "the AMD Athlon™ 64 939 Processor
Motherboard Design Guide, order# 30474" in some of the memory
configuration cases.

It seems that 30474 isn't publically available, maybe someone has
access to it and could check if we should take special considerations
for those cases in coreboot, maybe to choose a lower clock speed to
guarantee reliability?


Acked-by: Peter Stuge <peter@stuge.se>
Rudolf Marek - 2010-12-13 20:44:58
Hi all,

I fixed the comments and fixed the #if sections. Thanks for input commited as 
6176 (and as you noticed all my pending patches too ;)

Thanks,
Rudolf

Patch

Index: coreboot/src/northbridge/amd/amdk8/raminit.c
===================================================================
--- coreboot.orig/src/northbridge/amd/amdk8/raminit.c	2010-12-12 01:07:06.000000000 +0100
+++ coreboot/src/northbridge/amd/amdk8/raminit.c	2010-12-12 01:07:53.000000000 +0100
@@ -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)
 			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,147 @@ 
 	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)
+
+/* 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
+*/
+
+	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_2T, [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 +1670,16 @@ 
 	}
 #endif
 #endif
+
+#if defined(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);