[coreboot] New patch to review for coreboot: 901327e NOTFORMERGE: VX900 early init (non-functional)

Alexandru Gagniuc (mr.nuke.me@gmail.com) gerrit at coreboot.org
Fri Jul 13 22:37:00 CEST 2012


Alexandru Gagniuc (mr.nuke.me at gmail.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/1228

-gerrit

commit 901327ec7ecec94aba8d6ca6765b9f8da8495835
Author: Alexandru Gagniuc <mr.nuke.me at gmail.com>
Date:   Fri Jul 13 15:17:38 2012 -0500

    NOTFORMERGE: VX900 early init (non-functional)
    
    Just a backup push. Please ignore.
    
    Change-Id: I7624944dbc05fbf3019897a116954d71dfda0031
    Signed-off-by: Alexandru Gagniuc <mr.nuke.me at gmail.com>
---
 src/devices/dram/dram.h                            |  154 ++
 src/devices/dram/dram_util.c                       |  297 +++
 src/devices/smbus/early_smbus.c                    |  141 ++
 src/devices/smbus/smbus.h                          |   99 +
 src/mainboard/via/epia-m850/.directory             |    4 +
 src/mainboard/via/epia-m850/Kconfig                |   47 +
 src/mainboard/via/epia-m850/Makefile.inc           |   21 +
 src/mainboard/via/epia-m850/chip.h                 |   21 +
 src/mainboard/via/epia-m850/devicetree.cb          |   62 +
 src/mainboard/via/epia-m850/mainboard.c            |   25 +
 src/mainboard/via/epia-m850/romstage.c             |   76 +
 src/northbridge/via/vx900/Kconfig                  |   23 +
 src/northbridge/via/vx900/Makefile.inc             |   35 +
 src/northbridge/via/vx900/early_smbus.c            |  191 ++
 src/northbridge/via/vx900/early_vx900.h            |   35 +
 src/northbridge/via/vx900/forgotten.c              |  192 ++
 src/northbridge/via/vx900/forgotten.h              |   78 +
 src/northbridge/via/vx900/raminit.h                |   77 +
 src/northbridge/via/vx900/raminit_ddr3.c           | 1027 ++++++++++
 .../via/vx900/raminit_ddr3_delay_calib.c           | 2150 ++++++++++++++++++++
 src/northbridge/via/vx900/romstrap.inc             |   50 +
 src/northbridge/via/vx900/romstrap.lds             |   27 +
 src/northbridge/via/vx900/vx900.h                  |   26 +
 23 files changed, 4858 insertions(+), 0 deletions(-)

diff --git a/src/devices/dram/dram.h b/src/devices/dram/dram.h
new file mode 100644
index 0000000..d79cc52
--- /dev/null
+++ b/src/devices/dram/dram.h
@@ -0,0 +1,154 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011  Alexandru Gagniuc <mr.nuke.me 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, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DRAM_H
+#define DRAM_H
+
+#include <stdint.h>
+
+/* DRAM type, byte 2 of spd */
+#define DRAM_TYPE_UNDEFINED             0x00
+#define DRAM_TYPE_FPM_DRAM              0x01
+#define DRAM_TYPE_EDO                   0x02
+#define DRAM_TYPE_PIPELINED_NIBBLE      0x03
+#define DRAM_TYPE_SDRAM                 0x04
+#define DRAM_TYPE_ROM                   0x05
+#define DRAM_TYPE_DDR_SGRAM             0x06
+#define DRAM_TYPE_DDR                   0x07
+#define DRAM_TYPE_DDR2                  0x08
+#define DRAM_TYPE_DDR2_FBDIMM           0x09
+#define DRAM_TYPE_DDR2_FB_PROBE         0x0a
+#define DRAM_TYPE_DDR3                  0x0b
+
+/* Module type (byte 3, bits 3:0) of SPD */
+#define DIMM_TYPE_UNDEFINED     0
+#define DIMM_TYPE_RDIMM         1
+#define DIMM_TYPE_UDIMM         2
+#define DIMM_TYPE_SO_DIMM       3
+#define DIMM_TYPE_MICRO_DIMM    4
+#define DIMM_TYPE_MINI_RDIMM    5
+#define DIMM_TYPE_MINI_UDIMM    6
+#define DIMM_TYPE_MINI_CDIMM    7
+#define DIMM_TYPE_72B_SO_UDIMM  8
+#define DIMM_TYPE_72B_SO_RDIMM  9
+#define DIMM_TYPE_72B_SO_CDIMM  10
+
+#if defined(CONFIG_DEBUG_RAM_SETUP) && (CONFIG_DEBUG_RAM_SETUP)
+#define printram(x, ...) printk(BIOS_DEBUG, x, ##__VA_ARGS__)
+#else
+#define printram(x, ...)
+#endif
+
+/* Different values for tCK, representing standard DDR3 frequencies
+ * As always, these values are in 1/256 ns units */
+#define TCK_1066MHZ     240
+#define TCK_800MHZ      320
+#define TCK_666MHZ      384
+#define TCK_533MHZ      480
+#define TCK_400MHZ      640
+#define TCK_333MHZ      768
+#define TCK_266MHZ      960
+#define TCK_200MHZ      1280
+
+#define RAM_UNIT (1<<24)
+
+#include <stdint.h>
+
+/**
+ *\brief DIMM characteristics
+ *
+ * The characteristics of each DIMM, as presented by the SPD
+ */
+typedef struct dimm_attr_st {
+	u8 dram_type;
+	u16 cas_supported;
+	/* Number of ranks */
+	u8 ranks;
+	/* Number or row address bits */
+	u8 row_bits;
+	/* Number or column address bits */
+	u8 col_bits;
+	/* Size of module in (1 << 24) bytes (16MB) */
+	u16 size;
+	/* Latencies 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;
+
+} dimm_attr;
+
+typedef struct ramctr_timing_st {
+	u8 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 */
+	u8 WR;  /* write recovery time */
+	
+} ramctr_timing;
+
+
+typedef u8 spd_raw_data[256];
+
+/**
+ * \brief Decode the raw spd data
+ */
+void spd_decode_ddr3(dimm_attr *dimm, spd_raw_data spd_data);
+
+/**
+ * \brief Checks if the DIMM is Registered based on byte[3] of the spd
+ */
+int dimm_is_registered(u8 spd_byte3);
+
+/**
+ * \brief Print the info in dimm
+ */
+void dram_print_spd_ddr3(const dimm_attr *dimm);
+
+/**
+ * \brief Read double word from specified address
+ *
+ * Should be useful when doing an MRS to the DIMM
+ */
+u32 volatile_read(u32 addr);
+
+#endif /* DRAM_H */
diff --git a/src/devices/dram/dram_util.c b/src/devices/dram/dram_util.c
new file mode 100644
index 0000000..75913e4
--- /dev/null
+++ b/src/devices/dram/dram_util.c
@@ -0,0 +1,297 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011  Alexandru Gagniuc <mr.nuke.me 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, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "dram.h"
+#include <console/console.h>
+#include <device/device.h>
+
+u32 volatile_read(volatile u32 addr)
+{
+	volatile u32 result;
+	result = *(volatile u32*)addr;
+	return result;
+}
+
+int dimm_is_registered(u8 spd_byte3)
+{
+	spd_byte3 &= 0x0f;
+	if( (spd_byte3 == DIMM_TYPE_RDIMM)
+		| (spd_byte3 == DIMM_TYPE_MINI_RDIMM)
+		| (spd_byte3 == DIMM_TYPE_72B_SO_RDIMM) )
+		return 1;
+	
+	return 0;
+}
+
+void spd_decode_ddr3(dimm_attr *dimm, spd_raw_data spd)
+{
+	int nCRC, i;
+	u16 crc, spd_crc;
+	u8 * ptr = spd;
+	u8 ftb_divisor;
+	u8 ftb_dividend;
+	u8 capacity_shift, bus_width, sdram_width;
+	u32 mtb; /* medium time base */
+	/* Make sure that the spd dump is indeed from a DDR3 module */
+	if(spd[2] != DRAM_TYPE_DDR3)
+	{
+		printram("Not a DDR3 SPD!\n");
+		dimm->dram_type = DRAM_TYPE_UNDEFINED;
+		return;
+	}
+	dimm->dram_type = DRAM_TYPE_DDR3;
+
+	/* Find the number of bytes covered by CRC */
+	if(spd[0] & 0x80) {
+		nCRC = 117;
+	} else {
+		nCRC =126;
+	}
+
+	/* Compute the CRC */
+	crc = 0;
+	while (--nCRC >= 0) {
+		crc = crc ^ (int)*ptr++ << 8;
+		for (i = 0; i < 8; ++i)
+			if (crc & 0x8000) {
+				crc = crc << 1 ^ 0x1021;
+			} else {
+				crc = crc << 1;
+			}
+	}
+	/* Compare with the CRC in the SPD */
+	spd_crc = (spd[127] << 8) + spd[126];
+	/* Verify the CRC is correct */
+	if(crc != spd_crc)
+		printram("SPD CRC failed!!!");
+
+	
+	unsigned int reg, val, param;
+	printram("  Revision: %x \n", spd[1] );
+	printram("  Type    : %x \n", spd[2] );
+	printram("  Key     : %x \n", spd[3] );
+	
+	reg = spd[4];
+	/* Number of memory banks */
+	val = (reg >> 4) & 0x07;
+	if (val > 0x03) printram("  Invalid number of memory banks\n");
+	param = 1 << (val + 3);
+	printram("  Banks   : %u \n", param );
+	/* SDRAM capacity */
+	capacity_shift = reg & 0x0f;
+	if (capacity_shift > 0x06) printram("  Invalid module capacity\n");
+	if (capacity_shift < 0x02) {
+		printram("  Capacity: %u Mb\n", 256 << capacity_shift);
+	} else {
+		printram("  Capacity: %u Gb\n", 1 << (capacity_shift - 2));
+	}
+	
+	reg = spd[5];
+	/* Row address bits */
+	val = (reg >> 4) & 0x07;
+	if(val > 0x04) printram("  Invalid row address bits\n");
+	dimm->row_bits = val + 12;
+	/* Column address bits */
+	val = reg & 0x07;
+	if(val > 0x03) printram("  Invalid column address bits\n");
+	dimm->col_bits = val + 9;
+	
+	/* Module nominal voltage */
+	reg = spd[6];
+	print_debug("  Supported voltages: ");
+	if(reg & (1<<2) ) print_debug("1.2V ");
+	if(reg & (1<<1) ) print_debug("1.35V ");
+	if( !(reg & (1<<0)) ) print_debug("1.5V ");
+	print_debug("\n");
+
+	/* Module organization */
+	reg = spd[7];
+	/* Number of ranks*/
+	val = (reg >> 3) & 0x07;
+	if(val > 3) printram("  Invalid number of ranks\n");
+	dimm->ranks = val + 1;
+	/* SDRAM device width */
+	val = (reg & 0x07);
+	if(val > 3) printram("  Invalid SDRAM width\n");
+	sdram_width = (4 << val);
+	printram("  SDRAM width       : %u \n", sdram_width);
+
+	/* Memory bus width */
+	reg = spd[8];
+	/* Bus extension */
+	val = (reg >> 3) & 0x03;
+	if(val > 1) printram("  Invalid bus extension\n");
+	printram("  Bus extension     : %u bits\n", val?8:0);
+	/* Bus width */
+	val = reg & 0x07;
+	if(val > 3) printram("  Invalid bus width\n");
+	bus_width = 8 << val;
+	printram("  Bus width         : %u \n", bus_width);
+
+	/* We have all the info we need to compute the dimm size */
+	/* Capacity is 256Mbit multiplied by the power of 2 specified in
+	 * capacity_shift
+	 * The rest is the JEDEC formula */
+	/* I am certain it will fit in 16 bits
+	 * Remember, It's in units of 2^24 bytes*/
+	dimm->size =  ( (1 << (capacity_shift + (25-24)) ) * bus_width
+			* dimm->ranks ) / sdram_width;
+
+	/* Fine Timebase (FTB) Dividend/Divisor */
+	/* Dividend */
+	ftb_dividend = (spd[9] >> 4) & 0x0f;
+	/* Divisor */
+	ftb_divisor = spd[9] & 0x0f;
+
+	/* Medium Timebase =
+	 *   Medium Timebase (MTB) Dividend /
+	 *   Medium Timebase (MTB) Divisor */
+	mtb = (((u32)spd[10]) << 8) / spd [11];
+
+	/* SDRAM Minimum Cycle Time (tCKmin) */
+	dimm->tCK = spd[12] * mtb;
+
+	/* CAS Latencies Supported */
+	dimm->cas_supported = (spd[15] << 8) + spd[14];
+
+	/* Minimum CAS Latency Time (tAAmin) */
+	dimm->tAA = spd[16] * mtb;
+
+	/* Minimum Write Recovery Time (tWRmin) */
+	dimm->tWR = spd[17] * mtb;
+
+	/* Minimum RAS# to CAS# Delay Time (tRCDmin) */
+	dimm->tRCD = spd[18] * mtb;
+
+	/* Minimum Row Active to Row Active Delay Time (tRRDmin) */
+	dimm->tRRD = spd[19] * mtb;
+
+	/* Minimum Row Precharge Delay Time (tRPmin)*/
+	dimm->tRP = spd[20] * mtb;
+
+	/* Minimum Active to Precharge Delay Time (tRASmin) */
+	dimm->tRAS = (((spd[21] & 0x0f) << 8) + spd[22]) * mtb;
+	
+	/* Minimum Active to Active/Refresh Delay Time (tRCmin) */
+	dimm->tRC = (((spd[21] & 0xf0) << 4) + spd[23]) * mtb;
+
+	/* Minimum Refresh Recovery Delay Time (tRFCmin) */
+	dimm->tRFC = ((spd[25] << 8) + spd[24]) * mtb;
+
+	/* Minimum Internal Write to Read Command Delay Time (tWTRmin) */
+	dimm->tWTR = spd[26] * mtb;
+
+	/* Minimum Internal Read to Precharge Command Delay Time (tRTPmin) */
+	dimm->tRTP = spd[27] * mtb;
+
+	/* Minimum Four Activate Window Delay Time (tFAWmin) */
+	dimm->tFAW = (((spd[28] & 0x0f) << 8) + spd[29]) * mtb;
+
+	/* SDRAM Optional Features */
+	reg = spd[30];
+	print_debug("  Optional features :");
+	if(reg & 0x80) print_debug(" DLL-Off_mode");
+	if(reg & 0x02) print_debug(" RZQ/7");
+	if(reg & 0x01) print_debug(" RZQ/6");
+	print_debug("\n");
+
+	/* SDRAM Thermal and Refresh Options */
+	reg = spd[31];
+	print_debug("  Thermal features  :");
+	if(reg & 0x80) print_debug(" PASR");
+	if(reg & 0x08) print_debug(" ODTS");
+	if(reg & 0x04) print_debug(" ASR");
+	if(reg & 0x02) print_debug(" ext_temp_refresh");
+	if(reg & 0x01) print_debug(" ext_temp_range");
+	print_debug("\n");
+
+	/*  Module Thermal Sensor */
+	reg = spd[32];
+	print_debug("  Thermal sensor    : ");
+	if(reg & 0x80) print_debug("yes");
+	else print_debug("no");
+	print_debug("\n");
+
+	/*  SDRAM Device Type */
+	reg = spd[33];
+	print_debug("  Standard SDRAM    : ");
+	if(reg & 0x80) print_debug("no");
+	else print_debug("yes");
+	print_debug("\n");
+
+	/* Fine Offset for SDRAM Minimum Cycle Time (tCKmin) */
+	//printram("  tCKmin FTB        : %i \n", spd[34]);
+	/* Fine Offset for Minimum CAS Latency Time (tAAmin) */
+	//printram("  tAAmin FTB        : %i \n", spd[35]);
+	/* Fine Offset for Minimum RAS# to CAS# Delay Time (tRCDmin) */
+	//printram("  tRCDmin FTB       : %i \n", spd[36]);
+	/* Fine Offset for Minimum Row Precharge Delay Time (tRPmin) */
+	//printram("  tRPmin FTB        : %i \n", spd[37]);
+	/* Fine Offset for Minimum Active to Active/Refresh Delay Time (tRCmin) */
+	//printram("  tRCmin FTB        : %i \n", spd[38]);
+
+	if(spd[60] & 0x01)
+		printram("  DIMM Address bits mirrorred!!!\n");
+
+}
+
+static void print_ns(const char * msg, u32 val)
+{
+	u32 mant, fp;
+	mant = val/256;
+	fp = (val % 256) * 1000/256;
+
+	printram("%s%3u.%.3u ns\n", msg, mant, fp);
+}
+
+void dram_print_spd_ddr3(const dimm_attr *dimm)
+{
+	u16 val16;
+	int i;
+
+	printram("  Row    addr bits  : %u \n", dimm->row_bits);
+	printram("  Column addr bits  : %u \n", dimm->col_bits);
+	printram("  Number of ranks   : %u \n", dimm->ranks);
+	printram("  DIMM Capacity     : %u MB\n", dimm->size << 4);
+
+	/* CAS Latencies Supported */
+	val16 = dimm->cas_supported;
+	print_debug("  CAS latencies     :");
+	i = 0;
+	do{
+		if(val16 & 1) printram(" %u", i + 4);
+		i++;
+		val16 >>= 1;
+	} while(val16);
+	print_debug("\n");
+
+	print_ns("  tCKmin            : ", dimm->tCK);
+	print_ns("  tAAmin            : ", dimm->tAA);
+	print_ns("  tWRmin            : ", dimm->tWR);
+	print_ns("  tRCDmin           : ", dimm->tRCD);
+	print_ns("  tRRDmin           : ", dimm->tRRD);
+	print_ns("  tRPmin            : ", dimm->tRP);
+	print_ns("  tRASmin           : ", dimm->tRAS);
+	print_ns("  tRCmin            : ", dimm->tRC);
+	print_ns("  tRFCmin           : ", dimm->tRFC);
+	print_ns("  tWTRmin           : ", dimm->tWTR);
+	print_ns("  tRTPmin           : ", dimm->tRTP);
+	print_ns("  tFAWmin           : ", dimm->tFAW);
+
+}
diff --git a/src/devices/smbus/early_smbus.c b/src/devices/smbus/early_smbus.c
new file mode 100644
index 0000000..4583aca
--- /dev/null
+++ b/src/devices/smbus/early_smbus.c
@@ -0,0 +1,141 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011  Alexandru Gagniuc <mr.nuke.me 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, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/**
+ * @file post_codes.h
+ *
+ * This file defines the implementations for the functions defined in smbus.h
+ * These are a generic SMBUS implementation, which should work with a majority
+ * of chipsets.
+ * They are marked weak so that they can be overridden by the chipset code if
+ * necessary.
+ */
+
+#include "smbus.h"
+
+/**
+ * \brief Brief delay for SMBUS transactions
+ */
+__attribute__((weak))
+void __smbus_delay(void)
+{
+	inb(0x80);
+}
+
+/**
+ * \brief Clear the SMBUS host status register
+ */
+__attribute__((weak))
+void __smbus_reset(u16 __smbus_io_base)
+{
+	outb(0xdf, SMBHSTSTAT);
+}
+
+/**
+ * \brief Print an error, should it occur. If no error, just exit.
+ *
+ * @param host_status The data returned on the host status register after
+ *		      a transaction is processed.
+ * @param loops The number of times a transaction was attempted.
+ * @return	0 if no error occurred
+ * 		1 if an error was detected
+ */
+__attribute__((weak))
+int __smbus_print_error(u8 host_status, int loops, u16 __smbus_io_base)
+{
+	/* Check if there actually was an error. */
+	if ((host_status == 0x00 || host_status == 0x40 ||
+		host_status == 0x42) && (loops < SMBUS_TIMEOUT))
+		return 0;
+	
+	if (loops >= SMBUS_TIMEOUT)
+		printsmbus("SMBus timeout\n");
+	if (host_status & (1 << 4))
+		printsmbus("Interrupt/SMI# was Failed Bus Transaction\n");
+	if (host_status & (1 << 3))
+		printsmbus("Bus error\n");
+	if (host_status & (1 << 2))
+		printsmbus("Device error\n");
+	if (host_status & (1 << 1))
+		printsmbus("Interrupt/SMI# completed successfully\n");
+	if (host_status & (1 << 0))
+		printsmbus("Host busy\n");
+	return 1;
+}
+
+/**
+ * \brief Checks if the SMBUS is currently busy with a transaction
+ */
+__attribute__((weak))
+int __smbus_is_busy(u16 __smbus_io_base)
+{
+	/* Check if bit 0 of the status register is 1 (busy) or 0 (ready) */
+	return ( (inb(SMBHSTSTAT) & (1 << 0)) == 1);
+}
+
+/**
+ * \brief Wait for the SMBUS to become ready to process a new transaction.
+ */
+__attribute__((weak))
+int __smbus_wait_until_ready(u16 __smbus_io_base)
+{
+	int loops;
+	
+	printsmbus("Waiting until SMBus ready\n");
+	
+	/* Loop up to SMBUS_TIMEOUT times, waiting for bit 0 of the
+	 * SMBus Host Status register to go to 0, indicating the operation
+	 * was completed successfully. I don't remember why I did it this way,
+	 * but I think it was because ROMCC was running low on registers */
+	loops = 0;
+	while (__smbus_is_busy(__smbus_io_base) && loops < SMBUS_TIMEOUT)
+		++loops;
+	
+	return __smbus_print_error(inb(SMBHSTSTAT), loops, __smbus_io_base);
+}
+
+/**
+ * \brief Read a byte from the SMBUS.
+ *
+ * @param dimm The address location of the DIMM on the SMBus.
+ * @param offset The offset the data is located at.
+ */
+__attribute__((weak))
+u8 __smbus_read_byte(u8 dimm, u8 offset, u16 __smbus_io_base)
+{
+	u8 val;
+	
+	/* Initialize SMBUS sequence */
+	__smbus_reset(__smbus_io_base);
+	/* Clear host data port. */
+	outb(0x00, SMBHSTDAT0);
+	
+	__smbus_wait_until_ready(__smbus_io_base);
+	
+	/* Actual addr to reg format. */
+	dimm = (dimm << 1);
+	dimm |= 1; /* read command */
+	outb(dimm, SMBXMITADD);
+	outb(offset, SMBHSTCMD);
+	/* Start transaction, byte data read. */
+	outb(0x48, SMBHSTCTL);
+	__smbus_wait_until_ready(__smbus_io_base);
+	
+	val = inb(SMBHSTDAT0);
+	return val;
+}
\ No newline at end of file
diff --git a/src/devices/smbus/smbus.h b/src/devices/smbus/smbus.h
new file mode 100644
index 0000000..820a0dd
--- /dev/null
+++ b/src/devices/smbus/smbus.h
@@ -0,0 +1,99 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011  Alexandru Gagniuc <mr.nuke.me 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, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @file post_codes.h
+ *
+ * This file defines the prototypes for several common SMBUS functions
+ * These functions are prefixed with __smbus_ so that they do not conflict with
+ * the dozens of similar (duplicated) implementations in many southbridges.
+ *
+ * As a last parameter, the SMBUS functions take a u16 value __smbus_io_base,
+ * which represents the base IO port for smbus transactions
+ */
+
+#include <arch/io.h>
+
+/**
+ * \brief SMBUS IO ports in relation to the base IO port
+ */
+#define SMBHSTSTAT		__smbus_io_base + 0x0
+#define SMBSLVSTAT		__smbus_io_base + 0x1
+#define SMBHSTCTL		__smbus_io_base + 0x2
+#define SMBHSTCMD		__smbus_io_base + 0x3
+#define SMBXMITADD		__smbus_io_base + 0x4
+#define SMBHSTDAT0		__smbus_io_base + 0x5
+#define SMBHSTDAT1		__smbus_io_base + 0x6
+#define SMBBLKDAT		__smbus_io_base + 0x7
+#define SMBSLVCTL		__smbus_io_base + 0x8
+#define SMBTRNSADD		__smbus_io_base + 0x9
+#define SMBSLVDATA 		__smbus_io_base + 0xa
+
+#define SMBUS_TIMEOUT		(100*1000*10)
+
+/**
+ * \brief printk macro for SMBUS debugging
+ */
+#if defined(CONFIG_DEBUG_SMBUS_SETUP) && (CONFIG_DEBUG_SMBUS_SETUP)
+#define printsmbus(x, ...) printk(BIOS_DEBUG, x, ##__VA_ARGS__)
+#else
+#define printsmbus(x, ...)
+#endif
+
+void __smbus_reset(u16 __smbus_io_base);
+int __smbus_print_error(u8 host_status, int loops, u16 __smbus_io_base);
+int __smbus_is_busy(u16 __smbus_io_base);
+int __smbus_wait_until_ready(u16 __smbus_io_base);
+u8 __smbus_read_byte(u8 dimm, u8 offset, u16 __smbus_io_base);
+
+void __smbus_delay(void);
+
+#if defined(SMBUS_IO_BASE) && (SMBUS_IO_BASE != 0)
+
+__attribute__((always_inline, unused))
+static void smbus_reset(void)
+{
+	__smbus_reset(SMBUS_IO_BASE);
+}
+
+__attribute__((always_inline, unused))
+static int smbus_is_busy(void)
+{
+	return __smbus_is_busy(SMBUS_IO_BASE);
+}
+
+__attribute__((always_inline, unused))
+static int smbus_wait_until_ready(void)
+{
+	return __smbus_wait_until_ready(SMBUS_IO_BASE);
+}
+
+__attribute__((always_inline, unused))
+static int smbus_print_error(u8 host_status, int loops)
+{
+	return __smbus_print_error(host_status, loops, SMBUS_IO_BASE);
+}
+
+__attribute__((always_inline, unused))
+static u8 smbus_read_byte(u8 dimm, u8 offset)
+{
+	return __smbus_read_byte(dimm, offset, SMBUS_IO_BASE);
+}
+
+#endif
\ No newline at end of file
diff --git a/src/mainboard/via/epia-m850/.directory b/src/mainboard/via/epia-m850/.directory
new file mode 100644
index 0000000..8a30135
--- /dev/null
+++ b/src/mainboard/via/epia-m850/.directory
@@ -0,0 +1,4 @@
+[Dolphin]
+ShowPreview=true
+Timestamp=2011,8,1,4,33,6
+Version=2
diff --git a/src/mainboard/via/epia-m850/Kconfig b/src/mainboard/via/epia-m850/Kconfig
new file mode 100644
index 0000000..ae46646
--- /dev/null
+++ b/src/mainboard/via/epia-m850/Kconfig
@@ -0,0 +1,47 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2011  Alexandru Gagniuc <mr.nuke.me 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, either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program.  If not, see <http://www.gnu.org/licenses/>.
+##
+
+if BOARD_VIA_EPIA_M850
+
+config BOARD_SPECIFIC_OPTIONS # dummy
+	def_bool y
+	select ARCH_X86
+	select CPU_VIA_C7
+	select NORTHBRIDGE_VIA_VX900
+	select SUPERIO_FINTEK_F81865F
+	#select BOARD_HAS_FADT
+	#select HAVE_PIRQ_TABLE
+	#select HAVE_ACPI_TABLES
+	#select HAVE_OPTION_TABLE
+	select BOARD_ROMSIZE_KB_512
+	#select RAMINIT_SYSINFO
+
+config MAINBOARD_DIR
+	string
+	default via/epia-m850
+
+config MAINBOARD_PART_NUMBER
+	string
+	default "EPIA-M850"
+
+config IRQ_SLOT_COUNT
+	int
+	default 13
+
+endif # BOARD_VIA_EPIA_M850
diff --git a/src/mainboard/via/epia-m850/Makefile.inc b/src/mainboard/via/epia-m850/Makefile.inc
new file mode 100644
index 0000000..9dffc79
--- /dev/null
+++ b/src/mainboard/via/epia-m850/Makefile.inc
@@ -0,0 +1,21 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2011  Alexandru Gagniuc <mr.nuke.me 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, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+## 
+## You should have received a copy of the GNU General Public License
+## along with this program.  If not, see <http://www.gnu.org/licenses/>.
+##
+
+#romstage-y += ./../../../superio/fintek/f81865f/f81865f_early_serial.c
+
diff --git a/src/mainboard/via/epia-m850/chip.h b/src/mainboard/via/epia-m850/chip.h
new file mode 100644
index 0000000..2ac4f12
--- /dev/null
+++ b/src/mainboard/via/epia-m850/chip.h
@@ -0,0 +1,21 @@
+/*
+ * This file is part of the coreboot project.
+ * 
+ * Copyright (C) 2011  Alexandru Gagniuc <mr.nuke.me 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, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+extern struct chip_operations mainboard_ops;
+
+struct mainboard_config {};
diff --git a/src/mainboard/via/epia-m850/devicetree.cb b/src/mainboard/via/epia-m850/devicetree.cb
new file mode 100644
index 0000000..34f2848
--- /dev/null
+++ b/src/mainboard/via/epia-m850/devicetree.cb
@@ -0,0 +1,62 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2011  Alexandru Gagniuc <mr.nuke.me 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, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+## 
+## You should have received a copy of the GNU General Public License
+## along with this program.  If not, see <http://www.gnu.org/licenses/>.
+##
+
+chip northbridge/via/vx900		# Northbridge
+    device lapic_cluster 0 on		# APIC cluster
+        chip cpu/via/model_c7		# CPU ATOM (same as VIA C7)
+          device lapic 0 on end	# APIC
+        end
+    end
+    device pci_domain 0 on
+        device pci 0.0 on end		# AGP Bridge
+        device pci 0.1 on end		# Error Reporting
+        device pci 0.2 on end		# Host Bus Control
+        device pci 0.3 on end		# Memory Controller
+        device pci 0.4 on end		# Power Management
+        device pci 0.5 on end		# Power Management
+        device pci 0.6 on end		# Power Management
+        device pci 0.7 on end		# V-Link Controller
+        device pci 1.0 on end		# PCI Bridge
+        device pci 1.1 on end		# Audio
+        device pci 3.0 on end		# PCIE control
+        device pci 3.1 on end		# PEX1
+        device pci 3.2 on end		# PEX2
+        device pci 3.3 on end		# PEX3
+        device pci 3.4 on end		# PEX4
+        device pci f.0 on end		# IDE/SATA
+        device pci 10.0 on end		# USB 1.1
+        device pci 10.1 on end		# USB 1.1
+        device pci 10.2 on end		# USB 1.1
+        device pci 10.3 on end		# USB 1.1
+        device pci 10.4 on end		# USB 2.0
+        device pci 11.0 on end		# LPC
+            chip drivers/generic/generic	# DIMM 0-0-0
+              device i2c 50 on end
+            end
+            chip drivers/generic/generic	# DIMM 0-0-1
+              device i2c 51 on end
+            end
+          chip superio/fintek/f81865f	# Super duper IO
+          end
+        device pci 11.7 on end		# NB/SB control
+        device pci 13.0 on end		# PCI Bridge
+        device pci 14.0 on end		# HD Audio
+    end
+    
+end
diff --git a/src/mainboard/via/epia-m850/mainboard.c b/src/mainboard/via/epia-m850/mainboard.c
new file mode 100644
index 0000000..572f7da
--- /dev/null
+++ b/src/mainboard/via/epia-m850/mainboard.c
@@ -0,0 +1,25 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011  Alexandru Gagniuc <mr.nuke.me 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, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <device/device.h>
+#include "chip.h"
+
+struct chip_operations mainboard_ops = {
+	CHIP_NAME("VIA EPIA-M850 Mainboard")
+};
diff --git a/src/mainboard/via/epia-m850/romstage.c b/src/mainboard/via/epia-m850/romstage.c
new file mode 100644
index 0000000..60dd1fb
--- /dev/null
+++ b/src/mainboard/via/epia-m850/romstage.c
@@ -0,0 +1,76 @@
+/*
+ * This file is part of the coreboot project.
+ *
+  * Copyright (C) 2011-2012  Alexandru Gagniuc <mr.nuke.me 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, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Inspired from the EPIA-M700
+ */
+
+#include <stdint.h>
+#include <device/pci_def.h>
+#include <device/pci_ids.h>
+#include <arch/io.h>
+#include <device/pnp_def.h>
+#include <arch/romcc_io.h>
+#include <arch/hlt.h>
+#include <console/console.h>
+#include <lib.h>
+#include "cpu/x86/bist.h"
+#include <string.h>
+
+#include "northbridge/via/vx900/early_vx900.h"
+#include "northbridge/via/vx900/raminit.h"
+#include "superio/fintek/f81865f/f81865f_early_serial.c"
+
+#define SERIAL_DEV PNP_DEV(0x2e, 0)
+
+/* cache_as_ram.inc jumps to here. */
+void main(unsigned long bist)
+{
+	/* Enable multifunction bit for northbridge.
+	 * This enables the PCI configuration spaces of D0F1 to D0F7 to be
+	 * accessed */
+	pci_write_config8(PCI_DEV(0, 0, 0), 0x4f, 0x01);
+
+	f81865f_enable_serial(SERIAL_DEV, CONFIG_TTYS0_BASE);
+	console_init();
+	print_debug("Console initialized. \n");
+
+	/* Halt if there was a built-in self test failure. */
+	report_bist_failure(bist);
+
+	/* x86 cold boot I/O cmd. */
+	enable_smbus();
+
+	/* If this works, then SMBUS is up and running */
+	//dump_spd_data();
+
+	/* Now we can worry about raminit.
+	 * This board only has DDR3, so no need to worry about which DRAM type
+	 * to use */
+	dimm_layout dimms = {{0x50, 0x51, SPD_END_LIST}};
+	vx900_init_dram_ddr3(&dimms);
+	//ram_check(0, 640 * 1024);
+	//ram_check(1<<26, (1<<26) + 0x20);
+	ram_check(0, 0x80);
+
+	print_debug("We passed RAM verify\n");
+
+	return;
+
+}
diff --git a/src/northbridge/via/vx900/Kconfig b/src/northbridge/via/vx900/Kconfig
new file mode 100644
index 0000000..60e7993
--- /dev/null
+++ b/src/northbridge/via/vx900/Kconfig
@@ -0,0 +1,23 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2011  Alexandru Gagniuc <mr.nuke.me 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, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+## 
+## You should have received a copy of the GNU General Public License
+## along with this program.  If not, see <http://www.gnu.org/licenses/>.
+##
+
+config NORTHBRIDGE_VIA_VX900
+	bool
+	select HAVE_DEBUG_RAM_SETUP
+	select HAVE_DEBUG_SMBUS
\ No newline at end of file
diff --git a/src/northbridge/via/vx900/Makefile.inc b/src/northbridge/via/vx900/Makefile.inc
new file mode 100644
index 0000000..ac04434
--- /dev/null
+++ b/src/northbridge/via/vx900/Makefile.inc
@@ -0,0 +1,35 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2011  Alexandru Gagniuc <mr.nuke.me 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, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+## 
+## You should have received a copy of the GNU General Public License
+## along with this program.  If not, see <http://www.gnu.org/licenses/>.
+##
+
+romstage-y += early_smbus.c
+romstage-y += raminit_ddr3.c
+romstage-y += raminit_ddr3_delay_calib.c
+romstage-y += ./../../../devices/dram/dram_util.c
+romstage-y += ./../../../devices/smbus/early_smbus.c
+#romstage-y += ./../../../cpu/x86/lapic/apic_timer.c
+
+# Drivers for these devices already exist with the vx800
+# Use those instead of duplicating code
+
+driver-y += ./../vx800/northbridge.c
+driver-y += ./../vx800/vga.c
+driver-y += ./../vx800/lpc.c
+
+chipset_bootblock_inc += $(src)/northbridge/via/vx900/romstrap.inc
+chipset_bootblock_lds += $(src)/northbridge/via/vx900/romstrap.lds
diff --git a/src/northbridge/via/vx900/early_smbus.c b/src/northbridge/via/vx900/early_smbus.c
new file mode 100644
index 0000000..aa6cec0
--- /dev/null
+++ b/src/northbridge/via/vx900/early_smbus.c
@@ -0,0 +1,191 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011  Alexandru Gagniuc <mr.nuke.me 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, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <device/pci_ids.h>
+#include "early_vx900.h"
+
+#include <arch/io.h>
+#include <arch/romcc_io.h>
+#include <console/console.h>
+#include <devices/dram/dram.h>
+
+__attribute__((unused))
+static void smbus_delays(int delays)
+{
+	while(delays--) __smbus_delay();
+}
+
+
+/**
+ * Read a byte from the SMBus.
+ *
+ * @param dimm The address location of the DIMM on the SMBus.
+ * @param offset The offset the data is located at.
+ */
+u8 __smbus_read_byte(u8 dimm, u8 offset, u16 __smbus_io_base)
+{
+	u8 val;
+	
+	/* Initialize SMBUS sequence */
+	smbus_reset();
+	/* Clear host data port. */
+	outb(0x00, SMBHSTDAT0);
+	
+	smbus_wait_until_ready();
+	smbus_delays(50);
+	
+	/* Actual addr to reg format. */
+	dimm = (dimm << 1);
+	dimm |= 1; /* read command */
+	outb(dimm, SMBXMITADD);
+	outb(offset, SMBHSTCMD);
+	/* Start transaction, byte data read. */
+	outb(0x48, SMBHSTCTL);
+	smbus_wait_until_ready();
+	
+	val = inb(SMBHSTDAT0);
+	return val;
+}
+
+void enable_smbus(void)
+{
+	device_t dev;
+	u8 reg8;
+	u16 __smbus_io_base = SMBUS_IO_BASE;
+
+	/* Locate the Power Management control */
+	dev = pci_locate_device(PCI_ID(PCI_VENDOR_ID_VIA,
+				       PCI_DEVICE_ID_VIA_VX900_LPC), 0);
+	
+	if (dev == PCI_DEV_INVALID) {
+		die("Power Managment Controller not found\n");
+	}
+
+	/*
+	 * To use SMBus to manage devices on the system board, it is a must to
+	 * enable SMBus function by setting
+	 * PMU_RXD2[0] (SMBus Controller Enable) to 1.
+	 * And set PMU_RXD0 and PMU_RXD1 (SMBus I/O Base) to an appropriate
+	 * I/O port address, so that all registers in SMBus I/O port can be
+	 * accessed.
+	 */
+
+	reg8 = pci_read_config8(dev, 0xd2);
+	/* Enable SMBus controller */
+	reg8 |= 1;
+	/* Set SMBUS clock from 128k source */
+	reg8 |= 1<<2;
+	pci_write_config8(dev, 0xd2, reg8);
+
+	reg8 = pci_read_config8(dev, 0x94);
+	/* SMBUS clock from divider of 14.318 MHz */
+	reg8 &= ~(1<<7);
+	pci_write_config8(dev, 0x94, reg8);
+
+	/* Set SMBus IO base */
+	pci_write_config16(dev, 0xd0, SMBUS_IO_BASE);
+
+	/*
+	 * Initialize the SMBus sequence:
+	 */
+	/* Clear SMBus host status register */
+	smbus_reset();
+	/* Clear SMBus host data 0 register */
+	outb(0x00, SMBHSTDAT0);
+
+	/* Wait for SMBUS */
+	smbus_wait_until_ready();
+
+}
+
+void spd_read(u8 addr, spd_raw_data spd)
+{
+	u8 reg;
+	int i, regs;
+	reg = smbus_read_byte(addr, 2);
+	if(reg != 0x0b)
+	{
+		printk(BIOS_DEBUG, "SMBUS device %x not a DDR3 module\n", addr);
+		spd[2] = 0;
+		return;
+	}
+
+	reg = smbus_read_byte(addr, 0);
+	reg &= 0xf;
+	if (reg == 0x3) {
+		regs = 256;
+	} else if (reg == 0x2) {
+		regs = 176;
+	} else if (reg == 0x1) {
+		regs = 128;
+	} else {
+		printk(BIOS_INFO, "No DIMM present at %x\n", addr);
+		spd[2] = 0;
+		return;
+	}
+	printk(BIOS_DEBUG, "SPD Data for DIMM %x \n", addr);
+	for (i = 0; i < regs; i++) {
+		reg = smbus_read_byte(addr, i);
+		//printk(BIOS_DEBUG, "  Offset %u  = 0x%x \n", i, reg );
+		spd[i] = reg;
+	}
+}
+
+void dump_spd_data(void)
+{
+	int dimm, offset, regs;
+	unsigned int reg;
+	spd_raw_data spd;
+	dimm_attr dimmx;
+	
+	for (dimm = 0x50; dimm < 0x52; dimm++) {
+		reg = smbus_read_byte(dimm, 2);
+		if(reg != 0x0b)
+		{
+			printk(BIOS_DEBUG,
+			       "SMBUS device %x not a DDR3 module\n", dimm);
+			continue;
+		}
+
+		reg = smbus_read_byte(dimm, 0);
+		reg &= 0xf;
+		if (reg == 0x3) {
+			regs = 256;
+		} else if (reg == 0x2) {
+			regs = 176;
+		} else if (reg == 0x1) {
+			regs = 128;
+		} else {
+			printk(BIOS_INFO, "No DIMM present at %x\n", dimm);
+			regs = 0;
+			continue;
+		}
+		printk(BIOS_DEBUG, "SPD Data for DIMM %x \n", dimm);
+		for (offset = 0; offset < regs; offset++) {
+			reg = smbus_read_byte(dimm, offset);
+			//printk(BIOS_DEBUG, "  Offset %u  = 0x%x \n", offset, reg );
+			spd[offset] = reg;
+		}
+
+		spd_decode_ddr3(&dimmx, spd);
+		dram_print_spd_ddr3(&dimmx);
+
+	}
+}
+
diff --git a/src/northbridge/via/vx900/early_vx900.h b/src/northbridge/via/vx900/early_vx900.h
new file mode 100644
index 0000000..6b33077
--- /dev/null
+++ b/src/northbridge/via/vx900/early_vx900.h
@@ -0,0 +1,35 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011  Alexandru Gagniuc <mr.nuke.me 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, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EARLY_VX900_H
+#define EARLY_VX900_H
+
+#include <stdint.h>
+
+#define SMBUS_IO_BASE	0x500
+#include <devices/smbus/smbus.h>
+
+
+#include "raminit.h"
+
+void enable_smbus(void);
+void dump_spd_data(void);
+void spd_read(u8 addr, spd_raw_data spd);
+
+#endif /* EARLY_VX900_H */
diff --git a/src/northbridge/via/vx900/forgotten.c b/src/northbridge/via/vx900/forgotten.c
new file mode 100644
index 0000000..54cb88f
--- /dev/null
+++ b/src/northbridge/via/vx900/forgotten.c
@@ -0,0 +1,192 @@
+#include "forgotten.h"
+#include <../../dram/dram.h>
+
+u16 ddr3_get_mr0(
+	char precharge_pd,
+	u8 write_recovery,
+	char dll_reset,
+	char mode,
+	u8 cas,
+	char interleaved_burst,
+	u8 burst_lenght
+){
+	u32 cmd = 0;
+	if(precharge_pd) cmd |= (1 << 12);
+	/* Write recovery */
+	cmd |= ( ((write_recovery - 4) & 0x7) << 9);
+	if(dll_reset) cmd |= (1 << 8);
+	if(mode) cmd |= (1<<7);
+	/* CAS latency) */
+	cmd |= ( ((cas - 4) & 0x7) << 4);
+	if(interleaved_burst) cmd |= (1 << 3);
+	/* Burst lenght */
+	cmd |= (burst_lenght & 0x3);
+	return cmd;
+}
+
+u16 ddr3_get_mr1(
+	char q_off_disable,
+	char tdqs,
+	u8 rtt_nom,
+	char write_leveling,
+	u8 output_drive_strenght,
+	u8 additive_latency,
+	u8 dll_disable
+){
+	u32 cmd = 0;
+	if(q_off_disable) cmd |= (1 << 12);
+	if(tdqs) cmd |= (1 << 11);
+	/* rtt_nom */
+	cmd |= ( ((rtt_nom & 4) << (9-2)) | ((rtt_nom & 2) << (6-1))
+			| ((rtt_nom & 1) << 2));
+	if(write_leveling) cmd |= (1 << 7);
+	/* output drive strenght */
+	cmd |= ( ((output_drive_strenght & 2) << (5-1))
+	| ((output_drive_strenght & 1) << 1) );
+	/* Additive latency */
+	cmd |= ((additive_latency & 0x3) << 3);
+	if(dll_disable) cmd |= (1 << 0);
+	return cmd;
+}
+
+u16 ddr3_get_mr2(
+	u8 rtt_wr,
+	char extended_temp,
+	char auto_self_refresh,
+	u8 cas_write
+){
+	u32 cmd = 0;
+	/* Rtt_wr */
+	cmd |= ( (rtt_wr & 0x3) << 9);
+	if(extended_temp) cmd |= (1 << 7);
+	if(auto_self_refresh) cmd |= (1 << 6);
+	/* CAS write latency */
+	cmd |= ( ((cas_write - 5) & 0x7) << 3);
+	return cmd;
+}
+
+u16 ddr3_get_mr3(char dataflow_from_mpr)
+{
+	u32 cmd = 0;
+	if(dataflow_from_mpr) cmd |= (1<<2);
+	return cmd;
+}
+
+/*
+ * Translate the MRS command into the memory address corresponding to the
+ * command. This is based on the CPU address to memory address mapping described
+ * by the initial values of registers  0x52 and 0x53, so do not fuck with them
+ * until after the MRS commands have been sent to all ranks
+ */
+
+u32 vx900_get_mrs_addr(u8 mrs_type, u16 cmd);
+
+u32 vx900_get_mrs_addr(u8 mrs_type, u16 cmd)
+{
+	u32 addr = 0;
+	/* A3 <-> MA0, A4 <-> MA1, ... A12 <-> MA9 */
+	addr |= ((cmd &0x3ff)<< 3);
+	/* A20 <-> MA10 */
+	addr |= (((cmd >> 10) & 0x1) << 20);
+	/* A13 <-> MA11, A14 <-> MA12 */
+	addr |= (((cmd >> 11) & 0x3) << 13);
+	
+	/* Do not fuck with registers 0x52 and 0x53 if you want the following
+	 * mappings to work for you:
+	 * A17 <-> BA0, A18 <-> BA1, A19 <-> BA2 */
+	addr |= ((mrs_type & 0x7) << 17);
+	return addr;
+}
+void vx900_dram_ddr3_init_rank(device_t mcu, const ramctr_timing *ctrl,
+			       int rank);
+void vx900_dram_ddr3_init_rank(device_t mcu, const ramctr_timing *ctrl,
+			       int rank)
+{
+	u8 reg8;
+	u32 reg32, cmd, res, addr;
+	int i;
+	
+	printram("Initializing rank %u\n", rank);
+	
+	/* Step 06 - Set Fun3_RX6B[2:0] to 001b (NOP Command Enable). */
+	reg8 = pci_read_config8(mcu, 0x6b);
+	reg8 &= ~(0x03);
+	reg8 |= (1<<0);
+	pci_write_config8(mcu, 0x6b, reg8);
+	/* Step 07 - Read a double word from any address of the DIMM. */
+	reg32 = volatile_read(0x0);
+	udelay(10);
+	printram("We just read   0x%x\n", reg32);
+	/* Step 08 - Set Fun3_RX6B[2:0] to 011b (MSR Enable). */
+	reg8 = pci_read_config8(mcu, 0x6b);
+	reg8 &= ~(0x03);
+	reg8 |= (3<<0);
+	pci_write_config8(mcu, 0x6b, reg8);
+	
+	/* Step 09 – Issue MR2 cycle. Read a double word from the address depended
+	 * on DRAM’s Rtt_WR and CWL settings.
+	 * (Check the byte 63 of SPD. If memory address lines of target physical
+	 * rank is mirrored, MA3~MA8 and BA0~BA1 should be swapped.) */
+	cmd = ddr3_get_mr2(DDR3_MR2_RTT_WR_OFF,0,0,ctrl->CWL);
+	addr = vx900_get_mrs_addr(2, cmd);
+	if(addr != 0x00040040){
+		printram("MR2 needed at addr 0x%.8x, but done at 0x%.8x\n", 0x00040040, addr);
+		addr = 0x00040040;
+	}
+	res = volatile_read(addr);
+	udelay(10);
+	printram("MR2 cycle 0x%.4x @ addr 0x%.8x read 0x%.8x\n", cmd, addr, res);
+	
+	/* Step 10 – Issue MR3 cycle - set DRAM to normal operation mode.
+	 */
+	cmd = ddr3_get_mr3(0);
+	addr = vx900_get_mrs_addr(3, cmd);
+	res = volatile_read(addr);
+	udelay(10);
+	printram("MR3 cycle 0x%.4x @ addr 0x%.8x read 0x%.8x\n", cmd, addr, res);
+	
+	/* Step 11 –Issue MR1 cycle
+	 * Set DRAM’s output driver impedance, and Rtt_Nom settings.
+	 * * The DLL enable field, TDQS field, write leveling enable field,
+	 *   additive latency field, and Qoff field should be set to 0.
+	 */
+	cmd = ddr3_get_mr1(DDR3_MR1_QOFF_ENABLE, DDR3_MR1_TQDS_DISABLE,
+					   DDR3_MR1_RTT_NOM_RZQ2,
+					DDR3_MR1_WRITE_LEVELING_DISABLE, DDR3_MR1_ODS_RZQ7,
+					DDR3_MR1_AL_DISABLE, DDR3_MR1_DLL_ENABLE);
+	addr = vx900_get_mrs_addr(1, cmd);
+	res = volatile_read(addr);
+	udelay(10);
+	printram("MR1 cycle 0x%.4x @ addr 0x%.8x read 0x%.8x\n", cmd, addr, res);
+	
+	/* Step 12 - Issue MR0 cycle.
+	 * Set DRAM’s burst length, CAS latency and write recovery settings.
+	 * * The read burst type field should be set to interleave.
+	 * * The mode field should be set to normal mode.
+	 * * The DLL reset field should be set to No.
+	 * * The DLL control for precharge PD field should be set to Fast exit.
+	 */
+	cmd = ddr3_get_mr0(DDR3_MR0_PRECHARGE_FAST, ctrl->WR,
+			   DDR3_MR0_DLL_RESET_NO, DDR3_MR0_MODE_NORMAL,
+			   ctrl->CAS, DDR3_MR0_BURST_TYPE_INTERLEAVED, 1);
+	addr = vx900_get_mrs_addr(0, cmd);
+	if(addr != 0x000069c8){
+		printram("MR0 needed at addr 0x%.8x, but done at 0x%.8x\n", 0x000069c8, addr);
+		addr = 0x000069c8;
+	}
+	res = volatile_read(addr);
+	printram("MR0 cycle 0x%.4x @ addr 0x%.8x read 0x%.8x\n", cmd, addr, res);
+	
+	/* Step 13 - Set Rx6B[2:0] to 110b (Long ZQ calibration command). */
+	reg8 = pci_read_config8(mcu, 0x6b);
+	reg8 &= ~(0x03);
+	reg8 |= (3<<1);
+	pci_write_config8(mcu, 0x6b, reg8);
+	for(i = 0; i < 1000; i++) inb(0x80);
+	
+	
+	/* Step 14 - Read a double word from any address of the DIMM. */
+	reg32 = volatile_read(0x0);
+	printram("We just read   0x%x\n", reg32);
+}
+
diff --git a/src/northbridge/via/vx900/forgotten.h b/src/northbridge/via/vx900/forgotten.h
new file mode 100644
index 0000000..43641f7
--- /dev/null
+++ b/src/northbridge/via/vx900/forgotten.h
@@ -0,0 +1,78 @@
+#ifndef REDUNDANT_H
+#define REDUNDANT_H
+
+#define DDR3_MR0_PRECHARGE_SLOW    0
+#define DDR3_MR0_PRECHARGE_FAST    1
+#define DDR3_MR0_MODE_NORMAL       0
+#define DDR3_MR0_MODE_TEST         1
+#define DDR3_MR0_DLL_RESET_NO      0
+#define DDR3_MR0_DLL_RESET_YES     1
+#define DDR3_MR0_BURST_TYPE_SEQUENTIAL   0
+#define DDR3_MR0_BURST_TYPE_INTERLEAVED  1
+#define DDR3_MR0_BURST_LENGTH_FIXED_8    0
+#define DDR3_MR0_BURST_LENGTH_CHOP       1
+#define DDR3_MR0_BURST_LENGTH_FIXED_4    2
+/**
+ * \brief Get command address for a DDR3 MR0 command
+ */
+u16 ddr3_get_mr0(
+	char precharge_pd,
+	u8 write_recovery,
+	char dll_reset,
+	char mode,
+	u8 cas,
+	char interleaved_burst,
+	u8 burst_lenght
+);
+
+#define DDR3_MR1_TQDS_DISABLE            0
+#define DDR3_MR1_TQDS_ENABLE             1
+#define DDR3_MR1_QOFF_ENABLE             0
+#define DDR3_MR1_QOFF_DISABLE            1
+#define DDR3_MR1_WRITE_LEVELING_DISABLE  0
+#define DDR3_MR1_WRITE_LEVELING_ENABLE   1
+#define DDR3_MR1_RTT_NOM_OFF             0
+#define DDR3_MR1_RTT_NOM_RZQ4            1
+#define DDR3_MR1_RTT_NOM_RZQ2            2
+#define DDR3_MR1_RTT_NOM_RZQ6            3
+#define DDR3_MR1_RTT_NOM_RZQ12           4
+#define DDR3_MR1_RTT_NOM_RZQ8            5
+#define DDR3_MR1_AL_DISABLE              0
+#define DDR3_MR1_AL_CL_MINUS_1           1
+#define DDR3_MR1_AL_CL_MINUS_2           2
+#define DDR3_MR1_ODS_RZQ6                0
+#define DDR3_MR1_ODS_RZQ7                1
+#define DDR3_MR1_DLL_ENABLE              0
+#define DDR3_MR1_DLL_DISABLE             1
+/**
+ * \brief Get command address for a DDR3 MR1 command
+ */
+u16 ddr3_get_mr1(
+	char q_off,
+	char tdqs,
+	u8 rtt_nom,
+	char write_leveling,
+	u8 output_drive_strenght,
+	u8 additive_latency,
+	u8 dll_disable
+);
+
+#define DDR3_MR2_RTT_WR_OFF              0
+#define DDR3_MR2_RTT_WR_RZQ4             1
+#define DDR3_MR2_RTT_WR_RZQ2             2
+/**
+ * \brief Get command address for a DDR3 MR2 command
+ */
+u16 ddr3_get_mr2(
+	u8 rtt_wr,
+	char extended_temp,
+	char auto_self_refresh,
+	u8 cas_write
+);
+
+/**
+ * \brief Get command address for a DDR3 MR3 command
+ */
+u16 ddr3_get_mr3(char dataflow_from_mpr);
+
+#endif /* REDUNDANT_H */
\ No newline at end of file
diff --git a/src/northbridge/via/vx900/raminit.h b/src/northbridge/via/vx900/raminit.h
new file mode 100644
index 0000000..5c606c6
--- /dev/null
+++ b/src/northbridge/via/vx900/raminit.h
@@ -0,0 +1,77 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011  Alexandru Gagniuc <mr.nuke.me 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, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RAMINIT_VX900_H
+#define RAMINIT_VX900_H
+
+#include <devices/dram/dram.h>
+
+/* The maximum number of DIMM slots that the VX900 supports */
+#define VX900_MAX_DIMM_SLOTS 2
+
+#define VX900_MAX_MEM_RANKS 4
+
+
+#define SPD_END_LIST 0xff
+
+typedef struct dimm_layout_st
+{
+	/* The address of the DIMM on the SMBUS *
+	 * 0xFF to terminate the array*/
+	u8 spd_addr[VX900_MAX_DIMM_SLOTS + 1];
+} dimm_layout;
+
+typedef struct dimm_info_st
+{
+	dimm_attr dimm[VX900_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[VX900_MAX_MEM_RANKS];
+	mem_rank virt[VX900_MAX_MEM_RANKS];
+} 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 vx900_delay_calib_st {
+	delay_range rx_dqi_cr;
+	delay_range rx_dqsi;
+	delay_range tx_dqo;
+	delay_range tx_dqso;
+} vx900_delay_calib;
+
+void vx900_init_dram_ddr3(const dimm_layout *dimms);
+
+#endif /* RAMINIT_VX900_H */
diff --git a/src/northbridge/via/vx900/raminit_ddr3.c b/src/northbridge/via/vx900/raminit_ddr3.c
new file mode 100644
index 0000000..ea8513e
--- /dev/null
+++ b/src/northbridge/via/vx900/raminit_ddr3.c
@@ -0,0 +1,1027 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011-2012  Alexandru Gagniuc <mr.nuke.me 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, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <arch/io.h>
+#include <arch/romcc_io.h>
+
+#include "early_vx900.h"
+#include "raminit.h"
+
+#include <string.h>
+#include <console/console.h>
+#include <device/pci_ids.h>
+
+#include <delay.h>
+
+#define min(a,b) a<b?a:b
+#define max(a,b) a>b?a:b
+
+#define MCU PCI_DEV(0, 0, 3)
+
+
+/* FIXME: Really Alex, is this all you can come up with? */
+void udelay(unsigned usecs)
+{
+	int i;
+	for(i = 0; i < usecs; i++)
+		inb(0x80);
+}
+
+/* Registers 0x78 -> 0x7f contain calibration the settings for DRAM IO timing
+ * The dataset in these registers is selected from 0x70.
+ * Once the correct dataset is selected the delays can be altered.
+ *   mode refers to TxDQS, TxDQ, RxDQS, or RxCR
+ *   setting refers to either manual, average, upper bound, or lower bound
+ */
+#define VX900_CALIB_TxDQSO		0
+#define VX900_CALIB_TxDQO		1
+#define VX900_CALIB_RxDQSI		2
+#define VX900_CALIB_RxDQI_CR		3
+
+#define VX900_CALIB_AVERAGE		0
+#define VX900_CALIB_LOWER		1
+#define VX900_CALIB_UPPER		2
+#define VX900_CALIB_MANUAL		4
+
+static void vx900_delay_calib_mode_select(u8 delay_type, u8 bound)
+{
+	/* Which calibration setting */
+	u8 reg8 = (delay_type & 0x03) << 2;
+	/* Upper, lower, average, or manual setting */
+	reg8 |= (bound & 0x03);
+	pci_write_config8(MCU, 0x70, reg8);
+}
+
+static void vx900_read_0x78_0x7f(timing_dly dly)
+{
+	*((u32*) (&(dly[0]))) = pci_read_config32(MCU, 0x78);
+	*((u32*) (&(dly[4]))) = pci_read_config32(MCU, 0x7c);
+}
+
+static void vx900_write_0x78_0x7f(const timing_dly dly)
+{
+	pci_write_config32(MCU, 0x78, *((u32*) (&(dly[0]))) );
+	pci_write_config32(MCU, 0x7c, *((u32*) (&(dly[4]))) );
+}
+
+static void vx900_read_delay_range(delay_range *d_range, u8 mode)
+{
+	vx900_delay_calib_mode_select(mode, VX900_CALIB_LOWER);
+	vx900_read_0x78_0x7f(d_range->low);
+	vx900_delay_calib_mode_select(mode, VX900_CALIB_AVERAGE);
+	vx900_read_0x78_0x7f(d_range->avg);
+	vx900_delay_calib_mode_select(mode, VX900_CALIB_UPPER);
+	vx900_read_0x78_0x7f(d_range->high);
+}
+
+static void dump_delay(const timing_dly dly)
+{
+	u8 i;
+	for(i = 0; i < 8; i ++)
+	{
+		printram(" %.2x", dly[i]);
+	}
+	printram("\n");
+}
+
+static void dump_delay_range(const delay_range d_range)
+{
+	printram("Lower limit: ");
+	dump_delay(d_range.low);
+	printram("Average:     ");
+	dump_delay(d_range.avg);
+	printram("Upper limit: ");
+	dump_delay(d_range.high);
+}
+
+/* These are some "safe" values that can be used for memory initialization.
+ * Some will stay untouched, and others will be overwritten later on
+ * YOU REALLY NEED THE DATASHEET TO UNDERSTAND THESE !!! */
+static pci_reg8 mcu_init_config[] = {
+	{0x40, 0x01}, /* Virtual rank 0 ending address = 64M - 1 */
+	{0x41, 0x00}, {0x42, 0x00}, {0x43, 0x00}, /* Virtual Ranks ending */
+	{0x48, 0x00}, /* Virtual rank 0 starting address = 0 */
+	{0x49, 0x00}, {0x4a, 0x00}, {0x4b, 0x00}, /* Virtual Ranks beginning */
+	{0x50, 0xd8}, /* Set ranks 0-3 to 11 col bits, 16 row bits */
+	{0x52, 0x33}, /* Map BA0 to A17, BA1 to A18 */
+	{0x53, 0x5b}, /* Map BA2 to A19, FIXME: check RA0/RA1 */
+	/* Disable all virtual ranks */
+	{0x54, 0x00}, {0x55, 0x00}, {0x56, 0x00}, {0x57, 0x00},
+	/* Disable rank interleaving in ranks 0-3 */
+	{0x58, 0x00}, {0x59, 0x00}, {0x5a, 0x00}, {0x5b, 0x00},
+	{0x6c, 0xA0}, /* Memory type: DDR3, VDIMM: 1.5V, 64-bit DRAM */
+	{0xc4, 0x80}, /* Enable 8 memory banks */
+	{0xc6, 0x80}, /* Minimum latency from self-refresh. Bit [7] must be 1 */
+	{0xc8, 0x80}, /* Enable automatic triggering of short ZQ calibration */
+	{0x99, 0xf0}, /* Power Management and Bypass Reorder Queue */
+	/* Enable differential DQS; MODT assertion values suggested in DS */
+	{0x9e, 0xa1}, {0x9f, 0x51},
+	/* DQ/DQM Duty Control - Do not put any extra delays*/
+	{0xe9, 0x00}, {0xea, 0x00}, {0xeb, 0x00}, {0xec, 0x00},
+	{0xed, 0x00}, {0xee, 0x00}, {0xef, 0x00},
+	{0xfc, 0x00}, {0xfd, 0x00}, {0xfe, 0x00}, {0xff, 0x00},
+	/* The following parameters we may or may not change */
+	{0x61, 0x2e}, /* DRAMC Pipeline Control */
+	{0x77, 0x10}, /* MDQS Output Control */
+
+	/* The following are parameters we'll most likely never change again */
+	{0x60, 0xf4}, /* DRAM Pipeline Turn-Around Setting */
+	{0x65, 0x49}, /* DRAM Arbitration Bandwidth Timer - I */
+	{0x66, 0x80}, /* DRAM Queue / Arbitration */
+	{0x69, 0xc6}, /* Bank Interleave Control */
+	{0x6a, 0xfc}, /* DRAMC Request Reorder Control */
+	{0x6e, 0x38}, /* Burst lenght: 8, burst-chop: enable */
+	{0x73, 0x04}, /* Close All Pages Threshold */
+
+	/* The following need to be dynamically asserted */
+	/* See: check_special_registers.c */
+	{0x74, 0xa0}, /* Yes, same 0x74; add one more T */
+	{0x76, 0x60}, /* Write Data Phase Control */
+
+};
+
+/* This table keeps the driving strength control setting that we can safely use
+ * doring initialization. */
+static pci_reg8 mcu_drv_ctrl_config[] = {
+	{0xd3, 0x01}, /* Enable auto-compensation circuit for ODT strength */
+	{0xd4, 0x80}, /* Set internal ODT to dynamically turn on or off */
+	{0xd6, 0x20}, /* Enable strong driving for MA and DRAM commands*/
+	{0xd0, 0x88}, /* (ODT) Strength ?has effect? */
+	{0xe0, 0x88}, /* DRAM Driving – Group DQS (MDQS) */
+	{0xe1, 0x00}, /* Disable offset mode for driving strength control */
+	{0xe2, 0x88}, /* DRAM Driving – Group DQ (MD, MDQM) */
+	{0xe4, 0xcc}, /* DRAM Driving – Group CSA (MCS, MCKE, MODT) */
+	{0xe8, 0x88}, /* DRAM Driving – Group MA (MA, MBA, MSRAS, MSCAS, MSWE)*/
+	{0xe6, 0xff}, /* DRAM Driving – Group DCLK0 (DCLK[2:0] for DIMM0) */
+	{0xe7, 0xff}, /* DRAM Driving – Group DCLK1 (DCLK[5:3] for DIMM1) */
+	{0xe4, 0xcc}, /* DRAM Driving – Group CSA (MCS, MCKE, MODT)*/
+	{0x91, 0x08}, /* MCLKO Output Phase Delay - I */
+	{0x92, 0x08}, /* MCLKO Output Phase Delay - II */
+	{0x93, 0x16}, /* CS/CKE Output Phase Delay */
+	{0x95, 0x16}, /* SCMD/MA Output Phase Delay */
+	{0x9b, 0x3f}, /* Memory Clock Output Enable */
+};
+
+static void vx900_dram_write_init_config(void)
+{
+	size_t i;
+	for(i = 0; i < (sizeof(mcu_init_config)/sizeof(pci_reg8)); i++)
+	{
+		pci_write_config8(MCU, mcu_init_config[i].addr,
+				  mcu_init_config[i].val);
+	}
+}
+
+static void dram_find_spds_ddr3(const dimm_layout *addr, dimm_info *dimm)
+{
+	size_t i = 0;
+	int dimms = 0;
+	do {
+		spd_raw_data spd;
+		spd_read(addr->spd_addr[i], spd);
+		spd_decode_ddr3(&dimm->dimm[i], spd);
+		if(dimm->dimm[i].dram_type != DRAM_TYPE_DDR3) continue;
+		dimms++;
+		dram_print_spd_ddr3(&dimm->dimm[i]);
+	} while(addr->spd_addr[++i] != SPD_END_LIST
+		&& i < VX900_MAX_DIMM_SLOTS);
+
+	if(!dimms)
+		die("No DIMMs were found");
+}
+
+static void dram_find_common_params(const dimm_info *dimms, ramctr_timing *ctrl)
+{
+	size_t  i, valid_dimms;
+	memset(ctrl, 0, sizeof(ramctr_timing));
+	ctrl->cas_supported = 0xff;
+	valid_dimms = 0;
+	for(i = 0; i < VX900_MAX_DIMM_SLOTS; i++)
+	{
+		const dimm_attr *dimm = &dimms->dimm[i];
+		if(dimm->dram_type == DRAM_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);
+
+	}
+
+	if(!ctrl->cas_supported) die("Unsupported DIMM combination. "
+		"DIMMS do not support common CAS latency");
+	if(!valid_dimms) die("No valid DIMMs found");
+}
+
+static void vx900_dram_phys_bank_range(const dimm_info *dimms,
+				       rank_layout *ranks)
+{
+	size_t i;
+	//u8 reg8;
+	for(i = 0; i < VX900_MAX_DIMM_SLOTS; i ++)
+	{
+		if(dimms->dimm[i].dram_type == DRAM_TYPE_UNDEFINED)
+			continue;
+		u8 nranks = dimms->dimm[i].ranks;
+		if(nranks > 2)
+			die("Found DIMM with more than two ranks, which is not "
+			"supported by this chipset");
+		u32 size = dimms->dimm[i].size;
+		if(nranks == 2) {
+			/* Each rank holds half the capacity of the DIMM */
+			size >>= 1;
+			ranks->phys_rank_size[i<<1] = size;
+			ranks->phys_rank_size[(i<<1) | 1] = size;
+		} else {
+			/* Otherwise, everything is held in the first bank */
+			ranks->phys_rank_size[i<<1] = size;
+			ranks->phys_rank_size[(i<<1) | 1] = 0;;
+		}
+	}
+}
+
+static void vx900_dram_driving_ctrl(const dimm_info *dimm)
+{
+	size_t i, ndimms;
+	u8 val;
+
+	/* For ODT range selection, datasheet recommends
+	 * when 1 DIMM  present:  60 Ohm 
+	 * when 2 DIMMs present: 120 Ohm  */
+	ndimms = 0;
+	for(i = 0; i < VX900_MAX_DIMM_SLOTS; i++) {
+		if(dimm->dimm[i].dram_type == DRAM_TYPE_DDR3) ndimms++;
+	}
+	val = (ndimms > 1) ? 0x0 : 0x1;
+	pci_write_config8(MCU, 0xd5, val << 2);
+
+
+	/* FIXME: Assert dynamically based on dimm config */
+	/* DRAM ODT Lookup Table*/
+	pci_write_config8(MCU, 0x9c, 0xe4);
+	
+	for(i = 0; i < (sizeof(mcu_drv_ctrl_config)/sizeof(pci_reg8)); i++)
+	{
+		pci_write_config8(MCU, mcu_drv_ctrl_config[i].addr,
+				  mcu_drv_ctrl_config[i].val);
+	}
+}
+
+static void vx900_clear_vr_map(void)
+{
+	/* Disable all ranks */
+	pci_write_config16(MCU, 0x54, 0x0000);
+}
+
+static void vx900_pr_map_all_vr3(void)
+{
+	/* Enable all ranks and set them to VR3 */
+	pci_write_config16(MCU, 0x54, 0xbbbb);
+}
+/* Map physical rank pr to virtual rank vr */
+static void vx900_map_pr_vr(u8 pr, u8 vr)
+{
+	pr &= 0x3; vr &= 0x3;
+	/* Enable rank (bit [3], and set the VR number bits [1:0] */
+	u16 val = 0x8 | vr;
+	/* Now move the value to the appropriate PR */
+	val <<= (pr * 4);
+	pci_mod_config16(MCU, 0x54, 0xf << (pr * 4), val);
+	printram("Mapping PR %u to VR %u\n", pr, vr);
+}
+
+static void vx900_dram_range(ramctr_timing *ctrl, rank_layout *ranks)
+{
+	size_t i, vrank = 0;
+	u8 reg8;
+	u32 ramsize = 0;
+	vx900_clear_vr_map();
+	for(i = 0; i < VX900_MAX_MEM_RANKS; i++)
+	{
+		u32 rank_size = ranks->phys_rank_size[i];
+		if(!rank_size) continue;
+		ranks->virt[vrank].start_addr = ramsize;
+		ramsize += rank_size;
+		ranks->virt[vrank].end_addr = ramsize;
+
+		/* Physical to virtual rank mapping */
+		/* Rank memory range */
+		reg8 = (ranks->virt[vrank].start_addr >> 2);
+		pci_write_config8(MCU, 0x48 + vrank, reg8);
+		reg8 = (ranks->virt[vrank].end_addr >> 2);
+		pci_write_config8(MCU, 0x40 + vrank, reg8);
+
+		vx900_map_pr_vr(i, vrank);
+		
+		printram("Mapped Physical rank %u, to virtual rank %u\n"
+				 "    Start address: 0x%.8x000000\n"
+				 "    End   address: 0x%.8x000000\n",
+				(int) i, (int) vrank,
+				ranks->virt[vrank].start_addr,
+				ranks->virt[vrank].end_addr);
+
+		/* Move on to next virtual rank */
+		vrank++;
+	}
+
+	printram("Initialized %u virtual ranks, with a total size of %u MB\n",
+			 (int) vrank, ramsize << 4);
+}
+
+static u8 vx900_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; /* FIXME: Should this be an error condition? */
+	return cas_cwl_map[CAS - 4];
+}
+
+static void vx900_dram_timing(ramctr_timing *ctrl)
+{
+	/* Here we are calculating latencies, and writing them to the appropiate
+	 * registers. Some registers do not take latencies from 0T, for example:
+	 * CAS: 000 = 4T, 001 = 5T, 010 = 6T, etc
+	 * In this example we subtract 4T from the result for CAS: (val - 4)
+	 * The & 0x07 after (val - T0) just makes sure that, no matter what
+	 * crazy thing may happen, we do not write outside the bits allocated
+	 * in the register */
+	u8 reg8, val, tFAW, tRRD;
+	u32 val32;
+
+	/* Maximum supported DDR3 frequency is 533MHz (DDR3 1066)
+	 * so make sure we cap it if we have faster DIMMs */
+	if(ctrl->tCK < TCK_533MHZ) ctrl->tCK = TCK_533MHZ;
+	val32 = (1000 << 8) / ctrl->tCK;
+	printram("Selected DRAM frequency: %u MHz\n", val32);
+
+	/* Now find the right DRAM frequency setting,
+	 * and bring it to the closest JEDEC standard frequency */
+	if(ctrl->tCK <= TCK_533MHZ)      {val = 0x07; ctrl->tCK = TCK_533MHZ;}
+	else if(ctrl->tCK <= TCK_400MHZ) {val = 0x06; ctrl->tCK = TCK_400MHZ;}
+	else if(ctrl->tCK <= TCK_333MHZ) {val = 0x05; ctrl->tCK = TCK_333MHZ;}
+	else if(ctrl->tCK <= TCK_266MHZ) {val = 0x04; ctrl->tCK = TCK_266MHZ;}
+
+	/* 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 = vx900_get_CWL(ctrl->CAS);
+	printram("Selected CWL latency   : %uT\n", ctrl->CWL);
+	/* Write CAS and CWL */
+	reg8 = ( ((ctrl->CWL - 4) &0x07) << 4 ) | ((ctrl->CAS - 4) & 0x07);
+	pci_write_config8(MCU, 0xc0, reg8);
+	
+	/* Find tRCD */
+	val = (ctrl->tRCD + ctrl->tCK -1) / ctrl->tCK;
+	printram("Selected tRCD          : %uT\n", val);
+	reg8 = ((val-4) & 0x7) << 4;
+	/* Find tRP */
+	val = (ctrl->tRP + ctrl->tCK -1) / ctrl->tCK;
+	printram("Selected tRP           : %uT\n", val);
+	reg8 |= ((val-4) & 0x7);
+	pci_write_config8(MCU, 0xc1, reg8);
+	
+	/* Find tRAS */
+	val = (ctrl->tRAS + ctrl->tCK -1) / ctrl->tCK;
+	printram("Selected tRAS          : %uT\n", val);
+	reg8 = ((val-15) & 0x7) << 4;
+	/* Find tWR */
+	ctrl->WR = (ctrl->tWR + ctrl->tCK -1) / ctrl->tCK;
+	printram("Selected tWR           : %uT\n", ctrl->WR);
+	reg8 |= ((ctrl->WR-4) & 0x7);
+	pci_write_config8(MCU, 0xc2, reg8);
+	
+	/* Find tFAW */
+	tFAW = (ctrl->tFAW + ctrl->tCK -1) / ctrl->tCK;
+	printram("Selected tFAW          : %uT\n", tFAW);
+	/* Find tRRD */
+	tRRD = (ctrl->tRRD + ctrl->tCK -1) / ctrl->tCK;
+	printram("Selected tRRD          : %uT\n", tRRD);
+	val = tFAW - 4*tRRD;	/* number of cycles above 4*tRRD */
+	reg8 = ((val-0) & 0x7) << 4;	
+	reg8 |= ((tRRD-2) & 0x7);
+	pci_write_config8(MCU, 0xc3, reg8);
+	
+	/* FIXME: When Rx61[5] is set, 1T is added to tRTP */
+	/* Find tRTP */
+	val = (ctrl->tRTP + ctrl->tCK -1) / ctrl->tCK;
+	printram("Selected tRTP          : %uT\n", val);
+	reg8 = ((val & 0x3) << 4);
+	/* Find tWTR */
+	val = (ctrl->tWTR + ctrl->tCK -1) / ctrl->tCK;
+	printram("Selected tWTR          : %uT\n", val);
+	reg8 |= ((val - 2) & 0x7);
+	pci_mod_config8(MCU, 0xc4, 0x3f, reg8);
+	
+	/* DRAM Timing for All Ranks - VI
+	 * [7:6] CKE Assertion Minimum Pulse Width
+	 *     We probably don't want to mess with this just yet.
+	 * [5:0] Refresh-to-Active or Refresh-to-Refresh (tRFC)
+	 *     tRFC = (30 + 2 * [5:0])T
+	 *     Since we previously set RxC4[7]
+	 */
+	reg8 = pci_read_config8(MCU, 0xc5);
+	val = (ctrl->tRFC + ctrl->tCK -1) / ctrl->tCK;
+	printram("Minimum  tRFC          : %uT\n", val);
+	if(val < 30) {
+		val = 0;
+	} else {
+		val = (val -30 + 1 ) / 2;
+	}
+	;
+	printram("Selected tRFC          : %uT\n", 30 + 2 * val);
+	reg8 |= (val & 0x3f);
+	pci_write_config8(MCU, 0xc5, reg8);
+	
+	/* Where does this go??? */
+	val = (ctrl->tRC + ctrl->tCK -1) / ctrl->tCK;
+	printram("Required tRC           : %uT\n", val);
+}
+
+static void vx900_dram_freq(ramctr_timing *ctrl)
+{
+	u8 val;
+
+	/* Program the DRAM frequency */
+	
+	/* Step 1 - Reset the PLL */
+	pci_mod_config8(MCU, 0x90, 0x00, 0x0f);
+	/* Wait at least 10 ns; VIA code delays by 640us */
+	udelay(640);
+
+	/* Step 2 - Set target frequency */
+	if(ctrl->tCK <= TCK_533MHZ)      {val = 0x07; ctrl->tCK = TCK_533MHZ;}
+	else if(ctrl->tCK <= TCK_400MHZ) {val = 0x06; ctrl->tCK = TCK_400MHZ;}
+	else if(ctrl->tCK <= TCK_333MHZ) {val = 0x05; ctrl->tCK = TCK_333MHZ;}
+	else /*ctrl->tCK <= TCK_266MHZ*/ {val = 0x04; ctrl->tCK = TCK_266MHZ;}
+	/* Restart the PLL with the desired frequency */
+	pci_mod_config8(MCU, 0x90, 0x0f, val);
+
+	/* Step 3 - Wait for PLL to stabilize */
+	udelay(2000);
+
+	/* Step 4 - Reset the DLL - Clear [7,4]*/
+	pci_mod_config8(MCU, 0x6b, 0x90, 0x00);
+	udelay(2000);
+
+	/* Step 5 - Enable the DLL - Set bits [7,4] to 01b*/
+	pci_mod_config8(MCU, 0x6b, 0x00, 0x10);
+	udelay(2000);
+
+	/* Step 6 - Start DLL Calibration - Set bit [7] */
+	pci_mod_config8(MCU, 0x6b, 0x00, 0x80);
+	udelay(5);
+
+	/* Step 7 - Finish DLL Calibration - Clear bit [7]*/
+	pci_mod_config8(MCU, 0x6b, 0x80, 0x00);
+	
+	/* Step 8 - If we have registered DIMMs, we need to set bit[0] */
+	if(dimm_is_registered(ctrl->dram_type)){
+		printram("Enabling RDIMM support in memory controller\n");
+		pci_mod_config8(MCU, 0x6c, 0x00, 0x01);
+	}
+}
+
+/* The VX900 can send the MRS commands directly through hardware */
+void vx900_dram_ddr3_do_hw_mrs(u8 ma_swap, u8 rtt_nom,
+				      u8 ods, u8 rtt_wr, u8 srt, u8 asr);
+/*static*/ void vx900_dram_ddr3_do_hw_mrs(u8 ma_swap, u8 rtt_nom,
+				      u8 ods, u8 rtt_wr, u8 srt, u8 asr)
+{
+	u8 reg8 = 0;
+	u32 reg32;
+	if(asr) reg8 |= (1 << 0);
+	if(srt) reg8 |= (1 << 1);
+	reg8 |= ((rtt_wr & 0x03) << 4);
+	pci_write_config8(MCU, 0xcd, reg8);
+	reg8 = 1;
+	if(ma_swap) reg8 |= (1 << 1);
+	reg8 |= ((ods & 0x03) << 2);
+	reg8 |= ((rtt_nom & 0x7) << 4);
+	pci_write_config8(MCU, 0xcc, reg8);
+	reg32=0;
+	while(pci_read_config8(MCU, 0xcc) & 1) reg32++;
+	printram(" Waited %u PCI cycles for HW MRS\n", reg32);
+}
+#include "forgotten.h"
+#include "forgotten.c"
+
+static void vx900_dram_send_soft_mrs(u8 type, u16 cmd)
+{
+	u32 addr;
+	/* Set Fun3_RX6B[2:0] to 011b (MSR Enable). */
+	pci_mod_config8(MCU, 0x6b, 0x07, (3<<0));
+	/* Find the address corresponding to the MRS */
+	addr = vx900_get_mrs_addr(type, cmd);
+	//printram("MRS CPU addr: %.8x\n", addr);
+	/* Execute the MRS */
+	volatile_read(addr);
+	/* Set Fun3_Rx6B[2:0] to 000b (Normal SDRAM Mode). */
+	pci_mod_config8(MCU, 0x6b, 0x07, 0x00);
+}
+
+
+static void vx900_dram_ddr3_dimm_init(const ramctr_timing *ctrl,
+				      const rank_layout *ranks)
+{
+	size_t i;
+
+	/* FIXME: Do we want 0x5b33, or 0x0b33 */
+	/* Set BA[0/1/2] to [A17/18/19] */
+	pci_write_config16(MCU, 0x52, 0x5b33);
+
+	/* Step 01 - Set Fun3_Rx6E[5] to 1b to support burst length. */
+	pci_mod_config8(MCU, 0x6e, 0, 1<<5);
+	/* Step 02 - Set Fun3_RX69[0] to 0b (Disable Multiple Page Mode). */
+	pci_mod_config8(MCU, 0x69, (1<<0), 0x00);
+	/* And set [7:6] to 10b ?*/
+	pci_write_config8(MCU, 0x69, 0x87);
+	
+	/* Step 03 - Set the target physical rank to virtual rank0 and other
+	 * ranks to virtual rank3. */
+	vx900_pr_map_all_vr3();
+	
+	/* Step 04 - Set Fun3_Rx50 to D8h. */
+	pci_write_config8(MCU, 0x50, 0xd8);
+	/* Step 05 - Set Fun3_RX6B[5] to 1b to de-assert RESET# and wait for at
+	 * least 500 us. */
+	pci_mod_config8(MCU, 0x6b, 0x00, (1<<5) );
+	udelay(500);
+
+	/* Step 6 -> 15 - Set the target physical rank to virtual rank 0 and
+	 * other ranks to virtual rank 3.
+	 * Repeat Step 6 to 14 for every rank present, then jump to Step 16. */
+	for(i = 0; i < VX900_MAX_MEM_RANKS; i++)
+	{
+		if(ranks->phys_rank_size[i] == 0) continue;
+		printram("Initializing rank %lu\n", i);
+
+		/* Set target physical rank to virtual rank 0
+		 * other ranks to virtual rank 3*/
+		vx900_map_pr_vr(i, 0);
+
+#define USE_HW_INIT_SEQUENCE 0
+#if USE_HW_INIT_SEQUENCE
+
+		/* FIXME: Is this needed on HW init? */
+		pci_mod_config8(MCU, 0x6b, 0x07, 0x01);	/* Enable NOP */
+		volatile_read(0x0);			/* Do NOP */
+		pci_mod_config8(MCU, 0x6b, 0x07, 0x03);	/* MSR Enable */
+		
+		/* FIXME: Values dependent on DIMM setup
+		 * This works for 1 DIMM
+		 * See init_dram_by_rank.c and get_basic_information.c
+		 * in the VIA provided code */
+		const u8 rtt_nom = DDR3_MR1_RTT_NOM_RZQ2;
+		const u8 ods = DDR3_MR1_ODS_RZQ6;
+		const u8 rtt_wr = DDR3_MR2_RTT_WR_OFF;
+
+		printram("Using Hardware method\n");
+		/* FIXME: MA swap dependent on module */
+		vx900_dram_ddr3_do_hw_mrs(0, rtt_nom, ods, rtt_wr, 0, 0);
+
+		/* FIXME: Are these steps needed on HW init? */
+		/* Send a ZQ calibration command */
+		pci_mod_config8(MCU, 0x6b, 0x07, 0x06);
+		/* Read a double word from any address of the DIMM. */
+		volatile_read(0x0);
+		/* Normal SDRAM Mode */
+		pci_mod_config8(MCU, 0x6b, 0x07, 0x00);
+		
+#else  /* USE_HW_INIT_SEQUENCE */
+		printram("Using software method\n");
+		vx900_dram_ddr3_init_rank(MCU, ctrl, i);
+#endif /* USE_HW_INIT_SEQUENCE */
+		/* Step 15, set the rank to virtual rank 3*/
+		vx900_map_pr_vr(i, 3);
+	}
+
+	/* Step 16 – Set Fun3_Rx6B[2:0] to 000b (Normal SDRAM Mode). */
+	pci_mod_config8(MCU, 0x6b, 0x07, 0x00);
+
+	/* Set BA[0/1/2] to [A13/14/15] */
+	pci_mod_config16(MCU, 0x52, 0x0fff, 0x0911);
+	
+	/* Step 17 – Set Fun3_Rx69[0] to 1b (Enable Multiple Page Mode). */
+	pci_mod_config8(MCU, 0x69, 0x00, (1<<0) );
+
+	printram("DIMM initialization sequence complete\n");
+}
+
+
+static void vx900_rx_capture_range_calib(void)
+{
+	u8 reg8;
+	u16 cmd;
+	const u32 cal_addr = 0x20;
+	
+	/* Set IO calibration address */
+	pci_mod_config16(MCU, 0x8c , 0xfff0, cal_addr&(0xfff0));
+	/* Data pattern must be 0x00 for this calibration
+	 * See paragraph describing Rx8e */
+	pci_write_config8(MCU, 0x8e, 0x00);
+	
+	/* Precharge all */
+	pci_mod_config8(MCU, 0x06b, 0x07, 0x02);
+	volatile_read(0x0);
+	udelay(1000);
+	
+	/* Enable read leveling: Set D0F3Rx71[7]=1
+	 * Set return data format: Clear D0F3Rx71[6]=0 */
+	pci_mod_config8(MCU, 0x71, 0x40, 0x80);
+	
+	/* Put DRAM in read leveling mode */
+	cmd = ddr3_get_mr3(1);
+	vx900_dram_send_soft_mrs(3, cmd);
+	
+	/* Data pattern must be 0x00 for this calibration
+	 * See paragraph describing Rx8e */
+	pci_write_config8(MCU, 0x8e, 0x00);
+	/* Trigger calibration */
+	reg8 = 0xa0;
+	pci_write_config8(MCU, 0x71, reg8);
+	
+	/* Wait for it */
+	while(pci_read_config8(MCU, 0x71) & 0x10) cmd++;
+	printram("RX capture range calibration took %u PCI cycles\n", cmd);
+	
+	/* Disable read leveling, and put dram in normal operation mode */
+	cmd = ddr3_get_mr3(0);
+	vx900_dram_send_soft_mrs(3, cmd);
+	
+	/* Disable read leveling: Set D0F3Rx71[7]=0 */
+	pci_mod_config8(MCU, 0x71, 1<<7, 0);
+}
+
+static void vx900_rx_dqs_delay_calib(void)
+{
+	u16 cmd;
+	const u32 cal_addr = 0x30;
+	
+	/* Set IO calibration address */
+	pci_mod_config16(MCU, 0x8c , 0xfff0, cal_addr&(0xfff0));
+	/* Data pattern must be 0x00 for this calibration
+	 * See paragraph describing Rx8e */
+	pci_write_config8(MCU, 0x8e, 0x00);
+	
+	/* Precharge all */
+	pci_mod_config8(MCU, 0x06b, 0x07, 0x02);
+	volatile_read(0x0);
+	udelay(1000);
+	
+	/* Enable read leveling: Set D0F3Rx71[7]=1
+	 * Set return data format: Clear D0F3Rx71[6]=0
+	 * Set RxDQS to use calibration setting - Clear Rx71[2] and Rx71[0] */
+	pci_mod_config8(MCU, 0x71, 0x45, 0x80);
+	
+	/* Put DRAM in read leveling mode (enable MPR flow) */
+	cmd = ddr3_get_mr3(1);
+	vx900_dram_send_soft_mrs(3, cmd);
+	
+	/* From VIA code; Undocumented
+	 * In theory this enables MODT[3:0] to be asserted*/
+	pci_mod_config8(MCU, 0x9e, 0, 0x80);
+	
+	/* Trigger calibration: Set D0F3Rx71[1:0]=10b */
+	pci_mod_config8(MCU, 0x71, 0x03, 0x02);
+	
+	/* Wait for calibration to complete */
+	while( pci_read_config8(MCU, 0x71) & 0x02 );
+	
+	/* Put DRAM in normal mode (disable MPR flow) */
+	cmd = ddr3_get_mr3(0);
+	vx900_dram_send_soft_mrs(3, cmd);
+	
+	/* Disable read leveling: Set D0F3Rx71[7]=0 */
+	pci_mod_config8(MCU, 0x71, 1<<7, 0);
+}
+
+static void vx900_tx_dqs_trigger_calib(u8 pattern)
+{
+	u32 i;
+	/* Data pattern for calibration */
+	pci_write_config8(MCU, 0x8e, pattern);
+	/* Trigger calibration */
+	pci_mod_config8(MCU, 0x75, 0, 0x20);
+	/* Wait for calibration */
+	i = 0;
+	while(pci_read_config8(MCU, 0x75) & 0x20) i++;
+	printram(" Tx DQS calib took %u PCI cycles\n", i);
+}
+static void vx900_tx_dqs_delay_calib(void)
+{
+	const u32 cal_addr = 0x00;
+	/* Set IO calibration address */
+	pci_mod_config16(MCU, 0x8c , 0xfff0, cal_addr&(0xfff0));
+	/* Set circuit to use calibration results - Clear Rx75[0]*/
+	pci_mod_config8(MCU, 0x75, 0x01, 0);
+	/* Run calibration with first data pattern*/
+	vx900_tx_dqs_trigger_calib(0x5a);
+	/* Run again with different pattern */
+	vx900_tx_dqs_trigger_calib(0xa5);
+}
+
+static void vx900_tx_dq_delay_calib(void)
+{
+	int i = 0;
+	/* Data pattern for calibration */
+	pci_write_config8(MCU, 0x8e, 0x5a);
+	/* Trigger calibration */
+	pci_mod_config8(MCU, 0x75, 0, 0x02);
+	/* Wait for calibration */
+	while(pci_read_config8(MCU, 0x75) & 0x02) i++;
+	printram("TX DQ calibration took %u PCI cycles\n", i);
+}
+
+static void vx900_rxdqs_adjust(delay_range *dly)
+{
+	/* Adjust Rx DQS delay after calibration has been run. This is
+	 * recommended by VIA, but no explanation was provided as to why */
+	size_t i;
+	for(i = 0; i < 8; i++)
+	{
+		if(dly->low[i] < 3)
+		{
+			if(i == 2 || i== 4) dly->low[i] += 4;
+			else dly->avg[i] += 3;
+				
+		}
+		
+		if(dly->high[i] > 0x38) dly->avg[i] -= 6;
+		else if(dly->high[i] > 0x30) dly->avg[i] -= 4;
+
+		if(dly->avg[i] > 0x20) dly->avg[i] = 0x20;
+	}
+
+	/* Put Rx DQS delay into manual mode (Set Rx[2,0] to 01) */
+	pci_mod_config8(MCU, 0x71, 0x05, 0x01);
+	/* Now write the new settings */
+	vx900_write_0x78_0x7f(dly->avg);
+}
+
+static void vx900_calibrate_delays(const ramctr_timing *ctrl,
+				   const rank_layout *ranks)
+{
+	timing_dly dly;
+	size_t i;
+	u8 val;
+	vx900_delay_calib delay_cal;
+	memset(&delay_cal, 0, sizeof(delay_cal));
+	printram("Starting delay calibration\n");
+
+	/* MD Input Data Push Timing Control;
+	 *     use values recommended in datasheet */
+	if      (ctrl->tCK <= TCK_533MHZ) val = 2;
+	else if (ctrl->tCK <= TCK_333MHZ) val = 1;
+	else	 val = 0;
+	val ++; /* FIXME: vendor BIOS sets this to 3 */
+	pci_mod_config8(MCU, 0x74, (0x03 << 1), ((val & 0x03) << 1) );
+
+	/* FIXME: The vendor BIOS does this, but WHY?
+	 * Early DQ/DQS for write cycles */
+	pci_mod_config8(MCU, 0x76, (3<<2), 2<<2);
+	/* FIXME: The vendor BIOS increases the MD input delay - WHY ? */
+	pci_mod_config8(MCU, 0xef, (3<<4), 3<<4);
+	/* FIXME: The vendor BIOS does this - WHY ?*/
+	pci_write_config8(MCU, 0x77, 0x10);
+	/* FIXME: Vendor BIOS goes in with
+	 * 8 page registers
+	 * multiple page mode
+	 * High Priority Refresh request
+	 * -- WHY?*/
+	pci_write_config8(MCU, 0x69, 0xc7);
+
+	/* Set BA[0/1/2] to [A17/18/19] */
+	pci_write_config16(MCU, 0x52, 0x5b33);
+	/* Disable Multiple Page Mode - Set Rx69[0] to 0 */
+	pci_mod_config8(MCU, 0x69, (1<<0), 0x00);
+	
+	/* FIXME: we'll need to set up a loop and do the mapping there */
+	vx900_map_pr_vr(2, 0);
+	
+	/* Run calibrations */if(1){
+	vx900_rx_capture_range_calib();
+	vx900_read_delay_range(&(delay_cal.rx_dqi_cr),
+			       VX900_CALIB_RxDQI_CR);
+	dump_delay_range(delay_cal.rx_dqi_cr);}
+	
+	/*FIXME: Cheating with Rx CR setting
+	 * We need to either use Rx CR calibration
+	 * or set up a table for the calibration */
+	dly[0] = 0x28;
+	dly[1] = 0x1c;
+	dly[2] = 0x28;
+	dly[3] = 0x28;
+	dly[4] = 0x2c;
+	dly[5] = 0x30;
+	dly[6] = 0x30;
+	dly[7] = 0x34;
+	printram("Cheat RxCR 78-7f with Rx71 at 0x%.2x\n",
+		 pci_read_config8(MCU, 0x71));
+	/* We need to put the setting on manual mode */
+	pci_mod_config8(MCU, 0x71, 0, 0x10);
+	dump_delay(dly);
+	vx900_delay_calib_mode_select(VX900_CALIB_RxDQI_CR, VX900_CALIB_MANUAL);
+	vx900_write_0x78_0x7f(dly);
+	
+	/************* RxDQS *************/
+	vx900_rx_dqs_delay_calib();
+	vx900_read_delay_range(&(delay_cal.rx_dqsi), VX900_CALIB_RxDQSI);
+	printram("RX DQS calibration results\n");
+	dump_delay_range(delay_cal.rx_dqsi);
+
+	vx900_rxdqs_adjust(&(delay_cal.rx_dqsi));
+
+	vx900_read_delay_range(&(delay_cal.rx_dqsi), VX900_CALIB_RxDQSI);
+	printram("RX DQS calibration results after adjustment\n");
+	dump_delay_range(delay_cal.rx_dqsi);
+
+	/* FIXME: Vendor BIOS does it again (enable auto-precharge) - WHY? */
+	pci_write_config8(MCU, 0x69, 0xce);
+	
+	if(ctrl->tCK <= TCK_533MHZ){
+	for( i = 0; i< 8; i++) dly[i] = 0x80;
+	pci_mod_config8(MCU, 0x75, 0x00, 0x01); /* manual Tx DQ DQS */
+	vx900_delay_calib_mode_select(VX900_CALIB_TxDQO, VX900_CALIB_MANUAL);
+	vx900_write_0x78_0x7f(dly);
+	vx900_delay_calib_mode_select(VX900_CALIB_TxDQSO, VX900_CALIB_MANUAL);
+	vx900_write_0x78_0x7f(dly);}
+	/************* TxDQS *************/
+	vx900_tx_dqs_delay_calib();
+	
+	vx900_read_delay_range(&(delay_cal.tx_dqso), VX900_CALIB_TxDQSO);
+	printram("Tx DQS calibration results\n");
+	dump_delay_range(delay_cal.tx_dqso);
+	/************* TxDQ  *************/
+	/* FIXME: not sure if multiple page mode should be enabled here
+	 * Vendor BIOS does it*/
+	pci_mod_config8(MCU, 0x69, 0 , 0x01);
+	
+	vx900_tx_dq_delay_calib();
+	vx900_read_delay_range(&(delay_cal.tx_dqo), VX900_CALIB_TxDQO);
+	printram("TX DQ delay calibration results:\n");
+	dump_delay_range(delay_cal.tx_dqo);
+
+	/* write manual settings */
+	pci_mod_config8(MCU, 0x75, 0, 0x01);
+	vx900_delay_calib_mode_select(VX900_CALIB_TxDQSO, VX900_CALIB_MANUAL);
+	vx900_write_0x78_0x7f(delay_cal.tx_dqso.avg);
+	vx900_delay_calib_mode_select(VX900_CALIB_TxDQO, VX900_CALIB_MANUAL);
+	vx900_write_0x78_0x7f(delay_cal.tx_dqo.avg);
+
+	/* Set BA[0/1/2] to [A13/14/15] */
+	pci_mod_config16(MCU, 0x52, 0x0fff, 0x0911);
+}
+
+static void vx900_dram_set_refresh_counter(ramctr_timing *ctrl)
+{
+	u8 reg8;
+	/* Set DRAM refresh counter
+	 * Based on a refresh counter of 0x61 at 400MHz */
+	reg8 = (TCK_400MHZ * 0x61) / ctrl->tCK;
+	pci_write_config8(MCU, 0xc7, reg8);
+}
+
+static void vx900_dram_write_final_config(ramctr_timing *ctrl)
+{
+	/* FIXME: These are quick cheats */
+	pci_write_config8(MCU, 0x50, 0xa0); /* DRAM MA map */
+	pci_write_config16(MCU, 0x52, 0x5911); /* Rank interleave */
+	pci_write_config16(MCU, 0x54, 0x0800); /* Bank mapping */
+	pci_write_config8(MCU, 0x69, 0xe7);
+	pci_write_config8(MCU, 0x72, 0x0f);
+	pci_write_config8(MCU, 0x97, 0xa4);
+	pci_write_config8(MCU, 0xd3, 0x00);
+}
+
+static void print_debug_pci_dev(device_t dev)
+{
+	print_debug("PCI: ");
+	print_debug_hex8((dev >> 20) & 0xff);
+	print_debug_char(':');
+	print_debug_hex8((dev >> 15) & 0x1f);
+	print_debug_char('.');
+	print_debug_hex8((dev >> 12) & 7);
+}
+
+static void dump_pci_device(device_t dev)
+{
+	int i;
+	print_debug_pci_dev(dev);
+	print_debug("\n");
+	
+	for (i = 0; i <= 0xff; i++) {
+		unsigned char val;
+		if ((i & 0x0f) == 0) {
+			print_debug_hex8(i);
+			print_debug_char(':');
+		}
+		
+		if ((i & 0x0f) == 0x08) {
+			print_debug(" |");
+		}
+		
+		val = pci_read_config8(dev, i);
+		print_debug_char(' ');
+		print_debug_hex8(val);
+		
+		if ((i & 0x0f) == 0x0f) {
+			print_debug("\n");
+		}
+	}
+}
+
+void vx900_init_dram_ddr3(const dimm_layout *dimm_addr)
+{
+	dimm_info dimm_prop;
+	ramctr_timing ctrl_prop;
+	rank_layout ranks;
+	device_t mcu;
+
+	/* Locate the Memory controller */
+	mcu = pci_locate_device(PCI_ID(PCI_VENDOR_ID_VIA,
+				       PCI_DEVICE_ID_VIA_VX900_DRAM_CTR), 0);
+
+	if (mcu == PCI_DEV_INVALID) {
+		die("Memory Controller not found\n");
+	}
+
+	dump_pci_device(mcu);
+	memset(&ranks, 0, sizeof(ranks));
+	/* 1) Write some initial "safe" parameters */
+	vx900_dram_write_init_config();
+	/* 2) Get timing information from SPDs */
+	dram_find_spds_ddr3(dimm_addr, &dimm_prop);
+	/* 3) Find lowest common denominator for all modules */
+	dram_find_common_params(&dimm_prop, &ctrl_prop);
+	/* 4) Find the size of each memory rank */
+	vx900_dram_phys_bank_range(&dimm_prop, &ranks);
+	/* 5) Set DRAM driving strength */
+	vx900_dram_driving_ctrl(&dimm_prop);	
+	/* 6) Set DRAM frequency and latencies */
+	vx900_dram_timing(&ctrl_prop);
+	vx900_dram_freq(&ctrl_prop);
+	/* 7) Initialize the modules themselves */
+	vx900_dram_ddr3_dimm_init(&ctrl_prop, &ranks);
+	/* 8) Calibrate receive and transmit delays */
+	vx900_calibrate_delays(&ctrl_prop, &ranks);
+	/* 9) Set refresh counter based on DRAM frequency */
+	vx900_dram_set_refresh_counter(&ctrl_prop);
+	/* 10) Enable Physical to Virtual Rank mapping */
+	vx900_dram_range(&ctrl_prop, &ranks);
+	/* 99) Some final adjustments */
+	vx900_dram_write_final_config(&ctrl_prop);
+	/* Take a dump */
+	dump_pci_device(mcu);
+
+}
diff --git a/src/northbridge/via/vx900/raminit_ddr3_delay_calib.c b/src/northbridge/via/vx900/raminit_ddr3_delay_calib.c
new file mode 100644
index 0000000..a3f39dc
--- /dev/null
+++ b/src/northbridge/via/vx900/raminit_ddr3_delay_calib.c
@@ -0,0 +1,2150 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2009 One Laptop per Child, Association, Inc.
+ *
+ * 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 <stdint.h>
+#include "raminit.h"
+#include <stdint.h>
+#include <arch/io.h>
+#include <arch/romcc_io.h>
+#include <string.h>
+#include <console/console.h>
+#include <device/pci_ids.h>
+#include <console/console.h>
+
+#include "early_vx900.h"
+#include "raminit.h"
+
+
+typedef uint8_t BOOLEAN;
+#define TRUE 	1
+#define FALSE 	0
+
+/*===================================================================
+ * Function   : via_write_phys()
+ * Precondition :
+ * Input      :  addr
+ *                  value
+ * Output     : void
+ * Purpose    :
+ * Reference  : None
+ * ===================================================================*/
+
+static void via_write_phys(volatile uint32_t addr, volatile uint32_t value)
+{
+	volatile uint32_t *ptr;
+	ptr = (volatile uint32_t *)addr;
+	*ptr = (volatile uint32_t)value;
+}
+
+/*===================================================================
+ * Function   : via_read_phys()
+ * Precondition :
+ * Input      :  addr
+ * Output     : u32
+ * Purpose    :
+ * Reference  : None
+ * ===================================================================*/
+
+static uint32_t via_read_phys(volatile u32 addr)
+{
+	volatile uint32_t y;
+	y = *(volatile uint32_t *)addr;
+	return y;
+}
+
+/*===================================================================
+ * Function   : DimmRead()
+ * Precondition :
+ * Input      :  x
+ * Output     : u32
+ * Purpose    :
+ * Reference  : None
+ * ===================================================================*/
+/*
+ * static uint32_t DimmRead(volatile uint32_t x)
+ * {
+ *	volatile uint32_t y;
+ *	y = *(volatile uint32_t *)x;
+ * 
+ *	return y;
+ }
+ */
+/*===================================================================
+ * Function   : dram_base_test()
+ * Precondition : this function used to verify memory
+ * Input      :
+ *                 BaseAdd,
+ *                 length,
+ *                 mode
+ * Output     : u32
+ * Purpose    :write into and read out to verify if dram is correct
+ * Reference  : None
+ * ===================================================================*/
+#define   STEPSPAN        0x1000            //the span when test memory in spare mode
+#define   TESTCOUNT       0x4                // the test count in each range when test memory in spare mode
+#define   TEST_PATTERN    0x5A5A5A5A             //the test pattern
+
+typedef  enum   __DRAM_TEST_MODE{
+	EXTENSIVE,
+	SPARE,
+	MAXMODE
+} DRAM_TEST_MODE;
+
+static BOOLEAN dram_base_test(uint32_t base_addr, uint32_t len,
+			      DRAM_TEST_MODE mode, uint8_t print_flag)
+{
+	uint32_t test_span;
+	uint32_t d32, addr, addr2;
+	uint8_t i, test_count;
+	
+	//decide the test mode is continous or step
+	if (mode == EXTENSIVE) {
+		//the test mode is continuos and must test each unit
+		test_span = 4;
+		test_count = 1;
+	} else if (mode == SPARE) {
+		// the test mode is step and test some unit
+		test_span = STEPSPAN;
+		test_count = TESTCOUNT;
+	} else {
+		printram("the test mode is error\n");
+		return FALSE;
+	}
+	
+	//write each test unit the value with TEST_PATTERN
+	for (addr = base_addr; addr < base_addr + len; addr += test_span) {
+		for (i = 0; i < test_count; i++)
+			via_write_phys(addr + i * 4, TEST_PATTERN);
+		if (print_flag) {
+			if ((u32) addr % 0x10000000 == 0) {
+				printram("Write in Addr = 0x%.8x", addr);
+				printram("\n");
+			}
+		}
+	}
+	
+	//compare each test unit with the value of TEST_PATTERN
+	//and write it with compliment of TEST_PATTERN
+	for (addr = base_addr; addr < base_addr + len; addr += test_span) {
+		for (i = 0; i < test_count; i++) {
+			d32 = via_read_phys(addr + i * 4);
+			via_write_phys(addr + i * 4, (uint32_t) (~TEST_PATTERN));
+			if (d32 != TEST_PATTERN) {
+				addr2 = addr + i * 4;
+				printram("TEST_PATTERN ERROR !!!!! 0x%.8x", addr2);
+				printram(" : 0x%.8x", d32);
+				printram(" \n");
+				return FALSE;
+			}
+		}
+		if (print_flag) {
+			if ((u32) addr % 0x10000000 == 0) {
+				printram("Write in Addr =");
+				print_debug_hex32(addr);
+				printram("\n");
+			}
+		}
+	}
+	
+	//compare each test unit with the value of ~TEST_PATTERN
+	for (addr = base_addr; addr < base_addr + len; addr += test_span) {
+		for (i = (u8) (test_count); i > 0; i--) {
+			d32 = via_read_phys(addr + (i - 1) * 4);
+			if (d32 != ~TEST_PATTERN) {
+				
+				print_debug("~TEST_PATTERN ERROR !!!!! ");
+				addr2 = addr + (i - 1) * 4;
+				print_debug_hex32(addr2);
+				printram(" : ");
+				print_debug_hex32(d32);
+				printram(" \n");
+				return FALSE;
+			}
+		}
+	}
+	
+	return TRUE;
+}
+
+/*===================================================================
+ * Function   : DumpRegisters()
+ * Precondition :
+ * Input      :
+ *                pPCIPPI,
+ *                DevNum,
+ *                FuncNum
+ * Output     : Void
+ * Purpose    :
+ * Reference  : None
+ * ===================================================================*/
+#if 0
+static void dump_registers(uint8_t dev_num, uint8_t func_num)
+{
+	uint8_t i, j;
+	uint8_t d8;
+	
+	d8 = 0;
+	printk(BIOS_DEBUG, "Dev %01x Fun %01x:", dev_num, func_num);
+	printram
+	("\n     00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
+	printram
+	("---------------------------------------------------\n");
+	for (i = 0; i < 0x10; i++)
+	{
+		print_debug_hex8(i);
+		for (j = 0; j < 0x10; j++)
+		{
+			d8 =pci_read_config8(PCI_DEV(0, dev_num, func_num), i * 0x10 + j);
+			printram(" ");
+			print_debug_hex8(d8);
+		}
+		printram("\n");
+	}
+	return;
+}
+
+#endif
+static uint8_t shift_to_mask_bit(uint8_t MaskBit, uint8_t ShiftData)
+{
+	uint8_t value, index, buffer=0;
+	
+	for (index=0; index<8; index++)
+	{
+		buffer = MaskBit;
+		if ((buffer >> index) & 0x01)
+			break;
+	}
+	value = ShiftData << index;
+	return value;
+}
+
+//----------------------------------------------------------------------------
+//Ver	Date			Name	Note
+//----------------------------------------------------------------------------
+//R21
+// 1. 3410-21-LNA-06	Add define to support RDSIT calibration limitation.
+//R32
+// 1. 3410-32-LNA-01    Add different raw cards support.
+//R34
+//3410-34-LNA-03    Add raw card C support. If user populates raw card C DIMM, set the raw card type to raw card A.
+//R36
+//3410-36-LNA-01    When detect Mezza Dimm, bios need to modify DRAM TRCD.
+/***************************************************************************
+ * Copyright (C) 2006 VIA Technologies, Inc. All Rights Reserved.
+ * 
+ * Information in this file is the intellectual property of
+ * VIA Technologies, Inc., and may contains trade secrets that must be stored
+ * and viewed confidentially.
+ * 
+ * 
+ * Filename :      DramInit.h
+ * Version    :     0.1
+ * Date       :      20050401
+ * Author     :   Bruce Cao
+ * 
+ * Description :  The header file of the DramInit.c
+ *                     Noted by Jason Zhao in 20060627.
+ * 
+ * Others :  None
+ ****************************************************************************/
+#ifndef __DRAMINIT_H_
+#define __DRAMINIT_H_
+
+
+
+//#include "../vx900.h"
+//#include "../vx900_cfg.h"
+
+
+#define TRUE 	1
+#define FALSE 	0
+
+
+static void pci_modify_config8(device_t dev, unsigned int where,
+		     u8 clr_mask, u8 set_mask)
+{
+	u8 reg8 = pci_read_config8(dev, where);
+	reg8 &= ~clr_mask;
+	reg8 |= set_mask;
+	pci_write_config8(dev, where, reg8);
+}
+
+
+//==================================================================
+// DDR3 Code Start{
+	//==================================================================
+
+
+#define SPD_MEMORY_TYPE              2   ;/*Memory type FPM,EDO,SDRAM,DDR,DDR2, DDR3*/
+#define SPD_MODULE_TYPE              3
+#define SPD_DENSITY_BANKS            4
+#define SPD_ADDRESSING               5
+#define SPD_NOMINAL_VOLTAGE          6
+#define SPD_MODULE_ORG               7
+#define SPD_BUS_WIDTH                8
+#define SPD_FINE_TIMEBASE            9
+#define SPD_MEDIUM_TIMEBASE_DIVIDEND 10
+#define SPD_MEDIUM_TIMEBASE_DIVISOR  11
+#define SPD_MIN_CYCLE_TIME           12
+#define SPD_SUPPORTED_CL_LSB         14
+#define SPD_SUPPORTED_CL_MSB         15
+#define SPD_MIN_CL_TIME              16
+#define SPD_MIN_TWR                  17
+#define SPD_MIN_TRCD                 18
+#define SPD_MIN_TRRD                 19
+#define SPD_MIN_TRP	             		 20
+#define SPD_UPPER_NIBBLE_TRAS_TRC    21
+#define SPD_MIN_TRAS_LSB             22
+#define SPD_MIN_TRC_LSB              23
+#define SPD_MIN_TRFC_LSB             24
+#define SPD_MIN_TRFC_MSB             25
+#define SPD_MIN_TWTR                 26
+#define SPD_MIN_TRTP                 27
+#define SPD_UPPER_NIBBLE_TFAW        28
+#define SPD_MIN_TFAW                 29
+#define SPD_OPTIONAL_FEATURE         30
+#define SPD_THERMAL_REFRESH          31
+#define SPD_THERMAL_SENSOR           32
+#define SPD_DEVICE_TYPE              33
+#define SPD_RANK1_ADDRMAPPING        63
+
+// According to SPD byte 7 to define.
+#define	RAW_CARD_A	 0x0A // 2R x16
+#define	RAW_CARD_B	 0x01 // 1R x8
+#define RAW_CARD_C  0x02 // 1R x16
+#define	RAW_CARD_F	 0x09 // 2R x8
+//3410-32-LNA-01-start
+#define RAW_CARD_O  0x00 // dram controller can not support x4 DRAM chiop, so use x4 DRAM chip definition to define no dram
+//card type combination of DIMM1:DIMM0
+#define RAW_CARD_AO ((RAW_CARD_A << 4) | RAW_CARD_O)
+#define RAW_CARD_AA ((RAW_CARD_A << 4 )| RAW_CARD_A)
+#define RAW_CARD_AB ((RAW_CARD_A << 4 )| RAW_CARD_B)
+#define RAW_CARD_AF ((RAW_CARD_A << 4 )| RAW_CARD_F)
+#define RAW_CARD_BO ((RAW_CARD_B << 4 )| RAW_CARD_O)
+#define RAW_CARD_BA ((RAW_CARD_B << 4 )| RAW_CARD_A)
+#define RAW_CARD_BB ((RAW_CARD_B << 4 )| RAW_CARD_B)
+#define RAW_CARD_BF ((RAW_CARD_B << 4 )| RAW_CARD_F)
+#define RAW_CARD_FO ((RAW_CARD_F << 4 )| RAW_CARD_O)
+#define RAW_CARD_FA ((RAW_CARD_F << 4 )| RAW_CARD_A)
+#define RAW_CARD_FB ((RAW_CARD_F << 4 )| RAW_CARD_B)
+#define RAW_CARD_FF ((RAW_CARD_F << 4 )| RAW_CARD_F)
+#define RAW_CARD_OO ((RAW_CARD_O << 4 )| RAW_CARD_O)
+#define RAW_CARD_OA ((RAW_CARD_O << 4 )| RAW_CARD_A)
+#define RAW_CARD_OB ((RAW_CARD_O << 4 )| RAW_CARD_B)
+#define RAW_CARD_OF ((RAW_CARD_O << 4 )| RAW_CARD_F)
+
+
+#define DRAMFREQ_533 		0x04
+#define DRAMFREQ_667 		0x05
+#define DRAMFREQ_800 		0x06
+#define DRAMFREQ_1066 		0x07
+#define DRAMFREQ_1333 		0x08
+
+#define DRAMCYCLE_800 		2500
+#define DRAMCYCLE_1066 		1875
+#define DRAMCYCLE_1333 	  1500
+#define DRAMCYCLE_1600 	  1250
+
+#define CMD_2T 				0x00
+#define CMD_1T 				0x01
+
+#define BL4 				0x00
+#define BL8 				0x01
+
+
+#define MAA_BITMAP		0x0F
+#define MAB_BITMAP		0xF0
+#define CHA_BITMAP		MAA_BITMAP
+#define CHB_BITMAP		MAB_BITMAP
+#define ALL_BITMAP		CHA_BITMAP + CHB_BITMAP
+#define ALL_EVEN		((ALL_BITMAP) & 0x55)
+#define CHA_EVEN_BANK	( CHA_BITMAP & 0x55 )
+#define CHB_EVEN_BANK	( CHB_BITMAP & 0x55 )
+
+
+#define NOTREGDIMM 			0x00
+#define ISREGDIMM 			0x01
+
+#define RANK1_MAPPING_STANDARD 	0
+#define RANK1_MAPPING_MIRRORED 	1
+
+//---------------------------------------//
+// 		MSR setting 					 //
+//---------------------------------------//
+
+
+//Typical MA mapping for a MRS command
+//BA2 BA1 BA0	MA15 MA14 MA13 MA12 MA11 MA10 MA09 MA08 MA07 MA06 MA05 MA04 MA03 MA02 MA01 MA00
+//19  18  17	x    x    x    14   13   20   12   11   10   9    8    7    6    5    4    3
+
+// DDR3 MR0 (BA1=0, BA0=0)
+// MA[12]		- DLL Control for Precharge PD
+// MA[11:9]		- TWR
+// MA[8]		- DLL Reset
+// MA[7]		- Mode
+// MA[3]		- Read Burst Type
+// MA[6:4,2]    - CAS LAtency
+// MA[1:0]  	- BL
+//						BA1          BA0
+#define DDR3_MR0		(0 << 18) + (0 << 17)
+
+//							MA12
+#define MR0_DDR3_DLLCONTROL	(1 << 14)
+
+//							MA11	     MA10         MA9
+#define MR0_DDR3_WR5		(0 << 13) + (0 << 20) + (1 << 12)
+#define MR0_DDR3_WR6		(0 << 13) + (1 << 20) + (0 << 12)
+#define MR0_DDR3_WR7		(0 << 13) + (1 << 20) + (1 << 12)
+#define MR0_DDR3_WR8		(1 << 13) + (0 << 20) + (0 << 12)
+#define MR0_DDR3_WR10		(1 << 13) + (0 << 20) + (1 << 12)
+#define MR0_DDR3_WR12		(1 << 13) + (1 << 20) + (0 << 12)
+
+//							MA8
+#define MR0_DLL_RESET		(1 << 11)
+
+//							MA7
+#define MR0_TEST_MODE		(1 << 10)
+
+//							MA6	     MA5         MA4        MA2
+#define MR0_DDR3_CL5		(0 << 9) + (0 << 8) + (1 << 7) + (0 << 5)
+#define MR0_DDR3_CL6		(0 << 9) + (1 << 8) + (0 << 7) + (0 << 5)
+#define MR0_DDR3_CL7		(0 << 9) + (1 << 8) + (1 << 7) + (0 << 5)
+#define MR0_DDR3_CL8		(1 << 9) + (0 << 8) + (0 << 7) + (0 << 5)
+#define MR0_DDR3_CL9		(1 << 9) + (0 << 8) + (1 << 7) + (0 << 5)
+#define MR0_DDR3_CL10		(1 << 9) + (1 << 8) + (0 << 7) + (0 << 5)
+#define MR0_DDR3_CL11		(1 << 9) + (1 << 8) + (1 << 7) + (0 << 5)
+
+//									MA3
+#define MR0_DDR3_READ_BURST_INVL	(1 << 6)
+
+
+//								MA1        MA0
+#define MR0_DDR3_BL8			(0 << 4) + (0 << 3)
+#define MR0_DDR3_ON_THE_FLY		(0 << 4) + (1 << 3)
+#define MR0_DDR3_BL4			(1 << 4) + (0 << 3)
+
+
+//Typical MA mapping for a MRS command
+//BA2 BA1 BA0	MA15 MA14 MA13 MA12 MA11 MA10 MA09 MA08 MA07 MA06 MA05 MA04 MA03 MA02 MA01 MA00
+//19  18  17	x    x    x    14   13   20   12   11   10   9    8    7    6    5    4    3
+
+// DDR3 MR1 (BA1=0, BA0=1)
+// MA[12]	- Qoff, output buffer enable/disable
+// MA[11]	- TDQS Enable
+// MA[7]		- Write Leveling enable
+// MA[4:3]	- Additive Latency
+// MA[9,6,2]	- Rtt_Nom
+// MA[5,1]    	- Output Driver Impedance Control
+// MA[0]  	- DLL Enable
+
+//						BA1          BA0
+#define DDR3_MR1		(0 << 18) + (1 << 17)
+
+//							MA12
+#define MR1_DDR3_QOFF_DIS	(1 << 14)
+
+//							MA11
+#define MR1_DDR3_TDQS_EN	(1 << 13)
+
+//							MA7
+#define MR1_DDR3_WLVL_EN	(1 << 10)
+
+//							MA4           MA3
+#define MR1_DDR3_AL_DIS		(0 << 7) + (0 << 6)
+#define MR1_DDR3_AL_CL1		(0 << 7) + (1 << 6)
+#define MR1_DDR3_AL_CL2		(1 << 7) + (0 << 6)
+
+
+//							MA9          MA6           MA2
+#define MR1_DDR3_RTTNOM_DIS	(0 << 12) + (0 << 9) + (0 << 5)
+#define MR1_DDR3_RTTNOM_60OHM	(0 << 12) + (0 << 9) + (1 << 5)
+#define MR1_DDR3_RTTNOM_120OHM	(0 << 12) + (1 << 9) + (0 << 5)
+#define MR1_DDR3_RTTNOM_40OHM	(0 << 12) + (1 << 9) + (1 << 5)
+#define MR1_DDR3_RTTNOM_20OHM	(1 << 12) + (0 << 9) + (0 << 5)
+#define MR1_DDR3_RTTNOM_30OHM	(1 << 12) + (0 << 9) + (1 << 5)
+
+//							MA5          MA1
+#define MR1_DDR3_RZQ6		(0 << 8) + (0 << 4)
+#define MR1_DDR3_RZQ7		(0 << 8) + (1 << 4)
+
+//							MA0
+#define MR1_DDR3_DLL_EN		(0 << 3)
+#define MR1_DDR3_DLL_DIS	(1 << 3)
+//Typical MA mapping for a MRS command
+//BA2 BA1 BA0	MA15 MA14 MA13 MA12 MA11 MA10 MA09 MA08 MA07 MA06 MA05 MA04 MA03 MA02 MA01 MA00
+//19  18  17	x    x    x    14   13   20   12   11   10   9    8    7    6    5    4    3
+
+// DDR3 MR2 (BA1=1, BA0=0)
+// MA[10:9]	- Rtt_WR
+// MA[7]    	- Self-Refresh Temprerature (SRT) Range
+// MA[6]    	- Auto Self-Refresh (ASR)
+// MA[5:3]	- CAS Write Latency (CWL)
+// MA[2:0]  	- Partial Array Self-Refresh (Optional)
+
+//						BA1          BA0
+#define DDR3_MR2		(1 << 18) + (0 << 17)
+
+//							MA10          MA9
+#define MR2_DDR3_RTTWR_DIS	(0 << 20) + (0 << 12)
+#define MR2_DDR3_RTTWR_60OHM	(0 << 20) + (1 << 12)
+#define MR2_DDR3_RTTWR_120OHM	(1 << 20) + (0 << 12)
+
+//								MA7
+#define MR2_DDR3_SRT_EXTENDED	(1 << 10)
+
+//							MA6
+#define MR2_DDR3_ASR_EN		(1 << 9)
+
+//							MA5         MA4         MA3
+#define MR2_DDR3_CWL_5		(0 << 8) + (0 << 7) + (0 << 6)
+#define MR2_DDR3_CWL_6		(0 << 8) + (0 << 7) + (1 << 6)
+#define MR2_DDR3_CWL_7		(0 << 8) + (1 << 7) + (0 << 6)
+#define MR2_DDR3_CWL_8		(0 << 8) + (1 << 7) + (1 << 6)
+
+//								MA2         MA1         MA0
+#define MR2_DDR3_PASR_FULL		(0 << 5) + (0 << 4) + (0 << 3)
+#define MR2_DDR3_PASR_HALFL		(0 << 5) + (0 << 4) + (1 << 3)
+#define MR2_DDR3_PASR_QUATERL	(0 << 5) + (1 << 4) + (0 << 3)
+#define MR2_DDR3_PASR_1_8_L		(0 << 5) + (1 << 4) + (1 << 3)
+#define MR2_DDR3_PASR_3_4		(1 << 5) + (0 << 4) + (0 << 3)
+#define MR2_DDR3_PASR_HALFH		(1 << 5) + (0 << 4) + (1 << 3)
+#define MR2_DDR3_PASR_QUATEH	(1 << 5) + (1 << 4) + (0 << 3)
+#define MR2_DDR3_PASR_1_8_H		(1 << 5) + (1 << 4) + (1 << 3)
+
+
+//Typical MA mapping for a MRS command
+//BA2 BA1 BA0	MA15 MA14 MA13 MA12 MA11 MA10 MA09 MA08 MA07 MA06 MA05 MA04 MA03 MA02 MA01 MA00
+//19  18  17	x    x    x    14   13   20   12   11   10   9    8    7    6    5    4    3
+
+// DDR3 MR3 (BA1=1, BA0=1)
+// MA[2]    	- MPR
+// MA[1:0]  	- MPR location
+
+//						BA1          BA0
+#define DDR3_MR3		(1 << 18) + (1 << 17)
+
+//							MA2
+#define MR3_DDR3_MPR_EN		(1 << 5)
+
+//Action Type for MR0~3
+#define MR0_INIT			0x00
+
+#define MR1_INIT			0x00
+#define MR1_WLVL_UNDERCALPR	0x01
+#define MR1_WLVL_OTHERPR	0x02
+
+#define MR2_INIT			0x00
+#define MR2_RTTWR_DIS		0x01
+
+#define MR3_INIT			0x00
+#define MR3_MPR_ON			0x01
+
+
+
+
+#define CAL_FIRST_DIMM  0
+#define CAL_SECOND_DIMM 1
+
+#define MODULE_TYPE_UDIMM 0x02
+#define MODULE_TYPE_SODIMM 0x03
+
+#define	IO_RDSIT 	 0
+#define	IO_DQSI 	 1
+#define	IO_DQO 		 2
+#define	IO_DQSO 	 3
+
+#define IOCTL_MANUAL 0
+#define IOCTL_LOW    1
+#define IOCTL_HIGH   2
+#define IOCTL_CENTER 3
+
+
+
+typedef struct{
+	uint8_t	SI_Rdsit_Ref_Freq_1333;
+	uint8_t SI_Rdsit_Ref_Freq_1066;
+	uint8_t	SI_Rdsit_Ref_Freq_0800;
+}VIA_RDSIT_REF_DRAM;
+
+
+
+
+#define CHIP_MAX_SOCKETS 2
+#define CHIP_MAX_RANKS CHIP_MAX_SOCKETS*2
+#define CHIP_MAX_CHANNELS 1
+#define CHANNEL_MAX_SOCKETS	2
+#define CHANNEL_MAX_RANKS   CHANNEL_MAX_SOCKETS*2
+#define  SPD_SIZE 128
+
+typedef struct {
+	uint8_t                                 SpdPresent;
+	uint8_t                                   Buffer[SPD_SIZE];
+} VIA_MEMINIT_SPD_DATA;
+
+typedef struct {
+	uint8_t	PR_CurrentSize;
+	uint8_t	PR_VRank;
+	uint8_t	PR_MergeRank;
+	uint8_t	PR_MergeCount;
+	uint8_t	PR_Dual_En;
+	uint8_t	PR_Above4G;
+	uint8_t	PR_DRAMWaste;
+} VIA_VIRTUALRANK_INFO;
+
+
+//;PR_Above4G, PR_DRAMWaste	Action
+//;0		, 0			this VR in Below 4G
+//;0		, 1			this VR in Below 4G and waste
+//;1		, 0			this VR in Above 4G
+//;1		, 1			this VR in Above 4G and part or all Address >= 16G
+
+typedef struct {
+	uint8_t	TotalSize_GreatThen2G;
+	uint8_t	AllVR_LessThen2G;
+	uint8_t	AllVR_GreatThen2G;
+	uint8_t	TotalSizeOfPRsThatLessThen2G;
+	uint8_t	PR_Size_Table;
+	uint8_t	Virtual_Rank_Count ;
+	uint16_t	Total_DRAM_Size;
+} VIA_ABOVE4G_INFO;
+
+typedef struct{
+	//Tx DQ
+	uint8_t TxDq_Lower_Bound;
+	uint8_t TxDq_Center;
+	uint8_t TxDq_Upper_Bound;
+	//Tx DQS
+	uint8_t TxDqs_Lower_Bound;
+	uint8_t TxDqs_Center;
+	uint8_t TxDqs_Upper_Bound;
+}VIA_TX_IO_CALIBRATION_INFO;
+
+typedef struct {
+	uint8_t                                 BA_Number;
+	uint8_t                                 RA_Number;
+	uint8_t                                 CA_Number;
+	uint8_t    			                	RankSize;	//Unit:64MHz
+} VIA_RANK_INFO;
+
+typedef struct {
+	uint8_t                                 DQSI_LowBound;
+	uint8_t                                 DQSI_HighBound;
+} VIA_CHANNEL_INFO;
+
+
+//========================================
+// define function prototype
+//========================================
+
+//void dram_init(NB_CONFIGURATION *nb_cfg, DRAM_CONFIGURATION *dram_cfg);
+
+//void dram_s3_init(NB_CONFIGURATION *nb_cfg, DRAM_CONFIGURATION *dram_cfg);
+
+//========================================
+// define macro
+//========================================
+
+//==================================================================
+// DDR3 Code End}
+//==================================================================
+
+#endif //__DRAMINIT_H_
+
+
+/*
+ * 
+ * VISA 0.5 DRAM Module
+ * 
+ */
+//----------------------------------------------------------------------------
+//Ver	Date			Name	Note
+//----------------------------------------------------------------------------
+
+
+
+//#include "../via_io_lib.h"
+
+//#include "../vx900.h"
+//#include "../vx900_cfg.h"
+//#include "../stall.h"
+
+//#include "si_dram_tbl.h"
+
+//#include "dram_init.h"
+//#include "dram_util.h"
+
+#define D0F3VR0EA       0x40
+#define D0F3VR1EA       0x41
+#define D0F3VR2EA       0x42
+#define D0F3VR3EA       0x43
+#define D0F3VR0BA       0x48
+#define D0F3VR1BA       0x49
+#define D0F3VR2BA       0x4A
+#define D0F3VR3BA       0x4B
+
+#define D0F3MA          0x50
+#define D0F3INLVSEL     0x52
+#define D0F3INLVSEL_1   0x53
+#define D0F3PVRMAP      0x54
+#define D0F3PVRMAP_1    0x55
+#define D0F3PVRMAP_2    0x56
+#define D0F3PVRMAP_3    0x57
+
+#define D0F3DPTA        0x60
+#define D0F3DPCTL       0x61
+#define D0F3REOPMCTL    0x63
+#define D0F3REOPMCTL_1  0x64
+#define D0F3DARBTIM     0x65
+#define D0F3DQUARB      0x66
+#define D0F3DIMCHSEL    0x67
+#define D0F3PGCTIM      0x68
+#define D0F3BAINLVCTL   0x69
+#define D0F3RQREOCTL    0x6a
+#define D0F3DINITCTL    0x6b
+#define D0F3DTYPE       0x6c
+#define D0F3INORSERC    0x6c
+#define D0F3DATBURCTL   0x6e
+
+#define D0F3IOTIMSEL	0x70
+#define D0F3DITIMCTL    0x71
+#define D0F3PGSCLSAL    0x73
+#define D0F3RDPHCTL     0x74
+#define D0F3DOTIMCTL    0x75
+#define D0F3WDPHCTL     0x76
+#define D0F3DQSOCTL     0x77
+#define D0F3DQG0IOTC	0x78
+
+#define D0F3SEGFSHAD    0x83
+#define D0F3SMAPICDC    0x86
+#define D0F3WMGCTL      0x88
+#define D0F3DTSTMDC     0x8c
+#define D0F3ADRIOCAL    0x8c
+#define D0F3ADRIOCAL_1  0x8d
+#define D0F3DQPIOCAL	0x8e
+
+#define D0F3DCLKOPMD    0x90
+#define D0F3RSELFREF2   0x98
+#define D0F3PMBYREOQ    0x99
+#define D0F3MCLKAEN     0x9b
+#define D0F3ODTLTBL     0x9c
+#define D0F3ODTCTL      0x9e
+#define D0F3ODTCTL2     0x98
+
+#define D0F3DTIMING5    0xc4
+#define D0F3DTIMING6    0xc5
+#define D0F3DTIMING7    0xc6
+#define D0F3REFCNT	0xc7
+#define D0F3DINIT1      0xcc
+
+#define DRAM_ATTRIBUTE rank_layout
+
+void io_timing_control(DRAM_ATTRIBUTE *dram_attr);
+
+
+//==================================================================
+// DDR3 Code Start{
+//==================================================================
+
+
+#define TX_DQ_Clock_Address_H  0x10
+#define TX_DQ_Clock_Pattern    0x5A
+
+#define RX_Input_Enable_Clock_Address_H 	0x20
+#define RX_Input_Enable_Clock_Pattern   	0x5A
+
+#define RX_Input_DQS_Clock_Address_H 	0x30
+#define RX_Input_DQS_Clock_Pattern 	 	0x5A
+
+#define MCU PCI_DEV(0, 0, 3)
+
+#define D0F3IOTIMSEL_DMIOSEL3_0		0xf
+
+#define D0F3IOTIMSEL_DMIOSEL_MANUAL	0x0
+#define D0F3IOTIMSEL_DMIOSEL_CENTER	0x0
+#define D0F3IOTIMSEL_DMIOSEL_LOWER	0x1
+#define D0F3IOTIMSEL_DMIOSEL_UPPER	0x2
+
+#define D0F3IOTIMSEL_DMIOSEL_DQSO	(0<<2)
+#define D0F3IOTIMSEL_DMIOSEL_DQO	(1<<2)
+#define D0F3IOTIMSEL_DMIOSEL_DQSI	(2<<2)
+#define D0F3IOTIMSEL_DMIOSEL_RDSIT	(3<<2)
+
+
+static void set_io_timing_si_register(uint8_t io_timing_mode, uint8_t reg_off, uint8_t nand_data, uint8_t or_data)
+{
+	uint8_t d8;
+	
+	switch (io_timing_mode) {
+		
+		case IO_RDSIT:
+			pci_modify_config8(MCU, D0F3IOTIMSEL, D0F3IOTIMSEL_DMIOSEL3_0, D0F3IOTIMSEL_DMIOSEL_RDSIT+D0F3IOTIMSEL_DMIOSEL_MANUAL);
+			break;
+			
+		case IO_DQO:
+			pci_modify_config8(MCU, D0F3IOTIMSEL, D0F3IOTIMSEL_DMIOSEL3_0, D0F3IOTIMSEL_DMIOSEL_DQO+D0F3IOTIMSEL_DMIOSEL_MANUAL);
+			break;
+			
+		case IO_DQSO:
+			pci_modify_config8(MCU, D0F3IOTIMSEL, D0F3IOTIMSEL_DMIOSEL3_0, D0F3IOTIMSEL_DMIOSEL_DQSO+D0F3IOTIMSEL_DMIOSEL_MANUAL);
+			
+		case IO_DQSI:
+			pci_modify_config8(MCU, D0F3IOTIMSEL, D0F3IOTIMSEL_DMIOSEL3_0, D0F3IOTIMSEL_DMIOSEL_DQSI+D0F3IOTIMSEL_DMIOSEL_MANUAL);
+			break;
+			
+		default:
+			break;
+	}
+	
+	
+	d8= pci_read_config8(MCU, reg_off);
+	d8 = (d8 &(~nand_data))|or_data;
+	
+	switch (io_timing_mode)
+	{
+		case IO_RDSIT:
+			pci_modify_config8(MCU, D0F3IOTIMSEL, D0F3IOTIMSEL_DMIOSEL3_0, D0F3IOTIMSEL_DMIOSEL_RDSIT+D0F3IOTIMSEL_DMIOSEL_MANUAL);
+			break;
+			
+		case IO_DQO:
+			pci_modify_config8(MCU, D0F3IOTIMSEL,  D0F3IOTIMSEL_DMIOSEL3_0, D0F3IOTIMSEL_DMIOSEL_DQO+D0F3IOTIMSEL_DMIOSEL_MANUAL);
+			break;
+			
+		case IO_DQSO:
+			pci_modify_config8(MCU, D0F3IOTIMSEL,  D0F3IOTIMSEL_DMIOSEL3_0, D0F3IOTIMSEL_DMIOSEL_DQSO+D0F3IOTIMSEL_DMIOSEL_MANUAL);
+			break;
+			
+		case IO_DQSI:
+			pci_modify_config8(MCU, D0F3IOTIMSEL,  D0F3IOTIMSEL_DMIOSEL3_0, D0F3IOTIMSEL_DMIOSEL_DQSI+D0F3IOTIMSEL_DMIOSEL_MANUAL);
+			break;
+			
+		default:
+			break;
+	}
+	
+	pci_write_config8(MCU, reg_off, d8);
+}
+
+static void dram_ioctl(uint8_t io_timing_mode, uint8_t io_selection_mode, uint8_t is_write, uint8_t byte_i, uint8_t  *d8)
+{
+	uint8_t or_data, and_data;
+	
+	or_data = 0;
+	and_data = 0;
+	switch(io_timing_mode)
+	{
+		case IO_RDSIT:
+			or_data = D0F3IOTIMSEL_DMIOSEL_RDSIT;
+			break;
+			
+		case IO_DQO:
+			or_data = D0F3IOTIMSEL_DMIOSEL_DQO;
+			break;
+			
+		case IO_DQSO:
+			or_data = D0F3IOTIMSEL_DMIOSEL_DQSO;
+			break;
+			
+		case IO_DQSI:
+			or_data = D0F3IOTIMSEL_DMIOSEL_DQSI;
+			break;
+		default:
+			break;
+	}
+	
+	switch(io_selection_mode)
+	{
+		case IOCTL_MANUAL:
+			or_data |= D0F3IOTIMSEL_DMIOSEL_MANUAL;
+			break;
+			
+		case IOCTL_CENTER:
+			or_data |= D0F3IOTIMSEL_DMIOSEL_CENTER;
+			break;
+			
+		case IOCTL_LOW:
+			or_data |= D0F3IOTIMSEL_DMIOSEL_LOWER;
+			break;
+			
+		case IOCTL_HIGH:
+			or_data |= D0F3IOTIMSEL_DMIOSEL_UPPER;
+			break;
+			
+		default:
+			break;
+	}
+	
+	pci_modify_config8(MCU, D0F3IOTIMSEL, D0F3IOTIMSEL_DMIOSEL3_0, or_data);
+
+#define D0F3DITIMCTL_RSETDSIT	0x10
+#define D0F3DITIMCTL_RSETDSIDLY	0x01
+#define D0F3DOTIMCTL_RSETTXCLK	0x01
+
+
+	if (io_selection_mode == IOCTL_MANUAL)
+	{
+		switch(io_timing_mode)
+		{
+			case IO_RDSIT:
+				pci_modify_config8(MCU, D0F3DITIMCTL, D0F3DITIMCTL_RSETDSIT, D0F3DITIMCTL_RSETDSIT);
+				break;
+				
+			case IO_DQO:
+			case IO_DQSO:
+				pci_modify_config8(MCU, D0F3DOTIMCTL, D0F3DOTIMCTL_RSETTXCLK, D0F3DOTIMCTL_RSETTXCLK);
+				break;
+				
+			case IO_DQSI:
+				pci_modify_config8(MCU, D0F3DITIMCTL, D0F3DITIMCTL_RSETDSIDLY, D0F3DITIMCTL_RSETDSIDLY);
+				break;
+				
+			default:
+				break;
+		}
+	}else
+	{
+		switch(io_timing_mode)
+		{
+			case IO_RDSIT:
+				pci_modify_config8(MCU, D0F3DITIMCTL, D0F3DITIMCTL_RSETDSIT, 0);
+				break;
+			case IO_DQO:
+			case IO_DQSO:
+				pci_modify_config8(MCU, D0F3DOTIMCTL,  D0F3DOTIMCTL_RSETTXCLK, 0);
+				break;
+				
+			case IO_DQSI:
+				pci_modify_config8(MCU, D0F3DITIMCTL, D0F3DITIMCTL_RSETDSIDLY, 0);
+				break;
+				
+			default:
+				break;
+		}
+	}
+	
+	if (is_write){
+		pci_write_config8(MCU, (D0F3DQG0IOTC + byte_i), *d8);
+	}else
+	{
+		*d8 = pci_read_config8(MCU, (D0F3DQG0IOTC + byte_i));
+	}
+}
+
+static const u8 OutputTiming_Tbl[8][8];
+static const u8 OutputTiming_Tbl_Items = 0;
+static const u8 ReadTiming_Tbl[8][8];
+static const u8 ReadTiming_Tbl_Items = 0;
+
+static const u8 SIDramMask = 0;
+static const u8 SIDramAttr = 0;
+static const u8 SIDramReg = 0;
+
+#define Normal 0
+
+static void preset_si_normal_table(DRAM_ATTRIBUTE *dram_attr)
+{
+
+	uint8_t 	   reg_i, or_data, freq_i;
+	
+	uint8_t io_timing_mode = IO_DQO;
+
+	freq_i = 0;
+	/*FIXME:
+	switch (dram_attr->DRAMFreq) {
+		case DRAMFREQ_800: 	freq_i = SIDramFreq800; break;
+		case DRAMFREQ_1066: freq_i = SIDramFreq1066; break;
+		case DRAMFREQ_1333: freq_i = SIDramFreq1333; break;
+		default:			freq_i = SIDramFreq800; break;
+	}
+	*/
+	
+	for (reg_i=0; reg_i< OutputTiming_Tbl_Items; reg_i++)
+	{
+		
+		or_data = shift_to_mask_bit(OutputTiming_Tbl[reg_i][SIDramMask], OutputTiming_Tbl[reg_i][freq_i]);
+		switch (OutputTiming_Tbl[reg_i][SIDramAttr])
+		{
+			case Normal:
+				set_io_timing_si_register( io_timing_mode, OutputTiming_Tbl[reg_i][SIDramReg], OutputTiming_Tbl[reg_i][SIDramMask], or_data);
+				break;
+			default:
+				break;
+		}
+	}
+	
+	io_timing_mode = IO_RDSIT;
+	for (reg_i=0; reg_i< ReadTiming_Tbl_Items; reg_i++)
+	{
+		or_data = shift_to_mask_bit(ReadTiming_Tbl[reg_i][SIDramMask], ReadTiming_Tbl[reg_i][freq_i]);
+		switch (ReadTiming_Tbl[reg_i][SIDramAttr])
+		{
+			case Normal:
+				set_io_timing_si_register( io_timing_mode, ReadTiming_Tbl[reg_i][SIDramReg], ReadTiming_Tbl[reg_i][SIDramMask], or_data);
+				break;
+			default:
+				break;
+		}
+	}
+}
+
+
+static void preset_tx_dqo(DRAM_ATTRIBUTE *dram_attr)
+{
+	uint8_t i, d8;
+	if(1)//(dram_attr->DRAMFreq >= DRAMFREQ_1066)
+	{
+		for (i = 0; i < 8 ; i++)
+		{
+			//Set DQO Rx70-78 bit 7 = 1 when DRAMFReq_1066
+			d8 = 0x80;  //Set RDQWADJ   //3410-24-LNA-DRAM
+			dram_ioctl( IO_DQSO, IOCTL_MANUAL, TRUE, i, &d8);//3410-24-LNA-DRAM
+			d8 = 0x80;  //Set RDQWADJ
+			dram_ioctl( IO_DQO, IOCTL_MANUAL, TRUE, i, &d8);
+		}
+	}
+}
+
+static void preset_rdswadvos(DRAM_ATTRIBUTE *dram_attr)
+{
+	uint8_t i, d8;
+	
+	if(1)//(dram_attr->DRAMFreq >= DRAMFREQ_1066)
+	{
+		for (i = 0; i < 8 ; i++)
+		{
+			//Set DQO Rx70-78 bit 7 = 1 when DRAMFReq_1066
+			d8 = 0x80;
+			dram_ioctl( IO_DQSO, IOCTL_MANUAL, TRUE, i, &d8);
+			d8 = 0x80;
+			dram_ioctl( IO_DQO, IOCTL_MANUAL, TRUE, i, &d8);
+		}
+	}
+}
+
+static void preset_for_lcu_hw(DRAM_ATTRIBUTE *dram_attr)
+{
+	//uint8_t Index, Value8;
+	preset_tx_dqo(dram_attr);
+}
+
+#define D0F3DITIMCTL_RDLLDSIDLY 0x04
+static void rx_input_delay_save(DRAM_ATTRIBUTE *dram_attr)
+{
+	uint8_t i = 0;
+	uint8_t d8 = 0;
+	
+	//set Rx71[2] = 1
+	pci_modify_config8(MCU, D0F3DITIMCTL,
+			   D0F3DITIMCTL_RDLLDSIDLY, D0F3DITIMCTL_RDLLDSIDLY);
+	
+	for (i = 0; i < 8; i++){
+		dram_ioctl( IO_DQSI, IOCTL_MANUAL, FALSE,i, &d8);
+		dram_ioctl( IO_DQSI, IOCTL_MANUAL, TRUE,i, &d8);
+	}
+	
+	//set Rx71[2] = 0
+	pci_modify_config8(MCU, D0F3DITIMCTL, D0F3DITIMCTL_RDLLDSIDLY, 0);
+}
+
+void set_vr_map(uint8_t		dimm_i, uint8_t		vr_i);
+uint32_t get_ddr3_mr0(DRAM_ATTRIBUTE *dram_attr, uint8_t rank_i, uint8_t action_type);
+uint32_t get_ddr3_mr1(DRAM_ATTRIBUTE *dram_attr, uint8_t rank_i, uint8_t action_type);
+uint32_t get_ddr3_mr2(DRAM_ATTRIBUTE *dram_attr, uint8_t rank_i, uint8_t action_type);
+uint32_t get_ddr3_mr3(DRAM_ATTRIBUTE *dram_attr, uint8_t rank_i, uint8_t action_type);
+uint32_t rank1_address_mirror(uint32_t mrs_value);
+void udelay(unsigned usecs);
+static void rx_input_delay_hw(DRAM_ATTRIBUTE *dram_attr)
+{
+	uint8_t  save_rx52,save_rx53,save_rxc7;
+	uint8_t  i,tmp;
+	uint32_t buffer32=0, dummy_read_base=0x00;
+	uint32_t mrs_set;
+	
+	//Step1.Save Rx52, 53 and set Rx52=33, Rx53=5B before RDSIT CAL, NeedeCheck
+	save_rx52 = pci_read_config8(MCU, D0F3INLVSEL);
+	pci_write_config8(MCU, D0F3INLVSEL, 0x33);
+	
+	save_rx53 = pci_read_config8(MCU, D0F3INLVSEL_1);
+	pci_write_config8(MCU, D0F3INLVSEL_1, 0x5B);
+	
+	//save refresh counter value and disable it
+	save_rxc7 = pci_read_config8(MCU, D0F3REFCNT);
+	pci_write_config8(MCU, D0F3REFCNT, 0);
+	
+	//Step 2. Clear all VRank size
+	for(i = 0; i < CHIP_MAX_RANKS; i++)
+	{
+		pci_write_config8(MCU, (D0F3VR0BA+i), 0);
+		pci_write_config8(MCU, (D0F3VR0EA+i), 0);
+	}
+	
+	// Step 3. Map ChA lowest physical rank to VR0 and ChB lowest physical rank to VR4
+	//If DIMM exist, ChA, map to VR3; ChB, map to VR7
+	for(i = 0; i < CHIP_MAX_RANKS; i++)
+	{
+		if(dram_attr->phys_rank_size[i] != 0)
+		{
+			set_vr_map(i, 0x03);
+		}
+	}
+	
+	//Map the lowest rank to VR0/VR4
+	for(i = 0; i < CHIP_MAX_RANKS; i++)
+	{
+		if(dram_attr->phys_rank_size[i] != 0)
+		{
+			set_vr_map( i, 0x00);
+			pci_write_config8(MCU, D0F3VR0EA, 0x01);
+			break;
+		}
+	}
+
+
+#define RDMTSA_31_24 			0xff
+#define D0F3DINITCTL_RSDM2_0		0x07
+#define D0F3DINITCTL_RSDM_NORMAL	0x00
+#define D0F3DINITCTL_RSDM_PREA		0x02
+#define D0F3DINITCTL_RSDM_MRS		0x03
+
+#define D0F3DITIMCTL_RDMRLEN	0x80
+#define D0F3DITIMCTL_RDRLDQPAT	0x40
+	//Step 4. Set IO calibration address
+	pci_modify_config8(MCU, D0F3ADRIOCAL_1 , RDMTSA_31_24, 0);
+	pci_modify_config8(MCU, D0F3ADRIOCAL , 0xF0, RX_Input_DQS_Clock_Address_H);
+	//Precharge all
+	pci_modify_config8(MCU, D0F3DINITCTL, D0F3DINITCTL_RSDM2_0, D0F3DINITCTL_RSDM_PREA);
+	buffer32 = *(uint32_t volatile *)(uint32_t)(dummy_read_base);	// Dummy Read
+	udelay( 1000);	//delay 1ms
+	
+	//Step 5. Set MRS mode
+	pci_modify_config8(MCU, D0F3DINITCTL, D0F3DINITCTL_RSDM2_0, D0F3DINITCTL_RSDM_MRS);
+	
+	//Step6.Enable read leveling and set return data format
+	pci_modify_config8(MCU, D0F3DITIMCTL, D0F3DITIMCTL_RDMRLEN|D0F3DITIMCTL_RDRLDQPAT, D0F3DITIMCTL_RDMRLEN);
+	
+	//Step 8. Trigger MRS3 with MPR enable
+	for(i = 0; i < CHIP_MAX_RANKS; i++)
+	{
+		if(dram_attr->phys_rank_size[i] != 0)
+		{
+			buffer32 = get_ddr3_mr3( dram_attr, i, MR3_MPR_ON);
+			mrs_set = buffer32;
+			//if (dram_attr->Rank1_Mapping[i] == RANK1_MAPPING_MIRRORED)
+			//{
+			//	mrs_set = rank1_address_mirror(buffer32);
+			//}
+			buffer32 = *(u32 volatile *)(u32)(dummy_read_base|mrs_set);	// Dummy Read
+			outb(0x00, 0xed);
+			break;
+		}
+	}
+	
+	//Step9. Set Normal mode
+	pci_modify_config8(MCU, D0F3DINITCTL, D0F3DINITCTL_RSDM2_0, D0F3DINITCTL_RSDM_NORMAL);
+
+#define D0F3ODTCTL_RODTEN	0X80
+#define D0F3DITIMCTL_RSCNDSIDLY	0x02
+	pci_modify_config8(MCU, D0F3ODTCTL, D0F3ODTCTL_RODTEN, D0F3ODTCTL_RODTEN);
+	
+	//Step10.Set D0F3Rx71[1:0]=10b
+	pci_modify_config8(MCU, D0F3DITIMCTL, D0F3DITIMCTL_RSCNDSIDLY|D0F3DITIMCTL_RSETDSIDLY, D0F3DITIMCTL_RSCNDSIDLY);
+	
+	//Step 11. Check if calibration is done
+	do{
+		tmp = 0xFF;
+		tmp = pci_read_config8(MCU, D0F3DITIMCTL);
+		tmp = tmp & D0F3DITIMCTL_RSCNDSIDLY;
+	}while(tmp != 0);
+	
+	//Step 12. Set MRS mode
+	pci_modify_config8(MCU, D0F3DINITCTL, D0F3DINITCTL_RSDM2_0, D0F3DINITCTL_RSDM_MRS);
+	
+	//Step 13. Trigger MRS3 with MPR disable
+	for(i = 0; i < CHIP_MAX_RANKS; i++)
+	{
+		if(dram_attr->phys_rank_size[i] != 0)
+		{
+			buffer32 = get_ddr3_mr3(dram_attr, i, MR3_INIT);
+			mrs_set = buffer32;
+			//if (dram_attr->Rank1_Mapping[i] == RANK1_MAPPING_MIRRORED)
+			//{
+			//	mrs_set = rank1_address_mirror(buffer32);
+			//}
+			buffer32 = *(u32 volatile *)(u32)(dummy_read_base|mrs_set);	// Dummy Read
+			outb(0x00, 0xed);
+			break;
+		}
+	}
+	
+	//Step 14. Set Normal mode
+	pci_modify_config8(MCU, D0F3DINITCTL, D0F3DINITCTL_RSDM2_0, D0F3DINITCTL_RSDM_NORMAL);
+	
+	//Step 16. Set D0F3Rx71[7]=0 to disable read leveling
+	pci_modify_config8(MCU, D0F3DITIMCTL, D0F3DITIMCTL_RDMRLEN, 0);
+	
+	//restore refresh counter value
+	pci_write_config8(MCU, D0F3REFCNT, save_rxc7);
+	
+	//Restore Rx52, 53
+	pci_write_config8(MCU, D0F3INLVSEL, save_rx52);
+	pci_write_config8(MCU, D0F3INLVSEL_1, save_rx53);
+}
+
+static void tx_dws_clock_hw(DRAM_ATTRIBUTE *dram_attr)
+{
+	uint8_t  save_rx52, save_rx53, save_rxc7;
+	uint8_t  i,tmp;
+	uint32_t buffer32=0,dummy_read_base=0x00;
+	uint32_t mrs_set;
+	uint8_t  save_begin[CHIP_MAX_RANKS];
+	uint8_t  save_end[CHIP_MAX_RANKS];
+	uint8_t  save_rx9e,save_rx9c;
+	uint8_t  save_rx54, save_rx55;
+	uint8_t  cha_cal_rank_selected = 0;
+	uint8_t  cal_rank_index = 0;
+	
+	//Step1.Save Rx52, 53 and set Rx52=33, Rx53=5B
+	save_rx52 = pci_read_config8(MCU, D0F3INLVSEL);
+	pci_write_config8(MCU, D0F3INLVSEL, 0x33);
+	
+	save_rx53 = pci_read_config8(MCU, D0F3INLVSEL_1);
+	pci_write_config8(MCU, D0F3INLVSEL_1, 0x5B);
+	
+	//save all VR begin/End addr, and clear them
+	for(i = 0; i < CHIP_MAX_RANKS; i++)
+	{
+		save_begin[i] = pci_read_config8(MCU, (D0F3VR0BA+i));
+		pci_write_config8(MCU, (D0F3VR0BA+i), 0);
+		save_end[i] = pci_read_config8(MCU, (D0F3VR0EA+i));
+		pci_write_config8(MCU, (D0F3VR0EA+i), 0);
+	}
+	
+	//save ODT Control register and disable ODT control
+	save_rx9e = pci_read_config8(MCU, D0F3ODTCTL);
+	pci_write_config8(MCU, D0F3ODTCTL, (save_rx9e & (~D0F3ODTCTL_RODTEN)));
+	
+	//save ODT Lookup Table and set 11100100b for ChA
+	save_rx9c = pci_read_config8(MCU, D0F3ODTLTBL);
+	pci_write_config8(MCU, D0F3ODTLTBL, 0xE4);
+	
+	//save refresh counter value and disable it
+	save_rxc7 = pci_read_config8(MCU, D0F3REFCNT);
+	pci_write_config8(MCU, D0F3REFCNT, 0);
+	
+	//save Rx54-57
+	save_rx54 = pci_read_config8(MCU, D0F3PVRMAP);
+	save_rx55 = pci_read_config8(MCU, D0F3PVRMAP_1);
+
+#define D0F3BAINLVCTL_RPGEN	0x01
+	//Disable Page Mode and Disable Page Mode of multibanks
+	pci_modify_config8(MCU, D0F3BAINLVCTL, D0F3BAINLVCTL_RPGEN, 0);
+	
+	//set MRS mode
+	pci_modify_config8(MCU, D0F3DINITCTL, D0F3DINITCTL_RSDM2_0, D0F3DINITCTL_RSDM_MRS);
+	
+	// Step 3. Map ChA lowest physical rank to VR0 and ChB lowest physical rank to VR4
+	//If DIMM exist, ChA, map to VR3; ChB, map to VR7
+	for(i = 0; i < CHIP_MAX_RANKS; i++)
+	{
+		if(dram_attr->phys_rank_size[i] != 0)
+		{
+			set_vr_map( i, 0x03);
+		}
+	}
+	
+	//A Find the lowest rank existed on ChA and set MRS1 to (WLVL = enabled) and (Rtt_Nom = 120ohm)
+	//Set other rank existed to (WLVL = enabled) and (Qoff = disabled)
+	for(i = 0; i < CHIP_MAX_RANKS; i++)
+	{
+		if(dram_attr->phys_rank_size[i] != 0)
+		{
+			set_vr_map( i, 0x00);
+			pci_write_config8(MCU, D0F3VR0EA, 0x01);
+			if (cha_cal_rank_selected == 0)
+			{
+				//set MRS1 to (WLVL = enabled) and (Rtt_Nom = 120ohm)
+				buffer32 = get_ddr3_mr1( dram_attr, i, MR1_WLVL_UNDERCALPR);
+				mrs_set = buffer32;
+				//if (dram_attr->Rank1_Mapping[i] == RANK1_MAPPING_MIRRORED)
+				//{
+				//	mrs_set = rank1_address_mirror(buffer32);
+				//}
+				buffer32 = *(u32 volatile *)(u32)(dummy_read_base|mrs_set);	// Dummy Read
+				outb(0x00, 0xed);
+				cal_rank_index = i;
+				cha_cal_rank_selected = 1;
+			}else{
+				//set MRS1 to (WLVL = enabled) and (Qoff = disabled)
+				buffer32 = get_ddr3_mr1( dram_attr, i, MR1_WLVL_OTHERPR);
+				mrs_set = buffer32;
+				//if (dram_attr->Rank1_Mapping[i] == RANK1_MAPPING_MIRRORED)
+				//{
+				//	mrs_set = rank1_address_mirror( buffer32);
+				//}
+				buffer32 = *(u32 volatile *)(u32)(dummy_read_base|mrs_set);	// Dummy Read
+				outb(0x00, 0xed);
+			}
+			//Map to VR3 and Clear End Address
+			set_vr_map( i, 0x03);
+			pci_write_config8(MCU, D0F3VR0EA, 0x00);
+		}
+	}
+
+#define D0F3ODTCTL_RDMWLPR_Offset	2
+#define D0F3ODTCTL_RDMWLPR1_0		0xc0
+
+#define D0F3DOTIMCTL_RDMWLEN		0x04
+	//Step 8. Set which physical rank is under write leveling
+	tmp = cal_rank_index << D0F3ODTCTL_RDMWLPR_Offset;
+	pci_modify_config8(MCU, D0F3ODTCTL, D0F3ODTCTL_RDMWLPR1_0, tmp);
+	
+	//Step 9. Start Write Leveling
+	pci_modify_config8(MCU, D0F3DOTIMCTL, D0F3DOTIMCTL_RDMWLEN, D0F3DOTIMCTL_RDMWLEN);
+	
+	//Step10.Check if Write Leveling is done
+	do{
+		tmp = pci_read_config8(MCU, D0F3DOTIMCTL);
+	}while((tmp & D0F3DOTIMCTL_RDMWLEN)!=0);
+	
+	//Step11.Stop Write Leveling
+	pci_modify_config8(MCU, D0F3DOTIMCTL, D0F3DOTIMCTL_RDMWLEN, 0);
+	
+	//Step 17. Restore all existed ranks MR1 to normal value
+	for(i = 0; i < CHIP_MAX_RANKS; i++)
+	{
+		if(dram_attr->phys_rank_size[i] != 0)
+		{
+			set_vr_map( i, 0x00);
+			pci_write_config8(MCU, D0F3VR0EA, 0x01);
+			
+			//set MRS1 to normal value
+			buffer32 = get_ddr3_mr1( dram_attr, i, MR1_INIT);
+			mrs_set = buffer32;
+			//if (dram_attr->Rank1_Mapping[i] == RANK1_MAPPING_MIRRORED)
+			//{
+			//	mrs_set = rank1_address_mirror( buffer32);
+			//}
+			buffer32 = *(u32 volatile *)(u32)(dummy_read_base|mrs_set);	// Dummy Read
+			outb(0x00, 0xed);
+			
+			//Map to VR3 and Clear End Address
+			set_vr_map( i, 0x03);
+			pci_write_config8(MCU, D0F3VR0EA, 0x00);
+		}
+	}
+	
+	//Step18.Assign PR number to VR number  <-Tony_debug maybe remove Step 18
+	for(i = 0; i < CHIP_MAX_RANKS; i++)
+	{
+		set_vr_map( i, i);
+	}
+	
+	//Restore Rx54-57
+	pci_write_config8(MCU, D0F3PVRMAP_1, save_rx55);
+	pci_write_config8(MCU, D0F3PVRMAP, save_rx54);
+	
+	//Step 19. Set Normal mode
+	pci_modify_config8(MCU, D0F3DINITCTL, D0F3DINITCTL_RSDM2_0, D0F3DINITCTL_RSDM_NORMAL);
+	
+	//Enable Page Mode and Enable Page Mode of multibanks
+	pci_modify_config8(MCU, D0F3BAINLVCTL, D0F3BAINLVCTL_RPGEN, D0F3BAINLVCTL_RPGEN
+	);
+	
+	//Step 20. Restore auto refresh value
+	pci_write_config8(MCU, D0F3REFCNT, save_rxc7);
+	
+	//Step 22. Restore ChA ODT lookup table
+	pci_write_config8(MCU, D0F3ODTLTBL, save_rx9c);
+	
+	//Step 23. Restore ODT control
+	pci_write_config8(MCU, D0F3ODTCTL, save_rx9e);
+	
+	//Restore all ranks' begin/end address
+	for(i = 0; i < CHIP_MAX_RANKS; i++)
+	{
+		pci_write_config8(MCU, (D0F3VR0BA+i), save_begin[i]);
+		pci_write_config8(MCU, (D0F3VR0EA+i), save_end[i]);
+	}
+	
+	//Restore Rx52, 53
+	pci_write_config8(MCU, D0F3INLVSEL, save_rx52);
+	pci_write_config8(MCU, D0F3INLVSEL_1, save_rx53);
+}
+
+static void tx_dqs_clock_lcuhw(DRAM_ATTRIBUTE *dram_attr, uint8_t dimm_no)   //3410-32-LNA-01
+{
+	uint8_t i,tmp;
+	uint8_t d8;
+	
+	//Step 2. Clear all VRank size
+	for(i = 0; i < CHIP_MAX_RANKS; i++)
+	{
+		pci_write_config8(MCU, (D0F3VR0BA+i), 0);
+		pci_write_config8(MCU, (D0F3VR0EA+i), 0);
+	}
+	
+	
+	//If DIMM exist, ChA, map to VR3; ChB, map to VR7
+	for(i = 0; i < CHIP_MAX_RANKS; i++)
+	{
+		if(dram_attr->phys_rank_size[i] != 0)
+		{
+			set_vr_map( i, 0x03);
+		}
+	}
+	
+	
+	//Step 3. Set the begin/end address of the lowest virthual rank of channel A/B to 0~256
+	//Because we will assign PR number to VR number, so we could check the PR rank map
+	//to determine which VR we should program.
+	for(i = 0; i < CHIP_MAX_RANKS; i++)
+	{
+		if(dram_attr->phys_rank_size[i] != 0)
+		{
+			if(dimm_no == CAL_SECOND_DIMM)
+			{
+				pci_write_config8(MCU, D0F3PVRMAP_1, 0xB8);
+				pci_write_config8(MCU, D0F3PVRMAP, 0xBB);
+			}else    //3410-32-LNA-01
+			{
+				set_vr_map( i, 0x00);
+			}
+			pci_write_config8(MCU, D0F3VR0EA, 0x01);
+			break;
+		}
+	}
+	
+#define D0F3BAINLVCTL_RNONPGMEN	0
+#define RDMTSA_23_20		0xf0
+#define D0F3DOTIMCTL_RSCNTXCLKS	0x20
+	//Disable Page Mode and Disable Page Mode of multibanks, NeedCheck Why MUST use this???
+	d8 = pci_read_config8(MCU, D0F3BAINLVCTL);
+	pci_modify_config8(MCU, D0F3BAINLVCTL, D0F3BAINLVCTL_RNONPGMEN + D0F3BAINLVCTL_RPGEN, D0F3BAINLVCTL_RNONPGMEN);
+	
+	//Step 3. Program F3Rx8E, set IO calibration data pattern
+	pci_write_config8(MCU, D0F3DQPIOCAL, 0x5A);
+	
+	//Step 4. Set F3Rx8D[7:0] and F3Rx8C[7:4] to the value
+	//which is different with that for the upper calibration
+	pci_modify_config8(MCU, D0F3ADRIOCAL_1,RDMTSA_31_24, 0);
+	pci_modify_config8(MCU, D0F3ADRIOCAL,RDMTSA_23_20, 0);
+	
+	//Step 6. Start Calibration, set auto mode for Internal Clock Phase for DQ/DQS output
+	pci_modify_config8(MCU, D0F3DOTIMCTL,D0F3DOTIMCTL_RSETTXCLK, 0);
+	
+	//Step 7. enable scan internal clock phase for DQS output
+	pci_modify_config8(MCU, D0F3DOTIMCTL,D0F3DOTIMCTL_RSCNTXCLKS, D0F3DOTIMCTL_RSCNTXCLKS);
+	
+	//Step 8. Check if calibration is done
+	do{
+		tmp = pci_read_config8(MCU, D0F3DOTIMCTL);
+	}while((tmp & D0F3DOTIMCTL_RSCNTXCLKS) != 0);
+	
+	//second test use different pattern
+	//Step 3. Program F3Rx8E, set IO calibration data pattern
+	pci_write_config8(MCU, D0F3DQPIOCAL, 0xA5);
+	
+	//Step 7. enable scan internal clock phase for DQS output
+	pci_modify_config8(MCU, D0F3DOTIMCTL,D0F3DOTIMCTL_RSCNTXCLKS, D0F3DOTIMCTL_RSCNTXCLKS);
+	
+	//Step 8. Check if calibration is done
+	do{
+		tmp = pci_read_config8(MCU, D0F3DOTIMCTL);
+	}while((tmp & D0F3DOTIMCTL_RSCNTXCLKS) != 0);
+	
+	//Enable Page Mode and Enable Page Mode of multibanks
+	pci_write_config8(MCU, D0F3BAINLVCTL,d8);
+	
+}
+
+static void tx_dq_clock_hw(DRAM_ATTRIBUTE *dram_attr, uint8_t diomm_no)
+{
+	uint8_t i,tmp;
+	
+	//Step 2. Clear all VRank size
+	for(i = 0; i < CHIP_MAX_RANKS; i++)
+	{
+		pci_write_config8(MCU, (D0F3VR0BA+i), 0);
+		pci_write_config8(MCU, (D0F3VR0EA+i), 0);
+	}
+	
+	//If DIMM exist, ChA, map to VR3; ChB, map to VR7
+	for(i = 0; i < CHIP_MAX_RANKS; i++)
+	{
+		if(dram_attr->phys_rank_size[i] != 0)
+		{
+			set_vr_map( i, 0x03);
+		}
+	}
+	
+	//Step 3. Set the begin/end address of the lowest virthual rank of channel A/B to 0~256
+	//Because we will assign PR number to VR number, so we could check the PR rank map
+	//      to determine which VR we should program.
+	for(i = 0; i < CHIP_MAX_RANKS; i++)
+	{
+		if(dram_attr->phys_rank_size[i] != 0)
+		{
+			if(diomm_no == CAL_SECOND_DIMM)
+			{
+				set_vr_map( 2, 0x00);    //set rank2
+			}else
+			{
+				set_vr_map( i, 0x00);
+			}
+			pci_write_config8(MCU, D0F3VR0EA, 0x01);
+			break;
+		}
+	}
+	
+	//Step 4. Set IO calibration address
+	pci_modify_config8(MCU, D0F3ADRIOCAL_1,RDMTSA_31_24, 0);
+	pci_modify_config8(MCU, D0F3ADRIOCAL,RDMTSA_23_20, 0);
+	
+	//step5.Set IO calibration pattern
+	pci_write_config8(MCU, D0F3DQPIOCAL, 0x5A);
+	//set Rx75[0]=0
+	pci_modify_config8(MCU, D0F3DOTIMCTL,D0F3DOTIMCTL_RSETTXCLK, 0);
+#define D0F3DOTIMCTL_RSCNTXCLKD 0x02
+	//Step 6. Enable scan internal clock phase for DQ output
+	pci_modify_config8(MCU, D0F3DOTIMCTL,D0F3DOTIMCTL_RSCNTXCLKD, D0F3DOTIMCTL_RSCNTXCLKD);
+	
+	//Step 7. Check if DQ output clock calibration is done
+	do{
+		tmp = pci_read_config8(MCU, D0F3DOTIMCTL);
+	}while((tmp&D0F3DOTIMCTL_RSCNTXCLKD)!= 0x00);
+}
+
+
+static void rx_input_delay_cal_to_manual(DRAM_ATTRIBUTE *dram_attr, uint16_t * p)
+{
+	uint8_t  i, d8, d81;
+	
+	
+	//for (i = 0; i < 8; i++){
+		for (i = 0; i < 8; i++)
+		{
+			dram_ioctl(IO_DQSI,IOCTL_CENTER,FALSE,i,&d8);
+			
+			dram_ioctl(IO_DQSI,IOCTL_LOW,FALSE,i,&d81);
+			if(d81 < 3)
+			{
+				if(i == 0x02 || i == 0x04)
+				{
+					d8 += 0x04;
+				}else
+				{
+					d8 += 0x03;
+				}
+			}
+			//3410-25-LNA-01-end
+			
+			//3410-24-LNA-DRAM-end
+			//3410-31-LNA-03-start
+			dram_ioctl(IO_DQSI,IOCTL_HIGH,FALSE,i,&d81);
+			if (d81 > 0x38)
+			{
+				d8 -= 0x06;
+			}else if(d81 > 0x30)
+			{
+				d8 -= 0x04;
+			}
+			
+			if (d8 > 0x20)
+			{
+				d8 = 0x20;
+			}
+			
+			p[i] += d8;//3410-42-LNA-02
+		}
+}
+
+static void tx_dqs_clock_cal_to_manual(DRAM_ATTRIBUTE *dram_attr)
+{
+	uint8_t  i,d8;
+	
+	for (i = 0; i < 8; i++)
+	{
+		dram_ioctl(IO_DQSO,IOCTL_CENTER, FALSE, i, &d8);
+		dram_ioctl(IO_DQSO,IOCTL_MANUAL, TRUE, i, &d8);
+	}
+}
+
+static void tx_dq_clock_cal_to_mannual(DRAM_ATTRIBUTE *dram_attr)
+{
+	uint8_t  i,d8;
+	
+	for (i = 0; i < 8; i++)
+	{
+		dram_ioctl(IO_DQO,IOCTL_CENTER, FALSE, i, &d8);
+		dram_ioctl(IO_DQO,IOCTL_MANUAL, TRUE, i, &d8);
+	}
+}
+
+
+#define DQSI_THRESHOLD  0x10
+//3410-30-LNA-02    #define	DQSI_THRESHOLD_1066	0x20		//3410-21-LNA-04
+#define	DQSI_THRESHOLD_1066	0x10    //3410-30-LNA-02    //for OC 10%+Vcore=1.00V
+static BOOLEAN check_dqsi_range(DRAM_ATTRIBUTE *dram_attr)
+{
+	uint8_t i, low, high;
+	BOOLEAN flag;
+	
+	flag = TRUE;
+	//check DQSI range, if one of 8 bytes is less than 10h, return FALSE.
+	for(i = 8; i > 0; i--)
+	{
+		dram_ioctl(IO_DQSI,IOCTL_LOW,FALSE,i-1,&low);
+		dram_ioctl(IO_DQSI,IOCTL_HIGH,FALSE,i-1,&high);
+		
+		if(1)//(dram_attr->DRAMFreq == DRAMFREQ_1066)
+		{
+			if((high -low) < DQSI_THRESHOLD_1066)
+			{
+				flag = FALSE;
+				break;
+			}
+		}else {
+			if ((high - low) < DQSI_THRESHOLD)
+			{
+				flag = FALSE;
+				break;
+			}
+		}
+	}
+	
+	return flag;
+}
+
+#define DQSO_THRESHOLD  0x12
+static BOOLEAN check_dqso_range(void)
+{
+	//check DQSO range, if one of 8 bytes is less than 18, set CF = 1
+	uint8_t i, low, high;
+	BOOLEAN flag = TRUE;
+	
+	for(i = 0; i < 8; i++)
+	{
+		dram_ioctl(IO_DQSO,IOCTL_LOW,FALSE,i,&low);
+		if(low == 0x7F)
+		{
+			printram("DQS lower bound error\n");
+			flag = FALSE;
+			break;
+		}
+		dram_ioctl(IO_DQSO,IOCTL_HIGH,FALSE,i,&high);
+		if((high - low) < DQSO_THRESHOLD)
+		{
+			printram("DQS range error at %u with "
+			"0x%.2x - 0x%.2x = 0x%.2x \n", i, high, low, high - low);
+			flag = FALSE;
+			break;
+		}
+	}
+	return flag;
+}
+
+
+#define LOW_BOUND_OFFSET 0x0-0x5
+#define HIGH_BOUND_OFFSET 0x5
+#define DQO_THRESHOLD  0x09     //3410-36-LNA-03
+
+static BOOLEAN check_dqo_range(void)
+{
+	uint8_t i, low,high;
+	BOOLEAN flag;
+	
+	flag = TRUE;
+	//check DQSI range, if one of 8 bytes is less than 9h, return FALSE.
+	for(i = 8; i > 0; i--)
+	{
+		dram_ioctl(IO_DQO,IOCTL_LOW,FALSE,i-1,&low);
+		if (low == 0x7F)
+		{
+			flag = FALSE;
+			printram("DQ lower bound error\n");
+			break;
+		}
+		dram_ioctl(IO_DQO,IOCTL_HIGH,FALSE,i-1,&high);
+		if((high - low) < DQO_THRESHOLD)
+		{
+			flag = FALSE;
+			printram("DQ Range error\n");
+			break;
+		}
+	}
+	
+	return flag;
+}
+
+
+static void	rx_input_enable_si2(DRAM_ATTRIBUTE *dram_attr)
+{
+	//uint32_t count;
+	//uint8_t d8;
+	u8 dram_freq;
+	
+	dram_freq = DRAMFREQ_1066; //dram_attr->DRAMFreq;
+	
+	pci_modify_config8(MCU, D0F3IOTIMSEL, D0F3IOTIMSEL_DMIOSEL3_0, D0F3IOTIMSEL_DMIOSEL_RDSIT+D0F3IOTIMSEL_DMIOSEL_MANUAL);
+	//4310-32-LNA-01-start
+#if 0
+	if(dram_attr->ModuleType == MODULE_TYPE_SODIMM)
+	{
+		switch(dram_attr->RawCardType)
+		{
+			case RAW_CARD_AO:
+			case RAW_CARD_AA:
+			case RAW_CARD_AB:
+			case RAW_CARD_BO:
+			case RAW_CARD_BA:
+			case RAW_CARD_BB:
+			case RAW_CARD_OA:
+			case RAW_CARD_OB:
+				for(count = 0; count < RdsitRefRawCardABTbl_Items; count++)
+				{
+					if(dram_freq == DRAMFREQ_1333)
+					{
+						d8 = RdsitRefRawCardABTbl[count][SI_Rdsit_Ref_Freq_1333_OFFSET];
+					}else if(dram_freq == DRAMFREQ_1066)
+					{
+						d8 = RdsitRefRawCardABTbl[count][SI_Rdsit_Ref_Freq_1066_OFFSET];
+					}else
+					{
+						d8 = RdsitRefRawCardABTbl[count][SI_Rdsit_Ref_Freq_0800_OFFSET];
+					}
+					pci_write_config8(MCU, (D0F3DQG0IOTC+count), d8);
+				}
+				break;
+			case RAW_CARD_AF:
+			case RAW_CARD_BF:
+			case RAW_CARD_FA:
+			case RAW_CARD_FB:
+				for(count = 0; count < RdsitRefRawCardABFTbl_Items; count++)
+				{
+					if(dram_freq == DRAMFREQ_1333)
+					{
+						d8 = RdsitRefRawCardABFTbl[count][SI_Rdsit_Ref_Freq_1333_OFFSET];
+					}else if(dram_freq == DRAMFREQ_1066)
+					{
+						d8 = RdsitRefRawCardABFTbl[count][SI_Rdsit_Ref_Freq_1066_OFFSET];
+					}else
+					{
+						d8 = RdsitRefRawCardABFTbl[count][SI_Rdsit_Ref_Freq_0800_OFFSET];
+					}
+					pci_write_config8(MCU, (D0F3DQG0IOTC+count), d8);
+				}
+				break;
+			case RAW_CARD_FO:
+			case RAW_CARD_FF:
+			case RAW_CARD_OF:
+				for(count = 0; count < RdsitRefRawCardFTbl_Items; count++)
+				{
+					if(dram_freq == DRAMFREQ_1333)
+					{
+						d8 = RdsitRefRawCardFTbl[count][SI_Rdsit_Ref_Freq_1333_OFFSET];
+					}else if(dram_freq == DRAMFREQ_1066)
+					{
+						d8 = RdsitRefRawCardFTbl[count][SI_Rdsit_Ref_Freq_1066_OFFSET];
+					}else
+					{
+						d8 = RdsitRefRawCardFTbl[count][SI_Rdsit_Ref_Freq_0800_OFFSET];
+					}
+					pci_write_config8(MCU, (D0F3DQG0IOTC+count), d8);
+				}
+				
+				break;
+			default:
+				break;
+		}
+		
+	}    else
+#endif
+/* FIXME:
+	{
+		for(count = 0; count < RdsitRefUDIMMTbl_Items; count++)
+		{
+			if(dram_freq == DRAMFREQ_1333)
+			{
+				d8 = RdsitRefUDIMMTbl[count][SI_Rdsit_Ref_Freq_1333_OFFSET];
+			}else if(dram_freq == DRAMFREQ_1066)
+			{
+				d8 = RdsitRefUDIMMTbl[count][SI_Rdsit_Ref_Freq_1066_OFFSET];
+			}else
+			{
+				d8 = RdsitRefUDIMMTbl[count][SI_Rdsit_Ref_Freq_0800_OFFSET];
+			}
+			pci_write_config8(MCU, (D0F3DQG0IOTC+count), d8);
+		}
+	}
+	*/
+	pci_write_config8(MCU, 0x78,  0x28);
+	pci_write_config8(MCU, 0x79,  0x1c);
+	pci_write_config8(MCU, 0x7a,  0x28);
+	pci_write_config8(MCU, 0x7b,  0x28);
+	pci_write_config8(MCU, 0x7c,  0x2c);
+	pci_write_config8(MCU, 0x7d,  0x30);
+	pci_write_config8(MCU, 0x7e,  0x30);
+	pci_write_config8(MCU, 0x7f,  0x34);
+}
+
+//3410-22-LNA-DRAM-end
+
+//3410-32-LNA-01-start
+static void set_average_setting(DRAM_ATTRIBUTE *dram_attr)
+{
+	/*FIXME:
+	uint8_t i,d81, d82,d83, d84;
+	//set TxDQS
+	for (i = 0; i < 8; i++)
+	{
+		d81 = dram_attr->VIA_DRAM_AutoCal[CAL_FIRST_DIMM][i].TxDqs_Upper_Bound;
+		d82 = dram_attr->VIA_DRAM_AutoCal[CAL_SECOND_DIMM][i].TxDqs_Upper_Bound;
+		if( d81 > d82)
+		{
+			d81 = d82;
+		}
+		d83 = dram_attr->VIA_DRAM_AutoCal[CAL_FIRST_DIMM][i].TxDqs_Lower_Bound;
+		d84 = dram_attr->VIA_DRAM_AutoCal[CAL_SECOND_DIMM][i].TxDqs_Lower_Bound;
+		if(d83 < d84)
+		{
+			d83 = d84;
+		}
+		d81 += d83;
+		d81 >>= 1;
+		d82 = d81 & 0x1F;
+		if(d82 >= 0x1C)
+		{
+			d81 -= 0x1C;
+		}else
+		{
+			d81 += 0x04;
+		}
+		dram_ioctl(IO_DQSO,IOCTL_MANUAL,TRUE,i,&d81);
+	}
+	//set TxDQ
+	for (i = 0; i < 8; i++)
+	{
+		d81 = dram_attr->VIA_DRAM_AutoCal[CAL_FIRST_DIMM][i].TxDq_Upper_Bound;
+		d82 = dram_attr->VIA_DRAM_AutoCal[CAL_SECOND_DIMM][i].TxDq_Upper_Bound;
+		if( d81 > d82)
+		{
+			d81 = d82;
+		}
+		d83 = dram_attr->VIA_DRAM_AutoCal[CAL_FIRST_DIMM][i].TxDq_Lower_Bound;
+		d84 = dram_attr->VIA_DRAM_AutoCal[CAL_SECOND_DIMM][i].TxDq_Lower_Bound;
+		if( d83 < d84)
+		{
+			d83 = d84;
+		}
+		d81 += d83;
+		d81 >>= 1;
+		d82 = d81 & 0x1F;
+		if(d82 >= 0x14)
+		{
+			d81 -= 0x14;
+		}else
+		{
+			d81 += 0x0C;
+		}
+		dram_ioctl(IO_DQO,IOCTL_MANUAL,TRUE,i,&d81);
+	}
+	*/
+	
+}
+static void tx_dqs_clock_save_setting(DRAM_ATTRIBUTE *dram_attr, uint8_t dimm_no)
+{
+	/* FIXME:
+	uint8_t i;
+	pci_modify_config8(MCU, D0F3IOTIMSEL,D0F3IOTIMSEL_DMIOSEL3_0,D0F3IOTIMSEL_DMIOSEL_DQSO+D0F3IOTIMSEL_DMIOSEL_UPPER);
+	//save TX DQS Upper Bound
+	for (i = 0; i < 8; i++)
+	{
+		dram_attr->VIA_DRAM_AutoCal[dimm_no][i].TxDqs_Upper_Bound = pci_read_config8(MCU, (D0F3DQG0IOTC + i));
+	}
+	//save TX DQS Center Bound
+	pci_modify_config8(MCU, D0F3IOTIMSEL,D0F3IOTIMSEL_DMIOSEL3_0,D0F3IOTIMSEL_DMIOSEL_DQSO+D0F3IOTIMSEL_DMIOSEL_CENTER);
+	for (i = 0; i < 8; i++)
+	{
+		dram_attr->VIA_DRAM_AutoCal[dimm_no][i].TxDqs_Center = pci_read_config8(MCU, (D0F3DQG0IOTC + i));
+	}
+	//save TX DQS Lower Bound
+	pci_modify_config8(MCU, D0F3IOTIMSEL,D0F3IOTIMSEL_DMIOSEL3_0,D0F3IOTIMSEL_DMIOSEL_DQSO+D0F3IOTIMSEL_DMIOSEL_LOWER);
+	for (i = 0; i < 8; i++)
+	{
+		dram_attr->VIA_DRAM_AutoCal[dimm_no][i].TxDqs_Lower_Bound = pci_read_config8(MCU, (D0F3DQG0IOTC + i));
+	}
+	*/
+}
+static void tx_dq_clock_save_setting(DRAM_ATTRIBUTE *dram_attr, uint8_t dimm_no)
+{
+	/*FIXME:
+	uint8_t i;
+	pci_modify_config8(MCU, D0F3IOTIMSEL,D0F3IOTIMSEL_DMIOSEL3_0,D0F3IOTIMSEL_DMIOSEL_DQO+D0F3IOTIMSEL_DMIOSEL_UPPER);
+	//save TX DQS Upper Bound
+	for (i = 0; i < 8; i++)
+	{
+		dram_attr->VIA_DRAM_AutoCal[dimm_no][i].TxDq_Upper_Bound = pci_read_config8(MCU, (D0F3DQG0IOTC + i));
+	}
+	//save TX DQS Center Bound
+	pci_modify_config8(MCU, D0F3IOTIMSEL,D0F3IOTIMSEL_DMIOSEL3_0,D0F3IOTIMSEL_DMIOSEL_DQO+D0F3IOTIMSEL_DMIOSEL_CENTER);
+	for (i = 0; i < 8; i++)
+	{
+		dram_attr->VIA_DRAM_AutoCal[dimm_no][i].TxDq_Center = pci_read_config8(MCU, (D0F3DQG0IOTC + i));
+	}
+	//save TX DQS Lower Bound
+	pci_modify_config8(MCU, D0F3IOTIMSEL,D0F3IOTIMSEL_DMIOSEL3_0,D0F3IOTIMSEL_DMIOSEL_DQO+D0F3IOTIMSEL_DMIOSEL_LOWER);
+	for (i = 0; i < 8; i++)
+	{
+		dram_attr->VIA_DRAM_AutoCal[dimm_no][i].TxDq_Lower_Bound = pci_read_config8(MCU, (D0F3DQG0IOTC + i));
+	}
+	*/
+}
+
+//defined in init_dram_by_rank.c
+void reinit_dram(DRAM_ATTRIBUTE *dram_attr);
+
+#define USE_WLVL  0
+
+
+
+//==================================================================
+// DDR3 Code End}
+//==================================================================
+
+
+void set_vr_map(uint8_t		dimm_i, uint8_t		vr_i)
+{
+	uint8_t				vr_reg_off;
+	uint8_t				d8;
+	
+	vr_reg_off = D0F3PVRMAP + ((dimm_i & 6) >> 1);
+	
+	d8 = pci_read_config8(MCU, vr_reg_off); // 0x54 - 0x55
+	if (dimm_i & 8)
+	{
+		if (dimm_i & 0x10)
+		{
+			d8 = 0;
+		} else
+		{
+			d8 = 0x88;
+		}
+	} else {
+		if (dimm_i & 1)
+		{
+			d8 &= 0x0F;
+			d8 |= (vr_i << 4);
+			if (!(dimm_i & 0x10))
+			{
+				d8 |= 0x80;
+			}
+		} else {
+			d8 &= 0xF0;
+			d8 |= vr_i;
+			if (!(dimm_i & 0x10))
+			{
+				d8 |= 0x08;
+			}
+		}
+	}
+	
+	pci_write_config8( MCU, vr_reg_off, d8);	// 0x54 - 0x55
+}
+
+uint32_t get_ddr3_mr3(DRAM_ATTRIBUTE *dram_attr, uint8_t rank_i, uint8_t action_type)
+{
+	
+	//action_type - action type [MR3_Init/MR3_MPR_On]
+	
+	u32		mr3_setting = DDR3_MR3;
+	
+	if (action_type == MR3_MPR_ON)
+	{
+		mr3_setting |= MR3_DDR3_MPR_EN;
+	}
+	return mr3_setting;
+	
+}
+
+static void dbg_dump_0x78_0x7f(void)
+{
+	u8 i;
+	for(i = 0x78; i < 0x80; i ++)
+	{
+		printram(" %.2x", pci_read_config8(MCU, i));
+	}
+	printram("\n");
+}
+
+static void dbg_dump_calib(const u8 what)
+{
+	u8 reg8;
+	/* Dump lower bound */
+	reg8 = ((what & 0x3) << 2) | 0x1;
+	pci_write_config8(MCU, 0x70, reg8);
+	printram("Lower bound : ");
+	dbg_dump_0x78_0x7f();
+	
+	/* Dump average values */
+	reg8 = ((what & 0x3) << 2);
+	pci_write_config8(MCU, 0x70, reg8);
+	printram("Average     : ");
+	dbg_dump_0x78_0x7f();
+	
+	/* Dump upper bound */
+	reg8 = ((what & 0x3) << 2) | 0x2;
+	pci_write_config8(MCU, 0x70, reg8);
+	printram("Upper bound : ");
+	dbg_dump_0x78_0x7f();
+}
+
+void io_timing_control(DRAM_ATTRIBUTE *dram_attr)
+{
+	BOOLEAN    result, result1;
+	uint16_t   cal_tx_num = 0;
+	uint8_t    dimm_no;
+	uint16_t   cal_rx_num = 0;
+	uint8_t 	  i;
+	uint16_t   my_dqsi[8];
+	
+	result = FALSE;
+	result1 = FALSE;
+	u32 ncals = 0;
+	
+	
+	
+	//Cal Rx
+	preset_si_normal_table(dram_attr);
+	rx_input_delay_save(dram_attr);
+	rx_input_enable_si2( dram_attr);
+	
+	while(!(result&&result1))
+	{
+		ncals++;
+		for(i = 0; i < 8; i++)
+		{
+			my_dqsi[i] = 0;
+		}
+		printram("R");
+		for(cal_rx_num = 0; cal_rx_num < 8; cal_rx_num++)
+		{
+			rx_input_delay_hw(dram_attr);
+			rx_input_delay_cal_to_manual( dram_attr, &my_dqsi[0]);
+		}
+
+		
+		for(i = 0; i < 8; i++)
+		{
+			if(my_dqsi[i] & 0x0007)
+			{
+				my_dqsi[i] >>= 3;
+				my_dqsi[i]++;
+			}else
+			{
+				my_dqsi[i] >>= 3;
+			}
+			
+			dram_ioctl(IO_DQSI,IOCTL_MANUAL, TRUE, i, (uint8_t *)&my_dqsi[i]);
+		}
+		
+		result = check_dqsi_range(dram_attr);
+		//enable Rx Manual mode
+		pci_modify_config8(MCU,D0F3DITIMCTL,
+				   D0F3DITIMCTL_RSETDSIDLY | D0F3DITIMCTL_RSETDSIT,
+		     D0F3DITIMCTL_RSETDSIDLY | D0F3DITIMCTL_RSETDSIT);
+		
+		//Cal Tx
+		dimm_no = CAL_FIRST_DIMM;
+		while(1)
+		{    //3410-32-LNA-01
+			result =FALSE;     //3410-36-LNA-02
+			result1 = FALSE;    //3410-36-LNA-02
+			printram("T");
+			for (cal_tx_num = 0; cal_tx_num < 300; cal_tx_num++)
+			{
+				if (USE_WLVL == 1)
+				{
+					preset_rdswadvos( dram_attr);
+					tx_dws_clock_hw( dram_attr);
+					tx_dqs_clock_cal_to_manual( dram_attr);
+					tx_dq_clock_hw( dram_attr,dimm_no);
+					tx_dq_clock_cal_to_mannual( dram_attr);
+				}else{
+					preset_for_lcu_hw( dram_attr);
+					tx_dqs_clock_lcuhw( dram_attr,dimm_no);
+					tx_dqs_clock_cal_to_manual( dram_attr);
+					tx_dqs_clock_save_setting( dram_attr, dimm_no);
+					
+					result = check_dqso_range();
+					
+					if(!result)
+					{
+						printram("DQS range fail\n");
+						dbg_dump_calib(0);
+						continue;
+					}
+				
+					tx_dq_clock_hw(dram_attr, dimm_no);
+					tx_dq_clock_cal_to_mannual(dram_attr);
+					tx_dq_clock_save_setting(dram_attr, dimm_no);
+				}
+			
+				result = check_dqo_range();
+				if(!result)
+				{
+					printram("DQ range fail\n");
+					dbg_dump_calib(1);
+					continue;
+				}
+				//enable Tx Manual mode
+				pci_modify_config8(MCU,D0F3DOTIMCTL, D0F3DOTIMCTL_RSETTXCLK, D0F3DOTIMCTL_RSETTXCLK);
+				
+				result1 = dram_base_test( 0, 0x40,EXTENSIVE, FALSE);
+				if(result&&result1)
+				{
+					break;
+				}
+				if((!result) || (!result1))
+				{
+					printram("f");
+				}
+			}
+			if(result &&result1)
+			{
+				if(dimm_no == CAL_SECOND_DIMM)  //DIMM1 calibration is done?
+				{
+					set_average_setting( dram_attr);
+					break;
+				} else {
+					if(0)//dram_attr->ChADIMMNumber == 2)    //3410-35-LNA-02
+					{
+						dimm_no = CAL_SECOND_DIMM;
+					} else {
+						break;
+					}
+				}
+			} else {
+				break;
+			}
+		}
+		//after adjust DQSI
+		//		reinit_dram(dram_attr);    //3410-36-LNA-02
+	}
+	
+	pci_write_config8(MCU, D0F3IOTIMSEL, 0x04);
+	printram("Calib loop did %u loops\n", ncals);
+}
\ No newline at end of file
diff --git a/src/northbridge/via/vx900/romstrap.inc b/src/northbridge/via/vx900/romstrap.inc
new file mode 100644
index 0000000..2eb2de5
--- /dev/null
+++ b/src/northbridge/via/vx900/romstrap.inc
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2004 Tyan Computer
+ * (Written by Yinghai Lu <yhlu at tyan.com> for Tyan Computer)
+ * Copyright (C) 2007 Rudolf Marek <r.marek at assembler.cz>
+ * Copyright (C) 2009 One Laptop per Child, Association, Inc.
+ * Copyright (C) 2011-2012  Alexandru Gagniuc <mr.nuke.me 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
+ */
+
+/* This file constructs the ROM strap table for VX900 */
+
+	.section ".romstrap", "a", @progbits
+
+	.globl __romstrap_start
+__romstrap_start:
+tblpointer:
+	.long 0x77886047
+	.long 0x00777777
+	.long 0x00000000
+	.long 0x00000000
+	.long 0x00888888
+	.long 0x00AA1111
+	.long 0x00000000
+	.long 0x00000000
+
+/*
+ * The pointer to above table should be at 0xffffffd0,
+ * the table itself MUST be aligned to 128B it seems!
+ */
+rspointers:
+	.long tblpointer				// It will be 0xffffffd0
+
+	.globl __romstrap_end
+
+__romstrap_end:
+.previous
diff --git a/src/northbridge/via/vx900/romstrap.lds b/src/northbridge/via/vx900/romstrap.lds
new file mode 100644
index 0000000..b51c979
--- /dev/null
+++ b/src/northbridge/via/vx900/romstrap.lds
@@ -0,0 +1,27 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007 AMD
+ * (Written by Yinghai Lu <yinghai.lu at amd.com> for AMD)
+ * Copyright (C) 2011  Alexandru Gagniuc <mr.nuke.me 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, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+SECTIONS {
+	. = (0x100000000 - 0x2c) - (__romstrap_end - __romstrap_start);
+	.romstrap (.): {
+		*(.romstrap)
+	}
+}
diff --git a/src/northbridge/via/vx900/vx900.h b/src/northbridge/via/vx900/vx900.h
new file mode 100644
index 0000000..2137daf
--- /dev/null
+++ b/src/northbridge/via/vx900/vx900.h
@@ -0,0 +1,26 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011  Alexandru Gagniuc <mr.nuke.me 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, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define VX800_ACPI_IO_BASE	0x0400
+
+#define VX900_NB_IOAPIC_ID		0x2
+#define VX900_NB_IOAPIC_BASE		0xfecc000
+
+#define VX900_SB_IOAPIC_ID		0x1
+#define VX900_SB_IOAPIC_BASE		0xfec0000
\ No newline at end of file




More information about the coreboot mailing list