[coreboot-gerrit] Change in coreboot[master]: nb/x4x/raminit: Decode ddr3 dimms

Arthur Heymans (Code Review) gerrit at coreboot.org
Wed May 24 22:10:32 CEST 2017


Arthur Heymans has uploaded a new change for review. ( https://review.coreboot.org/19869 )

Change subject: nb/x4x/raminit: Decode ddr3 dimms
......................................................................

nb/x4x/raminit: Decode ddr3 dimms

The raw_spd array needs to be increased since the ddr3 variant of
spd_decode assumes an array of that size.

Change-Id: I8dba19ca1e6e6b0a03b56c8de9633f9c1a2eb7d7
Signed-off-by: Arthur Heymans <arthur at aheymans.xyz>
---
M src/northbridge/intel/x4x/Makefile.inc
M src/northbridge/intel/x4x/raminit.c
M src/northbridge/intel/x4x/raminit_ddr2.c
M src/northbridge/intel/x4x/spd_ddr2_decode.c
A src/northbridge/intel/x4x/spd_ddr3_decode.c
M src/northbridge/intel/x4x/x4x.h
6 files changed, 197 insertions(+), 8 deletions(-)


  git pull ssh://review.coreboot.org:29418/coreboot refs/changes/69/19869/1

diff --git a/src/northbridge/intel/x4x/Makefile.inc b/src/northbridge/intel/x4x/Makefile.inc
index b433810..86ecf7b 100644
--- a/src/northbridge/intel/x4x/Makefile.inc
+++ b/src/northbridge/intel/x4x/Makefile.inc
@@ -21,6 +21,7 @@
 romstage-y += raminit_ddr2.c
 romstage-y += ram_calc.c
 romstage-y += spd_ddr2_decode.c
+romstage-y += spd_ddr3_decode.c
 
 ramstage-y += acpi.c
 ramstage-y += ram_calc.c
diff --git a/src/northbridge/intel/x4x/raminit.c b/src/northbridge/intel/x4x/raminit.c
index 7e69b4c..b2bc280 100644
--- a/src/northbridge/intel/x4x/raminit.c
+++ b/src/northbridge/intel/x4x/raminit.c
@@ -116,7 +116,7 @@
 	unsigned int device;
 	u8 dram_type_mask = (1 << DDR2) | (1 << DDR3);
 	u8 dimm_mask = 0;
-	u8 raw_spd[128];
+	u8 raw_spd[256];
 	int i;
 	struct abs_timings saved_timings = { };
 	saved_timings.cas_supported = (u32)-1;
@@ -151,7 +151,12 @@
 				continue;
 			}
 		} else { /* DDR3: not implemented so don't decode */
-			die("DDR3 support is not implemented\n");
+			i2c_block_read(device, 0, 128, raw_spd);
+			if (ddr3_save_dimminfo(i, raw_spd, &saved_timings, s)) {
+				/* something in decoded SPD was unsupported */
+				s->dimms[i].card_type = RAW_CARD_UNPOPULATED;
+				continue;
+			}
 		}
 		dimm_mask = (1 << i);
 	}
@@ -159,6 +164,8 @@
 		die("No memory installed.\n");
 	if (s->spd_type == DDR2)
 		select_cas_dramfreq_ddr2(s, &saved_timings);
+	else
+		select_cas_dramfreq_ddr3(s, &saved_timings);
 	select_discrete_timings(s, &saved_timings);
 }
 
diff --git a/src/northbridge/intel/x4x/raminit_ddr2.c b/src/northbridge/intel/x4x/raminit_ddr2.c
index 8984fad..e663564 100644
--- a/src/northbridge/intel/x4x/raminit_ddr2.c
+++ b/src/northbridge/intel/x4x/raminit_ddr2.c
@@ -1469,7 +1469,6 @@
 	};
 
 	u8 drbtab[10] = {0x04, 0x02, 0x08, 0x04, 0x08, 0x04, 0x10, 0x08, 0x20, 0x10};
-	u8 tab_width;
 
 	// DRA
 	rankpop0 = 0;
@@ -1480,9 +1479,8 @@
 			i = ch << 1;
 		else
 			i = (ch << 1) + 1;
-		tab_width = (s->dimms[i].width >> 3) - 1; /* 16->1, 8->0 */
 		dra = dratab[s->dimms[i].n_banks]
-			[tab_width]
+			[s->dimms[i].width]
 			[s->dimms[i].cols-9]
 			[s->dimms[i].rows-12];
 		if (s->dimms[i].n_banks == 1)
diff --git a/src/northbridge/intel/x4x/spd_ddr2_decode.c b/src/northbridge/intel/x4x/spd_ddr2_decode.c
index 938aab7..f8f4403 100644
--- a/src/northbridge/intel/x4x/spd_ddr2_decode.c
+++ b/src/northbridge/intel/x4x/spd_ddr2_decode.c
@@ -82,12 +82,14 @@
 	  * Used to be content of spd byte 62 which does not make
 	  * that much sense.
 	  */
