[coreboot-gerrit] New patch to review for coreboot: f3e96a1 sandy/ivybridge: Skeleton native raminit.

Vladimir Serbinenko (phcoder@gmail.com) gerrit at coreboot.org
Sun May 18 23:10:58 CEST 2014


Vladimir Serbinenko (phcoder at gmail.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/5786

-gerrit

commit f3e96a104be496ed59151aa739314e6a65980d0e
Author: Vladimir Serbinenko <phcoder at gmail.com>
Date:   Sun May 18 11:05:56 2014 +0200

    sandy/ivybridge: Skeleton native raminit.
    
    Based on damo22 work and my X230 tracing.
    
    The "meat" (timing discovery) is stillbugging, so publishing this for
    now with hardcoded timings for my laptop, may work for other
    systems if timings are replaced.
    
    Also-By: Damien Zammit <damien at zamaudio.com>
    Change-Id: I1aa024c55a8416fc53b25e7123037df0e55a2769
    Signed-off-by: Vladimir Serbinenko <phcoder at gmail.com>
---
 src/device/dram/ddr3.c                         |    8 +-
 src/include/device/dram/ddr3.h                 |   11 +-
 src/mainboard/lenovo/x230/romstage.c           |   70 +-
 src/northbridge/intel/sandybridge/Makefile.inc |    2 +
 src/northbridge/intel/sandybridge/pchinit.c    |  638 +++++++
 src/northbridge/intel/sandybridge/raminit.c    | 2364 ++++++++++++++++++++++--
 src/northbridge/intel/sandybridge/raminit.h    |  149 +-
 7 files changed, 2983 insertions(+), 259 deletions(-)

diff --git a/src/device/dram/ddr3.c b/src/device/dram/ddr3.c
index 9b4f490..69782ab 100644
--- a/src/device/dram/ddr3.c
+++ b/src/device/dram/ddr3.c
@@ -110,7 +110,7 @@ int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd)
 {
 	int ret;
 	u16 crc, spd_crc;
-	u8 ftb_divisor, ftb_dividend, capacity_shift, bus_width, sdram_width;
+	u8 ftb_divisor, ftb_dividend, capacity_shift, bus_width;
 	u8 reg8;
 	u32 mtb;		/* medium time base */
 	unsigned int val, param;
@@ -209,8 +209,8 @@ int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd)
 		printram("  Invalid SDRAM width\n");
 		ret = SPD_STATUS_INVALID_FIELD;
 	}
-	sdram_width = (4 << val);
-	printram("  SDRAM width       : %u\n", sdram_width);
+	dimm->width = (4 << val);
+	printram("  SDRAM width       : %u\n", dimm->width);
 
 	/* Memory bus width */
 	reg8 = spd[8];
@@ -236,7 +236,7 @@ int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd)
 	 * capacity_shift
 	 * The rest is the JEDEC formula */
 	dimm->size_mb = ((1 << (capacity_shift + (25 - 20))) * bus_width
-			 * dimm->ranks) / sdram_width;
+			 * dimm->ranks) / dimm->width;
 
 	/* Fine Timebase (FTB) Dividend/Divisor */
 	/* Dividend */
