[coreboot] [PATCH 2/3] rework i855GM/i855GME support

Andreas Schultz aschultz at tpip.net
Mon Aug 30 12:10:08 CEST 2010


Signed-off-by: Andreas Schultz <aschultz at tpip.net>
---
 src/northbridge/intel/i855/Kconfig       |   30 +
 src/northbridge/intel/i855/i855.h        |   76 +++
 src/northbridge/intel/i855/northbridge.c |   21 +
 src/northbridge/intel/i855/raminit.c     | 1036 +++++++++++++++++++++++++-----
 src/northbridge/intel/i855/raminit.h     |   14 +-
 5 files changed, 1002 insertions(+), 175 deletions(-)
 create mode 100644 src/northbridge/intel/i855/i855.h

diff --git a/src/northbridge/intel/i855/Kconfig b/src/northbridge/intel/i855/Kconfig
index 3d3443a..f5c2890 100644
--- a/src/northbridge/intel/i855/Kconfig
+++ b/src/northbridge/intel/i855/Kconfig
@@ -1,3 +1,33 @@
 config NORTHBRIDGE_INTEL_I855
 	bool
+        select HAVE_DEBUG_RAM_SETUP
 
+choice
+        prompt "Onboard graphics"
+        default I855_VIDEO_MB_8MB
+        depends on NORTHBRIDGE_INTEL_I855
+
+config I855_VIDEO_MB_OFF
+        bool "Disabled, 0KB"
+config I855_VIDEO_MB_1MB
+        bool "Enabled, 1MB"
+config I855_VIDEO_MB_4MB
+        bool "Enabled, 4MB"
+config I855_VIDEO_MB_8MB
+        bool "Enabled, 8MB"
+config I855_VIDEO_MB_16MB
+        bool "Enabled, 16MB"
+config I855_VIDEO_MB_32MB
+        bool "Enabled, 32MB"
+
+endchoice
+
+config VIDEO_MB
+        int
+        default 0   if I855_VIDEO_MB_OFF
+        default 1   if I855_VIDEO_MB_1MB
+        default 4   if I855_VIDEO_MB_4MB
+        default 8   if I855_VIDEO_MB_8MB
+        default 16  if I855_VIDEO_MB_16MB
+        default 32  if I855_VIDEO_MB_32MB
+        depends on NORTHBRIDGE_INTEL_I855
diff --git a/src/northbridge/intel/i855/i855.h b/src/northbridge/intel/i855/i855.h
new file mode 100644
index 0000000..0e11e23
--- /dev/null
+++ b/src/northbridge/intel/i855/i855.h
@@ -0,0 +1,76 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Travelping GmbH <info at travelping.com>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+/* Host-Hub Interface Bridge */
+#define GMC      0x50 /* GMCH Misc. Control (0x0000) */
+#define GGC      0x52 /* GMCH Graphics Control (0x0030) */
+#define DAFC     0x54 /* Device and Function Control (0x0000) */
+#define FDHC     0x58 /* Fixed Dram Hole Control */
+#define PAM0     0x59 /* Programmable Attribute Map #0 (0x00) */
+#define PAM1     0x5a /* Programmable Attribute Map #1 (0x00) */
+#define PAM2     0x5b /* Programmable Attribute Map #2 (0x00) */
+#define PAM3     0x5c /* Programmable Attribute Map #3 (0x00) */
+#define PAM4     0x5d /* Programmable Attribute Map #4 (0x00) */
+#define PAM5     0x5e /* Programmable Attribute Map #5 (0x00) */
+#define PAM6     0x5f /* Programmable Attribute Map #6 (0x00) */
+#define SMRAM    0x60 /* System Management RAM Control (0x02) */
+#define ESMRAMC  0x61 /* Extended System Management RAM Control (0x38) */
+#define ERRSTS   0x62 /* Error Status (0x0000) */
+#define ERRCMD   0x64 /* Error Command (0x0000) */
+#define SMICMD   0x66 /* SMI Command (0x00) */
+#define SCICMD   0x67 /* SCI Command (0x00) */
+#define SHIC     0x74 /* Secondary Host Interface Control Register (0x00006010) */
+#define ACAPID   0xA0 /* AGP Capability Identifier (0x00200002) */
+#define AGPSTAT  0xA4 /* AGP Status Register (0x1f000217) */
+#define AGPCMD   0xA8 /* AGP Command (0x0000) */
+#define AGPCTRL  0xB0 /* AGP Control (0x0000) */
+#define AFT      0xB2 /* AGP Functional Test (0xe9f0) */
+#define ATTBASE  0xB8 /* Aperture Translation Table Base (0x00000000) */
+#define AMTT     0xBC /* AGP Interface Multi Transaction Timer (0x00) */
+#define LPTT     0xBD /* Low Priority Transaction Timer (0x00) */
+#define HEM      0xF0 /* Host Error Control/Status/Obs (0x00000000) */
+
+/* Main Memory Control */
+#define DRB      0x40 /* DRAM Row 0-3 Boundary (0x00000000) */
+#define DRA      0x50 /* DRAM Row 0-3 Attribute (0x7777) */
+#define DRT      0x60 /* DRAM Timing (0x18004425) */
+#define PWRMG    0x68 /* DRAM Controller Power Management Control (0x00000000) */
+#define DRC      0x70 /* DRAM Controller Mode (0x00000081) */
+#define DTC      0xA0 /* DRAM Throttling Control (0x00000000) */
+
+#define DRT_CAS_MASK    (3 << 5)
+#define DRT_CAS_2_0     (1 << 5)
+#define DRT_CAS_2_5     (0 << 5)
+
+#define DRT_TRP_MASK    3
+#define DRT_TRP_4       0
+#define DRT_TRP_3       1
+#define DRT_TRP_2       2
+
+#define DRT_RCD_MASK    (3 << 2)
+#define DRT_RCD_4       (0 << 2)
+#define DRT_RCD_3       (1 << 2)
+#define DRT_RCD_2       (2 << 2)
+
+#define DRT_TRAS_MIN_MASK    (3 << 9)
+#define DRT_TRAS_MIN_8       (0 << 9)
+#define DRT_TRAS_MIN_7       (1 << 9)
+#define DRT_TRAS_MIN_6       (2 << 9)
+#define DRT_TRAS_MIN_5       (3 << 9)
diff --git a/src/northbridge/intel/i855/northbridge.c b/src/northbridge/intel/i855/northbridge.c
index 77d1564..220f722 100644
--- a/src/northbridge/intel/i855/northbridge.c
+++ b/src/northbridge/intel/i855/northbridge.c
@@ -25,6 +25,7 @@
 #include <stdint.h>
 #include <device/device.h>
 #include <device/pci.h>
+#include <device/pci_ids.h>
 #include <stdlib.h>
 #include <string.h>
 #include <bitops.h>
@@ -32,6 +33,26 @@
 #include <cpu/cpu.h>
 #include "chip.h"
 
