[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