[coreboot] Patch set updated for coreboot: d5864ea NOTFORMERGE: VX900 early init (non-functional)

Alexandru Gagniuc (mr.nuke.me@gmail.com) gerrit at coreboot.org
Tue Jul 17 06:28:18 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 d5864eadb7f7360e88af9029601f58a6c1f0ecc1
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/arch/x86/include/arch/romcc_io.h      |   28 +
 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/Kconfig                 |    3 +
 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    |   89 +++
 src/northbridge/via/Kconfig               |    1 +
 src/northbridge/via/Makefile.inc          |    1 +
 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  | 1063 +++++++++++++++++++++++++++++
 src/northbridge/via/vx900/romstrap.inc    |   50 ++
 src/northbridge/via/vx900/romstrap.lds    |   27 +
 src/northbridge/via/vx900/vx900.h         |   26 +
 26 files changed, 2790 insertions(+), 0 deletions(-)

diff --git a/src/arch/x86/include/arch/romcc_io.h b/src/arch/x86/include/arch/romcc_io.h
index 37fb7ab..8876b28 100644
--- a/src/arch/x86/include/arch/romcc_io.h
+++ b/src/arch/x86/include/arch/romcc_io.h
@@ -244,6 +244,34 @@ static inline __attribute__((always_inline)) void pci_write_config32(device_t de
 #endif
 }
 
