summaryrefslogtreecommitdiff
path: root/src/northbridge/via
diff options
context:
space:
mode:
authorStefan Reinauer <stepan@coresystems.de>2009-04-17 08:37:18 +0000
committerStefan Reinauer <stepan@openbios.org>2009-04-17 08:37:18 +0000
commitaeba92ab5b0afd1464d6b1a275b5f5b00b351b32 (patch)
tree225fbff67fc05e70507ac6ef7b3af32f00bac6f8 /src/northbridge/via
parent56c51bd120a935e64cfd96d8ad71c9d1f7aab323 (diff)
downloadcoreboot-aeba92ab5b0afd1464d6b1a275b5f5b00b351b32.tar.xz
Add VIA CX700 support, plus VIA vt8454c reference board support.
Signed-off-by: Stefan Reinauer <stepan@coresystems.de> Acked-by: Uwe Hermann <uwe@hermann-uwe.de> Acked-by: Ronald G. Minnich <rminnich@gmail.com> git-svn-id: svn://svn.coreboot.org/coreboot/trunk@4126 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
Diffstat (limited to 'src/northbridge/via')
-rw-r--r--src/northbridge/via/cx700/Config.lb28
-rw-r--r--src/northbridge/via/cx700/chip.h23
-rw-r--r--src/northbridge/via/cx700/cx700_agp.c91
-rw-r--r--src/northbridge/via/cx700/cx700_early_serial.c102
-rw-r--r--src/northbridge/via/cx700/cx700_early_smbus.c268
-rw-r--r--src/northbridge/via/cx700/cx700_lpc.c397
-rw-r--r--src/northbridge/via/cx700/cx700_registers.h272
-rw-r--r--src/northbridge/via/cx700/cx700_reset.c25
-rw-r--r--src/northbridge/via/cx700/cx700_sata.c160
-rw-r--r--src/northbridge/via/cx700/cx700_usb.c56
-rw-r--r--src/northbridge/via/cx700/cx700_vga.c119
-rw-r--r--src/northbridge/via/cx700/northbridge.c182
-rw-r--r--src/northbridge/via/cx700/northbridge.h25
-rw-r--r--src/northbridge/via/cx700/raminit.c1480
-rw-r--r--src/northbridge/via/cx700/raminit.h28
-rw-r--r--src/northbridge/via/cx700/vgabios.c783
16 files changed, 4039 insertions, 0 deletions
diff --git a/src/northbridge/via/cx700/Config.lb b/src/northbridge/via/cx700/Config.lb
new file mode 100644
index 0000000000..f4e558033e
--- /dev/null
+++ b/src/northbridge/via/cx700/Config.lb
@@ -0,0 +1,28 @@
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2007-2009 coresystems GmbH
+##
+## 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
+
+config chip.h
+
+object cx700_reset.o
+object northbridge.o
+object vgabios.o
+
+driver cx700_agp.o
+driver cx700_lpc.o
+driver cx700_sata.o
+driver cx700_vga.o
+
diff --git a/src/northbridge/via/cx700/chip.h b/src/northbridge/via/cx700/chip.h
new file mode 100644
index 0000000000..7b7aca8e89
--- /dev/null
+++ b/src/northbridge/via/cx700/chip.h
@@ -0,0 +1,23 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * 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
+ */
+
+struct northbridge_via_cx700_config {
+};
+
+extern struct chip_operations northbridge_via_cx700_ops;
diff --git a/src/northbridge/via/cx700/cx700_agp.c b/src/northbridge/via/cx700/cx700_agp.c
new file mode 100644
index 0000000000..0166ee135a
--- /dev/null
+++ b/src/northbridge/via/cx700/cx700_agp.c
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <console/console.h>
+#include <arch/io.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+
+/* This is the AGP 3.0 "bridge" @ Bus 0 Device 1 Func 0 */
+
+static void agp_bridge_init(device_t dev)
+{
+
+ device_t north_dev;
+ u8 reg8;
+ north_dev = dev_find_device(PCI_VENDOR_ID_VIA, 0x3324, 0);
+
+ pci_write_config8(north_dev, 0xa0, 0x1); // Enable CPU Direct Access Frame Buffer
+
+ pci_write_config8(north_dev, 0xa2, 0x4a);
+
+ reg8 = pci_read_config8(north_dev, 0xc0);
+ reg8 |= 0x1;
+ pci_write_config8(north_dev, 0xc0, reg8);
+
+ /*
+ * Since Internal Graphic already set to AGP3.0 compatible in its Capability Pointer
+ * We must set RAGP8X=1 B0D0F0 Rx84[3]=1 from backdoor register B0D0F0 RxB5[1:0]=11b
+ */
+ north_dev = dev_find_device(PCI_VENDOR_ID_VIA, 0x0324, 0);
+ reg8 = pci_read_config8(north_dev, 0xb5);
+ reg8 |= 0x3;
+ pci_write_config8(north_dev, 0xb5, reg8);
+ pci_write_config8(north_dev, 0x94, 0x20);
+ pci_write_config8(north_dev, 0x13, 0xd0);
+
+ pci_write_config16(dev, 0x4, 0x0007);
+
+ pci_write_config8(dev, 0x19, 0x01);
+ pci_write_config8(dev, 0x1a, 0x01);
+ pci_write_config8(dev, 0x1c, 0xe0);
+ pci_write_config8(dev, 0x1d, 0xe0);
+ pci_write_config16(dev, 0x1e, 0xa220);
+
+ pci_write_config16(dev, 0x20, 0xdd00);
+ pci_write_config16(dev, 0x22, 0xdef0);
+ pci_write_config16(dev, 0x24, 0xa000);
+ pci_write_config16(dev, 0x26, 0xbff0);
+
+ pci_write_config8(dev, 0x3e, 0x0c);
+ pci_write_config8(dev, 0x40, 0x8b);
+ pci_write_config8(dev, 0x41, 0x43);
+ pci_write_config8(dev, 0x42, 0x62);
+ pci_write_config8(dev, 0x43, 0x44);
+ pci_write_config8(dev, 0x44, 0x34);
+}
+
+static void cx700_noop(device_t dev)
+{
+}
+
+static struct device_operations agp_bridge_operations = {
+ .read_resources = cx700_noop,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_bus_enable_resources,
+ .init = agp_bridge_init,
+ .scan_bus = pci_scan_bridge,
+};
+
+static const struct pci_driver agp_bridge_driver __pci_driver = {
+ .ops = &agp_bridge_operations,
+ .vendor = PCI_VENDOR_ID_VIA,
+ .device = 0xb198,
+};
diff --git a/src/northbridge/via/cx700/cx700_early_serial.c b/src/northbridge/via/cx700/cx700_early_serial.c
new file mode 100644
index 0000000000..a0d7301e20
--- /dev/null
+++ b/src/northbridge/via/cx700/cx700_early_serial.c
@@ -0,0 +1,102 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * 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
+ */
+
+/*
+ * Enable the serial devices on the VIA CX700
+ */
+
+#include <arch/romcc_io.h>
+
+static void cx700_writepnpaddr(u8 val)
+{
+ outb(val, 0x2e);
+ outb(val, 0xeb);
+}
+
+static void cx700_writepnpdata(u8 val)
+{
+ outb(val, 0x2f);
+ outb(val, 0xeb);
+}
+
+static void cx700_writesiobyte(u16 reg, u8 val)
+{
+ outb(val, reg);
+}
+
+static void cx700_writesioword(u16 reg, u16 val)
+{
+ outw(val, reg);
+}
+
+static void enable_cx700_serial(void)
+{
+ outb(6, 0x80);
+
+ // WTH?
+ outb(0x03, 0x22);
+
+ // Set UART1 I/O Base Address
+ pci_write_config8(PCI_DEV(0, 17, 0), 0xb4, 0x7e);
+
+ // UART1 Enable
+ pci_write_config8(PCI_DEV(0, 17, 0), 0xb0, 0x10);
+
+ // turn on pnp
+ cx700_writepnpaddr(0x87);
+ cx700_writepnpaddr(0x87);
+ // now go ahead and set up com1.
+ // set address
+ cx700_writepnpaddr(0x7);
+ cx700_writepnpdata(0x2);
+ // enable serial out
+ cx700_writepnpaddr(0x30);
+ cx700_writepnpdata(0x1);
+ // serial port 1 base address (FEh)
+ cx700_writepnpaddr(0x60);
+ cx700_writepnpdata(0xfe);
+ // serial port 1 IRQ (04h)
+ cx700_writepnpaddr(0x70);
+ cx700_writepnpdata(0x4);
+ // serial port 1 control
+ cx700_writepnpaddr(0xf0);
+ cx700_writepnpdata(0x2);
+ // turn of pnp
+ cx700_writepnpaddr(0xaa);
+
+ // XXX This part should be fully taken care of by
+ // src/pc80/serial.c:uart_init
+
+ // set up reg to set baud rate.
+ cx700_writesiobyte(0x3fb, 0x80);
+ // Set 115 kb
+ cx700_writesioword(0x3f8, 1);
+ // Set 9.6 kb
+ // cx700_writesioword(0x3f8, 12)
+ // now set no parity, one stop, 8 bits
+ cx700_writesiobyte(0x3fb, 3);
+ // now turn on RTS, DRT
+ cx700_writesiobyte(0x3fc, 3);
+ // Enable interrupts
+ cx700_writesiobyte(0x3f9, 0xf);
+ // should be done. Dump a char for fun.
+ cx700_writesiobyte(0x3f8, 48);
+
+ outb(7, 0x80);
+}
diff --git a/src/northbridge/via/cx700/cx700_early_smbus.c b/src/northbridge/via/cx700/cx700_early_smbus.c
new file mode 100644
index 0000000000..218ae0a7a1
--- /dev/null
+++ b/src/northbridge/via/cx700/cx700_early_smbus.c
@@ -0,0 +1,268 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * 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
+ */
+
+// other bioses use this, too:
+#define SMBUS_IO_BASE 0x0500
+
+#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 SMLINK_PIN_CTL SMBUS_IO_BASE + 0xe
+#define SMBUS_PIN_CTL SMBUS_IO_BASE + 0xf
+
+/* Define register settings */
+#define HOST_RESET 0xff
+#define DIMM_BASE 0xa0 // 1010000 is base for DIMM in SMBus
+#define READ_CMD 0x01 // 1 in the 0 bit of SMBHSTADD states to READ
+
+#define SMBUS_TIMEOUT (100*1000*10)
+
+#define I2C_TRANS_CMD 0x40
+#define CLOCK_SLAVE_ADDRESS 0x69
+
+#define SMBUS_DELAY() outb(0x80, 0x80)
+
+/* Debugging macros. */
+
+// #define DEBUG_SMBUS 1
+
+#ifdef DEBUG_SMBUS
+#define PRINT_DEBUG(x) print_debug(x)
+#define PRINT_DEBUG_HEX16(x) print_debug_hex16(x)
+#else
+#define PRINT_DEBUG(x)
+#define PRINT_DEBUG_HEX16(x)
+#endif
+
+/* Internal functions */
+static void smbus_print_error(unsigned char host_status_register, int loops)
+{
+ /* Check if there actually was an error */
+ if (host_status_register == 0x00 || host_status_register == 0x40 ||
+ host_status_register == 0x42)
+ return;
+ print_err("SMBus Error: ");
+ print_err_hex8(host_status_register);
+
+ print_err("\r\n");
+ if (loops >= SMBUS_TIMEOUT) {
+ print_err("SMBus Timout\r\n");
+ }
+ if (host_status_register & (1 << 4)) {
+ print_err("Interrup/SMI# was Failed Bus Transaction\r\n");
+ }
+ if (host_status_register & (1 << 3)) {
+ print_err("Bus Error\r\n");
+ }
+ if (host_status_register & (1 << 2)) {
+ print_err("Device Error\r\n");
+ }
+ if (host_status_register & (1 << 1)) {
+ /* This isn't a real error... */
+ print_debug("Interrupt/SMI# was Successful Completion\r\n");
+ }
+ if (host_status_register & (1 << 0)) {
+ print_err("Host Busy\r\n");
+ }
+}
+
+static void smbus_wait_until_ready(void)
+{
+ int loops;
+
+ loops = 0;
+
+ /* Yes, this is a mess, but it's the easiest way to do it */
+ while (((inb(SMBHSTSTAT) & 1) == 1) && (loops <= SMBUS_TIMEOUT)) {
+ SMBUS_DELAY();
+ ++loops;
+ }
+#ifdef DEBUG_SMBUS
+ /* Some systems seem to have a flakey SMBus. No need to spew a lot of
+ * errors on those, once we know that SMBus access is principally
+ * working.
+ */
+ smbus_print_error(inb(SMBHSTSTAT), loops);
+#endif
+}
+
+static void smbus_reset(void)
+{
+ outb(HOST_RESET, SMBHSTSTAT);
+}
+
+/* Public functions */
+static void set_ics_data(unsigned char dev, int data, char len)
+{
+ //int i;
+ smbus_reset();
+ /* clear host data port */
+ outb(0x00, SMBHSTDAT0);
+ SMBUS_DELAY();
+ smbus_wait_until_ready();
+
+ /* read to reset block transfer counter */
+ inb(SMBHSTCTL);
+
+ /* fill blocktransfer array */
+ if (dev = 0xd2) {
+ //char d2_data[] = {0x0d,0x00,0x3f,0xcd,0x7f,0xbf,0x1a,0x2a,0x01,0x0f,0x0b,0x00,0x8d,0x9b};
+ outb(0x0d, SMBBLKDAT);
+ outb(0x00, SMBBLKDAT);
+ outb(0x3f, SMBBLKDAT);
+ outb(0xcd, SMBBLKDAT);
+ outb(0x7f, SMBBLKDAT);
+ outb(0xbf, SMBBLKDAT);
+ outb(0x1a, SMBBLKDAT);
+ outb(0x2a, SMBBLKDAT);
+ outb(0x01, SMBBLKDAT);
+ outb(0x0f, SMBBLKDAT);
+ outb(0x0b, SMBBLKDAT);
+ outb(0x80, SMBBLKDAT);
+ outb(0x8d, SMBBLKDAT);
+ outb(0x9b, SMBBLKDAT);
+ } else {
+ //char d4_data[] = {0x08,0xff,0x3f,0x00,0x00,0xff,0xff,0xff,0xff};
+ outb(0x08, SMBBLKDAT);
+ outb(0xff, SMBBLKDAT);
+ outb(0x3f, SMBBLKDAT);
+ outb(0x00, SMBBLKDAT);
+ outb(0x00, SMBBLKDAT);
+ outb(0xff, SMBBLKDAT);
+ outb(0xff, SMBBLKDAT);
+ outb(0xff, SMBBLKDAT);
+ outb(0xff, SMBBLKDAT);
+ }
+
+ //for (i=0; i < len; i++)
+ // outb(data[i],SMBBLKDAT);
+
+ outb(dev, SMBXMITADD);
+ outb(0, SMBHSTCMD);
+ outb(len, SMBHSTDAT0);
+ outb(0x74, SMBHSTCTL);
+
+ SMBUS_DELAY();
+
+ smbus_wait_until_ready();
+
+ smbus_reset();
+
+}
+
+static unsigned int get_spd_data(const struct mem_controller *ctrl, unsigned int dimm,
+ unsigned int offset)
+{
+ unsigned int val, addr;
+
+ smbus_reset();
+
+ /* clear host data port */
+ outb(0x00, SMBHSTDAT0);
+ SMBUS_DELAY();
+ smbus_wait_until_ready();
+
+ /* Fetch the SMBus address of the SPD ROM from
+ * the ctrl struct in auto.c in case they are at
+ * non-standard positions.
+ * SMBus Address shifted by 1
+ */
+ addr = (ctrl->channel0[dimm]) << 1;
+
+ outb(addr | 0x1, SMBXMITADD);
+ outb(offset, SMBHSTCMD);
+ outb(0x48, SMBHSTCTL);
+
+ SMBUS_DELAY();
+
+ smbus_wait_until_ready();
+
+ val = inb(SMBHSTDAT0);
+ smbus_reset();
+ return val;
+}
+
+static void enable_smbus(void)
+{
+ device_t dev;
+
+ /* The CX700 ISA Bridge (0x1106, 0x8324) is hardcoded to this location,
+ * no need to probe.
+ */
+ dev = PCI_DEV(0, 17, 0);
+
+ /* SMBus Clock Select: Divider fof 14.318MHz */
+ pci_write_config8(dev, 0x94, 0x20);
+
+ /* SMBus I/O Base, enable SMBus */
+ pci_write_config16(dev, 0xd0, SMBUS_IO_BASE | 1);
+
+ /* SMBus Clock from 128K Source, Enable SMBus Host Controller */
+ pci_write_config8(dev, 0xd2, 0x05);
+
+ /* Enable I/O decoding */
+ pci_write_config16(dev, 0x04, 0x0003);
+
+ /* Setup clock chips */
+ set_ics_data(0xd2, 0, 14);
+ set_ics_data(0xd4, 0, 9);
+}
+
+/* Debugging Function */
+#ifdef DEBUG_SMBUS
+static void dump_spd_data(const struct mem_controller *ctrl)
+{
+ int dimm, offset, regs;
+ unsigned int val;
+
+ for (dimm = 0; dimm < DIMM_SOCKETS; dimm++) {
+ print_debug("SPD Data for DIMM ");
+ print_debug_hex8(dimm);
+ print_debug("\r\n");
+
+ val = get_spd_data(ctrl, dimm, 0);
+ if (val == 0xff) {
+ regs = 256;
+ } else if (val == 0x80) {
+ regs = 128;
+ } else {
+ print_debug("No DIMM present\r\n");
+ regs = 0;
+ }
+ for (offset = 0; offset < regs; offset++) {
+ print_debug(" Offset ");
+ print_debug_hex8(offset);
+ print_debug(" = 0x");
+ print_debug_hex8(get_spd_data(ctrl, dimm, offset));
+ print_debug("\r\n");
+ }
+ }
+}
+#else
+#define dump_spd_data(ctrl)
+#endif
diff --git a/src/northbridge/via/cx700/cx700_lpc.c b/src/northbridge/via/cx700/cx700_lpc.c
new file mode 100644
index 0000000000..65275baa81
--- /dev/null
+++ b/src/northbridge/via/cx700/cx700_lpc.c
@@ -0,0 +1,397 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <arch/io.h>
+#include <console/console.h>
+
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ops.h>
+#include <device/pci_ids.h>
+
+#include <pc80/mc146818rtc.h>
+#include <pc80/i8259.h>
+#include <pc80/keyboard.h>
+#include <pc80/isa-dma.h>
+
+#include <cpu/x86/lapic.h>
+#include <stdlib.h>
+
+#define ACPI_IO_BASE 0x400
+#define HPET_ADDR 0xfe800000UL
+#define IOAPIC_ADDR 0xfec00000ULL
+
+#ifdef CONFIG_IOAPIC
+struct ioapicreg {
+ unsigned int reg;
+ unsigned int value_low, value_high;
+};
+
+static struct ioapicreg ioapicregvalues[] = {
+#define ALL (0xff << 24)
+#define NONE (0)
+#define DISABLED (1 << 16)
+#define ENABLED (0 << 16)
+#define TRIGGER_EDGE (0 << 15)
+#define TRIGGER_LEVEL (1 << 15)
+#define POLARITY_HIGH (0 << 13)
+#define POLARITY_LOW (1 << 13)
+#define PHYSICAL_DEST (0 << 11)
+#define LOGICAL_DEST (1 << 11)
+#define ExtINT (7 << 8)
+#define NMI (4 << 8)
+#define SMI (2 << 8)
+#define INT (1 << 8)
+ /* IO-APIC virtual wire mode configuration */
+ /* mask, trigger, polarity, destination, delivery, vector */
+ { 0, ENABLED | TRIGGER_EDGE | POLARITY_HIGH | PHYSICAL_DEST | ExtINT, NONE},
+ { 1, DISABLED, NONE},
+ { 2, DISABLED, NONE},
+ { 3, DISABLED, NONE},
+ { 4, DISABLED, NONE},
+ { 5, DISABLED, NONE},
+ { 6, DISABLED, NONE},
+ { 7, DISABLED, NONE},
+ { 8, DISABLED, NONE},
+ { 9, DISABLED, NONE},
+ {10, DISABLED, NONE},
+ {11, DISABLED, NONE},
+ {12, DISABLED, NONE},
+ {13, DISABLED, NONE},
+ {14, DISABLED, NONE},
+ {15, DISABLED, NONE},
+ {16, DISABLED, NONE},
+ {17, DISABLED, NONE},
+ {18, DISABLED, NONE},
+ {19, DISABLED, NONE},
+ {20, DISABLED, NONE},
+ {21, DISABLED, NONE},
+ {22, DISABLED, NONE},
+ {23, DISABLED, NONE},
+};
+
+static void setup_ioapic(void)
+{
+ int i;
+ unsigned long value_low, value_high, val;
+ unsigned long ioapic_base = IOAPIC_ADDR;
+ volatile unsigned long *l;
+ struct ioapicreg *a = ioapicregvalues;
+ unsigned long bsp_lapicid = lapicid();
+
+ l = (unsigned long *)ioapic_base;
+
+ /* Set APIC ADDR */
+ l[0] = 0;
+ val = l[4];
+ l[4] = (val & 0xF0FFFF) | (2 << 24); // 2 == ID as programmed elsewhere. should be a define? XXX
+
+ /* Set APIC to FSB message bus. */
+ l[0] = 0x3;
+ val = l[4];
+ l[4] = (val & 0xFFFFFE) | 1;
+
+ ioapicregvalues[0].value_high = bsp_lapicid << (56 - 32);
+
+ printk_debug("IOAPIC: Bootstrap Processor Local APIC ID = %02x\n", bsp_lapicid);
+
+ for (i = 0; i < ARRAY_SIZE(ioapicregvalues); i++, a++) {
+ l[0] = (a->reg * 2) + 0x10;
+ l[4] = a->value_low;
+ value_low = l[4];
+ l[0] = (a->reg * 2) + 0x11;
+ l[4] = a->value_high;
+ value_high = l[4];
+ if ((i == 0) && (value_low == 0xffffffff)) {
+ printk_warning("IOAPIC is not responding.\n");
+ return;
+ }
+ printk_debug("IOAPIC: IRQ reg 0x%08x value 0x%08x 0x%08x\n",
+ a->reg, a->value_low, a->value_high);
+ }
+}
+#endif
+
+static const unsigned char pci_irqs[4] = { 11, 11, 10, 10 };
+
+static const unsigned char usb_pins[4] = { 'A', 'B', 'C', 'D' };
+static const unsigned char vga_pins[4] = { 'A', 'B', 'C', 'D' };
+static const unsigned char slot_pins[4] = { 'B', 'C', 'D', 'A' };
+static const unsigned char ac97_pins[4] = { 'B', 'C', 'D', 'A' };
+
+static unsigned char *pin_to_irq(const unsigned char *pin)
+{
+ static unsigned char irqs[4];
+ int i;
+ for (i = 0; i < 4; i++)
+ irqs[i] = pci_irqs[pin[i] - 'A'];
+
+ return irqs;
+}
+
+static void pci_routing_fixup(struct device *dev)
+{
+ printk_debug("%s: device is %p\n", __FUNCTION__, dev);
+
+ /* set up PCI IRQ routing */
+ pci_write_config8(dev, 0x55, pci_irqs[0] << 4);
+ pci_write_config8(dev, 0x56, pci_irqs[1] | (pci_irqs[2] << 4));
+ pci_write_config8(dev, 0x57, pci_irqs[3] << 4);
+
+ /* Assigning IRQs */
+ printk_debug("Setting up USB interrupts.\n");
+ pci_assign_irqs(0, 0x10, pin_to_irq(usb_pins));
+
+ printk_debug("Setting up VGA interrupts.\n");
+ pci_assign_irqs(1, 0x00, pin_to_irq(vga_pins));
+
+ printk_debug("Setting up PCI slot interrupts.\n");
+ pci_assign_irqs(2, 0x04, pin_to_irq(slot_pins));
+ // more?
+
+ printk_debug("Setting up AC97 interrupts.\n");
+ pci_assign_irqs(0x80, 0x1, pin_to_irq(ac97_pins));
+}
+
+/*
+ * Set up the power management capabilities directly into ACPI mode. This
+ * avoids having to handle any System Management Interrupts (SMI's) which I
+ * can't figure out how to do !!!!
+ */
+
+void setup_pm(device_t dev)
+{
+ /* Debounce LID and PWRBTN# Inputs for 16ms. */
+ pci_write_config8(dev, 0x80, 0x20);
+
+ /* Set ACPI base address to IO ACPI_IO_BASE */
+ pci_write_config16(dev, 0x88, ACPI_IO_BASE | 1);
+
+ /* set ACPI irq to 9 */
+ pci_write_config8(dev, 0x82, 0x49);
+
+ /* Primary interupt channel, define wake events 0=IRQ0 15=IRQ15 1=en. */
+ pci_write_config16(dev, 0x84, 0x609a);
+
+ /* SMI output level to low, 7.5us throttle clock */
+ pci_write_config8(dev, 0x8d, 0x18);
+
+ /* GP Timer Control 1s */
+ pci_write_config8(dev, 0x93, 0x88);
+
+ /* Power Well */
+ pci_write_config8(dev, 0x94, 0x20); // 0x20??
+
+ /* 7 = stp to sust delay 1msec
+ * 6 = SUSST# Deasserted Before PWRGD for STD
+ */
+ pci_write_config8(dev, 0x95, 0xc0); // 0xc1??
+
+ /* Disable GP2 & GP3 Timer */
+ pci_write_config8(dev, 0x98, 0);
+
+ /* GP2 Timer Counter */
+ pci_write_config8(dev, 0x99, 0xfb);
+ /* GP3 Timer Counter */
+ //pci_write_config8(dev, 0x9a, 0x20);
+
+ /* Multi Function Select 1 */
+ pci_write_config8(dev, 0xe4, 0x00);
+
+ /* Multi Function Select 2 */
+ pci_write_config8(dev, 0xe5, 0x41); //??
+
+ /* Enable ACPI access (and setup like award) */
+ pci_write_config8(dev, 0x81, 0x84);
+
+ /* Clear status events. */
+ outw(0xffff, ACPI_IO_BASE + 0x00);
+ outw(0xffff, ACPI_IO_BASE + 0x20);
+ outw(0xffff, ACPI_IO_BASE + 0x28);
+ outl(0xffffffff, ACPI_IO_BASE + 0x30);
+
+ /* Disable SCI on GPIO. */
+ outw(0x0, ACPI_IO_BASE + 0x22);
+
+ /* Disable SMI on GPIO. */
+ outw(0x0, ACPI_IO_BASE + 0x24);
+
+ /* Disable all global enable SMIs. */
+ outw(0x0, ACPI_IO_BASE + 0x2a);
+
+ /* All SMI off, both IDE buses ON, PSON rising edge. */
+ outw(0x0, ACPI_IO_BASE + 0x2c);
+
+ /* Primary activity SMI disable. */
+ outl(0x0, ACPI_IO_BASE + 0x34);
+
+ /* GP timer reload on none. */
+ outl(0x0, ACPI_IO_BASE + 0x38);
+
+ /* Disable extended IO traps. */
+ outb(0x0, ACPI_IO_BASE + 0x42);
+
+ /* SCI is generated for RTC/pwrBtn/slpBtn. */
+ outw(0x0001, ACPI_IO_BASE + 0x04);
+
+ /* Allow SLP# signal to assert LDTSTOP_L.
+ * Will work for C3 and for FID/VID change.
+ */
+ outb(0x1, ACPI_IO_BASE + 0x11);
+}
+
+static void cx700_set_lpc_registers(struct device *dev)
+{
+ unsigned char enables;
+
+ printk_debug("VIA CX700 LPC bridge init\n");
+
+ // enable the internal I/O decode
+ enables = pci_read_config8(dev, 0x6C);
+ enables |= 0x80;
+ pci_write_config8(dev, 0x6C, enables);
+
+ // Map 4MB of FLASH into the address space
+// pci_write_config8(dev, 0x41, 0x7f);
+
+ // Set bit 6 of 0x40, because Award does it (IO recovery time)
+ // IMPORTANT FIX - EISA 0x4d0 decoding must be on so that PCI
+ // interrupts can be properly marked as level triggered.
+ enables = pci_read_config8(dev, 0x40);
+ enables |= 0x44;
+ pci_write_config8(dev, 0x40, enables);
+
+ /* DMA Line buffer control */
+ enables = pci_read_config8(dev, 0x42);
+ enables |= 0xf0;
+ pci_write_config8(dev, 0x42, enables);
+
+ /* I/O recovery time */
+ pci_write_config8(dev, 0x4c, 0x44);
+
+ /* ROM memory cycles go to LPC. */
+ pci_write_config8(dev, 0x59, 0x80);
+
+ /* Enable SM dynamic clock gating */
+ pci_write_config8(dev, 0x5b, 0x01);
+
+ /* Set Read Pass Write Control Enable */
+ pci_write_config8(dev, 0x48, 0x0c);
+
+ /* Set SM Misc Control: Enable Internal APIC . */
+ enables = pci_read_config8(dev, 0x58);
+ enables |= 1 << 6;
+ pci_write_config8(dev, 0x58, enables);
+ enables = pci_read_config8(dev, 0x4d);
+ enables |= 1 << 3;
+ pci_write_config8(dev, 0x4d, enables);
+
+ /* Set bit 3 of 0x4f to match award (use INIT# as cpu reset) */
+ enables = pci_read_config8(dev, 0x4f);
+ enables |= 0x08;
+ pci_write_config8(dev, 0x4f, enables);
+
+ /* enable KBC configuration */
+ pci_write_config8(dev, 0x51, 0x1f);
+
+ /* enable serial irq */
+ pci_write_config8(dev, 0x52, 0x9);
+
+ /* dma */
+ pci_write_config8(dev, 0x53, 0x00);
+
+ // Power management setup
+ setup_pm(dev);
+
+ /* set up isa bus -- i/o recovery time, rom write enable, extend-ale */
+ pci_write_config8(dev, 0x40, 0x54);
+
+ /* Enable HPET timer */
+ pci_write_config32(dev, 0x68, (1 << 31) | (HPET_ADDR >> 8));
+
+}
+
+void cx700_read_resources(device_t dev)
+{
+ struct resource *resource;
+
+ /* Make sure we call our childrens set/enable functions - these
+ * are not called unless this device has a resource to set.
+ */
+
+ pci_dev_read_resources(dev);
+
+ resource = new_resource(dev, 1);
+ resource->flags |=
+ IORESOURCE_FIXED | IORESOURCE_ASSIGNED | IORESOURCE_IO | IORESOURCE_STORED;
+ resource->size = 2;
+ resource->base = 0x2e;
+}
+
+void cx700_set_resources(device_t dev)
+{
+ struct resource *resource;
+ resource = find_resource(dev, 1);
+ resource->flags |= IORESOURCE_STORED;
+ pci_dev_set_resources(dev);
+}
+
+void cx700_enable_resources(device_t dev)
+{
+ /* Enable SuperIO decoding */
+ pci_dev_enable_resources(dev);
+ enable_childrens_resources(dev);
+}
+
+static void cx700_lpc_init(struct device *dev)
+{
+ cx700_set_lpc_registers(dev);
+
+#ifdef CONFIG_IOAPIC
+ setup_ioapic();
+#endif
+
+ /* Initialize interrupts */
+ pci_routing_fixup(dev);
+ /* make sure interupt controller is configured before keyboard init */
+ setup_i8259();
+
+ /* Start the Real Time Clock */
+ rtc_init(0);
+
+ /* Initialize isa dma */
+ isa_dma_init();
+
+ /* Initialize keyboard controller */
+ init_pc_keyboard(0x60, 0x64, 0);
+}
+
+static struct device_operations cx700_lpc_ops = {
+ .read_resources = cx700_read_resources,
+ .set_resources = cx700_set_resources,
+ .enable_resources = cx700_enable_resources,
+ .init = &cx700_lpc_init,
+ .scan_bus = scan_static_bus,
+};
+
+static const struct pci_driver lpc_driver __pci_driver = {
+ .ops = &cx700_lpc_ops,
+ .vendor = PCI_VENDOR_ID_VIA,
+ .device = 0x8324,
+};
diff --git a/src/northbridge/via/cx700/cx700_registers.h b/src/northbridge/via/cx700/cx700_registers.h
new file mode 100644
index 0000000000..996c00f015
--- /dev/null
+++ b/src/northbridge/via/cx700/cx700_registers.h
@@ -0,0 +1,272 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * 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
+ */
+
+/* CX700 has 48 bytes of scratch registers in D0F4 starting at Reg. 0xd0 */
+#define SCRATCH_REG_BASE 0xd0
+#define SCRATCH_RANK_0 0xd0
+#define SCRATCH_RANK_1 0xd1
+#define SCRATCH_RANK_2 0xd2
+#define SCRATCH_RANK_3 0xd3
+#define SCRATCH_DIMM_NUM 0xd4
+#define SCRATCH_RANK_NUM 0xd5
+#define SCRATCH_RANK_MAP 0xd6
+#define SCRATCH_DRAM_FREQ 0xd7
+#define SCRATCH_DRAM_NB_ODT 0xd8
+#define SCRATCH_RANK0_SIZE_REG 0xe0 /* RxE0~RxE3 */
+#define SCRATCH_RANK0_MA_REG 0xe4 /* RxE4~RxE7 */
+#define SCRATCH_CHA_DQSI_LOW_REG 0xe8
+#define SCRATCH_CHA_DQSI_HIGH_REG 0xe9
+#define SCRATCH_ChA_DQSI_REG 0xea
+#define SCRATCH_DRAM_256M_BIT 0xee
+#define SCRATCH_FLAGS 0xef
+
+static const u8 Reg_Val[] = {
+/* REG, VALUE */
+ 0x70, 0x33,
+ 0x71, 0x11,
+ 0x72, 0x33,
+ 0x73, 0x11,
+ 0x74, 0x20,
+ 0x75, 0x2e,
+ 0x76, 0x64,
+ 0x77, 0x00,
+ 0x78, 0x44,
+ 0x79, 0xaa,
+ 0x7a, 0x33,
+ 0x7b, 0xaa,
+ 0x7c, 0x00,
+ 0x7e, 0x33,
+ 0x7f, 0x33,
+ 0x80, 0x44,
+ 0x81, 0x44,
+ 0x82, 0x44,
+ 0x83, 0x02,
+ 0x50, 0x88,
+ 0x51, 0x7b,
+ 0x52, 0x6f,
+ 0x53, 0x88,
+ 0x54, 0x0e,
+ 0x55, 0x00,
+ 0x56, 0x00,
+ 0x59, 0x00,
+ 0x5d, 0x72,
+ 0x5e, 0x88,
+ 0x5f, 0xc7,
+ 0x68, 0x01,
+};
+
+/* Host registers initial value */
+static const u8 Host_Reg_Val[] = {
+/* REG, VALUE */
+ 0x60, 0xff,
+ 0x61, 0xff,
+ 0x62, 0x0f,
+ 0x63, 0xff,
+ 0x64, 0xff,
+ 0x65, 0x0f,
+ 0x66, 0xff,
+ 0x67, 0x30,
+};
+
+static const u8 Mem_Reg_Init[] = {
+/* REG, AND, OR */
+ 0x50, 0x11, 0x66,
+ 0x51, 0x11, 0x66,
+ 0x52, 0x00, 0x11,
+ 0x53, 0x00, 0x0f,
+ 0x54, 0x00, 0x00,
+ 0x55, 0x00, 0x00,
+ 0x56, 0x00, 0x00,
+ 0x57, 0x00, 0x00,
+ 0x60, 0x00, 0x00,
+ 0x62, 0xf7, 0x08,
+ 0x65, 0x00, 0xd9,
+ 0x66, 0x00, 0x80,
+ 0x67, 0x00, 0x50, /* OR 0x00 ?? */
+ 0x69, 0xf0, 0x00,
+ 0x6a, 0x00, 0x00,
+ 0x6d, 0xcf, 0xc0,
+ 0x6e, 0xff, 0x80,
+ 0x75, 0x0f, 0x40,
+ 0x77, 0x00, 0x00,
+ 0x80, 0x00, 0x00,
+ 0x81, 0x00, 0x00,
+ 0x82, 0x00, 0x00,
+ 0x83, 0x00, 0x00,
+ 0x84, 0x00, 0x00,
+ 0x85, 0x00, 0x00,
+ 0x86, 0xff, 0x2c, /* OR 0x28 if we don't want enable top 1M SM memory */
+ 0x40, 0x00, 0x00,
+ 0x7c, 0x00, 0x00,
+ 0x7e, 0x00, 0x00,
+ 0xa4, 0xfe, 0x00,
+ 0xb0, 0x7f, 0x80,
+ 0xb1, 0x00, 0xaa,
+ 0xb4, 0xfd, 0x02,
+ 0xb8, 0xfe, 0x00,
+};
+
+static const u8 Dram_Driving_ODT_CTRL[] = {
+/* REG, VALUE */
+ 0xd6, 0xa8,
+ 0xd4, 0x80,
+ 0xd0, 0x88,
+ 0xd3, 0x01,
+ 0xd8, 0x00,
+ 0xda, 0x80,
+};
+
+#define Rank0_ODT 0x00
+#define Rank1_ODT 0x01
+#define Rank2_ODT 0x02
+#define Rank3_ODT 0x03
+#define NA_ODT 0x00
+#define NB_ODT_75ohm 0x00
+#define NB_ODT_150ohm 0x01
+#define DDR2_ODT_75ohm 0x20
+#define DDR2_ODT_150ohm 0x40
+
+static const u8 ODT_TBL[] = {
+/* RankMap, ODT Control Bits, DRAM & NB ODT setting */
+ 0x01, ((NA_ODT << 6) | (NA_ODT << 4) | (NA_ODT << 2) | Rank0_ODT), (DDR2_ODT_150ohm | NB_ODT_75ohm),
+ 0x03, ((NA_ODT << 6) | (NA_ODT << 4) | (Rank0_ODT << 2) | Rank1_ODT), (DDR2_ODT_150ohm | NB_ODT_75ohm),
+ 0x04, ((NA_ODT << 6) | (Rank2_ODT << 4) | (NA_ODT << 2) | NA_ODT), (DDR2_ODT_150ohm | NB_ODT_75ohm),
+ 0x05, ((NA_ODT << 6) | (Rank0_ODT << 4) | (NA_ODT << 2) | Rank2_ODT), (DDR2_ODT_75ohm | NB_ODT_150ohm),
+ 0x07, ((NA_ODT << 6) | (Rank0_ODT << 4) | (Rank2_ODT << 2) | Rank2_ODT), (DDR2_ODT_75ohm | NB_ODT_150ohm),
+ 0x0c, ((Rank2_ODT << 6) | (Rank3_ODT << 4) | (NA_ODT << 2) | NA_ODT), (DDR2_ODT_150ohm | NB_ODT_75ohm),
+ 0x0d, ((Rank0_ODT << 6) | (Rank0_ODT << 4) | (NA_ODT << 2) | Rank2_ODT), (DDR2_ODT_75ohm | NB_ODT_150ohm),
+ 0x0f, ((Rank0_ODT << 6) | (Rank0_ODT << 4) | (Rank2_ODT << 2) | Rank2_ODT), (DDR2_ODT_75ohm | NB_ODT_150ohm),
+};
+
+static const u8 DQS_DQ_TBL[] = {
+/* RxE0: DRAM Timing DQS */
+/* RxE2: DRAM Timing DQ */
+/* RxE0, RxE2 */
+ 0xee, 0xba,
+ 0xee, 0xba,
+ 0xcc, 0xba,
+ 0xcc, 0xba,
+};
+
+static const u8 Duty_Control_DDR2[] = {
+/* RxEC, RxED, RxEE, RXEF */
+ /* DDRII533 1~2 rank, DDRII400 */
+ 0x84, 0x10, 0x00, 0x10,
+ /* DDRII533 3~4 rank */
+ 0x44, 0x10, 0x00, 0x10,
+};
+
+static const u8 ChA_Clk_Phase_DDR2_Table[] = {
+/* Rx91, Rx92, Rx93 */
+ /* DDRII533 1 rank */
+ 0x04, 0x05, 0x06,
+ /* DDRII533 2~4 rank */
+ 0x04, 0x05, 0x05,
+ /* DDRII400 */
+ 0x02, 0x04, 0x04,
+};
+
+static const u8 DQ_DQS_Table[] = {
+/* REG, VALUE */
+/* DRAM DQ/DQS Output Delay Control */
+ 0xdc, 0x65,
+ 0xdd, 0x01,
+ 0xde, 0xc0,
+/* DRAM DQ/DQS input Capture Control */
+ 0x78, 0x83,
+ 0x79, 0x83,
+ 0x7a, 0x00,
+};
+
+static const u8 DQSOChA_DDR2_Driving_Table[] = {
+/* Rx70, Rx71 */
+ /* DDRII533 1~2 rank */
+ 0x00, 0x01,
+ /* DDRII533 3~4 rank */
+ 0x03, 0x00,
+ /* DDRII400 1~2 rank */
+ 0x00, 0x04,
+ /* DDRII400 3~4 rank */
+ 0x00, 0x01,
+};
+
+/************************************************************************/
+/* Chipset Performance UP and other setting after DRAM Sizing Registers */
+/************************************************************************/
+static const u8 Dram_Table[] = {
+/* REG, AND, OR */
+ 0x60, 0xff, 0x03,
+ 0x66, 0xcf, 0x80,
+ 0x68, 0x00, 0x00,
+ 0x69, 0xfd, 0x03,
+ 0x6e, 0xff, 0x01,
+ 0x95, 0xff, 0x40,
+};
+
+static const u8 Host_Table[] = {
+/* REG, AND, OR */
+ 0x51, 0x81, 0x7a,
+ 0x55, 0xff, 0x06,
+ 0x5e, 0x00, 0x88,
+ 0x5d, 0xff, 0xb2,
+};
+
+static const u8 Init_Rank_Reg_Table[] = {
+ /* Rank Ending Address Registers */
+ 0x40, 0x41, 0x42, 0x43,
+ /* Rank Beginning Address Registers */
+ 0x48, 0x49, 0x4a, 0x4b,
+ /* Physical-to-Virtual Rank Mapping Registers */
+ 0x54, 0x55,
+};
+
+static const u16 DDR2_MRS_table[] = {
+/* CL: 2, 3, 4, 5 */
+ 0x150, 0x1d0, 0x250, 0x2d0, /* BL=4 ;Use 1X-bandwidth MA table to init DRAM */
+ 0x158, 0x1d8, 0x258, 0x2d8, /* BL=8 ;Use 1X-bandwidth MA table to init DRAM */
+};
+
+#define MRS_DDR2_TWR2 ((0 << 15) | (0 << 20) | (1 << 12))
+#define MRS_DDR2_TWR3 ((0 << 15) | (1 << 20) | (0 << 12))
+#define MRS_DDR2_TWR4 ((0 << 15) | (1 << 20) | (1 << 12))
+#define MRS_DDR2_TWR5 ((1 << 15) | (0 << 20) | (0 << 12))
+static const u32 DDR2_Twr_table[] = {
+ MRS_DDR2_TWR2,
+ MRS_DDR2_TWR3,
+ MRS_DDR2_TWR4,
+ MRS_DDR2_TWR5,
+};
+
+static const u8 DQSI_Rate_Table[] = {
+ 8, /* DDRII 200 */
+ 8, /* DDRII 266 */
+ 8, /* DDRII 333 */
+ 7, /* DDRII 400 */
+ 8, /* DDRII 533 */
+ 8, /* DDRII 666 */
+};
+
+static const u8 REFC_Table[] = {
+ 0x65, 0x32, /* DDRII 100 */
+ 0x86, 0x43, /* DDRII 266 */
+ 0xa8, 0x54, /* DDRII 333 */
+ 0xca, 0x65, /* DDRII 400 */
+ 0xca, 0x86, /* DDRII 533 */
+ 0xca, 0xa8, /* DDRII 666 */
+};
diff --git a/src/northbridge/via/cx700/cx700_reset.c b/src/northbridge/via/cx700/cx700_reset.c
new file mode 100644
index 0000000000..80e09d7fab
--- /dev/null
+++ b/src/northbridge/via/cx700/cx700_reset.c
@@ -0,0 +1,25 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <arch/io.h>
+
+void hard_reset(void)
+{
+ outb((1 << 2) | (1 << 1), 0xcf9);
+}
diff --git a/src/northbridge/via/cx700/cx700_sata.c b/src/northbridge/via/cx700/cx700_sata.c
new file mode 100644
index 0000000000..893126606e
--- /dev/null
+++ b/src/northbridge/via/cx700/cx700_sata.c
@@ -0,0 +1,160 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <console/console.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+
+/* IDE specific bits */
+#define IDE_MODE_REG 0x09
+#define IDE0_NATIVE_MODE (1 << 0)
+#define IDE1_NATIVE_MODE (1 << 2)
+
+/* These are default addresses */
+#define IDE0_DATA_ADDR 0x1f0
+#define IDE0_CONTROL_ADDR 0x3f4
+#define IDE1_DATA_ADDR 0x170
+#define IDE1_CONTROL_ADDR 0x370
+
+#define BUS_MASTER_ADDR 0xfc00
+
+#define CHANNEL_ENABLE_REG 0x40
+#define ENABLE_IDE0 (1 << 0)
+#define ENABLE_IDE1 (1 << 1)
+
+/* TODO: better user configuration */
+#define DISABLE_SATA 0
+
+static void sata_init(struct device *dev)
+{
+ u8 reg8;
+
+ printk_debug("Configuring VIA SATA & EIDE Controller\n");
+
+ /* Class IDE Disk, instead of RAID controller */
+ reg8 = pci_read_config8(dev, 0x45);
+ reg8 &= 0x7f; /* Sub Class Write Protect off */
+ pci_write_config8(dev, 0x45, reg8);
+ pci_write_config8(dev, 0x0a, 0x01);
+ reg8 |= 0x80; /* Sub Class Write Protect on */
+ pci_write_config8(dev, 0x45, reg8);
+
+#if defined(DISABLE_SATA) && (DISABLE_SATA == 1)
+ printk_info("Disabling SATA (Primary Channel)\n");
+ /* Disable SATA channels */
+ pci_write_config8(dev, 0x40, 0x00);
+#else
+ pci_write_config8(dev, 0x40, 0x43);
+#endif
+
+ reg8 = pci_read_config8(dev, 0x6a);
+ reg8 |= 0x8; /* Mode Select set to Manual Mode */
+ reg8 &= ~7;
+ reg8 |= 0x2; /* Manual setting to 50 ohm */
+
+ pci_write_config8(dev, 0x6a, reg8);
+
+ reg8 = pci_read_config8(dev, 0x6b);
+ reg8 &= ~7;
+ reg8 |= 0x01; /* Autocomp of Termination */
+ pci_write_config8(dev, 0x6b, reg8);
+
+ /* Enable EIDE (secondary channel) even if SATA disabled */
+ reg8 = pci_read_config8(dev, 0xc0);
+ reg8 |= 0x1;
+ pci_write_config8(dev, 0xc0, reg8);
+
+ // Enable bus mastering, memory space acces, io space access
+ pci_write_config16(dev, 0x04, 0x0007);
+
+ /* Set SATA base ports. */
+ pci_write_config32(dev, 0x10, 0x01f1);
+ pci_write_config32(dev, 0x14, 0x03f5);
+ /* Set EIDE base ports. */
+ pci_write_config32(dev, 0x18, 0x0171);
+ pci_write_config32(dev, 0x1c, 0x0375);
+
+ /* SATA/EIDE Bus Master mode base address */
+ pci_write_config32(dev, 0x20, BUS_MASTER_ADDR | 1);
+
+ /* Enable read/write prefetch buffers */
+ reg8 = pci_read_config8(dev, 0xc1);
+ reg8 |= 0x30;
+ pci_write_config8(dev, 0xc1, reg8);
+
+ /* Set FIFO thresholds like */
+ pci_write_config8(dev, 0xc3, 0x1); /* FIFO flushed when 1/2 full */
+
+ /* EIDE Sector Size */
+ pci_write_config16(dev, 0xe8, 0x200);
+
+ /* Some Miscellaneous Control */
+ pci_write_config8(dev, 0x44, 0x7);
+ pci_write_config8(dev, 0x45, 0xaf);
+ pci_write_config8(dev, 0x46, 0x8);
+
+ /* EIDE Configuration */
+ reg8 = pci_read_config8(dev, 0xc4);
+ reg8 |= 0x10;
+ pci_write_config8(dev, 0xc4, reg8);
+
+ pci_write_config8(dev, 0xc5, 0xc);
+
+ /* Interrupt Line */
+ reg8 = pci_read_config8(dev, 0x45);
+ reg8 &= ~(1 << 4); /* Interrupt Line Write Protect off */
+ pci_write_config8(dev, 0x45, reg8);
+
+ pci_write_config8(dev, 0x3c, 0x0e); /* Interrupt */
+
+ /* Set the drive timing control */
+ pci_write_config16(dev, 0x48, 0x5d5d);
+
+ /* Enable only compatibility mode. */
+ reg8 = pci_read_config8(dev, 0x42);
+ reg8 &= ~0xa0;
+ pci_write_config8(dev, 0x42, reg8);
+ reg8 = pci_read_config8(dev, 0x42);
+ printk_debug("Reg 0x42 read back as 0x%x\n", reg8);
+
+ /* Support Staggered Spin-Up */
+ reg8 = pci_read_config8(dev, 0xb9);
+ if ((reg8 & 0x8) == 0) {
+ printk_debug("start OOB sequence on both drives\n");
+ reg8 |= 0x30;
+ pci_write_config8(dev, 0xb9, reg8);
+ }
+}
+
+static struct device_operations sata_ops = {
+ .read_resources = pci_dev_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .init = sata_init,
+ .enable = 0,
+ .ops_pci = 0,
+};
+
+/* When the SATA controller is in IDE mode, the Device ID is 0x5324 */
+static const struct pci_driver northbridge_driver __pci_driver = {
+ .ops = &sata_ops,
+ .vendor = PCI_VENDOR_ID_VIA,
+ .device = 0x5324,
+};
diff --git a/src/northbridge/via/cx700/cx700_usb.c b/src/northbridge/via/cx700/cx700_usb.c
new file mode 100644
index 0000000000..b2dc482df3
--- /dev/null
+++ b/src/northbridge/via/cx700/cx700_usb.c
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <console/console.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+
+static void usb_init(struct device *dev)
+{
+ u32 reg32;
+ u8 reg8;
+
+ /* USB Specification says the device must be Bus Master */
+ printk_debug("UHCI: Setting up controller.. ");
+
+ reg32 = pci_read_config32(dev, PCI_COMMAND);
+ pci_write_config32(dev, PCI_COMMAND, reg32 | PCI_COMMAND_MASTER);
+
+ reg8 = pci_read_config8(dev, 0xca);
+ reg8 |= (1 << 0);
+ pci_write_config8(dev, 0xca, reg8);
+
+ printk_debug("done.\n");
+}
+
+static struct device_operations usb_ops = {
+ .read_resources = pci_dev_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .init = usb_init,
+ .enable = 0,
+ .ops_pci = 0,
+};
+
+static const struct pci_driver via_usb_driver __pci_driver = {
+ .ops = &usb_ops,
+ .vendor = PCI_VENDOR_ID_VIA,
+ .device = 0x3038,
+};
diff --git a/src/northbridge/via/cx700/cx700_vga.c b/src/northbridge/via/cx700/cx700_vga.c
new file mode 100644
index 0000000000..96bb769e20
--- /dev/null
+++ b/src/northbridge/via/cx700/cx700_vga.c
@@ -0,0 +1,119 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <console/console.h>
+#include <arch/io.h>
+#include <stdint.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <stdlib.h>
+#include <string.h>
+#include <bitops.h>
+#include <cpu/cpu.h>
+#include <cpu/x86/mtrr.h>
+#include <cpu/x86/msr.h>
+#include "chip.h"
+#include "northbridge.h"
+
+/* PCI Domain 1 Device 0 Function 0 */
+
+#define SR_INDEX 0x3c4
+#define SR_DATA 0x3c5
+#define CRTM_INDEX 0x3b4
+#define CRTM_DATA 0x3b5
+#define CRTC_INDEX 0x3d4
+#define CRTC_DATA 0x3d5
+
+void setup_realmode_idt(void);
+void do_vgabios(void);
+void vga_enable_console(void);
+
+void write_protect_vgabios(void)
+{
+ device_t dev;
+
+ printk_debug("write_protect_vgabios\n");
+
+ dev = dev_find_device(PCI_VENDOR_ID_VIA, 0x3324, 0);
+ if (dev)
+ pci_write_config8(dev, 0x80, 0xff);
+
+ dev = dev_find_device(PCI_VENDOR_ID_VIA, 0x7324, 0);
+ if (dev)
+ pci_write_config8(dev, 0x61, 0xff);
+}
+
+static void vga_init(device_t dev)
+{
+ u8 reg8;
+
+ printk_debug("Initializing VGA...\n");
+
+ //*
+ pci_write_config8(dev, 0x04, 0x07);
+ pci_write_config8(dev, 0x3e, 0x02);
+ pci_write_config8(dev, 0x0d, 0x40);
+ pci_write_config32(dev, 0x10, 0xa0000008);
+ pci_write_config32(dev, 0x14, 0xdd000000);
+ pci_write_config8(dev, 0x3c, 0x0b);
+ //*/
+
+ printk_debug("Executing VGA option rom in real mode\n");
+ setup_realmode_idt();
+ do_vgabios();
+ printk_debug("Enable VGA console\n");
+ vga_enable_console();
+
+ /* It's not clear if these need to be programmed before or after
+ * the VGA bios runs. Try both, clean up later */
+ /* Set memory rate to 200MHz */
+ outb(0x3d, CRTM_INDEX);
+ reg8 = inb(CRTM_DATA);
+ reg8 &= 0x0f;
+ reg8 |= (0x3 << 4);
+ outb(0x3d, CRTM_INDEX);
+ outb(reg8, CRTM_DATA);
+
+ /* Set framebuffer size to 32mb */
+ reg8 = (32 / 4);
+ outb(0x39, SR_INDEX);
+ outb(reg8, SR_DATA);
+}
+
+static void vga_read_resources(device_t dev)
+{
+ dev->rom_address = 0xfff80000;
+ dev->on_mainboard = 1;
+ pci_dev_read_resources(dev);
+}
+
+static struct device_operations vga_operations = {
+ .read_resources = vga_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .init = vga_init,
+ .ops_pci = 0,
+};
+
+static const struct pci_driver vga_driver __pci_driver = {
+ .ops = &vga_operations,
+ .vendor = PCI_VENDOR_ID_VIA,
+ .device = 0x3157,
+};
diff --git a/src/northbridge/via/cx700/northbridge.c b/src/northbridge/via/cx700/northbridge.c
new file mode 100644
index 0000000000..dc0c667d10
--- /dev/null
+++ b/src/northbridge/via/cx700/northbridge.c
@@ -0,0 +1,182 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <console/console.h>
+#include <arch/io.h>
+#include <stdint.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/hypertransport.h>
+#include <device/pci_ids.h>
+#include <stdlib.h>
+#include <string.h>
+#include <bitops.h>
+#include <cpu/cpu.h>
+#include <cpu/x86/mtrr.h>
+#include "chip.h"
+#include "northbridge.h"
+
+static void pci_domain_read_resources(device_t dev)
+{
+ struct resource *resource;
+
+ /* Initialize the system wide io space constraints */
+ resource = new_resource(dev, IOINDEX_SUBTRACTIVE(0, 0));
+ resource->limit = 0xffffUL;
+ resource->flags = IORESOURCE_IO | IORESOURCE_SUBTRACTIVE | IORESOURCE_ASSIGNED;
+
+ /* Initialize the system wide memory resources constraints */
+ resource = new_resource(dev, IOINDEX_SUBTRACTIVE(1, 0));
+ resource->limit = 0xffffffffULL;
+ resource->flags = IORESOURCE_MEM | IORESOURCE_SUBTRACTIVE | IORESOURCE_ASSIGNED;
+}
+
+static void ram_resource(device_t dev, unsigned long index,
+ unsigned long basek, unsigned long sizek)
+{
+ struct resource *resource;
+
+ if (!sizek) {
+ return;
+ }
+ resource = new_resource(dev, index);
+ resource->base = ((resource_t) basek) << 10;
+ resource->size = ((resource_t) sizek) << 10;
+ resource->flags = IORESOURCE_MEM | IORESOURCE_CACHEABLE |
+ IORESOURCE_FIXED | IORESOURCE_STORED | IORESOURCE_ASSIGNED;
+}
+
+static void tolm_test(void *gp, struct device *dev, struct resource *new)
+{
+ struct resource **best_p = gp;
+ struct resource *best;
+ best = *best_p;
+ if (!best || (best->base > new->base)) {
+ best = new;
+ }
+ *best_p = best;
+}
+
+static u32 find_pci_tolm(struct bus *bus)
+{
+ struct resource *min = NULL;
+ u32 tolm;
+
+ search_bus_resources(bus, IORESOURCE_MEM, IORESOURCE_MEM, tolm_test, &min);
+ tolm = 0xffffffffUL;
+ if (min && tolm > min->base) {
+ tolm = min->base;
+ }
+
+ return tolm;
+}
+
+static void pci_domain_set_resources(device_t dev)
+{
+ device_t mc_dev;
+ u32 pci_tolm;
+ unsigned char reg;
+ unsigned long tomk, tolmk;
+ unsigned char rambits;
+ int idx;
+
+ pci_tolm = find_pci_tolm(&dev->link[0]);
+ mc_dev = dev_find_device(PCI_VENDOR_ID_VIA, 0x3324, 0);
+
+ rambits = pci_read_config8(mc_dev, 0x88);
+ rambits >>= 2;
+
+ /* Get memory size and frame buffer from northbridge's registers.
+ *
+ * If register contains an invalid value we set frame buffer size to a
+ * default of 32M, but that probably won't happen.
+ */
+ reg = pci_read_config8(mc_dev, 0xa1);
+ reg &= 0x70;
+ reg = reg >> 4;
+
+ /* TOP 1M SMM Memory */
+ if (reg == 0x0 || reg == 0x6 || reg == 0x7)
+ tomk = (((rambits << 6) - 32 - 1) * 1024); // Set frame buffer 32M for default
+ else
+ tomk = (((rambits << 6) - (4 << reg) - 1) * 1024);
+
+ /* Compute the top of Low memory */
+ tolmk = pci_tolm >> 10;
+ if (tolmk >= tomk) {
+ /* The PCI hole does does not overlap the memory. */
+ tolmk = tomk;
+ tolmk -= 1024; // TOP 1M SM Memory
+ }
+
+ /* Report the memory regions */
+ idx = 10;
+
+ /* TODO: Hole needed? Should this go elsewhere? */
+ ram_resource(dev, idx++, 0, 640); /* first 640k */
+ ram_resource(dev, idx++, 768, (tolmk - 768)); /* leave a hole for vga */
+ assign_resources(&dev->link[0]);
+}
+
+static unsigned int pci_domain_scan_bus(device_t dev, unsigned int max)
+{
+ max = pci_scan_bus(&dev->link[0], PCI_DEVFN(0, 0), 0xff, max);
+ return max;
+}
+
+static struct device_operations pci_domain_ops = {
+ .read_resources = pci_domain_read_resources,
+ .set_resources = pci_domain_set_resources,
+ .enable_resources = enable_childrens_resources,
+ .init = 0,
+ .scan_bus = pci_domain_scan_bus,
+};
+
+static void cpu_bus_init(device_t dev)
+{
+ initialize_cpus(&dev->link[0]);
+}
+
+static void cpu_bus_noop(device_t dev)
+{
+}
+
+static struct device_operations cpu_bus_ops = {
+ .read_resources = cpu_bus_noop,
+ .set_resources = cpu_bus_noop,
+ .enable_resources = cpu_bus_noop,
+ .init = cpu_bus_init,
+ .scan_bus = 0,
+};
+
+static void enable_dev(struct device *dev)
+{
+ /* Our wonderful device model */
+ if (dev->path.type == DEVICE_PATH_PCI_DOMAIN) {
+ dev->ops = &pci_domain_ops;
+ pci_set_method(dev);
+ } else if (dev->path.type == DEVICE_PATH_APIC_CLUSTER) {
+ dev->ops = &cpu_bus_ops;
+ }
+}
+
+struct chip_operations northbridge_via_cx700_ops = {
+ CHIP_NAME("VIA CX700 Northbridge")
+ .enable_dev = enable_dev
+};
diff --git a/src/northbridge/via/cx700/northbridge.h b/src/northbridge/via/cx700/northbridge.h
new file mode 100644
index 0000000000..1048a96643
--- /dev/null
+++ b/src/northbridge/via/cx700/northbridge.h
@@ -0,0 +1,25 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * 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
+ */
+
+#ifndef NORTHBRIDGE_VIA_CX700_H
+#define NORTHBRIDGE_VIA_CX700_H
+
+extern unsigned int cx700_scan_root_bus(device_t root, unsigned int max);
+
+#endif /* NORTHBRIDGE_VIA_CX700_H */
diff --git a/src/northbridge/via/cx700/raminit.c b/src/northbridge/via/cx700/raminit.c
new file mode 100644
index 0000000000..ff21e537ca
--- /dev/null
+++ b/src/northbridge/via/cx700/raminit.c
@@ -0,0 +1,1480 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <types.h>
+#include <spd.h>
+#include <spd_ddr2.h>
+#include <sdram_mode.h>
+#include <delay.h>
+#include "cx700_registers.h"
+
+// #define DEBUG_RAM_SETUP 1
+
+/* Debugging macros. */
+#if defined(DEBUG_RAM_SETUP)
+#define PRINTK_DEBUG(x...) printk_debug(x)
+#else
+#define PRINTK_DEBUG(x...)
+#endif
+
+#define RAM_COMMAND_NORMAL 0x0
+#define RAM_COMMAND_NOP 0x1
+#define RAM_COMMAND_PRECHARGE 0x2
+#define RAM_COMMAND_MRS 0x3
+#define RAM_COMMAND_CBR 0x4
+
+#define HOSTCTRL PCI_DEV(0, 0, 2)
+#define MEMCTRL PCI_DEV(0, 0, 3)
+
+#define DDRII_666 0x5
+#define DDRII_533 0x4
+#define DDRII_400 0x3
+#define DDRII_333 0x2
+#define DDRII_266 0x1
+#define DDRII_200 0x0
+
+#define OHM_150 1
+
+#ifdef MEM_WIDTH_32BIT_MODE
+#define SDRAM1X_RA_14 30
+#define SDRAM1X_RA_13 29
+#define SDRAM1X_RA_12 28
+#define SDRAM1X_RA_12_8bk 26
+#define SDRAM1X_CA_12 15
+#define SDRAM1X_CA_11 14
+#define SDRAM1X_CA_09 11
+#define SDRAM1X_CA_09_8bk 11
+#define SDRAM1X_BA1 13
+#define SDRAM1X_BA2_8bk 14
+#define SDRAM1X_BA1_8bk 13
+#else
+#define SDRAM1X_RA_14 31
+#define SDRAM1X_RA_13 30
+#define SDRAM1X_RA_12 29
+#define SDRAM1X_RA_12_8bk 27
+#define SDRAM1X_CA_12 16
+#define SDRAM1X_CA_11 15
+#define SDRAM1X_CA_09 12
+#define SDRAM1X_CA_09_8bk 12
+#define SDRAM1X_BA1 14
+#define SDRAM1X_BA2_8bk 15
+#define SDRAM1X_BA1_8bk 14
+#endif
+
+#define MA_Column 0x06
+#define MA_Bank 0x08
+#define MA_Row 0x30
+#define MA_4_Bank 0x00
+#define MA_8_Bank 0x08
+#define MA_12_Row 0x00
+#define MA_13_Row 0x10
+#define MA_14_Row 0x20
+#define MA_15_Row 0x30
+#define MA_9_Column 0x00
+#define MA_10_Column 0x02
+#define MA_11_Column 0x04
+#define MA_12_Column 0x06
+
+#define GET_SPD(i, val, tmp, reg) \
+ do{ \
+ val = 0; \
+ tmp = 0; \
+ for(i = 0; i < 2; i++) { \
+ if(pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_REG_BASE + (i << 1)))) { \
+ tmp = get_spd_data(ctrl, i, reg); \
+ if(tmp > val) \
+ val = tmp; \
+ } \
+ } \
+ } while ( 0 )
+
+#define REGISTERPRESET(bus,dev,fun,bdfspec) \
+ { u8 i, reg; \
+ for (i=0; i<(sizeof((bdfspec))/sizeof(struct regmask)); i++) { \
+ printk_debug("Writing bus " #bus " dev " #dev " fun " #fun " register "); \
+ printk_debug("%02x", (bdfspec)[i].reg); \
+ printk_debug("\n"); \
+ reg = pci_read_config8(PCI_DEV((bus), (dev), (fun)), (bdfspec)[i].reg); \
+ reg &= (bdfspec)[i].mask; \
+ reg |= (bdfspec)[i].val; \
+ pci_write_config8(PCI_DEV((bus), (dev), (fun)), (bdfspec)[i].reg, reg); \
+ } \
+ }
+
+
+static void do_ram_command(const struct mem_controller *ctrl, u8 command)
+{
+ u8 reg;
+
+ reg = pci_read_config8(MEMCTRL, 0x6b);
+ reg &= 0xf8; /* Clear bits 2-0. */
+ reg |= command;
+ pci_write_config8(MEMCTRL, 0x6b, reg);
+
+ PRINTK_DEBUG(" Sending RAM command 0x%02x\n", reg);
+}
+
+// TODO factor out to another file
+static void c7_cpu_setup(const struct mem_controller *ctrl)
+{
+ u8 size, i;
+ size = sizeof(Reg_Val) / sizeof(Reg_Val[0]);
+ for (i = 0; i < size; i += 2)
+ pci_write_config8(HOSTCTRL, Reg_Val[i], Reg_Val[i + 1]);
+}
+
+static void ddr_detect(const struct mem_controller *ctrl)
+{
+ /* FIXME: Only supports 2 ranks per DIMM */
+ u8 val, rsize, dimm;
+ u8 nrank = 0;
+ u8 ndimm = 0;
+ u8 rmap = 0;
+ for (dimm = 0; dimm < DIMM_SOCKETS; dimm++) {
+ val = get_spd_data(ctrl, dimm, 0);
+ if ((val == 0x80) || (val == 0xff)) {
+ ndimm++;
+ rsize = get_spd_data(ctrl, dimm, SPD_RANK_SIZE);
+ /* unit is 128M */
+ rsize = (rsize << 3) | (rsize >> 5);
+ val =
+ get_spd_data(ctrl, dimm,
+ SPD_MOD_ATTRIB_RANK) & SPD_MOD_ATTRIB_RANK_NUM_MASK;
+ switch (val) {
+ case 1:
+ pci_write_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK_1 + (dimm << 1)),
+ rsize);
+ rmap |= (1 << ((dimm << 1) + 1));
+ nrank++;
+ case 0:
+ pci_write_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK_0 + (dimm << 1)),
+ rsize);
+ rmap |= (1 << (dimm << 1));
+ nrank++;
+ }
+ }
+ }
+ pci_write_config8(PCI_DEV(0, 0, 4), SCRATCH_DIMM_NUM, ndimm);
+ pci_write_config8(PCI_DEV(0, 0, 4), SCRATCH_RANK_NUM, nrank);
+ pci_write_config8(PCI_DEV(0, 0, 4), SCRATCH_RANK_MAP, rmap);
+}
+
+static void sdram_set_safe_values(const struct mem_controller *ctrl)
+{
+ /* The purpose of this function is to set initial values for the dram
+ * size and timings. It will be replaced with the SPD based function
+ * once the RAM commands are working with these values.
+ */
+ u8 regs, val, t, dimm;
+ u32 spds, tmp;
+
+ regs = pci_read_config8(MEMCTRL, 0x6c);
+ if (regs & (1 << 6))
+ printk_debug("DDR2 Detected.\n");
+ else
+ die("ERROR: DDR1 memory detected but not supported by coreboot.\n");
+
+ /* Enable DDR2 */
+ regs |= (1 << 7);
+ pci_write_config8(MEMCTRL, 0x6c, regs);
+
+ /* SPD 5 # of ranks */
+ pci_write_config8(MEMCTRL, 0x6d, 0xc0);
+
+ /**********************************************/
+ /* Set DRAM Freq (DDR2 533) */
+ /**********************************************/
+ /* SPD 9 SDRAM Cycle Time */
+ GET_SPD(dimm, spds, regs, 9);
+
+ printk_debug("\nDDRII ");
+ if (spds <= 0x3d) {
+ printk_debug("533");
+ val = DDRII_533;
+ t = 38;
+ } else if (spds <= 0x50) {
+ printk_debug("400");
+ val = DDRII_400;
+ t = 50;
+ } else if (spds <= 0x60) {
+ printk_debug("333");
+ val = DDRII_333;
+ t = 60;
+ } else if (spds <= 0x75) {
+ printk_debug("266");
+ val = DDRII_266;
+ t = 75;
+ } else {
+ printk_debug("200");
+ val = DDRII_200;
+ t = 100;
+ }
+ /* To store DDRII frequence */
+ pci_write_config8(PCI_DEV(0, 0, 4), SCRATCH_DRAM_FREQ, val);
+
+ /* Manual reset and adjust DLL when DRAM change frequency
+ * This is a necessary sequence.
+ */
+ udelay(2000);
+ regs = pci_read_config8(MEMCTRL, 0x90);
+ regs |= 0x7;
+ pci_write_config8(MEMCTRL, 0x90, regs);
+ udelay(2000);
+ regs = pci_read_config8(MEMCTRL, 0x90);
+ regs &= ~0x7;
+ regs |= val;
+ pci_write_config8(MEMCTRL, 0x90, regs);
+ udelay(2000);
+ regs = pci_read_config8(MEMCTRL, 0x6b);
+ regs |= 0xc0;
+ regs &= ~0x10;
+ pci_write_config8(MEMCTRL, 0x6b, regs);
+ udelay(1);
+ regs |= 0x10;
+ pci_write_config8(MEMCTRL, 0x6b, regs);
+ udelay(1);
+ regs &= ~0xc0;
+ pci_write_config8(MEMCTRL, 0x6b, regs);
+ regs = pci_read_config8(MEMCTRL, 0x6f);
+ regs |= 0x1;
+ pci_write_config8(MEMCTRL, 0x6f, regs);
+
+ /**********************************************/
+ /* Set DRAM Timing Setting (DDR2 533) */
+ /**********************************************/
+ /* SPD 9 18 23 25 CAS Latency NB3DRAM_REG62[2:0] */
+ /* Read SPD byte 18 CAS Latency */
+ GET_SPD(dimm, spds, regs, SPD_CAS_LAT);
+ printk_debug("\nCAS Supported ");
+ if (spds & SPD_CAS_LAT_2)
+ printk_debug("2 ");
+ if (spds & SPD_CAS_LAT_3)
+ printk_debug("3 ");
+ if (spds & SPD_CAS_LAT_4)
+ printk_debug("4 ");
+ if (spds & SPD_CAS_LAT_5)
+ printk_debug("5 ");
+ if (spds & SPD_CAS_LAT_6)
+ printk_debug("6");
+
+ /* We don't consider CAS = 6, because CX700 doesn't support it */
+ printk_debug("\n CAS:");
+ if (spds & SPD_CAS_LAT_5) {
+ printk_debug("Starting at CL5");
+ val = 0x3;
+ /* See whether we can improve it */
+ GET_SPD(dimm, tmp, regs, SPD_CAS_LAT_MIN_X_1);
+ if ((spds & SPD_CAS_LAT_4) && (tmp < 0x50)) {
+ printk_debug("\n... going to CL4");
+ val = 0x2;
+ }
+ GET_SPD(dimm, tmp, regs, SPD_CAS_LAT_MIN_X_2);
+ if ((spds & SPD_CAS_LAT_3) && (tmp < 0x50)) {
+ printk_debug("\n... going to CL3");
+ val = 0x1;
+ }
+ } else {
+ printk_debug("Starting at CL4");
+ val = 0x2;
+ GET_SPD(dimm, tmp, regs, SPD_CAS_LAT_MIN_X_1);
+ if ((spds & SPD_CAS_LAT_3) && (tmp < 0x50)) {
+ printk_debug("\n... going to CL3");
+ val = 0x1;
+ }
+ GET_SPD(dimm, tmp, regs, SPD_CAS_LAT_MIN_X_2);
+ if ((spds & SPD_CAS_LAT_2) && (tmp < 0x50)) {
+ printk_debug("\n... going to CL2");
+ val = 0x0;
+ }
+ }
+ regs = pci_read_config8(MEMCTRL, 0x62);
+ regs &= ~0x7;
+ regs |= val;
+ pci_write_config8(MEMCTRL, 0x62, regs);
+
+ /* SPD 27 Trp NB3DRAM_REG64[3:2] */
+ GET_SPD(dimm, spds, regs, SPD_TRP);
+ printk_debug("\nTrp %d", spds);
+ spds >>= 2;
+ for (val = 2; val <= 5; val++) {
+ if (spds <= (val * t / 10)) {
+ val = val - 2;
+ break;
+ }
+ }
+ val <<= 2;
+ regs = pci_read_config8(MEMCTRL, 0x64);
+ regs &= ~0xc;
+ regs |= val;
+ pci_write_config8(MEMCTRL, 0x64, regs);
+
+ /* SPD 29 Trcd NB3DRAM_REG64[7:6] */
+ GET_SPD(dimm, spds, regs, SPD_TRCD);
+ printk_debug("\nTrcd %d", spds);
+ spds >>= 2;
+ for (val = 2; val <= 5; val++) {
+ if (spds <= (val * t / 10)) {
+ val = val - 2;
+ break;
+ }
+ }
+ val <<= 6;
+ regs = pci_read_config8(MEMCTRL, 0x64);
+ regs &= ~0xc0;
+ regs |= val;
+ pci_write_config8(MEMCTRL, 0x64, regs);
+
+ /* SPD 30 Tras NB3DRAM_REG62[7:4] */
+ GET_SPD(dimm, spds, regs, SPD_TRAS);
+ printk_debug("\nTras %d", spds);
+ for (val = 5; val <= 20; val++) {
+ if (spds <= (val * t / 10)) {
+ val = val - 5;
+ break;
+ }
+ }
+ val <<= 4;
+ regs = pci_read_config8(MEMCTRL, 0x62);
+ regs &= ~0xf0;
+ regs |= val;
+ pci_write_config8(MEMCTRL, 0x62, regs);
+
+ /* SPD 42 SPD 40 Trfc NB3DRAM_REG61[5:0] */
+ GET_SPD(dimm, spds, regs, SPD_TRFC);
+ printk_debug("\nTrfc %d", spds);
+ tmp = spds;
+ GET_SPD(dimm, spds, regs, SPD_EX_TRC_TRFC);
+ if (spds & 0x1)
+ tmp += 256;
+ if (spds & 0xe)
+ tmp++;
+ for (val = 8; val <= 71; val++) {
+ if (tmp <= (val * t / 10)) {
+ val = val - 8;
+ break;
+ }
+ }
+ regs = pci_read_config8(MEMCTRL, 0x61);
+ regs &= ~0x3f;
+ regs |= val;
+ pci_write_config8(MEMCTRL, 0x61, regs);
+
+ /* SPD 28 Trrd NB3DRAM_REG63[7:6] */
+ GET_SPD(dimm, spds, regs, SPD_TRRD);
+ for (val = 2; val <= 5; val++) {
+ if (spds <= (val * t / 10)) {
+ val = val - 2;
+ break;
+ }
+ }
+ val <<= 6;
+ printk_debug("\nTrrd val = 0x%x", val);
+ regs = pci_read_config8(MEMCTRL, 0x63);
+ regs &= ~0xc0;
+ regs |= val;
+ pci_write_config8(MEMCTRL, 0x63, regs);
+
+ /* SPD 36 Twr NB3DRAM_REG61[7:6] */
+ GET_SPD(dimm, spds, regs, SPD_TWR);
+ for (val = 2; val <= 5; val++) {
+ if (spds <= (val * t / 10)) {
+ val = val - 2;
+ break;
+ }
+ }
+ val <<= 6;
+ printk_debug("\nTwr val = 0x%x", val);
+
+ regs = pci_read_config8(MEMCTRL, 0x61);
+ regs &= ~0xc0;
+ regs |= val;
+ pci_write_config8(MEMCTRL, 0x61, regs);
+
+ /* SPD 37 Twtr NB3DRAM_REG63[1] */
+ GET_SPD(dimm, spds, regs, SPD_TWTR);
+ spds >>= 2;
+ printk_debug("\nTwtr 0x%x", spds);
+ if (spds <= (t * 2 / 10))
+ val = 0;
+ else
+ val = 1;
+ val <<= 1;
+ printk_debug("\nTwtr val = 0x%x", val);
+
+ regs = pci_read_config8(MEMCTRL, 0x63);
+ regs &= ~0x2;
+ regs |= val;
+ pci_write_config8(MEMCTRL, 0x63, regs);
+
+ /* SPD 38 Trtp NB3DRAM_REG63[3] */
+ GET_SPD(dimm, spds, regs, SPD_TRTP);
+ spds >>= 2;
+ printk_debug("\nTrtp 0x%x", spds);
+ if (spds <= (t * 2 / 10))
+ val = 0;
+ else
+ val = 1;
+ val <<= 3;
+ printk_debug("\nTrtp val = 0x%x", val);
+
+ regs = pci_read_config8(MEMCTRL, 0x63);
+ regs &= ~0x8;
+ regs |= val;
+ pci_write_config8(MEMCTRL, 0x63, regs);
+
+ /**********************************************/
+ /* Set DRAM DRDY Setting */
+ /**********************************************/
+ /* Write slowest value to register */
+ tmp = sizeof(Host_Reg_Val) / sizeof(Host_Reg_Val[0]);
+ for (val = 0; val < tmp; val += 2)
+ pci_write_config8(HOSTCTRL, Host_Reg_Val[val], Host_Reg_Val[val + 1]);
+
+ /* F2_RX51[7]=0, disable DRDY timing */
+ regs = pci_read_config8(HOSTCTRL, 0x51);
+ regs &= ~0x80;
+ pci_write_config8(HOSTCTRL, 0x51, regs);
+
+ /**********************************************/
+ /* Set DRAM BurstLength */
+ /**********************************************/
+ regs = pci_read_config8(MEMCTRL, 0x6c);
+ for (dimm = 0; dimm < DIMM_SOCKETS; dimm++) {
+ if (pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_REG_BASE + (dimm << 1)))) {
+ spds = get_spd_data(ctrl, dimm, 16);
+ if (!(spds & 0x8))
+ break;
+ }
+ }
+ if (dimm == 2)
+ regs |= 0x8;
+ pci_write_config8(MEMCTRL, 0x6c, regs);
+ val = pci_read_config8(HOSTCTRL, 0x54);
+ val &= ~0x10;
+ if (dimm == 2)
+ val |= 0x10;
+ pci_write_config8(HOSTCTRL, 0x54, val);
+
+ /**********************************************/
+ /* Set DRAM Driving Setting */
+ /**********************************************/
+ /* DRAM Timing ODT */
+ tmp = sizeof(Dram_Driving_ODT_CTRL) / sizeof(Dram_Driving_ODT_CTRL[0]);
+ for (val = 0; val < tmp; val += 2)
+ pci_write_config8(MEMCTRL, Dram_Driving_ODT_CTRL[val],
+ Dram_Driving_ODT_CTRL[val + 1]);
+
+ regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_RANK_NUM);
+ val = pci_read_config8(MEMCTRL, 0xd5);
+ val &= ~0xaa;
+ switch (regs) {
+ case 3:
+ case 2:
+ val |= 0xa0;
+ break;
+ default:
+ val |= 0x80;
+ }
+ regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_DIMM_NUM);
+ if (regs == 1)
+ val |= 0xa;
+ pci_write_config8(MEMCTRL, 0xd5, val);
+
+ regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_DIMM_NUM);
+ val = pci_read_config8(MEMCTRL, 0xd6);
+ val &= ~0x2;
+ if (regs == 1)
+ val |= 0x2;
+ pci_write_config8(MEMCTRL, 0xd6, val);
+
+ regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_RANK_MAP);
+ tmp = sizeof(ODT_TBL) / sizeof(ODT_TBL[0]);
+ for (val = 0; val < tmp; val += 3) {
+ if (regs == ODT_TBL[val]) {
+ pci_write_config8(MEMCTRL, 0xd8, ODT_TBL[val + 1]);
+ /* Store DRAM & NB ODT setting in d0f4_Rxd8 */
+ pci_write_config8(PCI_DEV(0, 0, 4), SCRATCH_DRAM_NB_ODT, ODT_TBL[val + 2]);
+ break;
+ }
+ }
+
+ pci_write_config8(MEMCTRL, 0xd9, 0x0a);
+ regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_RANK_NUM);
+ regs--;
+ regs = regs << 1;
+ pci_write_config8(MEMCTRL, 0xe0, DQS_DQ_TBL[regs++]);
+ pci_write_config8(MEMCTRL, 0xe2, DQS_DQ_TBL[regs]);
+
+ /* DRAM Timing CS */
+ pci_write_config8(MEMCTRL, 0xe4, 0x66);
+
+ /* DRAM Timing MAA */
+ val = 0;
+ for (dimm = 0; dimm < DIMM_SOCKETS; dimm++) {
+ if (pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_REG_BASE + (dimm << 1)))) {
+ spds = get_spd_data(ctrl, dimm, SPD_PRI_WIDTH);
+ spds = 64 / spds;
+ if (pci_read_config8
+ (PCI_DEV(0, 0, 4), (SCRATCH_REG_BASE + (dimm << 1) + 1)))
+ spds = spds << 1;
+ val += spds;
+ }
+ }
+ printk_debug("\nchip #%d", val);
+ if (val > 18)
+ regs = 0xdb;
+ else
+ regs = 0x86;
+ pci_write_config8(MEMCTRL, 0xe8, regs);
+
+ /* DRAM Timing MAB */
+ pci_write_config8(MEMCTRL, 0xe9, 0x0);
+
+ /* DRAM Timing DCLK VT8454C always 0x66 */
+ pci_write_config8(MEMCTRL, 0xe6, 0xaa);
+
+ /**********************************************/
+ /* Set DRAM Duty Control */
+ /**********************************************/
+ regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_RANK_NUM);
+ switch (regs) {
+ case 1:
+ case 2: /* 1~2 rank */
+ val = 0;
+ break;
+ case 3:
+ case 4: /* 3~4 rank */
+ regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_DRAM_FREQ);
+ if (regs == DDRII_533)
+ val = 4;
+ else /* DDRII-400 */
+ val = 0;
+ break;
+ }
+ regs = 0xec;
+ for (t = 0; t < 4; t++) {
+ pci_write_config8(MEMCTRL, regs, Duty_Control_DDR2[val]);
+ regs++;
+ val++;
+ }
+
+ /**********************************************/
+ /* Set DRAM Clock Control */
+ /**********************************************/
+ /* Write Data Phase */
+ val = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_DRAM_FREQ);
+ regs = pci_read_config8(MEMCTRL, 0x75);
+ regs &= 0xf0;
+ switch (val) {
+ case DDRII_533:
+ pci_write_config8(MEMCTRL, 0x74, 0x07);
+ regs |= 0x7;
+ break;
+ case DDRII_400:
+ default:
+ pci_write_config8(MEMCTRL, 0x74, 0x05);
+ regs |= 0x5;
+ break;
+ }
+ pci_write_config8(MEMCTRL, 0x75, regs);
+ pci_write_config8(MEMCTRL, 0x76, 0x80);
+
+ /* Clock Phase Control for FeedBack Mode */
+ regs = pci_read_config8(MEMCTRL, 0x90);
+// regs |= 0x80;
+ pci_write_config8(MEMCTRL, 0x90, regs);
+
+ regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_DRAM_FREQ);
+ switch (regs) {
+ case DDRII_533:
+ regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_RANK_NUM);
+ if (regs == 1)
+ val = 0;
+ else
+ val = 3;
+ break;
+ case DDRII_400:
+ default:
+ val = 6;
+ break;
+ }
+ regs = pci_read_config8(MEMCTRL, 0x91);
+ regs &= ~0xc0;
+ regs |= 0x80;
+ pci_write_config8(MEMCTRL, 0x91, regs);
+ regs = 0x91;
+ for (t = 0; t < 3; t++) {
+ dimm = pci_read_config8(MEMCTRL, regs);
+ dimm &= ~0x7;
+ dimm |= ChA_Clk_Phase_DDR2_Table[val];
+ pci_write_config8(MEMCTRL, regs, dimm);
+ regs++;
+ val++;
+ }
+
+ pci_write_config8(MEMCTRL, 0x97, 0x12);
+ pci_write_config8(MEMCTRL, 0x98, 0x33);
+
+ regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_RANK_0);
+ val = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_RANK_2);
+ if (regs && val)
+ pci_write_config8(MEMCTRL, 0x9d, 0x00);
+ else
+ pci_write_config8(MEMCTRL, 0x9d, 0x0f);
+
+ tmp = sizeof(DQ_DQS_Table) / sizeof(DQ_DQS_Table[0]);
+ for (val = 0; val < tmp; val += 2)
+ pci_write_config8(MEMCTRL, DQ_DQS_Table[val], DQ_DQS_Table[val + 1]);
+ regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_DRAM_FREQ);
+ if (regs == DDRII_533)
+ pci_write_config8(MEMCTRL, 0x7b, 0xa0);
+ else
+ pci_write_config8(MEMCTRL, 0x7b, 0x10);
+
+ /***************************************************/
+ /* Set necessary register before DRAM initialize */
+ /***************************************************/
+ tmp = sizeof(Mem_Reg_Init) / sizeof(Mem_Reg_Init[0]);
+ for (val = 0; val < tmp; val += 3) {
+ regs = pci_read_config8(MEMCTRL, Mem_Reg_Init[val]);
+ regs &= Mem_Reg_Init[val + 1];
+ regs |= Mem_Reg_Init[val + 2];
+ pci_write_config8(MEMCTRL, Mem_Reg_Init[val], regs);
+ }
+ regs = pci_read_config8(HOSTCTRL, 0x51);
+ regs &= 0xbf; // Clear bit 6 Disable Read Around Write
+ pci_write_config8(HOSTCTRL, 0x51, regs);
+
+ regs = pci_read_config8(HOSTCTRL, 0x54);
+ t = regs >> 5;
+ val = pci_read_config8(HOSTCTRL, 0x57);
+ dimm = val >> 5;
+ if (t == dimm)
+ t = 0x0;
+ else
+ t = 0x1;
+ regs &= ~0x1;
+ regs |= t;
+ val &= ~0x1;
+ val |= t;
+ pci_write_config8(HOSTCTRL, 0x57, val);
+
+ regs = pci_read_config8(HOSTCTRL, 0x51);
+ regs |= t;
+ pci_write_config8(HOSTCTRL, 0x51, regs);
+
+ regs = pci_read_config8(MEMCTRL, 0x90);
+ regs &= 0x7;
+ val = 0;
+ if (regs < 0x2)
+ val = 0x80;
+ regs = pci_read_config8(MEMCTRL, 0x76);
+ regs &= 0x80;
+ regs |= val;
+ pci_write_config8(MEMCTRL, 0x76, regs);
+
+ regs = pci_read_config8(MEMCTRL, 0x6f);
+ regs |= 0x10;
+ pci_write_config8(MEMCTRL, 0x6f, regs);
+
+ /***************************************************/
+ /* Find suitable DQS value for ChA and ChB */
+ /***************************************************/
+ // Set DQS output delay for Channel A
+ regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_DRAM_FREQ);
+ val = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_RANK_NUM);
+ switch (regs) {
+ case DDRII_533:
+ if (val < 2)
+ val = 0;
+ else
+ val = 2;
+ break;
+ case DDRII_400:
+ default:
+ if (val < 2)
+ val = 4;
+ else
+ val = 6;
+ break;
+ }
+ for (t = 0; t < 2; t++)
+ pci_write_config8(MEMCTRL, (0x70 + t), DQSOChA_DDR2_Driving_Table[val + t]);
+ // Set DQS output delay for Channel B
+ pci_write_config8(MEMCTRL, 0x72, 0x0);
+
+ regs = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_RANK_0);
+ val = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_RANK_2);
+ if (regs && val)
+ pci_write_config8(MEMCTRL, 0x73, 0xfd);
+ else
+ pci_write_config8(MEMCTRL, 0x73, 0x01);
+}
+
+static void sdram_set_registers(const struct mem_controller *ctrl)
+{
+ c7_cpu_setup(ctrl);
+ ddr_detect(ctrl);
+ sdram_set_safe_values(ctrl);
+}
+
+static void step_20_21(const struct mem_controller *ctrl)
+{
+ u8 val;
+
+ // Step 20
+ udelay(200);
+
+ val = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_DRAM_NB_ODT);
+ if (val & DDR2_ODT_150ohm)
+ read32(0x102200);
+ else
+ read32(0x102020);
+
+ /* Step 21. Normal operation */
+ print_spew("RAM Enable 5: Normal operation\n");
+ do_ram_command(ctrl, RAM_COMMAND_NORMAL);
+ udelay(3);
+}
+
+static void step_2_19(const struct mem_controller *ctrl)
+{
+ u32 i;
+ u8 val;
+
+ // Step 2
+ val = pci_read_config8(MEMCTRL, 0x69);
+ val &= ~0x03;
+ pci_write_config8(MEMCTRL, 0x69, val);
+
+ /* Step 3 Apply NOP. */
+ print_spew("RAM Enable 1: Apply NOP\n");
+ do_ram_command(ctrl, RAM_COMMAND_NOP);
+
+ udelay(15);
+
+ // Step 4
+ print_spew("SEND: ");
+ read32(0);
+ print_spew("OK\n");
+
+ // Step 5
+ udelay(400);
+
+ /* 6. Precharge all. Wait tRP. */
+ print_spew("RAM Enable 2: Precharge all\n");
+ do_ram_command(ctrl, RAM_COMMAND_PRECHARGE);
+
+ // Step 7
+ print_spew("SEND: ");
+ read32(0);
+ print_spew("OK\n");
+
+ /* Step 8. Mode register set. */
+ print_spew("RAM Enable 4: Mode register set\n");
+ do_ram_command(ctrl, RAM_COMMAND_MRS); //enable dll
+
+ // Step 9
+ print_spew("SEND: ");
+
+ val = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_DRAM_NB_ODT);
+ if (val & DDR2_ODT_150ohm)
+ read32(0x102200); //DDR2_ODT_150ohm
+ else
+ read32(0x102020);
+ print_spew("OK\n");
+
+ // Step 10
+ print_spew("SEND: ");
+ read32(0x800);
+ print_spew("OK\n");
+
+ /* Step 11. Precharge all. Wait tRP. */
+ print_spew("RAM Enable 2: Precharge all\n");
+ do_ram_command(ctrl, RAM_COMMAND_PRECHARGE);
+
+ // Step 12
+ print_spew("SEND: ");
+ read32(0x0);
+ print_spew("OK\n");
+
+ /* Step 13. Perform 8 refresh cycles. Wait tRC each time. */
+ print_spew("RAM Enable 3: CBR\n");
+ do_ram_command(ctrl, RAM_COMMAND_CBR);
+
+ /* JEDEC says only twice, do 8 times for posterity */
+ // Step 16: Repeat Step 14 and 15 another 7 times
+ for (i = 0; i < 8; i++) {
+ // Step 14
+ read32(0);
+ print_spew(".");
+
+ // Step 15
+ udelay(100);
+ }
+
+ /* Step 17. Mode register set. Wait 200us. */
+ print_spew("\nRAM Enable 4: Mode register set\n");
+
+ //safe value for now, BL=8, WR=4, CAS=4
+ do_ram_command(ctrl, RAM_COMMAND_MRS);
+ udelay(200);
+
+ /* Use Single Chanel temporarily */
+ val = pci_read_config8(MEMCTRL, 0x6c);
+ if (val & 0x8) { /* Burst Length = 8 */
+ val = pci_read_config8(MEMCTRL, 0x62);
+ val &= 0x7;
+ i = DDR2_MRS_table[4 + val];
+ } else {
+ val = pci_read_config8(MEMCTRL, 0x62);
+ val &= 0x7;
+ i = DDR2_MRS_table[val];
+ }
+
+ // Step 18
+ val = pci_read_config8(MEMCTRL, 0x61);
+ val = val >> 6;
+ i |= DDR2_Twr_table[val];
+ read32(i);
+
+ printk_debug("MRS = %08x\n", i);
+
+ udelay(15);
+
+ // Step 19
+ val = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_DRAM_NB_ODT);
+ if (val & DDR2_ODT_150ohm)
+ read32(0x103e00); //EMRS OCD Default
+ else
+ read32(0x103c20);
+}
+
+static void sdram_set_vr(const struct mem_controller *ctrl, u8 num)
+{
+ u8 reg, val;
+ val = 0x54 + (num >> 1);
+ reg = pci_read_config8(MEMCTRL, val);
+ reg &= (0xf << (4 * (num & 0x1)));
+ reg |= (((0x8 | num) << 4) >> (4 * (num & 0x1)));
+ pci_write_config8(MEMCTRL, val, reg);
+}
+static void sdram_ending_addr(const struct mem_controller *ctrl, u8 num)
+{
+ u8 reg, val;
+ /* Set Ending Address */
+ val = 0x40 + num;
+ reg = pci_read_config8(MEMCTRL, val);
+ reg += 0x10;
+ pci_write_config8(MEMCTRL, val, reg);
+ /* Set Beginning Address */
+ val = 0x48 + num;
+ pci_write_config8(MEMCTRL, val, 0x0);
+}
+
+static void sdram_clear_vr_addr(const struct mem_controller *ctrl, u8 num)
+{
+ u8 reg, val;
+ val = 0x54 + (num >> 1);
+ reg = pci_read_config8(MEMCTRL, val);
+ reg = ~(0x80 >> (4 * (num & 0x1)));
+ pci_write_config8(MEMCTRL, val, reg);
+ val = 0x40 + num;
+ reg = pci_read_config8(MEMCTRL, val);
+ reg -= 0x10;
+ pci_write_config8(MEMCTRL, val, reg);
+ val = 0x48 + num;
+ pci_write_config8(MEMCTRL, val, 0x0);
+}
+
+/* Perform sizing DRAM by dynamic method */
+static void sdram_calc_size(const struct mem_controller *ctrl, u8 num)
+{
+ u8 ca, ra, ba, reg;
+ ba = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_FLAGS);
+ if (ba == 8) {
+ write8(0, 0x0d);
+ ra = read8(0);
+ write8((1 << SDRAM1X_RA_12_8bk), 0x0c);
+ ra = read8(0);
+
+ write8(0, 0x0a);
+ ca = read8(0);
+ write8((1 << SDRAM1X_CA_09_8bk), 0x0c);
+ ca = read8(0);
+
+ write8(0, 0x03);
+ ba = read8(0);
+ write8((1 << SDRAM1X_BA2_8bk), 0x02);
+ ba = read8(0);
+ write8((1 << SDRAM1X_BA1_8bk), 0x01);
+ ba = read8(0);
+ } else {
+ write8(0, 0x0f);
+ ra = read8(0);
+ write8((1 << SDRAM1X_RA_14), 0x0e);
+ ra = read8(0);
+ write8((1 << SDRAM1X_RA_13), 0x0d);
+ ra = read8(0);
+ write8((1 << SDRAM1X_RA_12), 0x0c);
+ ra = read8(0);
+
+ write8(0, 0x0c);
+ ca = read8(0);
+ write8((1 << SDRAM1X_CA_12), 0x0b);
+ ca = read8(0);
+ write8((1 << SDRAM1X_CA_11), 0x0a);
+ ca = read8(0);
+ write8((1 << SDRAM1X_CA_09), 0x09);
+ ca = read8(0);
+
+ write8(0, 0x02);
+ ba = read8(0);
+ write8((1 << SDRAM1X_BA1), 0x01);
+ ba = read8(0);
+ }
+
+ if (ra < 10 || ra > 15)
+ die("bad RA");
+ if (ca < 8 || ca > 12)
+ die("bad CA");
+ if (ba < 1 || ba > 3)
+ die("bad BA");
+
+ /* Calculate MA type save to scratch register */
+ reg = 0;
+
+ switch (ra) {
+ case 12:
+ reg |= MA_12_Row;
+ break;
+ case 13:
+ reg |= MA_13_Row;
+ break;
+ case 14:
+ reg |= MA_14_Row;
+ break;
+ default:
+ reg |= MA_15_Row;
+ }
+
+ switch (ca) {
+ case 9:
+ reg |= MA_9_Column;
+ break;
+ case 10:
+ reg |= MA_10_Column;
+ break;
+ case 11:
+ reg |= MA_11_Column;
+ break;
+ default:
+ reg |= MA_12_Column;
+ }
+
+ switch (ba) {
+ case 3:
+ reg |= MA_8_Bank;
+ break;
+ default:
+ reg |= MA_4_Bank;
+ }
+
+ pci_write_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK0_MA_REG + num), reg);
+
+ if (ra >= 13)
+ pci_write_config8(PCI_DEV(0, 0, 4), SCRATCH_DRAM_256M_BIT, 1);
+
+ /* Calculate rank size save to scratch register */
+ ra = ra + ca + ba + 3 - 26; /* 1 unit = 64M */
+ ra = 1 << ra;
+ pci_write_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK0_SIZE_REG + num), ra);
+}
+
+static void sdram_enable(const struct mem_controller *ctrl)
+{
+ u8 reg8;
+ u8 val, i;
+ device_t dev;
+ u8 dl, dh;
+ u32 quot;
+
+ /* Init Present Bank */
+ val = sizeof(Init_Rank_Reg_Table) / sizeof(Init_Rank_Reg_Table[0]);
+ for (i = 0; i < val; i++)
+ pci_write_config8(MEMCTRL, Init_Rank_Reg_Table[i], 0x0);
+
+ /* Init other banks */
+ for (i = 0; i < 4; i++) {
+ reg8 = pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK_0 + i));
+ if (reg8) {
+ sdram_set_vr(ctrl, i);
+ sdram_ending_addr(ctrl, i);
+ step_2_19(ctrl);
+ step_20_21(ctrl);
+ sdram_clear_vr_addr(ctrl, i);
+ }
+ }
+
+#ifdef MEM_WIDTH_32BIT_MODE
+ /****************************************************************/
+ /* Set Dram 32bit Mode */
+ /****************************************************************/
+ reg8 = pci_read_config8(MEMCTRL, 0x6c);
+ reg8 |= 0x20;
+ pci_write_config(MEMCTRL, 0x6c, reg8);
+#endif
+
+ /****************************************************************/
+ /* Find the DQSI Low/High bound and save it to Scratch register */
+ /****************************************************************/
+ for (dl = 0; dl < 0x3f; dl += 2) {
+ reg8 = dl & 0x3f;
+ reg8 |= 0x80; /* Set Manual Mode */
+ pci_write_config8(MEMCTRL, 0x77, reg8);
+ for (i = 0; i < 4; i++) {
+ reg8 = pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK_0 + i));
+ if (reg8) {
+ sdram_set_vr(ctrl, i);
+ sdram_ending_addr(ctrl, i);
+ write32(0, 0x55555555);
+ write32(4, 0x55555555);
+ udelay(15);
+ if (read32(0) != 0x55555555)
+ break;
+ if (read32(4) != 0x55555555)
+ break;
+ write32(0, 0xaaaaaaaa);
+ write32(4, 0xaaaaaaaa);
+ udelay(15);
+ if (read32(0) != 0xaaaaaaaa)
+ break;
+ if (read32(4) != 0xaaaaaaaa)
+ break;
+ sdram_clear_vr_addr(ctrl, i);
+ }
+ }
+ if (i == 4)
+ break;
+ else
+ sdram_clear_vr_addr(ctrl, i);
+ }
+ printk_debug("\nDQSI Low %08x", dl);
+ for (dh = dl; dh < 0x3f; dh += 2) {
+ reg8 = dh & 0x3f;
+ reg8 |= 0x80; /* Set Manual Mode */
+ pci_write_config8(MEMCTRL, 0x77, reg8);
+ for (i = 0; i < 4; i++) {
+ reg8 = pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK_0 + i));
+ if (reg8) {
+ sdram_set_vr(ctrl, i);
+ sdram_ending_addr(ctrl, i);
+
+ write32(0, 0x55555555);
+ write32(4, 0x55555555);
+ udelay(15);
+ if (read32(0) != 0x55555555)
+ break;
+ if (read32(4) != 0x55555555)
+ break;
+ write32(0, 0xaaaaaaaa);
+ write32(4, 0xaaaaaaaa);
+ udelay(15);
+ if (read32(0) != 0xaaaaaaaa)
+ break;
+ if (read32(4) != 0xaaaaaaaa)
+ break;
+ sdram_clear_vr_addr(ctrl, i);
+ }
+ }
+ if (i != 4) {
+ sdram_clear_vr_addr(ctrl, i);
+ break;
+ }
+ }
+ printk_debug("\nDQSI High %02x", dh);
+ pci_write_config8(PCI_DEV(0, 0, 4), SCRATCH_CHA_DQSI_LOW_REG, dl);
+ pci_write_config8(PCI_DEV(0, 0, 4), SCRATCH_CHA_DQSI_HIGH_REG, dh);
+ reg8 = pci_read_config8(MEMCTRL, 0X90) & 0X7;
+ val = DQSI_Rate_Table[reg8];
+ quot = dh - dl;
+ quot = quot * val;
+ quot >>= 4;
+ val = quot + dl;
+ pci_write_config8(PCI_DEV(0, 0, 4), SCRATCH_ChA_DQSI_REG, val);
+ reg8 = val & 0x3f;
+ reg8 |= 0x80;
+ pci_write_config8(MEMCTRL, 0x77, reg8);
+
+ /****************************************************************/
+ /* Find out the lowest Bank Interleave and Set Register */
+ /****************************************************************/
+#if 0
+ //TODO
+ reg8 = pci_read_config8(MEMCTRL, 0x69);
+ reg8 &= ~0xc0;
+ reg8 |= 0x80; //8 banks
+ pci_write_config8(MEMCTRL, 0x69, reg8);
+#endif
+ dl = 2;
+ for (i = 0; i < 4; i++) {
+ reg8 = pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK_0 + i));
+ if (reg8) {
+ reg8 = get_spd_data(ctrl, (i >> 1), 17);
+ sdram_set_vr(ctrl, i);
+ sdram_ending_addr(ctrl, i);
+ if (reg8 == 4) {
+ write8(0, 0x02);
+ val = read8(0);
+ write8((1 << SDRAM1X_BA1), 0x01);
+ val = read8(0);
+ } else {
+ write8(0, 0x03);
+ val = read8(0);
+ write8((1 << SDRAM1X_BA2_8bk), 0x02);
+ val = read8(0);
+ write8((1 << SDRAM1X_BA1_8bk), 0x01);
+ val = read8(0);
+ }
+ if (val < dl)
+ dl = val;
+ sdram_clear_vr_addr(ctrl, i);
+ }
+ }
+ dl <<= 6;
+ reg8 = pci_read_config8(MEMCTRL, 0x69);
+ reg8 &= ~0xc0;
+ reg8 |= dl;
+ pci_write_config8(MEMCTRL, 0x69, reg8);
+
+ /****************************************************************/
+ /* DRAM Sizing and Fill MA type */
+ /****************************************************************/
+ for (i = 0; i < 4; i++) {
+ val = pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK_0 + i));
+ if (val) {
+ reg8 = get_spd_data(ctrl, (i >> 1), 17);
+ pci_write_config8(PCI_DEV(0, 0, 4), SCRATCH_FLAGS, reg8);
+ if (reg8 == 4) {
+ /* Use MA Type 3 for DRAM sizing */
+ reg8 = pci_read_config8(MEMCTRL, 0x50);
+ reg8 &= 0x11;
+ reg8 |= 0x66;
+ pci_write_config8(MEMCTRL, 0x50, reg8);
+ pci_write_config8(MEMCTRL, 0x51, reg8);
+ } else {
+ /* Use MA Type 5 for DRAM sizing */
+ reg8 = pci_read_config8(MEMCTRL, 0x50);
+ reg8 &= 0x11;
+ reg8 |= 0xaa;
+ pci_write_config8(MEMCTRL, 0x50, reg8);
+ pci_write_config8(MEMCTRL, 0x51, reg8);
+ reg8 = pci_read_config8(MEMCTRL, 0x53);
+ reg8 &= 0x0f;
+ reg8 |= 0x90;
+ pci_write_config8(MEMCTRL, 0x53, reg8);
+ }
+ sdram_set_vr(ctrl, i);
+ val = 0x40 + i;
+ reg8 = pci_read_config8(MEMCTRL, val);
+ /* max size 3G for new MA table */
+ reg8 += 0x30;
+ pci_write_config8(MEMCTRL, val, reg8);
+ /* Set Beginning Address */
+ val = 0x48 + i;
+ pci_write_config8(MEMCTRL, val, 0x0);
+
+ sdram_calc_size(ctrl, i);
+
+ /* Clear */
+ val = 0x54 + (i >> 1);
+ reg8 = pci_read_config8(MEMCTRL, val);
+ reg8 = ~(0x80 >> (4 * (i & 0x1)));
+ pci_write_config8(MEMCTRL, val, reg8);
+ val = 0x40 + i;
+ reg8 = pci_read_config8(MEMCTRL, val);
+ reg8 -= 0x30;
+ pci_write_config8(MEMCTRL, val, reg8);
+ val = 0x48 + i;
+ pci_write_config8(MEMCTRL, val, 0x0);
+
+ }
+ }
+ /* Clear MA Type */
+ reg8 = pci_read_config8(MEMCTRL, 0x50);
+ reg8 &= 0x11;
+ pci_write_config8(MEMCTRL, 0x50, reg8);
+ pci_write_config8(MEMCTRL, 0x51, reg8);
+ reg8 = pci_read_config8(MEMCTRL, 0x6b);
+ reg8 &= ~0x08;
+ pci_write_config8(MEMCTRL, 0x6b, reg8);
+
+ /****************************************************************/
+ /* DRAM re-initialize for burst length */
+ /****************************************************************/
+ for (i = 0; i < 4; i++) {
+ reg8 = pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK_0 + i));
+ if (reg8) {
+ sdram_set_vr(ctrl, i);
+ sdram_ending_addr(ctrl, i);
+ step_2_19(ctrl);
+ step_20_21(ctrl);
+ sdram_clear_vr_addr(ctrl, i);
+ }
+ }
+
+ /****************************************************************/
+ /* Set the MA Type */
+ /****************************************************************/
+ reg8 = pci_read_config8(MEMCTRL, 0x50);
+ reg8 &= 0x11;
+ pci_write_config8(MEMCTRL, 0x50, reg8);
+
+ reg8 = pci_read_config8(MEMCTRL, 0x51);
+ reg8 &= 0x11;
+ pci_write_config8(MEMCTRL, 0x51, reg8);
+
+ reg8 = pci_read_config8(MEMCTRL, 0x6b);
+ reg8 &= ~0x08;
+ pci_write_config8(MEMCTRL, 0x6b, reg8);
+
+ for (i = 0; i < 4; i += 2) {
+ reg8 = pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK_0 + i));
+ if (reg8) {
+ reg8 = pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK0_MA_REG + i));
+ reg8 &= (MA_Bank + MA_Column);
+ val = pci_read_config8(MEMCTRL, 0x50);
+ if (i == 0) {
+ reg8 <<= 4;
+ val &= 0x1f;
+ } else
+ val &= 0xf1;
+ val |= reg8;
+ pci_write_config8(MEMCTRL, 0x50, val);
+ }
+ }
+
+ /****************************************************************/
+ /* Set Start and Ending Address */
+ /****************************************************************/
+ dl = 0; /* Begin Address */
+ dh = 0; /* Ending Address */
+ for (i = 0; i < 4; i++) {
+ reg8 = pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK_0 + i));
+ if (reg8) {
+ reg8 = pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK0_SIZE_REG + i));
+ if (reg8 == 0)
+ continue;
+ dh += reg8;
+ pci_write_config8(MEMCTRL, (0x40 + i), dh);
+ pci_write_config8(MEMCTRL, (0x48 + i), dl);
+ dl = dh;
+ }
+ }
+ dh <<= 2;
+ // F7_Rx57 Ending address mirror register
+ pci_write_config8(PCI_DEV(0, 0, 7), 0x57, dh);
+ dev = pci_locate_device(PCI_ID(0x1106, 0x324e), 0);
+ pci_write_config8(dev, 0x57, dh);
+ // LOW TOP Address
+ pci_write_config8(MEMCTRL, 0x88, dh);
+ pci_write_config8(MEMCTRL, 0x85, dh);
+ // also program vlink mirror
+ pci_write_config8(PCI_DEV(0, 0, 7), 0xe5, dh);
+
+ /****************************************************************/
+ /* Set Physical to Virtual Rank mapping */
+ /****************************************************************/
+ pci_write_config32(MEMCTRL, 0x54, 0x0);
+ for (i = 0; i < 4; i++) {
+ reg8 = pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK_0 + i));
+ if (reg8) {
+ reg8 = pci_read_config8(MEMCTRL, (0x54 + (i >> 1)));
+ if (i & 0x1) { /* Odd Rank */
+ reg8 &= 0xf0;
+ reg8 |= (0x8 | i);
+ } else { /* Even Rank */
+
+ reg8 &= 0x0f;
+ reg8 |= ((0x8 | i) << 4);
+ }
+ pci_write_config8(MEMCTRL, (0x54 + (i >> 1)), reg8);
+ }
+ }
+
+ /****************************************************************/
+ /* Set DRAM Refresh Counter */
+ /****************************************************************/
+ val = pci_read_config8(MEMCTRL, 0X90) & 0X7;
+ val <<= 1;
+ reg8 = pci_read_config8(PCI_DEV(0, 0, 4), SCRATCH_DRAM_256M_BIT);
+ if (reg8)
+ val++;
+ pci_write_config8(MEMCTRL, 0x6a, REFC_Table[val]);
+
+ /****************************************************************/
+ /* Chipset Performance UP and other setting after DRAM Sizing */
+ /****************************************************************/
+ /* Dram Registers */
+ val = sizeof(Dram_Table) / sizeof(Dram_Table[0]);
+ for (i = 0; i < val; i += 3) {
+ reg8 = pci_read_config8(MEMCTRL, Dram_Table[i]);
+ reg8 &= Dram_Table[i + 1];
+ reg8 |= Dram_Table[i + 2];
+ pci_write_config8(MEMCTRL, Dram_Table[i], reg8);
+ }
+
+ /* Host Registers */
+ val = sizeof(Host_Table) / sizeof(Host_Table[0]);
+ for (i = 0; i < val; i += 3) {
+ reg8 = pci_read_config8(HOSTCTRL, Host_Table[i]);
+ reg8 &= Host_Table[i + 1];
+ reg8 |= Host_Table[i + 2];
+ pci_write_config8(HOSTCTRL, Host_Table[i], reg8);
+ }
+
+ /* PM Registers */
+#ifdef SETUP_PM_REGISTERS
+ val = sizeof(PM_Table) / sizeof(PM_Table[0]);
+ for (i = 0; i < val; i += 3) {
+ reg8 = pci_read_config8(PCI_DEV(0, 0, 4), PM_Table[i]);
+ reg8 &= PM_Table[i + 1];
+ reg8 |= PM_Table[i + 2];
+ pci_write_config8(PCI_DEV(0, 0, 4), PM_Table[i], reg8);
+ }
+#endif
+ pci_write_config8(HOSTCTRL, 0x5d, 0xb2);
+
+ /****************************************************************/
+ /* UMA registers for N-series projects */
+ /****************************************************************/
+
+ /* Manual setting frame buffer bank */
+ for (i = 0; i < 4; i++) {
+ reg8 = pci_read_config8(PCI_DEV(0, 0, 4), (SCRATCH_RANK_0 + i));
+ if (reg8)
+ val = i;
+ }
+ pci_write_config8(MEMCTRL, 0xb0, val);
+ reg8 = 0x40; // Frame buffer size 64M
+ reg8 |= 0x80; // VGA Enable
+ reg8 |= 0x0a; // A[31:28] = 1010b
+ pci_write_config8(MEMCTRL, 0xa1, reg8);
+
+#ifdef ECC
+ // Clear Ecc
+ outl(0x80000180, 0xcf8);
+ outb(0xff, 0xcfc);
+ // Enable Ecc
+ outl(0x80000188, 0xcf8);
+ outb(0xcf, 0xcfc);
+
+ reg8 = pci_read_config8(PCI_DEV(0, 0, 0), 0xa5);
+ reg8 |= 0x10;
+ pci_write_config8(PCI_DEV(0, 0, 0), 0xa5, reg8);
+
+ reg8 = pci_read_config8(PCI_DEV(0, 0, 0), 0x91);
+ reg8 |= 0x20;
+ pci_write_config8(PCI_DEV(0, 0, 0), 0x91, reg8);
+#endif
+
+ static const struct regmask {
+ u8 reg;
+ u8 mask;
+ u8 val;
+ } b0d1f0[] = {
+ { 0x40, 0x00, 0x8b},
+ { 0x41, 0x80, 0x43},
+ { 0x42, 0x00, 0x62},
+ { 0x43, 0x00, 0x44},
+ { 0x44, 0x00, 0x34},
+ { 0x45, 0x00, 0x72}
+ }, b0d0f3[] = {
+ { 0x53, 0xf0, 0x0f},
+ { 0x60, 0x00, 0x03},
+ { 0x65, 0x00, 0xd9},
+ { 0x66, 0x00, 0x80},
+ { 0x67, 0x00, 0x00},
+ { 0x68, 0x00, 0x01},
+ { 0x69, 0xe0, 0x03},
+ { 0x6b, 0x00, 0x10},
+ { 0x6c, 0xc1, 0x08},
+ { 0x6e, 0x00, 0x89},
+ { 0x6f, 0x00, 0x51},
+ { 0x75, ~0x40, 0x40},
+ { 0x76, 0x8f, 0x00},
+ { 0x7b, 0x00, 0xa0},
+ { 0x86, 0x01, 0x24},
+ { 0x86, 0x04, 0x29},
+ { 0x8c, 0x00, 0x00},
+ { 0x8d, 0x00, 0x00},
+ { 0x95, ~0x40, 0x00},
+ { 0xa2, 0x00, 0x44},
+ { 0xb1, 0x00, 0xaa}
+ }, b0d0f0[] = {
+ { 0x4d, 0x00, 0x24},
+ { 0x4f, 0x00, 0x01},
+ { 0xbc, 0x00, 0x21},
+ { 0xbe, 0x00, 0x00},
+ { 0xbf, 0x7f, 0x80}
+ }, b0d17f0[] = {
+ { 0x40, ~0x01, 0x01}, // enable timer/counter shadow registers
+ { 0x67, ~0x03, 0x01},
+ { 0x5b, ~0x01, 0x00},
+ { 0x8d, ~0x02, 0x02},
+ { 0x97, ~0x80, 0x00},
+ { 0xd2, ~0x18, 0x00},
+ { 0xe2, ~0x36, 0x06},
+ { 0xe4, ~0x80, 0x00},
+ { 0xe5, 0x00, 0x40},
+ { 0xe6, 0x00, 0x20},
+ { 0xe7, ~0xd0, 0xc0},
+ { 0xec, ~0x08, 0x00}
+ }, b0d17f7[] = {
+ { 0x4e, ~0x80, 0x80},
+ { 0x4f, ~(1 << 6), 1 << 6 }, /* PG_CX700: 14.1.1 enable P2P Bridge Header for External PCI Bus */
+ { 0x74, ~0x00, 0x04}, /* PG_CX700: 14.1.2 APIC FSB directly up to snmic, not on pci */
+ { 0x7c, ~0x00, 0x02}, /* PG_CX700: 14.1.1 APIC FSB directly up to snmic, not on pci */
+ { 0xe6, 0x0, 0x04} // MSI post
+ }, b0d19f0[] = { /* P2PE */
+ { 0x42, ~0x08, 0x08}, // Disable HD Audio,
+ { 0x40, ~0xc0, 0x80} // 14.1.3.1.1 of the PG: extended cfg mode for pcie. enable capability, but don't activate
+ }, b0d0f2[] = {
+ { 0x50, ~0x40, 0x88},
+ { 0x51, 0x80, 0x7b},
+ { 0x52, 0x90, 0x6f},
+ { 0x53, 0x00, 0x88},
+ { 0x54, 0xe4, 0x16},
+ { 0x55, 0xf2, 0x04},
+ { 0x56, 0x0f, 0x00},
+ { 0x57, ~0x04, 0x00},
+ { 0x5d, 0x00, 0xb2},
+ { 0x5e, 0x00, 0x88},
+ { 0x5f, 0x00, 0xc7},
+ { 0x5c, 0x00, 0x01}
+ };
+
+ REGISTERPRESET(0, 0, 0, b0d0f0);
+ REGISTERPRESET(0, 0, 2, b0d0f2);
+ REGISTERPRESET(0, 0, 3, b0d0f3);
+ REGISTERPRESET(0, 1, 0, b0d1f0);
+ REGISTERPRESET(0, 17, 0, b0d17f0);
+ REGISTERPRESET(0, 17, 7, b0d17f7);
+ REGISTERPRESET(0, 19, 0, b0d19f0);
+}
diff --git a/src/northbridge/via/cx700/raminit.h b/src/northbridge/via/cx700/raminit.h
new file mode 100644
index 0000000000..482902e59b
--- /dev/null
+++ b/src/northbridge/via/cx700/raminit.h
@@ -0,0 +1,28 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * 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
+ */
+
+#ifndef RAMINIT_H
+#define RAMINIT_H
+
+#define DIMM_SOCKETS 2
+
+struct mem_controller {
+ u16 channel0[DIMM_SOCKETS];
+};
+#endif
diff --git a/src/northbridge/via/cx700/vgabios.c b/src/northbridge/via/cx700/vgabios.c
new file mode 100644
index 0000000000..e7ba33ab3b
--- /dev/null
+++ b/src/northbridge/via/cx700/vgabios.c
@@ -0,0 +1,783 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * (C) 2007-2009 coresystems GmbH
+ * (See further copyright notices below)
+ *
+ * NOTE: This file is supposed to go away once the generic vm86 handler
+ * in util/x86emu is able to handle intXX hooks like yabel does.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <console/console.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <device/pci_ops.h>
+#undef __KERNEL__
+#include <arch/io.h>
+#include <string.h>
+
+void write_protect_vgabios(void);
+
+/* vgabios.c. Derived from: */
+
+/*------------------------------------------------------------ -*- C -*-
+ * 2 Kernel Monte a.k.a. Linux loading Linux on x86
+ *
+ * Erik Arjan Hendriks <hendriks@lanl.gov>
+ *
+ * This version is a derivative of the original two kernel monte
+ * which is (C) 2000 Scyld.
+ *
+ * Copyright (C) 2000 Scyld Computing Corporation
+ *
+ * Portions related to the alpha architecture are:
+ *
+ * Copyright(C) 2001 University of California. LA-CC Number 01-67.
+ * This software has been authored by an employee or employees of the
+ * University of California, operator of the Los Alamos National
+ * Laboratory under Contract No. W-7405-ENG-36 with the U.S.
+ * Department of Energy. The U.S. Government has rights to use,
+ * reproduce, and distribute this software. If the software is
+ * modified to produce derivative works, such modified software should
+ * be clearly marked, so as not to confuse it with the version
+ * available from LANL.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by
+ * reference to http://www.gnu.org/licenses/gpl.html.
+ *
+ * This software is provided by the author(s) "as is" and any express
+ * or implied warranties, including, but not limited to, the implied
+ * warranties of merchantability and fitness for a particular purpose
+ * are disclaimed. In no event shall the author(s) be liable for any
+ * direct, indirect, incidental, special, exemplary, or consequential
+ * damages (including, but not limited to, procurement of substitute
+ * goods or services; loss of use, data, or profits; or business
+ * interruption) however caused and on any theory of liability,
+ * whether in contract, strict liability, or tort (including
+ * negligence or otherwise) arising in any way out of the use of this
+ * software, even if advised of the possibility of such damage.
+ *
+ *--------------------------------------------------------------------*/
+
+/* Modified to be a self sufficient plug in so that it can be used
+ without reliance on other parts of core Linuxbios
+ (C) 2005 Nick.Barker9@btinternet.com
+
+ Used initially for epia-m where there are problems getting the bios
+ emulator to successfully run this bios.
+*/
+
+/* Declare a temporary global descriptor table - necessary because the
+ Core part of the bios no longer sets up any 16 bit segments */
+__asm__(
+ /* pointer to original gdt */
+ "gdtarg: \n"
+ " .word gdt_limit \n"
+ " .long gdt \n"
+ /* compute the table limit */
+ "__mygdt_limit = __mygdt_end - __mygdt - 1 \n"
+ "__mygdtaddr: \n"
+ " .word __mygdt_limit \n"
+ " .long __mygdt \n"
+ "__mygdt: \n"
+ /* selgdt 0, unused */
+ " .word 0x0000, 0x0000 \n"
+ " .byte 0x00, 0x00, 0x00, 0x00 \n"
+ /* selgdt 8, unused */
+ " .word 0x0000, 0x0000 \n"
+ " .byte 0x00, 0x00, 0x00, 0x00 \n"
+ /* selgdt 0x10, flat code segment */
+ " .word 0xffff, 0x0000 \n"
+ " .byte 0x00, 0x9b, 0xcf, 0x00 \n"
+ /* selgdt 0x18, flat data segment */
+ " .word 0xffff, 0x0000 \n"
+ " .byte 0x00, 0x93, 0xcf, 0x00 \n"
+ /* selgdt 0x20, unused */
+ " .word 0x0000, 0x0000 \n"
+ " .byte 0x00, 0x00, 0x00, 0x00 \n"
+ /* selgdt 0x28 16-bit 64k code at 0x00000000 */
+ " .word 0xffff, 0x0000 \n"
+ " .byte 0, 0x9a, 0, 0 \n"
+ /* selgdt 0x30 16-bit 64k data at 0x00000000 */
+ " .word 0xffff, 0x0000 \n"
+ " .byte 0, 0x92, 0, 0 \n"
+ "__mygdt_end: \n");
+
+/* Declare a pointer to where our idt is going to be i.e. at mem zero */
+__asm__("__myidt: \n"
+ /* 16-bit limit */
+ " .word 1023 \n"
+ /* 24-bit base */
+ " .long 0 \n" " .word 0 \n");
+
+/* The address arguments to this function are PHYSICAL ADDRESSES */
+static void real_mode_switch_call_vga(unsigned long devfn)
+{
+ __asm__ __volatile__(
+ // paranoia -- does ecx get saved? not sure. This is
+ // the easiest safe thing to do.
+ " pushal \n"
+ /* save the stack */
+ " mov %esp, __stack \n"
+ " jmp 1f \n"
+ "__stack: .long 0 \n" "1:\n"
+ /* get devfn into %ecx */
+ " movl %esp, %ebp \n"
+ " movl 8(%ebp), %ecx \n"
+ /* load 'our' gdt */
+ " lgdt %cs:__mygdtaddr \n"
+ /* This configures CS properly for real mode. */
+ " ljmp $0x28, $__rms_16bit\n"
+ "__rms_16bit: \n"
+ " .code16 \n"
+ /* 16 bit code from here on... */
+ /* Load the segment registers w/ properly configured segment
+ * descriptors. They will retain these configurations (limits,
+ * writability, etc.) once protected mode is turned off. */
+ " mov $0x30, %ax \n"
+ " mov %ax, %ds \n"
+ " mov %ax, %es \n"
+ " mov %ax, %fs \n"
+ " mov %ax, %gs \n"
+ " mov %ax, %ss \n"
+ /* Turn off protection (bit 0 in CR0) */
+ " movl %cr0, %eax \n"
+ " andl $0xFFFFFFFE, %eax \n"
+ " movl %eax, %cr0 \n"
+ /* Now really going into real mode */
+ " ljmp $0, $__rms_real\n"
+ "__rms_real: \n"
+ /* put the stack at the end of page zero.
+ * that way we can easily share it between real and protected,
+ * since the 16-bit ESP at segment 0 will work for any case.
+ */
+ /* Setup a stack */
+ " mov $0x0, %ax \n"
+ " mov %ax, %ss \n"
+ " movl $0x1000, %eax \n"
+ " movl %eax, %esp \n"
+ /* Load our 16 it idt */
+ " xor %ax, %ax \n"
+ " mov %ax, %ds \n"
+ " lidt __myidt \n"
+ /* Dump zeros in the other segregs */
+ " mov %ax, %es \n"
+ " mov %ax, %fs \n"
+ " mov %ax, %gs \n"
+ " mov $0x40, %ax \n"
+ " mov %ax, %ds \n"
+ " mov %cx, %ax \n"
+ /* run VGA BIOS at 0xc000:0003 */
+ " lcall $0xc000, $0x0003\n"
+ /* if we got here, just about done.
+ * Need to get back to protected mode */
+ " movl %cr0, %eax \n" " orl $0x0000001, %eax\n" /* PE = 1 */
+ " movl %eax, %cr0 \n"
+ /* Now that we are in protected mode jump to a 32 bit code segment. */
+ " data32 ljmp $0x10, $vgarestart\n"
+ "vgarestart:\n"
+ " .code32\n"
+ " movw $0x18, %ax \n"
+ " mov %ax, %ds \n"
+ " mov %ax, %es \n"
+ " mov %ax, %fs \n"
+ " mov %ax, %gs \n"
+ " mov %ax, %ss \n"
+ /* restore proper gdt and idt */
+ " lgdt %cs:gdtarg \n"
+ " lidt idtarg \n"
+ ".globl vga_exit \n"
+ "vga_exit: \n"
+ " mov __stack, %esp \n"
+ " popal \n");
+}
+
+__asm__(".text\n" "real_mode_switch_end:\n");
+extern char real_mode_switch_end[];
+
+/* call vga bios int 10 function 0x4f14 to enable main console
+ epia-m does not always autosence the main console so forcing it on is good !! */
+void vga_enable_console(void)
+{
+ __asm__ __volatile__(
+ /* paranoia -- does ecx get saved? not sure. This is
+ * the easiest safe thing to do. */
+ " pushal \n"
+ /* save the stack */
+ " mov %esp, __stack \n"
+ /* load 'our' gdt */
+ " lgdt %cs:__mygdtaddr \n"
+ /* This configures CS properly for real mode. */
+ " ljmp $0x28, $__vga_ec_16bit\n"
+ "__vga_ec_16bit: \n"
+ " .code16 \n"
+ /* 16 bit code from here on... */
+ /* Load the segment registers w/ properly configured segment
+ * descriptors. They will retain these configurations (limits,
+ * writability, etc.) once protected mode is turned off. */
+ " mov $0x30, %ax \n"
+ " mov %ax, %ds \n"
+ " mov %ax, %es \n"
+ " mov %ax, %fs \n"
+ " mov %ax, %gs \n"
+ " mov %ax, %ss \n"
+ /* Turn off protection (bit 0 in CR0) */
+ " movl %cr0, %eax \n"
+ " andl $0xFFFFFFFE, %eax\n"
+ " movl %eax, %cr0 \n"
+ /* Now really going into real mode */
+ " ljmp $0, $__vga_ec_real \n"
+ "__vga_ec_real: \n"
+ /* put the stack at the end of page zero.
+ * that way we can easily share it between real and protected,
+ * since the 16-bit ESP at segment 0 will work for any case.
+ */
+ /* Setup a stack */
+ " mov $0x0, %ax \n"
+ " mov %ax, %ss \n"
+ " movl $0x1000, %eax \n"
+ " movl %eax, %esp \n"
+ /* debugging for RGM */
+ " mov $0x11, %al \n"
+ " outb %al, $0x80 \n"
+ /* Load our 16 it idt */
+ " xor %ax, %ax \n"
+ " mov %ax, %ds \n"
+ " lidt __myidt \n"
+ /* Dump zeros in the other segregs */
+ " mov %ax, %ds \n"
+ " mov %ax, %es \n"
+ " mov %ax, %fs \n"
+ " mov %ax, %gs \n"
+ /* ask bios to enable main console */
+ /* set up for int 10 call - values found from X server
+ * bios call routines */
+ " movw $0x4f14,%ax \n"
+ " movw $0x8003,%bx \n"
+ " movw $1, %cx \n"
+ " movw $0, %dx \n"
+ " movw $0, %di \n"
+ " int $0x10 \n"
+ " movb $0x55, %al \n"
+ " outb %al, $0x80 \n"
+ /* if we got here, just about done.
+ * Need to get back to protected mode */
+ " movl %cr0, %eax \n" " orl $0x0000001, %eax\n" /* PE = 1 */
+ " movl %eax, %cr0 \n"
+ /* Now that we are in protected mode jump to a 32 bit code segment. */
+ " data32 ljmp $0x10, $vga_ec_restart\n"
+ "vga_ec_restart:\n"
+ " .code32\n"
+ " movw $0x18, %ax \n"
+ " mov %ax, %ds \n"
+ " mov %ax, %es \n"
+ " mov %ax, %fs \n"
+ " mov %ax, %gs \n"
+ " mov %ax, %ss \n"
+ /* restore proper gdt and idt */
+ " lgdt %cs:gdtarg \n"
+ " lidt idtarg \n"
+ " .globl vga__ec_exit \n"
+ "vga_ec_exit:\n"
+ " mov __stack, %esp \n"
+ " popal\n");
+}
+
+void do_vgabios(void)
+{
+ device_t dev;
+ unsigned long busdevfn;
+ unsigned int rom = 0;
+ unsigned char *buf;
+ unsigned int size = 64 * 1024;
+ int i;
+
+ /* clear vga bios data area */
+ for (i = 0x400; i < 0x500; i++) {
+ *(unsigned char *)i = 0;
+ }
+
+ dev = dev_find_class(PCI_CLASS_DISPLAY_VGA << 8, 0);
+
+ if (!dev) {
+ printk_debug("NO VGA FOUND\n");
+ return;
+ }
+ printk_debug("found VGA: vid=%x, did=%x\n", dev->vendor, dev->device);
+
+ /* declare rom address here - keep any config data out of the way
+ * of core LXB stuff */
+
+#warning ROM address hardcoded to 512K
+ rom = 0xfff80000;
+ pci_write_config32(dev, PCI_ROM_ADDRESS, rom | 1);
+ printk_debug("rom base, size: %x\n", rom);
+
+ buf = (unsigned char *)rom;
+ if ((buf[0] == 0x55) && (buf[1] == 0xaa)) {
+ memcpy((void *)0xc0000, buf, size);
+
+ write_protect_vgabios(); // in northbridge
+
+ // check signature again
+ buf = (unsigned char *)0xc0000;
+ if (buf[0] == 0x55 && buf[1] == 0xAA) {
+ busdevfn =
+ (dev->bus->secondary << 8) | dev->path.pci.devfn;
+ printk_debug("bus/devfn = %#x\n", busdevfn);
+
+ real_mode_switch_call_vga(busdevfn);
+ } else
+ printk_debug("Failed to copy VGA BIOS to 0xc0000\n");
+ } else
+ printk_debug("BAD SIGNATURE 0x%x 0x%x\n", buf[0], buf[1]);
+
+ pci_write_config32(dev, PCI_ROM_ADDRESS, 0);
+}
+
+// we had hoped to avoid this.
+// this is a stub IDT only. It's main purpose is to ignore calls
+// to the BIOS.
+// no longer. Dammit. We have to respond to these.
+struct realidt {
+ unsigned short offset, cs;
+};
+
+// from a handy writeup that andrey found.
+
+// handler.
+// There are some assumptions we can make here.
+// First, the Top Of Stack (TOS) is located on the top of page zero.
+// we can share this stack between real and protected mode.
+// that simplifies a lot of things ...
+// we'll just push all the registers on the stack as longwords,
+// and pop to protected mode.
+// second, since this only ever runs as part of linuxbios,
+// we know all the segment register values -- so we don't save any.
+// keep the handler that calls things small. It can do a call to
+// more complex code in linuxbios itself. This helps a lot as we don't
+// have to do address fixup in this little stub, and calls are absolute
+// so the handler is relocatable.
+void handler(void)
+{
+ __asm__ __volatile__(" .code16 \n"
+ "idthandle: \n"
+ " pushal \n"
+ " movb $0, %al \n"
+ " ljmp $0, $callbiosint16\n"
+ "end_idthandle: \n"
+ " .code32 \n");
+}
+
+void debughandler(void)
+{
+ __asm__ __volatile__(" .code16 \n"
+ "debughandle: \n"
+ " pushw %cx \n"
+ " movw $250, %cx \n"
+ "dbh1: \n"
+ " loop dbh1 \n"
+ " popw %cx \n"
+ " iret \n"
+ "end_debughandle: \n" ".code32 \n");
+}
+
+// Calling conventions. The first C function is called with this stuff
+// on the stack. They look like value parameters, but note that if you
+// modify them they will go back to the INTx function modified.
+// the C function will call the biosint function with these as
+// REFERENCE parameters. In this way, we can easily get
+// returns back to the INTx caller (i.e. vgabios)
+void callbiosint(void)
+{
+ __asm__ __volatile__(" .code16 \n"
+ "callbiosint16: \n"
+ " push %ds \n"
+ " push %es \n"
+ " push %fs \n" " push %gs \n"
+ // clean up the int #. To save space we put it in the lower
+ // byte. But the top 24 bits are junk.
+ " andl $0xff, %eax\n"
+ // this push does two things:
+ // - put the INT # on the stack as a parameter
+ // - provides us with a temp for the %cr0 mods.
+ " pushl %eax \n" " movl %cr0, %eax\n" " orl $0x00000001, %eax\n" /* PE = 1 */
+ " movl %eax, %cr0\n"
+ /* Now that we are in protected mode jump to a 32 bit code segment. */
+ " data32 ljmp $0x10, $biosprotect\n"
+ "biosprotect: \n"
+ " .code32 \n"
+ " movw $0x18, %ax \n"
+ " mov %ax, %ds \n"
+ " mov %ax, %es \n"
+ " mov %ax, %fs \n"
+ " mov %ax, %gs \n"
+ " mov %ax, %ss \n"
+ " lidt idtarg \n"
+ " call biosint \n"
+ // back to real mode ...
+ " ljmp $0x28, $__rms_16bit2\n"
+ "__rms_16bit2: \n"
+ " .code16 \n"
+ /* 16 bit code from here on... */
+ /* Load the segment registers w/ properly configured segment
+ * descriptors. They will retain these configurations (limits,
+ * writability, etc.) once protected mode is turned off. */
+ " mov $0x30, %ax \n"
+ " mov %ax, %ds \n"
+ " mov %ax, %es \n"
+ " mov %ax, %fs \n"
+ " mov %ax, %gs \n"
+ " mov %ax, %ss \n"
+ /* Turn off protection (bit 0 in CR0) */
+ " movl %cr0, %eax \n"
+ " andl $0xFFFFFFFE, %eax \n"
+ " movl %eax, %cr0 \n"
+ /* Now really going into real mode */
+ " ljmp $0, $__rms_real2 \n"
+ "__rms_real2: \n"
+ /* Setup a stack
+ * FixME: where is esp? */
+ " mov $0x0, %ax \n"
+ " mov %ax, %ss \n"
+ /* ebugging for RGM */
+ " mov $0x11, %al \n"
+ " outb %al, $0x80 \n"
+ /* Load our 16 it idt */
+ " xor %ax, %ax \n"
+ " mov %ax, %ds \n"
+ " lidt __myidt \n"
+ /* Dump zeros in the other segregs */
+ " mov %ax, %es \n"
+ " mov %ax, %fs \n"
+ " mov %ax, %gs \n"
+ " mov $0x40, %ax \n"
+ " mov %ax, %ds \n"
+ /* pop the INT # that you pushed earlier */
+ " popl %eax \n"
+ " pop %gs \n"
+ " pop %fs \n"
+ " pop %es \n"
+ " pop %ds \n"
+ " popal \n"
+ " iret \n"
+ " .code32 \n");
+}
+
+enum {
+ PCIBIOS = 0x1a,
+ MEMSIZE = 0x12
+};
+
+int pcibios(unsigned long *pedi, unsigned long *pesi, unsigned long *pebp,
+ unsigned long *pesp, unsigned long *pebx, unsigned long *pedx,
+ unsigned long *pecx, unsigned long *peax, unsigned long *pflags);
+
+int handleint21(unsigned long *pedi, unsigned long *pesi, unsigned long *pebp,
+ unsigned long *pesp, unsigned long *pebx, unsigned long *pedx,
+ unsigned long *pecx, unsigned long *peax,
+ unsigned long *pflags);
+
+extern void vga_exit(void);
+
+int biosint(unsigned long intnumber,
+ unsigned long gsfs, unsigned long dses,
+ unsigned long edi, unsigned long esi,
+ unsigned long ebp, unsigned long esp,
+ unsigned long ebx, unsigned long edx,
+ unsigned long ecx, unsigned long eax,
+ unsigned long cs_ip, unsigned short stackflags)
+{
+ unsigned long ip;
+ unsigned long cs;
+ unsigned long flags;
+ int ret = -1;
+
+ ip = cs_ip & 0xffff;
+ cs = cs_ip >> 16;
+ flags = stackflags;
+
+ printk_debug("biosint: INT# 0x%lx\n", intnumber);
+ printk_debug("biosint: eax 0x%lx ebx 0x%lx ecx 0x%lx edx 0x%lx\n",
+ eax, ebx, ecx, edx);
+ printk_debug("biosint: ebp 0x%lx esp 0x%lx edi 0x%lx esi 0x%lx\n",
+ ebp, esp, edi, esi);
+ printk_debug("biosint: ip 0x%x cs 0x%x flags 0x%x\n",
+ ip, cs, flags);
+
+ // cases in a good compiler are just as good as your own tables.
+ switch (intnumber) {
+ case 0 ... 15:
+ // These are not BIOS service, but the CPU-generated exceptions
+ printk_info("biosint: Oops, exception %u\n", intnumber);
+ if (esp < 0x1000) {
+ printk_debug("Stack contents: ");
+ while (esp < 0x1000) {
+ printk_debug("0x%04x ", *(unsigned short *)esp);
+ esp += 2;
+ }
+ printk_debug("\n");
+ }
+ printk_debug("biosint: Bailing out\n");
+ // "longjmp"
+ vga_exit();
+ break;
+
+ case PCIBIOS:
+ ret = pcibios(&edi, &esi, &ebp, &esp,
+ &ebx, &edx, &ecx, &eax, &flags);
+ break;
+ case MEMSIZE:
+ // who cares.
+ eax = 64 * 1024;
+ ret = 0;
+ break;
+ case 0x15:
+ ret = handleint21(&edi, &esi, &ebp, &esp,
+ &ebx, &edx, &ecx, &eax, &flags);
+ break;
+ default:
+ printk_info("BIOSINT: Unsupport int #0x%x\n", intnumber);
+ break;
+ }
+ if (ret)
+ flags |= 1; // carry flags
+ else
+ flags &= ~1;
+ stackflags = flags;
+ return ret;
+}
+
+void setup_realmode_idt(void)
+{
+ extern unsigned char idthandle, end_idthandle;
+ extern unsigned char debughandle, end_debughandle;
+
+ int i;
+ struct realidt *idts = (struct realidt *)0;
+ int codesize = &end_idthandle - &idthandle;
+ unsigned char *intbyte, *codeptr;
+
+ // for each int, we create a customized little handler
+ // that just pushes %ax, puts the int # in %al,
+ // then calls the common interrupt handler.
+ // this necessitated because intel didn't know much about
+ // architecture when they did the 8086 (it shows)
+ // (hmm do they know anymore even now :-)
+ // obviously you can see I don't really care about memory
+ // efficiency. If I did I would probe back through the stack
+ // and get it that way. But that's really disgusting.
+ for (i = 0; i < 256; i++) {
+ idts[i].cs = 0;
+ codeptr = (unsigned char *)4096 + i * codesize;
+ idts[i].offset = (unsigned)codeptr;
+ memcpy(codeptr, &idthandle, codesize);
+ intbyte = codeptr + 3;
+ *intbyte = i;
+ }
+
+ // fixed entry points
+
+ // VGA BIOSes tend to hardcode f000:f065 as the previous handler of
+ // int10.
+ // calling convention here is the same as INTs, we can reuse
+ // the int entry code.
+ codeptr = (unsigned char *)0xff065;
+ memcpy(codeptr, &idthandle, codesize);
+ intbyte = codeptr + 3;
+ *intbyte = 0x42; /* int42 is the relocated int10 */
+
+ // VIA's VBIOS will call f000:f859 instead of sending int15.
+ codeptr = (unsigned char *)0xff859;
+ memcpy(codeptr, &idthandle, codesize);
+ intbyte = codeptr + 3;
+ *intbyte = 0x15;
+
+ /* debug handler - useful to set a programmable delay between instructions if the
+ TF bit is set upon call to real mode */
+ idts[1].cs = 0;
+ idts[1].offset = 16384;
+ memcpy((void *)16384UL, &debughandle, &end_debughandle - &debughandle);
+}
+
+enum {
+ CHECK = 0xb001,
+ FINDDEV = 0xb102,
+ READCONFBYTE = 0xb108,
+ READCONFWORD = 0xb109,
+ READCONFDWORD = 0xb10a,
+ WRITECONFBYTE = 0xb10b,
+ WRITECONFWORD = 0xb10c,
+ WRITECONFDWORD = 0xb10d
+};
+
+// errors go in AH. Just set these up so that word assigns
+// will work. KISS.
+enum {
+ PCIBIOS_NODEV = 0x8600,
+ PCIBIOS_BADREG = 0x8700
+};
+
+int
+pcibios(unsigned long *pedi, unsigned long *pesi, unsigned long *pebp,
+ unsigned long *pesp, unsigned long *pebx, unsigned long *pedx,
+ unsigned long *pecx, unsigned long *peax, unsigned long *pflags)
+{
+ unsigned short func = (unsigned short)(*peax);
+ int retval = 0;
+ unsigned short devid, vendorid, devfn;
+ short devindex; /* Use short to get rid of garbage in upper half of 32-bit register */
+ unsigned char bus;
+ device_t dev;
+
+ switch (func) {
+ case CHECK:
+ *pedx = 0x4350;
+ *pecx = 0x2049;
+ retval = 0;
+ break;
+ case FINDDEV:
+ {
+ devid = *pecx;
+ vendorid = *pedx;
+ devindex = *pesi;
+ dev = 0;
+ while ((dev = dev_find_device(vendorid, devid, dev))) {
+ if (devindex <= 0)
+ break;
+ devindex--;
+ }
+ if (dev) {
+ unsigned short busdevfn;
+ *peax = 0;
+ // busnum is an unsigned char;
+ // devfn is an int, so we mask it off.
+ busdevfn = (dev->bus->secondary << 8)
+ | (dev->path.pci.devfn & 0xff);
+ printk_debug("0x%x: return 0x%x\n", func,
+ busdevfn);
+ *pebx = busdevfn;
+ retval = 0;
+ } else {
+ *peax = PCIBIOS_NODEV;
+ retval = -1;
+ }
+ }
+ break;
+ case READCONFDWORD:
+ case READCONFWORD:
+ case READCONFBYTE:
+ case WRITECONFDWORD:
+ case WRITECONFWORD:
+ case WRITECONFBYTE:
+ {
+ unsigned long dword;
+ unsigned short word;
+ unsigned char byte;
+ unsigned char reg;
+
+ devfn = *pebx & 0xff;
+ bus = *pebx >> 8;
+ reg = *pedi;
+ dev = dev_find_slot(bus, devfn);
+ if (!dev) {
+ printk_debug
+ ("0x%x: BAD DEVICE bus %d devfn 0x%x\n",
+ func, bus, devfn);
+ // idiots. the pcibios guys assumed you'd never pass a bad bus/devfn!
+ *peax = PCIBIOS_BADREG;
+ retval = -1;
+ }
+ switch (func) {
+ case READCONFBYTE:
+ byte = pci_read_config8(dev, reg);
+ *pecx = byte;
+ break;
+ case READCONFWORD:
+ word = pci_read_config16(dev, reg);
+ *pecx = word;
+ break;
+ case READCONFDWORD:
+ dword = pci_read_config32(dev, reg);
+ *pecx = dword;
+ break;
+ case WRITECONFBYTE:
+ byte = *pecx;
+ pci_write_config8(dev, reg, byte);
+ break;
+ case WRITECONFWORD:
+ word = *pecx;
+ pci_write_config16(dev, reg, word);
+ break;
+ case WRITECONFDWORD:
+ dword = *pecx;
+ pci_write_config32(dev, reg, dword);
+ break;
+ }
+
+ if (retval)
+ retval = PCIBIOS_BADREG;
+ printk_debug
+ ("0x%x: bus %d devfn 0x%x reg 0x%x val 0x%lx\n",
+ func, bus, devfn, reg, *pecx);
+ *peax = 0;
+ retval = 0;
+ }
+ break;
+ default:
+ printk_err("UNSUPPORTED PCIBIOS FUNCTION 0x%x\n", func);
+ break;
+ }
+
+ return retval;
+}
+
+int handleint21(unsigned long *edi, unsigned long *esi, unsigned long *ebp,
+ unsigned long *esp, unsigned long *ebx, unsigned long *edx,
+ unsigned long *ecx, unsigned long *eax, unsigned long *flags)
+{
+ int res = -1;
+ switch (*eax & 0xffff) {
+ case 0x5f19:
+ break;
+ case 0x5f18:
+ *eax = 0x5f;
+ *ebx = 0x545; // MCLK = 133, 32M frame buffer, 256 M main memory
+ *ecx = 0x060;
+ res = 0;
+ break;
+ case 0x5f00:
+ *eax = 0x8600;
+ break;
+ case 0x5f01:
+ *eax = 0x5f;
+ *ecx = (*ecx & 0xffffff00) | 2; // panel type = 2 = 1024 * 768
+ res = 0;
+ break;
+ case 0x5f02:
+ *eax = 0x5f;
+ *ebx = (*ebx & 0xffff0000) | 2;
+ *ecx = (*ecx & 0xffff0000) | 0x401; // PAL + crt only
+ *edx = (*edx & 0xffff0000) | 0; // TV Layout - default
+ res = 0;
+ break;
+ case 0x5f0f:
+ *eax = 0x860f;
+ break;
+ }
+ return res;
+}