aboutsummaryrefslogtreecommitdiff
path: root/src/northbridge/via/vx900/sata.c
diff options
context:
space:
mode:
authorAlexandru Gagniuc <mr.nuke.me@gmail.com>2013-06-09 16:06:07 -0500
committerRonald G. Minnich <rminnich@gmail.com>2013-06-10 22:58:21 +0200
commit23211b0200eac11c64fe9dadadb5ec5ef1deabc6 (patch)
tree865130c93105ab780e90d252a9fb66a0ad599380 /src/northbridge/via/vx900/sata.c
parent7d31e7c13897e4b2548136c7a6f701b9121b7ad3 (diff)
VIA VX900: Add minimal ramstage needed to boot linux
This is the minimal code needed to get past ramstage, load SeaBIOS, jump to GRUB2, and boot linux (or load memtest). See individual source files for the status of each individual component. Change-Id: Ib7d5d7593c945f18af2c2fc5e0ae689ba66131a2 Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com> Reviewed-on: http://review.coreboot.org/3419 Tested-by: build bot (Jenkins) Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
Diffstat (limited to 'src/northbridge/via/vx900/sata.c')
-rw-r--r--src/northbridge/via/vx900/sata.c280
1 files changed, 280 insertions, 0 deletions
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,
+};