summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/northbridge/via/vx900/Makefile.inc12
-rw-r--r--src/northbridge/via/vx900/chip.h52
-rw-r--r--src/northbridge/via/vx900/chrome9hd.c368
-rw-r--r--src/northbridge/via/vx900/lpc.c223
-rw-r--r--src/northbridge/via/vx900/northbridge.c341
-rw-r--r--src/northbridge/via/vx900/pcie.c123
-rw-r--r--src/northbridge/via/vx900/sata.c280
-rw-r--r--src/northbridge/via/vx900/traf_ctrl.c145
-rw-r--r--src/northbridge/via/vx900/vx900.h1
9 files changed, 1545 insertions, 0 deletions
diff --git a/src/northbridge/via/vx900/Makefile.inc b/src/northbridge/via/vx900/Makefile.inc
index 1586c87751..13d7de65ed 100644
--- a/src/northbridge/via/vx900/Makefile.inc
+++ b/src/northbridge/via/vx900/Makefile.inc
@@ -31,6 +31,18 @@ romstage-y += ./../../../southbridge/via/common/early_smbus_wait_until_ready.c
romstage-y += ./../../../drivers/pc80/udelay_io.c
romstage-$(CONFIG_COLLECT_TIMESTAMPS) += ./../../../lib/cbmem.c
+ramstage-y += pci_util.c
+ramstage-y += pcie.c
+ramstage-y += northbridge.c
+ramstage-y += chrome9hd.c
+ramstage-y += traf_ctrl.c
+ramstage-y += sata.c
+ramstage-y += lpc.c
+
+# The buildsystem only includes this file if CONFIG_VGA is selected.
+# We need to do some VGA I/O before the VGA can be initialized. We can make good
+# use of some of the functions there, so include them unconditionally
+ramstage-y += ./../../../drivers/pc80/vga/vga_io.c
chipset_bootblock_inc += $(src)/northbridge/via/vx900/romstrap.inc
diff --git a/src/northbridge/via/vx900/chip.h b/src/northbridge/via/vx900/chip.h
new file mode 100644
index 0000000000..6334a8e123
--- /dev/null
+++ b/src/northbridge/via/vx900/chip.h
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+struct northbridge_via_vx900_config {
+ /**
+ * \brief PCIe Lane[3:0] Function Select
+ *
+ * PCIe Lane3~Lane0 (PEXTX[3:0]P/VCC) can be used by the integrated
+ * graphic controller to output its display data. The PCIe lanes will
+ * be used to output DisplayPort data.
+ */
+ u8 assign_pex_to_dp;
+
+ /**
+ * \brief Lane Width for Root Port 1
+ *
+ * Two PCIe lanes are used for Root port 1. Root port 2 is disabled.
+ */
+ u8 pcie_port1_2_lane_wide;
+
+ /**
+ * \brief PIRQ line to which to route the external interrupt
+ *
+ * The VX900 features an external interrupt which can be routed to any
+ * of the PIRQA->PIRQH lines. Usually, on-board devices are connected
+ * to the external interrupt. In some vendor BIOS's pirq table, this
+ * appears as link 9.
+ *
+ * Setting this line only affects the behavior of the integrated PIC. It
+ * has no effect on the IOAPIC.
+ *
+ * The value of this register must be a literal upper-case character
+ * from 'A' to 'H'.
+ */
+ char ext_int_route_to_pirq;
+};
diff --git a/src/northbridge/via/vx900/chrome9hd.c b/src/northbridge/via/vx900/chrome9hd.c
new file mode 100644
index 0000000000..633328e164
--- /dev/null
+++ b/src/northbridge/via/vx900/chrome9hd.c
@@ -0,0 +1,368 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <arch/io.h>
+#include <config.h>
+#include <console/console.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <pc80/vga_io.h>
+
+#include "vx900.h"
+
+#define CHROME_9_HD_MIN_FB_SIZE 8
+#define CHROME_9_HD_MAX_FB_SIZE 512
+
+/**
+ * @file chrome9hd.c
+ *
+ * \brief Initialization for Chrome9HD integrated graphics adapters
+ *
+ * This takes care of the initialization we need to do before calling the VGA
+ * BIOS. The device is not documented in the VX900 datasheet.
+ *
+ * The device is documented in:
+ * Open Graphics Programming Manual
+ * Chrome9GraphicsHD Processor
+ * VX900 Series System Processor
+ * Part I: Graphics Core / 2D
+ *
+ * This document was released by VIA to the Xorg project, and is available at:
+ * <http://www.x.org/docs/via/OGPM_Chrome9%20HD%20DX9%20_R100_PartI_Core_2D.pdf>
+ *
+ * STATUS:
+ * We do the minimal initialization described in VIA documents. Running the VGA
+ * option ROM does not get us a usable display. We configure the framebuffer and
+ * the IGP is able to use it. GRUB2 and linux are capable of getting a usable
+ * text console, which uses the monitor's native resolution (even 1920x1080).
+ * The graphical console (linux) does not work properly.
+ * @TODO
+ * 1. Figure out what sequence we need to do to get the VGA BIOS running
+ * properly. Use the code provided by VIA and compare their sequence to ours,
+ * fill in any missing steps, etc.
+ * 2. Make BAR2 and the framebuffer use the same memory space. This is a feature
+ * called "Direct framebuffer access" which allows us to save memory space by
+ * setting BAR2 of the VGA to the location in memory of the framebuffer. This
+ * reduces the amount of PCI MMIO space we need below 4G, and is especially
+ * useful considering we only have 8GB (33 bits) of memory-mapped space.
+ */
+
+/* Helper to determine the framebuffer size */
+u32 chrome9hd_fb_size(void)
+{
+ static u32 fb_size = 0;
+ u8 reg8, ranksize;
+ u32 size_mb, tom_mb, max_size_mb;
+ int i;
+ /* We do some PCI and CMOS IO to find our value, so if we've already
+ * found it, save some time */
+ if (fb_size != 0)
+ return fb_size;
+ /* FIXME: read fb_size from CMOS, but until that is implemented, start
+ * from 512MB */
+ size_mb = 512;
+
+ /* The minimum framebuffer size is 8MB. */
+ size_mb = max(size_mb, CHROME_9_HD_MIN_FB_SIZE);
+
+ const device_t mcu = dev_find_device(PCI_VENDOR_ID_VIA,
+ PCI_DEVICE_ID_VIA_VX900_MEMCTRL,
+ 0);
+ /*
+ * We have two limitations on the maximum framebuffer size:
+ * 1) (Sanity) No more that 1/4 of system RAM
+ * 2) (Hardware limitation) No larger than DRAM in last rank
+ * Check both of these limitations and apply them to our framebuffer */
+ tom_mb = (pci_read_config16(mcu, 0x88) & 0x07ff) << (24 - 20);
+ max_size_mb = tom_mb >> 2;
+ if (size_mb > max_size_mb) {
+ printk(BIOS_ALERT, "The framebuffer size of of %dMB is larger"
+ " than 1/4 of available memory.\n"
+ " Limiting framebuffer to %dMB\n", size_mb, max_size_mb);
+ size_mb = max_size_mb;
+ }
+
+ /* Now handle limitation #2
+ * Look at the ending address of the memory ranks, from last to first,
+ * until we find one that is not zero. That is our last rank, and its
+ * size is the limit of our framebuffer. */
+ /* FIXME: This has a bug. If we remap memory above 4G, we consider the
+ * memory hole as part of our RAM. Thus if we install 3G, with a TOLM of
+ * 2.5G, our TOM will be at 5G and we'll assume we have 5G RAM instead
+ * of the actual 3.5G */
+ for (i = VX900_MAX_MEM_RANKS - 1; i > -1; i--) {
+ reg8 = pci_read_config8(mcu, 0x40 + i);
+ if (reg8 == 0)
+ continue;
+ /* We've reached the last populated rank */
+ ranksize = reg8 - pci_read_config8(mcu, 0x48 + i);
+ max_size_mb = ranksize << 6;
+ /* That's it. We got what we needed. */
+ break;
+ };
+ if (size_mb > max_size_mb) {
+ printk(BIOS_ALERT, "The framebuffer size of %dMB is larger"
+ " than size of the last DRAM rank.\n"
+ " Limiting framebuffer to %dMB\n", size_mb, max_size_mb);
+ size_mb = max_size_mb;
+ }
+
+ /* Now round the framebuffer size to the closest power of 2 */
+ u8 fb_pow = 0;
+ while (size_mb >> fb_pow)
+ fb_pow++;
+ fb_pow--;
+ size_mb = (1 << fb_pow);
+ /* We store the framebuffer size in bytes, for simplicity */
+ fb_size = size_mb << 20;
+ return fb_size;
+}
+
+/**
+ * @defgroup vx900_int15
+ *
+ * \brief INT15 helpers for Chrome9HD IGP
+ *
+ * The following are helpers for INT15 handlers for the VGA BIOS. The full set
+ * of INT15 callbacks is described in
+ *
+ * VIA/S3Graphics
+ * Video BIOS External Interface Specification for Chrome9 Series IGP
+ * VX900 Series
+ *
+ * This document is only available under NDA, however, the callbacks are very
+ * similar to other VIA/Intel IGP callbacks.
+ *
+ * Callback 0x5f18 is the most important one. It informs the VGA BIOS of the
+ * RAM speed and framebuffer size. The other callbacks seem to be optional.
+ * @{
+ */
+
+/**
+ * \brief Get X86_BL value for VGA INT15 function 5f18
+ *
+ * Int15 5f18 lets the VGA BIOS know the framebuffer size and the memory speed.
+ * This handler is very important. If it is not implemented, the VGA BIOS will
+ * not work correctly.
+ *
+ * To use, just call this from the 15f18 handler, and place the return value in
+ * X86_BL
+ *
+ * @code{.c}
+ * case 0x5f18:
+ * X86_BX = vx900_int15_get_5f18_bl();
+ * res = 0;
+ * break;
+ * @endcode
+ *
+ */
+u8 vx900_int15_get_5f18_bl(void)
+{
+ u8 reg8, ret;
+ device_t dev;
+ /*
+ * BL Bit[7:4]
+ * Memory Data Rate (not to be confused with fCLK)
+ * 0000: 66MHz
+ * 0001: 100MHz
+ * 0010: 133MHz
+ * 0011: 200MHz ( DDR200 )
+ * 0100: 266MHz ( DDR266 )
+ * 0101: 333MHz ( DDR333 )
+ * 0110: 400MHz ( DDR400 )
+ * 0111: 533MHz ( DDR I/II 533)
+ * 1000: 667MHz ( DDR I/II 667)
+ * 1001: 800MHz ( DDR3 800)
+ * 1010: 1066MHz ( DDR3 1066)
+ * 1011: 1333MHz ( DDR3 1333)
+ * Bit[3:0]
+ * N: Frame Buffer Size 2^N MB
+ */
+ dev = dev_find_slot(0, PCI_DEVFN(0, 3));
+ reg8 = pci_read_config8(dev, 0xa1);
+ ret = (u32) ((reg8 & 0x70) >> 4) + 2;
+ reg8 = pci_read_config8(dev, 0x90);
+ reg8 = ((reg8 & 0x07) + 3) << 4;
+ ret |= (u32) reg8;
+
+ return ret;
+}
+/** @} */
+
+static void chrome9hd_set_sid_vid(u16 vendor, u16 device)
+{
+ vga_sr_write(0x36, vendor >> 8); /* SVID high byte */
+ vga_sr_write(0x35, vendor & 0xff); /* SVID low byte */
+ vga_sr_write(0x38, device >> 8); /* SID high byte */
+ vga_sr_write(0x37, device & 0xff); /* SID low byte */
+}
+
+static void chrome9hd_handle_uma(device_t dev)
+{
+ /* Mirror mirror, shiny glass, tell me that is not my ass */
+ u32 fb_size = chrome9hd_fb_size() >> 20;
+
+ //uma_resource(dev, 0x18, uma_memory_base>>10, uma_memory_size>>10);
+
+ printk(BIOS_DEBUG, "UMA base 0x%.8llx (%lluMB)\n", uma_memory_base,
+ uma_memory_base >> 20);
+ printk(BIOS_DEBUG, "UMA size 0x%.8llx (%lluMB)\n", uma_memory_size,
+ uma_memory_size >> 20);
+ u8 fb_pow = 0;
+ while (fb_size >> fb_pow)
+ fb_pow++;
+ fb_pow--;
+
+ /* Step 6 - Let MCU know the framebuffer size */
+ device_t mcu = dev_find_device(PCI_VENDOR_ID_VIA,
+ PCI_DEVICE_ID_VIA_VX900_MEMCTRL, 0);
+ pci_mod_config8(mcu, 0xa1, 7 << 4, (fb_pow - 2) << 4);
+
+ /* Step 7 - Let GFX know the framebuffer size (through PCI and IOCTL)
+ * The size we set here affects the behavior of BAR2, and the amount of
+ * MMIO space it requests. The default is 512MB, so if we don't set this
+ * before reading the resources, we could waste space below 4G */
+ pci_write_config8(dev, 0xb2, ((0xff << (fb_pow - 2)) & ~(1 << 7)));
+ vga_sr_write(0x68, (0xff << (fb_pow - 1)));
+ /* And also that the framebuffer is in the system, RAM */
+ pci_mod_config8(dev, 0xb0, 0, 1 << 0);
+}
+
+/**
+ * \brief Initialization sequence before running the VGA BIOS
+ *
+ * This is the initialization sequence described in:
+ *
+ * BIOS Porting Guide
+ * VX900 Series
+ * All-in-One System Processor
+ *
+ * This document is only available under NDA.
+ */
+static void chrome9hd_biosguide_init_seq(device_t dev)
+{
+ device_t mcu = dev_find_device(PCI_VENDOR_ID_VIA,
+ PCI_DEVICE_ID_VIA_VX900_MEMCTRL, 0);
+ device_t host = dev_find_device(PCI_VENDOR_ID_VIA,
+ PCI_DEVICE_ID_VIA_VX900_HOST_BR, 0);
+
+ /* Step 1 - Enable VGA controller */
+ /* FIXME: This is the VGA hole @ 640k-768k, and the vga port io
+ * We need the port IO, but can we disable the memory hole? */
+ pci_mod_config8(mcu, 0xa4, 0, (1 << 7)); /* VGA memory hole */
+
+ /* Step 2 - Forward MDA cycles to GFX */
+ pci_mod_config8(host, 0x4e, 0, (1 << 1));
+
+ /* Step 3 - Enable GFX I/O space */
+ pci_mod_config8(dev, PCI_COMMAND, 0, PCI_COMMAND_IO);
+
+ /* Step 4 - Enable video subsystem */
+ vga_enable_mask((1 << 0), (1 << 0));
+
+ /* FIXME: VGA IO Address Select. 3B5 or 3D5? */
+ vga_misc_mask((1 << 0), (1 << 0));
+
+ /* Step 5 - Unlock accessing of IO space */
+ vga_sr_write(0x10, 0x01);
+
+ chrome9hd_handle_uma(dev);
+
+ /* Step 8 - Enable memory base register on the GFX */
+ if (uma_memory_base == 0)
+ die("uma_memory_base not set. Abandon ship!\n");
+ printk(BIOS_DEBUG, "UMA base 0x%.10llx (%lluMB)\n", uma_memory_base,
+ uma_memory_base >> 20);
+ vga_sr_write(0x6d, (uma_memory_base >> 21) & 0xff); /* base 28:21 */
+ vga_sr_write(0x6e, (uma_memory_base >> 29) & 0xff); /* base 36:29 */
+ vga_sr_write(0x6f, 0x00); /* base 43:37 */
+
+ /* Step 9 - Set SID/VID */
+ chrome9hd_set_sid_vid(0x1106, 0x7122);
+
+}
+
+static void chrome9hd_init(device_t dev)
+{
+ print_debug("======================================================\n");
+ print_debug("== Chrome9 HD INIT\n");
+ print_debug("======================================================\n");
+
+ chrome9hd_biosguide_init_seq(dev);
+
+ /* Prime PLL FIXME: bad comment */
+ vga_sr_mask(0x3c, 1 << 2, 1 << 2);
+
+ /* FIXME: recheck; VGA IO Address Select. 3B5 or 3D5? */
+ vga_misc_mask(1 << 0, 1 << 0);
+
+ /* FIXME: recheck; Enable Base VGA 16 Bits Decode */
+ ////pci_mod_config8(host, 0x4e, 0, 1<<4);
+
+ u32 fb_address = pci_read_config32(dev, PCI_BASE_ADDRESS_2);
+ fb_address &= ~0x0F;
+ if (!fb_address) {
+ printk(BIOS_WARNING, "Chrome9HD: No FB BAR assigned!\n");
+ return;
+ }
+
+ printk(BIOS_INFO, "Chrome: Using %dMB Framebuffer at 0x%08X.\n",
+ 256, fb_address);
+
+ printk(BIOS_DEBUG, "Initializing VGA...\n");
+
+ pci_dev_init(dev);
+
+ printk(BIOS_DEBUG, "Enable VGA console\n");
+
+ dump_pci_device(dev);
+}
+
+static void chrome9hd_enable(device_t dev)
+{
+ device_t mcu = dev_find_device(PCI_VENDOR_ID_VIA,
+ PCI_DEVICE_ID_VIA_VX900_MEMCTRL, 0);
+ /* FIXME: here? -=- ACLK 250Mhz */
+ pci_mod_config8(mcu, 0xbb, 0, 0x01);
+}
+
+static void chrome9hd_disable(device_t dev)
+{
+ device_t mcu = dev_find_device(PCI_VENDOR_ID_VIA,
+ PCI_DEVICE_ID_VIA_VX900_MEMCTRL, 0);
+ /* Disable GFX - This step effectively renders the GFX inert
+ * It won't even show up as a PCI device during enumeration */
+ pci_mod_config8(mcu, 0xa1, 1 << 7, 0);
+}
+
+static struct device_operations chrome9hd_operations = {
+ .read_resources = pci_dev_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .init = chrome9hd_init,
+ .disable = chrome9hd_disable,
+ .enable = chrome9hd_enable,
+ .ops_pci = 0,
+};
+
+static const struct pci_driver chrome9hd_driver __pci_driver = {
+ .ops = &chrome9hd_operations,
+ .vendor = PCI_VENDOR_ID_VIA,
+ .device = PCI_DEVICE_ID_VIA_VX900_VGA,
+};
diff --git a/src/northbridge/via/vx900/lpc.c b/src/northbridge/via/vx900/lpc.c
new file mode 100644
index 0000000000..ac5e4c840e
--- /dev/null
+++ b/src/northbridge/via/vx900/lpc.c
@@ -0,0 +1,223 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2012-2013 Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <arch/io.h>
+#include <arch/pirq_routing.h>
+#include <console/console.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <pc80/i8259.h>
+#include <pc80/mc146818rtc.h>
+#include <drivers/generic/ioapic/chip.h>
+
+#include "vx900.h"
+#include "chip.h"
+
+/**
+ * @file lpc.c
+ *
+ * STATUS:
+ * We do a fair bit of setup, and most of it seems to work fairly well. There
+ * are still a few FIXME items here and there, but overall, this code hasn't
+ * been touched much from its initial 2012 version to 2013, when it was revived.
+ *
+ * We do the IOAPIC setup with the assumption that it is declared in the
+ * mainboard's devicetree.cb. We cannot use the IOAPIC however. The interrupts
+ * do not make it to the CPU. This issue is still under investigation.
+ *
+ * We also route PIRQs with CONFIG_PIRQ_ROUTE. This is currently the only way to
+ * get interrupts working.
+ *
+ * On the VX900, the keyboard can be connected directly to the chipset
+ * (referenced as "internal keyboard" in the documents). As long as that is the
+ * case (not connected to the superIO), and we disable the superIO keyboard LDN,
+ * it will work, but perhaps this should be more configurable.
+ */
+
+static void vx900_lpc_misc_stuff(device_t dev)
+{
+ char extint;
+ u8 val;
+ struct northbridge_via_vx900_config *nb = (void *)dev->chip_info;
+
+ /* GPIO 11,10 to SATALED [1,0] */
+ pci_mod_config8(dev, 0xe4, 0, 1 << 0);
+
+ /* Route the external interrupt line */
+ extint = nb->ext_int_route_to_pirq;
+ if (extint < 'A' || extint > 'H') {
+ printk(BIOS_WARNING, "Invalid PIRQ%c for external interrupt\n",
+ extint);
+ } else {
+ printk(BIOS_INFO, "Routing external interrupt to PIRQ%c\n",
+ extint);
+ val = extint - 'A';
+ val |= (1 << 3); /* bit3 enables the external int */
+ pci_mod_config8(dev, 0x55, 0xf, val);
+
+ }
+}
+
+static void vx900_lpc_dma_setup(device_t dev)
+{
+ /* These are the steps recommended by VIA in order to get DMA running */
+
+ /* Enable Positive South Module PCI Cycle Decoding */
+ /* FIXME: Setting this seems to hang our system */
+ //pci_mod_config8(dev, 0x58, 0, 1<<4);
+ /* Positive decoding for ROM + APIC + On-board IO ports */
+ pci_mod_config8(dev, 0x6c, 0, (1 << 2) | (1 << 3) | (1 << 7));
+ /* Enable DMA channels. BIOS guide recommends DMA channel 2 off */
+ pci_write_config8(dev, 0x53, 0xfb);
+ /* Disable PCI/DMA Memory Cycles Output to PCI Bus */
+ pci_mod_config8(dev, 0x5b, (1 << 5), 0);
+ /* DMA bandwidth control - Improved bandwidth */
+ pci_write_config8(dev, 0x53, 0xff);
+ /* ISA Positive Decoding control */
+ pci_write_config8(dev, 0x6d, 0xdf);
+ pci_write_config8(dev, 0x6e, 0x98);
+ pci_write_config8(dev, 0x6f, 0x30);
+}
+
+/**
+ *\brief VX900: Set up the south module IOAPIC (for the ISA/LPC bus)
+ *
+ * Enable the IOAPIC in the south module, and properly set it up.
+ * \n
+ * This is the hardware specific initialization for the IOAPIC, and complements
+ * the setup done by the generic IOAPIC driver. In order for the IOAPIC to work
+ * properly, it _must_ be declared in devicetree.cb .
+ * \n
+ * We are assuming this is called before the drivers/generic/ioapic code,
+ * which should be the case if devicetree.cb is set up properly.
+ */
+static void vx900_lpc_ioapic_setup(device_t dev)
+{
+ /* Find the IOAPIC, and make sure it's set up correctly in devicetree.cb
+ * If it's not, then the generic ioapic driver will not set it up
+ * correctly, and the MP table will not be correctly generated */
+ device_t ioapic;
+ for (ioapic = dev->next; ioapic; ioapic = ioapic->next) {
+ if (ioapic->path.type == DEVICE_PATH_IOAPIC)
+ break;
+ }
+
+ /* You did put an IOAPIC in devicetree.cb, didn't you? */
+ if (ioapic == 0) {
+ /* We don't have enough info to set up the IOAPIC */
+ printk(BIOS_ERR, "ERROR: South module IOAPIC not found. "
+ "Check your devicetree.cb\n");
+ return;
+ }
+
+ /* Found an IOAPIC, now we need to make sure it's the right one */
+ ioapic_config_t *config = (ioapic_config_t *) ioapic->chip_info;
+ if (!config->have_isa_interrupts) {
+ /* Umh, is this the right IOAPIC ? */
+ printk(BIOS_ERR, "ERROR: South module IOAPIC not carrying ISA "
+ "interrupts. Check your devicetree.cb\n");
+ printk(BIOS_ERR, "Will not initialize this IOAPIC.\n");
+ return;
+ }
+
+ /* The base address of this IOAPIC _must_ be at 0xfec00000.
+ * Don't move this value to a #define, as people might think it's
+ * configurable. It is not. */
+ const u32 base = config->base;
+ if (base != 0xfec00000) {
+ printk(BIOS_ERR, "ERROR: South module IOAPIC base should be at "
+ "0xfec00000\n but we found it at 0x%.8x\n", base);
+ return;
+ }
+
+ print_debug("VX900 LPC: Setting up the south module IOAPIC.\n");
+ /* Enable IOAPIC
+ * So much work for one line of code. Talk about bloat :)
+ * The 8259 PIC should still work even if the IOAPIC is enabled, so
+ * there's no crime in enabling the IOAPIC here. */
+ pci_mod_config8(dev, 0x58, 0, 1 << 6);
+}
+
+static void vx900_lpc_interrupt_stuff(device_t dev)
+{
+ /* Enable setting trigger mode through 0x4d0, and 0x4d1 ports
+ * And enable I/O recovery time */
+ pci_mod_config8(dev, 0x40, 0, (1 << 2) | (1 << 6));
+ /* Set serial IRQ frame width to 6 PCI cycles (recommended by VIA)
+ * And enable serial IRQ */
+ pci_mod_config8(dev, 0x52, 3 << 0, (1 << 3) | (1 << 0));
+
+ /* Disable IRQ12 storm FIXME: bad comment */
+ pci_mod_config8(dev, 0x51, (1 << 2), 0);
+
+ pci_write_config8(dev, 0x4c, (1 << 6));
+
+ /* FIXME: Do we really need this? SeaBIOS/linux runs fine without it.
+ * Is this something the payload/OS should do, or is it safe for us to
+ * do it? */
+ /* Get the IRQs up and running */
+ setup_i8259();
+
+ vx900_lpc_dma_setup(dev);
+
+ /* The IOAPIC is special, and we treat it separately */
+ vx900_lpc_ioapic_setup(dev);
+}
+
+static void vx900_lpc_init(device_t dev)
+{
+ vx900_lpc_interrupt_stuff(dev);
+ vx900_lpc_misc_stuff(dev);
+ dump_pci_device(dev);
+}
+
+static struct device_operations vx900_lpc_ops = {
+ .read_resources = pci_dev_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .init = vx900_lpc_init,
+ .scan_bus = scan_static_bus,
+};
+
+static const struct pci_driver lpc_driver __pci_driver = {
+ .ops = &vx900_lpc_ops,
+ .vendor = PCI_VENDOR_ID_VIA,
+ .device = PCI_DEVICE_ID_VIA_VX900_LPC,
+};
+
+#if CONFIG_PIRQ_ROUTE
+void pirq_assign_irqs(const u8 * pirq)
+{
+ device_t lpc;
+
+ lpc = dev_find_device(PCI_VENDOR_ID_VIA,
+ PCI_DEVICE_ID_VIA_VX900_LPC, 0);
+
+ /* Take care of INTA -> INTD */
+ pci_mod_config8(lpc, 0x55, (0xf << 4), pirq[0] << 4);
+ pci_write_config8(lpc, 0x56, pirq[1] | (pirq[2] << 4));
+ pci_write_config8(lpc, 0x57, pirq[3] << 4);
+
+ /* Enable INTE -> INTH to be on separate IRQs */
+ pci_mod_config8(lpc, 0x46, 0, 1 << 4);
+ /* Now do INTE -> INTH */
+ pci_write_config8(lpc, 0x44, pirq[4] | (pirq[5] << 4));
+ pci_write_config8(lpc, 0x45, pirq[6] | (pirq[7] << 4));
+}
+#endif
diff --git a/src/northbridge/via/vx900/northbridge.c b/src/northbridge/via/vx900/northbridge.c
new file mode 100644
index 0000000000..fe42493dbb
--- /dev/null
+++ b/src/northbridge/via/vx900/northbridge.c
@@ -0,0 +1,341 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "vx900.h"
+#include "chip.h"
+
+#include <console/console.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <cpu/cpu.h>
+#include <cbmem.h>
+#include <lib.h>
+#include <reset.h>
+#include <string.h>
+
+#define RAM_4GB (((u64)1) << 32)
+
+/**
+ * @file northbridge.c
+ *
+ * STATUS: Pretty good
+ * One thing that needs to be thoroughly tested is the remap above 4G logic.
+ * Unfortunately, while we cannot initialize odd ranks, our testing
+ * possibilities are somewhat limited. A point of failure that is not covered is
+ * when the amount of RAM and PCI config space added up exceeds 8GB. The
+ * remapping mechanism will overflow, the effects of which are unknown.
+ */
+
+void hard_reset(void)
+{
+ outb((1 << 2) | (1 << 1), 0xcf9);
+}
+
+static u64 vx900_get_top_of_ram(device_t mcu)
+{
+ u16 reg16;
+ /* The last valid DRAM address is computed by the MCU
+ * One issue might be if we have a hole in the rank mappings, so that
+ * virtual ranks are not mapped successively in the linear address space
+ * (Ex: rank 0 mapped 0-1G, rank 1 mapped 2G-3G)
+ * We don't do this awkward mapping in RAM init, so we don't worry about
+ * it here, but it is something to keep in mind if having RAM issues */
+ reg16 = pci_read_config16(mcu, 0x88) & 0x07ff;
+ return (u64) reg16 << 24;
+}
+
+/*
+ * This guy is meant to go away, but for now, leave it in so that we can see
+ * if the logic to remap RAM above 4G has errors.
+ */
+static void killme_debug_4g_remap_reg(u32 reg32)
+{
+ if (reg32 & (1 << 0))
+ print_debug("Mem remapping enabled\n");
+ u64 remapstart = (reg32 >> 2) & 0x3ff;
+ u64 remapend = (reg32 >> 14) & 0x3ff;
+ remapstart <<= 26;
+ remapend <<= 26;
+ printk(BIOS_DEBUG, "Remapstart %lld(MB) \n", remapstart >> 20);
+ printk(BIOS_DEBUG, "Remapend %lld(MB) \n", remapend >> 20);
+}
+
+/**
+ * \brief Remap low memory colliding with PCI MMIO space, above 4G
+ *
+ * @param mcu The memory controller
+ * @param tolm Top of low memory.
+ *
+ * @return The new top of memory.
+ */
+static u64 vx900_remap_above_4g(device_t mcu, u32 tolm)
+{
+ size_t i;
+ u8 reg8, start8, end8, start, end;
+ u16 reg16;
+ u32 reg32;
+ u64 tor, newtor, chunk;
+
+ /*
+ * The remapping mechanism works like this:
+ *
+ * - Choose the top of low memory.
+ * This becomes the "remap from"
+ * - Choose a chunk above 4G where to remap.
+ * This becomes "remap to"
+ * - Choose a chunk above 4G where to end the remapping.
+ * This becomes "remap until"
+ *
+ * This remaps a "chunk" of memory where we want to.
+ * sizeof(chunk) = until - to;
+ *
+ * Therefore the memory region from "from" to " from + sizeof(chunk)"
+ * becomes accessible at "to" to "until"
+ */
+ if (tolm >= vx900_get_top_of_ram(mcu)) {
+ print_debug("Nothing to remap\n");
+ }
+
+ /* This is how the Vendor BIOS. Keep it for comparison for now */
+ killme_debug_4g_remap_reg(0x00180141);
+ /* We can remap with a granularity of 64MB, so align tolm */
+ tolm &= ~((64 * MiB) - 1);
+
+ /* The "start remapping from where ?" register */
+ reg16 = ((tolm >> 20) & 0xfff) << 4;
+ pci_mod_config16(mcu, 0x84, 0xfff0, reg16);
+
+ /* Find the chunk size */
+ tor = vx900_get_top_of_ram(mcu);
+ printk(BIOS_DEBUG, "Top of RAM %lldMB\n", tor >> 20);
+
+ if (tor < RAM_4GB) {
+ chunk = tor - tolm;
+ newtor = RAM_4GB + chunk;
+ } else {
+ chunk = (RAM_4GB - tolm);
+ newtor = tor + chunk;
+ }
+ printk(BIOS_DEBUG, "New top of RAM %lldMB\n", newtor >> 20);
+
+ reg8 = tolm >> 26;
+ /* Which rank does the PCI TOLM fall on? */
+ for (i = 0; i < VX900_MAX_MEM_RANKS; i++) {
+ end8 = pci_read_config8(mcu, 0x40 + i);
+ if (reg8 > end8)
+ continue;
+ start8 = pci_read_config8(mcu, 0x48 + i);
+ if (reg8 <= start8)
+ continue;
+ printk(BIOS_DEBUG, "Address %x falls on rank %ld\n", tolm, i);
+ break;
+ }
+
+ for (; i < VX900_MAX_MEM_RANKS; i++) {
+ start = pci_read_config8(mcu, 0x48 + i);
+ end = pci_read_config8(mcu, 0x40 + i);
+
+ if (end == 0) {
+ printk(BIOS_DEBUG, "Huh? rank %ld empty?\n", i);
+ continue;
+ }
+
+ if (end < (tolm >> 26)) {
+ printk(BIOS_DEBUG, "Huh? rank %ld don't need remap?\n",
+ i);
+ continue;
+ }
+
+ printk(BIOS_DEBUG, "Physical rank %u is mapped to\n"
+ " Start address: 0x%.10llx (%dMB)\n"
+ " End address: 0x%.10llx (%dMB)\n",
+ (int)i,
+ ((u64) start << 26), (start << (26 - 20)),
+ ((u64) end << 26), (end << (26 - 20)));
+
+ if (end < (RAM_4GB >> 26))
+ end = (RAM_4GB >> 26);
+
+ if (end >= (tolm >> 26))
+ end += chunk >> 26;
+
+ if (start > (tolm >> 26))
+ start += chunk >> 26;
+
+ pci_write_config8(mcu, 0x48 + i, start);
+ pci_write_config8(mcu, 0x40 + i, end);
+
+ printk(BIOS_DEBUG, "ReMapped Physical rank %u, to\n"
+ " Start address: 0x%.10llx (%dMB)\n"
+ " End address: 0x%.10llx (%dMB)\n",
+ (int)i,
+ ((u64) start << 26), (start << (26 - 20)),
+ ((u64) end << 26), (end << (26 - 20)));
+ }
+
+ /* The "remap to where?" register */
+ reg32 = ((max(tor, RAM_4GB) >> 26) & 0x3ff) << 2;
+ /* The "remap until where?" register */
+ reg32 |= ((newtor >> 26) & 0x3ff) << 14;
+ /* Now enable the goodies */
+ reg32 |= (1 << 0);
+ pci_write_config32(mcu, 0xf8, reg32);
+ printk(BIOS_DEBUG, "Wrote remap map %x\n", reg32);
+ killme_debug_4g_remap_reg(reg32);
+
+ printk(BIOS_DEBUG, "New top of memory is at %lldMB\n", newtor >> 20);
+ return newtor;
+}
+
+static void vx900_set_resources(device_t dev)
+{
+ u32 pci_tolm, tomk, vx900_tolm, full_tolmk, fbufk, tolmk;
+
+ print_debug("========================================"
+ "========================================\n");
+ print_debug("============= VX900 memory sizing & Co. "
+ "========================================\n");
+ print_debug("========================================"
+ "========================================\n");
+
+ int idx = 10;
+ const device_t mcu = dev_find_device(PCI_VENDOR_ID_VIA,
+ PCI_DEVICE_ID_VIA_VX900_MEMCTRL,
+ 0);
+ if (!mcu) {
+ die("Something is terribly wrong.\n"
+ " We tried locating the MCU on the PCI bus, "
+ "but couldn't find it. Halting.\n");
+ }
+
+ /* How much low adrress space do we have? */
+ pci_tolm = find_pci_tolm(dev->link_list);
+ printk(BIOS_SPEW, "Found PCI tolm at %.8x\n", pci_tolm);
+ printk(BIOS_SPEW, "Found PCI tolm at %dMB\n", pci_tolm >> 20);
+
+ /* Figure out the total amount of RAM */
+ tomk = vx900_get_top_of_ram(mcu) >> 10;
+ printk(BIOS_SPEW, "Found top of memory at %dMB\n", tomk >> 10);
+
+ /* Do the same for top of low RAM */
+ vx900_tolm = (pci_read_config16(mcu, 0x84) & 0xfff0) >> 4;
+ full_tolmk = vx900_tolm << (20 - 10);
+ /* Remap above 4G if needed */
+ full_tolmk = min(full_tolmk, pci_tolm >> 10);
+ printk(BIOS_SPEW, "Found top of low memory at %dMB\n",
+ full_tolmk >> 10);
+
+ /* What about the framebuffer for the integrated GPU? */
+ fbufk = chrome9hd_fb_size() >> 10;
+ printk(BIOS_SPEW, "Integrated graphics buffer: %dMB\n", fbufk >> 10);
+
+ /* Can't use the framebuffer as system RAM, sorry */
+ tolmk = min(full_tolmk, tomk);
+ tolmk -= fbufk;
+ ram_resource(dev, idx++, 0, 640);
+ printk(BIOS_SPEW, "System ram left: %dMB\n", tolmk >> 10);
+ /* FIXME: how can we avoid leaving this hole?
+ * Leave a hole for VGA, 0xa0000 - 0xc0000 ?? */
+ /* TODO: VGA Memory hole can be disabled in SNMIC. Upper 64k of ROM seem
+ * to be always mapped to the top of 1M, but this can be overcome with
+ * some smart positive/subtractive resource decoding */
+ ram_resource(dev, idx++, 768, (tolmk - 768));
+ uma_memory_size = fbufk << 10;
+ uma_memory_base = tolmk << 10;
+
+ printk(BIOS_DEBUG, "UMA @ %lldMB + %lldMB\n", uma_memory_base >> 20,
+ uma_memory_size >> 20);
+ /* FIXME: How do we handle remapping above 4G? */
+ u64 tor = vx900_remap_above_4g(mcu, pci_tolm);
+ ram_resource(dev, idx++, RAM_4GB >> 10, (tor - RAM_4GB) >> 10);
+
+ /* Leave some space for ACPI, PIRQ and MP tables */
+ high_tables_base = (tolmk << 10) - HIGH_MEMORY_SIZE;
+ high_tables_size = HIGH_MEMORY_SIZE;
+ printk(BIOS_DEBUG, "high_tables_base: %08llx, size %lld\n",
+ high_tables_base, high_tables_size);
+
+ print_debug("======================================================\n");
+ assign_resources(dev->link_list);
+}
+
+static void vx900_read_resources(device_t dev)
+{
+ /* Our fixed resources start at 0 */
+ int idx = 0;
+ /* Reserve our ROM mapped space */
+ struct resource *res;
+ res = new_resource(dev, idx++);
+ res->size = CONFIG_ROM_SIZE;
+ res->base = 0xffffffff - (res->size - 1);
+ res->flags = IORESOURCE_MEM | IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
+
+ /* Now do the same for our MMCONF
+ * We always run with MMCONF enabled. We need to access the extended
+ * config space when configuring PCI-Express links */
+ res = new_resource(dev, idx++);
+ res->size = 256 * MiB;
+ res->base = CONFIG_MMCONF_BASE_ADDRESS;
+ res->flags = IORESOURCE_MEM | IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
+
+ pci_domain_read_resources(dev);
+}
+
+static struct device_operations pci_domain_ops = {
+ .read_resources = vx900_read_resources,
+ .set_resources = vx900_set_resources,
+ .enable_resources = NULL,
+ .init = NULL,
+ .scan_bus = pci_domain_scan_bus,
+ /* We always run with MMCONF enabled. */
+ .ops_pci_bus = &pci_ops_mmconf,
+};
+
+static void cpu_bus_init(device_t dev)
+{
+ initialize_cpus(dev->link_list);
+}
+
+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(device_t dev)
+{
+ /* Set the operations if it is a special bus type */
+ if (dev->path.type == DEVICE_PATH_DOMAIN) {
+ dev->ops = &pci_domain_ops;
+ } else if (dev->path.type == DEVICE_PATH_CPU_CLUSTER) {
+ dev->ops = &cpu_bus_ops;
+ }
+}
+
+struct chip_operations northbridge_via_vx900_ops = {
+ CHIP_NAME("VIA VX900 Chipset")
+ .enable_dev = enable_dev,
+};
diff --git a/src/northbridge/via/vx900/pcie.c b/src/northbridge/via/vx900/pcie.c
new file mode 100644
index 0000000000..109e5c9ffe
--- /dev/null
+++ b/src/northbridge/via/vx900/pcie.c
@@ -0,0 +1,123 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2013 Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <arch/io.h>
+#include <console/console.h>
+#include <device/pci.h>
+#include <device/pciexp.h>
+#include <device/pci_ids.h>
+
+#include "vx900.h"
+
+/**
+ * @file pcie.c
+ *
+ * STATUS:
+ * We do part of the sequence to initialize the PCIE link. The problem is that
+ * the reset signal for each slot is connected to a GPO pin, but We don't know
+ * which GPO pin. We need to figure out which GPIO pin is hooked to which slot,
+ * and have a mechanism to specify this per-mainboard (devicetree.cb).
+ *
+ * There is currently no timeout detection mechanism for when a link comes up.
+ * If the link never comes up, we hang.
+ */
+
+static void vx900_pcie_link_init(device_t dev)
+{
+ u8 reg8;
+ u32 reg32;
+
+ u8 fn = dev->path.pci.devfn & 0x07;
+
+ /* Step 1 : Check for presence of PCIE device */
+ reg8 = pci_read_config8(dev, 0x5a);
+
+ if (reg8 & (1 << 6))
+ printk(BIOS_DEBUG, "Card detected in PEX%i\n", fn);
+ else
+ return;
+
+ /* Step 2: Wait for device to enter L0 state */
+ /* FIXME: implement timeout detection */
+ while (0x8a != pci_read_config8(dev, 0x1c3)) ;
+
+ /* Step 3: Clear PCIe error status, then check for failures */
+ pci_write_config32(dev, 0x104, 0xffffffff);
+ reg32 = pci_read_config32(dev, 0x104);
+ if (0 != reg32) {
+ printk(BIOS_DEBUG, "PEX init error. flags 0x%.8x\n", reg32);
+ return;
+ }
+
+ pci_write_config32(dev, 0x110, 0xffffffff);
+ reg32 = pci_read_config32(dev, 0x110);
+ if (0 != reg32)
+ printk(BIOS_DEBUG, "PEX errors. flags 0x%.8x\n", reg32);
+
+ pci_write_config8(dev, 0xa4, 0xff);
+ if (pci_read_config8(dev, 0x4a) & (1 << 3))
+ print_debug("Unsupported request detected.\n");
+
+ pci_write_config8(dev, 0x15a, 0xff);
+ if (pci_read_config8(dev, 0x15a) & (1 << 1))
+ print_debug("Negotiation pending.\n");
+
+ /* Step 4: Read vendor ID */
+ /* FIXME: Do we want to run through the whole sequence and delay boot
+ * by several seconds if the device does not respond properly the first
+ * time? */
+}
+
+static void vx900_pex_dev_set_resources(device_t dev)
+{
+ assign_resources(dev->link_list);
+}
+
+static void vx900_pex_init(device_t dev)
+{
+ /* FIXME: For some reason, PEX0 hangs on init. Find issue, fix it. */
+ if ((dev->path.pci.devfn & 0x7) == 0)
+ return;
+
+ vx900_pcie_link_init(dev);
+}
+
+static struct device_operations vx900_pex_ops = {
+ .read_resources = pci_bus_read_resources,
+ .set_resources = vx900_pex_dev_set_resources,
+ .enable_resources = pci_bus_enable_resources,
+ .init = vx900_pex_init,
+ .scan_bus = pciexp_scan_bridge,
+ .reset_bus = pci_bus_reset,
+};
+
+static const unsigned short pci_device_ids[] = {
+ PCI_DEVICE_ID_VIA_VX900_PEX1,
+ PCI_DEVICE_ID_VIA_VX900_PEX2,
+ PCI_DEVICE_ID_VIA_VX900_PEX3,
+ PCI_DEVICE_ID_VIA_VX900_PEX4,
+ 0,
+};
+
+static const struct pci_driver pex_driver __pci_driver = {
+ .ops = &vx900_pex_ops,
+ .vendor = PCI_VENDOR_ID_VIA,
+ .devices = pci_device_ids,
+
+};
diff --git a/src/northbridge/via/vx900/sata.c b/src/northbridge/via/vx900/sata.c
new file mode 100644
index 0000000000..63295e56c3
--- /dev/null
+++ b/src/northbridge/via/vx900/sata.c
@@ -0,0 +1,280 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <console/console.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+
+#include "vx900.h"
+
+/**
+ * @file sata.c
+ *
+ * STATUS: Pretty good
+ * The only issue is the SATA EPHY configuration. We do not know if it is board
+ * specific or not. Otherwise, the SATA controller works without issues.
+ */
+
+static void vx900_print_sata_errors(u32 flags)
+{
+ /* Status flags */
+ printk(BIOS_DEBUG, "\tPhyRdy %s\n",
+ (flags & (1 << 16)) ? "changed" : "not changed");
+ printk(BIOS_DEBUG, "\tCOMWAKE %s\n",
+ (flags & (1 << 16)) ? "detected" : "not detected");
+ printk(BIOS_DEBUG, "\tExchange as determined by COMINIT %s\n",
+ (flags & (1 << 26)) ? "occured" : "not occured");
+ printk(BIOS_DEBUG, "\tPort selector presence %s\n",
+ (flags & (1 << 27)) ? "detected" : "not detected");
+ /* Errors */
+ if (flags & (1 << 0))
+ print_debug("\tRecovered data integrity ERROR\n");
+ if (flags & (1 << 1))
+ print_debug("\tRecovered data communication ERROR\n");
+ if (flags & (1 << 8))
+ print_debug("\tNon-recovered Transient Data Integrity ERROR\n");
+ if (flags & (1 << 9))
+ print_debug("\tNon-recovered Persistent Communication or"
+ "\tData Integrity ERROR\n");
+ if (flags & (1 << 10))
+ print_debug("\tProtocol ERROR\n");
+ if (flags & (1 << 11))
+ print_debug("\tInternal ERROR\n");
+ if (flags & (1 << 17))
+ print_debug("\tPHY Internal ERROR\n");
+ if (flags & (1 << 19))
+ print_debug("\t10B to 8B Decode ERROR\n");
+ if (flags & (1 << 20))
+ print_debug("\tDisparity ERROR\n");
+ if (flags & (1 << 21))
+ print_debug("\tCRC ERROR\n");
+ if (flags & (1 << 22))
+ print_debug("\tHandshake ERROR\n");
+ if (flags & (1 << 23))
+ print_debug("\tLink Sequence ERROR\n");
+ if (flags & (1 << 24))
+ print_debug("\tTransport State Transition ERROR\n");
+ if (flags & (1 << 25))
+ print_debug("\tUNRECOGNIZED FIS type\n");
+}
+
+static void vx900_dbg_sata_errors(device_t dev)
+{
+ /* Port 0 */
+ if (pci_read_config8(dev, 0xa0) & (1 << 0)) {
+ print_debug("Device detected in SATA port 0.\n");
+ u32 flags = pci_read_config32(dev, 0xa8);
+ vx900_print_sata_errors(flags);
+ };
+ /* Port 1 */
+ if (pci_read_config8(dev, 0xa1) & (1 << 0)) {
+ print_debug("Device detected in SATA port 1.\n");
+ u32 flags = pci_read_config32(dev, 0xac);
+ vx900_print_sata_errors(flags);
+ };
+}
+
+typedef u8 sata_phy_config[64];
+
+static sata_phy_config reference_ephy = {
+ 0x80, 0xb8, 0xf0, 0xfe, 0x40, 0x7e, 0xf6, 0xdd,
+ 0x1a, 0x22, 0xa0, 0x10, 0x02, 0xa9, 0x7c, 0x7e,
+ 0x00, 0x00, 0x00, 0x00, 0x40, 0x30, 0x84, 0x8c,
+ 0x75, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x40, 0xd0, 0x41, 0x40, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x40, 0x50, 0x41, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static u32 sata_phy_read32(device_t dev, u8 index)
+{
+ /* The SATA PHY control registers are accessed by a funny index/value
+ * scheme. Each byte (0,1,2,3) has its own 4-bit index */
+ index = (index >> 2) & 0xf;
+ u16 i16 = index | (index << 4) | (index << 8) | (index << 12);
+ /* The index */
+ pci_write_config16(dev, 0x68, i16);
+ /* The value */
+ return pci_read_config32(dev, 0x64);
+}
+
+static void sata_phy_write32(device_t dev, u8 index, u32 val)
+{
+ /* The SATA PHY control registers are accessed by a funny index/value
+ * scheme. Each byte (0,1,2,3) has its own 4-bit index */
+ index = (index >> 2) & 0xf;
+ u16 i16 = index | (index << 4) | (index << 8) | (index << 12);
+ /* The index */
+ pci_write_config16(dev, 0x68, i16);
+ /* The value */
+ pci_write_config32(dev, 0x64, val);
+}
+
+static void vx900_sata_read_phy_config(device_t dev, sata_phy_config cfg)
+{
+ size_t i;
+ u32 *data = (u32 *) cfg;
+ for (i = 0; i < (sizeof(sata_phy_config)) >> 2; i++) {
+ data[i] = sata_phy_read32(dev, i << 2);
+ }
+}
+
+static void vx900_sata_write_phy_config(device_t dev, sata_phy_config cfg)
+{
+ size_t i;
+ u32 *data = (u32 *) cfg;
+ for (i = 0; i < (sizeof(sata_phy_config)) >> 2; i++) {
+ sata_phy_write32(dev, i << 2, data[i]);
+ }
+}
+
+static void vx900_sata_dump_phy_config(sata_phy_config cfg)
+{
+ print_debug("SATA PHY config:\n");
+ int i;
+ for (i = 0; i < sizeof(sata_phy_config); i++) {
+ unsigned char val;
+ if ((i & 0x0f) == 0) {
+ print_debug_hex8(i);
+ print_debug_char(':');
+ }
+ val = cfg[i];
+ if ((i & 7) == 0)
+ print_debug(" |");
+ print_debug_char(' ');
+ print_debug_hex8(val);
+ if ((i & 0x0f) == 0x0f) {
+ print_debug("\n");
+ }
+ }
+}
+
+/**
+ * \brief VX900: Place the onboard SATA controller in Native IDE mode
+ *
+ * AHCI mode requires a sub-class of 0x06, and Interface of 0x0
+ * SATA mode requires a sub-class of 0x06, and Interface of 0x00
+ * Unfortunately, setting the class to SATA, will prevent us from modyfing the
+ * interface register to an AHCI/SATA compliant value. Thus, payloads or OS may
+ * not properly identify this as a SATA controller.
+ * We could set the class code to 0x04, which would cause the interface register
+ * to become 0x00, which represents a RAID controller. Unfortunately, when we do
+ * this, SeaBIOS will skip this as a storage device, and we will not be able to
+ * boot.
+ * Our only option is to operate in IDE mode. We choose native IDE so that we
+ * can freely assign an IRQ, and are not forced to use IRQ14
+ */
+static void vx900_native_ide_mode(device_t dev)
+{
+ /* Disable subclass write protect */
+ pci_mod_config8(dev, 0x45, 1 << 7, 0);
+ /* Change the device class to IDE */
+ pci_write_config16(dev, PCI_CLASS_DEVICE, PCI_CLASS_STORAGE_IDE);
+ /* Re-enable subclass write protect */
+ pci_mod_config8(dev, 0x45, 0, 1 << 7);
+ /* Put it in native IDE mode */
+ pci_write_config8(dev, PCI_CLASS_PROG, 0x8f);
+}
+
+static void vx900_sata_init(device_t dev)
+{
+ /* Enable SATA primary channel IO access */
+ pci_mod_config8(dev, 0x40, 0, 1 << 1);
+ /* Just SATA, so it makes sense to be in native SATA mode */
+ vx900_native_ide_mode(dev);
+
+ /* TP Layer Idle at least 20us before the Following Command */
+ pci_mod_config8(dev, 0x53, 0, 1 << 7);
+ /* Resend COMRESET When Recovering SATA Gen2 Device Error */
+ pci_mod_config8(dev, 0x62, 1 << 1, 1 << 7);
+
+ /* Fix "PMP Device Can’t Detect HDD Normally" (VIA Porting Guide)
+ * SATA device detection will not work unless we clear these bits.
+ * Without doing this, SeaBIOS (and potentially other payloads) will
+ * timeout when detecting SATA devices */
+ pci_mod_config8(dev, 0x89, (1 << 3) | (1 << 6), 0);
+
+ /* 12.7 Two Software Resets May Affect the System
+ * When the software does the second reset before the first reset
+ * finishes, it may cause the system hang. It would do one software
+ * reset and check the BSY bit of one port only, and the BSY bit of
+ * other port would be 1, then it does another software reset
+ * immediately and causes the system hang.
+ * This is because the first software reset doesn’t finish, and the
+ * state machine of the host controller conflicts, it can’t finish the
+ * second one anymore. The BSY bit of slave port would be always 1 after
+ * the second software reset issues. BIOS should set the following
+ * bit to avoid this issue. */
+ pci_mod_config8(dev, 0x80, 0, 1 << 6);
+
+ /* We need to set the EPHY values before doing anything with the link */
+ sata_phy_config ephy;
+ vx900_sata_read_phy_config(dev, ephy);
+ if (1) {
+ vx900_sata_dump_phy_config(ephy);
+ vx900_sata_write_phy_config(dev, reference_ephy);
+ } else {
+ /* Enable TX and RX driving resistance */
+ /* TX - 50 Ohm */
+ ephy[1] &= ~(0x1f << 3);
+ ephy[1] |= (1 << 7) | (8 << 3);
+ /* RX - 50 Ohm */
+ ephy[2] &= ~(0x1f << 3);
+ ephy[2] |= (1 << 7) | (8 << 3);
+ vx900_sata_write_phy_config(dev, ephy);
+ }
+
+ vx900_sata_read_phy_config(dev, ephy);
+ vx900_sata_dump_phy_config(ephy);
+
+ /* Clear error flags */
+ pci_write_config32(dev, 0xa8, 0xffffffff);
+ pci_write_config32(dev, 0xac, 0xffffffff);
+
+ /* Start OOB link negotiation sequence */
+ pci_mod_config8(dev, 0xb9, 0, 3 << 4);
+
+ /* FIXME: From now on, we are just doing DEBUG stuff
+ * Wait until PHY communication is enabled */
+ u32 wloops = 0;
+ while (!(pci_read_config8(dev, 0xa0) & (1 << 1)))
+ wloops++;
+ printk(BIOS_SPEW, "SATA wait loops: %u\n", wloops);
+
+ vx900_dbg_sata_errors(dev);
+}
+
+static void vx900_sata_read_resources(device_t dev)
+{
+ pci_dev_read_resources(dev);
+}
+
+static struct device_operations vga_operations = {
+ .read_resources = vx900_sata_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .init = vx900_sata_init,
+};
+
+static const struct pci_driver chrome9hd_driver __pci_driver = {
+ .ops = &vga_operations,
+ .vendor = PCI_VENDOR_ID_VIA,
+ .device = PCI_DEVICE_ID_VIA_VX900_SATA,
+};
diff --git a/src/northbridge/via/vx900/traf_ctrl.c b/src/northbridge/via/vx900/traf_ctrl.c
new file mode 100644
index 0000000000..2e73ea439e
--- /dev/null
+++ b/src/northbridge/via/vx900/traf_ctrl.c
@@ -0,0 +1,145 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2012 Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <console/console.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <drivers/generic/ioapic/chip.h>
+
+#include "vx900.h"
+#include "chip.h"
+
+/**
+ * @file traf_ctrl.c
+ *
+ * STATUS:
+ * The same issues with the IOAPIC pointe in lpc.c also apply here.
+ *
+ * We need to check if the current PCIE lane configuration mechanism is sane.
+ */
+
+/**
+ * \brief VX900: Set up the north module IOAPIC (for PCIE and VGA)
+ *
+ * Enable the IOAPIC in the south module, and properly set it up.
+ * \n
+ * This is the hardware specific initialization for the IOAPIC, and complements
+ * the setup done by the generic IOAPIC driver. In order for the IOAPIC to work
+ * properly, it _must_ be declared in devicetree.cb .
+ * \n
+ * We are assuming this is called before the drivers/generic/ioapic code,
+ * which should be the case if devicetree.cb is set up properly.
+ */
+static void vx900_north_ioapic_setup(device_t dev)
+{
+ u8 base_val;
+ device_t ioapic;
+ ioapic_config_t *config;
+ /* Find the IOAPIC, and make sure it's set up correctly in devicetree.cb
+ * If it's not, then the generic ioapic driver will not set it up
+ * correctly, and the MP table will not be correctly generated */
+ for (ioapic = dev->next; ioapic; ioapic = ioapic->next) {
+ if (ioapic->path.type == DEVICE_PATH_IOAPIC)
+ break;
+ }
+ /* You did put an IOAPIC in devicetree.cb, didn't you? */
+ if (ioapic == 0) {
+ /* We don't have enough info to set up the IOAPIC */
+ printk(BIOS_ERR, "ERROR: North module IOAPIC not found. "
+ "Check your devicetree.cb\n");
+ return;
+ }
+ /* Found our IOAPIC, and it should not carry ISA interrupts */
+ config = (ioapic_config_t *) ioapic->chip_info;
+ if (config->have_isa_interrupts) {
+ /* Umh, is this the right IOAPIC ? */
+ printk(BIOS_ERR, "ERROR: North module IOAPIC should not carry "
+ "ISA interrupts.\n" "Check your devicetree.cb\n");
+ printk(BIOS_ERR, "Will not initialize this IOAPIC.\n");
+ return;
+ }
+ /* The base address of this IOAPIC _must_
+ * be between 0xfec00000 and 0xfecfff00
+ * be 256-byte aligned
+ */
+ if ((config->base < 0xfec0000 || config->base > 0xfecfff00)
+ || ((config->base & 0xff) != 0)) {
+ printk(BIOS_ERR, "ERROR: North module IOAPIC base should be "
+ "between 0xfec00000 and 0xfecfff00\n"
+ "and must be aligned to a 256-byte boundary, "
+ "but we found it at 0x%.8x\n", config->base);
+ return;
+ }
+
+ printk(BIOS_DEBUG, "VX900 TRAF_CTR: Setting up the north module IOAPIC "
+ "at 0%.8x\n", config->base);
+
+ /* First register of the IOAPIC base */
+ base_val = (config->base >> 8) & 0xff;
+ pci_write_config8(dev, 0x41, base_val);
+ /* Second register of the base.
+ * Bit[7] also enables the IOAPIC and bit[5] enables MSI cycles */
+ base_val = (config->base >> 16) & 0xf;
+ pci_mod_config8(dev, 0x40, 0, base_val | (1 << 7) | (1 << 5));
+}
+
+/*
+ * Configures the PCI-express ports
+ *
+ * FIXME: triple-quadruple-check this
+ */
+static void vx900_pex_link_setup(device_t dev)
+{
+ u8 reg8;
+ struct northbridge_via_vx900_config *nb = (void *)dev->chip_info;
+
+ reg8 = pci_read_config8(dev, 0xb0);
+ reg8 &= ~((1 << 7) | (1 << 3));
+
+ if (nb->assign_pex_to_dp)
+ reg8 |= (1 << 7);
+
+ if (!nb->pcie_port1_2_lane_wide)
+ reg8 |= (1 << 3);
+
+ pci_write_config8(dev, 0xb0, reg8);
+}
+
+static void vx900_traf_ctr_init(device_t dev)
+{
+ vx900_north_ioapic_setup(dev);
+ vx900_pex_link_setup(dev);
+}
+
+static struct device_operations traf_ctrl_ops = {
+ .read_resources = pci_dev_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .init = vx900_traf_ctr_init,
+ /* Need this here, or the IOAPIC driver won't be called */
+ .scan_bus = scan_static_bus,
+};
+
+static const struct pci_driver traf_ctrl_driver __pci_driver = {
+ .ops = &traf_ctrl_ops,
+ .vendor = PCI_VENDOR_ID_VIA,
+ .device = PCI_DEVICE_ID_VIA_VX900_TRAF,
+};
diff --git a/src/northbridge/via/vx900/vx900.h b/src/northbridge/via/vx900/vx900.h
index 52c895c598..b4a77c5b6b 100644
--- a/src/northbridge/via/vx900/vx900.h
+++ b/src/northbridge/via/vx900/vx900.h
@@ -42,6 +42,7 @@
#include <console/console.h>
u32 chrome9hd_fb_size(void);
+u8 vx900_int15_get_5f18_bl(void);
/* We use these throughout the code. They really belong in a generic part of
* coreboot, but until bureaucracy gets them there, we still need them */