diff options
author | Stefan Reinauer <stepan@coresystems.de> | 2009-04-17 08:37:18 +0000 |
---|---|---|
committer | Stefan Reinauer <stepan@openbios.org> | 2009-04-17 08:37:18 +0000 |
commit | aeba92ab5b0afd1464d6b1a275b5f5b00b351b32 (patch) | |
tree | 225fbff67fc05e70507ac6ef7b3af32f00bac6f8 /src/northbridge/via | |
parent | 56c51bd120a935e64cfd96d8ad71c9d1f7aab323 (diff) |
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.lb | 28 | ||||
-rw-r--r-- | src/northbridge/via/cx700/chip.h | 23 | ||||
-rw-r--r-- | src/northbridge/via/cx700/cx700_agp.c | 91 | ||||
-rw-r--r-- | src/northbridge/via/cx700/cx700_early_serial.c | 102 | ||||
-rw-r--r-- | src/northbridge/via/cx700/cx700_early_smbus.c | 268 | ||||
-rw-r--r-- | src/northbridge/via/cx700/cx700_lpc.c | 397 | ||||
-rw-r--r-- | src/northbridge/via/cx700/cx700_registers.h | 272 | ||||
-rw-r--r-- | src/northbridge/via/cx700/cx700_reset.c | 25 | ||||
-rw-r--r-- | src/northbridge/via/cx700/cx700_sata.c | 160 | ||||
-rw-r--r-- | src/northbridge/via/cx700/cx700_usb.c | 56 | ||||
-rw-r--r-- | src/northbridge/via/cx700/cx700_vga.c | 119 | ||||
-rw-r--r-- | src/northbridge/via/cx700/northbridge.c | 182 | ||||
-rw-r--r-- | src/northbridge/via/cx700/northbridge.h | 25 | ||||
-rw-r--r-- | src/northbridge/via/cx700/raminit.c | 1480 | ||||
-rw-r--r-- | src/northbridge/via/cx700/raminit.h | 28 | ||||
-rw-r--r-- | src/northbridge/via/cx700/vgabios.c | 783 |
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; +} |