[coreboot] New patch to review: c28dcca NOTFORMERGE: vx900 (not-yet-functional) raminit

Alexandru Gagniuc (mr.nuke.me@gmail.com) gerrit at coreboot.org
Thu Aug 25 06:14:55 CEST 2011


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/171

-gerrit

commit c28dcca158728bcb540f2f0acf62a78bb1e2925a
Author: Alexandru Gagniuc <mr.nuke.me at gmail.com>
Date:   Wed Aug 24 23:11:44 2011 -0500

    NOTFORMERGE: vx900 (not-yet-functional) raminit
    
    Change-Id: I38193572bf0416fd642002dba94c19257f0f6f5b
    Signed-off-by: Alexandru Gagniuc <mr.nuke.me at gmail.com>
---
 src/devices/dram/dram.h                        |  149 +++++
 src/devices/dram/dram_util.c                   |  297 +++++++++
 src/devices/smbus/early_smbus.c                |  141 +++++
 src/devices/smbus/smbus.h                      |   99 +++
 src/mainboard/via/epia-m850/.directory         |    4 +
 src/mainboard/via/epia-m850/Kconfig            |   47 ++
 src/mainboard/via/epia-m850/Makefile.inc       |   21 +
 src/mainboard/via/epia-m850/chip.h             |   21 +
 src/mainboard/via/epia-m850/devicetree.cb      |   62 ++
 src/mainboard/via/epia-m850/mainboard.c        |   25 +
 src/mainboard/via/epia-m850/romstage.c         |   77 +++
 src/northbridge/amd/amdfam10/.directory        |    7 +
 src/northbridge/amd/amdmct/mct_ddr3/.directory |    7 +
 src/northbridge/via/vx900/Kconfig              |   23 +
 src/northbridge/via/vx900/Makefile.inc         |   33 +
 src/northbridge/via/vx900/early_smbus.c        |  191 ++++++
 src/northbridge/via/vx900/early_vx900.h        |   35 +
 src/northbridge/via/vx900/forgotten.c          |  180 ++++++
 src/northbridge/via/vx900/forgotten.h          |   78 +++
 src/northbridge/via/vx900/raminit.h            |   56 ++
 src/northbridge/via/vx900/raminit_ddr3.c       |  804 ++++++++++++++++++++++++
 src/northbridge/via/vx900/romstrap.inc         |   50 ++
 src/northbridge/via/vx900/romstrap.lds         |   27 +
 src/northbridge/via/vx900/vx900.h              |   26 +
 24 files changed, 2460 insertions(+), 0 deletions(-)