+static inline __attribute__((always_inline))
+void pci_mod_config8(device_t dev, unsigned int where,
+		     uint8_t clr_mask, uint8_t set_mask)
+{
+	uint8_t reg8 = pci_read_config8(dev, where);
+	reg8 &= ~clr_mask;
+	reg8 |= set_mask;
+	pci_write_config8(dev, where, reg8);
+}
+static inline __attribute__((always_inline))
+void pci_mod_config16(device_t dev, unsigned int where,
+		      uint16_t clr_mask, uint16_t set_mask)
+{
+	uint16_t reg16 = pci_read_config16(dev, where);
+	reg16 &= ~clr_mask;
+	reg16 |= set_mask;
+	pci_write_config16(dev, where, reg16);
+}
+static inline __attribute__((always_inline))
+void pci_mod_config32(device_t dev, unsigned int where,
+		      uint32_t clr_mask, uint32_t set_mask)
+{
+	uint32_t reg32 = pci_read_config32(dev, where);
+	reg32 &= ~clr_mask;
+	reg32 |= set_mask;
+	pci_write_config32(dev, where, reg32);
+}
+
 #define PCI_DEV_INVALID (0xffffffffU)
 static inline device_t pci_io_locate_device(unsigned pci_id, device_t dev)
 {
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/Kconfig b/src/mainboard/via/Kconfig
index 6980548..48f4055 100644
--- a/src/mainboard/via/Kconfig
+++ b/src/mainboard/via/Kconfig
@@ -9,6 +9,8 @@ config BOARD_VIA_EPIA_CN
 	bool "EPIA-CN"
 config BOARD_VIA_EPIA_M700
 	bool "EPIA-M700"
+config BOARD_VIA_EPIA_M850
+	bool "EPIA-M850"
 config BOARD_VIA_EPIA_M
 	bool "EPIA-M"
 config BOARD_VIA_EPIA_N
@@ -23,6 +25,7 @@ endchoice
 source "src/mainboard/via/epia/Kconfig"
 source "src/mainboard/via/epia-cn/Kconfig"
 source "src/mainboard/via/epia-m700/Kconfig"
+source "src/mainboard/via/epia-m850/Kconfig"
 source "src/mainboard/via/epia-m/Kconfig"
 source "src/mainboard/via/epia-n/Kconfig"
 source "src/mainboard/via/pc2500e/Kconfig"
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..9c07f47
--- /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..8f68bf7
--- /dev/null
+++ b/src/mainboard/via/epia-m850/romstage.c
@@ -0,0 +1,89 @@
+/*
+ * 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(0x4e, 0x10)
+
+/* 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);
+
+
+	device_t hbc = PCI_DEV(0,0,2);
+	pci_write_config8(hbc, 0x50, 0x08);
+	pci_write_config8(hbc, 0x51, 0x3c);
+	pci_write_config8(hbc, 0x52, 0xcf);
+	pci_write_config8(hbc, 0x53, 0x44);
+	pci_write_config8(hbc, 0x54, 0x70);
+	pci_write_config8(hbc, 0x55, 0x20);
+	pci_write_config8(hbc, 0x56, 0x63);
+	pci_write_config8(hbc, 0x5d, 0xa2);
+	pci_write_config8(hbc, 0x5e, 0x44);
+	pci_write_config8(hbc, 0x5f, 0x46);
+
+	if(0)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/Kconfig b/src/northbridge/via/Kconfig
index 2c38acf..8a747b9 100644
--- a/src/northbridge/via/Kconfig
+++ b/src/northbridge/via/Kconfig
@@ -4,3 +4,4 @@ source src/northbridge/via/cn400/Kconfig
 source src/northbridge/via/vt8601/Kconfig
 source src/northbridge/via/vt8623/Kconfig
 source src/northbridge/via/vx800/Kconfig
+source src/northbridge/via/vx900/Kconfig
diff --git a/src/northbridge/via/Makefile.inc b/src/northbridge/via/Makefile.inc
index 75cb15b..2e74b61 100644
--- a/src/northbridge/via/Makefile.inc
+++ b/src/northbridge/via/Makefile.inc
@@ -4,4 +4,5 @@ subdirs-$(CONFIG_NORTHBRIDGE_VIA_CN700) += cn700
 subdirs-$(CONFIG_NORTHBRIDGE_VIA_CX700) += cx700
 subdirs-$(CONFIG_NORTHBRIDGE_VIA_CN400) += cn400
 subdirs-$(CONFIG_NORTHBRIDGE_VIA_VX800) += vx800
+subdirs-$(CONFIG_NORTHBRIDGE_VIA_VX900) += vx900
 
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..37aae81
--- /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 &= ~(0x07);
+	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 &= ~(0x07);
+	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);
+	}
+	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,
+			   DDR3_MR0_BURST_LENGTH_CHOP);
+	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 &= ~(0x07);
+	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..6268070
--- /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_dq_cr;
+	delay_range rx_dqs;
+	delay_range tx_dq;
+	delay_range tx_dqs;
+} 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..f1ca45a
--- /dev/null
+++ b/src/northbridge/via/vx900/raminit_ddr3.c
@@ -0,0 +1,1063 @@
+/*
+ * 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_TxDQS		0
+#define VX900_CALIB_TxDQ		1
+#define VX900_CALIB_RxDQS		2
+#define VX900_CALIB_RxDQ_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, 0x03}, /* 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 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;
+	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 align 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);
+
+	/* 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;
+
+	/* 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 1
+#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);
+
+		/* 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;
+
+	/* We need to disable refresh commands so that they don't interfere */
+	const u8 ref_cnt = pci_read_config8(MCU, 0xc7);
+	pci_write_config8(MCU, 0xc7, 0);
+	/* 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);
+
+	/* Restore the refresh counter*/
+	//pci_write_config8(MCU, 0xc7, ref_cnt);
+	if(ref_cnt);
+
+	/* FIXME: should we save it before, or should we just set it as is */
+	pci_write_config16(MCU, 0x52, 0x5911);
+}
+
+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_dram_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");
+
+	/**** Read delay control ****/
+	/* MD Input Data Push Timing Control;
+	 *     use values recommended in datasheet
+	 * Setting this too low causes the Rx window to move below the range we
+	 * need it so we can capture it with Rx_78_7f
+	 * This causes Rx calibrations to be too close to 0, and Tx
+	 * calibrations will fail.
+	 * Setting this too high causes the window to move above the range.
+	 */
+	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 increases the MD input delay - WHY ? */
+	pci_mod_config8(MCU, 0xef, (3<<4), 3<<4);
+
+
+	/**** Write delay control ****/
+	/* 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 does this - Output preamble ?*/
+	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);
+
+	/* It's very important that we keep all ranks which are not calibrated
+	 * mapped to VR3. Even if we disable them, if they are mapped to VR0
+	 * (the rank we use for calibrations), the calibrations may fail in
+	 * unexpected ways. */
+	vx900_pr_map_all_vr3();
+
+	for(i = 0; i < VX900_MAX_DIMM_SLOTS; i++)
+	{
+		/* Do we have a valid DIMM? */
+		if(ranks->phys_rank_size[i] + ranks->phys_rank_size[i+1] == 0 )
+			continue;
+
+		/* Map the first rank of the DIMM to VR0 */
+		vx900_map_pr_vr(2*i, 0);
+
+		/* Run calibrations */if(1){
+		vx900_rx_capture_range_calib();
+		vx900_read_delay_range(&(delay_cal.rx_dq_cr),
+				       VX900_CALIB_RxDQ_CR);
+		dump_delay_range(delay_cal.rx_dq_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("Bypassing RxCR 78-7f calibration with:\n");
+		dump_delay(dly);
+		/* We need to put the setting on manual mode */
+		pci_mod_config8(MCU, 0x71, 0, 0x10);
+		vx900_delay_calib_mode_select(VX900_CALIB_RxDQ_CR, VX900_CALIB_MANUAL);
+		vx900_write_0x78_0x7f(dly);
+
+		/************* RxDQS *************/
+		vx900_rx_dqs_delay_calib();
+		vx900_read_delay_range(&(delay_cal.rx_dqs), VX900_CALIB_RxDQS);
+		printram("RX DQS calibration results\n");
+		dump_delay_range(delay_cal.rx_dqs);
+
+		vx900_rxdqs_adjust(&(delay_cal.rx_dqs));
+
+		vx900_read_delay_range(&(delay_cal.rx_dqs), VX900_CALIB_RxDQS);
+		printram("RX DQS calibration results after adjustment\n");
+		dump_delay_range(delay_cal.rx_dqs);
+
+		/* FIXME: Vendor BIOS does it again (enable auto-precharge) - WHY? */
+		pci_write_config8(MCU, 0x69, 0xce);
+
+		/* FIXME: this is done by vendor BIOS, and recommended by VIA
+		 * However, datasheet says that bit[7] is reserved, and
+		 * calibration works just as well if we don't set this to 1b .
+		 * Should we really do this, or can we drop it ? */
+		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_TxDQ, VX900_CALIB_MANUAL);
+		vx900_write_0x78_0x7f(dly);
+		vx900_delay_calib_mode_select(VX900_CALIB_TxDQS, VX900_CALIB_MANUAL);
+		vx900_write_0x78_0x7f(dly);
+		}
+
+		/************* TxDQS *************/
+		vx900_tx_dqs_delay_calib();
+
+		vx900_read_delay_range(&(delay_cal.tx_dqs), VX900_CALIB_TxDQS);
+		printram("Tx DQS calibration results\n");
+		dump_delay_range(delay_cal.tx_dqs);
+		/************* 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_dq), VX900_CALIB_TxDQ);
+		printram("TX DQ delay calibration results:\n");
+		dump_delay_range(delay_cal.tx_dq);
+
+		/* write manual settings */
+		pci_mod_config8(MCU, 0x75, 0, 0x01);
+		vx900_delay_calib_mode_select(VX900_CALIB_TxDQS, VX900_CALIB_MANUAL);
+		vx900_write_0x78_0x7f(delay_cal.tx_dqs.avg);
+		vx900_delay_calib_mode_select(VX900_CALIB_TxDQ, VX900_CALIB_MANUAL);
+		vx900_write_0x78_0x7f(delay_cal.tx_dq.avg);
+	}
+}
+
+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_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;
+
+		/* 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 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); /* self-refresh */
+
+	//pci_write_config8(MCU, 0xd3, 0x00); /* ODT auto-compensation */
+
+	/* Enable automatic triggering of short ZQ calibration */
+	pci_write_config8(MCU, 0xc8, 0x80);
+
+}
+
+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");
+	}
+
+	//device_t HBC = PCI_DEV(0,0,2);
+	//pci_mod_config8(HBC, 0x51, 0x80, 0);
+	//pci_mod_config8(HBC, 0x54, 0, 0x20);
+
+	//pci_mod_config8(MCU, 0x97, 0, 0x80);
+
+	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) Set refresh counter based on DRAM frequency */
+	vx900_dram_set_refresh_counter(&ctrl_prop);
+	/* 9) Calibrate receive and transmit delays */
+	vx900_dram_calibrate_delays(&ctrl_prop, &ranks);
+	/* 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/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