[coreboot] Patch set updated for coreboot: a18b5e1 NOTFORMERGE: VX900 early init
Alexandru Gagniuc (mr.nuke.me@gmail.com)
gerrit at coreboot.org
Tue Jul 17 06:43:35 CEST 2012
Alexandru Gagniuc (mr.nuke.me at gmail.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/1228
-gerrit
commit a18b5e15e9d2068ea64b5853701380ea16f25d59
Author: Alexandru Gagniuc <mr.nuke.me at gmail.com>
Date: Mon Aug 1 05:16:20 2011 -0500
NOTFORMERGE: VX900 early init
Early initialization for VIA VX900.
SMBUS is functional.
Raminit works with 1 rank, but untested with several ranks.
Change-Id: I7624944dbc05fbf3019897a116954d71dfda0031
Signed-off-by: Alexandru Gagniuc <mr.nuke.me at gmail.com>
---
src/arch/x86/include/arch/romcc_io.h | 28 +
src/devices/dram/dram.h | 154 +++++
src/devices/dram/dram_util.c | 297 ++++++++
src/devices/smbus/early_smbus.c | 141 ++++
src/devices/smbus/smbus.h | 99 +++
src/include/device/pci_ids.h | 22 +
src/mainboard/via/Kconfig | 3 +
src/mainboard/via/epia-m850/Kconfig | 47 ++
src/mainboard/via/epia-m850/Makefile.inc | 21 +
src/mainboard/via/epia-m850/chip.h | 21 +
src/mainboard/via/epia-m850/devicetree.cb | 62 ++
src/mainboard/via/epia-m850/mainboard.c | 25 +
src/mainboard/via/epia-m850/romstage.c | 89 +++
src/northbridge/via/Kconfig | 1 +
src/northbridge/via/Makefile.inc | 1 +
src/northbridge/via/vx900/Kconfig | 23 +
src/northbridge/via/vx900/Makefile.inc | 34 +
src/northbridge/via/vx900/early_smbus.c | 191 ++++++
src/northbridge/via/vx900/early_vx900.h | 35 +
src/northbridge/via/vx900/forgotten.c | 192 ++++++
src/northbridge/via/vx900/forgotten.h | 78 +++
src/northbridge/via/vx900/raminit.h | 77 +++
src/northbridge/via/vx900/raminit_ddr3.c | 1063 +++++++++++++++++++++++++++++
src/northbridge/via/vx900/romstrap.inc | 50 ++
src/northbridge/via/vx900/romstrap.lds | 27 +
src/northbridge/via/vx900/vx900.h | 26 +
26 files changed, 2807 insertions(+), 0 deletions(-)
diff --git a/src/arch/x86/include/arch/romcc_io.h b/src/arch/x86/include/arch/romcc_io.h
index 37fb7ab..8876b28 100644
--- a/src/arch/x86/include/arch/romcc_io.h
+++ b/src/arch/x86/include/arch/romcc_io.h
@@ -244,6 +244,34 @@ static inline __attribute__((always_inline)) void pci_write_config32(device_t de
#endif
}
+static inline __attribute__((always_inline))
+void pci_mod_config8(device_t dev, unsigned int where,
+ uint8_t clr_mask, uint8_t set_mask)
+{
+ uint8_t reg8 = pci_read_config8(dev, where);
+ reg8 &= ~clr_mask;
+ reg8 |= set_mask;
+ pci_write_config8(dev, where, reg8);
+}
+static inline __attribute__((always_inline))
+void pci_mod_config16(device_t dev, unsigned int where,
+ uint16_t clr_mask, uint16_t set_mask)
+{
+ uint16_t reg16 = pci_read_config16(dev, where);
+ reg16 &= ~clr_mask;
+ reg16 |= set_mask;
+ pci_write_config16(dev, where, reg16);
+}
+static inline __attribute__((always_inline))
+void pci_mod_config32(device_t dev, unsigned int where,
+ uint32_t clr_mask, uint32_t set_mask)
+{
+ uint32_t reg32 = pci_read_config32(dev, where);
+ reg32 &= ~clr_mask;
+ reg32 |= set_mask;
+ pci_write_config32(dev, where, reg32);
+}
+
#define PCI_DEV_INVALID (0xffffffffU)
static inline device_t pci_io_locate_device(unsigned pci_id, device_t dev)
{
diff --git a/src/devices/dram/dram.h b/src/devices/dram/dram.h
new file mode 100644
index 0000000..d79cc52
--- /dev/null
+++ b/src/devices/dram/dram.h
@@ -0,0 +1,154 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 Alexandru Gagniuc <mr.nuke.me at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DRAM_H
+#define DRAM_H
+
+#include <stdint.h>
+
+/* DRAM type, byte 2 of spd */
+#define DRAM_TYPE_UNDEFINED 0x00
+#define DRAM_TYPE_FPM_DRAM 0x01
+#define DRAM_TYPE_EDO 0x02
+#define DRAM_TYPE_PIPELINED_NIBBLE 0x03
+#define DRAM_TYPE_SDRAM 0x04
+#define DRAM_TYPE_ROM 0x05
+#define DRAM_TYPE_DDR_SGRAM 0x06
+#define DRAM_TYPE_DDR 0x07
+#define DRAM_TYPE_DDR2 0x08
+#define DRAM_TYPE_DDR2_FBDIMM 0x09
+#define DRAM_TYPE_DDR2_FB_PROBE 0x0a
+#define DRAM_TYPE_DDR3 0x0b
+
+/* Module type (byte 3, bits 3:0) of SPD */
+#define DIMM_TYPE_UNDEFINED 0
+#define DIMM_TYPE_RDIMM 1
+#define DIMM_TYPE_UDIMM 2
+#define DIMM_TYPE_SO_DIMM 3
+#define DIMM_TYPE_MICRO_DIMM 4
+#define DIMM_TYPE_MINI_RDIMM 5
+#define DIMM_TYPE_MINI_UDIMM 6
+#define DIMM_TYPE_MINI_CDIMM 7
+#define DIMM_TYPE_72B_SO_UDIMM 8
+#define DIMM_TYPE_72B_SO_RDIMM 9
+#define DIMM_TYPE_72B_SO_CDIMM 10
+
+#if defined(CONFIG_DEBUG_RAM_SETUP) && (CONFIG_DEBUG_RAM_SETUP)
+#define printram(x, ...) printk(BIOS_DEBUG, x, ##__VA_ARGS__)
+#else
+#define printram(x, ...)
+#endif
+
+/* Different values for tCK, representing standard DDR3 frequencies
+ * As always, these values are in 1/256 ns units */
+#define TCK_1066MHZ 240
+#define TCK_800MHZ 320
+#define TCK_666MHZ 384
+#define TCK_533MHZ 480
+#define TCK_400MHZ 640
+#define TCK_333MHZ 768
+#define TCK_266MHZ 960
+#define TCK_200MHZ 1280
+
+#define RAM_UNIT (1<<24)
+
+#include <stdint.h>
+
+/**
+ *\brief DIMM characteristics
+ *
+ * The characteristics of each DIMM, as presented by the SPD
+ */
+typedef struct dimm_attr_st {
+ u8 dram_type;
+ u16 cas_supported;
+ /* Number of ranks */
+ u8 ranks;
+ /* Number or row address bits */
+ u8 row_bits;
+ /* Number or column address bits */
+ u8 col_bits;
+ /* Size of module in (1 << 24) bytes (16MB) */
+ u16 size;
+ /* Latencies are in units of ns, scaled by x256 */
+ u32 tCK;
+ u32 tAA;
+ u32 tWR;
+ u32 tRCD;
+ u32 tRRD;
+ u32 tRP;
+ u32 tRAS;
+ u32 tRC;
+ u32 tRFC;
+ u32 tWTR;
+ u32 tRTP;
+ u32 tFAW;
+
+} dimm_attr;
+
+typedef struct ramctr_timing_st {
+ u8 dram_type;
+ u16 cas_supported;
+ /* tLatencies are in units of ns, scaled by x256 */
+ u32 tCK;
+ u32 tAA;
+ u32 tWR;
+ u32 tRCD;
+ u32 tRRD;
+ u32 tRP;
+ u32 tRAS;
+ u32 tRC;
+ u32 tRFC;
+ u32 tWTR;
+ u32 tRTP;
+ u32 tFAW;
+ /* Latencies in terms of clock cycles
+ * They are saved separately as they are needed for DRAM MRS commands*/
+ u8 CAS; /* CAS read latency */
+ u8 CWL; /* CAS write latency */
+ u8 WR; /* write recovery time */
+
+} ramctr_timing;
+
+
+typedef u8 spd_raw_data[256];
+
+/**
+ * \brief Decode the raw spd data
+ */
+void spd_decode_ddr3(dimm_attr *dimm, spd_raw_data spd_data);
+
+/**
+ * \brief Checks if the DIMM is Registered based on byte[3] of the spd
+ */
+int dimm_is_registered(u8 spd_byte3);
+
+/**
+ * \brief Print the info in dimm
+ */
+void dram_print_spd_ddr3(const dimm_attr *dimm);
+
+/**
+ * \brief Read double word from specified address
+ *
+ * Should be useful when doing an MRS to the DIMM
+ */
+u32 volatile_read(u32 addr);
+
+#endif /* DRAM_H */
diff --git a/src/devices/dram/dram_util.c b/src/devices/dram/dram_util.c
new file mode 100644
index 0000000..75913e4
--- /dev/null
+++ b/src/devices/dram/dram_util.c
@@ -0,0 +1,297 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 Alexandru Gagniuc <mr.nuke.me at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "dram.h"
+#include <console/console.h>
+#include <device/device.h>
+
+u32 volatile_read(volatile u32 addr)
+{
+ volatile u32 result;
+ result = *(volatile u32*)addr;
+ return result;
+}
+
+int dimm_is_registered(u8 spd_byte3)
+{
+ spd_byte3 &= 0x0f;
+ if( (spd_byte3 == DIMM_TYPE_RDIMM)
+ | (spd_byte3 == DIMM_TYPE_MINI_RDIMM)
+ | (spd_byte3 == DIMM_TYPE_72B_SO_RDIMM) )
+ return 1;
+
+ return 0;
+}
+
+void spd_decode_ddr3(dimm_attr *dimm, spd_raw_data spd)
+{
+ int nCRC, i;
+ u16 crc, spd_crc;
+ u8 * ptr = spd;
+ u8 ftb_divisor;
+ u8 ftb_dividend;
+ u8 capacity_shift, bus_width, sdram_width;
+ u32 mtb; /* medium time base */
+ /* Make sure that the spd dump is indeed from a DDR3 module */
+ if(spd[2] != DRAM_TYPE_DDR3)
+ {
+ printram("Not a DDR3 SPD!\n");
+ dimm->dram_type = DRAM_TYPE_UNDEFINED;
+ return;
+ }
+ dimm->dram_type = DRAM_TYPE_DDR3;
+
+ /* Find the number of bytes covered by CRC */
+ if(spd[0] & 0x80) {
+ nCRC = 117;
+ } else {
+ nCRC =126;
+ }
+
+ /* Compute the CRC */
+ crc = 0;
+ while (--nCRC >= 0) {
+ crc = crc ^ (int)*ptr++ << 8;
+ for (i = 0; i < 8; ++i)
+ if (crc & 0x8000) {
+ crc = crc << 1 ^ 0x1021;
+ } else {
+ crc = crc << 1;
+ }
+ }
+ /* Compare with the CRC in the SPD */
+ spd_crc = (spd[127] << 8) + spd[126];
+ /* Verify the CRC is correct */
+ if(crc != spd_crc)
+ printram("SPD CRC failed!!!");
+
+
+ unsigned int reg, val, param;
+ printram(" Revision: %x \n", spd[1] );
+ printram(" Type : %x \n", spd[2] );
+ printram(" Key : %x \n", spd[3] );
+
+ reg = spd[4];
+ /* Number of memory banks */
+ val = (reg >> 4) & 0x07;
+ if (val > 0x03) printram(" Invalid number of memory banks\n");
+ param = 1 << (val + 3);
+ printram(" Banks : %u \n", param );
+ /* SDRAM capacity */
+ capacity_shift = reg & 0x0f;
+ if (capacity_shift > 0x06) printram(" Invalid module capacity\n");
+ if (capacity_shift < 0x02) {
+ printram(" Capacity: %u Mb\n", 256 << capacity_shift);
+ } else {
+ printram(" Capacity: %u Gb\n", 1 << (capacity_shift - 2));
+ }
+
+ reg = spd[5];
+ /* Row address bits */
+ val = (reg >> 4) & 0x07;
+ if(val > 0x04) printram(" Invalid row address bits\n");
+ dimm->row_bits = val + 12;
+ /* Column address bits */
+ val = reg & 0x07;
+ if(val > 0x03) printram(" Invalid column address bits\n");
+ dimm->col_bits = val + 9;
+
+ /* Module nominal voltage */
+ reg = spd[6];
+ print_debug(" Supported voltages: ");
+ if(reg & (1<<2) ) print_debug("1.2V ");
+ if(reg & (1<<1) ) print_debug("1.35V ");
+ if( !(reg & (1<<0)) ) print_debug("1.5V ");
+ print_debug("\n");
+
+ /* Module organization */
+ reg = spd[7];
+ /* Number of ranks*/
+ val = (reg >> 3) & 0x07;
+ if(val > 3) printram(" Invalid number of ranks\n");
+ dimm->ranks = val + 1;
+ /* SDRAM device width */
+ val = (reg & 0x07);
+ if(val > 3) printram(" Invalid SDRAM width\n");
+ sdram_width = (4 << val);
+ printram(" SDRAM width : %u \n", sdram_width);
+
+ /* Memory bus width */
+ reg = spd[8];
+ /* Bus extension */
+ val = (reg >> 3) & 0x03;
+ if(val > 1) printram(" Invalid bus extension\n");
+ printram(" Bus extension : %u bits\n", val?8:0);
+ /* Bus width */
+ val = reg & 0x07;
+ if(val > 3) printram(" Invalid bus width\n");
+ bus_width = 8 << val;
+ printram(" Bus width : %u \n", bus_width);
+
+ /* We have all the info we need to compute the dimm size */
+ /* Capacity is 256Mbit multiplied by the power of 2 specified in
+ * capacity_shift
+ * The rest is the JEDEC formula */
+ /* I am certain it will fit in 16 bits
+ * Remember, It's in units of 2^24 bytes*/
+ dimm->size = ( (1 << (capacity_shift + (25-24)) ) * bus_width
+ * dimm->ranks ) / sdram_width;
+
+ /* Fine Timebase (FTB) Dividend/Divisor */
+ /* Dividend */
+ ftb_dividend = (spd[9] >> 4) & 0x0f;
+ /* Divisor */
+ ftb_divisor = spd[9] & 0x0f;
+
+ /* Medium Timebase =
+ * Medium Timebase (MTB) Dividend /
+ * Medium Timebase (MTB) Divisor */
+ mtb = (((u32)spd[10]) << 8) / spd [11];
+
+ /* SDRAM Minimum Cycle Time (tCKmin) */
+ dimm->tCK = spd[12] * mtb;
+
+ /* CAS Latencies Supported */
+ dimm->cas_supported = (spd[15] << 8) + spd[14];
+
+ /* Minimum CAS Latency Time (tAAmin) */
+ dimm->tAA = spd[16] * mtb;
+
+ /* Minimum Write Recovery Time (tWRmin) */
+ dimm->tWR = spd[17] * mtb;
+
+ /* Minimum RAS# to CAS# Delay Time (tRCDmin) */
+ dimm->tRCD = spd[18] * mtb;
+
+ /* Minimum Row Active to Row Active Delay Time (tRRDmin) */
+ dimm->tRRD = spd[19] * mtb;
+
+ /* Minimum Row Precharge Delay Time (tRPmin)*/
+ dimm->tRP = spd[20] * mtb;
+
+ /* Minimum Active to Precharge Delay Time (tRASmin) */
+ dimm->tRAS = (((spd[21] & 0x0f) << 8) + spd[22]) * mtb;
+
+ /* Minimum Active to Active/Refresh Delay Time (tRCmin) */
+ dimm->tRC = (((spd[21] & 0xf0) << 4) + spd[23]) * mtb;
+
+ /* Minimum Refresh Recovery Delay Time (tRFCmin) */
+ dimm->tRFC = ((spd[25] << 8) + spd[24]) * mtb;
+
+ /* Minimum Internal Write to Read Command Delay Time (tWTRmin) */
+ dimm->tWTR = spd[26] * mtb;
+
+ /* Minimum Internal Read to Precharge Command Delay Time (tRTPmin) */
+ dimm->tRTP = spd[27] * mtb;
+
+ /* Minimum Four Activate Window Delay Time (tFAWmin) */
+ dimm->tFAW = (((spd[28] & 0x0f) << 8) + spd[29]) * mtb;
+
+ /* SDRAM Optional Features */
+ reg = spd[30];
+ print_debug(" Optional features :");
+ if(reg & 0x80) print_debug(" DLL-Off_mode");
+ if(reg & 0x02) print_debug(" RZQ/7");
+ if(reg & 0x01) print_debug(" RZQ/6");
+ print_debug("\n");
+
+ /* SDRAM Thermal and Refresh Options */
+ reg = spd[31];
+ print_debug(" Thermal features :");
+ if(reg & 0x80) print_debug(" PASR");
+ if(reg & 0x08) print_debug(" ODTS");
+ if(reg & 0x04) print_debug(" ASR");
+ if(reg & 0x02) print_debug(" ext_temp_refresh");
+ if(reg & 0x01) print_debug(" ext_temp_range");
+ print_debug("\n");
+
+ /* Module Thermal Sensor */
+ reg = spd[32];
+ print_debug(" Thermal sensor : ");
+ if(reg & 0x80) print_debug("yes");
+ else print_debug("no");
+ print_debug("\n");
+
+ /* SDRAM Device Type */
+ reg = spd[33];
+ print_debug(" Standard SDRAM : ");
+ if(reg & 0x80) print_debug("no");
+ else print_debug("yes");
+ print_debug("\n");
+
+ /* Fine Offset for SDRAM Minimum Cycle Time (tCKmin) */
+ //printram(" tCKmin FTB : %i \n", spd[34]);
+ /* Fine Offset for Minimum CAS Latency Time (tAAmin) */
+ //printram(" tAAmin FTB : %i \n", spd[35]);
+ /* Fine Offset for Minimum RAS# to CAS# Delay Time (tRCDmin) */
+ //printram(" tRCDmin FTB : %i \n", spd[36]);
+ /* Fine Offset for Minimum Row Precharge Delay Time (tRPmin) */
+ //printram(" tRPmin FTB : %i \n", spd[37]);
+ /* Fine Offset for Minimum Active to Active/Refresh Delay Time (tRCmin) */
+ //printram(" tRCmin FTB : %i \n", spd[38]);
+
+ if(spd[60] & 0x01)
+ printram(" DIMM Address bits mirrorred!!!\n");
+
+}
+
+static void print_ns(const char * msg, u32 val)
+{
+ u32 mant, fp;
+ mant = val/256;
+ fp = (val % 256) * 1000/256;
+
+ printram("%s%3u.%.3u ns\n", msg, mant, fp);
+}
+
+void dram_print_spd_ddr3(const dimm_attr *dimm)
+{
+ u16 val16;
+ int i;
+
+ printram(" Row addr bits : %u \n", dimm->row_bits);
+ printram(" Column addr bits : %u \n", dimm->col_bits);
+ printram(" Number of ranks : %u \n", dimm->ranks);
+ printram(" DIMM Capacity : %u MB\n", dimm->size << 4);
+
+ /* CAS Latencies Supported */
+ val16 = dimm->cas_supported;
+ print_debug(" CAS latencies :");
+ i = 0;
+ do{
+ if(val16 & 1) printram(" %u", i + 4);
+ i++;
+ val16 >>= 1;
+ } while(val16);
+ print_debug("\n");
+
+ print_ns(" tCKmin : ", dimm->tCK);
+ print_ns(" tAAmin : ", dimm->tAA);
+ print_ns(" tWRmin : ", dimm->tWR);
+ print_ns(" tRCDmin : ", dimm->tRCD);
+ print_ns(" tRRDmin : ", dimm->tRRD);
+ print_ns(" tRPmin : ", dimm->tRP);
+ print_ns(" tRASmin : ", dimm->tRAS);
+ print_ns(" tRCmin : ", dimm->tRC);
+ print_ns(" tRFCmin : ", dimm->tRFC);
+ print_ns(" tWTRmin : ", dimm->tWTR);
+ print_ns(" tRTPmin : ", dimm->tRTP);
+ print_ns(" tFAWmin : ", dimm->tFAW);
+
+}
diff --git a/src/devices/smbus/early_smbus.c b/src/devices/smbus/early_smbus.c
new file mode 100644
index 0000000..4583aca
--- /dev/null
+++ b/src/devices/smbus/early_smbus.c
@@ -0,0 +1,141 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 Alexandru Gagniuc <mr.nuke.me at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+/**
+ * @file post_codes.h
+ *
+ * This file defines the implementations for the functions defined in smbus.h
+ * These are a generic SMBUS implementation, which should work with a majority
+ * of chipsets.
+ * They are marked weak so that they can be overridden by the chipset code if
+ * necessary.
+ */
+
+#include "smbus.h"
+
+/**
+ * \brief Brief delay for SMBUS transactions
+ */
+__attribute__((weak))
+void __smbus_delay(void)
+{
+ inb(0x80);
+}
+
+/**
+ * \brief Clear the SMBUS host status register
+ */
+__attribute__((weak))
+void __smbus_reset(u16 __smbus_io_base)
+{
+ outb(0xdf, SMBHSTSTAT);
+}
+
+/**
+ * \brief Print an error, should it occur. If no error, just exit.
+ *
+ * @param host_status The data returned on the host status register after
+ * a transaction is processed.
+ * @param loops The number of times a transaction was attempted.
+ * @return 0 if no error occurred
+ * 1 if an error was detected
+ */
+__attribute__((weak))
+int __smbus_print_error(u8 host_status, int loops, u16 __smbus_io_base)
+{
+ /* Check if there actually was an error. */
+ if ((host_status == 0x00 || host_status == 0x40 ||
+ host_status == 0x42) && (loops < SMBUS_TIMEOUT))
+ return 0;
+
+ if (loops >= SMBUS_TIMEOUT)
+ printsmbus("SMBus timeout\n");
+ if (host_status & (1 << 4))
+ printsmbus("Interrupt/SMI# was Failed Bus Transaction\n");
+ if (host_status & (1 << 3))
+ printsmbus("Bus error\n");
+ if (host_status & (1 << 2))
+ printsmbus("Device error\n");
+ if (host_status & (1 << 1))
+ printsmbus("Interrupt/SMI# completed successfully\n");
+ if (host_status & (1 << 0))
+ printsmbus("Host busy\n");
+ return 1;
+}
+
+/**
+ * \brief Checks if the SMBUS is currently busy with a transaction
+ */
+__attribute__((weak))
+int __smbus_is_busy(u16 __smbus_io_base)
+{
+ /* Check if bit 0 of the status register is 1 (busy) or 0 (ready) */
+ return ( (inb(SMBHSTSTAT) & (1 << 0)) == 1);
+}
+
+/**
+ * \brief Wait for the SMBUS to become ready to process a new transaction.
+ */
+__attribute__((weak))
+int __smbus_wait_until_ready(u16 __smbus_io_base)
+{
+ int loops;
+
+ printsmbus("Waiting until SMBus ready\n");
+
+ /* Loop up to SMBUS_TIMEOUT times, waiting for bit 0 of the
+ * SMBus Host Status register to go to 0, indicating the operation
+ * was completed successfully. I don't remember why I did it this way,
+ * but I think it was because ROMCC was running low on registers */
+ loops = 0;
+ while (__smbus_is_busy(__smbus_io_base) && loops < SMBUS_TIMEOUT)
+ ++loops;
+
+ return __smbus_print_error(inb(SMBHSTSTAT), loops, __smbus_io_base);
+}
+
+/**
+ * \brief Read a byte from the SMBUS.
+ *
+ * @param dimm The address location of the DIMM on the SMBus.
+ * @param offset The offset the data is located at.
+ */
+__attribute__((weak))
+u8 __smbus_read_byte(u8 dimm, u8 offset, u16 __smbus_io_base)
+{
+ u8 val;
+
+ /* Initialize SMBUS sequence */
+ __smbus_reset(__smbus_io_base);
+ /* Clear host data port. */
+ outb(0x00, SMBHSTDAT0);
+
+ __smbus_wait_until_ready(__smbus_io_base);
+
+ /* Actual addr to reg format. */
+ dimm = (dimm << 1);
+ dimm |= 1; /* read command */
+ outb(dimm, SMBXMITADD);
+ outb(offset, SMBHSTCMD);
+ /* Start transaction, byte data read. */
+ outb(0x48, SMBHSTCTL);
+ __smbus_wait_until_ready(__smbus_io_base);
+
+ val = inb(SMBHSTDAT0);
+ return val;
+}
\ No newline at end of file
diff --git a/src/devices/smbus/smbus.h b/src/devices/smbus/smbus.h
new file mode 100644
index 0000000..820a0dd
--- /dev/null
+++ b/src/devices/smbus/smbus.h
@@ -0,0 +1,99 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 Alexandru Gagniuc <mr.nuke.me at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @file post_codes.h
+ *
+ * This file defines the prototypes for several common SMBUS functions
+ * These functions are prefixed with __smbus_ so that they do not conflict with
+ * the dozens of similar (duplicated) implementations in many southbridges.
+ *
+ * As a last parameter, the SMBUS functions take a u16 value __smbus_io_base,
+ * which represents the base IO port for smbus transactions
+ */
+
+#include <arch/io.h>
+
+/**
+ * \brief SMBUS IO ports in relation to the base IO port
+ */
+#define SMBHSTSTAT __smbus_io_base + 0x0
+#define SMBSLVSTAT __smbus_io_base + 0x1
+#define SMBHSTCTL __smbus_io_base + 0x2
+#define SMBHSTCMD __smbus_io_base + 0x3
+#define SMBXMITADD __smbus_io_base + 0x4
+#define SMBHSTDAT0 __smbus_io_base + 0x5
+#define SMBHSTDAT1 __smbus_io_base + 0x6
+#define SMBBLKDAT __smbus_io_base + 0x7
+#define SMBSLVCTL __smbus_io_base + 0x8
+#define SMBTRNSADD __smbus_io_base + 0x9
+#define SMBSLVDATA __smbus_io_base + 0xa
+
+#define SMBUS_TIMEOUT (100*1000*10)
+
+/**
+ * \brief printk macro for SMBUS debugging
+ */
+#if defined(CONFIG_DEBUG_SMBUS_SETUP) && (CONFIG_DEBUG_SMBUS_SETUP)
+#define printsmbus(x, ...) printk(BIOS_DEBUG, x, ##__VA_ARGS__)
+#else
+#define printsmbus(x, ...)
+#endif
+
+void __smbus_reset(u16 __smbus_io_base);
+int __smbus_print_error(u8 host_status, int loops, u16 __smbus_io_base);
+int __smbus_is_busy(u16 __smbus_io_base);
+int __smbus_wait_until_ready(u16 __smbus_io_base);
+u8 __smbus_read_byte(u8 dimm, u8 offset, u16 __smbus_io_base);
+
+void __smbus_delay(void);
+
+#if defined(SMBUS_IO_BASE) && (SMBUS_IO_BASE != 0)
+
+__attribute__((always_inline, unused))
+static void smbus_reset(void)
+{
+ __smbus_reset(SMBUS_IO_BASE);
+}
+
+__attribute__((always_inline, unused))
+static int smbus_is_busy(void)
+{
+ return __smbus_is_busy(SMBUS_IO_BASE);
+}
+
+__attribute__((always_inline, unused))
+static int smbus_wait_until_ready(void)
+{
+ return __smbus_wait_until_ready(SMBUS_IO_BASE);
+}
+
+__attribute__((always_inline, unused))
+static int smbus_print_error(u8 host_status, int loops)
+{
+ return __smbus_print_error(host_status, loops, SMBUS_IO_BASE);
+}
+
+__attribute__((always_inline, unused))
+static u8 smbus_read_byte(u8 dimm, u8 offset)
+{
+ return __smbus_read_byte(dimm, offset, SMBUS_IO_BASE);
+}
+
+#endif
\ No newline at end of file
diff --git a/src/include/device/pci_ids.h b/src/include/device/pci_ids.h
index fd886da..7a8600e 100644
--- a/src/include/device/pci_ids.h
+++ b/src/include/device/pci_ids.h
@@ -1337,6 +1337,28 @@
#define PCI_DEVICE_ID_VIA_VX855_VGA 0x5122
#define PCI_DEVICE_ID_VIA_VX855_VLINK 0x7409
#define PCI_DEVICE_ID_VIA_VX855_MEMCTRL 0x3409
+/* VIA VX900 PCI IDs */
+#define PCI_DEVICE_ID_VIA_VX900_HOST_BR 0x0410
+#define PCI_DEVICE_ID_VIA_VX900_ERROR_REPORT 0x1410
+#define PCI_DEVICE_ID_VIA_VX900_CPU_CTR 0x2410
+#define PCI_DEVICE_ID_VIA_VX900_DRAM_CTR 0x3410
+#define PCI_DEVICE_ID_VIA_VX900_PWR_MGMT 0x4410
+#define PCI_DEVICE_ID_VIA_VX900_TRAF_CTR 0x5410
+#define PCI_DEVICE_ID_VIA_VX900_SCRATCH_REG 0x6410
+#define PCI_DEVICE_ID_VIA_VX900_NORTH_NB_SB_CTR 0x7410
+#define PCI_DEVICE_ID_VIA_VX900_LPC 0x8410
+#define PCI_DEVICE_ID_VIA_VX900_PEX1 0xa410
+#define PCI_DEVICE_ID_VIA_VX900_PEX2 0xb410
+#define PCI_DEVICE_ID_VIA_VX900_PEX3 0xc410
+#define PCI_DEVICE_ID_VIA_VX900_PEX4 0xd410
+#define PCI_DEVICE_ID_VIA_VX900_PEX_CTR 0xe410
+#define PCI_DEVICE_ID_VIA_VX900_SOUTH_NB_SB_CTR 0xa535
+#define PCI_DEVICE_ID_VIA_VX900_PCI_BR 0xb535
+#define PCI_DEVICE_ID_VIA_VX900_VGA 0x7122
+#define PCI_DEVICE_ID_VIA_VX900_VGA_UNKNOWN 0x9170
+#define PCI_DEVICE_ID_VIA_VX900_HDAC 0x3288
+#define PCI_DEVICE_ID_VIA_VX900_ETHERNER 0x3119
+/* VIA CN700 */
#define PCI_DEVICE_ID_VIA_CN700_AGP 0x0314
#define PCI_DEVICE_ID_VIA_CN700_ERR 0x1314
#define PCI_DEVICE_ID_VIA_CN700_HOST 0x2314
diff --git a/src/mainboard/via/Kconfig b/src/mainboard/via/Kconfig
index 6980548..48f4055 100644
--- a/src/mainboard/via/Kconfig
+++ b/src/mainboard/via/Kconfig
@@ -9,6 +9,8 @@ config BOARD_VIA_EPIA_CN
bool "EPIA-CN"
config BOARD_VIA_EPIA_M700
bool "EPIA-M700"
+config BOARD_VIA_EPIA_M850
+ bool "EPIA-M850"
config BOARD_VIA_EPIA_M
bool "EPIA-M"
config BOARD_VIA_EPIA_N
@@ -23,6 +25,7 @@ endchoice
source "src/mainboard/via/epia/Kconfig"
source "src/mainboard/via/epia-cn/Kconfig"
source "src/mainboard/via/epia-m700/Kconfig"
+source "src/mainboard/via/epia-m850/Kconfig"
source "src/mainboard/via/epia-m/Kconfig"
source "src/mainboard/via/epia-n/Kconfig"
source "src/mainboard/via/pc2500e/Kconfig"
diff --git a/src/mainboard/via/epia-m850/Kconfig b/src/mainboard/via/epia-m850/Kconfig
new file mode 100644
index 0000000..9c07f47
--- /dev/null
+++ b/src/mainboard/via/epia-m850/Kconfig
@@ -0,0 +1,47 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2011 Alexandru Gagniuc <mr.nuke.me at gmail.com>
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+
+if BOARD_VIA_EPIA_M850
+
+config BOARD_SPECIFIC_OPTIONS # dummy
+ def_bool y
+ select ARCH_X86
+ select CPU_VIA_C7
+ select NORTHBRIDGE_VIA_VX900
+ select SUPERIO_FINTEK_F81865F
+ #select BOARD_HAS_FADT
+ #select HAVE_PIRQ_TABLE
+ #select HAVE_ACPI_TABLES
+ #select HAVE_OPTION_TABLE
+ select BOARD_ROMSIZE_KB_512
+ select RAMINIT_SYSINFO
+
+config MAINBOARD_DIR
+ string
+ default via/epia-m850
+
+config MAINBOARD_PART_NUMBER
+ string
+ default "EPIA-M850"
+
+config IRQ_SLOT_COUNT
+ int
+ default 13
+
+endif # BOARD_VIA_EPIA_M850
diff --git a/src/mainboard/via/epia-m850/Makefile.inc b/src/mainboard/via/epia-m850/Makefile.inc
new file mode 100644
index 0000000..9dffc79
--- /dev/null
+++ b/src/mainboard/via/epia-m850/Makefile.inc
@@ -0,0 +1,21 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2011 Alexandru Gagniuc <mr.nuke.me at gmail.com>
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+
+#romstage-y += ./../../../superio/fintek/f81865f/f81865f_early_serial.c
+
diff --git a/src/mainboard/via/epia-m850/chip.h b/src/mainboard/via/epia-m850/chip.h
new file mode 100644
index 0000000..2ac4f12
--- /dev/null
+++ b/src/mainboard/via/epia-m850/chip.h
@@ -0,0 +1,21 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 Alexandru Gagniuc <mr.nuke.me at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+extern struct chip_operations mainboard_ops;
+
+struct mainboard_config {};
diff --git a/src/mainboard/via/epia-m850/devicetree.cb b/src/mainboard/via/epia-m850/devicetree.cb
new file mode 100644
index 0000000..34f2848
--- /dev/null
+++ b/src/mainboard/via/epia-m850/devicetree.cb
@@ -0,0 +1,62 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2011 Alexandru Gagniuc <mr.nuke.me at gmail.com>
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+
+chip northbridge/via/vx900 # Northbridge
+ device lapic_cluster 0 on # APIC cluster
+ chip cpu/via/model_c7 # CPU ATOM (same as VIA C7)
+ device lapic 0 on end # APIC
+ end
+ end
+ device pci_domain 0 on
+ device pci 0.0 on end # AGP Bridge
+ device pci 0.1 on end # Error Reporting
+ device pci 0.2 on end # Host Bus Control
+ device pci 0.3 on end # Memory Controller
+ device pci 0.4 on end # Power Management
+ device pci 0.5 on end # Power Management
+ device pci 0.6 on end # Power Management
+ device pci 0.7 on end # V-Link Controller
+ device pci 1.0 on end # PCI Bridge
+ device pci 1.1 on end # Audio
+ device pci 3.0 on end # PCIE control
+ device pci 3.1 on end # PEX1
+ device pci 3.2 on end # PEX2
+ device pci 3.3 on end # PEX3
+ device pci 3.4 on end # PEX4
+ device pci f.0 on end # IDE/SATA
+ device pci 10.0 on end # USB 1.1
+ device pci 10.1 on end # USB 1.1
+ device pci 10.2 on end # USB 1.1
+ device pci 10.3 on end # USB 1.1
+ device pci 10.4 on end # USB 2.0
+ device pci 11.0 on end # LPC
+ chip drivers/generic/generic # DIMM 0-0-0
+ device i2c 50 on end
+ end
+ chip drivers/generic/generic # DIMM 0-0-1
+ device i2c 51 on end
+ end
+ chip superio/fintek/f81865f # Super duper IO
+ end
+ device pci 11.7 on end # NB/SB control
+ device pci 13.0 on end # PCI Bridge
+ device pci 14.0 on end # HD Audio
+ end
+
+end
diff --git a/src/mainboard/via/epia-m850/mainboard.c b/src/mainboard/via/epia-m850/mainboard.c
new file mode 100644
index 0000000..572f7da
--- /dev/null
+++ b/src/mainboard/via/epia-m850/mainboard.c
@@ -0,0 +1,25 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 Alexandru Gagniuc <mr.nuke.me at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <device/device.h>
+#include "chip.h"
+
+struct chip_operations mainboard_ops = {
+ CHIP_NAME("VIA EPIA-M850 Mainboard")
+};
diff --git a/src/mainboard/via/epia-m850/romstage.c b/src/mainboard/via/epia-m850/romstage.c
new file mode 100644
index 0000000..8f68bf7
--- /dev/null
+++ b/src/mainboard/via/epia-m850/romstage.c
@@ -0,0 +1,89 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011-2012 Alexandru Gagniuc <mr.nuke.me at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Inspired from the EPIA-M700
+ */
+
+#include <stdint.h>
+#include <device/pci_def.h>
+#include <device/pci_ids.h>
+#include <arch/io.h>
+#include <device/pnp_def.h>
+#include <arch/romcc_io.h>
+#include <arch/hlt.h>
+#include <console/console.h>
+#include <lib.h>
+#include "cpu/x86/bist.h"
+#include <string.h>
+
+#include "northbridge/via/vx900/early_vx900.h"
+#include "northbridge/via/vx900/raminit.h"
+#include "superio/fintek/f81865f/f81865f_early_serial.c"
+
+#define SERIAL_DEV PNP_DEV(0x4e, 0x10)
+
+/* cache_as_ram.inc jumps to here. */
+void main(unsigned long bist)
+{
+ /* Enable multifunction bit for northbridge.
+ * This enables the PCI configuration spaces of D0F1 to D0F7 to be
+ * accessed */
+ pci_write_config8(PCI_DEV(0, 0, 0), 0x4f, 0x01);
+
+
+ device_t hbc = PCI_DEV(0,0,2);
+ pci_write_config8(hbc, 0x50, 0x08);
+ pci_write_config8(hbc, 0x51, 0x3c);
+ pci_write_config8(hbc, 0x52, 0xcf);
+ pci_write_config8(hbc, 0x53, 0x44);
+ pci_write_config8(hbc, 0x54, 0x70);
+ pci_write_config8(hbc, 0x55, 0x20);
+ pci_write_config8(hbc, 0x56, 0x63);
+ pci_write_config8(hbc, 0x5d, 0xa2);
+ pci_write_config8(hbc, 0x5e, 0x44);
+ pci_write_config8(hbc, 0x5f, 0x46);
+
+ if(0)f81865f_enable_serial(SERIAL_DEV, CONFIG_TTYS0_BASE);
+ console_init();
+ print_debug("Console initialized. \n");
+
+ /* Halt if there was a built-in self test failure. */
+ report_bist_failure(bist);
+
+ /* x86 cold boot I/O cmd. */
+ enable_smbus();
+
+ /* If this works, then SMBUS is up and running */
+ //dump_spd_data();
+
+ /* Now we can worry about raminit.
+ * This board only has DDR3, so no need to worry about which DRAM type
+ * to use */
+ dimm_layout dimms = {{0x50, 0x51, SPD_END_LIST}};
+ vx900_init_dram_ddr3(&dimms);
+ //ram_check(0, 640 * 1024);
+ //ram_check(1<<26, (1<<26) + 0x20);
+ ram_check(0, 0x80);
+
+ print_debug("We passed RAM verify\n");
+
+ return;
+
+}
diff --git a/src/northbridge/via/Kconfig b/src/northbridge/via/Kconfig
index 2c38acf..8a747b9 100644
--- a/src/northbridge/via/Kconfig
+++ b/src/northbridge/via/Kconfig
@@ -4,3 +4,4 @@ source src/northbridge/via/cn400/Kconfig
source src/northbridge/via/vt8601/Kconfig
source src/northbridge/via/vt8623/Kconfig
source src/northbridge/via/vx800/Kconfig
+source src/northbridge/via/vx900/Kconfig
diff --git a/src/northbridge/via/Makefile.inc b/src/northbridge/via/Makefile.inc
index 75cb15b..2e74b61 100644
--- a/src/northbridge/via/Makefile.inc
+++ b/src/northbridge/via/Makefile.inc
@@ -4,4 +4,5 @@ subdirs-$(CONFIG_NORTHBRIDGE_VIA_CN700) += cn700
subdirs-$(CONFIG_NORTHBRIDGE_VIA_CX700) += cx700
subdirs-$(CONFIG_NORTHBRIDGE_VIA_CN400) += cn400
subdirs-$(CONFIG_NORTHBRIDGE_VIA_VX800) += vx800
+subdirs-$(CONFIG_NORTHBRIDGE_VIA_VX900) += vx900
diff --git a/src/northbridge/via/vx900/Kconfig b/src/northbridge/via/vx900/Kconfig
new file mode 100644
index 0000000..60e7993
--- /dev/null
+++ b/src/northbridge/via/vx900/Kconfig
@@ -0,0 +1,23 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2011 Alexandru Gagniuc <mr.nuke.me at gmail.com>
+##
+## This program is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program. If not, see <http://www.gnu.org/licenses/>.
+##
+
+config NORTHBRIDGE_VIA_VX900
+ bool
+ select HAVE_DEBUG_RAM_SETUP
+ select HAVE_DEBUG_SMBUS
\ No newline at end of file
diff --git a/src/northbridge/via/vx900/Makefile.inc b/src/northbridge/via/vx900/Makefile.inc
new file mode 100644
index 0000000..89c8763
--- /dev/null
+++ b/src/northbridge/via/vx900/Makefile.inc
@@ -0,0 +1,34 @@
+##
+## 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
+#romstage-y += ./../../../cpu/x86/lapic/apic_timer.c
+
+# Drivers for these devices already exist with the vx800
+# Use those instead of duplicating code
+
+driver-y += ./../vx800/northbridge.c
+driver-y += ./../vx800/vga.c
+driver-y += ./../vx800/lpc.c
+
+chipset_bootblock_inc += $(src)/northbridge/via/vx900/romstrap.inc
+chipset_bootblock_lds += $(src)/northbridge/via/vx900/romstrap.lds
diff --git a/src/northbridge/via/vx900/early_smbus.c b/src/northbridge/via/vx900/early_smbus.c
new file mode 100644
index 0000000..aa6cec0
--- /dev/null
+++ b/src/northbridge/via/vx900/early_smbus.c
@@ -0,0 +1,191 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 Alexandru Gagniuc <mr.nuke.me at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <device/pci_ids.h>
+#include "early_vx900.h"
+
+#include <arch/io.h>
+#include <arch/romcc_io.h>
+#include <console/console.h>
+#include <devices/dram/dram.h>
+
+__attribute__((unused))
+static void smbus_delays(int delays)
+{
+ while(delays--) __smbus_delay();
+}
+
+
+/**
+ * Read a byte from the SMBus.
+ *
+ * @param dimm The address location of the DIMM on the SMBus.
+ * @param offset The offset the data is located at.
+ */
+u8 __smbus_read_byte(u8 dimm, u8 offset, u16 __smbus_io_base)
+{
+ u8 val;
+
+ /* Initialize SMBUS sequence */
+ smbus_reset();
+ /* Clear host data port. */
+ outb(0x00, SMBHSTDAT0);
+
+ smbus_wait_until_ready();
+ smbus_delays(50);
+
+ /* Actual addr to reg format. */
+ dimm = (dimm << 1);
+ dimm |= 1; /* read command */
+ outb(dimm, SMBXMITADD);
+ outb(offset, SMBHSTCMD);
+ /* Start transaction, byte data read. */
+ outb(0x48, SMBHSTCTL);
+ smbus_wait_until_ready();
+
+ val = inb(SMBHSTDAT0);
+ return val;
+}
+
+void enable_smbus(void)
+{
+ device_t dev;
+ u8 reg8;
+ u16 __smbus_io_base = SMBUS_IO_BASE;
+
+ /* Locate the Power Management control */
+ dev = pci_locate_device(PCI_ID(PCI_VENDOR_ID_VIA,
+ PCI_DEVICE_ID_VIA_VX900_LPC), 0);
+
+ if (dev == PCI_DEV_INVALID) {
+ die("Power Managment Controller not found\n");
+ }
+
+ /*
+ * To use SMBus to manage devices on the system board, it is a must to
+ * enable SMBus function by setting
+ * PMU_RXD2[0] (SMBus Controller Enable) to 1.
+ * And set PMU_RXD0 and PMU_RXD1 (SMBus I/O Base) to an appropriate
+ * I/O port address, so that all registers in SMBus I/O port can be
+ * accessed.
+ */
+
+ reg8 = pci_read_config8(dev, 0xd2);
+ /* Enable SMBus controller */
+ reg8 |= 1;
+ /* Set SMBUS clock from 128k source */
+ reg8 |= 1<<2;
+ pci_write_config8(dev, 0xd2, reg8);
+
+ reg8 = pci_read_config8(dev, 0x94);
+ /* SMBUS clock from divider of 14.318 MHz */
+ reg8 &= ~(1<<7);
+ pci_write_config8(dev, 0x94, reg8);
+
+ /* Set SMBus IO base */
+ pci_write_config16(dev, 0xd0, SMBUS_IO_BASE);
+
+ /*
+ * Initialize the SMBus sequence:
+ */
+ /* Clear SMBus host status register */
+ smbus_reset();
+ /* Clear SMBus host data 0 register */
+ outb(0x00, SMBHSTDAT0);
+
+ /* Wait for SMBUS */
+ smbus_wait_until_ready();
+
+}
+
+void spd_read(u8 addr, spd_raw_data spd)
+{
+ u8 reg;
+ int i, regs;
+ reg = smbus_read_byte(addr, 2);
+ if(reg != 0x0b)
+ {
+ printk(BIOS_DEBUG, "SMBUS device %x not a DDR3 module\n", addr);
+ spd[2] = 0;
+ return;
+ }
+
+ reg = smbus_read_byte(addr, 0);
+ reg &= 0xf;
+ if (reg == 0x3) {
+ regs = 256;
+ } else if (reg == 0x2) {
+ regs = 176;
+ } else if (reg == 0x1) {
+ regs = 128;
+ } else {
+ printk(BIOS_INFO, "No DIMM present at %x\n", addr);
+ spd[2] = 0;
+ return;
+ }
+ printk(BIOS_DEBUG, "SPD Data for DIMM %x \n", addr);
+ for (i = 0; i < regs; i++) {
+ reg = smbus_read_byte(addr, i);
+ //printk(BIOS_DEBUG, " Offset %u = 0x%x \n", i, reg );
+ spd[i] = reg;
+ }
+}
+
+void dump_spd_data(void)
+{
+ int dimm, offset, regs;
+ unsigned int reg;
+ spd_raw_data spd;
+ dimm_attr dimmx;
+
+ for (dimm = 0x50; dimm < 0x52; dimm++) {
+ reg = smbus_read_byte(dimm, 2);
+ if(reg != 0x0b)
+ {
+ printk(BIOS_DEBUG,
+ "SMBUS device %x not a DDR3 module\n", dimm);
+ continue;
+ }
+
+ reg = smbus_read_byte(dimm, 0);
+ reg &= 0xf;
+ if (reg == 0x3) {
+ regs = 256;
+ } else if (reg == 0x2) {
+ regs = 176;
+ } else if (reg == 0x1) {
+ regs = 128;
+ } else {
+ printk(BIOS_INFO, "No DIMM present at %x\n", dimm);
+ regs = 0;
+ continue;
+ }
+ printk(BIOS_DEBUG, "SPD Data for DIMM %x \n", dimm);
+ for (offset = 0; offset < regs; offset++) {
+ reg = smbus_read_byte(dimm, offset);
+ //printk(BIOS_DEBUG, " Offset %u = 0x%x \n", offset, reg );
+ spd[offset] = reg;
+ }
+
+ spd_decode_ddr3(&dimmx, spd);
+ dram_print_spd_ddr3(&dimmx);
+
+ }
+}
+
diff --git a/src/northbridge/via/vx900/early_vx900.h b/src/northbridge/via/vx900/early_vx900.h
new file mode 100644
index 0000000..6b33077
--- /dev/null
+++ b/src/northbridge/via/vx900/early_vx900.h
@@ -0,0 +1,35 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 Alexandru Gagniuc <mr.nuke.me at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EARLY_VX900_H
+#define EARLY_VX900_H
+
+#include <stdint.h>
+
+#define SMBUS_IO_BASE 0x500
+#include <devices/smbus/smbus.h>
+
+
+#include "raminit.h"
+
+void enable_smbus(void);
+void dump_spd_data(void);
+void spd_read(u8 addr, spd_raw_data spd);
+
+#endif /* EARLY_VX900_H */
diff --git a/src/northbridge/via/vx900/forgotten.c b/src/northbridge/via/vx900/forgotten.c
new file mode 100644
index 0000000..37aae81
--- /dev/null
+++ b/src/northbridge/via/vx900/forgotten.c
@@ -0,0 +1,192 @@
+#include "forgotten.h"
+#include <../../dram/dram.h>
+
+u16 ddr3_get_mr0(
+ char precharge_pd,
+ u8 write_recovery,
+ char dll_reset,
+ char mode,
+ u8 cas,
+ char interleaved_burst,
+ u8 burst_lenght
+){
+ u32 cmd = 0;
+ if(precharge_pd) cmd |= (1 << 12);
+ /* Write recovery */
+ cmd |= ( ((write_recovery - 4) & 0x7) << 9);
+ if(dll_reset) cmd |= (1 << 8);
+ if(mode) cmd |= (1<<7);
+ /* CAS latency) */
+ cmd |= ( ((cas - 4) & 0x7) << 4);
+ if(interleaved_burst) cmd |= (1 << 3);
+ /* Burst lenght */
+ cmd |= (burst_lenght & 0x3);
+ return cmd;
+}
+
+u16 ddr3_get_mr1(
+ char q_off_disable,
+ char tdqs,
+ u8 rtt_nom,
+ char write_leveling,
+ u8 output_drive_strenght,
+ u8 additive_latency,
+ u8 dll_disable
+){
+ u32 cmd = 0;
+ if(q_off_disable) cmd |= (1 << 12);
+ if(tdqs) cmd |= (1 << 11);
+ /* rtt_nom */
+ cmd |= ( ((rtt_nom & 4) << (9-2)) | ((rtt_nom & 2) << (6-1))
+ | ((rtt_nom & 1) << 2));
+ if(write_leveling) cmd |= (1 << 7);
+ /* output drive strenght */
+ cmd |= ( ((output_drive_strenght & 2) << (5-1))
+ | ((output_drive_strenght & 1) << 1) );
+ /* Additive latency */
+ cmd |= ((additive_latency & 0x3) << 3);
+ if(dll_disable) cmd |= (1 << 0);
+ return cmd;
+}
+
+u16 ddr3_get_mr2(
+ u8 rtt_wr,
+ char extended_temp,
+ char auto_self_refresh,
+ u8 cas_write
+){
+ u32 cmd = 0;
+ /* Rtt_wr */
+ cmd |= ( (rtt_wr & 0x3) << 9);
+ if(extended_temp) cmd |= (1 << 7);
+ if(auto_self_refresh) cmd |= (1 << 6);
+ /* CAS write latency */
+ cmd |= ( ((cas_write - 5) & 0x7) << 3);
+ return cmd;
+}
+
+u16 ddr3_get_mr3(char dataflow_from_mpr)
+{
+ u32 cmd = 0;
+ if(dataflow_from_mpr) cmd |= (1<<2);
+ return cmd;
+}
+
+/*
+ * Translate the MRS command into the memory address corresponding to the
+ * command. This is based on the CPU address to memory address mapping described
+ * by the initial values of registers 0x52 and 0x53, so do not fuck with them
+ * until after the MRS commands have been sent to all ranks
+ */
+
+u32 vx900_get_mrs_addr(u8 mrs_type, u16 cmd);
+
+u32 vx900_get_mrs_addr(u8 mrs_type, u16 cmd)
+{
+ u32 addr = 0;
+ /* A3 <-> MA0, A4 <-> MA1, ... A12 <-> MA9 */
+ addr |= ((cmd &0x3ff)<< 3);
+ /* A20 <-> MA10 */
+ addr |= (((cmd >> 10) & 0x1) << 20);
+ /* A13 <-> MA11, A14 <-> MA12 */
+ addr |= (((cmd >> 11) & 0x3) << 13);
+
+ /* Do not fuck with registers 0x52 and 0x53 if you want the following
+ * mappings to work for you:
+ * A17 <-> BA0, A18 <-> BA1, A19 <-> BA2 */
+ addr |= ((mrs_type & 0x7) << 17);
+ return addr;
+}
+void vx900_dram_ddr3_init_rank(device_t mcu, const ramctr_timing *ctrl,
+ int rank);
+void vx900_dram_ddr3_init_rank(device_t mcu, const ramctr_timing *ctrl,
+ int rank)
+{
+ u8 reg8;
+ u32 reg32, cmd, res, addr;
+ int i;
+
+ printram("Initializing rank %u\n", rank);
+
+ /* Step 06 - Set Fun3_RX6B[2:0] to 001b (NOP Command Enable). */
+ reg8 = pci_read_config8(mcu, 0x6b);
+ reg8 &= ~(0x07);
+ reg8 |= (1<<0);
+ pci_write_config8(mcu, 0x6b, reg8);
+ /* Step 07 - Read a double word from any address of the DIMM. */
+ reg32 = volatile_read(0x0);
+ udelay(10);
+ printram("We just read 0x%x\n", reg32);
+ /* Step 08 - Set Fun3_RX6B[2:0] to 011b (MSR Enable). */
+ reg8 = pci_read_config8(mcu, 0x6b);
+ reg8 &= ~(0x07);
+ reg8 |= (3<<0);
+ pci_write_config8(mcu, 0x6b, reg8);
+
+ /* Step 09 – Issue MR2 cycle. Read a double word from the address depended
+ * on DRAM’s Rtt_WR and CWL settings.
+ * (Check the byte 63 of SPD. If memory address lines of target physical
+ * rank is mirrored, MA3~MA8 and BA0~BA1 should be swapped.) */
+ cmd = ddr3_get_mr2(DDR3_MR2_RTT_WR_OFF,0,0,ctrl->CWL);
+ addr = vx900_get_mrs_addr(2, cmd);
+ if(addr != 0x00040040){
+ printram("MR2 needed at addr 0x%.8x, but done at 0x%.8x\n", 0x00040040, addr);
+ }
+ res = volatile_read(addr);
+ udelay(10);
+ printram("MR2 cycle 0x%.4x @ addr 0x%.8x read 0x%.8x\n", cmd, addr, res);
+
+ /* Step 10 – Issue MR3 cycle - set DRAM to normal operation mode.
+ */
+ cmd = ddr3_get_mr3(0);
+ addr = vx900_get_mrs_addr(3, cmd);
+ res = volatile_read(addr);
+ udelay(10);
+ printram("MR3 cycle 0x%.4x @ addr 0x%.8x read 0x%.8x\n", cmd, addr, res);
+
+ /* Step 11 –Issue MR1 cycle
+ * Set DRAM’s output driver impedance, and Rtt_Nom settings.
+ * * The DLL enable field, TDQS field, write leveling enable field,
+ * additive latency field, and Qoff field should be set to 0.
+ */
+ cmd = ddr3_get_mr1(DDR3_MR1_QOFF_ENABLE, DDR3_MR1_TQDS_DISABLE,
+ DDR3_MR1_RTT_NOM_RZQ2,
+ DDR3_MR1_WRITE_LEVELING_DISABLE, DDR3_MR1_ODS_RZQ7,
+ DDR3_MR1_AL_DISABLE, DDR3_MR1_DLL_ENABLE);
+ addr = vx900_get_mrs_addr(1, cmd);
+ res = volatile_read(addr);
+ udelay(10);
+ printram("MR1 cycle 0x%.4x @ addr 0x%.8x read 0x%.8x\n", cmd, addr, res);
+
+ /* Step 12 - Issue MR0 cycle.
+ * Set DRAM’s burst length, CAS latency and write recovery settings.
+ * * The read burst type field should be set to interleave.
+ * * The mode field should be set to normal mode.
+ * * The DLL reset field should be set to No.
+ * * The DLL control for precharge PD field should be set to Fast exit.
+ */
+ cmd = ddr3_get_mr0(DDR3_MR0_PRECHARGE_FAST, ctrl->WR,
+ DDR3_MR0_DLL_RESET_NO, DDR3_MR0_MODE_NORMAL,
+ ctrl->CAS, DDR3_MR0_BURST_TYPE_INTERLEAVED,
+ DDR3_MR0_BURST_LENGTH_CHOP);
+ addr = vx900_get_mrs_addr(0, cmd);
+ if(addr != 0x000069c8){
+ printram("MR0 needed at addr 0x%.8x, but done at 0x%.8x\n", 0x000069c8, addr);
+ addr = 0x000069c8;
+ }
+ res = volatile_read(addr);
+ printram("MR0 cycle 0x%.4x @ addr 0x%.8x read 0x%.8x\n", cmd, addr, res);
+
+ /* Step 13 - Set Rx6B[2:0] to 110b (Long ZQ calibration command). */
+ reg8 = pci_read_config8(mcu, 0x6b);
+ reg8 &= ~(0x07);
+ reg8 |= (3<<1);
+ pci_write_config8(mcu, 0x6b, reg8);
+ for(i = 0; i < 1000; i++) inb(0x80);
+
+
+ /* Step 14 - Read a double word from any address of the DIMM. */
+ reg32 = volatile_read(0x0);
+ printram("We just read 0x%x\n", reg32);
+}
+
diff --git a/src/northbridge/via/vx900/forgotten.h b/src/northbridge/via/vx900/forgotten.h
new file mode 100644
index 0000000..43641f7
--- /dev/null
+++ b/src/northbridge/via/vx900/forgotten.h
@@ -0,0 +1,78 @@
+#ifndef REDUNDANT_H
+#define REDUNDANT_H
+
+#define DDR3_MR0_PRECHARGE_SLOW 0
+#define DDR3_MR0_PRECHARGE_FAST 1
+#define DDR3_MR0_MODE_NORMAL 0
+#define DDR3_MR0_MODE_TEST 1
+#define DDR3_MR0_DLL_RESET_NO 0
+#define DDR3_MR0_DLL_RESET_YES 1
+#define DDR3_MR0_BURST_TYPE_SEQUENTIAL 0
+#define DDR3_MR0_BURST_TYPE_INTERLEAVED 1
+#define DDR3_MR0_BURST_LENGTH_FIXED_8 0
+#define DDR3_MR0_BURST_LENGTH_CHOP 1
+#define DDR3_MR0_BURST_LENGTH_FIXED_4 2
+/**
+ * \brief Get command address for a DDR3 MR0 command
+ */
+u16 ddr3_get_mr0(
+ char precharge_pd,
+ u8 write_recovery,
+ char dll_reset,
+ char mode,
+ u8 cas,
+ char interleaved_burst,
+ u8 burst_lenght
+);
+
+#define DDR3_MR1_TQDS_DISABLE 0
+#define DDR3_MR1_TQDS_ENABLE 1
+#define DDR3_MR1_QOFF_ENABLE 0
+#define DDR3_MR1_QOFF_DISABLE 1
+#define DDR3_MR1_WRITE_LEVELING_DISABLE 0
+#define DDR3_MR1_WRITE_LEVELING_ENABLE 1
+#define DDR3_MR1_RTT_NOM_OFF 0
+#define DDR3_MR1_RTT_NOM_RZQ4 1
+#define DDR3_MR1_RTT_NOM_RZQ2 2
+#define DDR3_MR1_RTT_NOM_RZQ6 3
+#define DDR3_MR1_RTT_NOM_RZQ12 4
+#define DDR3_MR1_RTT_NOM_RZQ8 5
+#define DDR3_MR1_AL_DISABLE 0
+#define DDR3_MR1_AL_CL_MINUS_1 1
+#define DDR3_MR1_AL_CL_MINUS_2 2
+#define DDR3_MR1_ODS_RZQ6 0
+#define DDR3_MR1_ODS_RZQ7 1
+#define DDR3_MR1_DLL_ENABLE 0
+#define DDR3_MR1_DLL_DISABLE 1
+/**
+ * \brief Get command address for a DDR3 MR1 command
+ */
+u16 ddr3_get_mr1(
+ char q_off,
+ char tdqs,
+ u8 rtt_nom,
+ char write_leveling,
+ u8 output_drive_strenght,
+ u8 additive_latency,
+ u8 dll_disable
+);
+
+#define DDR3_MR2_RTT_WR_OFF 0
+#define DDR3_MR2_RTT_WR_RZQ4 1
+#define DDR3_MR2_RTT_WR_RZQ2 2
+/**
+ * \brief Get command address for a DDR3 MR2 command
+ */
+u16 ddr3_get_mr2(
+ u8 rtt_wr,
+ char extended_temp,
+ char auto_self_refresh,
+ u8 cas_write
+);
+
+/**
+ * \brief Get command address for a DDR3 MR3 command
+ */
+u16 ddr3_get_mr3(char dataflow_from_mpr);
+
+#endif /* REDUNDANT_H */
\ No newline at end of file
diff --git a/src/northbridge/via/vx900/raminit.h b/src/northbridge/via/vx900/raminit.h
new file mode 100644
index 0000000..6268070
--- /dev/null
+++ b/src/northbridge/via/vx900/raminit.h
@@ -0,0 +1,77 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 Alexandru Gagniuc <mr.nuke.me at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RAMINIT_VX900_H
+#define RAMINIT_VX900_H
+
+#include <devices/dram/dram.h>
+
+/* The maximum number of DIMM slots that the VX900 supports */
+#define VX900_MAX_DIMM_SLOTS 2
+
+#define VX900_MAX_MEM_RANKS 4
+
+
+#define SPD_END_LIST 0xff
+
+typedef struct dimm_layout_st
+{
+ /* The address of the DIMM on the SMBUS *
+ * 0xFF to terminate the array*/
+ u8 spd_addr[VX900_MAX_DIMM_SLOTS + 1];
+} dimm_layout;
+
+typedef struct dimm_info_st
+{
+ dimm_attr dimm[VX900_MAX_DIMM_SLOTS];
+} dimm_info;
+
+typedef struct mem_rank_st {
+ u16 start_addr;
+ u16 end_addr;
+}mem_rank;
+
+typedef struct rank_layout_st {
+ u32 phys_rank_size[VX900_MAX_MEM_RANKS];
+ mem_rank virt[VX900_MAX_MEM_RANKS];
+} rank_layout;
+
+typedef struct pci_reg8_st {
+ u8 addr;
+ u8 val;
+} pci_reg8;
+
+typedef u8 timing_dly[8];
+
+typedef struct delay_range_st {
+ timing_dly low;
+ timing_dly avg;
+ timing_dly high;
+} delay_range;
+
+typedef struct vx900_delay_calib_st {
+ delay_range rx_dq_cr;
+ delay_range rx_dqs;
+ delay_range tx_dq;
+ delay_range tx_dqs;
+} vx900_delay_calib;
+
+void vx900_init_dram_ddr3(const dimm_layout *dimms);
+
+#endif /* RAMINIT_VX900_H */
diff --git a/src/northbridge/via/vx900/raminit_ddr3.c b/src/northbridge/via/vx900/raminit_ddr3.c
new file mode 100644
index 0000000..f1ca45a
--- /dev/null
+++ b/src/northbridge/via/vx900/raminit_ddr3.c
@@ -0,0 +1,1063 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011-2012 Alexandru Gagniuc <mr.nuke.me at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <arch/io.h>
+#include <arch/romcc_io.h>
+
+#include "early_vx900.h"
+#include "raminit.h"
+
+#include <string.h>
+#include <console/console.h>
+#include <device/pci_ids.h>
+
+#include <delay.h>
+
+#define min(a,b) a<b?a:b
+#define max(a,b) a>b?a:b
+
+#define MCU PCI_DEV(0, 0, 3)
+
+
+/* FIXME: Really Alex, is this all you can come up with? */
+void udelay(unsigned usecs)
+{
+ int i;
+ for(i = 0; i < usecs; i++)
+ inb(0x80);
+}
+
+/* Registers 0x78 -> 0x7f contain calibration the settings for DRAM IO timing
+ * The dataset in these registers is selected from 0x70.
+ * Once the correct dataset is selected the delays can be altered.
+ * mode refers to TxDQS, TxDQ, RxDQS, or RxCR
+ * setting refers to either manual, average, upper bound, or lower bound
+ */
+#define VX900_CALIB_TxDQS 0
+#define VX900_CALIB_TxDQ 1
+#define VX900_CALIB_RxDQS 2
+#define VX900_CALIB_RxDQ_CR 3
+
+#define VX900_CALIB_AVERAGE 0
+#define VX900_CALIB_LOWER 1
+#define VX900_CALIB_UPPER 2
+#define VX900_CALIB_MANUAL 4
+
+static void vx900_delay_calib_mode_select(u8 delay_type, u8 bound)
+{
+ /* Which calibration setting */
+ u8 reg8 = (delay_type & 0x03) << 2;
+ /* Upper, lower, average, or manual setting */
+ reg8 |= (bound & 0x03);
+ pci_write_config8(MCU, 0x70, reg8);
+}
+
+static void vx900_read_0x78_0x7f(timing_dly dly)
+{
+ *((u32*) (&(dly[0]))) = pci_read_config32(MCU, 0x78);
+ *((u32*) (&(dly[4]))) = pci_read_config32(MCU, 0x7c);
+}
+
+static void vx900_write_0x78_0x7f(const timing_dly dly)
+{
+ pci_write_config32(MCU, 0x78, *((u32*) (&(dly[0]))) );
+ pci_write_config32(MCU, 0x7c, *((u32*) (&(dly[4]))) );
+}
+
+static void vx900_read_delay_range(delay_range *d_range, u8 mode)
+{
+ vx900_delay_calib_mode_select(mode, VX900_CALIB_LOWER);
+ vx900_read_0x78_0x7f(d_range->low);
+ vx900_delay_calib_mode_select(mode, VX900_CALIB_AVERAGE);
+ vx900_read_0x78_0x7f(d_range->avg);
+ vx900_delay_calib_mode_select(mode, VX900_CALIB_UPPER);
+ vx900_read_0x78_0x7f(d_range->high);
+}
+
+static void dump_delay(const timing_dly dly)
+{
+ u8 i;
+ for(i = 0; i < 8; i ++)
+ {
+ printram(" %.2x", dly[i]);
+ }
+ printram("\n");
+}
+
+static void dump_delay_range(const delay_range d_range)
+{
+ printram("Lower limit: ");
+ dump_delay(d_range.low);
+ printram("Average: ");
+ dump_delay(d_range.avg);
+ printram("Upper limit: ");
+ dump_delay(d_range.high);
+}
+
+/* These are some "safe" values that can be used for memory initialization.
+ * Some will stay untouched, and others will be overwritten later on
+ * YOU REALLY NEED THE DATASHEET TO UNDERSTAND THESE !!! */
+static pci_reg8 mcu_init_config[] = {
+ {0x40, 0x01}, /* Virtual rank 0 ending address = 64M - 1 */
+ {0x41, 0x00}, {0x42, 0x00}, {0x43, 0x00}, /* Virtual Ranks ending */
+ {0x48, 0x00}, /* Virtual rank 0 starting address = 0 */
+ {0x49, 0x00}, {0x4a, 0x00}, {0x4b, 0x00}, /* Virtual Ranks beginning */
+ {0x50, 0xd8}, /* Set ranks 0-3 to 11 col bits, 16 row bits */
+ {0x52, 0x33}, /* Map BA0 to A17, BA1 to A18 */
+ {0x53, 0x5b}, /* Map BA2 to A19, FIXME: check RA0/RA1 */
+ /* Disable all virtual ranks */
+ {0x54, 0x00}, {0x55, 0x00}, {0x56, 0x00}, {0x57, 0x00},
+ /* Disable rank interleaving in ranks 0-3 */
+ {0x58, 0x00}, {0x59, 0x00}, {0x5a, 0x00}, {0x5b, 0x00},
+ {0x6c, 0xA0}, /* Memory type: DDR3, VDIMM: 1.5V, 64-bit DRAM */
+ {0xc4, 0x80}, /* Enable 8 memory banks */
+ {0xc6, 0x80}, /* Minimum latency from self-refresh. Bit [7] must be 1 */
+ //{0xc8, 0x80}, /* Enable automatic triggering of short ZQ calibration */
+ {0x99, 0xf0}, /* Power Management and Bypass Reorder Queue */
+ /* Enable differential DQS; MODT assertion values suggested in DS */
+ {0x9e, 0xa1}, {0x9f, 0x51},
+ /* DQ/DQM Duty Control - Do not put any extra delays*/
+ {0xe9, 0x00}, {0xea, 0x00}, {0xeb, 0x00}, {0xec, 0x00},
+ {0xed, 0x00}, {0xee, 0x00}, {0xef, 0x00},
+ {0xfc, 0x00}, {0xfd, 0x00}, {0xfe, 0x00}, {0xff, 0x00},
+ /* The following parameters we may or may not change */
+ {0x61, 0x2e}, /* DRAMC Pipeline Control */
+ {0x77, 0x10}, /* MDQS Output Control */
+
+ /* The following are parameters we'll most likely never change again */
+ {0x60, 0xf4}, /* DRAM Pipeline Turn-Around Setting */
+ {0x65, 0x49}, /* DRAM Arbitration Bandwidth Timer - I */
+ {0x66, 0x80}, /* DRAM Queue / Arbitration */
+ {0x69, 0xc6}, /* Bank Interleave Control */
+ {0x6a, 0xfc}, /* DRAMC Request Reorder Control */
+ {0x6e, 0x38}, /* Burst lenght: 8, burst-chop: enable */
+ {0x73, 0x04}, /* Close All Pages Threshold */
+
+ /* The following need to be dynamically asserted */
+ /* See: check_special_registers.c */
+ {0x74, 0xa0}, /* Yes, same 0x74; add one more T */
+ {0x76, 0x60}, /* Write Data Phase Control */
+
+};
+
+/* This table keeps the driving strength control setting that we can safely use
+ * doring initialization. */
+static pci_reg8 mcu_drv_ctrl_config[] = {
+ {0xd3, 0x03}, /* Enable auto-compensation circuit for ODT strength */
+ {0xd4, 0x80}, /* Set internal ODT to dynamically turn on or off */
+ {0xd6, 0x20}, /* Enable strong driving for MA and DRAM commands*/
+ {0xd0, 0x88}, /* (ODT) Strength ?has effect? */
+ {0xe0, 0x88}, /* DRAM Driving – Group DQS (MDQS) */
+ {0xe1, 0x00}, /* Disable offset mode for driving strength control */
+ {0xe2, 0x88}, /* DRAM Driving – Group DQ (MD, MDQM) */
+ {0xe4, 0xcc}, /* DRAM Driving – Group CSA (MCS, MCKE, MODT) */
+ {0xe8, 0x88}, /* DRAM Driving – Group MA (MA, MBA, MSRAS, MSCAS, MSWE)*/
+ {0xe6, 0xff}, /* DRAM Driving – Group DCLK0 (DCLK[2:0] for DIMM0) */
+ {0xe7, 0xff}, /* DRAM Driving – Group DCLK1 (DCLK[5:3] for DIMM1) */
+ {0xe4, 0xcc}, /* DRAM Driving – Group CSA (MCS, MCKE, MODT)*/
+ {0x91, 0x08}, /* MCLKO Output Phase Delay - I */
+ {0x92, 0x08}, /* MCLKO Output Phase Delay - II */
+ {0x93, 0x16}, /* CS/CKE Output Phase Delay */
+ {0x95, 0x16}, /* SCMD/MA Output Phase Delay */
+ {0x9b, 0x3f}, /* Memory Clock Output Enable */
+};
+
+static void vx900_dram_write_init_config(void)
+{
+ size_t i;
+ for(i = 0; i < (sizeof(mcu_init_config)/sizeof(pci_reg8)); i++)
+ {
+ pci_write_config8(MCU, mcu_init_config[i].addr,
+ mcu_init_config[i].val);
+ }
+}
+
+static void dram_find_spds_ddr3(const dimm_layout *addr, dimm_info *dimm)
+{
+ size_t i = 0;
+ int dimms = 0;
+ do {
+ spd_raw_data spd;
+ spd_read(addr->spd_addr[i], spd);
+ spd_decode_ddr3(&dimm->dimm[i], spd);
+ if(dimm->dimm[i].dram_type != DRAM_TYPE_DDR3) continue;
+ dimms++;
+ dram_print_spd_ddr3(&dimm->dimm[i]);
+ } while(addr->spd_addr[++i] != SPD_END_LIST
+ && i < VX900_MAX_DIMM_SLOTS);
+
+ if(!dimms)
+ die("No DIMMs were found");
+}
+
+static void dram_find_common_params(const dimm_info *dimms, ramctr_timing *ctrl)
+{
+ size_t i, valid_dimms;
+ memset(ctrl, 0, sizeof(ramctr_timing));
+ ctrl->cas_supported = 0xff;
+ valid_dimms = 0;
+ for(i = 0; i < VX900_MAX_DIMM_SLOTS; i++)
+ {
+ const dimm_attr *dimm = &dimms->dimm[i];
+ if(dimm->dram_type == DRAM_TYPE_UNDEFINED) continue;
+ valid_dimms++;
+
+ if(valid_dimms == 1) {
+ /* First DIMM defines the type of DIMM */
+ ctrl->dram_type = dimm->dram_type;
+ } else {
+ /* Check if we have mismatched DIMMs */
+ if(ctrl->dram_type != dimm->dram_type)
+ die("Mismatched DIMM Types");
+ }
+ /* Find all possible CAS combinations */
+ ctrl->cas_supported &= dimm->cas_supported;
+
+ /* Find the smallest common latencies supported by all DIMMs */
+ ctrl->tCK = max(ctrl->tCK, dimm->tCK );
+ ctrl->tAA = max(ctrl->tAA, dimm->tAA );
+ ctrl->tWR = max(ctrl->tWR, dimm->tWR );
+ ctrl->tRCD = max(ctrl->tRCD, dimm->tRCD);
+ ctrl->tRRD = max(ctrl->tRRD, dimm->tRRD);
+ ctrl->tRP = max(ctrl->tRP, dimm->tRP );
+ ctrl->tRAS = max(ctrl->tRAS, dimm->tRAS);
+ ctrl->tRC = max(ctrl->tRC, dimm->tRC );
+ ctrl->tRFC = max(ctrl->tRFC, dimm->tRFC);
+ ctrl->tWTR = max(ctrl->tWTR, dimm->tWTR);
+ ctrl->tRTP = max(ctrl->tRTP, dimm->tRTP);
+ ctrl->tFAW = max(ctrl->tFAW, dimm->tFAW);
+
+ }
+
+ if(!ctrl->cas_supported) die("Unsupported DIMM combination. "
+ "DIMMS do not support common CAS latency");
+ if(!valid_dimms) die("No valid DIMMs found");
+}
+
+static void vx900_dram_phys_bank_range(const dimm_info *dimms,
+ rank_layout *ranks)
+{
+ size_t i;
+ //u8 reg8;
+ for(i = 0; i < VX900_MAX_DIMM_SLOTS; i ++)
+ {
+ if(dimms->dimm[i].dram_type == DRAM_TYPE_UNDEFINED)
+ continue;
+ u8 nranks = dimms->dimm[i].ranks;
+ if(nranks > 2)
+ die("Found DIMM with more than two ranks, which is not "
+ "supported by this chipset");
+ u32 size = dimms->dimm[i].size;
+ if(nranks == 2) {
+ /* Each rank holds half the capacity of the DIMM */
+ size >>= 1;
+ ranks->phys_rank_size[i<<1] = size;
+ ranks->phys_rank_size[(i<<1) | 1] = size;
+ } else {
+ /* Otherwise, everything is held in the first bank */
+ ranks->phys_rank_size[i<<1] = size;
+ ranks->phys_rank_size[(i<<1) | 1] = 0;;
+ }
+ }
+}
+
+static void vx900_dram_driving_ctrl(const dimm_info *dimm)
+{
+ size_t i, ndimms;
+ u8 val;
+
+ /* For ODT range selection, datasheet recommends
+ * when 1 DIMM present: 60 Ohm
+ * when 2 DIMMs present: 120 Ohm */
+ ndimms = 0;
+ for(i = 0; i < VX900_MAX_DIMM_SLOTS; i++) {
+ if(dimm->dimm[i].dram_type == DRAM_TYPE_DDR3) ndimms++;
+ }
+ val = (ndimms > 1) ? 0x0 : 0x1;
+ pci_write_config8(MCU, 0xd5, val << 2);
+
+
+ /* FIXME: Assert dynamically based on dimm config */
+ /* DRAM ODT Lookup Table*/
+ pci_write_config8(MCU, 0x9c, 0xe4);
+
+ for(i = 0; i < (sizeof(mcu_drv_ctrl_config)/sizeof(pci_reg8)); i++)
+ {
+ pci_write_config8(MCU, mcu_drv_ctrl_config[i].addr,
+ mcu_drv_ctrl_config[i].val);
+ }
+}
+
+static void vx900_clear_vr_map(void)
+{
+ /* Disable all ranks */
+ pci_write_config16(MCU, 0x54, 0x0000);
+}
+
+static void vx900_pr_map_all_vr3(void)
+{
+ /* Enable all ranks and set them to VR3 */
+ pci_write_config16(MCU, 0x54, 0xbbbb);
+}
+/* Map physical rank pr to virtual rank vr */
+static void vx900_map_pr_vr(u8 pr, u8 vr)
+{
+ pr &= 0x3; vr &= 0x3;
+ /* Enable rank (bit [3], and set the VR number bits [1:0] */
+ u16 val = 0x8 | vr;
+ /* Now move the value to the appropriate PR */
+ val <<= (pr * 4);
+ pci_mod_config16(MCU, 0x54, 0xf << (pr * 4), val);
+ printram("Mapping PR %u to VR %u\n", pr, vr);
+}
+
+static u8 vx900_get_CWL(u8 CAS)
+{
+ /* Get CWL based on CAS using the following rule:
+ * _________________________________________
+ * CAS: | 4T | 5T | 6T | 7T | 8T | 9T | 10T | 11T |
+ * CWL: | 5T | 5T | 5T | 6T | 6T | 7T | 7T | 8T |
+ */
+ static const u8 cas_cwl_map[] = {5, 5, 5, 6, 6, 7, 7, 8};
+ if(CAS > 11) return 8;
+ return cas_cwl_map[CAS - 4];
+}
+
+static void vx900_dram_timing(ramctr_timing *ctrl)
+{
+ /* Here we are calculating latencies, and writing them to the appropiate
+ * registers. Some registers do not take latencies from 0T, for example:
+ * CAS: 000 = 4T, 001 = 5T, 010 = 6T, etc
+ * In this example we subtract 4T from the result for CAS: (val - 4)
+ * The & 0x07 after (val - T0) just makes sure that, no matter what
+ * crazy thing may happen, we do not write outside the bits allocated
+ * in the register */
+ u8 reg8, val, tFAW, tRRD;
+ u32 val32;
+
+ /* Maximum supported DDR3 frequency is 533MHz (DDR3 1066)
+ * so make sure we cap it if we have faster DIMMs */
+ if(ctrl->tCK < TCK_533MHZ) ctrl->tCK = TCK_533MHZ;
+ val32 = (1000 << 8) / ctrl->tCK;
+ printram("Selected DRAM frequency: %u MHz\n", val32);
+
+ /* Now find the right DRAM frequency setting,
+ * and align it to the closest JEDEC standard frequency */
+ if(ctrl->tCK <= TCK_533MHZ) {val = 0x07; ctrl->tCK = TCK_533MHZ;}
+ else if(ctrl->tCK <= TCK_400MHZ) {val = 0x06; ctrl->tCK = TCK_400MHZ;}
+ else if(ctrl->tCK <= TCK_333MHZ) {val = 0x05; ctrl->tCK = TCK_333MHZ;}
+ else if(ctrl->tCK <= TCK_266MHZ) {val = 0x04; ctrl->tCK = TCK_266MHZ;}
+
+ /* Find CAS and CWL latencies */
+ val = (ctrl->tAA + ctrl->tCK -1) / ctrl->tCK;
+ printram("Minimum CAS latency : %uT\n", val);
+ /* Find lowest supported CAS latency that satisfies the minimum value */
+ while( !((ctrl->cas_supported >> (val-4))&1)
+ && (ctrl->cas_supported >> (val-4))) {
+ val++;
+ }
+ /* Is CAS supported */
+ if(!(ctrl->cas_supported & (1 << (val-4))) )
+ printram("CAS not supported\n");
+ printram("Selected CAS latency : %uT\n", val);
+ ctrl->CAS = val;
+ ctrl->CWL = vx900_get_CWL(ctrl->CAS);
+ printram("Selected CWL latency : %uT\n", ctrl->CWL);
+ /* Write CAS and CWL */
+ reg8 = ( ((ctrl->CWL - 4) &0x07) << 4 ) | ((ctrl->CAS - 4) & 0x07);
+ pci_write_config8(MCU, 0xc0, reg8);
+
+ /* Find tRCD */
+ val = (ctrl->tRCD + ctrl->tCK -1) / ctrl->tCK;
+ printram("Selected tRCD : %uT\n", val);
+ reg8 = ((val-4) & 0x7) << 4;
+ /* Find tRP */
+ val = (ctrl->tRP + ctrl->tCK -1) / ctrl->tCK;
+ printram("Selected tRP : %uT\n", val);
+ reg8 |= ((val-4) & 0x7);
+ pci_write_config8(MCU, 0xc1, reg8);
+
+ /* Find tRAS */
+ val = (ctrl->tRAS + ctrl->tCK -1) / ctrl->tCK;
+ printram("Selected tRAS : %uT\n", val);
+ reg8 = ((val-15) & 0x7) << 4;
+ /* Find tWR */
+ ctrl->WR = (ctrl->tWR + ctrl->tCK -1) / ctrl->tCK;
+ printram("Selected tWR : %uT\n", ctrl->WR);
+ reg8 |= ((ctrl->WR-4) & 0x7);
+ pci_write_config8(MCU, 0xc2, reg8);
+
+ /* Find tFAW */
+ tFAW = (ctrl->tFAW + ctrl->tCK -1) / ctrl->tCK;
+ printram("Selected tFAW : %uT\n", tFAW);
+ /* Find tRRD */
+ tRRD = (ctrl->tRRD + ctrl->tCK -1) / ctrl->tCK;
+ printram("Selected tRRD : %uT\n", tRRD);
+ val = tFAW - 4*tRRD; /* number of cycles above 4*tRRD */
+ reg8 = ((val-0) & 0x7) << 4;
+ reg8 |= ((tRRD-2) & 0x7);
+ pci_write_config8(MCU, 0xc3, reg8);
+
+ /* Find tRTP */
+ val = (ctrl->tRTP + ctrl->tCK -1) / ctrl->tCK;
+ printram("Selected tRTP : %uT\n", val);
+ reg8 = ((val & 0x3) << 4);
+ /* Find tWTR */
+ val = (ctrl->tWTR + ctrl->tCK -1) / ctrl->tCK;
+ printram("Selected tWTR : %uT\n", val);
+ reg8 |= ((val - 2) & 0x7);
+ pci_mod_config8(MCU, 0xc4, 0x3f, reg8);
+
+ /* DRAM Timing for All Ranks - VI
+ * [7:6] CKE Assertion Minimum Pulse Width
+ * We probably don't want to mess with this just yet.
+ * [5:0] Refresh-to-Active or Refresh-to-Refresh (tRFC)
+ * tRFC = (30 + 2 * [5:0])T
+ * Since we previously set RxC4[7]
+ */
+ reg8 = pci_read_config8(MCU, 0xc5);
+ val = (ctrl->tRFC + ctrl->tCK -1) / ctrl->tCK;
+ printram("Minimum tRFC : %uT\n", val);
+ if(val < 30) {
+ val = 0;
+ } else {
+ val = (val -30 + 1 ) / 2;
+ }
+ ;
+ printram("Selected tRFC : %uT\n", 30 + 2 * val);
+ reg8 |= (val & 0x3f);
+ pci_write_config8(MCU, 0xc5, reg8);
+
+ /* Where does this go??? */
+ val = (ctrl->tRC + ctrl->tCK -1) / ctrl->tCK;
+ printram("Required tRC : %uT\n", val);
+}
+
+static void vx900_dram_freq(ramctr_timing *ctrl)
+{
+ u8 val;
+
+ /* Program the DRAM frequency */
+
+ /* Step 1 - Reset the PLL */
+ pci_mod_config8(MCU, 0x90, 0x00, 0x0f);
+ /* Wait at least 10 ns; VIA code delays by 640us */
+ udelay(640);
+
+ /* Step 2 - Set target frequency */
+ if(ctrl->tCK <= TCK_533MHZ) {val = 0x07; ctrl->tCK = TCK_533MHZ;}
+ else if(ctrl->tCK <= TCK_400MHZ) {val = 0x06; ctrl->tCK = TCK_400MHZ;}
+ else if(ctrl->tCK <= TCK_333MHZ) {val = 0x05; ctrl->tCK = TCK_333MHZ;}
+ else /*ctrl->tCK <= TCK_266MHZ*/ {val = 0x04; ctrl->tCK = TCK_266MHZ;}
+ /* Restart the PLL with the desired frequency */
+ pci_mod_config8(MCU, 0x90, 0x0f, val);
+
+ /* Step 3 - Wait for PLL to stabilize */
+ udelay(2000);
+
+ /* Step 4 - Reset the DLL - Clear [7,4]*/
+ pci_mod_config8(MCU, 0x6b, 0x90, 0x00);
+ udelay(2000);
+
+ /* Step 5 - Enable the DLL - Set bits [7,4] to 01b*/
+ pci_mod_config8(MCU, 0x6b, 0x00, 0x10);
+ udelay(2000);
+
+ /* Step 6 - Start DLL Calibration - Set bit [7] */
+ pci_mod_config8(MCU, 0x6b, 0x00, 0x80);
+ udelay(5);
+
+ /* Step 7 - Finish DLL Calibration - Clear bit [7]*/
+ pci_mod_config8(MCU, 0x6b, 0x80, 0x00);
+
+ /* Step 8 - If we have registered DIMMs, we need to set bit[0] */
+ if(dimm_is_registered(ctrl->dram_type)){
+ printram("Enabling RDIMM support in memory controller\n");
+ pci_mod_config8(MCU, 0x6c, 0x00, 0x01);
+ }
+}
+
+/* The VX900 can send the MRS commands directly through hardware */
+void vx900_dram_ddr3_do_hw_mrs(u8 ma_swap, u8 rtt_nom,
+ u8 ods, u8 rtt_wr, u8 srt, u8 asr);
+/*static*/ void vx900_dram_ddr3_do_hw_mrs(u8 ma_swap, u8 rtt_nom,
+ u8 ods, u8 rtt_wr, u8 srt, u8 asr)
+{
+ u8 reg8 = 0;
+ u32 reg32;
+ if(asr) reg8 |= (1 << 0);
+ if(srt) reg8 |= (1 << 1);
+ reg8 |= ((rtt_wr & 0x03) << 4);
+ pci_write_config8(MCU, 0xcd, reg8);
+ reg8 = 1;
+ if(ma_swap) reg8 |= (1 << 1);
+ reg8 |= ((ods & 0x03) << 2);
+ reg8 |= ((rtt_nom & 0x7) << 4);
+ pci_write_config8(MCU, 0xcc, reg8);
+ reg32=0;
+ while(pci_read_config8(MCU, 0xcc) & 1) reg32++;
+ printram(" Waited %u PCI cycles for HW MRS\n", reg32);
+}
+#include "forgotten.h"
+#include "forgotten.c"
+
+static void vx900_dram_send_soft_mrs(u8 type, u16 cmd)
+{
+ u32 addr;
+ /* Set Fun3_RX6B[2:0] to 011b (MSR Enable). */
+ pci_mod_config8(MCU, 0x6b, 0x07, (3<<0));
+ /* Find the address corresponding to the MRS */
+ addr = vx900_get_mrs_addr(type, cmd);
+ //printram("MRS CPU addr: %.8x\n", addr);
+ /* Execute the MRS */
+ volatile_read(addr);
+ /* Set Fun3_Rx6B[2:0] to 000b (Normal SDRAM Mode). */
+ pci_mod_config8(MCU, 0x6b, 0x07, 0x00);
+}
+
+static void vx900_dram_ddr3_dimm_init(const ramctr_timing *ctrl,
+ const rank_layout *ranks)
+{
+ size_t i;
+
+ /* Set BA[0/1/2] to [A17/18/19] */
+ pci_write_config16(MCU, 0x52, 0x5b33);
+
+ /* Step 01 - Set Fun3_Rx6E[5] to 1b to support burst length. */
+ pci_mod_config8(MCU, 0x6e, 0, 1<<5);
+ /* Step 02 - Set Fun3_RX69[0] to 0b (Disable Multiple Page Mode). */
+ pci_mod_config8(MCU, 0x69, (1<<0), 0x00);
+ /* And set [7:6] to 10b ?*/
+ pci_write_config8(MCU, 0x69, 0x87);
+
+ /* Step 03 - Set the target physical rank to virtual rank0 and other
+ * ranks to virtual rank3. */
+ vx900_pr_map_all_vr3();
+
+ /* Step 04 - Set Fun3_Rx50 to D8h. */
+ pci_write_config8(MCU, 0x50, 0xd8);
+ /* Step 05 - Set Fun3_RX6B[5] to 1b to de-assert RESET# and wait for at
+ * least 500 us. */
+ pci_mod_config8(MCU, 0x6b, 0x00, (1<<5) );
+ udelay(500);
+
+ /* Step 6 -> 15 - Set the target physical rank to virtual rank 0 and
+ * other ranks to virtual rank 3.
+ * Repeat Step 6 to 14 for every rank present, then jump to Step 16. */
+ for(i = 0; i < VX900_MAX_MEM_RANKS; i++)
+ {
+ if(ranks->phys_rank_size[i] == 0) continue;
+ printram("Initializing rank %lu\n", i);
+
+ /* Set target physical rank to virtual rank 0
+ * other ranks to virtual rank 3*/
+ vx900_map_pr_vr(i, 0);
+
+#define USE_HW_INIT_SEQUENCE 1
+#if USE_HW_INIT_SEQUENCE
+
+ /* FIXME: Is this needed on HW init? */
+ pci_mod_config8(MCU, 0x6b, 0x07, 0x01); /* Enable NOP */
+ volatile_read(0x0); /* Do NOP */
+ pci_mod_config8(MCU, 0x6b, 0x07, 0x03); /* MSR Enable */
+
+ /* FIXME: Values dependent on DIMM setup
+ * This works for 1 DIMM
+ * See init_dram_by_rank.c and get_basic_information.c
+ * in the VIA provided code */
+ const u8 rtt_nom = DDR3_MR1_RTT_NOM_RZQ2;
+ const u8 ods = DDR3_MR1_ODS_RZQ6;
+ const u8 rtt_wr = DDR3_MR2_RTT_WR_OFF;
+
+ printram("Using Hardware method\n");
+ /* FIXME: MA swap dependent on module */
+ vx900_dram_ddr3_do_hw_mrs(0, rtt_nom, ods, rtt_wr, 0, 0);
+
+ /* Normal SDRAM Mode */
+ pci_mod_config8(MCU, 0x6b, 0x07, 0x00);
+
+#else /* USE_HW_INIT_SEQUENCE */
+ printram("Using software method\n");
+ vx900_dram_ddr3_init_rank(MCU, ctrl, i);
+#endif /* USE_HW_INIT_SEQUENCE */
+ /* Step 15, set the rank to virtual rank 3*/
+ vx900_map_pr_vr(i, 3);
+ }
+
+ /* Step 16 – Set Fun3_Rx6B[2:0] to 000b (Normal SDRAM Mode). */
+ pci_mod_config8(MCU, 0x6b, 0x07, 0x00);
+
+ /* Set BA[0/1/2] to [A13/14/15] */
+ pci_mod_config16(MCU, 0x52, 0x0fff, 0x0911);
+
+ /* Step 17 – Set Fun3_Rx69[0] to 1b (Enable Multiple Page Mode). */
+ pci_mod_config8(MCU, 0x69, 0x00, (1<<0) );
+
+ printram("DIMM initialization sequence complete\n");
+}
+
+
+static void vx900_rx_capture_range_calib(void)
+{
+ u8 reg8;
+ u16 cmd;
+ const u32 cal_addr = 0x20;
+
+ /* Set IO calibration address */
+ pci_mod_config16(MCU, 0x8c , 0xfff0, cal_addr&(0xfff0));
+ /* Data pattern must be 0x00 for this calibration
+ * See paragraph describing Rx8e */
+ pci_write_config8(MCU, 0x8e, 0x00);
+
+ /* Precharge all */
+ pci_mod_config8(MCU, 0x06b, 0x07, 0x02);
+ volatile_read(0x0);
+ udelay(1000);
+
+ /* Enable read leveling: Set D0F3Rx71[7]=1
+ * Set return data format: Clear D0F3Rx71[6]=0 */
+ pci_mod_config8(MCU, 0x71, 0x40, 0x80);
+
+ /* Put DRAM in read leveling mode */
+ cmd = ddr3_get_mr3(1);
+ vx900_dram_send_soft_mrs(3, cmd);
+
+ /* Data pattern must be 0x00 for this calibration
+ * See paragraph describing Rx8e */
+ pci_write_config8(MCU, 0x8e, 0x00);
+ /* Trigger calibration */
+ reg8 = 0xa0;
+ pci_write_config8(MCU, 0x71, reg8);
+
+ /* Wait for it */
+ while(pci_read_config8(MCU, 0x71) & 0x10) cmd++;
+ printram("RX capture range calibration took %u PCI cycles\n", cmd);
+
+ /* Disable read leveling, and put dram in normal operation mode */
+ cmd = ddr3_get_mr3(0);
+ vx900_dram_send_soft_mrs(3, cmd);
+
+ /* Disable read leveling: Set D0F3Rx71[7]=0 */
+ pci_mod_config8(MCU, 0x71, 1<<7, 0);
+}
+
+static void vx900_rx_dqs_delay_calib(void)
+{
+ u16 cmd;
+ const u32 cal_addr = 0x30;
+
+ /* We need to disable refresh commands so that they don't interfere */
+ const u8 ref_cnt = pci_read_config8(MCU, 0xc7);
+ pci_write_config8(MCU, 0xc7, 0);
+ /* Set IO calibration address */
+ pci_mod_config16(MCU, 0x8c , 0xfff0, cal_addr&(0xfff0));
+ /* Data pattern must be 0x00 for this calibration
+ * See paragraph describing Rx8e */
+ pci_write_config8(MCU, 0x8e, 0x00);
+
+ /* Precharge all */
+ pci_mod_config8(MCU, 0x06b, 0x07, 0x02);
+ volatile_read(0x0);
+ udelay(1000);
+
+ /* Enable read leveling: Set D0F3Rx71[7]=1
+ * Set return data format: Clear D0F3Rx71[6]=0
+ * Set RxDQS to use calibration setting - Clear Rx71[2] and Rx71[0] */
+ pci_mod_config8(MCU, 0x71, 0x45, 0x80);
+
+ /* Put DRAM in read leveling mode (enable MPR flow) */
+ cmd = ddr3_get_mr3(1);
+ vx900_dram_send_soft_mrs(3, cmd);
+
+ /* From VIA code; Undocumented
+ * In theory this enables MODT[3:0] to be asserted*/
+ pci_mod_config8(MCU, 0x9e, 0, 0x80);
+
+ /* Trigger calibration: Set D0F3Rx71[1:0]=10b */
+ pci_mod_config8(MCU, 0x71, 0x03, 0x02);
+
+ /* Wait for calibration to complete */
+ while( pci_read_config8(MCU, 0x71) & 0x02 );
+
+ /* Put DRAM in normal mode (disable MPR flow) */
+ cmd = ddr3_get_mr3(0);
+ vx900_dram_send_soft_mrs(3, cmd);
+
+ /* Disable read leveling: Set D0F3Rx71[7]=0 */
+ pci_mod_config8(MCU, 0x71, 1<<7, 0);
+
+ /* Restore the refresh counter*/
+ //pci_write_config8(MCU, 0xc7, ref_cnt);
+ if(ref_cnt);
+
+ /* FIXME: should we save it before, or should we just set it as is */
+ pci_write_config16(MCU, 0x52, 0x5911);
+}
+
+static void vx900_tx_dqs_trigger_calib(u8 pattern)
+{
+ u32 i;
+ /* Data pattern for calibration */
+ pci_write_config8(MCU, 0x8e, pattern);
+ /* Trigger calibration */
+ pci_mod_config8(MCU, 0x75, 0, 0x20);
+ /* Wait for calibration */
+ i = 0;
+ while(pci_read_config8(MCU, 0x75) & 0x20) i++;
+ printram(" Tx DQS calib took %u PCI cycles\n", i);
+}
+static void vx900_tx_dqs_delay_calib(void)
+{
+ const u32 cal_addr = 0x00;
+ /* Set IO calibration address */
+ pci_mod_config16(MCU, 0x8c , 0xfff0, cal_addr&(0xfff0));
+ /* Set circuit to use calibration results - Clear Rx75[0]*/
+ pci_mod_config8(MCU, 0x75, 0x01, 0);
+ /* Run calibration with first data pattern*/
+ vx900_tx_dqs_trigger_calib(0x5a);
+ /* Run again with different pattern */
+ vx900_tx_dqs_trigger_calib(0xa5);
+}
+
+static void vx900_tx_dq_delay_calib(void)
+{
+ int i = 0;
+ /* Data pattern for calibration */
+ pci_write_config8(MCU, 0x8e, 0x5a);
+ /* Trigger calibration */
+ pci_mod_config8(MCU, 0x75, 0, 0x02);
+ /* Wait for calibration */
+ while(pci_read_config8(MCU, 0x75) & 0x02) i++;
+ printram("TX DQ calibration took %u PCI cycles\n", i);
+}
+
+static void vx900_rxdqs_adjust(delay_range *dly)
+{
+ /* Adjust Rx DQS delay after calibration has been run. This is
+ * recommended by VIA, but no explanation was provided as to why */
+ size_t i;
+ for(i = 0; i < 8; i++)
+ {
+ if(dly->low[i] < 3)
+ {
+ if(i == 2 || i== 4) dly->low[i] += 4;
+ else dly->avg[i] += 3;
+
+ }
+
+ if(dly->high[i] > 0x38) dly->avg[i] -= 6;
+ else if(dly->high[i] > 0x30) dly->avg[i] -= 4;
+
+ if(dly->avg[i] > 0x20) dly->avg[i] = 0x20;
+ }
+
+ /* Put Rx DQS delay into manual mode (Set Rx[2,0] to 01) */
+ pci_mod_config8(MCU, 0x71, 0x05, 0x01);
+ /* Now write the new settings */
+ vx900_write_0x78_0x7f(dly->avg);
+}
+
+static void vx900_dram_calibrate_delays(const ramctr_timing *ctrl,
+ const rank_layout *ranks)
+{
+ timing_dly dly;
+ size_t i;
+ u8 val;
+ vx900_delay_calib delay_cal;
+ memset(&delay_cal, 0, sizeof(delay_cal));
+ printram("Starting delay calibration\n");
+
+ /**** Read delay control ****/
+ /* MD Input Data Push Timing Control;
+ * use values recommended in datasheet
+ * Setting this too low causes the Rx window to move below the range we
+ * need it so we can capture it with Rx_78_7f
+ * This causes Rx calibrations to be too close to 0, and Tx
+ * calibrations will fail.
+ * Setting this too high causes the window to move above the range.
+ */
+ if (ctrl->tCK <= TCK_533MHZ) val = 2;
+ else if (ctrl->tCK <= TCK_333MHZ) val = 1;
+ else val = 0;
+ val ++; /* FIXME: vendor BIOS sets this to 3 */
+ pci_mod_config8(MCU, 0x74, (0x03 << 1), ((val & 0x03) << 1) );
+
+ /* FIXME: The vendor BIOS increases the MD input delay - WHY ? */
+ pci_mod_config8(MCU, 0xef, (3<<4), 3<<4);
+
+
+ /**** Write delay control ****/
+ /* FIXME: The vendor BIOS does this, but WHY?
+ * Early DQ/DQS for write cycles */
+ pci_mod_config8(MCU, 0x76, (3<<2), 2<<2);
+ /* FIXME: The vendor BIOS does this - Output preamble ?*/
+ pci_write_config8(MCU, 0x77, 0x10);
+
+ /* FIXME: Vendor BIOS goes in with
+ * 8 page registers
+ * multiple page mode
+ * High Priority Refresh request
+ * -- WHY?*/
+ pci_write_config8(MCU, 0x69, 0xc7);
+
+ /* Set BA[0/1/2] to [A17/18/19] */
+ pci_write_config16(MCU, 0x52, 0x5b33);
+ /* Disable Multiple Page Mode - Set Rx69[0] to 0 */
+ pci_mod_config8(MCU, 0x69, (1<<0), 0x00);
+
+ /* It's very important that we keep all ranks which are not calibrated
+ * mapped to VR3. Even if we disable them, if they are mapped to VR0
+ * (the rank we use for calibrations), the calibrations may fail in
+ * unexpected ways. */
+ vx900_pr_map_all_vr3();
+
+ for(i = 0; i < VX900_MAX_DIMM_SLOTS; i++)
+ {
+ /* Do we have a valid DIMM? */
+ if(ranks->phys_rank_size[i] + ranks->phys_rank_size[i+1] == 0 )
+ continue;
+
+ /* Map the first rank of the DIMM to VR0 */
+ vx900_map_pr_vr(2*i, 0);
+
+ /* Run calibrations */if(1){
+ vx900_rx_capture_range_calib();
+ vx900_read_delay_range(&(delay_cal.rx_dq_cr),
+ VX900_CALIB_RxDQ_CR);
+ dump_delay_range(delay_cal.rx_dq_cr);}
+
+ /*FIXME: Cheating with Rx CR setting
+ * We need to either use Rx CR calibration
+ * or set up a table for the calibration */
+ dly[0] = 0x28; dly[1] = 0x1c; dly[2] = 0x28; dly[3] = 0x28;
+ dly[4] = 0x2c; dly[5] = 0x30; dly[6] = 0x30; dly[7] = 0x34;
+ printram("Bypassing RxCR 78-7f calibration with:\n");
+ dump_delay(dly);
+ /* We need to put the setting on manual mode */
+ pci_mod_config8(MCU, 0x71, 0, 0x10);
+ vx900_delay_calib_mode_select(VX900_CALIB_RxDQ_CR, VX900_CALIB_MANUAL);
+ vx900_write_0x78_0x7f(dly);
+
+ /************* RxDQS *************/
+ vx900_rx_dqs_delay_calib();
+ vx900_read_delay_range(&(delay_cal.rx_dqs), VX900_CALIB_RxDQS);
+ printram("RX DQS calibration results\n");
+ dump_delay_range(delay_cal.rx_dqs);
+
+ vx900_rxdqs_adjust(&(delay_cal.rx_dqs));
+
+ vx900_read_delay_range(&(delay_cal.rx_dqs), VX900_CALIB_RxDQS);
+ printram("RX DQS calibration results after adjustment\n");
+ dump_delay_range(delay_cal.rx_dqs);
+
+ /* FIXME: Vendor BIOS does it again (enable auto-precharge) - WHY? */
+ pci_write_config8(MCU, 0x69, 0xce);
+
+ /* FIXME: this is done by vendor BIOS, and recommended by VIA
+ * However, datasheet says that bit[7] is reserved, and
+ * calibration works just as well if we don't set this to 1b .
+ * Should we really do this, or can we drop it ? */
+ if(ctrl->tCK <= TCK_533MHZ){
+ for( i = 0; i< 8; i++) dly[i] = 0x80;
+ pci_mod_config8(MCU, 0x75, 0x00, 0x01); /* manual Tx DQ DQS */
+ vx900_delay_calib_mode_select(VX900_CALIB_TxDQ, VX900_CALIB_MANUAL);
+ vx900_write_0x78_0x7f(dly);
+ vx900_delay_calib_mode_select(VX900_CALIB_TxDQS, VX900_CALIB_MANUAL);
+ vx900_write_0x78_0x7f(dly);
+ }
+
+ /************* TxDQS *************/
+ vx900_tx_dqs_delay_calib();
+
+ vx900_read_delay_range(&(delay_cal.tx_dqs), VX900_CALIB_TxDQS);
+ printram("Tx DQS calibration results\n");
+ dump_delay_range(delay_cal.tx_dqs);
+ /************* TxDQ *************/
+ /* FIXME: not sure if multiple page mode should be enabled here
+ * Vendor BIOS does it */
+ pci_mod_config8(MCU, 0x69, 0 , 0x01);
+
+ vx900_tx_dq_delay_calib();
+ vx900_read_delay_range(&(delay_cal.tx_dq), VX900_CALIB_TxDQ);
+ printram("TX DQ delay calibration results:\n");
+ dump_delay_range(delay_cal.tx_dq);
+
+ /* write manual settings */
+ pci_mod_config8(MCU, 0x75, 0, 0x01);
+ vx900_delay_calib_mode_select(VX900_CALIB_TxDQS, VX900_CALIB_MANUAL);
+ vx900_write_0x78_0x7f(delay_cal.tx_dqs.avg);
+ vx900_delay_calib_mode_select(VX900_CALIB_TxDQ, VX900_CALIB_MANUAL);
+ vx900_write_0x78_0x7f(delay_cal.tx_dq.avg);
+ }
+}
+
+static void vx900_dram_set_refresh_counter(ramctr_timing *ctrl)
+{
+ u8 reg8;
+ /* Set DRAM refresh counter
+ * Based on a refresh counter of 0x61 at 400MHz */
+ reg8 = (TCK_400MHZ * 0x61) / ctrl->tCK;
+ pci_write_config8(MCU, 0xc7, reg8);
+}
+
+static void vx900_dram_range(ramctr_timing *ctrl, rank_layout *ranks)
+{
+ size_t i, vrank = 0;
+ u8 reg8;
+ u32 ramsize = 0;
+ vx900_clear_vr_map();
+ for(i = 0; i < VX900_MAX_MEM_RANKS; i++)
+ {
+ u32 rank_size = ranks->phys_rank_size[i];
+ if(!rank_size) continue;
+ ranks->virt[vrank].start_addr = ramsize;
+ ramsize += rank_size;
+ ranks->virt[vrank].end_addr = ramsize;
+
+ /* Rank memory range */
+ reg8 = (ranks->virt[vrank].start_addr >> 2);
+ pci_write_config8(MCU, 0x48 + vrank, reg8);
+ reg8 = (ranks->virt[vrank].end_addr >> 2);
+ pci_write_config8(MCU, 0x40 + vrank, reg8);
+
+ vx900_map_pr_vr(i, vrank);
+
+ printram("Mapped Physical rank %u, to virtual rank %u\n"
+ " Start address: 0x%.8x000000\n"
+ " End address: 0x%.8x000000\n",
+ (int) i, (int) vrank,
+ ranks->virt[vrank].start_addr,
+ ranks->virt[vrank].end_addr);
+
+ /* Move on to next virtual rank */
+ vrank++;
+ }
+
+ printram("Initialized %u virtual ranks, with a total size of %u MB\n",
+ (int) vrank, ramsize << 4);
+}
+
+static void vx900_dram_write_final_config(ramctr_timing *ctrl)
+{
+
+ /* FIXME: These are quick cheats */
+ //pci_write_config8(MCU, 0x50, 0xa0); /* DRAM MA map */
+ //pci_write_config16(MCU, 0x52, 0x5911); /* Rank interleave */
+ //pci_write_config16(MCU, 0x54, 0x0800); /* Bank mapping */
+
+ //pci_write_config8(MCU, 0x69, 0xe7);
+ //pci_write_config8(MCU, 0x72, 0x0f);
+
+ //pci_write_config8(MCU, 0x97, 0xa4); /* self-refresh */
+
+ //pci_write_config8(MCU, 0xd3, 0x00); /* ODT auto-compensation */
+
+ /* Enable automatic triggering of short ZQ calibration */
+ pci_write_config8(MCU, 0xc8, 0x80);
+
+}
+
+static void print_debug_pci_dev(device_t dev)
+{
+ print_debug("PCI: ");
+ print_debug_hex8((dev >> 20) & 0xff);
+ print_debug_char(':');
+ print_debug_hex8((dev >> 15) & 0x1f);
+ print_debug_char('.');
+ print_debug_hex8((dev >> 12) & 7);
+}
+
+static void dump_pci_device(device_t dev)
+{
+ int i;
+ print_debug_pci_dev(dev);
+ print_debug("\n");
+
+ for (i = 0; i <= 0xff; i++) {
+ unsigned char val;
+ if ((i & 0x0f) == 0) {
+ print_debug_hex8(i);
+ print_debug_char(':');
+ }
+
+ if ((i & 0x0f) == 0x08) {
+ print_debug(" |");
+ }
+
+ val = pci_read_config8(dev, i);
+ print_debug_char(' ');
+ print_debug_hex8(val);
+
+ if ((i & 0x0f) == 0x0f) {
+ print_debug("\n");
+ }
+ }
+}
+
+void vx900_init_dram_ddr3(const dimm_layout *dimm_addr)
+{
+ dimm_info dimm_prop;
+ ramctr_timing ctrl_prop;
+ rank_layout ranks;
+ device_t mcu;
+
+ /* Locate the Memory controller */
+ mcu = pci_locate_device(PCI_ID(PCI_VENDOR_ID_VIA,
+ PCI_DEVICE_ID_VIA_VX900_DRAM_CTR), 0);
+
+ if (mcu == PCI_DEV_INVALID) {
+ die("Memory Controller not found\n");
+ }
+
+ //device_t HBC = PCI_DEV(0,0,2);
+ //pci_mod_config8(HBC, 0x51, 0x80, 0);
+ //pci_mod_config8(HBC, 0x54, 0, 0x20);
+
+ //pci_mod_config8(MCU, 0x97, 0, 0x80);
+
+ dump_pci_device(mcu);
+ memset(&ranks, 0, sizeof(ranks));
+ /* 1) Write some initial "safe" parameters */
+ vx900_dram_write_init_config();
+ /* 2) Get timing information from SPDs */
+ dram_find_spds_ddr3(dimm_addr, &dimm_prop);
+ /* 3) Find lowest common denominator for all modules */
+ dram_find_common_params(&dimm_prop, &ctrl_prop);
+ /* 4) Find the size of each memory rank */
+ vx900_dram_phys_bank_range(&dimm_prop, &ranks);
+ /* 5) Set DRAM driving strength */
+ vx900_dram_driving_ctrl(&dimm_prop);
+ /* 6) Set DRAM frequency and latencies */
+ vx900_dram_timing(&ctrl_prop);
+ vx900_dram_freq(&ctrl_prop);
+ /* 7) Initialize the modules themselves */
+ vx900_dram_ddr3_dimm_init(&ctrl_prop, &ranks);
+ /* 8) Set refresh counter based on DRAM frequency */
+ vx900_dram_set_refresh_counter(&ctrl_prop);
+ /* 9) Calibrate receive and transmit delays */
+ vx900_dram_calibrate_delays(&ctrl_prop, &ranks);
+ /* 10) Enable Physical to Virtual Rank mapping */
+ vx900_dram_range(&ctrl_prop, &ranks);
+ /* 99) Some final adjustments */
+ vx900_dram_write_final_config(&ctrl_prop);
+ /* Take a dump */
+ dump_pci_device(mcu);
+
+}
+
diff --git a/src/northbridge/via/vx900/romstrap.inc b/src/northbridge/via/vx900/romstrap.inc
new file mode 100644
index 0000000..2eb2de5
--- /dev/null
+++ b/src/northbridge/via/vx900/romstrap.inc
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2004 Tyan Computer
+ * (Written by Yinghai Lu <yhlu at tyan.com> for Tyan Computer)
+ * Copyright (C) 2007 Rudolf Marek <r.marek at assembler.cz>
+ * Copyright (C) 2009 One Laptop per Child, Association, Inc.
+ * Copyright (C) 2011-2012 Alexandru Gagniuc <mr.nuke.me at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* This file constructs the ROM strap table for VX900 */
+
+ .section ".romstrap", "a", @progbits
+
+ .globl __romstrap_start
+__romstrap_start:
+tblpointer:
+ .long 0x77886047
+ .long 0x00777777
+ .long 0x00000000
+ .long 0x00000000
+ .long 0x00888888
+ .long 0x00AA1111
+ .long 0x00000000
+ .long 0x00000000
+
+/*
+ * The pointer to above table should be at 0xffffffd0,
+ * the table itself MUST be aligned to 128B it seems!
+ */
+rspointers:
+ .long tblpointer // It will be 0xffffffd0
+
+ .globl __romstrap_end
+
+__romstrap_end:
+.previous
diff --git a/src/northbridge/via/vx900/romstrap.lds b/src/northbridge/via/vx900/romstrap.lds
new file mode 100644
index 0000000..b51c979
--- /dev/null
+++ b/src/northbridge/via/vx900/romstrap.lds
@@ -0,0 +1,27 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007 AMD
+ * (Written by Yinghai Lu <yinghai.lu at amd.com> for AMD)
+ * Copyright (C) 2011 Alexandru Gagniuc <mr.nuke.me at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ */
+
+SECTIONS {
+ . = (0x100000000 - 0x2c) - (__romstrap_end - __romstrap_start);
+ .romstrap (.): {
+ *(.romstrap)
+ }
+}
diff --git a/src/northbridge/via/vx900/vx900.h b/src/northbridge/via/vx900/vx900.h
new file mode 100644
index 0000000..2137daf
--- /dev/null
+++ b/src/northbridge/via/vx900/vx900.h
@@ -0,0 +1,26 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 Alexandru Gagniuc <mr.nuke.me at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define VX800_ACPI_IO_BASE 0x0400
+
+#define VX900_NB_IOAPIC_ID 0x2
+#define VX900_NB_IOAPIC_BASE 0xfecc000
+
+#define VX900_SB_IOAPIC_ID 0x1
+#define VX900_SB_IOAPIC_BASE 0xfec0000
\ No newline at end of file
More information about the coreboot
mailing list