diff --git a/src/include/device/dram/ddr3.h b/src/include/device/dram/ddr3.h
index b19c51c..fcaf10b 100644
--- a/src/include/device/dram/ddr3.h
+++ b/src/include/device/dram/ddr3.h
@@ -37,6 +37,7 @@
  * @{
  */
 #define TCK_1066MHZ     240
+#define TCK_933MHZ	275
 #define TCK_800MHZ      320
 #define TCK_666MHZ      384
 #define TCK_533MHZ      480
@@ -54,11 +55,11 @@
  * disabled.
  * @{
  */
-#if defined(CONFIG_DEBUG_RAM_SETUP) && (CONFIG_DEBUG_RAM_SETUP)
+//#if defined(CONFIG_DEBUG_RAM_SETUP) && (CONFIG_DEBUG_RAM_SETUP)
 #define printram(x, ...) printk(BIOS_DEBUG, x, ##__VA_ARGS__)
-#else
-#define printram(x, ...)
-#endif
+//#else
+//#define printram(x, ...)
+//#endif
 /** @} */
 
 /*
@@ -137,6 +138,8 @@ typedef struct dimm_attr_st {
 	u16 cas_supported;
 	/* Flags extracted from SPD */
 	dimm_flags_t flags;
+	/* SDRAM width */
+	u8 width;
 	/* Number of ranks */
 	u8 ranks;
 	/* Number or row address bits */
diff --git a/src/mainboard/lenovo/x230/romstage.c b/src/mainboard/lenovo/x230/romstage.c
index 377c34a..31e84fb 100644
--- a/src/mainboard/lenovo/x230/romstage.c
+++ b/src/mainboard/lenovo/x230/romstage.c
@@ -114,60 +114,13 @@ void main(unsigned long bist)
 	int cbmem_was_initted;
 	u32 pm1_cnt;
 	u16 pm1_sts;
+	const dimm_layout dl = { {0x50, 0, 0x51, 0} };
 
 	if (MCHBAR16(SSKPD) == 0xCAFE) {
 		outb(0x6, 0xcf9);
 		hlt ();
 	}
 
-	struct pei_data pei_data = {
-		pei_version: PEI_VERSION,
-		mchbar: DEFAULT_MCHBAR,
-		dmibar: DEFAULT_DMIBAR,
-		epbar: DEFAULT_EPBAR,
-		pciexbar: CONFIG_MMCONF_BASE_ADDRESS,
-		smbusbar: SMBUS_IO_BASE,
-		wdbbar: 0x4000000,
-		wdbsize: 0x1000,
-		hpet_address: CONFIG_HPET_ADDRESS,
-		rcba: DEFAULT_RCBABASE,
-		pmbase: DEFAULT_PMBASE,
-		gpiobase: DEFAULT_GPIOBASE,
-		thermalbase: 0xfed08000,
-		system_type: 0, // 0 Mobile, 1 Desktop/Server
-		tseg_size: CONFIG_SMM_TSEG_SIZE,
-		spd_addresses: { 0xA0, 0x00,0xA2,0x00 },
-		ts_addresses: { 0x00, 0x00, 0x00, 0x00 },
-		ec_present: 1,
-		gbe_enable: 1,
-		ddr3lv_support: 0,
-		// 0 = leave channel enabled
-		// 1 = disable dimm 0 on channel
-		// 2 = disable dimm 1 on channel
-		// 3 = disable dimm 0+1 on channel
-		dimm_channel0_disabled: 2,
-		dimm_channel1_disabled: 2,
-		max_ddr3_freq: 1600,
-		usb_port_config: {
-			 /* enabled   usb oc pin    length */
-			{ 1, 0, 0x0080 }, /* P0 (left, fan side), OC 0 */
-			{ 1, 1, 0x0080 }, /* P1 (left touchpad side), OC 1 */
-			{ 1, 3, 0x0080 }, /* P2: dock, OC 3 */
-			{ 1, 0, 0x0040 }, /* P3: wwan, no OC */
-			{ 1, 0, 0x0080 }, /* P4: Wacom tablet on X230t, otherwise empty */
-			{ 1, 0, 0x0080 }, /* P5: Expresscard, no OC */
-			{ 0, 0, 0x0000 }, /* P6: Empty */
-			{ 1, 0, 0x0080 }, /* P7: dock, no OC */
-			{ 0, 0, 0x0000 }, /* P8: Empty */
-			{ 1, 5, 0x0080 }, /* P9: Right (EHCI debug), OC 5 */
-			{ 1, 0, 0x0040 }, /* P10: fingerprint reader, no OC */
-			{ 1, 0, 0x0040 }, /* P11: bluetooth, no OC. */
-			{ 1, 0, 0x0040 }, /* P12: wlan, no OC */
-			{ 1, 0, 0x0080 }, /* P13: webcam, no OC */
-		},
-		ddr_refresh_rate_config: 2, /* Force double refresh rate */
-	};
-
 	timestamp_init(get_initial_timestamp());
 	timestamp_add_now(TS_START_ROMSTAGE);
 
@@ -224,24 +177,9 @@ void main(unsigned long bist)
 	post_code(0x39);
 
 	post_code(0x3a);
-	pei_data.boot_mode = boot_mode;
 	timestamp_add_now(TS_BEFORE_INITRAM);
 
-	/* MRC.bin has a bug and sometimes halts (instead of reboot?).
-	 */
-	if (boot_mode != 2)
-	  {
-		  RCBA32(GCS) = RCBA32(GCS) & ~(1 << 5);	/* reset */
-		  outw((0 << 11), DEFAULT_PMBASE | 0x60 | 0x08);	/* let timer go */
-	  }
-
-	sdram_initialize(&pei_data);
-
-	if (boot_mode != 2)
-	  {
-		  RCBA32(GCS) = RCBA32(GCS) | (1 << 5);	/* No reset */
-		  outw((1 << 11), DEFAULT_PMBASE | 0x60 | 0x08);	/* halt timer */
-	  }
+	init_dram_ddr3 (&dl);
 
 	timestamp_add_now(TS_AFTER_INITRAM);
 	post_code(0x3c);
@@ -254,8 +192,8 @@ void main(unsigned long bist)
 
 	MCHBAR16(SSKPD) = 0xCAFE;
 	cbmem_was_initted = !cbmem_recovery(boot_mode==2);
-	if (boot_mode!=2)
-		save_mrc_data(&pei_data);
+//	if (boot_mode!=2)
+//		save_mrc_data(&pei_data);
 
 #if CONFIG_HAVE_ACPI_RESUME
 	/* If there is no high memory area, we didn't boot before, so
diff --git a/src/northbridge/intel/sandybridge/Makefile.inc b/src/northbridge/intel/sandybridge/Makefile.inc
index 6655e2a..707a8d0 100644
--- a/src/northbridge/intel/sandybridge/Makefile.inc
+++ b/src/northbridge/intel/sandybridge/Makefile.inc
@@ -27,6 +27,8 @@ ramstage-y += mrccache.c
 
 romstage-y += ram_calc.c
 romstage-y += raminit.c
+romstage-y += pchinit.c
+romstage-y += ../../../device/dram/ddr3.c
 romstage-y += mrccache.c
 romstage-y += early_init.c
 romstage-y += report_platform.c
diff --git a/src/northbridge/intel/sandybridge/pchinit.c b/src/northbridge/intel/sandybridge/pchinit.c
new file mode 100644
index 0000000..e03cfa8
--- /dev/null
+++ b/src/northbridge/intel/sandybridge/pchinit.c
@@ -0,0 +1,638 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2014 Vladimir Serbinenko <phcoder at gmail.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; version 2 of the License.
+ *
+ * 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
+ */
+
+#include <console/console.h>
+#include <string.h>
+#include <arch/hlt.h>
+#include <arch/io.h>
+#include <cbmem.h>
+#include <arch/cbfs.h>
+#include <cbfs.h>
+#include <ip_checksum.h>
+#include <pc80/mc146818rtc.h>
+#include <device/pci_def.h>
+#include "raminit.h"
+#include "pei_data.h"
+#include "sandybridge.h"
+#include <delay.h>
+
+/* Management Engine is in the southbridge */
+#include "southbridge/intel/bd82x6x/me.h"
+#include "southbridge/intel/bd82x6x/pch.h"
+#include <cpu/x86/msr.h>
+#include <cpu/cpu.h>
+#include "cpu/intel/model_2065x/model_2065x.h"
+
+#define SOUTHBRIDGE PCI_DEV(0, 0x1f, 0)
+#define NORTHBRIDGE PCI_DEV(0, 0x0, 0)
+#define GFX_DEV PCI_DEV(0, 0x2, 0)
+
+static int
+wait_2338 (void)
+{
+  int timeout;
+
+  timeout = 1000;
+  while (1)
+    {
+      if (!(read8 (DEFAULT_RCBABASE | 0x2338) & 1))
+	return 0;
+      if (!timeout--)
+	return -1;
+    }
+}
+
+static int
+read_2338 (u32 edx, u32 * result)
+{
+  int ret;
+
+  write32 (DEFAULT_RCBABASE | 0x2330, edx);
+  write16 (DEFAULT_RCBABASE | 0x2338, (read16 (DEFAULT_RCBABASE | 0x2338)
+				       & 0x1ff) | 0x600);
+  ret = wait_2338 ();
+  if (ret < 0)
+    return ret;
+  *result = read32 (DEFAULT_RCBABASE | 0x2334);	// !!! = 0x00590133
+  ret = wait_2338 ();
+  if (ret < 0)
+    return ret;
+  if (read8 (DEFAULT_RCBABASE | 0x2338) & 6)
+    return -2;
+  return 0;
+}
+
+static int
+and_or_2338 (u32 edx, u32 and, u32 or)
+{
+  u32 t1;
+  int ret;
+  ret = read_2338 (edx, &t1);
+  if (ret < 0)
+    return ret;
+  write16 (DEFAULT_RCBABASE | 0x2338, (read16 (DEFAULT_RCBABASE | 0x2338)
+				       & 0x1ff) | 0x600);
+  t1 &= and;
+  t1 |= or;
+  ret = wait_2338 ();
+  if (ret < 0)
+    return ret;
+
+  write32 (DEFAULT_RCBABASE | 0x2334, t1);
+  ret = wait_2338 ();
+  if (ret < 0)
+    return ret;
+  write16 (DEFAULT_RCBABASE | 0x2338,
+	   (read16 (DEFAULT_RCBABASE | 0x2338) & 0x1ff) | 0x600);
+  if (read8 (DEFAULT_RCBABASE | 0x2338) & 6)
+    return -2;
+  return 0;
+}
+
+#define USB_ACC_CONTROL 0x80
+
+static void
+init_usb (void)
+{
+  u32 base;
+  pcie_read_config32 (SOUTHBRIDGE, 0xf0);	// !!! = 0xfed1c001
+  read32 (DEFAULT_RCBABASE | 0x3418);	// !!! = 0x06000000
+  pcie_read_config16 (SOUTHBRIDGE, 0x40);	// !!! = 0x0501
+  read32 (DEFAULT_RCBABASE | 0x3598);	// !!! = 0x00000000
+  pcie_read_config32 (PCI_DEV (0, 0x1d, 0), 0x10);	// !!! = 0x00000000
+  pcie_read_config16 (PCI_DEV (0, 0x1d, 0), 0x04);	// !!! = 0x0000
+  base = 0xe8000000;
+  pcie_write_config32 (PCI_DEV (0, 0x1d, 0), 0x10, base);
+  pcie_read_config16 (PCI_DEV (0, 0x1d, 0), 0x04);	// !!! = 0x0000
+  pcie_write_config16 (PCI_DEV (0, 0x1d, 0), 0x04, 0x0006);
+
+  pcie_write_config16 (PCI_DEV (0, 0x1d, 0), USB_ACC_CONTROL,
+		       pcie_read_config16 (PCI_DEV (0, 0x1d, 0),
+					   USB_ACC_CONTROL) | 1);
+  read32 (base + 4);		// !!! = 0x00204008
+  write32 (base + 4, 0x00200008);
+  read32 (base + 4);		// !!! = 0x00200008
+  write32 (base + 4, 0x00200003);
+  pcie_write_config16 (PCI_DEV (0, 0x1d, 0), USB_ACC_CONTROL,
+		       pcie_read_config16 (PCI_DEV (0, 0x1d, 0),
+					   USB_ACC_CONTROL) & ~1);
+  read32 (0xe8000024);		// !!! = 0x00001000
+  write16 (0xe8000020, read16 (0xe8000020) | 2);
+  pcie_read_config32 (PCI_DEV (0, 0x1d, 0), 0x84);	// !!! = 0x83088e01
+  pcie_write_config32 (PCI_DEV (0, 0x1d, 0), 0x84, 0x930c8811);
+  pcie_read_config32 (PCI_DEV (0, 0x1d, 0), 0x88);	// !!! = 0x04000030
+  pcie_write_config32 (PCI_DEV (0, 0x1d, 0), 0x88, 0x24000d30);
+  pcie_read_config32 (PCI_DEV (0, 0x1d, 0), 0xf4);	// !!! = 0x00408588
+  pcie_write_config32 (PCI_DEV (0, 0x1d, 0), 0xf4, 0x80408588);
+  pcie_read_config32 (PCI_DEV (0, 0x1d, 0), 0xf4);	// !!! = 0x80408588
+  pcie_write_config32 (PCI_DEV (0, 0x1d, 0), 0xf4, 0x80808588);
+  pcie_read_config32 (PCI_DEV (0, 0x1d, 0), 0xf4);	// !!! = 0x80808588
+  pcie_write_config32 (PCI_DEV (0, 0x1d, 0), 0xf4, 0x00808588);
+  pcie_read_config32 (PCI_DEV (0, 0x1d, 0), 0xfc);	// !!! = 0x20591708
+  pcie_write_config32 (PCI_DEV (0, 0x1d, 0), 0xfc, 0x205b1708);
+  write32 (DEFAULT_RCBABASE | 0x3560,
+	   read32 (DEFAULT_RCBABASE | 0x3560) | 0x20c8000);
+  pcie_read_config16 (PCI_DEV (0, 0x1d, 0), 0x04);	// !!! = 0x0006
+  pcie_write_config16 (PCI_DEV (0, 0x1d, 0), 0x04, 0x0000);
+  pcie_write_config32 (PCI_DEV (0, 0x1d, 0), 0x10, 0x00000000);
+  base = 0xfef00000;
+  pcie_read_config32 (PCI_DEV (0, 0x1a, 0), 0x10);	// !!! = 0xfef00000
+  pcie_read_config32 (PCI_DEV (0, 0x1a, 0), 0x10);	// !!! = 0xfef00000
+  pcie_read_config16 (PCI_DEV (0, 0x1a, 0), 0x04);	// !!! = 0x0002
+  pcie_read_config16 (PCI_DEV (0, 0x1a, 0), 0x04);	// !!! = 0x0002
+  pcie_write_config16 (PCI_DEV (0, 0x1a, 0), 0x04, 0x0006);
+  pcie_write_config16 (PCI_DEV (0, 0x1a, 0), 0x80,
+		       pcie_read_config16 (PCI_DEV (0, 0x1a, 0), 0x80) | 1);
+  read32 (base + 4);		// !!! = 0x00203006
+  write32 (base + 4, 0x00200006);
+  read32 (base + 4);		// !!! = 0x00200006
+  write32 (base + 4, 0x00200003);
+  pcie_write_config16 (PCI_DEV (0, 0x1a, 0), USB_ACC_CONTROL,
+		       pcie_read_config16 (PCI_DEV (0, 0x1a, 0),
+					   USB_ACC_CONTROL) & ~1);
+  pcie_read_config32 (PCI_DEV (0, 0x1a, 0), 0x84);	// !!! = 0x83088e01
+  pcie_write_config32 (PCI_DEV (0, 0x1a, 0), 0x84, 0x930c8811);
+  pcie_read_config32 (PCI_DEV (0, 0x1a, 0), 0x88);	// !!! = 0x04000030
+  pcie_write_config32 (PCI_DEV (0, 0x1a, 0), 0x88, 0x24000d30);
+  pcie_read_config32 (PCI_DEV (0, 0x1a, 0), 0xf4);	// !!! = 0x00408588
+  pcie_write_config32 (PCI_DEV (0, 0x1a, 0), 0xf4, 0x80408588);
+  pcie_read_config32 (PCI_DEV (0, 0x1a, 0), 0xf4);	// !!! = 0x80408588
+  pcie_write_config32 (PCI_DEV (0, 0x1a, 0), 0xf4, 0x80808588);
+  pcie_read_config32 (PCI_DEV (0, 0x1a, 0), 0xf4);	// !!! = 0x80808588
+  pcie_write_config32 (PCI_DEV (0, 0x1a, 0), 0xf4, 0x00808588);
+  pcie_read_config32 (PCI_DEV (0, 0x1a, 0), 0xfc);	// !!! = 0x20591708
+  pcie_write_config32 (PCI_DEV (0, 0x1a, 0), 0xfc, 0x205b1708);
+  write32 (DEFAULT_RCBABASE | 0x3560,
+	   read32 (DEFAULT_RCBABASE | 0x3560) | 0x20c8000);
+  pcie_write_config16 (PCI_DEV (0, 0x1a, 0), 0x04, 0x0002);
+
+  pcie_read_config16 (SOUTHBRIDGE, 0x02);	// !!! = 0x1e55
+  outw (inw (DEFAULT_PMBASE | 0x003c) | 2, DEFAULT_PMBASE | 0x003c);
+  u16 reg_359c = 0x150;
+  int i;
+  for (i = 0; i < 14; i++)
+    if (reg_359c & (1 << i))
+      write16 (DEFAULT_RCBABASE | 0x359c,
+	       read16 (DEFAULT_RCBABASE | 0x359c) | (1 << i));
+    else
+      write16 (DEFAULT_RCBABASE | 0x359c,
+	       read16 (DEFAULT_RCBABASE | 0x359c) & ~(1 << i));
+  pcie_read_config8 (SOUTHBRIDGE, 0x08);	// !!! = 0x04
+  pcie_read_config16 (SOUTHBRIDGE, 0x02);	// !!! = 0x1e55
+  pcie_read_config32 (PCI_DEV (0, 0x14, 0), 0xe4);	// !!! = 0x00000000
+  pcie_write_config32 (PCI_DEV (0, 0x14, 0), 0xe4, 0x00000000);
+  outw (0x0000, DEFAULT_PMBASE | 0x003c);
+  write32 (DEFAULT_RCBABASE | 0x3418, 0x16001fe0);
+  read32 (DEFAULT_RCBABASE | 0x3418);	// !!! = 0x16001fe0
+}
+
+static void
+init_thermal (void)
+{
+  /* OK { */
+  pcie_write_config32 (PCI_DEV (0, 0x1f, 6), 0x40, 0xfed08000);
+  pcie_write_config32 (PCI_DEV (0, 0x1f, 6), 0x44, 0x00000000);
+  pcie_read_config32 (PCI_DEV (0, 0x1f, 6), 0x40);	// !!! = 0xfed08004
+  pcie_write_config32 (PCI_DEV (0, 0x1f, 6), 0x40, 0xfed08005);
+  write16 (0xfed08004, 0x3a2b);
+  write8 (0xfed0800c, 0xff);
+  write8 (0xfed0800d, 0x00);
+  write8 (0xfed0800e, 0x40);
+  write8 (0xfed08082, 0x00);
+  write8 (0xfed08001, 0xba);
+  msr_t msr;
+  msr = rdmsr (MSR_TEMPERATURE_TARGET);	// !!! = 0x0000000000691200
+  write16 (0xfed08012, ((msr.lo >> 16) & 0xff) << 6);
+  write16 (0xfed08016, 0x808c);
+
+
+  write32 (DEFAULT_RCBABASE | 0x38b0,
+	   (read32 (DEFAULT_RCBABASE | 0x38b0) & 0xffff8003) | 0x403c);
+
+  write16 (0xfed08014, 0xde87);
+  /* } OK */
+  if (read32 (DEFAULT_RCBABASE | 0x38b4) & 0x4000)	// !!! = 0x0000437e
+    write16 (0xfed0801a, 0x0000);
+  else
+    {
+      /* ? */
+    }
+  pcie_read_config32 (PCI_DEV (0, 0x1f, 6), 0x40);	// !!! = 0xfed08005
+  pcie_write_config32 (PCI_DEV (0, 0x1f, 6), 0x40, 0xfed08004);
+  pcie_write_config32 (PCI_DEV (0, 0x1f, 6), 0x40, 0x00000000);
+}
+
+void
+pch_init (void)
+{
+  int i;
+
+  pcie_read_config16 (SOUTHBRIDGE, PCI_DEVICE_ID);	// !!! = 0x1e55
+  pcie_read_config32 (SOUTHBRIDGE, RCBA);	// !!! = 0xfed1c001
+  pcie_write_config32 (SOUTHBRIDGE, RCBA, DEFAULT_RCBA | 1);
+  pcie_read_config32 (SOUTHBRIDGE, PMBASE);	// !!! = 0x00000501
+  pcie_write_config32 (SOUTHBRIDGE, PMBASE, DEFAULT_PMBASE | 1);
+  pcie_write_config8 (SOUTHBRIDGE, ACPI_CNTL,
+		      pcie_read_config8 (SOUTHBRIDGE, ACPI_CNTL) | ACPI_EN);
+
+  /* Undocumented.  */
+  pcie_write_config8 (SOUTHBRIDGE, 0xa6,
+		      pcie_read_config8 (SOUTHBRIDGE, 0xa6) | 2);
+
+  pcie_read_config32 (SOUTHBRIDGE, GPIO_BASE);	// !!! = 0x00000481
+  pcie_write_config32 (SOUTHBRIDGE, GPIO_BASE, DEFAULT_GPIOBASE | 1);
+  pcie_read_config8 (SOUTHBRIDGE, GPIO_CNTL);	// !!! = 0x10
+  pcie_write_config8 (SOUTHBRIDGE, GPIO_CNTL, 0x10);
+  pcie_read_config16 (SOUTHBRIDGE, PCI_DEVICE_ID);	// !!! = 0x1e55
+
+  write32 (DEFAULT_RCBABASE | 0x2088, 0x00109000);
+  read32 (DEFAULT_RCBABASE | 0x20ac);	// !!! = 0x00000000
+  write32 (DEFAULT_RCBABASE | 0x20ac, 0x40000000);
+  write32 (DEFAULT_RCBABASE | 0x100c, 0x01110000);
+  write8 (DEFAULT_RCBABASE | 0x2340, 0x1b);
+  read32 (DEFAULT_RCBABASE | 0x2314);	// !!! = 0x0a080000
+  write32 (DEFAULT_RCBABASE | 0x2314, 0x0a280000);
+  read32 (DEFAULT_RCBABASE | 0x2310);	// !!! = 0xc809605b
+  write32 (DEFAULT_RCBABASE | 0x2310, 0xa809605b);
+  write32 (DEFAULT_RCBABASE | 0x2324, 0x00854c74);
+  read8 (DEFAULT_RCBABASE | 0x0400);	// !!! = 0x00
+  read32 (DEFAULT_RCBABASE | 0x2310);	// !!! = 0xa809605b
+  write32 (DEFAULT_RCBABASE | 0x2310, 0xa809605b);
+  read32 (DEFAULT_RCBABASE | 0x2310);	// !!! = 0xa809605b
+  write32 (DEFAULT_RCBABASE | 0x2310, 0xa809605b);
+
+  and_or_2338 (0xea007f62, 0, 0x00590133);
+  and_or_2338 (0xec007f62, 0, 0x00590133);
+  and_or_2338 (0xec007f64, 0, 0x59555588);
+  and_or_2338 (0xea0040b9, 0, 0x0001051c);
+  and_or_2338 (0xeb0040a1, 0, 0x800084ff);
+  and_or_2338 (0xec0040a1, 0, 0x800084ff);
+  and_or_2338 (0xea004001, 0, 0x00008400);
+  and_or_2338 (0xeb004002, 0, 0x40201758);
+  and_or_2338 (0xec004002, 0, 0x40201758);
+  and_or_2338 (0xea004002, 0, 0x00601758);
+  and_or_2338 (0xea0040a1, 0, 0x810084ff);
+  and_or_2338 (0xeb0040b1, 0, 0x0001c598);
+  and_or_2338 (0xec0040b1, 0, 0x0001c598);
+  and_or_2338 (0xeb0040b6, 0, 0x0001c598);
+  and_or_2338 (0xea0000a9, 0, 0x80ff969f);
+  and_or_2338 (0xea0001a9, 0, 0x80ff969f);
+  and_or_2338 (0xeb0040b2, 0, 0x0001c396);
+  and_or_2338 (0xeb0040b3, 0, 0x0001c396);
+  and_or_2338 (0xec0040b2, 0, 0x0001c396);
+  and_or_2338 (0xea0001a9, 0, 0x80ff94ff);
+  and_or_2338 (0xea000151, 0, 0x0088037f);
+  and_or_2338 (0xea0000a9, 0, 0x80ff94ff);
+  and_or_2338 (0xea000051, 0, 0x0088037f);
+
+  u32 reg32;
+  read_2338 (0xea007f05, &reg32);
+
+  and_or_2338 (0xea007f05, 0, 0x00010642);
+  and_or_2338 (0xea0040b7, 0, 0x0001c91c);
+  and_or_2338 (0xea0040b8, 0, 0x0001c91c);
+  and_or_2338 (0xeb0040a1, 0, 0x820084ff);
+  and_or_2338 (0xec0040a1, 0, 0x820084ff);
+  and_or_2338 (0xea007f0a, 0, 0xc2480000);
+
+
+  pcie_read_config8 (SOUTHBRIDGE, 0x08);	// !!! = 0x04
+  pcie_read_config16 (SOUTHBRIDGE, 0x02);	// !!! = 0x1e55
+
+  and_or_2338 (0xec00404d, 0, 0x1ff177f);
+  and_or_2338 (0xec000084, 0, 0x5a600000);
+  and_or_2338 (0xec000184, 0, 0x5a600000);
+  and_or_2338 (0xec000284, 0, 0x5a600000);
+  and_or_2338 (0xec000384, 0, 0x5a600000);
+  and_or_2338 (0xec000094, 0, 0x000f0501);
+  and_or_2338 (0xec000194, 0, 0x000f0501);
+  and_or_2338 (0xec000294, 0, 0x000f0501);
+  and_or_2338 (0xec000394, 0, 0x000f0501);
+  and_or_2338 (0xec000096, 0, 0x00000001);
+  and_or_2338 (0xec000196, 0, 0x00000001);
+  and_or_2338 (0xec000296, 0, 0x00000001);
+  and_or_2338 (0xec000396, 0, 0x00000001);
+  and_or_2338 (0xec000001, 0, 0x00008c08);
+  and_or_2338 (0xec000101, 0, 0x00008c08);
+  and_or_2338 (0xec000201, 0, 0x00008c08);
+  and_or_2338 (0xec000301, 0, 0x00008c08);
+  and_or_2338 (0xec0040b5, 0, 0x0001c518);
+  and_or_2338 (0xec000087, 0, 0x06077597);
+  and_or_2338 (0xec000187, 0, 0x06077597);
+  and_or_2338 (0xec000287, 0, 0x06077597);
+  and_or_2338 (0xec000387, 0, 0x06077597);
+  and_or_2338 (0xea000050, 0, 0x00bb0157);
+  and_or_2338 (0xea000150, 0, 0x00bb0157);
+  and_or_2338 (0xec007f60, 0, 0x77777d77);
+  and_or_2338 (0xea00008d, 0, 0x01320000);
+  and_or_2338 (0xea00018d, 0, 0x01320000);
+
+  read16 (DEFAULT_RCBABASE | 0x0400);	// !!! = 0x0b00
+  read32 (DEFAULT_RCBABASE | 0x0400);	// !!! = 0x00000b00
+
+  and_or_2338 (0xec0007b2, 0, 0x04514b5e);
+  and_or_2338 (0xec00078c, 0, 0x40000200);
+  and_or_2338 (0xec000780, 0, 0x02000020);
+
+  read8 (DEFAULT_RCBABASE | 0x3414);	// !!! = 0x00
+  pcie_read_config16 (SOUTHBRIDGE, 0x02);	// !!! = 0x1e55
+
+  read32 (DEFAULT_RCBABASE | 0x3598);	// !!! = 0x00000001
+  read32 (DEFAULT_RCBABASE | 0x3598);	// !!! = 0x00000001
+  write32 (DEFAULT_RCBABASE | 0x3598, 0x00000000);
+  read32 (DEFAULT_RCBABASE | 0x3598);	// !!! = 0x00000000
+  pcie_read_config32 (PCI_DEV (0, 0x1d, 0), 0x88);	// !!! = 0x04000030
+  pcie_write_config32 (PCI_DEV (0, 0x1d, 0), 0x88, 0x04000030);
+  pcie_read_config32 (PCI_DEV (0, 0x1a, 0), 0x88);	// !!! = 0x04000030
+  pcie_write_config32 (PCI_DEV (0, 0x1a, 0), 0x88, 0x04000030);
+
+  read32 (DEFAULT_RCBABASE | 0x3410);	// !!! = 0x00000c20
+  write32 (DEFAULT_RCBABASE | 0x3410, 0x00000c20);
+  read32 (DEFAULT_RCBABASE | 0x3410);	// !!! = 0x00000c20
+  /* Disable SATA2. */
+  write32 (DEFAULT_RCBABASE | 0x3418,
+	   read32 (DEFAULT_RCBABASE | 0x3418) | 0x02000000);
+  read32 (DEFAULT_RCBABASE | 0x3418);	// !!! = 0x06000000
+
+  /* Configure thermal */
+  init_thermal ();
+
+  read8 (DEFAULT_RCBABASE | 0x31fe);	// !!! = 0x00
+  read16 (DEFAULT_RCBABASE | 0x31fe);	// !!! = 0x0000
+  write16 (DEFAULT_RCBABASE | 0x31fe, 0x0100);
+  read16 (DEFAULT_RCBABASE | 0x31fe);	// !!! = 0x0100
+  read8 (DEFAULT_RCBABASE | 0x31fe);	// !!! = 0x00
+  write8 (0xfec00000, 0x00);
+  read32 (0xfec00010);		// !!! = 0x00000000
+  pcie_write_config32 (PCI_DEV (0, 0x1f, 3), 0x20, 0x00000400);
+  pcie_read_config8 (PCI_DEV (0, 0x1f, 3), 0x04);	// !!! = 0x01
+  pcie_write_config8 (PCI_DEV (0, 0x1f, 3), 0x04, 0x01);
+  pcie_read_config8 (PCI_DEV (0, 0x1f, 3), 0x40);	// !!! = 0x01
+  pcie_write_config8 (PCI_DEV (0, 0x1f, 3), 0x40, 0x09);
+  pcie_read_config8 (PCI_DEV (0, 0x1f, 3), 0x40);	// !!! = 0x01
+  pcie_write_config8 (PCI_DEV (0, 0x1f, 3), 0x40, 0x01);
+  outb (0xff, 0x0400);
+  init_usb ();
+  pcie_read_config16 (NORTHBRIDGE, 0xe4);	// !!! = 0x619b
+  pcie_read_config16 (NORTHBRIDGE, 0x02);	// !!! = 0x0154
+  cpuid_ext (0x1, 0x0);		// !!! = 0x00000000000306a9
+  pcie_write_config32 (NORTHBRIDGE, 0x48, 0xfed10001);
+  pcie_write_config32 (NORTHBRIDGE, 0x4c, 0x00000000);
+  pcie_write_config32 (NORTHBRIDGE, 0x68, 0xfed18001);
+  pcie_write_config32 (NORTHBRIDGE, 0x6c, 0x00000000);
+  pcie_write_config32 (NORTHBRIDGE, 0x40, 0xfed19001);
+  pcie_write_config32 (NORTHBRIDGE, 0x44, 0x00000000);
+  read32 (DEFAULT_MCHBAR | 0x5f00);	// !!! = 0x0000270f
+  write32 (DEFAULT_MCHBAR | 0x5f00, 0x0000270f);
+  cpuid_ext (0x1, 0x0);		// !!! = 0x00000000000306a9
+  pcie_read_config16 (NORTHBRIDGE, 0x02);	// !!! = 0x0154
+  pcie_read_config32 (NORTHBRIDGE, 0x68);	// !!! = 0xfed18001
+  pcie_read_config32 (NORTHBRIDGE, 0x6c);	// !!! = 0x00000000
+  write32 (DEFAULT_DMIBAR | 0x0914,
+	   read32 (DEFAULT_DMIBAR | 0x0914) | 0x80000000);
+  write32 (DEFAULT_DMIBAR | 0x0934,
+	   read32 (DEFAULT_DMIBAR | 0x0934) | 0x80000000);
+  for (i = 0; i < 4; i++)
+    {
+      write32 (DEFAULT_DMIBAR | 0x0a00 | (i << 4),
+	       read32 (DEFAULT_DMIBAR | 0x0a00 | (i << 4)) & 0xf3ffffff);
+      write32 (DEFAULT_DMIBAR | 0x0a04 | (i << 4),
+	       read32 (DEFAULT_DMIBAR | 0x0a04 | (i << 4)) | 0x800);
+    }
+  write32 (DEFAULT_DMIBAR | 0x0c30, (read32 (DEFAULT_DMIBAR | 0x0c30)
+				     & 0xfffffff) | 0x40000000);
+  for (i = 0; i < 2; i++)
+    {
+      write32 (DEFAULT_DMIBAR | 0x0904 | (i << 5),
+	       read32 (DEFAULT_DMIBAR | 0x0904 | (i << 5)) & 0xfe3fffff);
+      write32 (DEFAULT_DMIBAR | 0x090c | (i << 5),
+	       read32 (DEFAULT_DMIBAR | 0x090c | (i << 5)) & 0xfff1ffff);
+    }
+  write32 (DEFAULT_DMIBAR | 0x090c,
+	   read32 (DEFAULT_DMIBAR | 0x090c) & 0xfe1fffff);
+  write32 (DEFAULT_DMIBAR | 0x092c,
+	   read32 (DEFAULT_DMIBAR | 0x092c) & 0xfe1fffff);
+  read32 (DEFAULT_DMIBAR | 0x0904);	// !!! = 0x7a1842ec
+  write32 (DEFAULT_DMIBAR | 0x0904, 0x7a1842ec);
+  read32 (DEFAULT_DMIBAR | 0x090c);	// !!! = 0x00000208
+  write32 (DEFAULT_DMIBAR | 0x090c, 0x00000128);
+  read32 (DEFAULT_DMIBAR | 0x0924);	// !!! = 0x7a1842ec
+  write32 (DEFAULT_DMIBAR | 0x0924, 0x7a1842ec);
+  read32 (DEFAULT_DMIBAR | 0x092c);	// !!! = 0x00000208
+  write32 (DEFAULT_DMIBAR | 0x092c, 0x00000128);
+  read32 (DEFAULT_DMIBAR | 0x0700);	// !!! = 0x46139008
+  write32 (DEFAULT_DMIBAR | 0x0700, 0x46139008);
+  read32 (DEFAULT_DMIBAR | 0x0720);	// !!! = 0x46139008
+  write32 (DEFAULT_DMIBAR | 0x0720, 0x46139008);
+  read32 (DEFAULT_DMIBAR | 0x0c04);	// !!! = 0x2e680008
+  write32 (DEFAULT_DMIBAR | 0x0c04, 0x2e680008);
+  read32 (DEFAULT_DMIBAR | 0x0904);	// !!! = 0x7a1842ec
+  write32 (DEFAULT_DMIBAR | 0x0904, 0x3a1842ec);
+  read32 (DEFAULT_DMIBAR | 0x0924);	// !!! = 0x7a1842ec
+  write32 (DEFAULT_DMIBAR | 0x0924, 0x3a1842ec);
+  read32 (DEFAULT_DMIBAR | 0x0910);	// !!! = 0x00006300
+  write32 (DEFAULT_DMIBAR | 0x0910, 0x00004300);
+  read32 (DEFAULT_DMIBAR | 0x0930);	// !!! = 0x00006300
+  write32 (DEFAULT_DMIBAR | 0x0930, 0x00004300);
+  read32 (DEFAULT_DMIBAR | 0x0a00);	// !!! = 0x03042010
+  write32 (DEFAULT_DMIBAR | 0x0a00, 0x03042018);
+  read32 (DEFAULT_DMIBAR | 0x0a10);	// !!! = 0x03042010
+  write32 (DEFAULT_DMIBAR | 0x0a10, 0x03042018);
+  read32 (DEFAULT_DMIBAR | 0x0a20);	// !!! = 0x03042010
+  write32 (DEFAULT_DMIBAR | 0x0a20, 0x03042018);
+  read32 (DEFAULT_DMIBAR | 0x0a30);	// !!! = 0x03042010
+  write32 (DEFAULT_DMIBAR | 0x0a30, 0x03042018);
+  read32 (DEFAULT_DMIBAR | 0x0c00);	// !!! = 0x29700c08
+  write32 (DEFAULT_DMIBAR | 0x0c00, 0x29700c08);
+  read32 (DEFAULT_DMIBAR | 0x0a04);	// !!! = 0x0c0708f0
+  write32 (DEFAULT_DMIBAR | 0x0a04, 0x0c0718f0);
+  read32 (DEFAULT_DMIBAR | 0x0a14);	// !!! = 0x0c0708f0
+  write32 (DEFAULT_DMIBAR | 0x0a14, 0x0c0718f0);
+  read32 (DEFAULT_DMIBAR | 0x0a24);	// !!! = 0x0c0708f0
+  write32 (DEFAULT_DMIBAR | 0x0a24, 0x0c0718f0);
+  read32 (DEFAULT_DMIBAR | 0x0a34);	// !!! = 0x0c0708f0
+  write32 (DEFAULT_DMIBAR | 0x0a34, 0x0c0718f0);
+  read32 (DEFAULT_DMIBAR | 0x0900);	// !!! = 0x50000000
+  write32 (DEFAULT_DMIBAR | 0x0900, 0x50000000);
+  read32 (DEFAULT_DMIBAR | 0x0920);	// !!! = 0x50000000
+  write32 (DEFAULT_DMIBAR | 0x0920, 0x50000000);
+  read32 (DEFAULT_DMIBAR | 0x0908);	// !!! = 0x51ffffff
+  write32 (DEFAULT_DMIBAR | 0x0908, 0x51ffffff);
+  read32 (DEFAULT_DMIBAR | 0x0928);	// !!! = 0x51ffffff
+  write32 (DEFAULT_DMIBAR | 0x0928, 0x51ffffff);
+  read32 (DEFAULT_DMIBAR | 0x0a00);	// !!! = 0x03042018
+  write32 (DEFAULT_DMIBAR | 0x0a00, 0x03042018);
+  read32 (DEFAULT_DMIBAR | 0x0a10);	// !!! = 0x03042018
+  write32 (DEFAULT_DMIBAR | 0x0a10, 0x03042018);
+  read32 (DEFAULT_DMIBAR | 0x0a20);	// !!! = 0x03042018
+  write32 (DEFAULT_DMIBAR | 0x0a20, 0x03042018);
+  read32 (DEFAULT_DMIBAR | 0x0a30);	// !!! = 0x03042018
+  write32 (DEFAULT_DMIBAR | 0x0a30, 0x03042018);
+  read32 (DEFAULT_DMIBAR | 0x0700);	// !!! = 0x46139008
+  write32 (DEFAULT_DMIBAR | 0x0700, 0x46139008);
+  read32 (DEFAULT_DMIBAR | 0x0720);	// !!! = 0x46139008
+  write32 (DEFAULT_DMIBAR | 0x0720, 0x46139008);
+  read32 (DEFAULT_DMIBAR | 0x0904);	// !!! = 0x3a1842ec
+  write32 (DEFAULT_DMIBAR | 0x0904, 0x3a1846ec);
+  read32 (DEFAULT_DMIBAR | 0x0924);	// !!! = 0x3a1842ec
+  write32 (DEFAULT_DMIBAR | 0x0924, 0x3a1846ec);
+  read32 (DEFAULT_DMIBAR | 0x0a00);	// !!! = 0x03042018
+  write32 (DEFAULT_DMIBAR | 0x0a00, 0x03042018);
+  read32 (DEFAULT_DMIBAR | 0x0a10);	// !!! = 0x03042018
+  write32 (DEFAULT_DMIBAR | 0x0a10, 0x03042018);
+  read32 (DEFAULT_DMIBAR | 0x0a20);	// !!! = 0x03042018
+  write32 (DEFAULT_DMIBAR | 0x0a20, 0x03042018);
+  read32 (DEFAULT_DMIBAR | 0x0a30);	// !!! = 0x03042018
+  write32 (DEFAULT_DMIBAR | 0x0a30, 0x03042018);
+  read32 (DEFAULT_DMIBAR | 0x0908);	// !!! = 0x51ffffff
+  write32 (DEFAULT_DMIBAR | 0x0908, 0x51ffffff);
+  read32 (DEFAULT_DMIBAR | 0x0928);	// !!! = 0x51ffffff
+  write32 (DEFAULT_DMIBAR | 0x0928, 0x51ffffff);
+  read32 (DEFAULT_DMIBAR | 0x0c00);	// !!! = 0x29700c08
+  write32 (DEFAULT_DMIBAR | 0x0c00, 0x29700c08);
+  read32 (DEFAULT_DMIBAR | 0x0c0c);	// !!! = 0x16063400
+  write32 (DEFAULT_DMIBAR | 0x0c0c, 0x00063400);
+  read32 (DEFAULT_DMIBAR | 0x0700);	// !!! = 0x46139008
+  write32 (DEFAULT_DMIBAR | 0x0700, 0x46339008);
+  read32 (DEFAULT_DMIBAR | 0x0720);	// !!! = 0x46139008
+  write32 (DEFAULT_DMIBAR | 0x0720, 0x46339008);
+  read32 (DEFAULT_DMIBAR | 0x0700);	// !!! = 0x46339008
+  write32 (DEFAULT_DMIBAR | 0x0700, 0x45339008);
+  read32 (DEFAULT_DMIBAR | 0x0720);	// !!! = 0x46339008
+  write32 (DEFAULT_DMIBAR | 0x0720, 0x45339008);
+  read32 (DEFAULT_DMIBAR | 0x0700);	// !!! = 0x45339008
+  write32 (DEFAULT_DMIBAR | 0x0700, 0x453b9008);
+  read32 (DEFAULT_DMIBAR | 0x0720);	// !!! = 0x45339008
+  write32 (DEFAULT_DMIBAR | 0x0720, 0x453b9008);
+  read32 (DEFAULT_DMIBAR | 0x0700);	// !!! = 0x453b9008
+  write32 (DEFAULT_DMIBAR | 0x0700, 0x45bb9008);
+  read32 (DEFAULT_DMIBAR | 0x0720);	// !!! = 0x453b9008
+  write32 (DEFAULT_DMIBAR | 0x0720, 0x45bb9008);
+  read32 (DEFAULT_DMIBAR | 0x0700);	// !!! = 0x45bb9008
+  write32 (DEFAULT_DMIBAR | 0x0700, 0x45fb9008);
+  read32 (DEFAULT_DMIBAR | 0x0720);	// !!! = 0x45bb9008
+  write32 (DEFAULT_DMIBAR | 0x0720, 0x45fb9008);
+  read32 (DEFAULT_DMIBAR | 0x0914);	// !!! = 0x9021a080
+  write32 (DEFAULT_DMIBAR | 0x0914, 0x9021a280);
+  read32 (DEFAULT_DMIBAR | 0x0934);	// !!! = 0x9021a080
+  write32 (DEFAULT_DMIBAR | 0x0934, 0x9021a280);
+  read32 (DEFAULT_DMIBAR | 0x0914);	// !!! = 0x9021a280
+  write32 (DEFAULT_DMIBAR | 0x0914, 0x9821a280);
+  read32 (DEFAULT_DMIBAR | 0x0934);	// !!! = 0x9021a280
+  write32 (DEFAULT_DMIBAR | 0x0934, 0x9821a280);
+  read32 (DEFAULT_DMIBAR | 0x0a00);	// !!! = 0x03042018
+  write32 (DEFAULT_DMIBAR | 0x0a00, 0x03242018);
+  read32 (DEFAULT_DMIBAR | 0x0a10);	// !!! = 0x03042018
+  write32 (DEFAULT_DMIBAR | 0x0a10, 0x03242018);
+  read32 (DEFAULT_DMIBAR | 0x0a20);	// !!! = 0x03042018
+  write32 (DEFAULT_DMIBAR | 0x0a20, 0x03242018);
+  read32 (DEFAULT_DMIBAR | 0x0a30);	// !!! = 0x03042018
+  write32 (DEFAULT_DMIBAR | 0x0a30, 0x03242018);
+  read32 (DEFAULT_DMIBAR | 0x0258);	// !!! = 0x40000600
+  write32 (DEFAULT_DMIBAR | 0x0258, 0x60000600);
+  read32 (DEFAULT_DMIBAR | 0x0904);	// !!! = 0x3a1846ec
+  write32 (DEFAULT_DMIBAR | 0x0904, 0x2a1846ec);
+  read32 (DEFAULT_DMIBAR | 0x0914);	// !!! = 0x9821a280
+  write32 (DEFAULT_DMIBAR | 0x0914, 0x98200280);
+  read32 (DEFAULT_DMIBAR | 0x0924);	// !!! = 0x3a1846ec
+  write32 (DEFAULT_DMIBAR | 0x0924, 0x2a1846ec);
+  read32 (DEFAULT_DMIBAR | 0x0934);	// !!! = 0x9821a280
+  write32 (DEFAULT_DMIBAR | 0x0934, 0x98200280);
+  read32 (DEFAULT_DMIBAR | 0x022c);	// !!! = 0x00c26460
+  write32 (DEFAULT_DMIBAR | 0x022c, 0x00c2403c);
+  read8 (DEFAULT_RCBABASE | 0x21a4);	// !!! = 0x42
+  pcie_read_config32 (NORTHBRIDGE, 0xe4);	// !!! = 0xe200619b
+  pcie_read_config32 (SOUTHBRIDGE, 0xf0);	// !!! = 0xfed1c001
+  read32 (DEFAULT_RCBABASE | 0x21a4);	// !!! = 0x00012c42
+  read32 (DEFAULT_RCBABASE | 0x2340);	// !!! = 0x0013001b
+  write32 (DEFAULT_RCBABASE | 0x2340, 0x003a001b);
+  read8 (DEFAULT_RCBABASE | 0x21b0);	// !!! = 0x01
+  write8 (DEFAULT_RCBABASE | 0x21b0, 0x02);
+  read32 (DEFAULT_DMIBAR | 0x0084);	// !!! = 0x0041ac41
+  write32 (DEFAULT_DMIBAR | 0x0084, 0x0041ac42);
+  read8 (DEFAULT_DMIBAR | 0x0088);	// !!! = 0x00
+  write8 (DEFAULT_DMIBAR | 0x0088, 0x20);
+  read16 (DEFAULT_DMIBAR | 0x008a);	// !!! = 0x0041
+  read8 (DEFAULT_DMIBAR | 0x0088);	// !!! = 0x00
+  write8 (DEFAULT_DMIBAR | 0x0088, 0x20);
+  read16 (DEFAULT_DMIBAR | 0x008a);	// !!! = 0x0042
+  read16 (DEFAULT_DMIBAR | 0x008a);	// !!! = 0x0042
+  pcie_read_config16 (NORTHBRIDGE, 0x50);	// !!! = 0x0208
+  pcie_write_config16 (NORTHBRIDGE, 0x50, 0x020a);
+  pcie_read_config16 (PCI_DEV (0, 0x1e, 0), 0x00);	// !!! = 0x8086
+  pcie_write_config32 (PCI_DEV (0, 0x1e, 0), 0x18, 0x00ff0200);
+  pcie_read_config8 (PCI_DEV (0, 0x1e, 0), 0x19);	// !!! = 0x02
+  pcie_write_config16 (PCI_DEV (2, 0x0, 0), 0x00, 0x0000);
+
+  pcie_write_config8 (PCI_DEV (0, 0x1e, 0), 0x1a, 0x02);
+  pcie_write_config32 (PCI_DEV (0, 0x1e, 0), 0x18, 0x00000000);
+
+  pcie_read_config16 (NORTHBRIDGE, 0x54);	// !!! = 0x0011
+  pcie_read_config16 (NORTHBRIDGE, 0x50);	// !!! = 0x020a
+  pcie_write_config16 (NORTHBRIDGE, 0x50, 0x0202);
+  pcie_read_config8 (NORTHBRIDGE, 0x50);	// !!! = 0x02
+  pcie_write_config8 (NORTHBRIDGE, 0x50, 0x0a);
+  pcie_read_config16 (NORTHBRIDGE, 0x50);	// !!! = 0x020a
+  pcie_write_config16 (NORTHBRIDGE, 0x50, 0x020a);
+  pcie_read_config8 (GFX_DEV, 0x62);	// !!! = 0x02
+  pcie_write_config8 (GFX_DEV, 0x62, 0x02);
+  pcie_read_config16 (NORTHBRIDGE, 0x50);	// !!! = 0x020a
+  pcie_write_config16 (NORTHBRIDGE, 0x50, 0x0208);
+  pcie_read_config32 (NORTHBRIDGE, 0x68);	// !!! = 0xfed18001
+  pcie_read_config32 (NORTHBRIDGE, 0x6c);	// !!! = 0x00000000
+  read32 (DEFAULT_DMIBAR | 0x0014);	// !!! = 0x8000007f
+  write32 (DEFAULT_DMIBAR | 0x0014, 0x80000019);
+  read32 (DEFAULT_DMIBAR | 0x0020);	// !!! = 0x01000000
+  write32 (DEFAULT_DMIBAR | 0x0020, 0x81000022);
+  read32 (DEFAULT_DMIBAR | 0x002c);	// !!! = 0x02000000
+  write32 (DEFAULT_DMIBAR | 0x002c, 0x82000044);
+  read32 (DEFAULT_DMIBAR | 0x0038);	// !!! = 0x07000080
+  write32 (DEFAULT_DMIBAR | 0x0038, 0x87000080);
+  read8 (DEFAULT_DMIBAR | 0x0004);	// !!! = 0x00
+  write8 (DEFAULT_DMIBAR | 0x0004, 0x01);
+  pcie_read_config32 (SOUTHBRIDGE, 0xf0);	// !!! = 0xfed1c001
+  read32 (DEFAULT_RCBABASE | 0x0050);	// !!! = 0x01200654
+  write32 (DEFAULT_RCBABASE | 0x0050, 0x01200654);
+  read32 (DEFAULT_RCBABASE | 0x0050);	// !!! = 0x01200654
+  write32 (DEFAULT_RCBABASE | 0x0050, 0x012a0654);
+  read32 (DEFAULT_RCBABASE | 0x0050);	// !!! = 0x012a0654
+  read8 (DEFAULT_RCBABASE | 0x1114);	// !!! = 0x00
+  write8 (DEFAULT_RCBABASE | 0x1114, 0x05);
+  read32 (DEFAULT_RCBABASE | 0x2014);	// !!! = 0x80000011
+  write32 (DEFAULT_RCBABASE | 0x2014, 0x80000019);
+  read32 (DEFAULT_RCBABASE | 0x2020);	// !!! = 0x00000000
+  write32 (DEFAULT_RCBABASE | 0x2020, 0x81000022);
+  read32 (DEFAULT_RCBABASE | 0x2020);	// !!! = 0x81000022
+  read32 (DEFAULT_RCBABASE | 0x2030);	// !!! = 0x00000000
+  write32 (DEFAULT_RCBABASE | 0x2030, 0x82000044);
+  read32 (DEFAULT_RCBABASE | 0x2030);	// !!! = 0x82000044
+  read32 (DEFAULT_RCBABASE | 0x2040);	// !!! = 0x00000000
+  write32 (DEFAULT_RCBABASE | 0x2040, 0x87000080);
+  read32 (DEFAULT_RCBABASE | 0x0050);	// !!! = 0x012a0654
+  write32 (DEFAULT_RCBABASE | 0x0050, 0x812a0654);
+  read32 (DEFAULT_RCBABASE | 0x0050);	// !!! = 0x812a0654
+  read16 (DEFAULT_RCBABASE | 0x201a);	// !!! = 0x0000
+  read16 (DEFAULT_RCBABASE | 0x2026);	// !!! = 0x0000
+  read16 (DEFAULT_RCBABASE | 0x2036);	// !!! = 0x0000
+  read16 (DEFAULT_RCBABASE | 0x2046);	// !!! = 0x0000
+  read16 (DEFAULT_DMIBAR | 0x001a);	// !!! = 0x0000
+  read16 (DEFAULT_DMIBAR | 0x0026);	// !!! = 0x0000
+  read16 (DEFAULT_DMIBAR | 0x0032);	// !!! = 0x0000
+  read16 (DEFAULT_DMIBAR | 0x003e);	// !!! = 0x0000
+}
diff --git a/src/northbridge/intel/sandybridge/raminit.c b/src/northbridge/intel/sandybridge/raminit.c
index b9c3839..966193c 100644
--- a/src/northbridge/intel/sandybridge/raminit.c
+++ b/src/northbridge/intel/sandybridge/raminit.c
@@ -1,7 +1,8 @@
 /*
  * This file is part of the coreboot project.
  *
- * Copyright (C) 2011 Google Inc.
+ * Copyright (C) 2014 Damien Zammit <damien at zamaudio.com>
+ * Copyright (C) 2014 Vladimir Serbinenko <phcoder at gmail.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
@@ -30,135 +31,74 @@
 #include "raminit.h"
 #include "pei_data.h"
 #include "sandybridge.h"
+#include <delay.h>
 
 /* Management Engine is in the southbridge */
 #include "southbridge/intel/bd82x6x/me.h"
+#include "southbridge/intel/bd82x6x/pch.h"
+#include "southbridge/intel/bd82x6x/smbus.h"
+#include <cpu/x86/msr.h>
+#include <cpu/cpu.h>
+#include "cpu/intel/model_2065x/model_2065x.h"
+
 #if CONFIG_CHROMEOS
 #include <vendorcode/google/chromeos/chromeos.h>
 #else
 #define recovery_mode_enabled(x) 0
 #endif
 
-/*
- * MRC scrambler seed offsets should be reserved in
- * mainboard cmos.layout and not covered by checksum.
- */
-#if CONFIG_USE_OPTION_TABLE
-#include "option_table.h"
-#define CMOS_OFFSET_MRC_SEED     (CMOS_VSTART_mrc_scrambler_seed >> 3)
-#define CMOS_OFFSET_MRC_SEED_S3  (CMOS_VSTART_mrc_scrambler_seed_s3 >> 3)
-#define CMOS_OFFSET_MRC_SEED_CHK (CMOS_VSTART_mrc_scrambler_seed_chk >> 3)
-#else
-#define CMOS_OFFSET_MRC_SEED     152
-#define CMOS_OFFSET_MRC_SEED_S3  156
-#define CMOS_OFFSET_MRC_SEED_CHK 160
-#endif
-
-void save_mrc_data(struct pei_data *pei_data)
-{
-	u16 c1, c2, checksum;
-
-#if CONFIG_EARLY_CBMEM_INIT
-	struct mrc_data_container *mrcdata;
-	int output_len = ALIGN(pei_data->mrc_output_len, 16);
-
-	/* Save the MRC S3 restore data to cbmem */
-	mrcdata = cbmem_add
-		(CBMEM_ID_MRCDATA,
-		 output_len + sizeof(struct mrc_data_container));
-
-	printk(BIOS_DEBUG, "Relocate MRC DATA from %p to %p (%u bytes)\n",
-	       pei_data->mrc_output, mrcdata, output_len);
-
-	mrcdata->mrc_signature = MRC_DATA_SIGNATURE;
-	mrcdata->mrc_data_size = output_len;
-	mrcdata->reserved = 0;
-	memcpy(mrcdata->mrc_data, pei_data->mrc_output,
-	       pei_data->mrc_output_len);
-
-	/* Zero the unused space in aligned buffer. */
-	if (output_len > pei_data->mrc_output_len)
-		memset(mrcdata->mrc_data+pei_data->mrc_output_len, 0,
-		       output_len - pei_data->mrc_output_len);
-
-	mrcdata->mrc_checksum = compute_ip_checksum(mrcdata->mrc_data,
-						    mrcdata->mrc_data_size);
-#endif
-
-	/* Save the MRC seed values to CMOS */
-	cmos_write32(CMOS_OFFSET_MRC_SEED, pei_data->scrambler_seed);
-	printk(BIOS_DEBUG, "Save scrambler seed    0x%08x to CMOS 0x%02x\n",
-	       pei_data->scrambler_seed, CMOS_OFFSET_MRC_SEED);
+#define SOUTHBRIDGE PCI_DEV(0, 0x1f, 0)
+#define NORTHBRIDGE PCI_DEV(0, 0x0, 0)
+#define GFX_DEV PCI_DEV(0, 0x2, 0)
+#define HECIDEV PCI_DEV(0, 0x16, 0)
+#define FOR_ALL_LANES for (lane = 0; lane < 8; lane++)
+#define FOR_ALL_CHANNELS for (channel = 0; channel < 2; channel++)
+#define FOR_ALL_POPULATED_RANKS for (slotrank = 0; slotrank < 4; slotrank++) if ((ctrl->rankmap[channel][0] | ctrl->rankmap[channel][1]) & (1 << slotrank))
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#define min(a,b) ((a) < (b) ? (a) : (b))
 
-	cmos_write32(CMOS_OFFSET_MRC_SEED_S3, pei_data->scrambler_seed_s3);
-	printk(BIOS_DEBUG, "Save s3 scrambler seed 0x%08x to CMOS 0x%02x\n",
-	       pei_data->scrambler_seed_s3, CMOS_OFFSET_MRC_SEED_S3);
+static const char* ecc_decoder[] = {
+	"inactive",
+	"active on IO",
+	"disabled on IO",
+	"active"
+};
 
-	/* Save a simple checksum of the seed values */
-	c1 = compute_ip_checksum((u8*)&pei_data->scrambler_seed,
-				 sizeof(u32));
-	c2 = compute_ip_checksum((u8*)&pei_data->scrambler_seed_s3,
-				 sizeof(u32));
-	checksum = add_ip_checksums(sizeof(u32), c1, c2);
+static void
+wait_txt_clear (void)
+{
+  struct cpuid_result cp;
 
-	cmos_write(checksum & 0xff, CMOS_OFFSET_MRC_SEED_CHK);
-	cmos_write((checksum >> 8) & 0xff, CMOS_OFFSET_MRC_SEED_CHK+1);
+  cp = cpuid_ext (0x1, 0x0);
+  /* Check if TXT is supported?  */
+  if (!(cp.ecx & 0x40))
+    return;
+  /* Some TXT public bit.  */
+  if (!(read32 (0xfed30010) & 1))
+    return;
+  /* Wait for TXT clear.  */
+  while (!(read8 (0xfed40000) & (1 << 7)));
 }
 
-static void prepare_mrc_cache(struct pei_data *pei_data)
+static void
+sfence (void)
 {
-	struct mrc_data_container *mrc_cache;
-	u16 c1, c2, checksum, seed_checksum;
-
-	// preset just in case there is an error
-	pei_data->mrc_input = NULL;
-	pei_data->mrc_input_len = 0;
-
-	/* Read scrambler seeds from CMOS */
-	pei_data->scrambler_seed = cmos_read32(CMOS_OFFSET_MRC_SEED);
-	printk(BIOS_DEBUG, "Read scrambler seed    0x%08x from CMOS 0x%02x\n",
-	       pei_data->scrambler_seed, CMOS_OFFSET_MRC_SEED);
-
-	pei_data->scrambler_seed_s3 = cmos_read32(CMOS_OFFSET_MRC_SEED_S3);
-	printk(BIOS_DEBUG, "Read S3 scrambler seed 0x%08x from CMOS 0x%02x\n",
-	       pei_data->scrambler_seed_s3, CMOS_OFFSET_MRC_SEED_S3);
-
-	/* Compute seed checksum and compare */
-	c1 = compute_ip_checksum((u8*)&pei_data->scrambler_seed,
-				 sizeof(u32));
-	c2 = compute_ip_checksum((u8*)&pei_data->scrambler_seed_s3,
-				 sizeof(u32));
-	checksum = add_ip_checksums(sizeof(u32), c1, c2);
-
-	seed_checksum = cmos_read(CMOS_OFFSET_MRC_SEED_CHK);
-	seed_checksum |= cmos_read(CMOS_OFFSET_MRC_SEED_CHK+1) << 8;
-
-	if (checksum != seed_checksum) {
-		printk(BIOS_ERR, "%s: invalid seed checksum\n", __func__);
-		pei_data->scrambler_seed = 0;
-		pei_data->scrambler_seed_s3 = 0;
-		return;
-	}
-
-	if ((mrc_cache = find_current_mrc_cache()) == NULL) {
-		/* error message printed in find_current_mrc_cache */
-		return;
-	}
-
-	pei_data->mrc_input = mrc_cache->mrc_data;
-	pei_data->mrc_input_len = mrc_cache->mrc_data_size;
-
-	printk(BIOS_DEBUG, "%s: at %p, size %x checksum %04x\n",
-	       __func__, pei_data->mrc_input,
-	       pei_data->mrc_input_len, mrc_cache->mrc_checksum);
+  asm volatile ("sfence");
 }
 
-static const char* ecc_decoder[] = {
-	"inactive",
-	"active on IO",
-	"disabled on IO",
-	"active"
-};
+static void
+fill_pattern2 (u32 base)
+{
+  u32 i;
+  for (i = 0; i < 4; i++)
+    {
+      write32 (base + (i << 4), 0x12345678);
+      write32 (base + (i << 4) + 4, 0x12345678);
+      write32 (base + (i << 4) + 8, 0x98765432);
+      write32 (base + (i << 4) + 12, 0x98765432);
+    }
+  sfence ();
+}
 
 /*
  * Dump in the log memory controller configuration as read from the memory
@@ -203,97 +143,2157 @@ static void report_memory_config(void)
 	}
 }
 
-static void post_system_agent_init(struct pei_data *pei_data)
+static void post_system_agent_init(void)
 {
 	/* If PCIe init is skipped, set the PEG clock gating */
-	if (!pei_data->pcie_init)
-		MCHBAR32(0x7010) = MCHBAR32(0x7010) | 0x01;
+	MCHBAR32(0x7010) = MCHBAR32(0x7010) | 0x01;
 }
 
-/**
- * Find PEI executable in coreboot filesystem and execute it.
- *
- * @param pei_data: configuration data for UEFI PEI reference code
- */
-void sdram_initialize(struct pei_data *pei_data)
+void dram_find_spds_ddr3(const dimm_layout * addr, dimm_info * dimm,
+			 ramctr_timing * ctrl)
+{
+	int i = 0;
+	int dimms = 0;
+	spd_raw_data spd;
+	static const u8 ch[4] = { 0, 0, 1, 1 };
+	static const u8 dm[4] = { 0, 1, 0, 1 };
+	ctrl->channelmap = 0;
+	for (i = 0; i < 4; i++) {
+		ctrl->rankmap[ch[i]][dm[i]] = 0;
+		if (addr->spd_addr[i] != 0x00) {
+			int j;
+			for (j = 0; j < 256; j++)
+				spd[j] = do_smbus_read_byte(SMBUS_IO_BASE, addr->spd_addr[i], j);
+			spd_decode_ddr3(&dimm->dimm[i], spd);
+			if (dimm->dimm[i].dram_type ==
+			    SPD_MEMORY_TYPE_SDRAM_DDR3) {
+				dram_print_spd_ddr3(&dimm->dimm[i]);
+				dimms++;
+				ctrl->thermalrefresh = spd[31];
+				ctrl->channelmap &= (1 << i);
+				ctrl->rankmap[ch[i]][dm[i]] =
+				    (dimm->dimm[i].ranks >=
+				     2) ? 0x3 : dimm->dimm[i].ranks;
+				printram("i=%d  rankmap[%d][%d] = %d\n", i,
+					 ch[i], dm[i],
+					 ctrl->rankmap[ch[i]][dm[i]]);
+			} else {
+				// set dimm invalid
+				dimm->dimm[i].ranks = 0;
+				dimm->dimm[i].size_mb = 0;
+			}
+		}
+	}
+	if (!dimms)
+		die("No DIMMs were found");
+}
+
+void dram_find_common_params(const dimm_info * dimms, ramctr_timing * ctrl)
 {
-	struct sys_info sysinfo;
-	int (*entry) (struct pei_data *pei_data) __attribute__ ((regparm(1)));
+	size_t i, valid_dimms;
+	ctrl->cas_supported = 0xff;
+	valid_dimms = 0;
+	for (i = 0; i < 4; i++) {
+		const dimm_attr *dimm = &dimms->dimm[i];
+		if (dimm->dram_type == SPD_MEMORY_TYPE_UNDEFINED)
+			continue;
+		valid_dimms++;
+
+		if (valid_dimms == 1) {
+			/* First DIMM defines the type of DIMM */
+			ctrl->dram_type = dimm->dram_type;
+		} else {
+			/* Check if we have mismatched DIMMs */
+			if (ctrl->dram_type != dimm->dram_type)
+				die("Mismatched DIMM Types");
+		}
+		/* Find all possible CAS combinations */
+		ctrl->cas_supported &= dimm->cas_supported;
+
+		/* Find the smallest common latencies supported by all DIMMs */
+		ctrl->tCK = MAX(ctrl->tCK, dimm->tCK);
+		ctrl->tAA = MAX(ctrl->tAA, dimm->tAA);
+		ctrl->tWR = MAX(ctrl->tWR, dimm->tWR);
+		ctrl->tRCD = MAX(ctrl->tRCD, dimm->tRCD);
+		ctrl->tRRD = MAX(ctrl->tRRD, dimm->tRRD);
+		ctrl->tRP = MAX(ctrl->tRP, dimm->tRP);
+		ctrl->tRAS = MAX(ctrl->tRAS, dimm->tRAS);
+		ctrl->tRC = MAX(ctrl->tRC, dimm->tRC);
+		ctrl->tRFC = MAX(ctrl->tRFC, dimm->tRFC);
+		ctrl->tWTR = MAX(ctrl->tWTR, dimm->tWTR);
+		ctrl->tRTP = MAX(ctrl->tRTP, dimm->tRTP);
+		ctrl->tFAW = MAX(ctrl->tFAW, dimm->tFAW);
+	}
+
+	ctrl->n_dimms = valid_dimms;
+	if (!ctrl->cas_supported)
+		die("Unsupported DIMM combination. "
+		    "DIMMS do not support common CAS latency");
+	if (!valid_dimms)
+		die("No valid DIMMs found");
+
+	ctrl->dualchannel =
+	    (pcie_read_config32(PCI_DEV(0, 0, 0), 0xE4) & 0x4000) >> 14;
+	if (ctrl->dualchannel) {
+		printram("Dual channel supported\n");
+	} else {
+		printram("Dual channel not supported\n");
+	}
+}
+
+static u8 get_CWL(u8 CAS)
+{
+	/* Get CWL based on CAS using the following rule:
+	 *       _________________________________________
+	 * CAS: | 4T | 5T | 6T | 7T | 8T | 9T | 10T | 11T |
+	 * CWL: | 5T | 5T | 5T | 6T | 6T | 7T |  7T |  8T |
+	 */
+	static const u8 cas_cwl_map[] = { 5, 5, 5, 6, 6, 7, 7, 8 };
+	if (CAS > 11)
+		return 8;
+	return cas_cwl_map[CAS - 4];
+}
+
+static u32 get_REFI(u32 tCK)
+{
+	/* Get REFI based on MCU frequency using the following rule:
+	 *        _________________________________________
+	 * FRQ : | 3    | 4    | 5    | 6    | 7    | 8    |
+	 * REFI: | 3120 | 4160 | 5200 | 6240 | 7280 | 8320 |
+	 */
+	u32 FRQ = (u32) 256000 / (tCK * BASEFREQ);
+	static const u32 frq_refi_map[] =
+	    { 3120, 4160, 5200, 6240, 7280, 8320 };
+	if (FRQ > 8)
+		return 8320;
+	return frq_refi_map[FRQ - 3];
+}
+
+static u8 get_XSOffset(u32 tCK)
+{
+	/* Get XSOffset based on MCU frequency using the following rule:
+	 *             _________________________
+	 * FRQ      : | 3 | 4 | 5 | 6 | 7  | 8  |
+	 * XSOffset : | 4 | 6 | 7 | 8 | 10 | 11 |
+	 */
+	u32 FRQ = (u32) 256000 / (tCK * BASEFREQ);
+	static const u8 frq_xs_map[] = { 4, 6, 7, 8, 10, 11 };
+	if (FRQ > 8)
+		return 11;
+	return frq_xs_map[FRQ - 3];
+}
+
+static u8 get_MOD(u32 tCK)
+{
+	/* Get MOD based on MCU frequency using the following rule:
+	 *        _____________________________
+	 * FRQ : | 3  | 4  | 5  | 6  | 7  | 8  |
+	 * MOD : | 12 | 12 | 12 | 12 | 15 | 16 |
+	 */
+	u32 FRQ = (u32) 256000 / (tCK * BASEFREQ);
+	static const u8 frq_mod_map[] = { 12, 12, 12, 12, 15, 16 };
+	if (FRQ > 8)
+		return 16;
+	return frq_mod_map[FRQ - 3];
+}
+
+static u8 get_WLO(u32 tCK)
+{
+	/* Get WLO based on MCU frequency using the following rule:
+	 *        _______________________
+	 * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 |
+	 * WLO : | 4 | 5 | 6 | 6 | 8 | 8 |
+	 */
+	u32 FRQ = (u32) 256000 / (tCK * BASEFREQ);
+	static const u8 frq_wlo_map[] = { 4, 5, 6, 6, 8, 8 };
+	if (FRQ > 8)
+		return 8;
+	return frq_wlo_map[FRQ - 3];
+}
+
+static u8 get_CKE(u32 tCK)
+{
+	/* Get CKE based on MCU frequency using the following rule:
+	 *        _______________________
+	 * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 |
+	 * CKE : | 3 | 3 | 4 | 4 | 5 | 6 |
+	 */
+	u32 FRQ = (u32) 256000 / (tCK * BASEFREQ);
+	static const u8 frq_cke_map[] = { 3, 3, 4, 4, 5, 6 };
+	if (FRQ > 8)
+		return 6;
+	return frq_cke_map[FRQ - 3];
+}
+
+static u8 get_XPDLL(u32 tCK)
+{
+	/* Get XPDLL based on MCU frequency using the following rule:
+	 *          _____________________________
+	 * FRQ   : | 3  | 4  | 5  | 6  | 7  | 8  |
+	 * XPDLL : | 10 | 13 | 16 | 20 | 23 | 26 |
+	 */
+	u32 FRQ = (u32) 256000 / (tCK * BASEFREQ);
+	static const u8 frq_xpdll_map[] = { 10, 13, 16, 20, 23, 26 };
+	if (FRQ > 8)
+		return 26;
+	return frq_xpdll_map[FRQ - 3];
+}
+
+static u8 get_XP(u32 tCK)
+{
+	/* Get XP based on MCU frequency using the following rule:
+	 *        _______________________
+	 * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 |
+	 * XP  : | 3 | 4 | 4 | 5 | 6 | 7 |
+	 */
+	u32 FRQ = (u32) 256000 / (tCK * BASEFREQ);
+	static const u8 frq_xp_map[] = { 3, 4, 4, 5, 6, 7 };
+	if (FRQ > 8)
+		return 7;
+	return frq_xp_map[FRQ - 3];
+}
+
+static u8 get_AONPD(u32 tCK)
+{
+	/* Get AONPD based on MCU frequency using the following rule:
+	 *          ________________________
+	 * FRQ   : | 3 | 4 | 5 | 6 | 7 | 8  |
+	 * AONPD : | 4 | 5 | 6 | 8 | 8 | 10 |
+	 */
+	u32 FRQ = (u32) 256000 / (tCK * BASEFREQ);
+	static const u8 frq_aonpd_map[] = { 4, 5, 6, 8, 8, 10 };
+	if (FRQ > 8)
+		return 10;
+	return frq_aonpd_map[FRQ - 3];
+}
+
+static u32 get_COMP2(u32 tCK)
+{
+	/* Get COMP2 based on MCU frequency using the following rule:
+	 *         ___________________________________________________________
+	 * FRQ  : | 3       | 4       | 5       | 6       | 7       | 8       |
+	 * COMP : | D6BEDCC | CE7C34C | CA57A4C | C6369CC | C42514C | C21410C |
+	 */
+	u32 FRQ = (u32) 256000 / (tCK * BASEFREQ);
+	static const u32 frq_comp2_map[] = { 0xD6BEDCC, 0xCE7C34C, 0xCA57A4C,
+		0xC6369CC, 0xC42514C, 0xC21410C
+	};
+	if (FRQ > 8)
+		return 0xD6BEDCC;
+	return frq_comp2_map[FRQ - 3];
+}
+
+static void dram_timing(ramctr_timing * ctrl)
+{
+	u8 val;
+	u32 val32;
+
+	/* Maximum supported DDR3 frequency is 1066MHz (DDR3 2133) so make sure
+	 * we cap it if we have faster DIMMs.
+	 * Then, align it to the closest JEDEC standard frequency */
+	if (ctrl->tCK <= TCK_1066MHZ) {
+		ctrl->tCK = TCK_1066MHZ;
+		ctrl->delay1 = 16;
+		ctrl->delay2 = 8;
+	} else if (ctrl->tCK <= TCK_933MHZ) {
+		ctrl->tCK = TCK_933MHZ;
+		ctrl->delay1 = 15;
+		ctrl->delay2 = 8;
+	} else if (ctrl->tCK <= TCK_800MHZ) {
+		ctrl->tCK = TCK_800MHZ;
+		ctrl->delay1 = 12;
+		ctrl->delay2 = 6;
+	} else if (ctrl->tCK <= TCK_666MHZ) {
+		ctrl->tCK = TCK_666MHZ;
+		ctrl->delay1 = 12;
+		ctrl->delay2 = 6;
+	} else {
+		ctrl->tCK = TCK_533MHZ;
+		ctrl->delay1 = 12;
+		ctrl->delay2 = 5;
+	}
+
+	val32 = (1000 << 8) / ctrl->tCK;
+	printram("Selected DRAM frequency: %u MHz\n", val32);
+
+	/* Find CAS and CWL latencies */
+	val = (ctrl->tAA + ctrl->tCK - 1) / ctrl->tCK;
+	printram("Minimum  CAS latency   : %uT\n", val);
+	/* Find lowest supported CAS latency that satisfies the minimum value */
+	while (!((ctrl->cas_supported >> (val - 4)) & 1)
+	       && (ctrl->cas_supported >> (val - 4))) {
+		val++;
+	}
+	/* Is CAS supported */
+	if (!(ctrl->cas_supported & (1 << (val - 4))))
+		printram("CAS not supported\n");
+	printram("Selected CAS latency   : %uT\n", val);
+	ctrl->CAS = val;
+	ctrl->CWL = get_CWL(ctrl->CAS);
+	printram("Selected CWL latency   : %uT\n", ctrl->CWL);
+
+	/* Find tRCD */
+	ctrl->tRCD = (ctrl->tRCD + ctrl->tCK - 1) / ctrl->tCK;
+	printram("Selected tRCD          : %uT\n", ctrl->tRCD);
+
+	ctrl->tRP = (ctrl->tRP + ctrl->tCK - 1) / ctrl->tCK;
+	printram("Selected tRP           : %uT\n", ctrl->tRP);
+ 
+ 	/* Find tRAS */
+	ctrl->tRAS = (ctrl->tRAS + ctrl->tCK - 1) / ctrl->tCK;
+	printram("Selected tRAS          : %uT\n", ctrl->tRAS);
+
+	/* Find tWR */
+	ctrl->tWR = (ctrl->tWR + ctrl->tCK - 1) / ctrl->tCK;
+	printram("Selected tWR           : %uT\n", ctrl->tWR);
 
+	/* Find tFAW */
+	ctrl->tFAW = (ctrl->tFAW + ctrl->tCK - 1) / ctrl->tCK;
+	printram("Selected tFAW          : %uT\n", ctrl->tFAW);
+ 
+ 	/* Find tRRD */
+	ctrl->tRRD = (ctrl->tRRD + ctrl->tCK - 1) / ctrl->tCK;
+	printram("Selected tRRD          : %uT\n", ctrl->tRRD);
+ 
+ 	/* Find tRTP */
+	ctrl->tRTP = (ctrl->tRTP + ctrl->tCK - 1) / ctrl->tCK;
+	printram("Selected tRTP          : %uT\n", ctrl->tRTP);
+ 
+ 	/* Find tWTR */
+	ctrl->tWTR = (ctrl->tWTR + ctrl->tCK - 1) / ctrl->tCK;
+	printram("Selected tWTR          : %uT\n", ctrl->tWTR);
+
+	/* Refresh-to-Active or Refresh-to-Refresh (tRFC) */
+	ctrl->tRFC = (ctrl->tRFC + ctrl->tCK - 1) / ctrl->tCK;
+	printram("Selected tRFC          : %uT\n", ctrl->tRFC);
+
+	ctrl->tRC = (ctrl->tRC + ctrl->tCK - 1) / ctrl->tCK;
+	printram("Required tRC           : %uT\n", ctrl->tRC);
+
+	ctrl->tREFI = get_REFI(ctrl->tCK);
+	ctrl->tMOD = get_MOD(ctrl->tCK);
+	ctrl->tXSOffset = get_XSOffset(ctrl->tCK);
+	ctrl->tWLO = get_WLO(ctrl->tCK);
+	ctrl->tCKE = get_CKE(ctrl->tCK);
+	ctrl->tXPDLL = get_XPDLL(ctrl->tCK);
+	ctrl->tXP = get_XP(ctrl->tCK);
+	ctrl->tAONPD = get_AONPD(ctrl->tCK);
+}
+
+static void dram_freq(ramctr_timing * ctrl)
+{
+	u8 val1, val2;
+	u32 reg1 = 0;
+
+	/* Step 1 - Set target PCU frequency */
+	if (ctrl->tCK <= TCK_1066MHZ) {
+		val1 = 0x08;
+		ctrl->tCK = TCK_1066MHZ;
+	} else if (ctrl->tCK <= TCK_933MHZ) {
+		val1 = 0x07;
+		ctrl->tCK = TCK_933MHZ;
+	} else if (ctrl->tCK <= TCK_800MHZ) {
+		val1 = 0x06;
+		ctrl->tCK = TCK_800MHZ;
+	} else if (ctrl->tCK <= TCK_666MHZ) {
+		val1 = 0x05;
+		ctrl->tCK = TCK_666MHZ;
+	} else {
+		val1 = 0x04;
+		ctrl->tCK = TCK_533MHZ;
+	}
+
+	/* Step 2 - Select frequency in the MCU */
+	reg1 = val1;
+	reg1 |= 0x80000000;	// set running bit
+	MCHBAR32(0x5e00) = reg1;
+	while (reg1 >> 0x1f) {
+		printram(" PLL busy...");
+		reg1 = MCHBAR32(0x5e00);
+	}
+	printram("done\n");
+
+	/* Step 3 - Verify lock frequency */
+	reg1 = MCHBAR32(0x5e04);
+	val2 = (u8) reg1;
+	if (val2 > val1) {
+		printram("Lock frequency is lower, recalculating\n");
+		switch (val2) {
+		case 8:
+			ctrl->tCK = TCK_1066MHZ;
+			break;
+		case 7:
+			ctrl->tCK = TCK_933MHZ;
+			break;
+		case 6:
+			ctrl->tCK = TCK_800MHZ;
+			break;
+		case 5:
+			ctrl->tCK = TCK_666MHZ;
+			break;
+		case 4:
+			ctrl->tCK = TCK_533MHZ;
+			break;
+		default:
+			printram("ERROR: PLL is off or unknown\n");
+			break;
+		}
+		dram_timing(ctrl);	// recalculate timings
+	}
+	printram("MCU frequency is set at : %d MHz\n", (1000 << 8) / ctrl->tCK);
+}
+
+static void dram_xover(ramctr_timing * ctrl)
+{
+	size_t ch;
+	u32 reg, addr;
+	u8 rmap;
+	for (ch = 0; ch < 2; ch++) {
+		// enable xover clk
+		reg = 0;
+		rmap = (ctrl->rankmap[ch][0] | (ctrl->rankmap[ch][1] << 2));
+		reg = (reg & ~0xf000000) | (rmap << 0x18);
+		addr = (ch == 0) ? 0xc14 : 0xd14;
+		printram ("[%x] = %x\n", addr, reg);
+		MCHBAR32(addr) = reg;
+
+		// enable xover ctl
+		reg = 0;
+		if ((ctrl->rankmap[ch][0] & 0x1) || (ctrl->rankmap[ch][1] & 0x1)) {
+			reg = (reg & ~0x20000) | (1 << 0x11);
+		}
+		if ((ctrl->rankmap[ch][0] & 0x2) || (ctrl->rankmap[ch][1] & 0x2)) {
+			reg = (reg & ~0x4000000) | (1 << 0x1a);
+		}
+		// enable xover cmd
+		reg = (reg & ~0x4000) | (1 << 0xe);
+		addr = (ch == 0) ? 0x320c : 0x330c;
+		printram ("[%x] = %x\n", addr, reg);
+		MCHBAR32(addr) = reg;
+	}
+}
+
+static void dram_timing_regs(ramctr_timing * ctrl)
+{
+	size_t ch;
+	u32 reg, addr, val32, cpu, stretch;
+	u8 val;
+	struct cpuid_result cpures;
+
+	for (ch = 0; ch < 2; ch++) {
+		// DBP
+		reg = 0;
+		val = ctrl->tRCD;
+		reg = (reg & ~0xf) | val;
+		val = ctrl->tRP;
+		reg = (reg & ~0xf0) | (val << 0x4);
+		val = ctrl->CAS;
+		reg = (reg & ~0xf00) | (val << 0x8);
+		val = ctrl->CWL;
+		reg = (reg & ~0xf000) | (val << 0xc);
+		val = ctrl->tRAS;
+		reg = (reg & ~0xff0000) | (val << 0x10);
+		addr = (ch == 0 ? 0x4000 : 0x4400);
+		printram ("[%x] = %x\n", addr, reg);
+		MCHBAR32(addr) = reg;
+
+		// RAP
+		reg = 0;
+		val = ctrl->tRRD;
+		reg = (reg & ~0xf) | val;
+		val = ctrl->tRTP;
+		reg = (reg & ~0xf0) | (val << 0x4);
+		val = ctrl->tCKE;
+		reg = (reg & ~0xf00) | (val << 0x8);
+		val = ctrl->tWTR;
+		reg = (reg & ~0xf000) | (val << 0xc);
+		val = ctrl->tFAW;
+		reg = (reg & ~0xff0000) | (val << 0x10);
+		val = ctrl->tWR;
+		reg = (reg & ~0x1f000000) | (val << 0x18);
+		reg = (reg & ~0xc0000000) | (3 << 0x1e);
+		addr = (ch == 0 ? 0x4004 : 0x4404);
+		printram ("[%x] = %x\n", addr, reg);
+		MCHBAR32(addr) = reg;
+
+		// OTHP
+		addr = (ch == 0 ? 0x400c : 0x440c);
+		reg = MCHBAR32(addr);
+		val = ctrl->tXPDLL;
+		reg = (reg & ~0x1f) | val;
+		val = ctrl->tXP;
+		reg = (reg & ~0xe0) | (val << 0x5);
+		val = ctrl->tAONPD;
+		reg = (reg & ~0xf00) | (val << 0x8);
+		printram ("[%x] = %x\n", addr, reg);
+		MCHBAR32(addr) = reg;
+
+		MCHBAR32(ch == 0 ? 0x4014 : 0x4414) = 0;
+
+		MCHBAR32(addr) |= 0x00020000;
+
+
+		// ODT stretch
+		reg = 0;
+
+		cpures = cpuid(0);
+		cpu = cpures.eax;
+		if (IS_IVY_CPU(cpu)
+		    || (IS_SANDY_CPU(cpu) && IS_SANDY_CPU_D2(cpu))) {
+			stretch = 2;
+			addr = (ch == 0 ? 0x400c : 0x440c);
+			printram ("[%x] = %x\n", addr, reg);
+			reg = MCHBAR32(addr);
+
+			if ((ctrl->rankmap[ch][0] == 0) ||
+			    ctrl->rankmap[ch][1] == 0) {
+
+				// Rank 0 - operate on rank 2
+				reg = (reg & ~0xc0000) | (stretch << 0x12);
+
+				// Rank 2 - operate on rank 0
+				reg = (reg & ~0x30000) | (stretch << 0x10);
+
+				addr = (ch == 0 ? 0x400c : 0x440c);
+				printram ("[%x] = %x\n", addr, reg);
+				MCHBAR32(addr) = reg;
+			}
+
+		} else if (IS_SANDY_CPU(cpu) && IS_SANDY_CPU_C(cpu)) {
+			stretch = 3;
+			addr = (ch == 0 ? 0x401c : 0x441c);
+			reg = MCHBAR32(addr);
+
+			if ((ctrl->rankmap[ch][0] == 0) ||
+			    ctrl->rankmap[ch][1] == 0) {
+
+				// Rank 0 - operate on rank 2
+				reg = (reg & ~0x3000) | (stretch << 0xc);
+
+				// Rank 2 - operate on rank 0
+				reg = (reg & ~0xc00) | (stretch << 0xa);
+
+				addr = (ch == 0 ? 0x401c : 0x441c);
+				printram ("[%x] = %x\n", addr, reg);
+				MCHBAR32(addr) = reg;
+			}
+		} else {
+			stretch = 0;
+		}
+
+		// REFI
+		reg = 0;
+		val32 = ctrl->tREFI;
+		reg = (reg & ~0xffff) | val32;
+		val32 = ctrl->tRFC;
+		reg = (reg & ~0x1ff0000) | (val32 << 0x10);
+		val32 = (u32) (ctrl->tREFI * 9) / 1024;
+		reg = (reg & ~0xfe000000) | (val32 << 0x19);
+		addr = (ch == 0 ? 0x4298 : 0x4698);
+		printram ("[%x] = %x\n", addr, reg);
+		MCHBAR32(addr) = reg;
+
+		addr = (ch == 0 ? 0x4294 : 0x4694);
+		MCHBAR32(addr) |= 0xff;
+
+		// SRFTP
+		reg = 0;
+		val32 = tDLLK;
+		reg = (reg & ~0xfff) | val32;
+		val32 = ctrl->tXSOffset;
+		reg = (reg & ~0xf000) | (val32 << 0xc);
+		val32 = tDLLK - ctrl->tXSOffset;
+		reg = (reg & ~0x3ff0000) | (val32 << 0x10);
+		val32 = ctrl->tMOD - 8;
+		reg = (reg & ~0xf0000000) | (val32 << 0x1c);
+		addr = (ch == 0 ? 0x42a4 : 0x46a4);
+		printram ("[%x] = %x\n", addr, reg);
+		MCHBAR32(addr) = reg;
+	}
+}
+
+static void dram_dimm_mapping(dimm_info * info, ramctr_timing * ctrl)
+{
+	size_t ch;
+	u32 reg, addr, val32;
+	for (ch = 0; ch < 2; ch++) {
+		dimm_attr *dimmA = 0;
+		dimm_attr *dimmB = 0;
+		reg = 0;
+		val32 = 0;
+		addr = 0;
+		if (info->dimm[2 * ch].size_mb >=
+		    info->dimm[2 * ch + 1].size_mb) {
+			// dimm 0 is bigger, set it to dimmA
+			dimmA = &info->dimm[2 * ch];
+			dimmB = &info->dimm[2 * ch + 1];
+			reg = (reg & ~0x10000) | (0 << 0x10);
+		} else {
+			// dimm 1 is bigger, set it to dimmA
+			dimmA = &info->dimm[2 * ch + 1];
+			dimmB = &info->dimm[2 * ch];
+			reg = (reg & ~0x10000) | (1 << 0x10);
+			// swap dimm info ?
+		}
+		// dimmA
+		if (dimmA && (dimmA->ranks > 0)) {
+			val32 = dimmA->size_mb / 256;
+			reg = (reg & ~0xff) | val32;
+			val32 = dimmA->ranks - 1;
+			reg = (reg & ~0x20000) | (val32 << 0x11);
+			val32 = (dimmA->width / 8) - 1;
+			reg = (reg & ~0x80000) | (val32 << 0x13);
+		}
+		// dimmB
+		if (dimmB && (dimmB->ranks > 0)) {
+			val32 = dimmB->size_mb / 256;
+			reg = (reg & ~0xff00) | (val32 << 0x8);
+			val32 = dimmB->ranks - 1;
+			reg = (reg & ~0x40000) | (val32 << 0x12);
+			val32 = (dimmB->width / 8) - 1;
+			reg = (reg & ~0x100000) | (val32 << 0x14);
+		}
+		reg = (reg & ~0x200000) | (1 << 0x15);	// rank interleave
+		reg = (reg & ~0x400000) | (1 << 0x16);	// enhanced interleave
+
+		// Set MAD-DIMM register
+		addr = 0x5004 + ch * 4;
+		if ((dimmA && (dimmA->ranks > 0)) ||
+		    (dimmB && (dimmB->ranks > 0))) {
+			MCHBAR32(addr) = reg;
+		}
+	}
+}
+
+static void dram_zones(dimm_info * info, ramctr_timing * ctrl)
+{
+	u32 reg, addr, ch0size, ch1size;
+	u8 val;
+	reg = 0;
+	addr = 0;
+	val = 0;
+	ch0size = info->dimm[0].size_mb + info->dimm[1].size_mb;
+	ch1size = info->dimm[2].size_mb + info->dimm[3].size_mb;
+
+	if (ch0size >= ch1size) {
+		addr = 0x5000;
+		reg = MCHBAR32(addr);
+		reg = (reg & ~0xf) | 0xc;
+		MCHBAR32(addr) = reg;
+
+		addr = 0x5014;
+		reg = MCHBAR32(addr);
+		val = ch1size / 256;
+		reg = (reg & ~0xff000000) | val << 0x18;
+		reg = (reg & ~0xff0000) | (2 * val) << 0x10;
+		MCHBAR32(addr) = reg;
+	} else {
+		addr = 0x5000;
+		reg = MCHBAR32(addr);
+		reg = (reg & ~0xf) | 0x3;
+		MCHBAR32(addr) = reg;
+
+		addr = 0x5014;
+		reg = MCHBAR32(addr);
+		val = ch0size / 256;
+		reg = (reg & ~0xff000000) | val << 0x18;
+		reg = (reg & ~0xff0000) | (2 * val) << 0x10;
+		MCHBAR32(addr) = reg;
+	}
+
+	reg = MCHBAR32(0x5000);
+	reg = (reg & ~0x18) | (1 << 4);
+	MCHBAR32(0x5000) = reg;
+}
+
+static void dram_memorymap(dimm_info * info)
+{
+	u32 reg, val, reclaim;
+	u32 tom, gfxstolen, gttsize, mestolen;
+	size_t tsegsize, mmiosize, toludbase, touudbase, gfxstolenbase, gttbase,
+	    tsegbase, mestolenbase;
+	size_t tsegbasetmp, tsegbasedelta, remapbase, remaplimit;
+
+	// TODO Make these configurable
+	gfxstolen = 128;
+	gttsize = 1;
+	mestolen = 32;
+	mmiosize = 0x400;
+	tsegsize = 4;
+	//
+
+	tom = info->dimm[0].size_mb + info->dimm[1].size_mb
+	    + info->dimm[2].size_mb + info->dimm[3].size_mb;
+
+	mestolenbase = tom - mestolen;
+
+	toludbase = MIN(4096 - mmiosize, tom - mestolen);
+	gfxstolenbase = toludbase - gfxstolen;
+	gttbase = gfxstolenbase - gttsize;
+
+	tsegbase = gttbase - tsegsize;
+	tsegbasetmp = tsegbase;
+
+	// Round tsegbase down to nearest address aligned to tsegsize
+	tsegbase /= tsegsize;
+	tsegbase *= tsegsize;
+
+	tsegbasedelta = tsegbasetmp - tsegbase;
+	gttbase -= tsegbasedelta;
+	gfxstolenbase -= tsegbasedelta;
+	toludbase -= tsegbasedelta;
+
+	// Test if it is possible to reclaim a hole in the ram addressing
+	if (tom - mestolen > toludbase) {
+		// Reclaim is possible
+		reclaim = 1;
+		remapbase = MAX(4096, tom - mestolen);
+		remaplimit = remapbase +
+		    MIN(4096, tom - mestolen) - toludbase - 1;
+		touudbase = remaplimit + 1;
+	} else {
+		// Reclaim not possible
+		reclaim = 0;
+		touudbase = tom - mestolen;
+	}
+
+	// Update memory map in pci-e configuration space
+
+	// TOM (top of memory)
+	reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xa0);
+	val = tom & 0xfff;
+	reg = (reg & ~0xfff00000) | (val << 20);
+	printram ("PCI:[%x] = %x\n", 0xa0, reg);
+	pcie_write_config32(PCI_DEV(0, 0, 0), 0xa0, reg);
+
+	reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xa4);
+	val = tom & 0xfffff000;
+	reg = (reg & ~0x000fffff) | (val >> 12);
+	printram ("PCI:[%x] = %x\n", 0xa4, reg);
+	pcie_write_config32(PCI_DEV(0, 0, 0), 0xa4, reg);
+
+	// TOLUD (top of low used dram)
+	reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xbc);
+	val = toludbase & 0xfff;
+	reg = (reg & ~0xfff00000) | (val << 20);
+	printram ("PCI:[%x] = %x\n", 0xbc, reg);
+	pcie_write_config32(PCI_DEV(0, 0, 0), 0xbc, reg);
+
+	// TOUUD MSB (top of upper usable dram)
+	reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xa8);
+	val = touudbase & 0xfff;
+	reg = (reg & ~0xfff00000) | (val << 20);
+	printram ("PCI:[%x] = %x\n", 0xa8, reg);
+	pcie_write_config32(PCI_DEV(0, 0, 0), 0xa8, reg);
+
+	// TOUUD LSB
+	reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xac);
+	val = touudbase & 0xfffff000;
+	reg = (reg & ~0x000fffff) | (val >> 12);
+	printram ("PCI:[%x] = %x\n", 0xac, reg);
+	pcie_write_config32(PCI_DEV(0, 0, 0), 0xac, reg);
+
+	if (reclaim) {
+		// REMAP BASE
+		reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x94);
+		val = remapbase & 0xfffff000;
+		reg = (reg & ~0x000fffff) | (val >> 12);
+		printram ("PCI:[%x] = %x\n", 0x94, reg);
+		pcie_write_config32(PCI_DEV(0, 0, 0), 0x94, reg);
+
+		// REMAP LIMIT
+		reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x98);
+		val = remaplimit & 0xfff;
+		reg = (reg & ~0xfff00000) | (val << 20);
+		printram ("PCI:[%x] = %x\n", 0x98, reg);
+		pcie_write_config32(PCI_DEV(0, 0, 0), 0x98, reg);
+
+		reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x9c);
+		val = remaplimit & 0xfffff000;
+		reg = (reg & ~0x000fffff) | (val >> 12);
+		printram ("PCI:[%x] = %x\n", 0x9c, reg);
+		pcie_write_config32(PCI_DEV(0, 0, 0), 0x9c, reg);
+	}
+	// TSEG
+	reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xb8);
+	val = tsegbase & 0xfff;
+	reg = (reg & ~0xfff00000) | (val << 20);
+	printram ("PCI:[%x] = %x\n", 0xb8, reg);
+	pcie_write_config32(PCI_DEV(0, 0, 0), 0xb8, reg);
+
+	// GFX stolen memory
+	reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xb0);
+	val = gfxstolenbase & 0xfff;
+	reg = (reg & ~0xfff00000) | (val << 20);
+	printram ("PCI:[%x] = %x\n", 0xb0, reg);
+	pcie_write_config32(PCI_DEV(0, 0, 0), 0xb0, reg);
+
+	// GTT stolen memory
+	reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xb4);
+	val = gttbase & 0xfff;
+	reg = (reg & ~0xfff00000) | (val << 20);
+	printram ("PCI:[%x] = %x\n", 0xb4, reg);
+	pcie_write_config32(PCI_DEV(0, 0, 0), 0xb4, reg);
+
+	if (mestolen) {
+		// ME mask
+		reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x78);
+		val = (0x80000 - mestolen) & 0xfff;
+		reg = (reg & ~0xfff00000) | (val << 20);
+		reg = (reg & ~0x400) | (1 << 10);	// set lockbit on ME mem
+
+		//Do later
+		//reg = (reg & ~0x800) | (1 << 11); // set ME memory enable
+		printram ("PCI:[%x] = %x\n", 0x78, reg);
+		pcie_write_config32(PCI_DEV(0, 0, 0), 0x78, reg);
+
+		reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x7c);
+		val = (0x80000 - mestolen) & 0xfffff000;
+		reg = (reg & ~0x000fffff) | (val >> 12);
+		printram ("PCI:[%x] = %x\n", 0x7c, reg);
+		pcie_write_config32(PCI_DEV(0, 0, 0), 0x7c, reg);
+
+		// ME base
+		reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x70);
+		val = (0x80000 - mestolenbase) & 0xfff;
+		reg = (reg & ~0xfff00000) | (val << 20);
+		printram ("PCI:[%x] = %x\n", 0x70, reg);
+		pcie_write_config32(PCI_DEV(0, 0, 0), 0x70, reg);
+
+		reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x74);
+		val = (0x80000 - mestolenbase) & 0xfffff000;
+		reg = (reg & ~0x000fffff) | (val >> 12);
+		printram ("PCI:[%x] = %x\n", 0x74, reg);
+		pcie_write_config32(PCI_DEV(0, 0, 0), 0x74, reg);
+	}
+}
+
+static void dram_ioregs(ramctr_timing * ctrl, u32 cpu)
+{
+	u32 reg, ch0rank, ch1rank, addr, comp2;
+	u8 clkpi, ch, clklogicdelay;
+
+	u8 initialclkpi = 8;
+	u8 initialclklogicdelay = 0;
+
+	ch0rank = ctrl->rankmap[0][0] | (ctrl->rankmap[0][1] << 2);
+	ch1rank = ctrl->rankmap[1][0] | (ctrl->rankmap[1][1] << 2);
+
+	// IO clock
+	MCHBAR32(0xc00) = ch0rank;
+	MCHBAR32(0xd00) = ch1rank;
+
+	// IO command
+	MCHBAR32(0x3200) = ch0rank;
+	MCHBAR32(0x3300) = ch1rank;
+
+	// IO control
+	for (ch = 0; ch < 2; ch++) {
+		// Read S
+		addr = 0x350c;
+		addr |= (ch == 0) ? 0xe00 : 0xf00;
+		reg = MCHBAR32(addr);
+
+		// Cmd pi and logic delay
+		reg &= ~0x3f;
+		reg &= ~0x1000;
+
+		// Drive broadcast to S and N
+		if (ctrl->channelmap & (1 << (2 * ch))) {	/* chxdimmA */
+			// Ctl pi and logic delay
+			reg &= ~0xfc0;
+			reg &= ~0x8000;
+		}
+		if (ctrl->channelmap & (1 << (2 * ch + 1))) {	/* chxdimmB */
+			// Ctl pi and logic delay
+			reg &= ~0xfc0000;
+			reg &= ~0x1000000;
+		}
+		MCHBAR32(addr) = reg;
+
+		// Clk pi
+		addr = 0x3614;
+		addr |= (ch == 0) ? 0xc00 : 0xd00;
+		reg = MCHBAR32(addr);
+
+		clkpi = initialclkpi;
+		reg = (reg & ~0x3f) | clkpi;	//rank0
+		reg = (reg & ~0xfc0) | (clkpi << 0x6);	//rank1
+		reg = (reg & ~0x3f000) | (clkpi << 0xc);	//rank2
+		reg = (reg & ~0xfc0000) | (clkpi << 0x12);	//rank3
+		MCHBAR32(addr) = reg;
+
+		clklogicdelay = initialclklogicdelay;
+		addr = 0x3618;
+		addr |= (ch == 0) ? 0xc00 : 0xd00;
+		reg = MCHBAR32(addr);
+		reg = (reg & ~0x1) | clklogicdelay;
+		reg = (reg & ~0x2) | (clklogicdelay << 1);
+		reg = (reg & ~0x4) | (clklogicdelay << 2);
+		reg = (reg & ~0x8) | (clklogicdelay << 3);
+		MCHBAR32(addr) = reg;
+	}
+
+	// Rcomp
+	printram("RCOMP...");
+	reg = 0;
+	while (reg == 0) {
+		reg = MCHBAR32(0x5084) & 0x10000;
+	}
+	printram("done\n");
+
+	// Set comp2
+	comp2 = get_COMP2(ctrl->tCK);
+	MCHBAR32(0x3714) = comp2;
+	printram("COMP2 done\n");
+
+	// Set comp1
+	reg = MCHBAR32(0x1810);	//ch0
+	reg = (reg & ~0xe00) | (1 << 0x9);	//odt
+	reg = (reg & ~0xe00000) | (1 << 0x15);	//clk drive up
+	reg = (reg & ~0x38000000) | (1 << 0x1b);	//ctl drive up
+	MCHBAR32(0x1810) = reg;
+
+	reg = MCHBAR32(0x1910);	//ch1
+	reg = (reg & ~0xe00) | (1 << 0x9);	//odt
+	reg = (reg & ~0xe00000) | (1 << 0x15);	//clk drive up
+	reg = (reg & ~0x38000000) | (1 << 0x1b);	//ctl drive up
+	MCHBAR32(0x1910) = reg;
+	printram("COMP1 done\n");
+
+	printram("FORCE RCOMP and wait 20us...");
+	reg = MCHBAR32(0x5f08);
+	reg = (reg & ~0x100) | (1 << 0x8);
+	MCHBAR32(0x5f08) = reg;
+	udelay(20);
+	printram("done\n");
+}
+
+void dram_jedecreset(ramctr_timing * ctrl)
+{
+	u32 reg, addr, rmap, rank;
+	u8 ch, chw;
+
+	while ( !(MCHBAR32(0x5084) & 0x10000) );
+	do {
+		reg = MCHBAR32(0x428c);
+	} while ((reg & 0x14) == 0);
+
+	// Set state of memory controller
+	reg = 0x112;
+	addr = 0x5030;
+	MCHBAR32(addr) = reg;
+	MCHBAR32(0x4ea0) = 0;
+	reg = (reg & ~0x2) | (1 << 0x1);	//ddr reset
+	MCHBAR32(addr) = reg;
+
+	// Assert dimm reset signal
+	reg = MCHBAR32(addr);
+	reg &= ~0x2;
+	MCHBAR32(addr) = reg;
+
+	// Wait 200us
+	udelay(200);
+
+	// Deassert dimm reset signal
+	reg = MCHBAR32(addr);
+	reg = (reg & ~0x2) | (1 << 0x1);
+	MCHBAR32(addr) = reg;
+
+	// Wait 500us
+	udelay(500);
+
+	// Enable DCLK
+	reg = MCHBAR32(addr);
+	reg = (reg & ~0x4) | (1 << 0x2);
+	MCHBAR32(addr) = reg;
+
+	// XXX Wait 20ns
+	udelay(1);
+
+	for (ch = 0; ch < 2; ch++) {
+		// Set valid rank CKE
+		reg = 0;
+		rmap = ctrl->rankmap[ch][0] | (ctrl->rankmap[ch][1] << 2);
+		reg = (reg & ~0xf) | rmap;
+		addr = (ch == 0) ? 0x42a0 : 0x46a0;
+		MCHBAR32(addr) = reg;
+
+		// Wait 10ns for ranks to settle
+		//udelay(0.01);
+
+		reg = (reg & ~0xf0) | (rmap << 0x4);
+		MCHBAR32(addr) = reg;
+
+		// Write reset using a NOP
+		reg = 0;
+		chw = (ctrl->rankmap[0][0] | ctrl->rankmap[0][1]) ? 0 : 1;
+		rank = (ctrl->rankmap[chw][0]) ? 0 : 2;
+		addr = (chw == 0) ? 0x428c : 0x468c;
+		do {
+			reg = MCHBAR32(addr);
+		} while ((reg & 0x14) == 0);
+
+		reg = 0;
+		reg = (reg & ~0xff) | 1;
+		reg = (reg & ~0x400000) | (1 << 0x16);
+		addr = (chw == 0) ? 0x4284 : 0x4684;
+		MCHBAR32(addr) = reg;
+	}
+}
+
+static odtmap get_ODT(ramctr_timing * ctrl, u8 rank)
+{
+	/* Get ODT based on rankmap: */
+	u8 dimms_per_ch, table;
+	u8 ch0dimmA, ch0dimmB, ch1dimmA, ch1dimmB;
+	static const odtmap odt_map[4][6] = {
+		{{60, 60}, {60, 60}, {120, 30}, {120, 30}, {120, 30},
+		 {120, 30}},
+		{{0, 0}, {60, 60}, {0, 0}, {0, 0}, {120, 30}, {120, 30}},
+		{{60, 60}, {60, 60}, {120, 30}, {120, 30}, {120, 30},
+		 {120, 30}},
+		{{0, 0}, {60, 60}, {0, 0}, {120, 30}, {0, 0}, {120, 30}}
+	};
+	ch0dimmA = (ctrl->rankmap[0][0] != 0) ? 1 : 0;
+	ch0dimmB = (ctrl->rankmap[0][1] != 0) ? 1 : 0;
+	ch1dimmA = (ctrl->rankmap[1][0] != 0) ? 1 : 0;
+	ch1dimmB = (ctrl->rankmap[1][1] != 0) ? 1 : 0;
+
+	dimms_per_ch = (ch0dimmA + ch0dimmB > ch1dimmA + ch1dimmB) ?
+	    ch0dimmA + ch0dimmB : ch1dimmA + ch1dimmB;
+	table = 0;
+
+	if (dimms_per_ch == 1) {
+		if ((ctrl->rankmap[0][0]
+		     | ctrl->rankmap[0][1]
+		     | ctrl->rankmap[1][0]
+		     | ctrl->rankmap[1][1]) != 3) {
+			table = 0;
+		} else {
+			table = 1;
+		}
+	} else if (dimms_per_ch == 2) {
+		if ((ctrl->rankmap[0][0] | ctrl->rankmap[0][1]) != 3) {
+			if ((ctrl->rankmap[1][0] | ctrl->rankmap[1][1]) != 3) {
+				table = 2;
+			} else {
+				table = 3;
+			}
+		} else {
+			if ((ctrl->rankmap[1][0] | ctrl->rankmap[1][1]) != 3) {
+				table = 4;
+			} else {
+				table = 5;
+			}
+		}
+	} else {
+		printram
+		    ("Huh, no dimms? m00 = %d m01 = %d m10 = %d m11 = %d dpc = %d\n",
+		     ctrl->rankmap[0][0], ctrl->rankmap[0][1],
+		     ctrl->rankmap[1][0], ctrl->rankmap[1][1], dimms_per_ch);
+		die("");
+	}
+
+	return odt_map[rank][table];
+}
+
+static void
+write_mrreg (ramctr_timing * ctrl, int channel, int slotrank, int reg, u32 val)
+{
+	u32 r32;
+	do {
+		r32 = MCHBAR32(0x428c + 0x400 * channel);
+	} while ((r32 & 0x50) == 0);
+
+	printk (BIOS_ERR, "MRd: %x <= %x\n", reg, val);
+
+  write32 (DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f000);
+  write32 (DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x41001);
+  write32 (DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, (slotrank << 24) | (reg << 20) | val | 0x60000);
+  write32 (DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0);
+
+  write32 (DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f000);
+  write32 (DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x41001);
+  write32 (DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, (slotrank << 24) | (reg << 20) | val | 0x60000);
+  write32 (DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0);
+
+  write32 (DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x0f000);
+  write32 (DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, 0x1001 | (ctrl->delay1 << 16));
+  write32 (DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, (slotrank << 24) | (reg << 20) | val | 0x60000);
+  write32 (DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0);
+  write32 (DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0x80001);
+}
+
+
+static void dram_mr0(ramctr_timing * ctrl, u8 rank)
+{
+	u16 mr0reg, mch_cas, mch_wr;
+	u8 ch;
+	static const u8 mch_wr_t[12] = { 1, 2, 3, 4, 0, 5, 0, 6, 0, 7, 0, 0 };
+	mr0reg = 0x100;
+
+	// Convert CAS to MCH register friendly
+	if (ctrl->CAS < 12) {
+		mch_cas = (u16) ((ctrl->CAS - 4) << 1);
+	} else {
+		mch_cas = (u16) (ctrl->CAS - 12);
+		mch_cas = ((mch_cas << 1) | 0x1);
+	}
+
+	// Convert tWR to MCH register friendly
+	mch_wr = mch_wr_t[ctrl->tWR - 5];
+
+	mr0reg = (mr0reg & ~0x4) | (mch_cas & 0x1);
+	mr0reg = (mr0reg & ~0x70) | ((mch_cas & 0xe) << 3);
+	mr0reg = (mr0reg & ~0xe00) | (mch_wr << 9);
+	// Fast (desktop) 0x1 or slow (mobile) 0x0
+	// FIXME: set this correctly
+	mr0reg = (mr0reg & ~0x1000) | (0x0 << 12);
+
+	for (ch = 0; ch < 2; ch++) {
+		write_mrreg (ctrl, ch, rank, 0, mr0reg);
+	}
+}
+
+static void dram_mr1(ramctr_timing * ctrl, u8 rank)
+{
+	u16 mr1reg;
+	u8 ch;
+	odtmap odt;
+
+	odt = get_ODT(ctrl, rank);
+	mr1reg = 0x2;
+
+	switch (odt.rttnom) {
+	case 30:
+		mr1reg = (mr1reg & ~0x200) | (1 << 9);	// RZQ/8
+		//fallthrough
+	case 60:
+		mr1reg = (mr1reg & ~0x4) | (1 << 2);	// RZQ/4
+		break;
+	case 120:
+		mr1reg = (mr1reg & ~0x40) | (1 << 6);	// RZQ/2
+		break;
+	default:
+	case 0:
+		break;
+	}
+
+	for (ch = 0; ch < 2; ch++) {
+		write_mrreg (ctrl, ch, rank, 1, mr1reg);
+	}
+}
+
+static void dram_mr2(ramctr_timing * ctrl, u8 rank)
+{
+	u16 pasr, cwl, asr, str, mr2reg;
+	u8 ch;
+	odtmap odt;
+
+	pasr = 0;
+	cwl = ctrl->CWL - 5;
+	asr =
+	    (ctrl->thermalrefresh & 0x1) & ((ctrl->thermalrefresh & 0x4) >> 2);
+	/* FIXME: compute STR.  */
+	str = 1;
+	odt = get_ODT(ctrl, rank);
+
+	mr2reg = 0;
+	mr2reg = (mr2reg & ~0x7) | pasr;
+	mr2reg = (mr2reg & ~0x38) | (cwl << 0x3);
+	mr2reg = (mr2reg & ~0x40) | (asr << 0x6);
+	mr2reg = (mr2reg & ~0x80) | (str << 0x7);
+	mr2reg |= (odt.rttwr / 60) << 9;
+
+	for (ch = 0; ch < 2; ch++) {
+		write_mrreg (ctrl, ch, rank, 2, mr2reg);
+	}
+}
+
+static void dram_mr3(ramctr_timing * ctrl, u8 rank)
+{
+	u16 mr3reg;
+	u8 ch;
+
+	mr3reg = 0;
+
+	for (ch = 0; ch < 2; ch++) {
+		write_mrreg (ctrl, ch, rank, 3, mr3reg);
+	}
+}
+
+void dram_mrscommands(ramctr_timing * ctrl)
+{
+	u8 rank, ch;
+	u32 reg, addr;
+
+	for (rank = 0; rank < 4; rank++) {
+		// MR2
+		printram("MR2 rank %d...", rank);
+		dram_mr2(ctrl, rank);
+		printram("done\n");
+
+		// MR3
+		printram("MR3 rank %d...", rank);
+		dram_mr3(ctrl, rank);
+		printram("done\n");
+
+		// MR1
+		printram("MR1 rank %d...", rank);
+		dram_mr1(ctrl, rank);
+		printram("done\n");
+
+		// MR0
+		printram("MR0 rank %d...", rank);
+		dram_mr0(ctrl, rank);
+		printram("done\n");
+	}
+
+	write32 (DEFAULT_MCHBAR + 0x4e20, 0x7);
+	write32 (DEFAULT_MCHBAR + 0x4e30, 0xf1001);
+	write32 (DEFAULT_MCHBAR + 0x4e00, 0x60002);
+	write32 (DEFAULT_MCHBAR + 0x4e10, 0);
+	write32 (DEFAULT_MCHBAR + 0x4e24, 0x1f003);
+	write32 (DEFAULT_MCHBAR + 0x4e34, 0x1901001);
+	write32 (DEFAULT_MCHBAR + 0x4e04, 0x60400);
+	write32 (DEFAULT_MCHBAR + 0x4e14, 0x288);
+	write32 (DEFAULT_MCHBAR + 0x4e84, 0x40004);
+
+	// Drain
+	for (ch = 0; ch < 2; ch++) {
+		// Wait for ref drained
+		addr = (ch == 0) ? 0x428c : 0x468c;
+		do {
+			reg = MCHBAR32(addr);
+		} while ((reg & 0x50) == 0);
+	}
+
+	// Refresh enable
+	reg = MCHBAR32(0x5030);
+	reg = (reg & ~0x8) | (1 << 0x3);
+	MCHBAR32(0x5030) = reg;
+
+	for (ch = 0; ch < 2; ch++) {
+		if ((ctrl->rankmap[ch][0] | ctrl->rankmap[ch][1]) != 0) {
+			addr = (ch == 0) ? 0x4020 : 0x4420;
+			reg = MCHBAR32(addr);
+			reg &= ~0x200000;
+			MCHBAR32(addr) = reg;
+
+			addr = (ch == 0) ? 0x428c : 0x468c;
+			reg = MCHBAR32(addr);
+
+			if ((reg & 0x10) == 0x10) {
+				printram("ERROR: Refresh enable failed\n");
+			} else {
+				printram("Refresh enable worked\n");
+			}
+
+			rank = (ctrl->rankmap[ch][0] != 0) ? 0 : 2;
+
+			// Drain
+			addr = (ch == 0) ? 0x428c : 0x468c;
+			do {
+				reg = MCHBAR32(addr);
+			} while ((reg & 0x50) == 0);
+
+			write32 (DEFAULT_MCHBAR + 0x4220 + ch * 0x400, 0x0f003);
+			write32 (0x4230 + 0x400 * ch + DEFAULT_MCHBAR, 0x659001);
+			write32 (DEFAULT_MCHBAR + 0x4200 + 0x400 * ch,
+				 (rank << 24) | (6 << 16));
+			write32 (DEFAULT_MCHBAR + 0x4210 + ch * 0x400, 0x3e0);
+
+			// Drain
+			addr = (ch == 0) ? 0x428c : 0x468c;
+			do {
+				reg = MCHBAR32(addr);
+			} while ((reg & 0x50) == 0);
+		}
+	}
+}
+
+static void
+wait_428c (int channel)
+{
+  while (1)
+    {
+      if (read32 (DEFAULT_MCHBAR | 0x428c | (channel << 10)) & 0x50)
+	return;
+    }
+}
+
+const u32 lane_registers[] = {0x0000, 0x0200, 0x0400, 0x0600,
+			      0x1000, 0x1200, 0x1400, 0x1600,
+			      0x0800 };
+
+
+static void
+program_timings (int channel, int slotrank, const struct ram_rank_timings *t)
+{
+  u32 reg32;
+  int lane;
+  reg32 = MCHBAR32 (0x4024 + channel * 0x400);
+  reg32 &= ~(0xff << (8 * slotrank));
+  reg32 |= t->val_4024 << (8 * slotrank);
+
+  MCHBAR32 (0x4024 + channel * 0x400) = reg32;
+
+  reg32 = MCHBAR32 (0x4028 + channel * 0x400);
+  reg32 &= ~(0xf << (4 * slotrank));
+  reg32 |= t->val_4028 << (4 * slotrank);
+  MCHBAR32 (0x4028 + channel * 0x400) = reg32;
+
+  FOR_ALL_LANES
+  {
+    MCHBAR32 (lane_registers[lane] + 0x10 + 0x100 * channel + 4 * slotrank)
+      = ((t->lanes[lane].r10b0 & 0x3f) | (t->lanes[lane].b1 << 8)
+	 | ((t->lanes[lane].r10b0 & 0x1c0) << 10) | (t->lanes[lane].b3 << 20));
+
+    MCHBAR32 (lane_registers[lane] + 0x20 + 0x100 * channel + 4 * slotrank)
+      = (t->lanes[lane].r20b0 | (t->lanes[lane].r20b8 << 8)
+	 | (t->lanes[lane].r20b15 << 15) | (t->lanes[lane].r20b19 << 19));
+  }
+}
+
+#define FOR_ALL_POPULATED_RANKS for (slotrank = 0; slotrank < 4; slotrank++) if ((ctrl->rankmap[channel][0] | ctrl->rankmap[channel][1]) & (1 << slotrank))
+
+static const struct ram_rank_timings rrt[2][4] =
+  {
+    {
+      {
+	.lanes = {
+	  { .r10b0 = 0x049, .b1 = 0x24, .b3 = 0x28, .r20b0 = 0x9, .r20b8 = 0x28, .r20b15 = 0x2, .r20b19 = 0x1 },
+	  { .r10b0 = 0x034, .b1 = 0x22, .b3 = 0x22, .r20b0 = 0x5, .r20b8 = 0x23, .r20b15 = 0x2, .r20b19 = 0x1 },
+	  { .r10b0 = 0x05f, .b1 = 0x21, .b3 = 0x23, .r20b0 = 0x2c, .r20b8 = 0xa, .r20b15 = 0x3, .r20b19 = 0x0 },
+	  { .r10b0 = 0x01a, .b1 = 0x24, .b3 = 0x26, .r20b0 = 0x30, .r20b8 = 0x11, .r20b15 = 0x2, .r20b19 = 0x0 },
+	  { .r10b0 = 0x0ae, .b1 = 0x1f, .b3 = 0x22, .r20b0 = 0x9, .r20b8 = 0x27, .r20b15 = 0x4, .r20b19 = 0x1 },
+	  { .r10b0 = 0x070, .b1 = 0x23, .b3 = 0x25, .r20b0 = 0x1, .r20b8 = 0x24, .r20b15 = 0x3, .r20b19 = 0x1 },
+	  { .r10b0 = 0x09a, .b1 = 0x22, .b3 = 0x26, .r20b0 = 0x2a, .r20b8 = 0x7, .r20b15 = 0x4, .r20b19 = 0x0 },
+	  { .r10b0 = 0x08f, .b1 = 0x26, .b3 = 0x2c, .r20b0 = 0x11, .r20b8 = 0x34, .r20b15 = 0x3, .r20b19 = 0x1 },
+	},
+	.val_4024 = 0x2f,
+	.val_4028 = 0x6,
+      },
+      {
+	.lanes = {
+	  { .r10b0 = 0x046, .b1 = 0x23, .b3 = 0x28, .r20b0 = 0x10, .r20b8 = 0x2f, .r20b15 = 0x2, .r20b19 = 0x1 },
+	  { .r10b0 = 0x034, .b1 = 0x1f, .b3 = 0x24, .r20b0 = 0xc, .r20b8 = 0x2a, .r20b15 = 0x2, .r20b19 = 0x1 },
+	  { .r10b0 = 0x05a, .b1 = 0x20, .b3 = 0x24, .r20b0 = 0x2d, .r20b8 = 0x9, .r20b15 = 0x3, .r20b19 = 0x0 },
+	  { .r10b0 = 0x019, .b1 = 0x23, .b3 = 0x26, .r20b0 = 0x2f, .r20b8 = 0x11, .r20b15 = 0x2, .r20b19 = 0x0 },
+	  { .r10b0 = 0x0ad, .b1 = 0x1f, .b3 = 0x22, .r20b0 = 0xc, .r20b8 = 0x29, .r20b15 = 0x4, .r20b19 = 0x1 },
+	  { .r10b0 = 0x06e, .b1 = 0x21, .b3 = 0x25, .r20b0 = 0x4, .r20b8 = 0x27, .r20b15 = 0x3, .r20b19 = 0x1 },
+	  { .r10b0 = 0x094, .b1 = 0x22, .b3 = 0x25, .r20b0 = 0x29, .r20b8 = 0x6, .r20b15 = 0x4, .r20b19 = 0x0 },
+	  { .r10b0 = 0x090, .b1 = 0x25, .b3 = 0x2b, .r20b0 = 0x14, .r20b8 = 0x37, .r20b15 = 0x3, .r20b19 = 0x1 },
+	},
+	.val_4024 = 0x2f,
+	.val_4028 = 0x6,
+      },
+      {
+	.lanes = {
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	},
+	.val_4024 = 0x0,
+	.val_4028 = 0x0,
+      },
+      {
+	.lanes = {
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	},
+	.val_4024 = 0x0,
+	.val_4028 = 0x0,
+      },
+    },
+    {
+      {
+	.lanes = {
+	  { .r10b0 = 0x06b, .b1 = 0x24, .b3 = 0x24, .r20b0 = 0x16, .r20b8 = 0x35, .r20b15 = 0x2, .r20b19 = 0x1 },
+	  { .r10b0 = 0x04c, .b1 = 0x25, .b3 = 0x26, .r20b0 = 0xc, .r20b8 = 0x2d, .r20b15 = 0x2, .r20b19 = 0x1 },
+	  { .r10b0 = 0x078, .b1 = 0x25, .b3 = 0x27, .r20b0 = 0x32, .r20b8 = 0x12, .r20b15 = 0x3, .r20b19 = 0x0 },
+	  { .r10b0 = 0x034, .b1 = 0x21, .b3 = 0x24, .r20b0 = 0x3a, .r20b8 = 0x1a, .r20b15 = 0x2, .r20b19 = 0x0 },
+	  { .r10b0 = 0x0cf, .b1 = 0x25, .b3 = 0x23, .r20b0 = 0x7, .r20b8 = 0x27, .r20b15 = 0x4, .r20b19 = 0x1 },
+	  { .r10b0 = 0x091, .b1 = 0x22, .b3 = 0x22, .r20b0 = 0x4, .r20b8 = 0x22, .r20b15 = 0x3, .r20b19 = 0x1 },
+	  { .r10b0 = 0x0b5, .b1 = 0x25, .b3 = 0x29, .r20b0 = 0x26, .r20b8 = 0x6, .r20b15 = 0x4, .r20b19 = 0x0 },
+	  { .r10b0 = 0x0b2, .b1 = 0x27, .b3 = 0x26, .r20b0 = 0x18, .r20b8 = 0x38, .r20b15 = 0x3, .r20b19 = 0x1 },
+	},
+	.val_4024 = 0x31,
+	.val_4028 = 0x8,
+      },
+      {
+	.lanes = {
+	  { .r10b0 = 0x06e, .b1 = 0x24, .b3 = 0x25, .r20b0 = 0x16, .r20b8 = 0x36, .r20b15 = 0x2, .r20b19 = 0x1 },
+	  { .r10b0 = 0x052, .b1 = 0x25, .b3 = 0x25, .r20b0 = 0x10, .r20b8 = 0x31, .r20b15 = 0x2, .r20b19 = 0x1 },
+	  { .r10b0 = 0x07d, .b1 = 0x26, .b3 = 0x26, .r20b0 = 0x39, .r20b8 = 0x19, .r20b15 = 0x3, .r20b19 = 0x0 },
+	  { .r10b0 = 0x035, .b1 = 0x23, .b3 = 0x24, .r20b0 = 0x3e, .r20b8 = 0x1f, .r20b15 = 0x2, .r20b19 = 0x0 },
+	  { .r10b0 = 0x0d4, .b1 = 0x25, .b3 = 0x25, .r20b0 = 0x12, .r20b8 = 0x31, .r20b15 = 0x4, .r20b19 = 0x1 },
+	  { .r10b0 = 0x09a, .b1 = 0x20, .b3 = 0x22, .r20b0 = 0x9, .r20b8 = 0x29, .r20b15 = 0x3, .r20b19 = 0x1 },
+	  { .r10b0 = 0x0b8, .b1 = 0x24, .b3 = 0x29, .r20b0 = 0x2d, .r20b8 = 0xd, .r20b15 = 0x4, .r20b19 = 0x0 },
+	  { .r10b0 = 0x0b7, .b1 = 0x23, .b3 = 0x27, .r20b0 = 0x1a, .r20b8 = 0x3a, .r20b15 = 0x3, .r20b19 = 0x1 },
+	},
+	.val_4024 = 0x31,
+	.val_4028 = 0x8,
+      },
+      {
+	.lanes = {
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	},
+	.val_4024 = 0x0,
+	.val_4028 = 0x0,
+      },
+      {
+	.lanes = {
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	  { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	},
+	.val_4024 = 0x0,
+	.val_4028 = 0x0,
+      },
+    },
+  };
+
+static void
+read_training (ramctr_timing *ctrl)
+{
+  int channel, slotrank, lane;
+
+  FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS
+  {
+    u32 r32;
+      wait_428c (channel);
+      write32 (DEFAULT_MCHBAR + 0x4220 + channel * 0x400, 0x1f002);
+      write32 (DEFAULT_MCHBAR + 0x4230 + channel * 0x400,
+	       0xc01 | (ctrl->tRP << 16));
+      write32 (DEFAULT_MCHBAR + 0x4200 + channel * 0x400,
+	       (slotrank << 24) | 0x60400);
+      write32 (DEFAULT_MCHBAR + 0x4210 + channel * 0x400, 0);
+      write32 (DEFAULT_MCHBAR + 0x4284 + channel * 0x400, 1);
+
+      write32 (DEFAULT_MCHBAR + 0x3400, (slotrank << 2) | 0x8001);
+
+      /* Timing discovery goes here.  */
+
+      write32 (DEFAULT_MCHBAR + 0x3400, 0);
+
+      r32 = read32 (DEFAULT_MCHBAR + 0x5030);
+      write32 (DEFAULT_MCHBAR + 0x5030, r32 | 0x20);
+      udelay (1);
+
+      write32 (DEFAULT_MCHBAR + 0x5030, r32 & ~0x20);
+
+     udelay (1);
+  }
+
+  FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS
+  {
+    ctrl->timings[channel][slotrank] = rrt[channel][slotrank];
+    program_timings (channel, slotrank, &rrt[channel][slotrank]);
+    FOR_ALL_LANES
+      write32 (0x4080 + 4 * lane + 0x400 * channel + DEFAULT_MCHBAR, 0);
+  }
+}
+
+static void
+write_training (ramctr_timing *ctrl)
+{
+  int channel, slotrank, lane;
+
+  FOR_ALL_CHANNELS
+	  if (ctrl->rankmap[channel][0] || ctrl->rankmap[channel][1])
+		  write32 (DEFAULT_MCHBAR + 0x4008 + 0x400 * channel,
+			   read32 (DEFAULT_MCHBAR + 0x4008 + 0x400 * channel) | 0x8000000);
+
+  /* Timing discovery goes here.  */
+
+  FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS
+  {
+    ctrl->timings[channel][slotrank] = rrt[channel][slotrank];
+    program_timings (channel, slotrank, &rrt[channel][slotrank]);
+    FOR_ALL_LANES
+      {
+	read32 (0x4080 + 4 * lane + 0x400 * channel + DEFAULT_MCHBAR);
+	write32 (0x4080 + 4 * lane + 0x400 * channel + DEFAULT_MCHBAR, 0);
+      }
+  }
+}
+
+  static const struct ram_rank_timings rrt2[2][4] = {
+    {
+      {
+	.lanes = {
+ { .r10b0 = 0x049, .b1 = 0x24, .b3 = 0x28, .r20b0 = 0x9, .r20b8 = 0x28, .r20b15 = 0x2, .r20b19 = 0x1 },
+ { .r10b0 = 0x034, .b1 = 0x22, .b3 = 0x22, .r20b0 = 0x5, .r20b8 = 0x23, .r20b15 = 0x2, .r20b19 = 0x1 },
+ { .r10b0 = 0x05f, .b1 = 0x21, .b3 = 0x23, .r20b0 = 0x2c, .r20b8 = 0xa, .r20b15 = 0x3, .r20b19 = 0x0 },
+ { .r10b0 = 0x01a, .b1 = 0x24, .b3 = 0x26, .r20b0 = 0x30, .r20b8 = 0x11, .r20b15 = 0x2, .r20b19 = 0x0 },
+ { .r10b0 = 0x0ae, .b1 = 0x1f, .b3 = 0x22, .r20b0 = 0x9, .r20b8 = 0x27, .r20b15 = 0x4, .r20b19 = 0x1 },
+ { .r10b0 = 0x070, .b1 = 0x23, .b3 = 0x25, .r20b0 = 0x1, .r20b8 = 0x24, .r20b15 = 0x3, .r20b19 = 0x1 },
+ { .r10b0 = 0x09a, .b1 = 0x22, .b3 = 0x26, .r20b0 = 0x2a, .r20b8 = 0x7, .r20b15 = 0x4, .r20b19 = 0x0 },
+ { .r10b0 = 0x08f, .b1 = 0x26, .b3 = 0x2c, .r20b0 = 0x11, .r20b8 = 0x34, .r20b15 = 0x3, .r20b19 = 0x1 },
+	},
+	.val_4024 = 0x2b,
+	.val_4028 = 0x6,
+      },
+      {
+	.lanes = {
+ { .r10b0 = 0x049, .b1 = 0x23, .b3 = 0x28, .r20b0 = 0x13, .r20b8 = 0x32, .r20b15 = 0x2, .r20b19 = 0x1 },
+ { .r10b0 = 0x037, .b1 = 0x1f, .b3 = 0x24, .r20b0 = 0xf, .r20b8 = 0x2d, .r20b15 = 0x2, .r20b19 = 0x1 },
+ { .r10b0 = 0x05d, .b1 = 0x20, .b3 = 0x24, .r20b0 = 0x30, .r20b8 = 0xc, .r20b15 = 0x3, .r20b19 = 0x0 },
+ { .r10b0 = 0x01c, .b1 = 0x23, .b3 = 0x26, .r20b0 = 0x32, .r20b8 = 0x14, .r20b15 = 0x2, .r20b19 = 0x0 },
+ { .r10b0 = 0x0b0, .b1 = 0x1f, .b3 = 0x22, .r20b0 = 0xf, .r20b8 = 0x2c, .r20b15 = 0x4, .r20b19 = 0x1 },
+ { .r10b0 = 0x071, .b1 = 0x21, .b3 = 0x25, .r20b0 = 0x7, .r20b8 = 0x2a, .r20b15 = 0x3, .r20b19 = 0x1 },
+ { .r10b0 = 0x097, .b1 = 0x22, .b3 = 0x25, .r20b0 = 0x2c, .r20b8 = 0x9, .r20b15 = 0x4, .r20b19 = 0x0 },
+ { .r10b0 = 0x093, .b1 = 0x25, .b3 = 0x2b, .r20b0 = 0x17, .r20b8 = 0x3a, .r20b15 = 0x3, .r20b19 = 0x1 },
+	},
+	.val_4024 = 0x2b,
+	.val_4028 = 0x6,
+      },
+      {
+	.lanes = {
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	},
+	.val_4024 = 0x20,
+	.val_4028 = 0x0,
+      },
+      {
+	.lanes = {
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	},
+	.val_4024 = 0x20,
+	.val_4028 = 0x0,
+      },
+    },
+    {
+      {
+	.lanes = {
+ { .r10b0 = 0x071, .b1 = 0x24, .b3 = 0x24, .r20b0 = 0x1c, .r20b8 = 0x3b, .r20b15 = 0x2, .r20b19 = 0x1 },
+ { .r10b0 = 0x052, .b1 = 0x25, .b3 = 0x26, .r20b0 = 0x12, .r20b8 = 0x33, .r20b15 = 0x2, .r20b19 = 0x1 },
+ { .r10b0 = 0x07e, .b1 = 0x25, .b3 = 0x27, .r20b0 = 0x38, .r20b8 = 0x18, .r20b15 = 0x3, .r20b19 = 0x0 },
+ { .r10b0 = 0x03a, .b1 = 0x21, .b3 = 0x24, .r20b0 = 0x0, .r20b8 = 0x20, .r20b15 = 0x2, .r20b19 = 0x1 },
+ { .r10b0 = 0x0d5, .b1 = 0x25, .b3 = 0x23, .r20b0 = 0xd, .r20b8 = 0x2d, .r20b15 = 0x4, .r20b19 = 0x1 },
+ { .r10b0 = 0x097, .b1 = 0x22, .b3 = 0x22, .r20b0 = 0xa, .r20b8 = 0x28, .r20b15 = 0x3, .r20b19 = 0x1 },
+ { .r10b0 = 0x0bb, .b1 = 0x25, .b3 = 0x29, .r20b0 = 0x2c, .r20b8 = 0xc, .r20b15 = 0x4, .r20b19 = 0x0 },
+ { .r10b0 = 0x0b8, .b1 = 0x27, .b3 = 0x26, .r20b0 = 0x1e, .r20b8 = 0x3e, .r20b15 = 0x3, .r20b19 = 0x1 },
+	},
+	.val_4024 = 0x2d,
+	.val_4028 = 0x8,
+      },
+      {
+	.lanes = {
+ { .r10b0 = 0x06e, .b1 = 0x24, .b3 = 0x25, .r20b0 = 0x16, .r20b8 = 0x36, .r20b15 = 0x2, .r20b19 = 0x1 },
+ { .r10b0 = 0x052, .b1 = 0x25, .b3 = 0x25, .r20b0 = 0x10, .r20b8 = 0x31, .r20b15 = 0x2, .r20b19 = 0x1 },
+ { .r10b0 = 0x07d, .b1 = 0x26, .b3 = 0x26, .r20b0 = 0x39, .r20b8 = 0x19, .r20b15 = 0x3, .r20b19 = 0x0 },
+ { .r10b0 = 0x035, .b1 = 0x23, .b3 = 0x24, .r20b0 = 0x3e, .r20b8 = 0x1f, .r20b15 = 0x2, .r20b19 = 0x0 },
+ { .r10b0 = 0x0d4, .b1 = 0x25, .b3 = 0x25, .r20b0 = 0x12, .r20b8 = 0x31, .r20b15 = 0x4, .r20b19 = 0x1 },
+ { .r10b0 = 0x09a, .b1 = 0x20, .b3 = 0x22, .r20b0 = 0x9, .r20b8 = 0x29, .r20b15 = 0x3, .r20b19 = 0x1 },
+ { .r10b0 = 0x0b8, .b1 = 0x24, .b3 = 0x29, .r20b0 = 0x2d, .r20b8 = 0xd, .r20b15 = 0x4, .r20b19 = 0x0 },
+ { .r10b0 = 0x0b7, .b1 = 0x23, .b3 = 0x27, .r20b0 = 0x1a, .r20b8 = 0x3a, .r20b15 = 0x3, .r20b19 = 0x1 },
+	},
+	.val_4024 = 0x2d,
+	.val_4028 = 0x8,
+      },
+      {
+	.lanes = {
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	},
+	.val_4024 = 0x20,
+	.val_4028 = 0x0,
+      },
+      {
+	.lanes = {
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+	},
+	.val_4024 = 0x20,
+	.val_4028 = 0x0,
+      },
+    },
+  };
+
+static const struct ram_rank_timings rrt3[2][4] = {
+
+  {
+    {
+      .lanes = {
+ { .r10b0 = 0x049, .b1 = 0x20, .b3 = 0x24, .r20b0 = 0x8, .r20b8 = 0x28, .r20b15 = 0x2, .r20b19 = 0x1 },
+ { .r10b0 = 0x034, .b1 = 0x1f, .b3 = 0x1f, .r20b0 = 0x4, .r20b8 = 0x23, .r20b15 = 0x2, .r20b19 = 0x1 },
+ { .r10b0 = 0x05f, .b1 = 0x20, .b3 = 0x20, .r20b0 = 0x2d, .r20b8 = 0xa, .r20b15 = 0x3, .r20b19 = 0x0 },
+ { .r10b0 = 0x01a, .b1 = 0x22, .b3 = 0x24, .r20b0 = 0x2f, .r20b8 = 0x11, .r20b15 = 0x2, .r20b19 = 0x0 },
+ { .r10b0 = 0x0ae, .b1 = 0x1f, .b3 = 0x1f, .r20b0 = 0x8, .r20b8 = 0x27, .r20b15 = 0x4, .r20b19 = 0x1 },
+ { .r10b0 = 0x070, .b1 = 0x21, .b3 = 0x22, .r20b0 = 0x0, .r20b8 = 0x24, .r20b15 = 0x3, .r20b19 = 0x1 },
+ { .r10b0 = 0x09a, .b1 = 0x20, .b3 = 0x23, .r20b0 = 0x27, .r20b8 = 0x7, .r20b15 = 0x4, .r20b19 = 0x0 },
+ { .r10b0 = 0x08f, .b1 = 0x24, .b3 = 0x24, .r20b0 = 0xf, .r20b8 = 0x34, .r20b15 = 0x3, .r20b19 = 0x1 },
+      },
+      .val_4024 = 0x2b,
+      .val_4028 = 0x6,
+    },
+    {
+      .lanes = {
+ { .r10b0 = 0x049, .b1 = 0x21, .b3 = 0x24, .r20b0 = 0x11, .r20b8 = 0x32, .r20b15 = 0x2, .r20b19 = 0x1 },
+ { .r10b0 = 0x037, .b1 = 0x20, .b3 = 0x1e, .r20b0 = 0xd, .r20b8 = 0x2d, .r20b15 = 0x2, .r20b19 = 0x1 },
+ { .r10b0 = 0x05d, .b1 = 0x20, .b3 = 0x1f, .r20b0 = 0x2f, .r20b8 = 0xc, .r20b15 = 0x3, .r20b19 = 0x0 },
+ { .r10b0 = 0x01c, .b1 = 0x23, .b3 = 0x24, .r20b0 = 0x31, .r20b8 = 0x14, .r20b15 = 0x2, .r20b19 = 0x0 },
+ { .r10b0 = 0x0b0, .b1 = 0x20, .b3 = 0x1f, .r20b0 = 0xe, .r20b8 = 0x2c, .r20b15 = 0x4, .r20b19 = 0x1 },
+ { .r10b0 = 0x071, .b1 = 0x20, .b3 = 0x21, .r20b0 = 0x7, .r20b8 = 0x2a, .r20b15 = 0x3, .r20b19 = 0x1 },
+ { .r10b0 = 0x097, .b1 = 0x20, .b3 = 0x22, .r20b0 = 0x29, .r20b8 = 0x9, .r20b15 = 0x4, .r20b19 = 0x0 },
+ { .r10b0 = 0x093, .b1 = 0x24, .b3 = 0x24, .r20b0 = 0x16, .r20b8 = 0x3a, .r20b15 = 0x3, .r20b19 = 0x1 },
+      },
+      .val_4024 = 0x2b,
+      .val_4028 = 0x6,
+    },
+    {
+      .lanes = {
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+      },
+      .val_4024 = 0x20,
+      .val_4028 = 0x0,
+    },
+    {
+      .lanes = {
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+      },
+      .val_4024 = 0x20,
+      .val_4028 = 0x0,
+    },
+  },
+  {
+    {
+      .lanes = {
+ { .r10b0 = 0x071, .b1 = 0x21, .b3 = 0x1f, .r20b0 = 0x19, .r20b8 = 0x3b, .r20b15 = 0x2, .r20b19 = 0x1 },
+ { .r10b0 = 0x052, .b1 = 0x24, .b3 = 0x21, .r20b0 = 0x10, .r20b8 = 0x33, .r20b15 = 0x2, .r20b19 = 0x1 },
+ { .r10b0 = 0x07e, .b1 = 0x23, .b3 = 0x22, .r20b0 = 0x36, .r20b8 = 0x18, .r20b15 = 0x3, .r20b19 = 0x0 },
+ { .r10b0 = 0x03a, .b1 = 0x1f, .b3 = 0x1f, .r20b0 = 0x3e, .r20b8 = 0x20, .r20b15 = 0x2, .r20b19 = 0x0 },
+ { .r10b0 = 0x0d5, .b1 = 0x23, .b3 = 0x22, .r20b0 = 0xa, .r20b8 = 0x2d, .r20b15 = 0x4, .r20b19 = 0x1 },
+ { .r10b0 = 0x097, .b1 = 0x1e, .b3 = 0x20, .r20b0 = 0x6, .r20b8 = 0x28, .r20b15 = 0x3, .r20b19 = 0x1 },
+ { .r10b0 = 0x0bb, .b1 = 0x20, .b3 = 0x23, .r20b0 = 0x2a, .r20b8 = 0xc, .r20b15 = 0x4, .r20b19 = 0x0 },
+ { .r10b0 = 0x0b8, .b1 = 0x20, .b3 = 0x22, .r20b0 = 0x1a, .r20b8 = 0x3e, .r20b15 = 0x3, .r20b19 = 0x1 },
+      },
+      .val_4024 = 0x2d,
+      .val_4028 = 0x8,
+    },
+    {
+      .lanes = {
+ { .r10b0 = 0x06e, .b1 = 0x20, .b3 = 0x21, .r20b0 = 0x13, .r20b8 = 0x36, .r20b15 = 0x2, .r20b19 = 0x1 },
+ { .r10b0 = 0x052, .b1 = 0x22, .b3 = 0x22, .r20b0 = 0xd, .r20b8 = 0x31, .r20b15 = 0x2, .r20b19 = 0x1 },
+ { .r10b0 = 0x07d, .b1 = 0x23, .b3 = 0x24, .r20b0 = 0x37, .r20b8 = 0x19, .r20b15 = 0x3, .r20b19 = 0x0 },
+ { .r10b0 = 0x035, .b1 = 0x1f, .b3 = 0x1f, .r20b0 = 0x3d, .r20b8 = 0x1f, .r20b15 = 0x2, .r20b19 = 0x0 },
+ { .r10b0 = 0x0d4, .b1 = 0x22, .b3 = 0x21, .r20b0 = 0xf, .r20b8 = 0x31, .r20b15 = 0x4, .r20b19 = 0x1 },
+ { .r10b0 = 0x09a, .b1 = 0x1d, .b3 = 0x20, .r20b0 = 0x7, .r20b8 = 0x29, .r20b15 = 0x3, .r20b19 = 0x1 },
+ { .r10b0 = 0x0b8, .b1 = 0x23, .b3 = 0x25, .r20b0 = 0x2b, .r20b8 = 0xd, .r20b15 = 0x4, .r20b19 = 0x0 },
+ { .r10b0 = 0x0b7, .b1 = 0x1e, .b3 = 0x23, .r20b0 = 0x15, .r20b8 = 0x3a, .r20b15 = 0x3, .r20b19 = 0x1 },
+      },
+      .val_4024 = 0x2d,
+      .val_4028 = 0x8,
+    },
+    {
+      .lanes = {
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+      },
+      .val_4024 = 0x20,
+      .val_4028 = 0x0,
+    },
+    {
+      .lanes = {
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+ { .r10b0 = 0x000, .b1 = 0x0, .b3 = 0x0, .r20b0 = 0x0, .r20b8 = 0x0, .r20b15 = 0x0, .r20b19 = 0x0 },
+      },
+      .val_4024 = 0x20,
+      .val_4028 = 0x0,
+    },
+  },
+};
+
+static void
+command_training (ramctr_timing *ctrl)
+{
+  int channel;
+  int slotrank;
+  u32 r32, reg_4004_b30;
+#if 0
+  MCHBAR32(0x4004) = 0x2c186465;
+  MCHBAR32(0x4020) = 0x10100005;
+  MCHBAR32(0x4404) = 0x2c186465;
+  MCHBAR32(0x4420) = 0x10100005;
+#endif
+
+  /* more timing discovery here.  */
+  /* c14/c18/320c discovery goes here.  */
+  /* 4004 discovery goes here*/
+  reg_4004_b30 = 0;
+
+  FOR_ALL_CHANNELS
+    if (ctrl->rankmap[channel][0] || ctrl->rankmap[channel][1])
+      /* FIXME: avoid unnecessarry readback.  */
+      MCHBAR32 (0x4004 + 0x400 * channel) = 
+	(MCHBAR32 (0x4004 + 0x400 * channel) & ~(3 << 30))
+	| (reg_4004_b30 << 30);
+
+  FOR_ALL_CHANNELS
+    FOR_ALL_POPULATED_RANKS
+      {
+	ctrl->timings[channel][slotrank] = rrt2[channel][slotrank];
+	program_timings (channel, slotrank, &rrt2[channel][slotrank]);
+      }
+
+  /* c14/c18/320c computation goes here.  */
+  MCHBAR32(0x320c) = 0x5fe4040;
+  MCHBAR32(0xc14) = 0x32cb3cc;
+  MCHBAR32(0xc18) = 0x0;
+  MCHBAR32(0xd14) = 0x32cb312;
+  MCHBAR32(0xd18) = 0x0;
+  MCHBAR32(0x330c) = 0x5fe40cc;
+
+  FOR_ALL_CHANNELS
+    if (ctrl->rankmap[channel][0] || ctrl->rankmap[channel][1])
+      {
+	wait_428c (channel);
+
+	/* choose an existing rank.  */
+	slotrank = !ctrl->rankmap[channel][0] ? 2 : 0;
+
+	write32 (DEFAULT_MCHBAR + 0x4220 + channel * 0x400, 0x0f003);
+	write32 (DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x41001);
+
+	write32 (DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, (slotrank << 24) | 0x60000);
+
+	write32 (DEFAULT_MCHBAR + 0x4210 + channel * 0x400, 0x3e0);
+
+	write32 (DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1);
+	wait_428c (channel);
+	write32 (DEFAULT_MCHBAR + 0x4020 + channel * 0x400,
+		 read32 (DEFAULT_MCHBAR + 0x4020 + channel * 0x400) | 0x200000);
+      }
+  write32 (DEFAULT_MCHBAR + 0x5030,
+	   read32 (DEFAULT_MCHBAR + 0x5030) & ~8);
+  FOR_ALL_CHANNELS
+    if (ctrl->rankmap[channel][0] || ctrl->rankmap[channel][1])
+      {
+	wait_428c (channel);
+
+	/* choose an existing rank.  */
+	slotrank = !ctrl->rankmap[channel][0] ? 2 : 0;
+
+	write32 (DEFAULT_MCHBAR + 0x4220 + channel * 0x400, 0x0f003);
+	write32 (DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x41001);
+
+	write32 (DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, (slotrank << 24) | 0x60000);
+
+	write32 (DEFAULT_MCHBAR + 0x4210 + channel * 0x400, 0x3e0);
+
+	write32 (DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1);
+	wait_428c (channel);
+      }
+
+  /* jedec reset */
+  dram_jedecreset(ctrl);
+  /* mrs commands. */
+  dram_mrscommands(ctrl);
+
+  r32 = read32 (DEFAULT_MCHBAR + 0x5030);
+  write32 (DEFAULT_MCHBAR + 0x5030, r32 | 0x20);
+  udelay (1);
+
+  write32 (DEFAULT_MCHBAR + 0x5030, r32 & ~0x20);
+
+  udelay (1);
+}
+
+static void
+round_trip_training (ramctr_timing *ctrl)
+{
+  int channel, slotrank;
+  /* more timing discovery here.  */
+  FOR_ALL_CHANNELS
+    for (slotrank = 0; slotrank < 4; slotrank++)
+      {
+	ctrl->timings[channel][slotrank] = rrt3[channel][slotrank];
+	program_timings (channel, slotrank, &rrt3[channel][slotrank]);
+      }
+}
+
+static void
+normalize_training (ramctr_timing *ctrl)
+{
+  int channel, slotrank, lane;
+  int mat = 0;
+
+  FOR_ALL_CHANNELS
+    FOR_ALL_POPULATED_RANKS
+	{
+	  int delta;
+	  FOR_ALL_LANES
+	    mat = max (ctrl->timings[channel][slotrank].lanes[lane].r10b0, mat);
+	  delta = (mat >> 6) - ctrl->timings[channel][slotrank].val_4028;
+	  ctrl->timings[channel][slotrank].val_4024 += delta;
+	  ctrl->timings[channel][slotrank].val_4028 += delta;
+	  program_timings (channel, slotrank, &ctrl->timings[channel][slotrank]);
+	}
+}
+
+
+void init_dram_ddr3(const dimm_layout *dimms)
+{
 	report_platform_info();
 
 	/* Wait for ME to be ready */
 	intel_early_me_init();
 	intel_early_me_uma_size();
 
-	printk(BIOS_DEBUG, "Starting UEFI PEI System Agent\n");
+	printk(BIOS_DEBUG, "Starting native Platform init\n");
 
-	memset(&sysinfo, 0, sizeof(sysinfo));
+	pch_init ();
 
-	sysinfo.boot_path = pei_data->boot_mode;
+	int channel;
+	int lane;
+	u32 reg_5d10;
+	msr_t msr;
 
-	/*
-	 * Do not pass MRC data in for recovery mode boot,
-	 * Always pass it in for S3 resume.
-	 */
-	if (!recovery_mode_enabled() || pei_data->boot_mode == 2)
-		prepare_mrc_cache(pei_data);
-
-	/* If MRC data is not found we cannot continue S3 resume. */
-	if (pei_data->boot_mode == 2 && !pei_data->mrc_input) {
-		printk(BIOS_DEBUG, "Giving up in sdram_initialize: No MRC data\n");
-		outb(0x6, 0xcf9);
-		hlt();
-	}
-
-	/* Pass console handler in pei_data */
-	pei_data->tx_byte = console_tx_byte;
-
-	/* Locate and call UEFI System Agent binary. */
-	/* TODO make MRC blob (0xab?) defined in cbfs_core.h. */
-	entry = cbfs_get_file_content(
-		CBFS_DEFAULT_MEDIA, "mrc.bin", 0xab, NULL);
-	if (entry) {
-		int rv;
-		rv = entry (pei_data);
-		if (rv) {
-			switch (rv) {
-			case -1:
-				printk(BIOS_ERR, "PEI version mismatch.\n");
-				break;
-			case -2:
-				printk(BIOS_ERR, "Invalid memory frequency.\n");
-				break;
-			default:
-				printk(BIOS_ERR, "MRC returned %x.\n", rv);
-			}
-			die("Nonzero MRC return value.\n");
-		}
+	wait_txt_clear ();
+
+	msr.lo = 0;
+	msr.hi = 0;
+	wrmsr (0x000002e6, msr);
+	pci_read_config32 (SOUTHBRIDGE, 0xa0);	// !!! = 0x00040200
+	cpuid_ext (0x1, 0x0);		// !!! = 0x00000000000306a9
+	pcie_read_config32 (HECIDEV, 0x48);	// !!! = 0x30000126
+
+	reg_5d10 = read32 (DEFAULT_MCHBAR | 0x5d10);	// !!! = 0x00000000
+	if ((pcie_read_config16 (SOUTHBRIDGE, 0xa2) & 0xa0) == 0x20	/* 0x0004 */
+	    && reg_5d10)
+	{
+		/* Need reset.  */
+		outb (0x6, 0xcf9);
+
+		while (1);
+	}
+
+	ramctr_timing ctrl;
+
+	dimm_info info;
+	u32 reg, addr;
+	struct cpuid_result cpures;
+	u32 cpu;
+
+	/* Get DDR3 SPD data */
+	dram_find_spds_ddr3(dimms, &info, &ctrl);
+
+	/* Find fastest common supported parameters */
+	dram_find_common_params(&info, &ctrl);
+
+	/* Calculate timings */
+	dram_timing(&ctrl);
+
+	/* Set MCU frequency */
+	dram_freq(&ctrl);
+
+	/* Set version register */
+	MCHBAR32(0x5034) = 0xC04EB002;
+
+	/* Enable crossover */
+	dram_xover(&ctrl);
+
+	/* Set timing and refresh registers */
+	dram_timing_regs(&ctrl);
+
+	/* Power mode preset */
+	MCHBAR32(0x4e80) = 0x5500;
+
+	/* Set scheduler parameters */
+	MCHBAR32(0x4c20) = 0x10100005;
+
+	/* Set cpu specific register */
+	cpures = cpuid(0);
+	cpu = (cpures.eax);
+	if (IS_SANDY_CPU(cpu) && (IS_SANDY_CPU_D0(cpu) || IS_SANDY_CPU_D1(cpu))) {
+		MCHBAR32(0x4f8c) = 0x141D1519;
 	} else {
-		die("UEFI PEI System Agent not found.\n");
+		MCHBAR32(0x4f8c) = 0x551D1519;
+	}
+
+	/* Clear IO reset bit */
+	reg = MCHBAR32(0x5030);
+	reg &= ~0x20;
+	MCHBAR32(0x5030) = reg;
+
+	/* TODO Fix dimm map - Set MAD-DIMM registers */
+	dram_dimm_mapping(&info, &ctrl);
+	printram("Done dimm mapping\n");
+
+	/* Zone config */
+	dram_zones(&info, &ctrl);
+
+	/* Set memory map */
+	dram_memorymap(&info);
+	printram("Done memory map\n");
+
+	/* Set IO registers */
+	dram_ioregs(&ctrl, cpu);
+	printram("Done io registers\n");
+
+	udelay (1);
+
+	/* Do jedec ddr3 reset sequence */
+	dram_jedecreset(&ctrl);
+	printram("Done jedec reset\n");
+
+	/* MRS commands */
+	dram_mrscommands(&ctrl);
+	printram("Done MRS commands\n");
+	dram_mrscommands(&ctrl);
+
+	/* Prepare for memory training */
+	FOR_ALL_CHANNELS {
+		// Always drive command bus
+		addr = 0x4004 + 0x400 * channel;
+		reg = MCHBAR32(addr);
+		reg = (reg & ~0x20000000) | (1 << 0x1d);
+		MCHBAR32(addr) = reg;
 	}
 
+	udelay (1);
+
+	wait_428c (0);
+
+	read_training (&ctrl);
+	write_training (&ctrl);
+  
+	printk (BIOS_ERR, "CP5b\n");
+
+	command_training(&ctrl);
+
+	printk (BIOS_ERR, "CP5c\n");
+
+	round_trip_training (&ctrl);
+	normalize_training (&ctrl);
+
+	int slotrank;
+
+	for (channel = 0; channel < 2; channel++)
+	{
+		read32 (DEFAULT_MCHBAR | 0x400c | (channel << 10));	// !!! = 0x000258b4
+		write32 (DEFAULT_MCHBAR | 0x400c | (channel << 10), 0x000058b4);
+		FOR_ALL_LANES
+		{
+			read32 (DEFAULT_MCHBAR | 0x4024 | (channel << 10));	// !!! = 0x20202828
+			read32 (DEFAULT_MCHBAR | (lane_registers[lane] + channel * 0x100) | 0x0010);	// !!! = 0x0231200a
+			read32 (DEFAULT_MCHBAR | 0x4028 | (channel << 10));	// !!! = 0x000e0033
+			read32 (DEFAULT_MCHBAR | (lane_registers[lane] + channel * 0x100) | 0x0010);	// !!! = 0x0231200a
+			read32 (DEFAULT_MCHBAR | 0x4024 | (channel << 10));	// !!! = 0x20202828
+			read32 (DEFAULT_MCHBAR | (lane_registers[lane] + channel * 0x100) | 0x0014);	// !!! = 0x02411f06
+			read32 (DEFAULT_MCHBAR | 0x4028 | (channel << 10));	// !!! = 0x000e0033
+			read32 (DEFAULT_MCHBAR | (lane_registers[lane] + channel * 0x100) | 0x0014);	// !!! = 0x02411f06
+		}
+		write32 (DEFAULT_MCHBAR | 0x4008 | (channel << 10), 0x0a042220);
+	}
+	write32 (DEFAULT_MCHBAR | 0x0004, 0x00000d70);
+	write32 (DEFAULT_MCHBAR | 0x0008, 0x00000006);
+	write32 (DEFAULT_MCHBAR | 0x0204, 0x00000d70);
+	write32 (DEFAULT_MCHBAR | 0x0208, 0x00000006);
+	write32 (DEFAULT_MCHBAR | 0x0104, 0x00000d70);
+	write32 (DEFAULT_MCHBAR | 0x0108, 0x00000006);
+	write32 (DEFAULT_MCHBAR | 0x0304, 0x00000d70);
+	write32 (DEFAULT_MCHBAR | 0x0308, 0x00000006);
+	read32 (DEFAULT_MCHBAR | 0x42a0);	// !!! = 0x00000033
+	read32 (DEFAULT_MCHBAR | 0x46a0);	// !!! = 0x00000033
+	for (channel = 0; channel < 2; channel++)
+	{
+		fill_pattern2 (0x04000000 + channel * 0x40);
+		write32 (DEFAULT_MCHBAR | 0x4288 | (channel << 10), 0x00000000);
+	}
+
+	for (slotrank = 0; slotrank < 2; slotrank++)
+		for (channel = 0; channel < 2; channel++)
+		{
+			write32 (DEFAULT_MCHBAR | 0x4f40, 0x00000000);
+			write32 (DEFAULT_MCHBAR | 0x4d40, 0x00000000);
+			write32 (DEFAULT_MCHBAR | 0x4f44, 0x00000000);
+			write32 (DEFAULT_MCHBAR | 0x4d44, 0x00000000);
+			write32 (DEFAULT_MCHBAR | 0x4f48, 0x00000000);
+			write32 (DEFAULT_MCHBAR | 0x4d48, 0x00000000);
+			write32 (DEFAULT_MCHBAR | 0x4f4c, 0x00000000);
+			write32 (DEFAULT_MCHBAR | 0x4d4c, 0x00000000);
+			write32 (DEFAULT_MCHBAR | 0x4f50, 0x00000000);
+			write32 (DEFAULT_MCHBAR | 0x4d50, 0x00000000);
+			write32 (DEFAULT_MCHBAR | 0x4f54, 0x00000000);
+			write32 (DEFAULT_MCHBAR | 0x4d54, 0x00000000);
+			write32 (DEFAULT_MCHBAR | 0x4f58, 0x00000000);
+			write32 (DEFAULT_MCHBAR | 0x4d58, 0x00000000);
+			write32 (DEFAULT_MCHBAR | 0x4f5c, 0x00000000);
+			write32 (DEFAULT_MCHBAR | 0x4d5c, 0x00000000);
+			write32 (DEFAULT_MCHBAR | 0x4f60, 0x00000000);
+			write32 (DEFAULT_MCHBAR | 0x4d60, 0x00000000);
+			wait_428c (channel);
+			write32 (DEFAULT_MCHBAR | 0x4220 | (channel << 10), 0x0001f006);
+			write32 (DEFAULT_MCHBAR | 0x4230 | (channel << 10), 0x0028a004);
+			write32 (DEFAULT_MCHBAR | 0x4200 | (channel << 10),
+				 0x00060000 | (slotrank << 24));
+			write32 (DEFAULT_MCHBAR | 0x4210 | (channel << 10), 0x00000244);
+			write32 (DEFAULT_MCHBAR | 0x4224 | (channel << 10), 0x0001f201);
+			write32 (DEFAULT_MCHBAR | 0x4234 | (channel << 10), 0x08281064);
+			write32 (DEFAULT_MCHBAR | 0x4204 | (channel << 10),
+				 0x00000000 | (slotrank << 24));
+			write32 (DEFAULT_MCHBAR | 0x4214 | (channel << 10), 0x00000242);
+			write32 (DEFAULT_MCHBAR | 0x4228 | (channel << 10), 0x0001f105);
+			write32 (DEFAULT_MCHBAR | 0x4238 | (channel << 10), 0x04281064);
+			write32 (DEFAULT_MCHBAR | 0x4208 | (channel << 10),
+				 0x00000000 | (slotrank << 24));
+			write32 (DEFAULT_MCHBAR | 0x4218 | (channel << 10), 0x00000242);
+			write32 (DEFAULT_MCHBAR | 0x422c | (channel << 10), 0x0001f002);
+			write32 (DEFAULT_MCHBAR | 0x423c | (channel << 10), 0x00280c01);
+			write32 (DEFAULT_MCHBAR | 0x420c | (channel << 10),
+				 0x00060400 | (slotrank << 24));
+			write32 (DEFAULT_MCHBAR | 0x421c | (channel << 10), 0x00000240);
+			write32 (DEFAULT_MCHBAR | 0x4284 | (channel << 10), 0x000c0001);
+			wait_428c (channel);
+			read32 (DEFAULT_MCHBAR | 0x4340 | (channel << 10));	// !!! = 0x00000000
+			read32 (DEFAULT_MCHBAR | 0x4340 | (channel << 10));	// !!! = 0x00000000
+			read32 (DEFAULT_MCHBAR | 0x4340 | (channel << 10));	// !!! = 0x00000000
+			read32 (DEFAULT_MCHBAR | 0x4340 | (channel << 10));	// !!! = 0x00000000
+			read32 (DEFAULT_MCHBAR | 0x4340 | (channel << 10));	// !!! = 0x00000000
+			read32 (DEFAULT_MCHBAR | 0x4340 | (channel << 10));	// !!! = 0x00000000
+			read32 (DEFAULT_MCHBAR | 0x4340 | (channel << 10));	// !!! = 0x00000000
+			read32 (DEFAULT_MCHBAR | 0x4340 | (channel << 10));	// !!! = 0x00000000
+		}
+
+	write32 (DEFAULT_MCHBAR | 0x5024, 0x00a030ce);
+	read32 (DEFAULT_MCHBAR | 0x4020);	// !!! = 0x10100005
+	write32 (DEFAULT_MCHBAR | 0x4020, 0x00100005);
+	write32 (DEFAULT_MCHBAR | 0x4034, 0x00009a36);
+	write32 (DEFAULT_MCHBAR | 0x403c, 0xbafcfdcf);
+	write32 (DEFAULT_MCHBAR | 0x4038, 0x46d1ab68);
+	read32 (DEFAULT_MCHBAR | 0x4420);	// !!! = 0x10100005
+	write32 (DEFAULT_MCHBAR | 0x4420, 0x00100005);
+	write32 (DEFAULT_MCHBAR | 0x4434, 0x00028bfa);
+	write32 (DEFAULT_MCHBAR | 0x443c, 0x53fe4b49);
+	write32 (DEFAULT_MCHBAR | 0x4438, 0x19ed5483);
+	read32 (DEFAULT_MCHBAR | 0x4004);	// !!! = 0x2c186465
+	write32 (DEFAULT_MCHBAR | 0x4004, 0x0c186465);
+	write32 (DEFAULT_MCHBAR | 0x42a0, 0x00001003);
+	read32 (DEFAULT_MCHBAR | 0x4404);	// !!! = 0x2c186465
+	write32 (DEFAULT_MCHBAR | 0x4404, 0x0c186465);
+	write32 (DEFAULT_MCHBAR | 0x46a0, 0x00001003);
+	write32 (DEFAULT_MCHBAR | 0x4cd4, 0x00000046);
+	read32 (DEFAULT_MCHBAR | 0x400c);	// !!! = 0x000058b4
+	write32 (DEFAULT_MCHBAR | 0x400c, 0x000058b4);
+	read32 (DEFAULT_MCHBAR | 0x440c);	// !!! = 0x000058b4
+	write32 (DEFAULT_MCHBAR | 0x440c, 0x000058b4);
+	write32 (DEFAULT_MCHBAR | 0x4cb0, 0x00000740);
+	write32 (DEFAULT_MCHBAR | 0x4380, 0x00000aaa);
+	write32 (DEFAULT_MCHBAR | 0x4780, 0x00000aaa);
+	write32 (DEFAULT_MCHBAR | 0x4f88, 0x5f7003ff);
+	write32 (DEFAULT_MCHBAR | 0x5064, 0x00073193);
+	write32 (DEFAULT_MCHBAR | 0x4384, 0x009b6ea1);
+	write32 (DEFAULT_MCHBAR | 0x4784, 0x009b6ea1);
+	write32 (DEFAULT_MCHBAR | 0x5880, 0xca9171e5);
+	read32 (DEFAULT_MCHBAR | 0x5888);	// !!! = 0x00e4d5d0
+	write32 (DEFAULT_MCHBAR | 0x5888, 0x00e4d5d0);
+	read32 (DEFAULT_MCHBAR | 0x58a8);	// !!! = 0x00000000
+	write32 (DEFAULT_MCHBAR | 0x58a8, 0x00000000);
+	read32 (DEFAULT_MCHBAR | 0x4294);	// !!! = 0x000098ff
+	write32 (DEFAULT_MCHBAR | 0x4294, 0x000198ff);
+	read32 (DEFAULT_MCHBAR | 0x4694);	// !!! = 0x000098ff
+	write32 (DEFAULT_MCHBAR | 0x4694, 0x000198ff);
+	read32 (DEFAULT_MCHBAR | 0x5030);	// !!! = 0x0000011e
+	write32 (DEFAULT_MCHBAR | 0x5030, 0x0000011f);
+	read32 (DEFAULT_MCHBAR | 0x5030);	// !!! = 0x0000011f
+	write32 (DEFAULT_MCHBAR | 0x5030, 0x0000019f);
+	write32 (DEFAULT_MCHBAR | 0x5f18, 0x000000fa);
+	read32 (DEFAULT_MCHBAR | 0x4290);	// !!! = 0x00004080
+	read32 (DEFAULT_MCHBAR | 0x5064);	// !!! = 0x00073193
+	read32 (DEFAULT_MCHBAR | 0x42a4);	// !!! = 0x41f88200
+	read32 (DEFAULT_MCHBAR | 0x5f10);	// !!! = 0x1f425810
+	read32 (DEFAULT_MCHBAR | 0x5f00);	// !!! = 0x0000270f
+	read32 (DEFAULT_MCHBAR | 0x5f20);	// !!! = 0x5dc20000
+	read32 (DEFAULT_MCHBAR | 0x5f18);	// !!! = 0x000000fa
+	read32 (DEFAULT_MCHBAR | 0x5d10);	// !!! = 0x00000000
+	write32 (DEFAULT_MCHBAR | 0x5d10, 0x2010040c);
+
 #if CONFIG_USBDEBUG_IN_ROMSTAGE
 	/* mrc.bin reconfigures USB, so reinit it to have debug */
 	usbdebug_init();
 #endif
 
-	/* For reference print the System Agent version
-	 * after executing the UEFI PEI stage.
-	 */
-	u32 version = MCHBAR32(0x5034);
-	printk(BIOS_DEBUG, "System Agent Version %d.%d.%d Build %d\n",
-		version >> 24 , (version >> 16) & 0xff,
-		(version >> 8) & 0xff, version & 0xff);
-
-	/* Send ME init done for SandyBridge here.  This is done
-	 * inside the SystemAgent binary on IvyBridge. */
-	if (BASE_REV_SNB ==
-	    (pci_read_config16(PCI_CPU_DEVICE, PCI_DEVICE_ID) & BASE_REV_MASK))
-		intel_early_me_init_done(ME_INIT_STATUS_SUCCESS);
-	else
-		intel_early_me_status();
-
-	post_system_agent_init(pei_data);
+//	intel_early_me_init_done(ME_INIT_STATUS_SUCCESS);
+
+	post_system_agent_init();
 	report_memory_config();
 }
diff --git a/src/northbridge/intel/sandybridge/raminit.h b/src/northbridge/intel/sandybridge/raminit.h
index c3b1c2a..a90c477 100644
--- a/src/northbridge/intel/sandybridge/raminit.h
+++ b/src/northbridge/intel/sandybridge/raminit.h
@@ -20,7 +20,7 @@
 #ifndef RAMINIT_H
 #define RAMINIT_H
 
-#include "pei_data.h"
+#include <device/dram/ddr3.h>
 
 struct sys_info {
 	u8 boot_path;
@@ -29,8 +29,151 @@ struct sys_info {
 #define BOOT_PATH_RESUME	2
 } __attribute__ ((packed));
 
-void sdram_initialize(struct pei_data *pei_data);
-void save_mrc_data(struct pei_data *pei_data);
 int fixup_sandybridge_errata(void);
 
+#define SANDYIVY_MAX_DIMM_SLOTS 4
+#define SANDYIVY_MAX_CHANNELS 2
+#define SANDYIVY_MAX_RANKS_PER_CH 4
+#define SANDYIVY_MAX_RANKS_PER_DIMM 2
+#define SANDYIVY_MAX_SDRAM_PER_DIMM 9
+
+#define BASEFREQ 133
+#define tDLLK 512
+
+#define IS_SANDY_CPU(x) ((x & 0xffff0) == 0x206a0)
+#define IS_SANDY_CPU_C(x) ((x & 0xf) == 4)
+#define IS_SANDY_CPU_D0(x) ((x & 0xf) == 5)
+#define IS_SANDY_CPU_D1(x) ((x & 0xf) == 6)
+#define IS_SANDY_CPU_D2(x) ((x & 0xf) == 7)
+
+#define IS_IVY_CPU(x) ((x & 0xffff0) == 0x306a0)
+#define IS_IVY_CPU_C(x) ((x & 0xf) == 4)
+#define IS_IVY_CPU_K(x) ((x & 0xf) == 5)
+#define IS_IVY_CPU_D(x) ((x & 0xf) == 6)
+#define IS_IVY_CPU_E(x) ((x & 0xf) >= 8)
+
+
+typedef struct odtmap_st {
+	u16 rttwr;
+	u16 rttnom;
+} odtmap;
+
+typedef struct dimm_layout_st
+{
+        /* The address of the DIMM on the SMBUS
+	 * the order is ch0dimmA, ch0dimmB, ch1dimmA, ch1dimmB
+	 */
+        u8 spd_addr[SANDYIVY_MAX_DIMM_SLOTS];
+} dimm_layout;
+
+typedef struct dimm_info_st
+{
+        dimm_attr dimm[SANDYIVY_MAX_DIMM_SLOTS];
+} dimm_info;
+
+typedef struct mem_rank_st {
+        u16 start_addr;
+        u16 end_addr;
+} mem_rank;
+
+typedef struct rank_layout_st {
+        u32 phys_rank_size_mb[SANDYIVY_MAX_RANKS_PER_CH];
+        mem_rank virt[SANDYIVY_MAX_RANKS_PER_CH];
+        dimm_flags_t flags[SANDYIVY_MAX_RANKS_PER_CH];
+} rank_layout;
+
+typedef struct pci_reg8_st {
+        u8 addr;
+        u8 val;
+} pci_reg8;
+
+typedef u8 timing_dly[8];
+
+typedef struct delay_range_st {
+        timing_dly low;
+        timing_dly avg;
+        timing_dly high;
+} delay_range;
+
+typedef struct board_delay_calib_st {
+        delay_range rx_dq_cr;
+        delay_range rx_dqs;
+        /* Transmit delays are calibrated for each dimm */
+        delay_range tx_dq[SANDYIVY_MAX_DIMM_SLOTS];
+        delay_range tx_dqs[SANDYIVY_MAX_DIMM_SLOTS];
+} board_delay_calib;
+
+struct ram_rank_timings
+{
+  /* Register 4024. One byte per slot.  */
+  u8 val_4024;
+  /* Register 4028. One nibble per slot.  */
+  u8 val_4028;
+  struct ram_lane_timings
+  {
+    /* lane register offset 0x10.  */
+    u16 r10b0; /* bits 0 - 5, bits 16 - 18 */
+    u8 b1;			/* bits 8 - 14 */
+    u8 b3;			/* bits 20 - 26.  */
+
+    /* lane register offset 0x20.  */
+    u8 r20b0;			/* bit 0 - ?.  */
+    u8 r20b8;			/* bits 8 - 13.  */
+    u8 r20b15;			/* bits 15 - 17.  */
+    u16 r20b19;			/* bits 19 - ?.  */
+  } lanes[8];
+};
+
+typedef struct ramctr_timing_st {
+        enum spd_memory_type dram_type;
+        u16 cas_supported;
+        /* tLatencies are in units of ns, scaled by x256 */
+        u32 tCK;
+        u32 tAA;
+        u32 tWR;
+        u32 tRCD;
+        u32 tRRD;
+        u32 tRP;
+        u32 tRAS;
+        u32 tRC;
+        u32 tRFC;
+        u32 tWTR;
+        u32 tRTP;
+        u32 tFAW;
+        /* Latencies in terms of clock cycles
+         * They are saved separately as they are needed for DRAM MRS commands*/
+        u8 CAS; /* CAS read latency */
+        u8 CWL; /* CAS write latency */
+        /* Number of dimms currently connected */
+        u8 n_dimms;
+
+	u32 tREFI;
+	u32 tMOD;
+	u32 tXSOffset;
+	u32 tWLO;
+	u32 tCKE;
+	u32 tXPDLL;
+	u32 tXP;
+	u32 tAONPD;
+
+	u32 delay1;
+	u32 delay2;
+
+	u8 eccsupport;
+	u8 dualchannel;
+	u8 thermalrefresh;
+
+	u8 channelmap;
+	u8 rankmap[2][2]; //channels, dimms
+
+	struct ram_rank_timings timings[2][4]; /* channel, ranks.  */
+} ramctr_timing;
+
+void dram_find_spds_ddr3(const dimm_layout *addr, dimm_info *dimm, ramctr_timing *ctrl);
+void dram_find_common_params(const dimm_info *info, ramctr_timing *ctrl);
+void init_dram_ddr3(const dimm_layout *dimms);
+void dram_jedecreset(ramctr_timing * ctrl);
+void dram_mrscommands(ramctr_timing * ctrl);
+void pch_init(void);
+
 #endif				/* RAMINIT_H */



More information about the coreboot-gerrit mailing list