blob: b748c392d11fbc67f8a2007c3090b7d8c4b074ce [file] [log] [blame]
/*
* 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))
printk(BIOS_DEBUG, "Unsupported request detected.\n");
pci_write_config8(dev, 0x15a, 0xff);
if (pci_read_config8(dev, 0x15a) & (1 << 1))
printk(BIOS_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,
};