+static void northbridge_init(device_t dev)
+{
+        printk(BIOS_SPEW, "Northbridge init\n");
+}
+
+static struct device_operations northbridge_operations = {
+        .read_resources = pci_dev_read_resources,
+        .set_resources = pci_dev_set_resources,
+        .enable_resources = pci_dev_enable_resources,
+        .init = northbridge_init,
+        .enable = 0,
+        .ops_pci = 0,
+};
+
+static const struct pci_driver northbridge_driver __pci_driver = {
+        .ops = &northbridge_operations,
+        .vendor = PCI_VENDOR_ID_INTEL,
+        .device = 0x3580,
+};
+
 static void ram_resource(device_t dev, unsigned long index,
         unsigned long basek, unsigned long sizek)
 {
diff --git a/src/northbridge/intel/i855/raminit.c b/src/northbridge/intel/i855/raminit.c
index 386eda1..e611d8e 100644
--- a/src/northbridge/intel/i855/raminit.c
+++ b/src/northbridge/intel/i855/raminit.c
@@ -18,256 +18,452 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
+#include <assert.h>
+#include <spd.h>
 #include <sdram_mode.h>
+#include <stdlib.h>
 #include <delay.h>
+#include "i855.h"
 
-#define dumpnorth() dump_pci_device(PCI_DEV(0, 0, 1))
-#define VG85X_MODE (SDRAM_BURST_4 | SDRAM_BURST_INTERLEAVED | SDRAM_CAS_2_5)
+#define VALIDATE_DIMM_COMPATIBILITY
 
-/**
-  * Set only what I need until it works, then make it figure things out on boot
-  * assumes only one dimm is populated
-  */
+/* Debugging macros. */
+#if CONFIG_DEBUG_RAM_SETUP
+#define PRINTK_DEBUG(x...)      printk(BIOS_DEBUG, x)
+#define DUMPNORTH()             dump_pci_device(NORTHBRIDGE_MMC)
+#else
+#define PRINTK_DEBUG(x...)
+#define DUMPNORTH()
+#endif
 
-static void sdram_set_registers(const struct mem_controller *ctrl)
+#define delay() udelay(200)
+
+#define VG85X_MODE (SDRAM_BURST_4 | SDRAM_BURST_INTERLEAVED | SDRAM_CAS_2_5)
+
+/* DRC[10:8] - Refresh Mode Select (RMS).
+ * 0x0 for Refresh Disabled (Self Refresh)
+ * 0x1 for Refresh interval 15.6 us for 133MHz
+ * 0x2 for Refresh interval 7.8 us for 133MHz
+ * 0x7 for Refresh interval 64 Clocks. (Fast Refresh Mode)
+ */
+#define RAM_COMMAND_REFRESH		0x1
+
+/* DRC[6:4] - SDRAM Mode Select (SMS). */
+#define RAM_COMMAND_SELF_REFRESH	0x0
+#define RAM_COMMAND_NOP			0x1
+#define RAM_COMMAND_PRECHARGE		0x2
+#define RAM_COMMAND_MRS			0x3
+#define RAM_COMMAND_EMRS		0x4
+#define RAM_COMMAND_CBR			0x6
+#define RAM_COMMAND_NORMAL		0x7
+
+/* DRC[29] - Initialization Complete (IC). */
+#define RAM_COMMAND_IC			0x1
+
+struct dimm_size {
+	unsigned int side1;
+	unsigned int side2;
+};
+
+/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
+/*				DEFINITIONS					  */
+/**********************************************************************************/
+
+static const uint32_t refresh_frequency[] = {
+	/* Relative frequency (array value) of each E7501 Refresh Mode Select
+	 * (RMS) value (array index)
+	 * 0 == least frequent refresh (longest interval between refreshes)
+	 * [0] disabled  -> 0
+	 * [1] 15.6 usec -> 2
+	 * [2]  7.8 usec -> 3
+	 * [3] 64   usec -> 1
+	 * [4] reserved  -> 0
+	 * [5] reserved  -> 0
+	 * [6] reserved  -> 0
+	 * [7] 64 clocks -> 4
+	 */
+	0, 2, 3, 1, 0, 0, 0, 4
+};
+
+static const uint32_t refresh_rate_map[] = {
+	/* Map the JEDEC spd refresh rates (array index) to i855 Refresh Mode
+	 * Select values (array value)
+	 * These are all the rates defined by JESD21-C Appendix D, Rev. 1.0
+	 * The i855 supports only 15.6 us (1), 7.8 us (2) and
+	 * 64 clock (481 ns) (7) refresh.
+	 * [0] ==  15.625 us -> 15.6 us
+	 * [1] ==   3.9   us -> 481  ns
+	 * [2] ==   7.8   us ->  7.8 us
+	 * [3] ==  31.3   us -> 15.6 us
+	 * [4] ==  62.5   us -> 15.6 us
+	 * [5] == 125     us -> 15.6 us
+	 */
+	1, 7, 2, 1, 1, 1
+};
+
+#define MAX_SPD_REFRESH_RATE ((sizeof(refresh_rate_map) / sizeof(uint32_t)) - 1)
+
+/*-----------------------------------------------------------------------------
+SPD functions.
+-----------------------------------------------------------------------------*/
+
+static void die_on_spd_error(int spd_return_value)
 {
-	/*
-	print_debug("Before configuration:\n");
-	dump_pci_devices();
-	*/
+	if (spd_return_value < 0)
+		PRINTK_DEBUG("Error reading SPD info: got %d\n", spd_return_value);
+/*
+	if (spd_return_value < 0)
+		die("Error reading SPD info\n");
+*/
 }
 
-static void spd_set_row_attributes(const struct mem_controller *ctrl)
+//----------------------------------------------------------------------------------
+// Function:            sdram_spd_get_page_size
+// Parameters:          dimm_socket_address - SMBus address of DIMM socket to interrogate
+// Return Value:        struct dimm_size - log2(page size) for each side of the DIMM.
+// Description:         Calculate the page size for each physical bank of the DIMM:
+//                                              log2(page size) = (# columns) + log2(data width)
+//
+//                                      NOTE: page size is the total number of data bits in a row.
+//
+static struct dimm_size sdram_spd_get_page_size(uint16_t dimm_socket_address)
 {
-	uint16_t dra_reg;
+	uint16_t module_data_width;
+	int value;
+	struct dimm_size pgsz;
 
-	dra_reg = 0x7733;
-	pci_write_config16(ctrl->d0, 0x50, dra_reg);
-}
+	pgsz.side1 = 0;
+	pgsz.side2 = 0;
 
-static void spd_set_dram_controller_mode(const struct mem_controller *ctrl)
-{
-	uint32_t drc_reg;
+	// Side 1
+	value = spd_read_byte(dimm_socket_address, SPD_NUM_COLUMNS);
+	die_on_spd_error(value);
 
-	/* drc_reg = 0x00009101; */
-	drc_reg = 0x00009901;
-	pci_write_config32(ctrl->d0, 0x70, drc_reg);
-}
+	pgsz.side1 = value & 0xf;	// # columns in bank 1
 
-static void spd_set_dram_timing(const struct mem_controller *ctrl)
-{
-	uint32_t drt_reg;
+	/* Get the module data width and convert it to a power of two */
+	value = spd_read_byte(dimm_socket_address, SPD_MODULE_DATA_WIDTH_MSB);
+	die_on_spd_error(value);
 
-	drt_reg = 0x2a004405;
-	pci_write_config32(ctrl->d0, 0x60, drt_reg);
-}
+	module_data_width = (value & 0xff) << 8;
 
-static void spd_set_dram_size(const struct mem_controller *ctrl)
-{
-	uint32_t drb_reg;
+	value = spd_read_byte(dimm_socket_address, SPD_MODULE_DATA_WIDTH_LSB);
+	die_on_spd_error(value);
 
-	drb_reg = 0x20202010;
-	pci_write_config32(ctrl->d0, 0x40, drb_reg);
-}
+	module_data_width |= (value & 0xff);
 
-static void spd_set_dram_pwr_management(const struct mem_controller *ctrl)
-{
-	uint32_t pwrmg_reg;
+	pgsz.side1 += log2(module_data_width);
 
-	pwrmg_reg = 0x10f10430;
-	pci_write_config32(ctrl->d0, 0x68, pwrmg_reg);
+	/* side two */
+	value = spd_read_byte(dimm_socket_address, SPD_NUM_DIMM_BANKS);
+	die_on_spd_error(value);
+
+/*
+	if (value > 2)
+		die("Bad SPD value\n");
+*/
+	if (value > 2)
+		PRINTK_DEBUG("Bad SPD value\n");
+
+	if (value == 2) {
+		pgsz.side2 = pgsz.side1;	// Assume symmetric banks until we know differently
+		value = spd_read_byte(dimm_socket_address, SPD_NUM_COLUMNS);
+		die_on_spd_error(value);
+
+		if ((value & 0xf0) != 0) {
+			// Asymmetric banks
+			pgsz.side2 -= value & 0xf;	/* Subtract out columns on side 1 */
+			pgsz.side2 += (value >> 4) & 0xf;	/* Add in columns on side 2 */
+		}
+	}
+
+	return pgsz;
 }
 
-static void spd_set_dram_throttle_control(const struct mem_controller *ctrl)
+//----------------------------------------------------------------------------------
+// Function:            sdram_spd_get_width
+// Parameters:          dimm_socket_address - SMBus address of DIMM socket to interrogate
+// Return Value:        dimm_size - width in bits of each DIMM side's DRAMs.
+// Description:         Read the width in bits of each DIMM side's DRAMs via SPD.
+//                                      (i.e. 4, 8, 16)
+//
+static struct dimm_size sdram_spd_get_width(uint16_t dimm_socket_address)
 {
-	uint32_t dtc_reg;
+	int value;
+	struct dimm_size width;
+
+	width.side1 = 0;
+	width.side2 = 0;
+
+	value = spd_read_byte(dimm_socket_address, SPD_PRIMARY_SDRAM_WIDTH);
+	die_on_spd_error(value);
+
+	width.side1 = value & 0x7f;	// Mask off bank 2 flag
+
+	if (value & 0x80) {
+		width.side2 = width.side1 << 1;	// Bank 2 exists and is double-width
+	} else {
+		// If bank 2 exists, it's the same width as bank 1
+		value = spd_read_byte(dimm_socket_address, SPD_NUM_DIMM_BANKS);
+		die_on_spd_error(value);
+
+#ifdef ROMCC_IF_BUG_FIXED
+		if (value == 2)
+			width.side2 = width.side1;
+#else
+		switch (value) {
+		case 2:
+			width.side2 = width.side1;
+			break;
+
+		default:
+			break;
+		}
+#endif
+	}
 
-	dtc_reg = 0x300aa2ff;
-	pci_write_config32(ctrl->d0, 0xa0, dtc_reg);
+	return width;
 }
 
-#define delay() udelay(200)
-
-/* if ram still doesn't work do this function */
-static void spd_set_undocumented_registers(const struct mem_controller *ctrl)
+//----------------------------------------------------------------------------------
+// Function:            spd_get_dimm_size
+// Parameters:          dimm_socket_address - SMBus address of DIMM socket to interrogate
+// Return Value:        dimm_size - log2(number of bits) for each side of the DIMM
+// Description:         Calculate the log base 2 size in bits of both DIMM sides.
+//                      log2(# bits) = (# columns) + log2(data width) +
+//                                         (# rows) + log2(banks per SDRAM)
+//
+//                      Note that it might be easier to use SPD byte 31 here, it has the
+//                      DIMM size as a multiple of 4MB.  The way we do it now we can size
+//                      both sides of an asymmetric dimm.
+//
+static struct dimm_size spd_get_dimm_size(unsigned dimm)
 {
-	/* 0:0.0 */
-	/*
-	pci_write_config32(PCI_DEV(0, 0, 0), 0x10, 0xe0000008);
-	pci_write_config32(PCI_DEV(0, 0, 0), 0x2c, 0x35808086);
-	pci_write_config32(PCI_DEV(0, 0, 0), 0x48, 0xfec10000);
-	pci_write_config32(PCI_DEV(0, 0, 0), 0x50, 0x00440100);
+	int value;
 
-	pci_write_config32(PCI_DEV(0, 0, 0), 0x58, 0x11111000);
+	// Start with log2(page size)
+	struct dimm_size sz = sdram_spd_get_page_size(dimm);
 
-	pci_write_config16(PCI_DEV(0, 0, 0), 0x52, 0x0002);
-	*/
-	pci_write_config16(PCI_DEV(0, 0, 0), 0x52, 0x0044);
-	/*
-	pci_write_config16(PCI_DEV(0, 0, 0), 0x52, 0x0000);
-	*/
-	pci_write_config32(PCI_DEV(0, 0, 0), 0x58, 0x33333000);
-	pci_write_config32(PCI_DEV(0, 0, 0), 0x5c, 0x33333333);
-	/*
-	pci_write_config32(PCI_DEV(0, 0, 0), 0x60, 0x0000390a);
-	pci_write_config32(PCI_DEV(0, 0, 0), 0x74, 0x02006056);
-	pci_write_config32(PCI_DEV(0, 0, 0), 0x78, 0x00800001);
-	*/
-	pci_write_config32(PCI_DEV(0, 0, 0), 0xa8, 0x00000001);
+	if (sz.side1 > 0) {
+		value = spd_read_byte(dimm, SPD_NUM_ROWS);
+		die_on_spd_error(value);
 
-	pci_write_config32(PCI_DEV(0, 0, 0), 0xbc, 0x00001020);
-	/*
-	pci_write_config32(PCI_DEV(0, 0, 0), 0xfc, 0x00000109);
-	*/
+		sz.side1 += value & 0xf;
 
-	/* 0:0.1 */
-	pci_write_config32(ctrl->d0, 0x74, 0x00000001);
-	pci_write_config32(ctrl->d0, 0x78, 0x001fe974);
-	pci_write_config32(ctrl->d0, 0x80, 0x00af0039);
-	pci_write_config32(ctrl->d0, 0x84, 0x0000033c);
-	pci_write_config32(ctrl->d0, 0x88, 0x00000010);
-	pci_write_config32(ctrl->d0, 0x98, 0xde5a868c);
-	pci_write_config32(ctrl->d0, 0x9c, 0x404e0046);
-	pci_write_config32(ctrl->d0, 0xa8, 0x00020e1a);
-	pci_write_config32(ctrl->d0, 0xb4, 0x0044cdac);
-	pci_write_config32(ctrl->d0, 0xb8, 0x000055d4);
-	pci_write_config32(ctrl->d0, 0xbc, 0x024acd38);
-	pci_write_config32(ctrl->d0, 0xc0, 0x00000003);
-
-	/* 0:0.3 */
-	/*
-	pci_write_config32(PCI_DEV(0, 0, 3), 0x2c, 0x35858086);
-	pci_write_config32(PCI_DEV(0, 0, 3), 0x44, 0x11110000);
-	pci_write_config32(PCI_DEV(0, 0, 3), 0x48, 0x09614a3c);
-	pci_write_config32(PCI_DEV(0, 0, 3), 0x4c, 0x4b09604a);
-	pci_write_config32(PCI_DEV(0, 0, 3), 0x50, 0x00000962);
-	pci_write_config32(PCI_DEV(0, 0, 3), 0x5c, 0x0b023331);
-	pci_write_config32(PCI_DEV(0, 0, 3), 0x6c, 0x0000402e);
-	pci_write_config32(PCI_DEV(0, 0, 3), 0x78, 0xe7c70f7f);
-	pci_write_config32(PCI_DEV(0, 0, 3), 0x7c, 0x0284007f);
-	pci_write_config32(PCI_DEV(0, 0, 3), 0x84, 0x000000ef);
-	*/
+		if (sz.side2 > 0) {
+			// Double-sided DIMM
+			if (value & 0xF0)
+				sz.side2 += value >> 4;	// Asymmetric
+			else
+				sz.side2 += value;	// Symmetric
+		}
 
-	/*
-	pci_write_config16(PCI_DEV(0, 0, 3), 0xc0, 0x0200);
-	pci_write_config16(PCI_DEV(0, 0, 3), 0xc0, 0x0400);
-	*/
+		value = spd_read_byte(dimm, SPD_NUM_BANKS_PER_SDRAM);
+		die_on_spd_error(value);
 
-	/*
-	pci_write_config32(PCI_DEV(0, 0, 3), 0xc4, 0x00000000);
-	pci_write_config32(PCI_DEV(0, 0, 3), 0xd8, 0xff00c308);
-	pci_write_config32(PCI_DEV(0, 0, 3), 0xdc, 0x00000025);
-	pci_write_config32(PCI_DEV(0, 0, 3), 0xe0, 0x001f002a);
-	pci_write_config32(PCI_DEV(0, 0, 3), 0xe4, 0x009f0098);
-	pci_write_config32(PCI_DEV(0, 0, 3), 0xec, 0x00000400);
-	pci_write_config32(PCI_DEV(0, 0, 3), 0xf0, 0xc0000000);
-	*/
-}
+		value = log2(value);
+		sz.side1 += value;
+		if (sz.side2 > 0)
+			sz.side2 += value;
+	}
 
-static void sdram_set_spd_registers(const struct mem_controller *ctrl)
-{
-	spd_set_row_attributes(ctrl);
-	spd_set_dram_controller_mode(ctrl);
-	spd_set_dram_timing(ctrl);
-	spd_set_dram_size(ctrl);
-	spd_set_dram_pwr_management(ctrl);
-	spd_set_dram_throttle_control(ctrl);
-	spd_set_undocumented_registers(ctrl);
+	return sz;
 }
 
-static void ram_command(const struct mem_controller *ctrl,
-			uint8_t command,
-			uint32_t addr)
+//----------------------------------------------------------------------------------
+// Function:            spd_get_supported_dimms
+// Parameters:          ctrl - PCI addresses of memory controller functions, and
+//                                              SMBus addresses of DIMM slots on the mainboard
+// Return Value:        uint8_t - a bitmask indicating which sockets contain a compatible DIMM.
+// Description:         Scan for compatible DIMMs.
+//
+static uint8_t spd_get_supported_dimms(const struct mem_controller *ctrl)
 {
-	uint32_t drc_reg;
+	int i;
+	uint8_t dimm_mask = 0;
+
+	for (i = 0; i < DIMM_SOCKETS; i++) {
+		uint16_t dimm = ctrl->channel0[i];
+
+#ifdef VALIDATE_DIMM_COMPATIBILITY
+		struct dimm_size page_size;
+		struct dimm_size sdram_width;
+#endif
+		int spd_value;
+
+		if (dimm == 0)
+			continue;	// No such socket on this mainboard
+
+		if (spd_read_byte(dimm, SPD_MEMORY_TYPE) != SPD_MEMORY_TYPE_SDRAM_DDR)
+			continue;
+
+#ifdef VALIDATE_DIMM_COMPATIBILITY
+		if ((spd_value = spd_read_byte(dimm, SPD_MODULE_VOLTAGE)) != SPD_VOLTAGE_SSTL2) {
+			PRINTK_DEBUG("Skipping DIMM with unsupported voltage: %02x\n", spd_value);
+			continue;	// Unsupported voltage
+		}
+
+/*
+		// E7501 does not support unregistered DIMMs
+		spd_value = spd_read_byte(dimm, SPD_MODULE_ATTRIBUTES);
+		if (!(spd_value & MODULE_REGISTERED) || (spd_value < 0)) {
+			PRINTK_DEBUG("Skipping unregistered DIMM: %02x\n", spd_value);
+			continue;
+		}
+*/
+
+		page_size = sdram_spd_get_page_size(dimm);
+		sdram_width = sdram_spd_get_width(dimm);
+
+		// Validate DIMM page size
+		// The i855 only supports page sizes of 4, 8, 16 KB per channel
+		// NOTE:  4 KB =  32 Kb = 2^15
+		//       16 KB = 128 Kb = 2^17
+
+		if ((page_size.side1 < 15) || (page_size.side1 > 17)) {
+			PRINTK_DEBUG("Skipping DIMM with unsupported page size: %d\n", page_size.side1);
+			continue;
+		}
+
+		// If DIMM is double-sided, verify side2 page size
+		if (page_size.side2 != 0) {
+			if ((page_size.side2 < 15) || (page_size.side2 > 17)) {
+				PRINTK_DEBUG("Skipping DIMM with unsupported page size: %d\n", page_size.side2);
+				continue;
+			}
+		}
+		// Validate SDRAM width
+		// The i855 only supports x8 and x16 devices
+		if ((sdram_width.side1 != 8) && (sdram_width.side1 != 16)) {
+			PRINTK_DEBUG("Skipping DIMM with unsupported width: %d\n", sdram_width.side2);
+			continue;
+		}
+
+		// If DIMM is double-sided, verify side2 width
+		if (sdram_width.side2 != 0) {
+			if ((sdram_width.side2 != 8)
+			    && (sdram_width.side2 != 16)) {
+				PRINTK_DEBUG("Skipping DIMM with unsupported width: %d\n", sdram_width.side2);
+				continue;
+			}
+		}
+#endif
+		// Made it through all the checks, this DIMM is usable
+		dimm_mask |= (1 << i);
+	}
 
-	drc_reg = pci_read_config32(ctrl->d0, 0x70);
-	drc_reg &= ~(7 << 4);
-	drc_reg |= (command << 4);
-	pci_write_config8(ctrl->d0, 0x70, drc_reg);
-	read32(addr);
+	return dimm_mask;
 }
 
-static void ram_command_mrs(const struct mem_controller *ctrl,
-							uint8_t command,
-							uint32_t mode,
-							uint32_t addr)
+/*-----------------------------------------------------------------------------
+DIMM-initialization functions.
+-----------------------------------------------------------------------------*/
+static void do_ram_command(uint8_t command, uint16_t jedec_mode_bits)
 {
-	uint32_t drc_reg;
-	uint32_t adjusted_mode;
-
-	drc_reg = pci_read_config32(ctrl->d0, 0x70);
-	drc_reg &= ~(7 << 4);
-	drc_reg |= (command << 4);
-	pci_write_config8(ctrl->d0, 0x70, drc_reg);
-	/* Host address lines [13:3] map to DIMM address lines [11, 9:0] */
-	adjusted_mode = ((mode & 0x800) << (13 - 11)) | ((mode & 0x3ff) << (12 - 9));
-	print_debug("Setting mode: ");
-	print_debug_hex32(adjusted_mode + addr);
-	print_debug("\n");
-	read32(adjusted_mode + addr);
+	int i;
+	u32 reg32;
+	uint8_t dimm_start_32M_multiple = 0;
+	uint16_t i855_mode_bits = jedec_mode_bits;
+
+	/* Configure the RAM command. */
+	reg32 = pci_read_config32(NORTHBRIDGE_MMC, DRC);
+	reg32 &= ~(7 << 4);
+	reg32 |= (command << 4);
+	PRINTK_DEBUG("  Sending RAM command 0x%08x\n", reg32);
+	pci_write_config32(NORTHBRIDGE_MMC, DRC, reg32);
+
+        // RAM_COMMAND_NORMAL is an exception.
+        // It affects only the memory controller and does not need to be "sent" to the DIMMs.
+
+        if (command != RAM_COMMAND_NORMAL) {
+
+                // Send the command to all DIMMs by accessing a memory location within each
+                // NOTE: for mode select commands, some of the location address bits
+                // are part of the command
+
+                // Map JEDEC mode bits to i855
+                if (command == RAM_COMMAND_MRS || command == RAM_COMMAND_EMRS) {
+			/* Host address lines [13:3] map to DIMM address lines [11, 9:0] */
+			i855_mode_bits = ((jedec_mode_bits & 0x800) << (13 - 11)) | ((jedec_mode_bits & 0x3ff) << (12 - 9));
+                }
+
+                for (i = 0; i < (DIMM_SOCKETS * 2); ++i) {
+                        uint8_t dimm_end_32M_multiple = pci_read_config8(NORTHBRIDGE_MMC, DRB + i);
+                        if (dimm_end_32M_multiple > dimm_start_32M_multiple) {
+
+                                uint32_t dimm_start_address = dimm_start_32M_multiple << 25;
+				PRINTK_DEBUG("  Sending RAM command to 0x%08x\n", dimm_start_address + i855_mode_bits);
+                                read32(dimm_start_address + i855_mode_bits);
+
+                                // Set the start of the next DIMM
+                                dimm_start_32M_multiple = dimm_end_32M_multiple;
+                        }
+		}
+	}
 }
 
 static void set_initialize_complete(const struct mem_controller *ctrl)
 {
 	uint32_t drc_reg;
 
-	drc_reg = pci_read_config32(ctrl->d0, 0x70);
+	drc_reg = pci_read_config32(NORTHBRIDGE_MMC, DRC);
 	drc_reg |= (1 << 29);
-	pci_write_config32(ctrl->d0, 0x70, drc_reg);
+	pci_write_config32(NORTHBRIDGE_MMC, DRC, drc_reg);
 }
 
 static void sdram_enable(int controllers, const struct mem_controller *ctrl)
 {
 	int i;
-	uint32_t rank1 = (1 << 30) / 2;
+
 	print_debug("Ram enable 1\n");
 	delay();
 	delay();
 
-	print_debug("Ram enable 2\n");
-	ram_command(ctrl, 1, 0);
-	ram_command(ctrl, 1, rank1);
+	/* NOP command */
+	PRINTK_DEBUG(" NOP\n");
+	do_ram_command(RAM_COMMAND_NOP, 0);
+	delay();
 	delay();
 	delay();
 
-	print_debug("Ram enable 3\n");
-	ram_command(ctrl, 2, 0);
-	ram_command(ctrl, 2, rank1);
+	/* Pre-charge all banks (at least 200 us after NOP) */
+	PRINTK_DEBUG(" Pre-charging all banks\n");
+	do_ram_command(RAM_COMMAND_PRECHARGE, 0);
+	delay();
 	delay();
 	delay();
 
 	print_debug("Ram enable 4\n");
-	ram_command_mrs(ctrl, 4, SDRAM_EXTMODE_DLL_ENABLE, 0);
-	ram_command_mrs(ctrl, 4, SDRAM_EXTMODE_DLL_ENABLE, rank1);
+	do_ram_command(RAM_COMMAND_EMRS, SDRAM_EXTMODE_DLL_ENABLE);
+	delay();
 	delay();
 	delay();
 
 	print_debug("Ram enable 5\n");
-	ram_command_mrs(ctrl, 3, VG85X_MODE | SDRAM_MODE_DLL_RESET, 0);
-	ram_command_mrs(ctrl, 3, VG85X_MODE | SDRAM_MODE_DLL_RESET, rank1);
+	do_ram_command(RAM_COMMAND_MRS, VG85X_MODE | SDRAM_MODE_DLL_RESET);
 
 	print_debug("Ram enable 6\n");
-	ram_command(ctrl, 2, 0);
-	ram_command(ctrl, 2, rank1);
+	do_ram_command(RAM_COMMAND_PRECHARGE, 0);
+	delay();
 	delay();
 	delay();
 
-	print_debug("Ram enable 7\n");
+	/* 8 CBR refreshes (Auto Refresh) */
+	PRINTK_DEBUG(" 8 CBR refreshes\n");
 	for(i = 0; i < 8; i++) {
-		ram_command(ctrl, 6, 0);
-		ram_command(ctrl, 6, rank1);
+		do_ram_command(RAM_COMMAND_CBR, 0);
+		delay();
 		delay();
 		delay();
 	}
 
 	print_debug("Ram enable 8\n");
-	ram_command_mrs(ctrl, 3, VG85X_MODE | SDRAM_MODE_NORMAL, 0);
-	ram_command_mrs(ctrl, 3, VG85X_MODE | SDRAM_MODE_NORMAL, rank1);
+	do_ram_command(RAM_COMMAND_MRS, VG85X_MODE | SDRAM_MODE_NORMAL);
 
-	print_debug("Ram enable 9\n");
-	ram_command(ctrl, 7, 0);
-	ram_command(ctrl, 7, rank1);
+	/* Set GME-M Mode Select bits back to NORMAL operation mode */
+	PRINTK_DEBUG(" Normal operation mode\n");
+	do_ram_command(RAM_COMMAND_NORMAL, 0);
+	delay();
 	delay();
 	delay();
 
@@ -277,6 +473,8 @@ static void sdram_enable(int controllers, const struct mem_controller *ctrl)
 	delay();
 	delay();
 	delay();
+	delay();
+	delay();
 
 	print_debug("After configuration:\n");
 	/* dump_pci_devices(); */
@@ -287,3 +485,497 @@ static void sdram_enable(int controllers, const struct mem_controller *ctrl)
 	ram_check(0x100000, 0x40000000);
 	*/
 }
+
+/*-----------------------------------------------------------------------------
+DIMM-independant configuration functions.
+-----------------------------------------------------------------------------*/
+
+/**
+  * Set only what I need until it works, then make it figure things out on boot
+  * assumes only one dimm is populated
+  */
+
+static void sdram_set_registers(const struct mem_controller *ctrl)
+{
+	/*
+	print_debug("Before configuration:\n");
+	dump_pci_devices();
+	*/
+}
+
+static void spd_set_row_attributes(const struct mem_controller *ctrl, uint8_t dimm_mask)
+{
+	int i;
+	uint16_t row_attributes = 0;
+
+	for (i = 0; i < DIMM_SOCKETS; i++) {
+		uint16_t dimm = ctrl->channel0[i];
+		struct dimm_size page_size;
+		struct dimm_size sdram_width;
+
+		if (!(dimm_mask & (1 << i))) {
+			row_attributes |= 0x77 << (i << 3);
+			continue;	// This DIMM not usable
+		}
+
+		// Get the relevant parameters via SPD
+		page_size = sdram_spd_get_page_size(dimm);
+		sdram_width = sdram_spd_get_width(dimm);
+
+		// Update the DRAM Row Attributes.
+		// Page size is encoded as log2(page size in bits) - log2(2 KB) or 4 KB == 1, 8 KB == 3, 16KB == 3
+		// NOTE:  2 KB =  16 Kb = 2^14
+		row_attributes |= (page_size.side1 - 14) << (i << 3);	// Side 1 of each DIMM is an EVEN row
+
+		if (sdram_width.side2 > 0)
+			row_attributes |= (page_size.side2 - 14) << ((i << 3) + 4);	// Side 2 is ODD
+		else
+			row_attributes |= 7 << ((i << 3) + 4);
+		/* go to the next DIMM */
+	}
+
+	PRINTK_DEBUG("DRA: %04x\n", row_attributes);
+
+	/* Write the new row attributes register */
+	pci_write_config16(NORTHBRIDGE_MMC, DRA, row_attributes);
+}
+
+static void spd_set_dram_controller_mode(const struct mem_controller *ctrl, uint8_t dimm_mask)
+{
+	int i;
+
+	// Initial settings
+	u32 controller_mode = pci_read_config32(NORTHBRIDGE_MMC, DRC);
+	u32 system_refresh_mode = (controller_mode >> 7) & 7;
+
+	controller_mode |= (1 << 20);  // ECC
+	controller_mode |= (1 << 15);  // RAS lockout
+	controller_mode |= (1 << 12);  // Address Tri-state enable (ADRTRIEN), FIXME: how is this detected?????
+	controller_mode |= (2 << 10);  // FIXME: Undocumented, really needed?????
+
+	for (i = 0; i < DIMM_SOCKETS; i++) {
+		uint16_t dimm = ctrl->channel0[i];
+		uint32_t dimm_refresh_mode;
+		int value;
+		u8 tRCD, tRP;
+
+		if (!(dimm_mask & (1 << i))) {
+			continue;	// This DIMM not usable
+		}
+
+		// Disable ECC mode if any one of the DIMMs does not support ECC
+		value = spd_read_byte(dimm, SPD_DIMM_CONFIG_TYPE);
+		die_on_spd_error(value);
+		if (value != ERROR_SCHEME_ECC)
+			controller_mode &= ~(3 << 20);
+
+		value = spd_read_byte(dimm, SPD_REFRESH);
+		die_on_spd_error(value);
+		value &= 0x7f;	// Mask off self-refresh bit
+		if (value > MAX_SPD_REFRESH_RATE) {
+			print_err("unsupported refresh rate\n");
+			continue;
+		}
+		// Get the appropriate i855 refresh mode for this DIMM
+		dimm_refresh_mode = refresh_rate_map[value];
+		if (dimm_refresh_mode > 7) {
+			print_err("unsupported refresh rate\n");
+			continue;
+		}
+		// If this DIMM requires more frequent refresh than others,
+		// update the system setting
+		if (refresh_frequency[dimm_refresh_mode] >
+		    refresh_frequency[system_refresh_mode])
+			system_refresh_mode = dimm_refresh_mode;
+
+		/* FIXME: is this correct? */
+		tRCD = spd_read_byte(dimm, SPD_tRCD);
+		tRP = spd_read_byte(dimm, SPD_tRP);
+		if (tRCD != tRP) {
+			PRINTK_DEBUG(" Disabling RAS lockouk due to tRCD (%d) != tRP (%d)\n", tRCD, tRP);
+			controller_mode &= ~(1 << 15);
+		}
+
+		/* go to the next DIMM */
+	}
+
+	controller_mode &= ~(7 << 7);
+	controller_mode |= (system_refresh_mode << 7);
+	PRINTK_DEBUG("DRC: %08x\n", controller_mode);
+
+	pci_write_config32(NORTHBRIDGE_MMC, DRC, controller_mode);
+}
+
+static void spd_set_dram_timing(const struct mem_controller *ctrl, uint8_t dimm_mask)
+{
+	int i;
+	u32 dram_timing;
+
+	// CAS# latency bitmasks in SPD_ACCEPTABLE_CAS_LATENCIES format
+	// NOTE: i82822 supports only 2.0 and 2.5
+	uint32_t system_compatible_cas_latencies = SPD_CAS_LATENCY_2_0 | SPD_CAS_LATENCY_2_5;
+	uint8_t slowest_row_precharge = 0;
+	uint8_t slowest_ras_cas_delay = 0;
+	uint8_t slowest_active_to_precharge_delay = 0;
+
+	for (i = 0; i < DIMM_SOCKETS; i++) {
+		uint16_t dimm = ctrl->channel0[i];
+		int value;
+		uint32_t current_cas_latency;
+		uint32_t dimm_compatible_cas_latencies;
+		if (!(dimm_mask & (1 << i)))
+			continue;	// This DIMM not usable
+
+		value = spd_read_byte(dimm, SPD_ACCEPTABLE_CAS_LATENCIES);
+		PRINTK_DEBUG("SPD_ACCEPTABLE_CAS_LATENCIES: %d\n", value);
+		die_on_spd_error(value);
+
+		dimm_compatible_cas_latencies = value & 0x7f;	// Start with all supported by DIMM
+		PRINTK_DEBUG("dimm_compatible_cas_latencies #1: %d\n", dimm_compatible_cas_latencies);
+
+		current_cas_latency = 1 << log2(dimm_compatible_cas_latencies);	// Max supported by DIMM
+		PRINTK_DEBUG("current_cas_latency: %d\n", current_cas_latency);
+
+		// Can we support the highest CAS# latency?
+		value = spd_read_byte(dimm, SPD_MIN_CYCLE_TIME_AT_CAS_MAX);
+		die_on_spd_error(value);
+		PRINTK_DEBUG("SPD_MIN_CYCLE_TIME_AT_CAS_MAX: %d.%d\n", value >> 4, value & 0xf);
+
+		// NOTE: At 133 MHz, 1 clock == 7.52 ns
+		if (value > 0x75) {
+			// Our bus is too fast for this CAS# latency
+			// Remove it from the bitmask of those supported by the DIMM that are compatible
+			dimm_compatible_cas_latencies &= ~current_cas_latency;
+			PRINTK_DEBUG("dimm_compatible_cas_latencies #2: %d\n", dimm_compatible_cas_latencies);
+		}
+		// Can we support the next-highest CAS# latency (max - 0.5)?
+
+		current_cas_latency >>= 1;
+		if (current_cas_latency != 0) {
+			value = spd_read_byte(dimm, SPD_SDRAM_CYCLE_TIME_2ND);
+			die_on_spd_error(value);
+			PRINTK_DEBUG("SPD_SDRAM_CYCLE_TIME_2ND: %d.%d\n", value >> 4, value & 0xf);
+			if (value > 0x75) {
+				dimm_compatible_cas_latencies &= ~current_cas_latency;
+				PRINTK_DEBUG("dimm_compatible_cas_latencies #2: %d\n", dimm_compatible_cas_latencies);
+			}
+		}
+		// Can we support the next-highest CAS# latency (max - 1.0)?
+		current_cas_latency >>= 1;
+		if (current_cas_latency != 0) {
+			value = spd_read_byte(dimm, SPD_SDRAM_CYCLE_TIME_3RD);
+			PRINTK_DEBUG("SPD_SDRAM_CYCLE_TIME_3RD: %d.%d\n", value >> 4, value & 0xf);
+			die_on_spd_error(value);
+			if (value > 0x75) {
+				dimm_compatible_cas_latencies &= ~current_cas_latency;
+				PRINTK_DEBUG("dimm_compatible_cas_latencies #2: %d\n", dimm_compatible_cas_latencies);
+			}
+		}
+		// Restrict the system to CAS# latencies compatible with this DIMM
+		system_compatible_cas_latencies &= dimm_compatible_cas_latencies;
+
+		value = spd_read_byte(dimm, SPD_MIN_ROW_PRECHARGE_TIME);
+		die_on_spd_error(value);
+		if (value > slowest_row_precharge)
+			slowest_row_precharge = value;
+
+		value = spd_read_byte(dimm, SPD_MIN_RAS_TO_CAS_DELAY);
+		die_on_spd_error(value);
+		if (value > slowest_ras_cas_delay)
+			slowest_ras_cas_delay = value;
+
+		value = spd_read_byte(dimm, SPD_MIN_ACTIVE_TO_PRECHARGE_DELAY);
+		die_on_spd_error(value);
+		if (value > slowest_active_to_precharge_delay)
+			slowest_active_to_precharge_delay = value;
+
+		/* go to the next DIMM */
+	}
+	PRINTK_DEBUG("CAS latency: %d\n", system_compatible_cas_latencies);
+
+	dram_timing = pci_read_config32(NORTHBRIDGE_MMC, DRT);
+	dram_timing &= ~(DRT_CAS_MASK | DRT_TRP_MASK | DRT_RCD_MASK);
+	PRINTK_DEBUG("DRT: %08x\n", dram_timing);
+
+	if (system_compatible_cas_latencies & SPD_CAS_LATENCY_2_0) {
+		dram_timing |= DRT_CAS_2_0;
+	} else if (system_compatible_cas_latencies & SPD_CAS_LATENCY_2_5) {
+		dram_timing |= DRT_CAS_2_5;
+	} else
+		die("No CAS# latencies compatible with all DIMMs!!\n");
+
+	uint32_t current_cas_latency = dram_timing & DRT_CAS_MASK;
+
+	/* tRP */
+
+	PRINTK_DEBUG("slowest_row_precharge: %d.%d\n", slowest_row_precharge >> 2, slowest_row_precharge & 0x3);
+	// i855 supports only 2, 3 or 4 clocks for tRP
+	if (slowest_row_precharge > ((30 << 2)))
+		die("unsupported DIMM tRP");	//  > 30.0 ns: 5 or more clocks
+	else if (slowest_row_precharge > ((22 << 2) | (2 << 0)))
+		dram_timing |= DRT_TRP_4;	//  > 22.5 ns: 4 or more clocks
+	else if (slowest_row_precharge > (15 << 2))
+		dram_timing |= DRT_TRP_3;	//  > 15.0 ns: 3 clocks
+	else
+		dram_timing |= DRT_TRP_2;	// <= 15.0 ns: 2 clocks
+
+	/*  tRCD */
+
+	PRINTK_DEBUG("slowest_ras_cas_delay: %d.%d\n", slowest_ras_cas_delay >> 2, slowest_ras_cas_delay & 0x3);
+	// i855 supports only 2, 3 or 4 clocks for tRCD
+	if (slowest_ras_cas_delay > ((30 << 2)))
+		die("unsupported DIMM tRCD");	//  > 30.0 ns: 5 or more clocks
+	else if (slowest_ras_cas_delay > ((22 << 2) | (2 << 0)))
+		dram_timing |= DRT_RCD_4;	//  > 22.5 ns: 4 or more clocks
+	else if (slowest_ras_cas_delay > (15 << 2))
+		dram_timing |= DRT_RCD_3;	//  > 15.0 ns: 3 clocks
+	else
+		dram_timing |= DRT_RCD_2;	// <= 15.0 ns: 2 clocks
+
+	/* tRAS, min */
+
+	PRINTK_DEBUG("slowest_active_to_precharge_delay: %d\n", slowest_active_to_precharge_delay);
+	// i855 supports only 5, 6, 7 or 8 clocks for tRAS
+	// 5 clocks ~= 37.6 ns, 6 clocks ~= 45.1 ns, 7 clocks ~= 52.6 ns, 8 clocks ~= 60.1 ns
+	if (slowest_active_to_precharge_delay > 60)
+		die("unsupported DIMM tRAS");	// > 52 ns:      8 or more clocks
+	else if (slowest_active_to_precharge_delay > 52)
+		dram_timing |= DRT_TRAS_MIN_8;	// 46-52 ns:     7 clocks
+	else if (slowest_active_to_precharge_delay > 45)
+		dram_timing |= DRT_TRAS_MIN_7;	// 46-52 ns:     7 clocks
+	else if (slowest_active_to_precharge_delay > 37)
+		dram_timing |= DRT_TRAS_MIN_6;	// 38-45 ns:     6 clocks
+	else
+		dram_timing |= DRT_TRAS_MIN_5;	// < 38 ns:      5 clocks
+
+	/* FIXME: guess work starts here...
+	 *
+	 * Intel refers to DQ turn-arround values for back to calculate the values,
+	 * but i have no idea what this means
+	 */
+
+	/*
+	 * Back to Back Read-Write command spaceing (DDR, different Rows/Bank)
+	 */
+	/* Set to a 3 clock back to back read to write turn around.
+	 *  2 is a good delay if the CAS latency is 2.0 */
+	dram_timing &= ~(3 << 28);
+	if (current_cas_latency == DRT_CAS_2_0)
+		dram_timing |= (2 << 28);	// 2 clocks
+	else
+		dram_timing |= (1 << 28);	// 3 clocks
+
+	/*
+	 * Back to Back Read-Write command spaceing (DDR, same or different Rows/Bank)
+	 */
+	dram_timing &= ~(3 << 26);
+	if (current_cas_latency == DRT_CAS_2_0)
+		dram_timing |= (2 << 26);	// 5 clocks
+	else
+		dram_timing |= (1 << 26);	// 6 clocks
+
+	/*
+	 * Back To Back Read-Read commands spacing (DDR, different Rows):
+	 */
+	dram_timing &= ~(1 << 25);
+	dram_timing |= (1 << 25);	// 3 clocks
+
+	PRINTK_DEBUG("DRT: %08x\n", dram_timing);
+	pci_write_config32(NORTHBRIDGE_MMC, DRT, dram_timing);
+}
+
+static void spd_set_dram_size(const struct mem_controller *ctrl, uint8_t dimm_mask)
+{
+	int i;
+	int total_dram = 0;
+	uint32_t drb_reg = 0;
+
+	for (i = 0; i < DIMM_SOCKETS; i++) {
+		uint16_t dimm = ctrl->channel0[i];
+		struct dimm_size sz;
+
+		if (!(dimm_mask & (1 << i))) {
+			/* fill values even for not present DIMMs */
+			drb_reg |= (total_dram << (i * 16));
+			drb_reg |= (total_dram << ((i * 16) + 8));
+
+			continue;	// This DIMM not usable
+		}
+		sz = spd_get_dimm_size(dimm);
+
+		total_dram += (1 << (sz.side1 - 28));
+		drb_reg |= (total_dram << (i * 16));
+
+		total_dram += (1 << (sz.side2 - 28));
+		drb_reg |= (total_dram << ((i * 16) + 8));
+	}
+	PRINTK_DEBUG("DRB: %08x\n", drb_reg);
+	pci_write_config32(NORTHBRIDGE_MMC, DRB, drb_reg);
+}
+
+
+static void spd_set_dram_pwr_management(const struct mem_controller *ctrl)
+{
+	uint32_t pwrmg_reg;
+
+	pwrmg_reg = 0x10f10430;
+	pci_write_config32(NORTHBRIDGE_MMC, PWRMG, pwrmg_reg);
+}
+
+static void spd_set_dram_throttle_control(const struct mem_controller *ctrl)
+{
+	uint32_t dtc_reg = 0;
+
+	/* DDR SDRAM Throttle Mode (TMODE):
+	 *   0011 = Both Rank and GMCH Thermal Sensor based throttling is enabled. When the external SO-
+	 *          DIMM Thermal Sensor is Tripped DDR SDRAM Throttling begins based on the setting in RTT
+	 */
+	dtc_reg |= (3 << 28);
+
+	/* Read Counter Based Power Throttle Control (RCTC): 
+	 *   0 = 85%
+	 */
+	dtc_reg |= (0 << 24);
+
+	/* Write Counter Based Power Throttle Control (WCTC): 
+	 *   0 = 85%
+	 */
+	dtc_reg |= (0 << 20);
+
+	/* Read Thermal Based Power Throttle Control (RTTC):
+	 *   0xA = 20%
+	 */
+	dtc_reg |= (0xA << 16);
+
+	/* Write Thermal Based Power Throttle Control (WTTC):
+	 *   0xA = 20%
+	 */
+	dtc_reg |= (0xA << 12);
+
+	/* Counter Based Throttle Lock (CTLOCK): */
+	dtc_reg |= (0 << 11);
+
+	/* Thermal Throttle Lock (TTLOCK): */
+	dtc_reg |= (0 << 10);
+
+	/* Thermal Power Throttle Control fields Enable: */
+	dtc_reg |= (1 << 9);
+
+	/* High Priority Stream Throttling Enable: */
+	dtc_reg |= (0 << 8);
+
+	/* Global DDR SDRAM Sampling Window (GDSW): */
+	dtc_reg |= 0xff;
+	PRINTK_DEBUG("DTC: %08x\n", dtc_reg);
+	pci_write_config32(NORTHBRIDGE_MMC, DTC, dtc_reg);
+}
+
+static void spd_update(const struct mem_controller *ctrl, u8 reg, u32 new_value)
+{
+	u32 value1 = pci_read_config32(ctrl->d0, reg);
+	pci_write_config32(ctrl->d0, reg, new_value);
+	u32 value2 = pci_read_config32(ctrl->d0, reg);
+	PRINTK_DEBUG("update reg %02x, old: %08x, new: %08x, read back: %08x\n", reg, value1, new_value, value2);
+}	
+
+/* if ram still doesn't work do this function */
+static void spd_set_undocumented_registers(const struct mem_controller *ctrl)
+{
+	spd_update(ctrl, 0x74, 0x00000001);
+	spd_update(ctrl, 0x78, 0x001fe974);
+	spd_update(ctrl, 0x80, 0x00af0039);
+	spd_update(ctrl, 0x84, 0x0000033c);
+	spd_update(ctrl, 0x88, 0x00000010);
+
+	spd_update(ctrl, 0xc0, 0x00000003);
+}
+
+static void northbridge_set_registers(void)
+{
+	u16 value;
+	int video_memory = 0;
+
+	printk(BIOS_DEBUG, "Setting initial Northbridge registers....\n");
+
+	/* Set the value for Fixed DRAM Hole Control Register */
+	pci_write_config8(NORTHBRIDGE, FDHC, 0x00);
+
+	/* Set the value for Programable Attribute Map Registers
+	 * Ideally, this should be R/W for as many ranges as possible.
+	 */
+	pci_write_config8(NORTHBRIDGE, PAM0, 0x30);
+	pci_write_config8(NORTHBRIDGE, PAM1, 0x33);
+	pci_write_config8(NORTHBRIDGE, PAM2, 0x33);
+	pci_write_config8(NORTHBRIDGE, PAM3, 0x33);
+	pci_write_config8(NORTHBRIDGE, PAM4, 0x33);
+	pci_write_config8(NORTHBRIDGE, PAM5, 0x33);
+	pci_write_config8(NORTHBRIDGE, PAM6, 0x33);
+
+	/* Set the value for System Management RAM Control Register */
+	pci_write_config8(NORTHBRIDGE, SMRAM, 0x02);
+
+	/* Set the value for GMCH Control Register #1 */
+	switch (CONFIG_VIDEO_MB) {
+	case 1: /* 1M of memory */
+		video_memory = 0x1;
+		break;
+	case 4: /* 4M of memory */
+		video_memory = 0x2;
+		break;
+	case 8: /* 8M of memory */
+		video_memory = 0x3;
+		break;
+	case 16: /* 16M of memory */
+		video_memory = 0x4;
+		break;
+	case 32: /* 32M of memory */
+		video_memory = 0x5;
+		break;
+	default: /* No memory */
+		pci_write_config16(NORTHBRIDGE, GMC, pci_read_config16(NORTHBRIDGE, GMC) | 1);
+		video_memory = 0x0;
+	}
+
+	value = pci_read_config16(NORTHBRIDGE, GGC);
+	value |= video_memory << 4;
+	if (video_memory == 0) {
+		value &= ~(1 < 1);
+	} else
+		value |= (1 < 1);
+	pci_write_config16(NORTHBRIDGE, GGC, value);
+
+	/* AGPCMD: disable AGP, Data-Rate: 1x */
+	pci_write_config32(NORTHBRIDGE, AGPCMD, 0x00000001);
+
+	pci_write_config8(NORTHBRIDGE, AMTT, 0x20);
+	pci_write_config8(NORTHBRIDGE, LPTT, 0x10);
+
+	printk(BIOS_DEBUG, "Initial Northbridge registers have been set.\n");
+}
+
+static void sdram_set_spd_registers(const struct mem_controller *ctrl)
+{
+	uint8_t dimm_mask;
+
+	PRINTK_DEBUG("Reading SPD data...\n");
+
+	dimm_mask = spd_get_supported_dimms(ctrl);
+
+	if (dimm_mask == 0) {
+		print_debug("No usable memory for this controller\n");
+	} else {
+		PRINTK_DEBUG("DIMM MASK: %02x\n", dimm_mask);	
+
+		spd_set_row_attributes(ctrl, dimm_mask);
+		spd_set_dram_controller_mode(ctrl, dimm_mask);
+		spd_set_dram_timing(ctrl, dimm_mask);
+		spd_set_dram_size(ctrl, dimm_mask);
+		spd_set_dram_pwr_management(ctrl);
+		spd_set_dram_throttle_control(ctrl);
+		spd_set_undocumented_registers(ctrl);
+	}
+
+	/* Setup Initial Northbridge Registers */
+	northbridge_set_registers();
+}
+
diff --git a/src/northbridge/intel/i855/raminit.h b/src/northbridge/intel/i855/raminit.h
index dbd0be6..1f1b34d 100644
--- a/src/northbridge/intel/i855/raminit.h
+++ b/src/northbridge/intel/i855/raminit.h
@@ -18,11 +18,19 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
-#ifndef RAMINIT_H
-#define RAMINIT_H
+#ifndef NORTHBRIDGE_INTEL_I855_RAMINIT_H
+#define NORTHBRIDGE_INTEL_I855_RAMINIT_H
 
+/* i855 Northbridge PCI device */
+#define NORTHBRIDGE         PCI_DEV(0, 0, 0)
+#define NORTHBRIDGE_MMC     PCI_DEV(0, 0, 1)
+
+/* The i855 supports max. 2 dual-sided SO-DIMMs. */
 #define DIMM_SOCKETS 2
 
+/* DIMM0 is at 0x50, DIMM1 is at 0x51. */
+#define DIMM_SPD_BASE   0x50
+
 struct mem_controller {
   device_t d0;
   uint16_t channel0[DIMM_SOCKETS];
@@ -31,4 +39,4 @@ struct mem_controller {
 void sdram_initialize(int controllers, const struct mem_controller *ctrl);
 
 
-#endif /* RAMINIT_H */
+#endif /* NORTHBRIDGE_INTEL_I855_RAMINIT_H */
-- 
1.7.0.4





More information about the coreboot mailing list