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 + * + * 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 . + */ + +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 + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +#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: + * + * + * 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 + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * + * 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 . + */ + +#include "vx900.h" +#include "chip.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * + * 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 . + */ + +#include +#include +#include +#include +#include + +#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 + * + * 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 . + */ + +#include +#include +#include + +#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 + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +#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 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 */