diff --git a/src/devices/dram/dram.h b/src/devices/dram/dram.h
new file mode 100644
index 0000000..7543d94
--- /dev/null
+++ b/src/devices/dram/dram.h
@@ -0,0 +1,149 @@
+/*
+ * 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;
+	/* 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;
+	/* 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;
+	
+} 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..ca652ce
--- /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 = (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;
+	print_debug("  CAS latencies     :");
+	i = 0;
+	do{
+		if(val16 & 1) printram(" %u", i + 4);
+		i++;
+		val16 >>= 1;
+	} while(val16);
+	print_debug("\n");
+
+	print_ns("  tCKmin            : ", dimm->tCK);
+	print_ns("  tAAmin            : ", dimm->tAA);
+	print_ns("  tWRmin            : ", dimm->tWR);
+	print_ns("  tRCDmin           : ", dimm->tRCD);
+	print_ns("  tRRDmin           : ", dimm->tRRD);
+	print_ns("  tRPmin            : ", dimm->tRP);
+	print_ns("  tRASmin           : ", dimm->tRAS);
+	print_ns("  tRCmin            : ", dimm->tRC);
+	print_ns("  tRFCmin           : ", dimm->tRFC);
+	print_ns("  tWTRmin           : ", dimm->tWTR);
+	print_ns("  tRTPmin           : ", dimm->tRTP);
+	print_ns("  tFAWmin           : ", dimm->tFAW);
+
+}
diff --git a/src/devices/smbus/early_smbus.c b/src/devices/smbus/early_smbus.c
new file mode 100644
index 0000000..4583aca
--- /dev/null
+++ b/src/devices/smbus/early_smbus.c
@@ -0,0 +1,141 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011  Alexandru Gagniuc <mr.nuke.me at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/**
+ * @file post_codes.h
+ *
+ * This file defines the implementations for the functions defined in smbus.h
+ * These are a generic SMBUS implementation, which should work with a majority
+ * of chipsets.
+ * They are marked weak so that they can be overridden by the chipset code if
+ * necessary.
+ */
+
+#include "smbus.h"
+
+/**
+ * \brief Brief delay for SMBUS transactions
+ */
+__attribute__((weak))
+void __smbus_delay(void)
+{
+	inb(0x80);
+}
+
+/**
+ * \brief Clear the SMBUS host status register
+ */
+__attribute__((weak))
+void __smbus_reset(u16 __smbus_io_base)
+{
+	outb(0xdf, SMBHSTSTAT);
+}
+
+/**
+ * \brief Print an error, should it occur. If no error, just exit.
+ *
+ * @param host_status The data returned on the host status register after
+ *		      a transaction is processed.
+ * @param loops The number of times a transaction was attempted.
+ * @return	0 if no error occurred
+ * 		1 if an error was detected
+ */
+__attribute__((weak))
+int __smbus_print_error(u8 host_status, int loops, u16 __smbus_io_base)
+{
+	/* Check if there actually was an error. */
+	if ((host_status == 0x00 || host_status == 0x40 ||
+		host_status == 0x42) && (loops < SMBUS_TIMEOUT))
+		return 0;
+	
+	if (loops >= SMBUS_TIMEOUT)
+		printsmbus("SMBus timeout\n");
+	if (host_status & (1 << 4))
+		printsmbus("Interrupt/SMI# was Failed Bus Transaction\n");
+	if (host_status & (1 << 3))
+		printsmbus("Bus error\n");
+	if (host_status & (1 << 2))
+		printsmbus("Device error\n");
+	if (host_status & (1 << 1))
+		printsmbus("Interrupt/SMI# completed successfully\n");
+	if (host_status & (1 << 0))
+		printsmbus("Host busy\n");
+	return 1;
+}
+
+/**
+ * \brief Checks if the SMBUS is currently busy with a transaction
+ */
+__attribute__((weak))
+int __smbus_is_busy(u16 __smbus_io_base)
+{
+	/* Check if bit 0 of the status register is 1 (busy) or 0 (ready) */
+	return ( (inb(SMBHSTSTAT) & (1 << 0)) == 1);
+}
+
+/**
+ * \brief Wait for the SMBUS to become ready to process a new transaction.
+ */
+__attribute__((weak))
+int __smbus_wait_until_ready(u16 __smbus_io_base)
+{
+	int loops;
+	
+	printsmbus("Waiting until SMBus ready\n");
+	
+	/* Loop up to SMBUS_TIMEOUT times, waiting for bit 0 of the
+	 * SMBus Host Status register to go to 0, indicating the operation
+	 * was completed successfully. I don't remember why I did it this way,
+	 * but I think it was because ROMCC was running low on registers */
+	loops = 0;
+	while (__smbus_is_busy(__smbus_io_base) && loops < SMBUS_TIMEOUT)
+		++loops;
+	
+	return __smbus_print_error(inb(SMBHSTSTAT), loops, __smbus_io_base);
+}
+
+/**
+ * \brief Read a byte from the SMBUS.
+ *
+ * @param dimm The address location of the DIMM on the SMBus.
+ * @param offset The offset the data is located at.
+ */
+__attribute__((weak))
+u8 __smbus_read_byte(u8 dimm, u8 offset, u16 __smbus_io_base)
+{
+	u8 val;
+	
+	/* Initialize SMBUS sequence */
+	__smbus_reset(__smbus_io_base);
+	/* Clear host data port. */
+	outb(0x00, SMBHSTDAT0);
+	
+	__smbus_wait_until_ready(__smbus_io_base);
+	
+	/* Actual addr to reg format. */
+	dimm = (dimm << 1);
+	dimm |= 1; /* read command */
+	outb(dimm, SMBXMITADD);
+	outb(offset, SMBHSTCMD);
+	/* Start transaction, byte data read. */
+	outb(0x48, SMBHSTCTL);
+	__smbus_wait_until_ready(__smbus_io_base);
+	
+	val = inb(SMBHSTDAT0);
+	return val;
+}
\ No newline at end of file
diff --git a/src/devices/smbus/smbus.h b/src/devices/smbus/smbus.h
new file mode 100644
index 0000000..820a0dd
--- /dev/null
+++ b/src/devices/smbus/smbus.h
@@ -0,0 +1,99 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011  Alexandru Gagniuc <mr.nuke.me at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @file post_codes.h
+ *
+ * This file defines the prototypes for several common SMBUS functions
+ * These functions are prefixed with __smbus_ so that they do not conflict with
+ * the dozens of similar (duplicated) implementations in many southbridges.
+ *
+ * As a last parameter, the SMBUS functions take a u16 value __smbus_io_base,
+ * which represents the base IO port for smbus transactions
+ */
+
+#include <arch/io.h>
+
+/**
+ * \brief SMBUS IO ports in relation to the base IO port
+ */
+#define SMBHSTSTAT		__smbus_io_base + 0x0
+#define SMBSLVSTAT		__smbus_io_base + 0x1
+#define SMBHSTCTL		__smbus_io_base + 0x2
+#define SMBHSTCMD		__smbus_io_base + 0x3
+#define SMBXMITADD		__smbus_io_base + 0x4
+#define SMBHSTDAT0		__smbus_io_base + 0x5
+#define SMBHSTDAT1		__smbus_io_base + 0x6
+#define SMBBLKDAT		__smbus_io_base + 0x7
+#define SMBSLVCTL		__smbus_io_base + 0x8
+#define SMBTRNSADD		__smbus_io_base + 0x9
+#define SMBSLVDATA 		__smbus_io_base + 0xa
+
+#define SMBUS_TIMEOUT		(100*1000*10)
+
+/**
+ * \brief printk macro for SMBUS debugging
+ */
+#if defined(CONFIG_DEBUG_SMBUS_SETUP) && (CONFIG_DEBUG_SMBUS_SETUP)
+#define printsmbus(x, ...) printk(BIOS_DEBUG, x, ##__VA_ARGS__)
+#else
+#define printsmbus(x, ...)
+#endif
+
+void __smbus_reset(u16 __smbus_io_base);
+int __smbus_print_error(u8 host_status, int loops, u16 __smbus_io_base);
+int __smbus_is_busy(u16 __smbus_io_base);
+int __smbus_wait_until_ready(u16 __smbus_io_base);
+u8 __smbus_read_byte(u8 dimm, u8 offset, u16 __smbus_io_base);
+
+void __smbus_delay(void);
+
+#if defined(SMBUS_IO_BASE) && (SMBUS_IO_BASE != 0)
+
+__attribute__((always_inline, unused))
+static void smbus_reset(void)
+{
+	__smbus_reset(SMBUS_IO_BASE);
+}
+
+__attribute__((always_inline, unused))
+static int smbus_is_busy(void)
+{
+	return __smbus_is_busy(SMBUS_IO_BASE);
+}
+
+__attribute__((always_inline, unused))
+static int smbus_wait_until_ready(void)
+{
+	return __smbus_wait_until_ready(SMBUS_IO_BASE);
+}
+
+__attribute__((always_inline, unused))
+static int smbus_print_error(u8 host_status, int loops)
+{
+	return __smbus_print_error(host_status, loops, SMBUS_IO_BASE);
+}
+
+__attribute__((always_inline, unused))
+static u8 smbus_read_byte(u8 dimm, u8 offset)
+{
+	return __smbus_read_byte(dimm, offset, SMBUS_IO_BASE);
+}
+
+#endif
\ No newline at end of file
diff --git a/src/mainboard/via/epia-m850/.directory b/src/mainboard/via/epia-m850/.directory
new file mode 100644
index 0000000..8a30135
--- /dev/null
+++ b/src/mainboard/via/epia-m850/.directory
@@ -0,0 +1,4 @@
+[Dolphin]
+ShowPreview=true
+Timestamp=2011,8,1,4,33,6
+Version=2
diff --git a/src/mainboard/via/epia-m850/Kconfig b/src/mainboard/via/epia-m850/Kconfig
new file mode 100644
index 0000000..ae46646
--- /dev/null
+++ b/src/mainboard/via/epia-m850/Kconfig
@@ -0,0 +1,47 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2011  Alexandru Gagniuc <mr.nuke.me at gmail.com>
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program.  If not, see <http://www.gnu.org/licenses/>.
+##
+
+if BOARD_VIA_EPIA_M850
+
+config BOARD_SPECIFIC_OPTIONS # dummy
+	def_bool y
+	select ARCH_X86
+	select CPU_VIA_C7
+	select NORTHBRIDGE_VIA_VX900
+	select SUPERIO_FINTEK_F81865F
+	#select BOARD_HAS_FADT
+	#select HAVE_PIRQ_TABLE
+	#select HAVE_ACPI_TABLES
+	#select HAVE_OPTION_TABLE
+	select BOARD_ROMSIZE_KB_512
+	#select RAMINIT_SYSINFO
+
+config MAINBOARD_DIR
+	string
+	default via/epia-m850
+
+config MAINBOARD_PART_NUMBER
+	string
+	default "EPIA-M850"
+
+config IRQ_SLOT_COUNT
+	int
+	default 13
+
+endif # BOARD_VIA_EPIA_M850
diff --git a/src/mainboard/via/epia-m850/Makefile.inc b/src/mainboard/via/epia-m850/Makefile.inc
new file mode 100644
index 0000000..9dffc79
--- /dev/null
+++ b/src/mainboard/via/epia-m850/Makefile.inc
@@ -0,0 +1,21 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2011  Alexandru Gagniuc <mr.nuke.me at gmail.com>
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+## 
+## You should have received a copy of the GNU General Public License
+## along with this program.  If not, see <http://www.gnu.org/licenses/>.
+##
+
+#romstage-y += ./../../../superio/fintek/f81865f/f81865f_early_serial.c
+
diff --git a/src/mainboard/via/epia-m850/chip.h b/src/mainboard/via/epia-m850/chip.h
new file mode 100644
index 0000000..2ac4f12
--- /dev/null
+++ b/src/mainboard/via/epia-m850/chip.h
@@ -0,0 +1,21 @@
+/*
+ * This file is part of the coreboot project.
+ * 
+ * Copyright (C) 2011  Alexandru Gagniuc <mr.nuke.me at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+extern struct chip_operations mainboard_ops;
+
+struct mainboard_config {};
diff --git a/src/mainboard/via/epia-m850/devicetree.cb b/src/mainboard/via/epia-m850/devicetree.cb
new file mode 100644
index 0000000..34f2848
--- /dev/null
+++ b/src/mainboard/via/epia-m850/devicetree.cb
@@ -0,0 +1,62 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2011  Alexandru Gagniuc <mr.nuke.me at gmail.com>
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+## 
+## You should have received a copy of the GNU General Public License
+## along with this program.  If not, see <http://www.gnu.org/licenses/>.
+##
+
+chip northbridge/via/vx900		# Northbridge
+    device lapic_cluster 0 on		# APIC cluster
+        chip cpu/via/model_c7		# CPU ATOM (same as VIA C7)
+          device lapic 0 on end	# APIC
+        end
+    end
+    device pci_domain 0 on
+        device pci 0.0 on end		# AGP Bridge
+        device pci 0.1 on end		# Error Reporting
+        device pci 0.2 on end		# Host Bus Control
+        device pci 0.3 on end		# Memory Controller
+        device pci 0.4 on end		# Power Management
+        device pci 0.5 on end		# Power Management
+        device pci 0.6 on end		# Power Management
+        device pci 0.7 on end		# V-Link Controller
+        device pci 1.0 on end		# PCI Bridge
+        device pci 1.1 on end		# Audio
+        device pci 3.0 on end		# PCIE control
+        device pci 3.1 on end		# PEX1
+        device pci 3.2 on end		# PEX2
+        device pci 3.3 on end		# PEX3
+        device pci 3.4 on end		# PEX4
+        device pci f.0 on end		# IDE/SATA
+        device pci 10.0 on end		# USB 1.1
+        device pci 10.1 on end		# USB 1.1
+        device pci 10.2 on end		# USB 1.1
+        device pci 10.3 on end		# USB 1.1
+        device pci 10.4 on end		# USB 2.0
+        device pci 11.0 on end		# LPC
+            chip drivers/generic/generic	# DIMM 0-0-0
+              device i2c 50 on end
+            end
+            chip drivers/generic/generic	# DIMM 0-0-1
+              device i2c 51 on end
+            end
+          chip superio/fintek/f81865f	# Super duper IO
+          end
+        device pci 11.7 on end		# NB/SB control
+        device pci 13.0 on end		# PCI Bridge
+        device pci 14.0 on end		# HD Audio
+    end
+    
+end
diff --git a/src/mainboard/via/epia-m850/mainboard.c b/src/mainboard/via/epia-m850/mainboard.c
new file mode 100644
index 0000000..572f7da
--- /dev/null
+++ b/src/mainboard/via/epia-m850/mainboard.c
@@ -0,0 +1,25 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011  Alexandru Gagniuc <mr.nuke.me at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <device/device.h>
+#include "chip.h"
+
+struct chip_operations mainboard_ops = {
+	CHIP_NAME("VIA EPIA-M850 Mainboard")
+};
diff --git a/src/mainboard/via/epia-m850/romstage.c b/src/mainboard/via/epia-m850/romstage.c
new file mode 100644
index 0000000..f458554
--- /dev/null
+++ b/src/mainboard/via/epia-m850/romstage.c
@@ -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/>.
+ */
+
+/*
+ * Inspired from the EPIA-M700
+ */
+
+#include <stdint.h>
+#include <device/pci_def.h>
+#include <device/pci_ids.h>
+#include <arch/io.h>
+#include <device/pnp_def.h>
+#include <arch/romcc_io.h>
+#include <arch/hlt.h>
+#include <console/console.h>
+#include <lib.h>
+#include "cpu/x86/bist.h"
+#include <string.h>
+
+#include "northbridge/via/vx900/early_vx900.h"
+#include "northbridge/via/vx900/raminit.h"
+#include "superio/fintek/f81865f/f81865f_early_serial.c"
+
+#define SERIAL_DEV PNP_DEV(0x2e, 0)
+
+/* cache_as_ram.inc jumps to here. */
+void main(unsigned long bist)
+{
+	/* Enable multifunction bit for northbridge.
+	 * This enables the PCI configuration spaces of D0F1 to D0F7 to be
+	 * accessed */
+	pci_write_config8(PCI_DEV(0, 0, 0), 0x4f, 0x01);
+
+	f81865f_enable_serial(SERIAL_DEV, CONFIG_TTYS0_BASE);
+	console_init();
+	print_debug("Console initialized. \n");
+
+	/* Halt if there was a built-in self test failure. */
+	report_bist_failure(bist);
+
+	/* x86 cold boot I/O cmd. */
+	enable_smbus();
+
+	/* If this works, then SMBUS is up and running */
+	//dump_spd_data();
+
+	/* Now we can worry about raminit.
+	 * This board only has DDR3, so no need to worry about which DRAM type
+	 * to use */
+	dimm_layout dimms = {{0x50, 0x51, SPD_END_LIST}};
+	vx900_init_dram_ddr3(&dimms);
+	//ram_check(0, 640 * 1024);
+	//ram_check(1<<26, (1<<26) + 0x20);
+	ram_check(0x04321000, 0x04321000 + 0x80);
+	ram_check(0, 0x80);
+
+	print_debug("We passed RAM verify\n");
+
+	return;
+
+}
diff --git a/src/northbridge/amd/amdfam10/.directory b/src/northbridge/amd/amdfam10/.directory
new file mode 100644
index 0000000..24fd569
--- /dev/null
+++ b/src/northbridge/amd/amdfam10/.directory
@@ -0,0 +1,7 @@
+[Dolphin]
+ShowPreview=true
+SortOrder=1
+Sorting=1
+Timestamp=2011,8,2,17,56,9
+Version=2
+ViewMode=1
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/.directory b/src/northbridge/amd/amdmct/mct_ddr3/.directory
new file mode 100644
index 0000000..601cad6
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/.directory
@@ -0,0 +1,7 @@
+[Dolphin]
+ShowPreview=true
+SortOrder=1
+Sorting=1
+Timestamp=2011,8,2,17,57,51
+Version=2
+ViewMode=1
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..5450b1d
--- /dev/null
+++ b/src/northbridge/via/vx900/Makefile.inc
@@ -0,0 +1,33 @@
+##
+## 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 += ./../../../devices/dram/dram_util.c
+romstage-y += ./../../../devices/smbus/early_smbus.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
\ No newline at end of file
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..c3d3ed8
--- /dev/null
+++ b/src/northbridge/via/vx900/forgotten.c
@@ -0,0 +1,180 @@
+#include "forgotten.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) | ((rtt_nom & 2) << 6)
+	| ((rtt_nom & 1) << 2));
+	if(write_leveling) cmd |= (1 << 7);
+	/* output drive strenght */
+	cmd |= ( ((output_drive_strenght & 2) << 5)
+	| ((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
+ */
+static 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, int rank);
+void vx900_dram_ddr3_init_rank(device_t mcu, int rank)
+{
+	u8 reg8;
+	u16 reg16;
+	u32 reg32, cmd, res, addr;
+	
+	printram("Initializing rank %u\n", rank);
+	
+	reg16 = 0xbbbb;
+	reg16 &= ~(0xf << (4 * rank));
+	reg16 |= (0x8 << (4 * rank));
+	pci_write_config16(mcu, 0x54, reg16);
+	
+	/* Step 06 - Set Fun3_RX6B[2:0] to 001b (NOP Command Enable). */
+	reg8 = pci_read_config8(mcu, 0x6b);
+	reg8 &= ~(0x03);
+	reg8 |= (1<<0);
+	pci_write_config8(mcu, 0x6b, reg8);
+	/* Step 07 - Read a double word from any address of the DIMM. */
+	reg32 = volatile_read(0x0);
+	printram("We just read   0x%x\n", reg32);
+	/* Step 08 - Set Fun3_RX6B[2:0] to 011b (MSR Enable). */
+	reg8 = pci_read_config8(mcu, 0x6b);
+	reg8 &= ~(0x03);
+	reg8 |= (3<<0);
+	pci_write_config8(mcu, 0x6b, reg8);
+	
+	/* Step 09 – Issue MR2 cycle. Read a double word from the address depended
+	 * on DRAM’s Rtt_WR and CWL settings.
+	 * (Check the byte 63 of SPD. If memory address lines of target physical
+	 * rank is mirrored, MA3~MA8 and BA0~BA1 should be swapped.) */
+	cmd = ddr3_get_mr2(2,0,0,7);
+	addr = vx900_get_mrs_addr(2, cmd);
+	res = volatile_read(addr);
+	printram("MR2 cycle 0x%.4x @ addr 0x%.8x read 0x%.8x\n", cmd, addr, res);
+	
+	/* Step 10 – Issue MR3 cycle. Read a double word from the address 60000h to
+	 * set DRAM to normal operation mode.
+	 * (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_mr3(0);
+	addr = vx900_get_mrs_addr(3, cmd);
+	res = volatile_read(addr);
+	printram("MR3 cycle 0x%.4x @ addr 0x%.8x read 0x%.8x\n", cmd, addr, res);
+	
+	/* Step 11 –Issue MR1 cycle. Read a double word from the address depended
+	 * on 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.
+	 * (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_mr1(DDR3_MR1_QOFF_ENABLE, DDR3_MR1_TQDS_DISABLE, 3,
+					   DDR3_MR1_WRITE_LEVELING_DISABLE, 1,
+					DDR3_MR1_AL_DISABLE, DDR3_MR1_DLL_ENABLE);
+	addr = vx900_get_mrs_addr(1, cmd);
+	res = volatile_read(addr);
+	printram("MR1 cycle 0x%.4x @ addr 0x%.8x read 0x%.8x\n", cmd, addr, res);
+	
+	/* Step 12 - Issue MR0 cycle. Read a double word from the address depended
+	 * on DRAM’s burst length, CAS latency and write recovery time 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.
+	 * (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_mr0(DDR3_MR0_PRECHARGE_FAST, 7, DDR3_MR0_MODE_NORMAL,
+					   DDR3_MR0_DLL_RESET_NO, 7,
+					DDR3_MR0_BURST_TYPE_INTERLEAVED,
+					1);
+	addr = vx900_get_mrs_addr(0, cmd);
+	res = volatile_read(addr);
+	printram("MR0 cycle 0x%.4x @ addr 0x%.8x read 0x%.8x\n", cmd, addr, res);
+	
+	/* Step 13 - Set Fun3_RX6B[2:0] to 110b (Long ZQ calibration command). */
+	reg8 = pci_read_config8(mcu, 0x6b);
+	reg8 &= ~(0x03);
+	reg8 |= (3<<1);
+	pci_write_config8(mcu, 0x6b, reg8);
+	
+	/* Step 14 - Read a double word from any address of the DIMM. */
+	reg32 = volatile_read(0x0);
+	printram("We just read   0x%x\n", reg32);
+}
\ No newline at end of file
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..f90cb60
--- /dev/null
+++ b/src/northbridge/via/vx900/raminit.h
@@ -0,0 +1,56 @@
+/*
+ * 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;
+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..2b1d9eb
--- /dev/null
+++ b/src/northbridge/via/vx900/raminit_ddr3.c
@@ -0,0 +1,804 @@
+/*
+ * 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 <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>
+
+#define min(a,b) a<b?a:b
+#define max(a,b) a>b?a:b
+
+#define MCU PCI_DEV(0, 0, 3)
+
+typedef struct pci_reg8_st {
+	u8 addr;
+	u8 val;
+} pci_reg8;
+
+/* These are some "safe" values that can be used for memory initialization.
+ * Some will stay untouched, and others will be overwritten later on */
+static pci_reg8 mcu_init_config[] = {
+	{0x40, 0x01}, /* Virtual rank 0 ending address = 64M - 1 */
+	{0x48, 0x00}, /* Virtual rank 0 starting address = 0 */
+	{0x50, 0xd8}, /* Set ranks 0-3 to 11 col bits, 16 row bits */
+	{0x52, 0x33}, /* Map BA0 to A15, BA1 to A18 */
+	{0x53, 0x0b}, /* Map BA2 to A19 */
+	/* Disable rank interkeaving in ranks 0-3 */
+	{0x58, 0x00}, {0x59, 0x00}, {0x5a, 0x00}, {0x5b, 0x00},
+	{0x6c, 0xA0}, /* Memory type: DDR3, VDIMM: 1.5V, 64-bit DRAM */
+	{0x6e, 0x38}, /* Burst lenght: 8, burst-chop: enable */
+	{0xc6, 0x80}, /* Minimum latency from self-refresh. Bit [7] must be 1 */
+	{0xc8, 0x80}, /* Enable automatic triggering of short ZQ calibration */
+	/* Enable differential DQS; MODT assertion values suggested in DS */
+	{0x9e, 0xa1}, {0x9f, 0x50}
+};
+
+/* FIXME: DO NOT ACCEPT THIS PATCH IF YOU SEE THIS ARRAY
+ * This is just a cheat sheet to get the config up and running, and is specific
+ * to my hardware setup. */
+static pci_reg8 mcu_cheat_sheet[] = {
+	/* Number of row, col and bank bits */
+	{0x50, 0xa0},
+	/* Rank interleave address select */
+	{0x52, 0x11}, {0x53, 0x59},
+	/* DRAM Pipeline control */
+	{0x60, 0xf4}, {0x61, 0x2e},
+	/* DRAM arbitration */
+	              {0x65, 0x49}, {0x66, 0x80},
+	/* Bank interleave control; request reorder control */
+	              {0x69, 0xe7}, {0x6a, 0xfc},
+
+	/* very finetuned timing; do not touch; leave commented out */
+	//                            {0x72, 0x0f}, {0x73, 0x04},
+	//                            {0x76, 0x68}, {0x77, 0x10},
+
+	/* Top mem; need to calculate this dynamically */
+	{0x84, 0x01}, {0x85, 0x40},
+
+	/* DRAM clocking control; mostly finetuned delays ; leave out */
+	              /*{0x91, 0x08}, {0x92, 0x08}, {0x93, 0x16},
+	{0x94, 0x00}, {0x95, 0x16}, {0x96, 0x00}, {0x97, 0xa4},
+	{0x98, 0xba}, {0x99, 0xff}, {0x9a, 0x80},
+	{0x9c, 0xe4},
+	*/
+
+};
+/* FIXME: Cheat sheet number two
+ * We need to set driving strength before trying to operate the controller
+ * It's like putting it in first gear before pushing the accelerator */
+static pci_reg8 memdrv_cheat_sheet[] = {
+	/* Enable memory clock output */
+	//{0x9b, 0x3f}, // on by default
+
+	{0xd3, 0x01}, /* Enable controller ODT auto-compensation */
+
+	/* Memory driving controls */
+	/* ?? Internal ODT control ?? */
+	//{0xd4, 0x80},
+	/* ?? ODT Range selection ?? */
+	              {0xd5, 0x04},
+	/* ?? MCLK/MA/MCS driving selection ?? */
+	                            //{0xd6, 0x20},
+	/* Auto compensation mode for _everything_ */
+	{0xe1, 0x7e},
+
+};
+
+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 = 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 &= dimm->cas;
+
+		/* 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) 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;;
+		}
+	}
+	/* There's a tiny thing we need to do:
+	 * set the column and row address bits
+	 * FIXME: Really bad programming here */
+	reg8 = 0;// ((( (1<<2) | (dimms->dimm[0].col_bits - 9) ) & 0x7) << 2);
+	reg8 |= ((( (1<<2) | (dimms->dimm[1].col_bits - 9) ) & 0x7) << 5);
+	pci_write_config8(PCI_DEV(0,0,3), 0x50, reg8);
+	
+}
+
+static void vx900_dram_driving_ctrl(void)
+{
+	/* My memory drive strength cheat sheet */
+	size_t i;
+	for(i = 0; i < (sizeof(memdrv_cheat_sheet)/sizeof(pci_reg8)); i++)
+		pci_write_config8(MCU, memdrv_cheat_sheet[i].addr,
+				  memdrv_cheat_sheet[i].val);
+}
+
+static void vx900_dram_range(ramctr_timing *ctrl, rank_layout *ranks)
+{
+	size_t i, vrank = 0;
+	u8 reg8;
+	u32 reg32 = 0, ramsize = 0;
+	for(i = 0; i < VX900_MAX_MEM_RANKS; i++)
+	{
+		u32 rank_size = ranks->phys_rank_size[i];
+		if(!rank_size) continue;
+		ranks->virt[vrank].start_addr = ramsize;
+		ramsize += rank_size;
+		ranks->virt[vrank].end_addr = ramsize;
+
+		/* Physical to virtual rank mapping */
+		/* The position of the bit that enables physical rank [i] */
+		const char rank_en_bit[] = {3, 7, 11, 15};
+		/* The position of the LSB of the VR mapping of rank [i] */
+		const char rank_map_pos[] = {0, 4, 8, 12};
+		/* Enable the physical rank */
+		reg32 |= ( 1 << rank_en_bit[i]);
+		/* Map the physical rank to the first unused virtual rank */
+		reg32 |= (vrank << rank_map_pos[i]);
+		/* 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);
+
+		printram("Mapped Physical rank %u, to virtual rank %u\n"
+				 "    Start address: 0x%.10x000000\n"
+				 "    End   address: 0x%.10x000000\n",
+				(int) i, (int) vrank,
+				ranks->virt[vrank].start_addr,
+				ranks->virt[vrank].end_addr);
+
+		/* Move on to next virtual rank */
+		vrank++;
+	}
+
+	/* Finally, write the mapping configuration to the MCU */
+	pci_write_config32(MCU, 0x54, reg32);
+
+	printram("Initialized %u virtual ranks, with a total size of %u MB\n",
+			 (int) vrank, ramsize << 4);
+}
+
+static void vx900_dram_freq_latency(ramctr_timing *ctrl)
+{
+	u8 reg8, val;
+	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);
+
+	/* To change the DRAM frequency, it is required to set this bits to
+	 * 1111b to reset the PLL first and set to the target value then.
+	 * Minimum assertion time for the PLL Reset is 10 ns.
+	 * Datasheet also states that the DRAM frequency must be set first */
+	/* Reset the PLL */
+	pci_write_config8(MCU, 0x90, 0x0f);
+	/* Wait at least 10 ns; in this case, we will wait about 1 us */
+	inb(0x80);
+	/* Now find the right DRAM frequency setting,
+	 * and bring it to the closest JEDEC standard frequency */
+	if(ctrl->tCK <= TCK_533MHZ)      {val = 0x07; ctrl->tCK = TCK_533MHZ;}
+	else if(ctrl->tCK <= TCK_400MHZ) {val = 0x06; ctrl->tCK = TCK_400MHZ;}
+	else if(ctrl->tCK <= TCK_333MHZ) {val = 0x05; ctrl->tCK = TCK_333MHZ;}
+	else if(ctrl->tCK <= TCK_266MHZ) {val = 0x04; ctrl->tCK = TCK_266MHZ;}
+
+	reg8 = val & 0x0f;
+	/* Restart the PLL with the correct frequency */
+	pci_write_config8(MCU, 0x90, reg8);
+
+	/* 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");
+		reg8 = pci_read_config8(MCU, 0x6c);
+		reg8 |= 1;
+		pci_write_config8(MCU, 0x6c, reg8);
+	}
+
+	/* 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 */
+	
+	/* Find CAS latency */
+	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 >> (val-4))&1) && (ctrl->cas >> (val-4))) val++;
+	/* Is CAS supported */
+	if(!(ctrl->cas & (1 << (val-4))) ) printram("CAS not supported\n");
+	printram("Selected CAS latency   : %uT\n", val);
+	/* Write CAS latency */
+	val -= 4; /* 000 means 4T */
+	reg8 = ((val &0x07) << 4 ) | (val & 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 */
+	val = (ctrl->tWR + ctrl->tCK -1) / ctrl->tCK;
+	printram("Selected tWR           : %uT\n", val);
+	reg8 |= ((val-4) & 0x7);
+	pci_write_config8(MCU, 0xc2, reg8);
+
+	/* Find tFAW */
+	val = (ctrl->tFAW + ctrl->tCK -1) / ctrl->tCK;
+	printram("Selected tFAW          : %uT\n", val);
+	reg8 = ((val-0) & 0x7) << 4;
+	/* Find tRRD */
+	val = (ctrl->tRRD + ctrl->tCK -1) / ctrl->tCK;
+	printram("Selected tRRD          : %uT\n", val);
+	reg8 |= ((val-2) & 0x7);
+	pci_write_config8(MCU, 0xc3, reg8);
+
+	/* This register needs a little more attention, as bit 7 controls some
+	 * other stuff:
+	 * [7] 8-Bank Device Timing Constraint (See datasheet)
+	 *     Since all DDR3 modules are minimum 8-banks, there's no need to
+	 *     worry about this bit, and we can just set it;
+	 * [6] Reserved
+	 * [5:4] tRTP with 00 = 3T for DDR3
+	 * [3] Reserved
+	 * [2-0] tWTR with 000 = 2T
+	 */
+	reg8 = 0x80;
+	/* 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_write_config8(MCU, 0xc4, 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)
+	 *   RxC4[7] = 0:
+	 *     tRFC = (10 + [5:0])T
+	 *   RxC4[7] = 1:
+	 *     tRFC = (30 + 2 * [5:0])T
+	 *     Since we previously set RxC4[7], we're going to use this formula
+	 */	
+	reg8 = pci_read_config8(MCU, 0xc5);
+	val = (ctrl->tRFC + ctrl->tCK -1) / ctrl->tCK;
+	printram("Minimum  tRFC          : %uT\n", val);
+	if(ctrl->tRFC < 30) {
+		val = 0;
+	} else {
+		val = (val -30 + 1 ) / 2;
+	}
+	;
+	printram("Selected tRFC          : %uT\n", 30 + 2 * val);
+	reg8 |= (val & 0x1f);
+	pci_write_config8(MCU, 0xc5, reg8);
+
+	/* Where does this go??? */
+	val = (ctrl->tRC + ctrl->tCK -1) / ctrl->tCK;
+	printram("Required tRC           : %uT\n", val);
+}
+
+/* The VX900 can send the MRS commands directly through hardware */
+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;
+	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);
+	while(pci_read_config8(MCU, 0xcc) & 1);
+}
+#include "forgotten.h"
+#include "forgotten.c"
+static void vx900_dump_0x78_0x7f(void)
+{
+	u8 i;
+	for(i = 0x78; i < 0x80; i ++)
+	{
+		printram(" %.2x", pci_read_config8(MCU, i));
+	}
+	printram("\n");
+}
+
+static void vx900_dump_calib(const u8 what)
+{
+	u8 reg8;
+	/* Dump lower bound */
+	reg8 = ((what & 0x3) << 2) | 0x1;
+	pci_write_config8(MCU, 0x70, reg8);
+	printram("Lower bound : ");
+	vx900_dump_0x78_0x7f();
+	
+	/* Dump upper bound */
+	reg8 = ((what & 0x3) << 2) | 0x2;
+	pci_write_config8(MCU, 0x70, reg8);
+	printram("Upper bound : ");
+	vx900_dump_0x78_0x7f();
+	
+	/* Dump average values */
+	reg8 = ((what & 0x3) << 2);
+	pci_write_config8(MCU, 0x70, reg8);
+	printram("Average     : ");
+	vx900_dump_0x78_0x7f();
+}
+
+static void vx900_dram_send_soft_mrs(u8 type, u16 cmd)
+{
+	u8 reg8;
+	u32 addr;
+	/* Set Fun3_RX6B[2:0] to 011b (MSR Enable). */
+	reg8 = pci_read_config8(MCU, 0x6b);
+	reg8 &= ~(0x03);
+	reg8 |= (3<<0);
+	pci_write_config8(MCU, 0x6b, reg8);
+	/* Find the address corresponding to the MRS */
+	addr = vx900_get_mrs_addr(type, cmd);
+	/* Execute the MRS */
+	volatile_read(addr);
+	/* Set Fun3_Rx6B[2:0] to 000b (Normal SDRAM Mode). */
+	reg8 = pci_read_config8(MCU, 0x6b);
+	reg8 &= ~(7<<0);
+	pci_write_config8(MCU, 0x6b, reg8);
+}
+static void vx900_rx_capture_range_calib(u8 pattern);
+static void vx900_rx_dqs_delay_calib(u8 pattern);
+static void vx900_tx_dq_delay_calib(void);
+static void vx900_tx_dqs_delay_calib(const u8  rank);
+
+static void vx900_delay_calib_rank(const u8 rank)
+{
+	u8 reg8, val;
+	u16 reg16;
+	printram("Starting delay calibration for rank %u\n", rank);
+	
+	reg16 = 0xbbbb;
+	reg16 &= ~(0xf << (4 * rank));
+	reg16 |= (0x8 << (4 * rank));
+	pci_write_config16(MCU, 0x54, reg16);
+
+	/* MD Input Data Push Timing Control;
+	 *     use values recommended in datasheet */
+	reg8 = pci_read_config8(MCU, 0x74);
+	val = (TCK_533MHZ <= TCK_400MHZ)?2:1; /* FIXME*/
+	reg8 &= ~(0x03 << 1);
+	reg8 |= ((val & 0x03) << 1);
+	pci_write_config8(MCU, 0x74, reg8);
+	
+	/* Disable additional delays */
+	pci_write_config8(MCU, 0xec, 0x00);
+	pci_write_config8(MCU, 0xed, 0x00);
+	pci_write_config8(MCU, 0xee, 0x00);
+
+	pci_write_config8(MCU, 0x77, 0x10);/*FIXME*/
+	const u8 pattern = 0x40; /*FIXME*/
+	//const u8 pattern = 0x00; /*FIXME*/
+	/* Run calibrations */
+	vx900_rx_capture_range_calib(pattern);
+	vx900_rx_dqs_delay_calib(pattern);
+	vx900_tx_dq_delay_calib();
+	vx900_tx_dqs_delay_calib(rank);
+}
+
+static void vx900_rx_capture_range_calib(u8 pattern)
+{
+	u8 reg8;
+	u16 cmd;
+	/*
+	 * Calibration of RX Capture Range can be triggered by setting
+	 * Rx71[5] to 1.
+	 * The process started by sending a series of read commands on the DRAM
+	 * bus with different setting for the RX Capture Range.
+	 * 
+	 * After a MRS (Mode Register Set) command to put DDR3 in a mode called
+	 * “Read Leveling Mode”,
+	 */
+	/* Put DRAM in read leveling mode */
+	cmd = ddr3_get_mr3(1);
+	vx900_dram_send_soft_mrs(3, cmd);
+
+	/* DDR3 returned the read command with a predefined
+	 * data pattern either “00h-01h-00h-01h” or “00h-FFh-00h-FFh”. There is
+	 * no specific definition in the current DDR3 specification for this
+	 * pattern, so, the working software might need to do the calibration
+	 * twice. It could triggered the scanning with
+	 * Rx71[6] set to 1 first.
+	 * If nothing can be working right, the software trigger the scanning
+	 * again with
+	 * Rx71[6] set to 0.
+	 * The controller thus can check those pattern to verify if the data
+	 * from DDR3 DIMM is correct or not. The calibration will work from the
+	 * lowest limit of the setting for the RX Capture Range, it marked the
+	 * setting for its lower bound when checking is correct. It increased
+	 * the setting and issued read cycle again and again until checking of
+	 * the data is incorrect. It then marked the last working setting as
+	 * the upper bound.
+	 * Controller also calculated the average of the lower bound and upper
+	 * bound to have the center of the setting, which presumably the most
+	 * stable one. Besides this capture range setting, there is another set
+	 * of delay control for internal MD paths at RxEF[5:4].
+	 * However, that one is independent of the calibration mechanism here.*/
+	/* Data pattern must be 0x00 for this calibration */
+	pci_write_config8(MCU, 0x8e, 0x00);
+	/* Trigger calibration */
+	reg8 = 0xa0 | (pattern & 0x40);
+	pci_write_config8(MCU, 0x71, reg8);
+
+	printram("RX capture range calibration results:\n");
+	vx900_dump_calib(3);
+
+	/* Disable read leveling, and put dram in normal operation mode */
+	cmd = ddr3_get_mr3(0);
+	vx900_dram_send_soft_mrs(3, cmd);
+}
+
+static void vx900_rx_dqs_delay_calib(u8 pattern)
+{
+	u8 reg8;
+	/* The same process is done by setting
+	 * Rx71[1] to 1
+	 * to start the calibration for RX DQS Delay. Besides the manual
+	 * setting, there is also a setting which is calculated by using
+	 * internal DCLK DLL to have a 1/4T delay for DQS.
+	 * Noted that when this option is set
+	 * (Rx71[2] =1)
+	 * the DQS of all the bytes (MDQS[7:0]P/N) will use this self-calculated
+	 * 1/4 delay setting. */
+
+	/* Data pattern must be 0x00 for this calibration */
+	pci_write_config8(MCU, 0x8e, 0x00);
+	/* Trigger calibration */
+	reg8 = 0x82 | (pattern & 0x40);
+	pci_write_config8(MCU, 0x71, reg8);
+
+	printram("RX DQS delay calibration results:\n");
+	vx900_dump_calib(2);
+}
+static void vx900_tx_dq_delay_calib()
+{
+	/* The calibration for TX DQ Delay is started once
+	 * Rx75[1] is set to 1.
+	 * That process is to issue a write with
+	 * data defined by Rx8E[7:0]
+	 * and to issue a read command right after it to do the checking.
+	 * Before issuing the write command, the calibration controller will set
+	 * the setting of TX DQ Delay. It started with the smallest setting, and
+	 * it will mark the one as lower bound when the corresponding checking
+	 * is right. It continue with the write + read + checking processes and
+	 * see if it started to fail. The last working setting is the upper
+	 * bound. This process will not stop until all the upper bound are found
+	 * for all the bytes. */
+	/* Data pattern for calibration */
+	pci_write_config8(MCU, 0x8e, 0x5a);
+	/* Trigger calibration */
+	pci_write_config8(MCU, 0x75, 0x02);
+
+	printram("TX DQ delay calibration results:\n");
+	vx900_dump_calib(1);
+}
+static void vx900_tx_dqs_delay_calib(const u8  rank)
+{
+	u8 reg8;
+	u16 cmd;
+	/* Noted that there are two types of calibration for the TX DQS Delay.
+	 * Setting Rx75[5] to 1,
+	 * the calibration process will go as that of TX DQ Delay. Only that the
+	 * adjustment made is on the TX DQS output path not the TX DQ output
+	 * path. There is another way of calibration worked with DDR3 DRAM
+	 * called “Write Leveling”. The procedure would be
+	 * 1) Set Rx9E[3:2] to select which Rank to do write leveling.
+	 * 2) Issue MRS commands to each Rank of DRAM to let them know which one
+	 *     is enabled for Write leveling.
+	 * 3) Set Rx75[2] to 1 to trigger the operation.
+	 * After step 3, the controller will adjust the TX DQS delay by one step
+	 * and send a pulse of DQS out to DRAM. The bit0 of MD of a byte
+	 * (MD0, MD8, MD16, .. MD57) will return the status (“high” or “low”) of
+	 * DCLK the selected Rank sensed. The controller will continue adjusting
+	 * the delay and sending a pulse of MDQS out. This scheme is to detect
+	 * the MDQS delay against the DCLK’s rising and falling edge. Thus we
+	 * can know where the lower bound of the TX DQS setting is when the
+	 * falling edge of DCLK is found, and where the upper bound of the
+	 * TX DQS setting is when the rising edge of DCLK is found by the DRAM.
+	 * With the upper bound and lower bound, we can choose the middle point
+	 * as the setting for the calibration (scan) result. */
+	
+	reg8 = pci_read_config8(MCU, 0x9e);
+	reg8 &= ~(3 << 2);
+	reg8 |= ((rank & 0x3) << 2);
+	pci_write_config8(MCU, 0x9e, reg8);
+	cmd = ddr3_get_mr1(0, 0, 0, 1, 0, 0, 0);
+	vx900_dram_send_soft_mrs(1, cmd);
+	/* Data pattern for calibration */
+	pci_write_config8(MCU, 0x8e, 0x5a);
+	/* Trigger calibration */
+	pci_write_config8(MCU, 0x75, 0x04);
+	printram("TX DQS delay calibration results:\n");
+	vx900_dump_calib(0);
+	cmd = ddr3_get_mr1(0, 0, 0, 0, 0, 0, 0);
+	vx900_dram_send_soft_mrs(1, cmd);
+
+}
+
+static void vx900_dram_ddr3_dimm_init(const ramctr_timing *ctrl,
+				      const rank_layout *ranks)
+{
+	u8 reg8;
+	size_t i;
+
+	/* Step 01 - Set Fun3_Rx6E[5] to 1b to support burst length. */
+	/* This is set at init time
+	reg8 = pci_read_config8(MCU, 0x6e);
+	reg8 |= (1<<5);
+	pci_write_config8(MCU, 0x6e, reg8);*/
+	/* Step 02 - Set Fun3_RX69[0] to 0b (Disable Multiple Page Mode). */
+	reg8 = pci_read_config8(MCU, 0x69);
+	reg8 &= ~(1<<0);
+	pci_write_config8(MCU, 0x69, reg8);
+	
+	/* Step 03 - Set the target physical rank to virtual rank0 and other
+	 * ranks to virtual rank3. If physical rank0 init is desired,
+	 * set Fun3_Rx54 to 08Bh and Fun3_Rx55=0BBh. */
+	pci_write_config16(MCU, 0x54, 0xbbb8);
+	
+	/* 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. */
+	reg8 = pci_read_config8(MCU, 0x6b);
+	reg8 |= (1<<5);
+	pci_write_config8(MCU, 0x6b, reg8);
+	for(i = 0; i < 500; i++) inb(0x80);
+
+	for(i = 0; i < VX900_MAX_MEM_RANKS; i++)
+	{
+		u16 reg16;
+		if(ranks->phys_rank_size[i] == 0) continue;
+		printram("Initializing rank %lu\n", i);
+
+		const u8 rtt_nom = 2;
+		const u8 ods = 0;
+		const u8 rtt_wr = 1;
+
+		reg16 = 0xbbbb;
+		reg16 &= ~(0xf << (4 * i));
+		reg16 |= (0x8 << (4 * i));
+		pci_write_config16(MCU, 0x54, reg16);
+		vx900_dram_ddr3_do_hw_mrs(0, rtt_nom, ods, rtt_wr, 0, 0);
+		vx900_delay_calib_rank(i);
+	}
+	/* Step 15 – Set the next target physical rank to virtual rank 0 and
+	 * other ranks to virtual rank 3. Repeat Step 6 to 14, then jump to
+	 * Step 16. */
+
+	/* Step 16 – Set Fun3_Rx6B[2:0] to 000b (Normal SDRAM Mode). */
+	reg8 = pci_read_config8(MCU, 0x6b);
+	reg8 &= ~(7<<0);
+	pci_write_config8(MCU, 0x6b, reg8);
+	
+	/* Step 17 – Set Fun3_Rx69[0] to 1b (Enable Multiple Page Mode). */
+	reg8 = pci_read_config8(MCU, 0x69);
+	reg8 |= (1<<0);
+	pci_write_config8(MCU, 0x69, reg8);
+
+	printram("DRAM initialization sequence complete\n");
+}
+
+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 <= 255; 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");
+		}
+	}
+}
+
+static void vx900_dram_write_final_config(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);
+
+	/* My cheat sheet */
+	size_t i;
+	for(i = 0; i < (sizeof(mcu_cheat_sheet)/sizeof(pci_reg8)); i++)
+		pci_write_config8(MCU, mcu_cheat_sheet[i].addr,
+				  mcu_cheat_sheet[i].val);
+}
+
+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");
+	}
+	
+	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();	
+	/* 6) Set DRAM frequency and latencies */
+	vx900_dram_freq_latency(&ctrl_prop);
+	/* 7) Initialize the modules themselves */
+	vx900_dram_ddr3_dimm_init(&ctrl_prop, &ranks);
+	/* 8) 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..8e54627
--- /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  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