-	s->dimms[dimm_idx].width = decoded_dimm.width;
-	if (!(s->dimms[dimm_idx].width & (0x8 | 0x10))) {
+
+	if (!(decoded_dimm.width & (0x8 | 0x10))) {
 		printk(BIOS_ERR, "DIMM%d Unsupported width: x%d. Disabling dimm\n",
 			dimm_idx, s->dimms[dimm_idx].width);
 		return 1;
 	}
+
+	s->dimms[dimm_idx].width = (decoded_dimm.width >> 3) - 1;
 	/*
 	 * This boils down to:
 	 * "Except for the x16 configuration, all DDR2 devices have a
@@ -96,7 +98,7 @@
 	 * page size."
 	 * Micron, 'TN-47-16 Designing for High-Density DDR2 Memory'
 	 */
-	 s->dimms[dimm_idx].page_size = s->dimms[dimm_idx].width *
+	 s->dimms[dimm_idx].page_size = decoded_dimm.width *
 		 (1 << decoded_dimm.col_bits);
 
 	switch (decoded_dimm.banks) {
diff --git a/src/northbridge/intel/x4x/spd_ddr3_decode.c b/src/northbridge/intel/x4x/spd_ddr3_decode.c
new file mode 100644
index 0000000..fd69fd4
--- /dev/null
+++ b/src/northbridge/intel/x4x/spd_ddr3_decode.c
@@ -0,0 +1,175 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2015 Damien Zammit <damien at zamaudio.com>
+ * Copyright (C) 2017 Arthur Heymans <arthur at aheymans.xyz>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <arch/io.h>
+#include <console/console.h>
+#include <device/dram/ddr3.h>
+#include "x4x.h"
+
+static void normalize_tCLK(u32 *tCLK)
+{
+	if (*tCLK <= TCK_666MHZ)
+		*tCLK = TCK_666MHZ;
+	else if (*tCLK <= TCK_533MHZ)
+		*tCLK = TCK_533MHZ;
+	else if (*tCLK <= TCK_400MHZ)
+		*tCLK = TCK_400MHZ;
+	else
+		*tCLK = 0;
+}
+
+static void increase_tCLK(u32 *tCLK)
+{
+	++*tCLK;
+	normalize_tCLK(tCLK);
+}
+
+void select_cas_dramfreq_ddr3(struct sysinfo *s,
+			struct abs_timings *saved_timings)
+{
+	/* various constraints must be fulfilled:
+	   CAS * tCK < 20ns == 160MTB
+	   tCK_max >= tCK >= tCK_min
+	   CAS >= roundup(tAA_min/tCK)
+	   CAS supported
+	   AND BTW: Clock(MT) = 2000 / tCK(ns) - intel uses MTs but calls them MHz
+	 */
+
+	u32 min_tCLK;
+	u8 try_CAS;
+	u16 capid = (pci_read_config16(PCI_DEV(0, 0, 0), 0xea) >> 4) & 0x3f;
+
+	switch (s->max_fsb) {
+	default:
+	case FSB_CLOCK_800MHz:
+		min_tCLK = TCK_400MHZ;
+		break;
+	case FSB_CLOCK_1066MHz:
+		min_tCLK = TCK_533MHZ;
+		break;
+	case FSB_CLOCK_1333MHz:
+		min_tCLK = TCK_666MHZ;
+		break;
+	}
+
+	switch (capid >> 3) {
+	default: /* Should not happen */
+		min_tCLK = TCK_400MHZ;
+		break;
+	case 1:
+		min_tCLK = MAX(min_tCLK, TCK_400MHZ);
+		break;
+	case 2:
+		min_tCLK = MAX(min_tCLK, TCK_533MHZ);
+		break;
+	case 3: /* Only on P45 */
+		min_tCLK = MAX(min_tCLK, TCK_666MHZ);
+		break;
+	}
+
+	min_tCLK = MAX(min_tCLK, saved_timings->min_tclk);
+	normalize_tCLK(&min_tCLK);
+	if (min_tCLK == 0) {
+		printk(BIOS_ERR, "DRAM frequency is under lowest supported "
+				"frequency (400 MHz). Increasing to 400 MHz as last resort");
+		min_tCLK = TCK_400MHZ;
+	}
+
+	while (1) {
+		if (min_tCLK == 0)
+			die("Couldn't find compatible clock / CAS settings.\n");
+		try_CAS = DIV_ROUND_UP(saved_timings->min_tAA, min_tCLK);
+		printk(BIOS_SPEW, "Trying CAS %u, tCK %u.\n", try_CAS, min_tCLK);
+		for (; try_CAS <= DDR3_MAX_CAS; ++try_CAS) {
+			if ((saved_timings->cas_supported << 4) & (1 << try_CAS))
+				break;
+		}
+		if ((try_CAS <= DDR3_MAX_CAS) && (try_CAS * min_tCLK < 20 * 256)) {
+			/* Found good CAS. */
+			printk(BIOS_SPEW, "Found compatible tCLK / CAS pair: %u / %u.\n",
+				min_tCLK, try_CAS);
+			break;
+		}
+		increase_tCLK(&min_tCLK);
+	}
+	s->selected_timings.tclk = min_tCLK;
+	s->selected_timings.CAS = try_CAS;
+
+	switch (s->selected_timings.tclk) {
+	case TCK_400MHZ:
+		s->selected_timings.mem_clk = MEM_CLOCK_800MHz; break;
+	case TCK_533MHZ:
+		s->selected_timings.mem_clk = MEM_CLOCK_1066MHz; break;
+	case TCK_666MHZ:
+		s->selected_timings.mem_clk = MEM_CLOCK_1333MHz; break;
+	}
+}
+
+int ddr3_save_dimminfo(u8 dimm_idx, u8 *raw_spd,
+		struct abs_timings *saved_timings, struct sysinfo *s)
+{
+	struct dimm_attr_st decoded_dimm;
+
+	if (spd_decode_ddr3(&decoded_dimm, raw_spd) != SPD_STATUS_OK)
+		return 1;
+
+	if (IS_ENABLED(CONFIG_DEBUG_RAM_SETUP))
+		dram_print_spd_ddr3(&decoded_dimm);
+
+	if (!(decoded_dimm.width & (0x8 | 0x10))) {
+		printk(BIOS_ERR, "DIMM%d Unsupported width: x%d. Disabling dimm\n",
+			dimm_idx, s->dimms[dimm_idx].width);
+		return 1;
+	}
+	s->dimms[dimm_idx].width = (decoded_dimm.width >> 3) - 1;
+	/*
+	 * This boils down to:
+	 * "Except for the x16 configuration, all DDR2 devices have a
+	 * 1KB page size. For the x16 configuration, the page size is 2KB
+	 * for all densities except the 256Mb device, which has a 1KB page size."
+	 * Micron, 'TN-47-16 Designing for High-Density DDR2 Memory'
+	*/
+	s->dimms[dimm_idx].page_size = s->dimms[dimm_idx].width *
+		(1 << decoded_dimm.col_bits);
+
+	s->dimms[dimm_idx].n_banks = 1; /* Always 8 banks on ddr3?? */
+
+	s->dimms[dimm_idx].ranks = decoded_dimm.ranks;
+	s->dimms[dimm_idx].rows = decoded_dimm.row_bits;
+	s->dimms[dimm_idx].cols = decoded_dimm.col_bits;
+
+	saved_timings->min_tRAS =
+		MAX(saved_timings->min_tRAS, decoded_dimm.tRAS);
+	saved_timings->min_tRP =
+		MAX(saved_timings->min_tRP, decoded_dimm.tRP);
+	saved_timings->min_tRCD =
+		MAX(saved_timings->min_tRCD, decoded_dimm.tRCD);
+	saved_timings->min_tWR =
+		MAX(saved_timings->min_tWR, decoded_dimm.tWR);
+	saved_timings->min_tRFC =
+		MAX(saved_timings->min_tRFC, decoded_dimm.tRFC);
+	saved_timings->min_tWTR =
+		MAX(saved_timings->min_tWTR, decoded_dimm.tWTR);
+	saved_timings->min_tRRD =
+		MAX(saved_timings->min_tRRD, decoded_dimm.tRRD);
+	saved_timings->min_tRTP =
+		MAX(saved_timings->min_tRTP, decoded_dimm.tRTP);
+	saved_timings->min_tAA =
+		MAX(saved_timings->min_tAA, decoded_dimm.tAA);
+	saved_timings->cas_supported &= decoded_dimm.cas_supported;
+	return 0;
+}
diff --git a/src/northbridge/intel/x4x/x4x.h b/src/northbridge/intel/x4x/x4x.h
index fc7fbfe..43e37ef 100644
--- a/src/northbridge/intel/x4x/x4x.h
+++ b/src/northbridge/intel/x4x/x4x.h
@@ -323,6 +323,7 @@
 
 struct abs_timings {
 	u32 min_tclk;
+	u32 min_tAA;
 	u32 min_tRAS;
 	u32 min_tRP;
 	u32 min_tRCD;
@@ -349,6 +350,11 @@
 				struct abs_timings *saved_timings);
 int ddr2_save_dimminfo(u8 dimm_idx, u8 *raw_spd,
 		struct abs_timings *saved_timings, struct sysinfo *s);
+void select_cas_dramfreq_ddr3(struct sysinfo *s,
+			struct abs_timings *saved_timings);
+int ddr3_save_dimminfo(u8 dimm_idx, u8 *raw_spd,
+		struct abs_timings *saved_timings, struct sysinfo *s);
+
 
 struct acpi_rsdp;
 #ifndef __SIMPLE_DEVICE__

-- 
To view, visit https://review.coreboot.org/19869
To unsubscribe, visit https://review.coreboot.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I8dba19ca1e6e6b0a03b56c8de9633f9c1a2eb7d7
Gerrit-PatchSet: 1
Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-Owner: Arthur Heymans <arthur at aheymans.xyz>



More information about the coreboot-gerrit mailing list