diff options
author | Stefan Reinauer <reinauer@chromium.org> | 2012-11-30 12:34:04 -0800 |
---|---|---|
committer | Ronald G. Minnich <rminnich@gmail.com> | 2012-11-30 23:59:58 +0100 |
commit | 8d7115560d469f901d7d8ccb242d0b437e7394aa (patch) | |
tree | 0f1b4bd63c48a233c49d5a9ca15f08a1675d1ff4 /src/device/pci_device.c | |
parent | 4b6be985aae8bff84ae442e7be7669e93694fa1e (diff) |
Rename devices -> device
to match src/include/device
Change-Id: I5d0e5b4361c34881a3b81347aac48738cb5b9af0
Signed-off-by: Stefan Reinauer <reinauer@google.com>
Reviewed-on: http://review.coreboot.org/1960
Tested-by: build bot (Jenkins)
Reviewed-by: David Hendricks <dhendrix@chromium.org>
Diffstat (limited to 'src/device/pci_device.c')
-rw-r--r-- | src/device/pci_device.c | 1325 |
1 files changed, 1325 insertions, 0 deletions
diff --git a/src/device/pci_device.c b/src/device/pci_device.c new file mode 100644 index 0000000000..ff334fee0e --- /dev/null +++ b/src/device/pci_device.c @@ -0,0 +1,1325 @@ +/* + * This file is part of the coreboot project. + * + * It was originally based on the Linux kernel (drivers/pci/pci.c). + * + * Modifications are: + * Copyright (C) 2003-2004 Linux Networx + * (Written by Eric Biederman <ebiederman@lnxi.com> for Linux Networx) + * Copyright (C) 2003-2006 Ronald G. Minnich <rminnich@gmail.com> + * Copyright (C) 2004-2005 Li-Ta Lo <ollie@lanl.gov> + * Copyright (C) 2005-2006 Tyan + * (Written by Yinghai Lu <yhlu@tyan.com> for Tyan) + * Copyright (C) 2005-2009 coresystems GmbH + * (Written by Stefan Reinauer <stepan@coresystems.de> for coresystems GmbH) + */ + +/* + * PCI Bus Services, see include/linux/pci.h for further explanation. + * + * Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter, + * David Mosberger-Tang + * + * Copyright 1997 -- 1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz> + */ + +#include <console/console.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <arch/io.h> +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <delay.h> +#if CONFIG_HYPERTRANSPORT_PLUGIN_SUPPORT +#include <device/hypertransport.h> +#endif +#if CONFIG_PCIX_PLUGIN_SUPPORT +#include <device/pcix.h> +#endif +#if CONFIG_PCIEXP_PLUGIN_SUPPORT +#include <device/pciexp.h> +#endif +#if CONFIG_AGP_PLUGIN_SUPPORT +#include <device/agp.h> +#endif +#if CONFIG_CARDBUS_PLUGIN_SUPPORT +#include <device/cardbus.h> +#endif +#if CONFIG_PC80_SYSTEM +#include <pc80/i8259.h> +#endif +#if CONFIG_HAVE_ACPI_RESUME && !CONFIG_S3_VGA_ROM_RUN +#include <arch/acpi.h> +#endif +#if CONFIG_CHROMEOS +#include <vendorcode/google/chromeos/chromeos.h> +#endif + +u8 pci_moving_config8(struct device *dev, unsigned int reg) +{ + u8 value, ones, zeroes; + + value = pci_read_config8(dev, reg); + + pci_write_config8(dev, reg, 0xff); + ones = pci_read_config8(dev, reg); + + pci_write_config8(dev, reg, 0x00); + zeroes = pci_read_config8(dev, reg); + + pci_write_config8(dev, reg, value); + + return ones ^ zeroes; +} + +u16 pci_moving_config16(struct device *dev, unsigned int reg) +{ + u16 value, ones, zeroes; + + value = pci_read_config16(dev, reg); + + pci_write_config16(dev, reg, 0xffff); + ones = pci_read_config16(dev, reg); + + pci_write_config16(dev, reg, 0x0000); + zeroes = pci_read_config16(dev, reg); + + pci_write_config16(dev, reg, value); + + return ones ^ zeroes; +} + +u32 pci_moving_config32(struct device *dev, unsigned int reg) +{ + u32 value, ones, zeroes; + + value = pci_read_config32(dev, reg); + + pci_write_config32(dev, reg, 0xffffffff); + ones = pci_read_config32(dev, reg); + + pci_write_config32(dev, reg, 0x00000000); + zeroes = pci_read_config32(dev, reg); + + pci_write_config32(dev, reg, value); + + return ones ^ zeroes; +} + +/** + * Given a device, a capability type, and a last position, return the next + * matching capability. Always start at the head of the list. + * + * @param dev Pointer to the device structure. + * @param cap PCI_CAP_LIST_ID of the PCI capability we're looking for. + * @param last Location of the PCI capability register to start from. + * @return The next matching capability. + */ +unsigned pci_find_next_capability(struct device *dev, unsigned cap, + unsigned last) +{ + unsigned pos = 0; + u16 status; + unsigned reps = 48; + + status = pci_read_config16(dev, PCI_STATUS); + if (!(status & PCI_STATUS_CAP_LIST)) + return 0; + + switch (dev->hdr_type & 0x7f) { + case PCI_HEADER_TYPE_NORMAL: + case PCI_HEADER_TYPE_BRIDGE: + pos = PCI_CAPABILITY_LIST; + break; + case PCI_HEADER_TYPE_CARDBUS: + pos = PCI_CB_CAPABILITY_LIST; + break; + default: + return 0; + } + + pos = pci_read_config8(dev, pos); + while (reps-- && (pos >= 0x40)) { /* Loop through the linked list. */ + int this_cap; + + pos &= ~3; + this_cap = pci_read_config8(dev, pos + PCI_CAP_LIST_ID); + printk(BIOS_SPEW, "Capability: type 0x%02x @ 0x%02x\n", + this_cap, pos); + if (this_cap == 0xff) + break; + + if (!last && (this_cap == cap)) + return pos; + + if (last == pos) + last = 0; + + pos = pci_read_config8(dev, pos + PCI_CAP_LIST_NEXT); + } + return 0; +} + +/** + * Given a device, and a capability type, return the next matching + * capability. Always start at the head of the list. + * + * @param dev Pointer to the device structure. + * @param cap PCI_CAP_LIST_ID of the PCI capability we're looking for. + * @return The next matching capability. + */ +unsigned pci_find_capability(device_t dev, unsigned cap) +{ + return pci_find_next_capability(dev, cap, 0); +} + +/** + * Given a device and register, read the size of the BAR for that register. + * + * @param dev Pointer to the device structure. + * @param index Address of the PCI configuration register. + * @return TODO + */ +struct resource *pci_get_resource(struct device *dev, unsigned long index) +{ + struct resource *resource; + unsigned long value, attr; + resource_t moving, limit; + + /* Initialize the resources to nothing. */ + resource = new_resource(dev, index); + + /* Get the initial value. */ + value = pci_read_config32(dev, index); + + /* See which bits move. */ + moving = pci_moving_config32(dev, index); + + /* Initialize attr to the bits that do not move. */ + attr = value & ~moving; + + /* If it is a 64bit resource look at the high half as well. */ + if (((attr & PCI_BASE_ADDRESS_SPACE_IO) == 0) && + ((attr & PCI_BASE_ADDRESS_MEM_LIMIT_MASK) == + PCI_BASE_ADDRESS_MEM_LIMIT_64)) { + /* Find the high bits that move. */ + moving |= + ((resource_t) pci_moving_config32(dev, index + 4)) << 32; + } + + /* Find the resource constraints. + * Start by finding the bits that move. From there: + * - Size is the least significant bit of the bits that move. + * - Limit is all of the bits that move plus all of the lower bits. + * See PCI Spec 6.2.5.1. + */ + limit = 0; + if (moving) { + resource->size = 1; + resource->align = resource->gran = 0; + while (!(moving & resource->size)) { + resource->size <<= 1; + resource->align += 1; + resource->gran += 1; + } + resource->limit = limit = moving | (resource->size - 1); + } + + /* + * Some broken hardware has read-only registers that do not + * really size correctly. + * + * Example: the Acer M7229 has BARs 1-4 normally read-only, + * so BAR1 at offset 0x10 reads 0x1f1. If you size that register + * by writing 0xffffffff to it, it will read back as 0x1f1 -- which + * is a violation of the spec. + * + * We catch this case and ignore it by observing which bits move. + * + * This also catches the common case of unimplemented registers + * that always read back as 0. + */ + if (moving == 0) { + if (value != 0) { + printk(BIOS_DEBUG, "%s register %02lx(%08lx), " + "read-only ignoring it\n", + dev_path(dev), index, value); + } + resource->flags = 0; + } else if (attr & PCI_BASE_ADDRESS_SPACE_IO) { + /* An I/O mapped base address. */ + attr &= PCI_BASE_ADDRESS_IO_ATTR_MASK; + resource->flags |= IORESOURCE_IO; + /* I don't want to deal with 32bit I/O resources. */ + resource->limit = 0xffff; + } else { + /* A Memory mapped base address. */ + attr &= PCI_BASE_ADDRESS_MEM_ATTR_MASK; + resource->flags |= IORESOURCE_MEM; + if (attr & PCI_BASE_ADDRESS_MEM_PREFETCH) + resource->flags |= IORESOURCE_PREFETCH; + attr &= PCI_BASE_ADDRESS_MEM_LIMIT_MASK; + if (attr == PCI_BASE_ADDRESS_MEM_LIMIT_32) { + /* 32bit limit. */ + resource->limit = 0xffffffffUL; + } else if (attr == PCI_BASE_ADDRESS_MEM_LIMIT_1M) { + /* 1MB limit. */ + resource->limit = 0x000fffffUL; + } else if (attr == PCI_BASE_ADDRESS_MEM_LIMIT_64) { + /* 64bit limit. */ + resource->limit = 0xffffffffffffffffULL; + resource->flags |= IORESOURCE_PCI64; + } else { + /* Invalid value. */ + printk(BIOS_ERR, "Broken BAR with value %lx\n", attr); + printk(BIOS_ERR, " on dev %s at index %02lx\n", + dev_path(dev), index); + resource->flags = 0; + } + } + + /* Don't let the limit exceed which bits can move. */ + if (resource->limit > limit) + resource->limit = limit; + + return resource; +} + +/** + * Given a device and an index, read the size of the BAR for that register. + * + * @param dev Pointer to the device structure. + * @param index Address of the PCI configuration register. + */ +static void pci_get_rom_resource(struct device *dev, unsigned long index) +{ + struct resource *resource; + unsigned long value; + resource_t moving; + + /* Initialize the resources to nothing. */ + resource = new_resource(dev, index); + + /* Get the initial value. */ + value = pci_read_config32(dev, index); + + /* See which bits move. */ + moving = pci_moving_config32(dev, index); + + /* Clear the Enable bit. */ + moving = moving & ~PCI_ROM_ADDRESS_ENABLE; + + /* Find the resource constraints. + * Start by finding the bits that move. From there: + * - Size is the least significant bit of the bits that move. + * - Limit is all of the bits that move plus all of the lower bits. + * See PCI Spec 6.2.5.1. + */ + if (moving) { + resource->size = 1; + resource->align = resource->gran = 0; + while (!(moving & resource->size)) { + resource->size <<= 1; + resource->align += 1; + resource->gran += 1; + } + resource->limit = moving | (resource->size - 1); + resource->flags |= IORESOURCE_MEM | IORESOURCE_READONLY; + } else { + if (value != 0) { + printk(BIOS_DEBUG, "%s register %02lx(%08lx), " + "read-only ignoring it\n", + dev_path(dev), index, value); + } + resource->flags = 0; + } + compact_resources(dev); +} + +/** + * Read the base address registers for a given device. + * + * @param dev Pointer to the dev structure. + * @param howmany How many registers to read (6 for device, 2 for bridge). + */ +static void pci_read_bases(struct device *dev, unsigned int howmany) +{ + unsigned long index; + + for (index = PCI_BASE_ADDRESS_0; + (index < PCI_BASE_ADDRESS_0 + (howmany << 2));) { + struct resource *resource; + resource = pci_get_resource(dev, index); + index += (resource->flags & IORESOURCE_PCI64) ? 8 : 4; + } + + compact_resources(dev); +} + +static void pci_record_bridge_resource(struct device *dev, resource_t moving, + unsigned index, unsigned long type) +{ + struct resource *resource; + unsigned long gran; + resource_t step; + + resource = NULL; + + if (!moving) + return; + + /* Initialize the constraints on the current bus. */ + resource = new_resource(dev, index); + resource->size = 0; + gran = 0; + step = 1; + while ((moving & step) == 0) { + gran += 1; + step <<= 1; + } + resource->gran = gran; + resource->align = gran; + resource->limit = moving | (step - 1); + resource->flags = type | IORESOURCE_PCI_BRIDGE | + IORESOURCE_BRIDGE; +} + +static void pci_bridge_read_bases(struct device *dev) +{ + resource_t moving_base, moving_limit, moving; + + /* See if the bridge I/O resources are implemented. */ + moving_base = ((u32) pci_moving_config8(dev, PCI_IO_BASE)) << 8; + moving_base |= + ((u32) pci_moving_config16(dev, PCI_IO_BASE_UPPER16)) << 16; + + moving_limit = ((u32) pci_moving_config8(dev, PCI_IO_LIMIT)) << 8; + moving_limit |= + ((u32) pci_moving_config16(dev, PCI_IO_LIMIT_UPPER16)) << 16; + + moving = moving_base & moving_limit; + + /* Initialize the I/O space constraints on the current bus. */ + pci_record_bridge_resource(dev, moving, PCI_IO_BASE, IORESOURCE_IO); + + /* See if the bridge prefmem resources are implemented. */ + moving_base = + ((resource_t) pci_moving_config16(dev, PCI_PREF_MEMORY_BASE)) << 16; + moving_base |= + ((resource_t) pci_moving_config32(dev, PCI_PREF_BASE_UPPER32)) << 32; + + moving_limit = + ((resource_t) pci_moving_config16(dev, PCI_PREF_MEMORY_LIMIT)) << 16; + moving_limit |= + ((resource_t) pci_moving_config32(dev, PCI_PREF_LIMIT_UPPER32)) << 32; + + moving = moving_base & moving_limit; + /* Initialize the prefetchable memory constraints on the current bus. */ + pci_record_bridge_resource(dev, moving, PCI_PREF_MEMORY_BASE, + IORESOURCE_MEM | IORESOURCE_PREFETCH); + + /* See if the bridge mem resources are implemented. */ + moving_base = ((u32) pci_moving_config16(dev, PCI_MEMORY_BASE)) << 16; + moving_limit = ((u32) pci_moving_config16(dev, PCI_MEMORY_LIMIT)) << 16; + + moving = moving_base & moving_limit; + + /* Initialize the memory resources on the current bus. */ + pci_record_bridge_resource(dev, moving, PCI_MEMORY_BASE, + IORESOURCE_MEM); + + compact_resources(dev); +} + +void pci_dev_read_resources(struct device *dev) +{ + pci_read_bases(dev, 6); + pci_get_rom_resource(dev, PCI_ROM_ADDRESS); +} + +void pci_bus_read_resources(struct device *dev) +{ + pci_bridge_read_bases(dev); + pci_read_bases(dev, 2); + pci_get_rom_resource(dev, PCI_ROM_ADDRESS1); +} + +void pci_domain_read_resources(struct device *dev) +{ + struct resource *res; + + /* Initialize the system-wide I/O space constraints. */ + res = new_resource(dev, IOINDEX_SUBTRACTIVE(0, 0)); + res->limit = 0xffffUL; + res->flags = IORESOURCE_IO | IORESOURCE_SUBTRACTIVE | + IORESOURCE_ASSIGNED; + + /* Initialize the system-wide memory resources constraints. */ + res = new_resource(dev, IOINDEX_SUBTRACTIVE(1, 0)); + res->limit = 0xffffffffULL; + res->flags = IORESOURCE_MEM | IORESOURCE_SUBTRACTIVE | + IORESOURCE_ASSIGNED; +} + +static void pci_set_resource(struct device *dev, struct resource *resource) +{ + resource_t base, end; + + /* Make certain the resource has actually been assigned a value. */ + if (!(resource->flags & IORESOURCE_ASSIGNED)) { + printk(BIOS_ERR, "ERROR: %s %02lx %s size: 0x%010llx not " + "assigned\n", dev_path(dev), resource->index, + resource_type(resource), resource->size); + return; + } + + /* If this resource is fixed don't worry about it. */ + if (resource->flags & IORESOURCE_FIXED) + return; + + /* If I have already stored this resource don't worry about it. */ + if (resource->flags & IORESOURCE_STORED) + return; + + /* If the resource is subtractive don't worry about it. */ + if (resource->flags & IORESOURCE_SUBTRACTIVE) + return; + + /* Only handle PCI memory and I/O resources for now. */ + if (!(resource->flags & (IORESOURCE_MEM | IORESOURCE_IO))) + return; + + /* Enable the resources in the command register. */ + if (resource->size) { + if (resource->flags & IORESOURCE_MEM) + dev->command |= PCI_COMMAND_MEMORY; + if (resource->flags & IORESOURCE_IO) + dev->command |= PCI_COMMAND_IO; + if (resource->flags & IORESOURCE_PCI_BRIDGE) + dev->command |= PCI_COMMAND_MASTER; + } + + /* Get the base address. */ + base = resource->base; + + /* Get the end. */ + end = resource_end(resource); + + /* Now store the resource. */ + resource->flags |= IORESOURCE_STORED; + + /* + * PCI bridges have no enable bit. They are disabled if the base of + * the range is greater than the limit. If the size is zero, disable + * by setting the base = limit and end = limit - 2^gran. + */ + if (resource->size == 0 && (resource->flags & IORESOURCE_PCI_BRIDGE)) { + base = resource->limit; + end = resource->limit - (1 << resource->gran); + resource->base = base; + } + + if (!(resource->flags & IORESOURCE_PCI_BRIDGE)) { + unsigned long base_lo, base_hi; + + /* + * Some chipsets allow us to set/clear the I/O bit + * (e.g. VIA 82C686A). So set it to be safe. + */ + base_lo = base & 0xffffffff; + base_hi = (base >> 32) & 0xffffffff; + if (resource->flags & IORESOURCE_IO) + base_lo |= PCI_BASE_ADDRESS_SPACE_IO; + pci_write_config32(dev, resource->index, base_lo); + if (resource->flags & IORESOURCE_PCI64) + pci_write_config32(dev, resource->index + 4, base_hi); + } else if (resource->index == PCI_IO_BASE) { + /* Set the I/O ranges. */ + pci_write_config8(dev, PCI_IO_BASE, base >> 8); + pci_write_config16(dev, PCI_IO_BASE_UPPER16, base >> 16); + pci_write_config8(dev, PCI_IO_LIMIT, end >> 8); + pci_write_config16(dev, PCI_IO_LIMIT_UPPER16, end >> 16); + } else if (resource->index == PCI_MEMORY_BASE) { + /* Set the memory range. */ + pci_write_config16(dev, PCI_MEMORY_BASE, base >> 16); + pci_write_config16(dev, PCI_MEMORY_LIMIT, end >> 16); + } else if (resource->index == PCI_PREF_MEMORY_BASE) { + /* Set the prefetchable memory range. */ + pci_write_config16(dev, PCI_PREF_MEMORY_BASE, base >> 16); + pci_write_config32(dev, PCI_PREF_BASE_UPPER32, base >> 32); + pci_write_config16(dev, PCI_PREF_MEMORY_LIMIT, end >> 16); + pci_write_config32(dev, PCI_PREF_LIMIT_UPPER32, end >> 32); + } else { + /* Don't let me think I stored the resource. */ + resource->flags &= ~IORESOURCE_STORED; + printk(BIOS_ERR, "ERROR: invalid resource->index %lx\n", + resource->index); + } + + report_resource_stored(dev, resource, ""); +} + +void pci_dev_set_resources(struct device *dev) +{ + struct resource *res; + struct bus *bus; + u8 line; + + for (res = dev->resource_list; res; res = res->next) + pci_set_resource(dev, res); + + for (bus = dev->link_list; bus; bus = bus->next) { + if (bus->children) + assign_resources(bus); + } + + /* Set a default latency timer. */ + pci_write_config8(dev, PCI_LATENCY_TIMER, 0x40); + + /* Set a default secondary latency timer. */ + if ((dev->hdr_type & 0x7f) == PCI_HEADER_TYPE_BRIDGE) + pci_write_config8(dev, PCI_SEC_LATENCY_TIMER, 0x40); + + /* Zero the IRQ settings. */ + line = pci_read_config8(dev, PCI_INTERRUPT_PIN); + if (line) + pci_write_config8(dev, PCI_INTERRUPT_LINE, 0); + + /* Set the cache line size, so far 64 bytes is good for everyone. */ + pci_write_config8(dev, PCI_CACHE_LINE_SIZE, 64 >> 2); +} + +void pci_dev_enable_resources(struct device *dev) +{ + const struct pci_operations *ops; + u16 command; + + /* Set the subsystem vendor and device ID for mainboard devices. */ + ops = ops_pci(dev); + if (dev->on_mainboard && ops && ops->set_subsystem) { + printk(BIOS_DEBUG, "%s subsystem <- %04x/%04x\n", + dev_path(dev), dev->subsystem_vendor, + dev->subsystem_device); + ops->set_subsystem(dev, dev->subsystem_vendor, + dev->subsystem_device); + } + command = pci_read_config16(dev, PCI_COMMAND); + command |= dev->command; + + /* v3 has + * command |= (PCI_COMMAND_PARITY + PCI_COMMAND_SERR); // Error check. + */ + + printk(BIOS_DEBUG, "%s cmd <- %02x\n", dev_path(dev), command); + pci_write_config16(dev, PCI_COMMAND, command); +} + +void pci_bus_enable_resources(struct device *dev) +{ + u16 ctrl; + + /* + * Enable I/O in command register if there is VGA card + * connected with (even it does not claim I/O resource). + */ + if (dev->link_list->bridge_ctrl & PCI_BRIDGE_CTL_VGA) + dev->command |= PCI_COMMAND_IO; + ctrl = pci_read_config16(dev, PCI_BRIDGE_CONTROL); + ctrl |= dev->link_list->bridge_ctrl; + ctrl |= (PCI_BRIDGE_CTL_PARITY + PCI_BRIDGE_CTL_SERR); /* Error check. */ + printk(BIOS_DEBUG, "%s bridge ctrl <- %04x\n", dev_path(dev), ctrl); + pci_write_config16(dev, PCI_BRIDGE_CONTROL, ctrl); + + pci_dev_enable_resources(dev); +} + +void pci_bus_reset(struct bus *bus) +{ + u16 ctl; + + ctl = pci_read_config16(bus->dev, PCI_BRIDGE_CONTROL); + ctl |= PCI_BRIDGE_CTL_BUS_RESET; + pci_write_config16(bus->dev, PCI_BRIDGE_CONTROL, ctl); + mdelay(10); + + ctl &= ~PCI_BRIDGE_CTL_BUS_RESET; + pci_write_config16(bus->dev, PCI_BRIDGE_CONTROL, ctl); + delay(1); +} + +void pci_dev_set_subsystem(struct device *dev, unsigned vendor, unsigned device) +{ + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + ((device & 0xffff) << 16) | (vendor & 0xffff)); +} + +#if CONFIG_CHROMEOS +int oprom_is_loaded = 0; +#endif + +/** Default handler: only runs the relevant PCI BIOS. */ +void pci_dev_init(struct device *dev) +{ +#if CONFIG_PCI_ROM_RUN || CONFIG_VGA_ROM_RUN + struct rom_header *rom, *ram; + + if (CONFIG_PCI_ROM_RUN != 1 && /* Only execute VGA ROMs. */ + ((dev->class >> 8) != PCI_CLASS_DISPLAY_VGA)) + return; + + if (CONFIG_VGA_ROM_RUN != 1 && /* Only execute non-VGA ROMs. */ + ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA)) + return; + +#if CONFIG_CHROMEOS + /* In ChromeOS we want to boot blazingly fast. Therefore + * we don't run (VGA) option ROMs, unless we have to print + * something on the screen before the kernel is loaded. + */ + if (!developer_mode_enabled() && !recovery_mode_enabled() && + !vboot_wants_oprom()) { + printk(BIOS_DEBUG, "Not loading VGA Option ROM\n"); + return; + } +#endif + + rom = pci_rom_probe(dev); + if (rom == NULL) + return; + + ram = pci_rom_load(dev, rom); + if (ram == NULL) + return; + +#if CONFIG_HAVE_ACPI_RESUME && !CONFIG_S3_VGA_ROM_RUN + /* If S3_VGA_ROM_RUN is disabled, skip running VGA option + * ROMs when coming out of an S3 resume. + */ + if ((acpi_slp_type == 3) && + ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA)) + return; +#endif + run_bios(dev, (unsigned long)ram); +#if CONFIG_CHROMEOS + oprom_is_loaded = 1; + printk(BIOS_DEBUG, "VGA Option ROM has been loaded\n"); +#endif +#endif /* CONFIG_PCI_ROM_RUN || CONFIG_VGA_ROM_RUN */ +} + +/** Default device operation for PCI devices */ +static struct pci_operations pci_dev_ops_pci = { + .set_subsystem = pci_dev_set_subsystem, +}; + +struct device_operations default_pci_ops_dev = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = pci_dev_init, + .scan_bus = 0, + .enable = 0, + .ops_pci = &pci_dev_ops_pci, +}; + +/** Default device operations for PCI bridges */ +static struct pci_operations pci_bus_ops_pci = { + .set_subsystem = 0, +}; + +struct device_operations default_pci_ops_bus = { + .read_resources = pci_bus_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_bus_enable_resources, + .init = 0, + .scan_bus = pci_scan_bridge, + .enable = 0, + .reset_bus = pci_bus_reset, + .ops_pci = &pci_bus_ops_pci, +}; + +/** + * Detect the type of downstream bridge. + * + * This function is a heuristic to detect which type of bus is downstream + * of a PCI-to-PCI bridge. This functions by looking for various capability + * blocks to figure out the type of downstream bridge. PCI-X, PCI-E, and + * Hypertransport all seem to have appropriate capabilities. + * + * When only a PCI-Express capability is found the type is examined to see + * which type of bridge we have. + * + * @param dev Pointer to the device structure of the bridge. + * @return Appropriate bridge operations. + */ +static struct device_operations *get_pci_bridge_ops(device_t dev) +{ +#if CONFIG_PCIX_PLUGIN_SUPPORT + unsigned int pcixpos; + pcixpos = pci_find_capability(dev, PCI_CAP_ID_PCIX); + if (pcixpos) { + printk(BIOS_DEBUG, "%s subordinate bus PCI-X\n", dev_path(dev)); + return &default_pcix_ops_bus; + } +#endif +#if CONFIG_AGP_PLUGIN_SUPPORT + /* How do I detect a PCI to AGP bridge? */ +#endif +#if CONFIG_HYPERTRANSPORT_PLUGIN_SUPPORT + unsigned int htpos = 0; + while ((htpos = pci_find_next_capability(dev, PCI_CAP_ID_HT, htpos))) { + u16 flags; + flags = pci_read_config16(dev, htpos + PCI_CAP_FLAGS); + if ((flags >> 13) == 1) { + /* Host or Secondary Interface */ + printk(BIOS_DEBUG, "%s subordinate bus HT\n", + dev_path(dev)); + return &default_ht_ops_bus; + } + } +#endif +#if CONFIG_PCIEXP_PLUGIN_SUPPORT + unsigned int pciexpos; + pciexpos = pci_find_capability(dev, PCI_CAP_ID_PCIE); + if (pciexpos) { + u16 flags; + flags = pci_read_config16(dev, pciexpos + PCI_EXP_FLAGS); + switch ((flags & PCI_EXP_FLAGS_TYPE) >> 4) { + case PCI_EXP_TYPE_ROOT_PORT: + case PCI_EXP_TYPE_UPSTREAM: + case PCI_EXP_TYPE_DOWNSTREAM: + printk(BIOS_DEBUG, "%s subordinate bus PCI Express\n", + dev_path(dev)); + return &default_pciexp_ops_bus; + case PCI_EXP_TYPE_PCI_BRIDGE: + printk(BIOS_DEBUG, "%s subordinate PCI\n", + dev_path(dev)); + return &default_pci_ops_bus; + default: + break; + } + } +#endif + return &default_pci_ops_bus; +} + +/** + * Check if a device id matches a PCI driver entry. + * + * The driver entry can either point at a zero terminated array of acceptable + * device IDs, or include a single device ID. + * + * @driver pointer to the PCI driver entry being checked + * @device_id PCI device ID of the device being matched + */ +static int device_id_match(struct pci_driver *driver, unsigned short device_id) +{ + if (driver->devices) { + unsigned short check_id; + const unsigned short *device_list = driver->devices; + while ((check_id = *device_list++) != 0) + if (check_id == device_id) + return 1; + } + + return (driver->device == device_id); +} + +/** + * Set up PCI device operation. + * + * Check if it already has a driver. If not, use find_device_operations(), + * or set to a default based on type. + * + * @param dev Pointer to the device whose pci_ops you want to set. + * @see pci_drivers + */ +static void set_pci_ops(struct device *dev) +{ + struct pci_driver *driver; + + if (dev->ops) + return; + + /* + * Look through the list of setup drivers and find one for + * this PCI device. + */ + for (driver = &pci_drivers[0]; driver != &epci_drivers[0]; driver++) { + if ((driver->vendor == dev->vendor) && + device_id_match(driver, dev->device)) { + dev->ops = (struct device_operations *)driver->ops; + printk(BIOS_SPEW, "%s [%04x/%04x] %sops\n", + dev_path(dev), driver->vendor, driver->device, + (driver->ops->scan_bus ? "bus " : "")); + return; + } + } + + /* If I don't have a specific driver use the default operations. */ + switch (dev->hdr_type & 0x7f) { /* Header type */ + case PCI_HEADER_TYPE_NORMAL: + if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) + goto bad; + dev->ops = &default_pci_ops_dev; + break; + case PCI_HEADER_TYPE_BRIDGE: + if ((dev->class >> 8) != PCI_CLASS_BRIDGE_PCI) + goto bad; + dev->ops = get_pci_bridge_ops(dev); + break; +#if CONFIG_CARDBUS_PLUGIN_SUPPORT + case PCI_HEADER_TYPE_CARDBUS: + dev->ops = &default_cardbus_ops_bus; + break; +#endif +default: +bad: + if (dev->enabled) { + printk(BIOS_ERR, "%s [%04x/%04x/%06x] has unknown " + "header type %02x, ignoring.\n", dev_path(dev), + dev->vendor, dev->device, + dev->class >> 8, dev->hdr_type); + } + } +} + +/** + * See if we have already allocated a device structure for a given devfn. + * + * Given a linked list of PCI device structures and a devfn number, find the + * device structure correspond to the devfn, if present. This function also + * removes the device structure from the linked list. + * + * @param list The device structure list. + * @param devfn A device/function number. + * @return Pointer to the device structure found or NULL if we have not + * allocated a device for this devfn yet. + */ +static struct device *pci_scan_get_dev(struct device **list, unsigned int devfn) +{ + struct device *dev; + + dev = 0; + for (; *list; list = &(*list)->sibling) { + if ((*list)->path.type != DEVICE_PATH_PCI) { + printk(BIOS_ERR, "child %s not a PCI device\n", + dev_path(*list)); + continue; + } + if ((*list)->path.pci.devfn == devfn) { + /* Unlink from the list. */ + dev = *list; + *list = (*list)->sibling; + dev->sibling = NULL; + break; + } + } + + /* + * Just like alloc_dev() add the device to the list of devices on the + * bus. When the list of devices was formed we removed all of the + * parents children, and now we are interleaving static and dynamic + * devices in order on the bus. + */ + if (dev) { + struct device *child; + + /* Find the last child of our parent. */ + for (child = dev->bus->children; child && child->sibling;) + child = child->sibling; + + /* Place the device on the list of children of its parent. */ + if (child) + child->sibling = dev; + else + dev->bus->children = dev; + } + + return dev; +} + +/** + * Scan a PCI bus. + * + * Determine the existence of a given PCI device. Allocate a new struct device + * if dev==NULL was passed in and the device exists in hardware. + * + * @param dev Pointer to the dev structure. + * @param bus Pointer to the bus structure. + * @param devfn A device/function number to look at. + * @return The device structure for the device (if found), NULL otherwise. + */ +device_t pci_probe_dev(device_t dev, struct bus *bus, unsigned devfn) +{ + u32 id, class; + u8 hdr_type; + + /* Detect if a device is present. */ + if (!dev) { + struct device dummy; + + dummy.bus = bus; + dummy.path.type = DEVICE_PATH_PCI; + dummy.path.pci.devfn = devfn; + + id = pci_read_config32(&dummy, PCI_VENDOR_ID); + /* + * Have we found something? Some broken boards return 0 if a + * slot is empty, but the expected answer is 0xffffffff. + */ + if (id == 0xffffffff) + return NULL; + + if ((id == 0x00000000) || (id == 0x0000ffff) || + (id == 0xffff0000)) { + printk(BIOS_SPEW, "%s, bad id 0x%x\n", + dev_path(&dummy), id); + return NULL; + } + dev = alloc_dev(bus, &dummy.path); + } else { + /* + * Enable/disable the device. Once we have found the device- + * specific operations this operations we will disable the + * device with those as well. + * + * This is geared toward devices that have subfunctions + * that do not show up by default. + * + * If a device is a stuff option on the motherboard + * it may be absent and enable_dev() must cope. + */ + /* Run the magic enable sequence for the device. */ + if (dev->chip_ops && dev->chip_ops->enable_dev) + dev->chip_ops->enable_dev(dev); + + /* Now read the vendor and device ID. */ + id = pci_read_config32(dev, PCI_VENDOR_ID); + + /* + * If the device does not have a PCI ID disable it. Possibly + * this is because we have already disabled the device. But + * this also handles optional devices that may not always + * show up. + */ + /* If the chain is fully enumerated quit */ + if ((id == 0xffffffff) || (id == 0x00000000) || + (id == 0x0000ffff) || (id == 0xffff0000)) { + if (dev->enabled) { + printk(BIOS_INFO, "PCI: Static device %s not " + "found, disabling it.\n", dev_path(dev)); + dev->enabled = 0; + } + return dev; + } + } + + /* Read the rest of the PCI configuration information. */ + hdr_type = pci_read_config8(dev, PCI_HEADER_TYPE); + class = pci_read_config32(dev, PCI_CLASS_REVISION); + + /* Store the interesting information in the device structure. */ + dev->vendor = id & 0xffff; + dev->device = (id >> 16) & 0xffff; + dev->hdr_type = hdr_type; + + /* Class code, the upper 3 bytes of PCI_CLASS_REVISION. */ + dev->class = class >> 8; + + /* Architectural/System devices always need to be bus masters. */ + if ((dev->class >> 16) == PCI_BASE_CLASS_SYSTEM) + dev->command |= PCI_COMMAND_MASTER; + + /* + * Look at the vendor and device ID, or at least the header type and + * class and figure out which set of configuration methods to use. + * Unless we already have some PCI ops. + */ + set_pci_ops(dev); + + /* Now run the magic enable/disable sequence for the device. */ + if (dev->ops && dev->ops->enable) + dev->ops->enable(dev); + + /* Display the device. */ + printk(BIOS_DEBUG, "%s [%04x/%04x] %s%s\n", dev_path(dev), + dev->vendor, dev->device, dev->enabled ? "enabled" : "disabled", + dev->ops ? "" : " No operations"); + + return dev; +} + +/** + * Scan a PCI bus. + * + * Determine the existence of devices and bridges on a PCI bus. If there are + * bridges on the bus, recursively scan the buses behind the bridges. + * + * This function is the default scan_bus() method for the root device + * 'dev_root'. + * + * @param bus Pointer to the bus structure. + * @param min_devfn Minimum devfn to look at in the scan, usually 0x00. + * @param max_devfn Maximum devfn to look at in the scan, usually 0xff. + * @param max Current bus number. + * @return The maximum bus number found, after scanning all subordinate busses. + */ +unsigned int pci_scan_bus(struct bus *bus, unsigned min_devfn, + unsigned max_devfn, unsigned int max) +{ + unsigned int devfn; + struct device *old_devices; + struct device *child; + +#if CONFIG_PCI_BUS_SEGN_BITS + printk(BIOS_DEBUG, "PCI: pci_scan_bus for bus %04x:%02x\n", + bus->secondary >> 8, bus->secondary & 0xff); +#else + printk(BIOS_DEBUG, "PCI: pci_scan_bus for bus %02x\n", bus->secondary); +#endif + + /* Maximum sane devfn is 0xFF. */ + if (max_devfn > 0xff) { + printk(BIOS_ERR, "PCI: pci_scan_bus limits devfn %x - " + "devfn %x\n", min_devfn, max_devfn); + printk(BIOS_ERR, "PCI: pci_scan_bus upper limit too big. " + "Using 0xff.\n"); + max_devfn=0xff; + } + + old_devices = bus->children; + bus->children = NULL; + + post_code(0x24); + + /* + * Probe all devices/functions on this bus with some optimization for + * non-existence and single function devices. + */ + for (devfn = min_devfn; devfn <= max_devfn; devfn++) { + struct device *dev; + + /* First thing setup the device structure. */ + dev = pci_scan_get_dev(&old_devices, devfn); + + /* See if a device is present and setup the device structure. */ + dev = pci_probe_dev(dev, bus, devfn); + + /* + * If this is not a multi function device, or the device is + * not present don't waste time probing another function. + * Skip to next device. + */ + if ((PCI_FUNC(devfn) == 0x00) && (!dev + || (dev->enabled && ((dev->hdr_type & 0x80) != 0x80)))) { + devfn += 0x07; + } + } + + post_code(0x25); + + /* + * Warn if any leftover static devices are are found. + * There's probably a problem in devicetree.cb. + */ + if (old_devices) { + device_t left; + printk(BIOS_WARNING, "PCI: Left over static devices:\n"); + for (left = old_devices; left; left = left->sibling) + printk(BIOS_WARNING, "%s\n", dev_path(left)); + + printk(BIOS_WARNING, "PCI: Check your devicetree.cb.\n"); + } + + /* + * For all children that implement scan_bus() (i.e. bridges) + * scan the bus behind that child. + */ + for (child = bus->children; child; child = child->sibling) + max = scan_bus(child, max); + + /* + * We've scanned the bus and so we know all about what's on the other + * side of any bridges that may be on this bus plus any devices. + * Return how far we've got finding sub-buses. + */ + printk(BIOS_DEBUG, "PCI: pci_scan_bus returning with max=%03x\n", max); + post_code(0x55); + return max; +} + +/** + * Scan a PCI bridge and the buses behind the bridge. + * + * Determine the existence of buses behind the bridge. Set up the bridge + * according to the result of the scan. + * + * This function is the default scan_bus() method for PCI bridge devices. + * + * @param dev Pointer to the bridge device. + * @param max The highest bus number assigned up to now. + * @param do_scan_bus TODO + * @return The maximum bus number found, after scanning all subordinate buses. + */ +unsigned int do_pci_scan_bridge(struct device *dev, unsigned int max, + unsigned int (*do_scan_bus) (struct bus * bus, + unsigned min_devfn, + unsigned max_devfn, + unsigned int max)) +{ + struct bus *bus; + u32 buses; + u16 cr; + + printk(BIOS_SPEW, "%s for %s\n", __func__, dev_path(dev)); + + if (dev->link_list == NULL) { + struct bus *link; + link = malloc(sizeof(*link)); + if (link == NULL) + die("Couldn't allocate a link!\n"); + memset(link, 0, sizeof(*link)); + link->dev = dev; + dev->link_list = link; + } + + bus = dev->link_list; + + /* + * Set up the primary, secondary and subordinate bus numbers. We have + * no idea how many buses are behind this bridge yet, so we set the + * subordinate bus number to 0xff for the moment. + */ + bus->secondary = ++max; + bus->subordinate = 0xff; + + /* Clear all status bits and turn off memory, I/O and master enables. */ + cr = pci_read_config16(dev, PCI_COMMAND); + pci_write_config16(dev, PCI_COMMAND, 0x0000); + pci_write_config16(dev, PCI_STATUS, 0xffff); + + /* + * Read the existing primary/secondary/subordinate bus + * number configuration. + */ + buses = pci_read_config32(dev, PCI_PRIMARY_BUS); + + /* + * Configure the bus numbers for this bridge: the configuration + * transactions will not be propagated by the bridge if it is not + * correctly configured. + */ + buses &= 0xff000000; + buses |= (((unsigned int)(dev->bus->secondary) << 0) | + ((unsigned int)(bus->secondary) << 8) | + ((unsigned int)(bus->subordinate) << 16)); + pci_write_config32(dev, PCI_PRIMARY_BUS, buses); + + /* Now we can scan all subordinate buses (those behind the bridge). */ + max = do_scan_bus(bus, 0x00, 0xff, max); + + /* + * We know the number of buses behind this bridge. Set the subordinate + * bus number to its real value. + */ + bus->subordinate = max; + buses = (buses & 0xff00ffff) | ((unsigned int)(bus->subordinate) << 16); + pci_write_config32(dev, PCI_PRIMARY_BUS, buses); + pci_write_config16(dev, PCI_COMMAND, cr); + + printk(BIOS_SPEW, "%s returns max %d\n", __func__, max); + return max; +} + +/** + * Scan a PCI bridge and the buses behind the bridge. + * + * Determine the existence of buses behind the bridge. Set up the bridge + * according to the result of the scan. + * + * This function is the default scan_bus() method for PCI bridge devices. + * + * @param dev Pointer to the bridge device. + * @param max The highest bus number assigned up to now. + * @return The maximum bus number found, after scanning all subordinate buses. + */ +unsigned int pci_scan_bridge(struct device *dev, unsigned int max) +{ + return do_pci_scan_bridge(dev, max, pci_scan_bus); +} + +/** + * Scan a PCI domain. + * + * This function is the default scan_bus() method for PCI domains. + * + * @param dev Pointer to the domain. + * @param max The highest bus number assigned up to now. + * @return The maximum bus number found, after scanning all subordinate busses. + */ +unsigned int pci_domain_scan_bus(device_t dev, unsigned int max) +{ + max = pci_scan_bus(dev->link_list, PCI_DEVFN(0, 0), 0xff, max); + return max; +} + +#if CONFIG_PC80_SYSTEM +/** + * Assign IRQ numbers. + * + * This function assigns IRQs for all functions contained within the indicated + * device address. If the device does not exist or does not require interrupts + * then this function has no effect. + * + * This function should be called for each PCI slot in your system. + * + * @param bus Pointer to the bus structure. + * @param slot TODO + * @param pIntAtoD An array of IRQ #s that are assigned to PINTA through PINTD + * of this slot. The particular IRQ #s that are passed in depend on the + * routing inside your southbridge and on your board. + */ +void pci_assign_irqs(unsigned bus, unsigned slot, + const unsigned char pIntAtoD[4]) +{ + unsigned int funct; + device_t pdev; + u8 line, irq; + + /* Each slot may contain up to eight functions. */ + for (funct = 0; funct < 8; funct++) { + pdev = dev_find_slot(bus, (slot << 3) + funct); + + if (!pdev) + continue; + + line = pci_read_config8(pdev, PCI_INTERRUPT_PIN); + + /* PCI spec says all values except 1..4 are reserved. */ + if ((line < 1) || (line > 4)) + continue; + + irq = pIntAtoD[line - 1]; + + printk(BIOS_DEBUG, "Assigning IRQ %d to %d:%x.%d\n", + irq, bus, slot, funct); + + pci_write_config8(pdev, PCI_INTERRUPT_LINE, + pIntAtoD[line - 1]); + +#ifdef PARANOID_IRQ_ASSIGNMENTS + irq = pci_read_config8(pdev, PCI_INTERRUPT_LINE); + printk(BIOS_DEBUG, " Readback = %d\n", irq); +#endif + +#if CONFIG_PC80_SYSTEM + /* Change to level triggered. */ + i8259_configure_irq_trigger(pIntAtoD[line - 1], + IRQ_LEVEL_TRIGGERED); +#endif + } +} +#endif |