diff options
Diffstat (limited to 'src/northbridge/via/vx900/sata.c')
-rw-r--r-- | src/northbridge/via/vx900/sata.c | 280 |
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, +}; |