X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=src%2Fdevices%2Fdevice.c;h=0e9c39e2031d7fe77c5fc4fa8b42d23a6e5a124f;hb=b4aaaa7632116c2fe466095d0cd3cc1247344ca6;hp=60e79c5a580dc54d7ffbfc5bb96fc455642e2bfe;hpb=69c5a905ede8053fd2993ed2e4ff14e9970f2dce;p=coreboot.git diff --git a/src/devices/device.c b/src/devices/device.c index 60e79c5a5..0e9c39e20 100644 --- a/src/devices/device.c +++ b/src/devices/device.c @@ -1,17 +1,35 @@ +/* + * This file is part of the coreboot project. + * + * It was originally based on the Linux kernel (arch/i386/kernel/pci-pc.c). + * + * Modifications are: + * Copyright (C) 2003 Eric Biederman + * Copyright (C) 2003-2004 Linux Networx + * (Written by Eric Biederman for Linux Networx) + * Copyright (C) 2003 Ronald G. Minnich + * Copyright (C) 2004-2005 Li-Ta Lo + * Copyright (C) 2005-2006 Tyan + * (Written by Yinghai Lu for Tyan) + * Copyright (C) 2005-2006 Stefan Reinauer + * Copyright (C) 2009 Myles Watson + */ + /* * (c) 1999--2000 Martin Mares - * (c) 2003 Eric Biederman - * (c) 2003 Linux Networx */ -/* lots of mods by ron minnich (rminnich@lanl.gov), with - * the final architecture guidance from Tom Merritt (tjm@codegen.com) - * In particular, we changed from the one-pass original version to - * Tom's recommended multiple-pass version. I wasn't sure about doing + +/* + * Lots of mods by Ron Minnich , with + * the final architecture guidance from Tom Merritt . + * + * In particular, we changed from the one-pass original version to + * Tom's recommended multiple-pass version. I wasn't sure about doing * it with multiple passes, until I actually started doing it and saw - * the wisdom of Tom's recommendations ... + * the wisdom of Tom's recommendations... * * Lots of cleanups by Eric Biederman to handle bridges, and to - * handle resource allocation for non-pci devices. + * handle resource allocation for non-PCI devices. */ #include @@ -19,168 +37,149 @@ #include #include #include +#include #include #include +#include +#if CONFIG_ARCH_X86 +#include +#endif /** Linked list of ALL devices */ struct device *all_devices = &dev_root; /** Pointer to the last device */ -static struct device **last_dev_p = &dev_root.next; +extern struct device *last_dev; +/** Linked list of free resources */ +struct resource *free_resources = NULL; -/** The upper limit of MEM resource of the devices. - * Reserve 20M for the system */ -#define DEVICE_MEM_HIGH 0xFEC00000UL -/** The lower limit of IO resource of the devices. - * Reserve 4k for ISA/Legacy devices */ -#define DEVICE_IO_START 0x1000 +DECLARE_SPIN_LOCK(dev_lock) /** - * @brief Allocate a new device structure. - * - * Allocte a new device structure and attached it to the device tree as a child - * of the parent bus. + * Allocate a new device structure. * - * @param parent parent bus the newly created device attached to. - * @param path path to the device to be created. + * Allocte a new device structure and attach it to the device tree as a + * child of the parent bus. * - * @return pointer to the newly created device structure. + * @param parent Parent bus the newly created device should be attached to. + * @param path Path to the device to be created. + * @return Pointer to the newly created device structure. * * @see device_path */ device_t alloc_dev(struct bus *parent, struct device_path *path) { device_t dev, child; - int link; - /* Find the last child of our parent */ - for (child = parent->children; child && child->sibling; ) { + spin_lock(&dev_lock); + + /* Find the last child of our parent. */ + for (child = parent->children; child && child->sibling; /* */ ) child = child->sibling; - } dev = malloc(sizeof(*dev)); - if (dev == 0) { - die("DEV: out of memory.\n"); - } + if (dev == 0) + die("alloc_dev(): out of memory.\n"); + memset(dev, 0, sizeof(*dev)); memcpy(&dev->path, path, sizeof(*path)); - /* Append a new device to the global device list. - * The list is used to find devices once everything is set up. - */ - *last_dev_p = dev; - last_dev_p = &dev->next; - - /* Initialize the back pointers in the link fields */ - for (link = 0; link < MAX_LINKS; link++) { - dev->link[link].dev = dev; - dev->link[link].link = link; - } + /* By default devices are enabled. */ + dev->enabled = 1; - /* Add the new device to the children of the bus. */ + /* Add the new device to the list of children of the bus. */ dev->bus = parent; - if (child) { + if (child) child->sibling = dev; - } else { + else parent->children = dev; - } - /* If we don't have any other information about a device enable it */ - dev->enabled = 1; + /* Append a new device to the global device list. + * The list is used to find devices once everything is set up. + */ + last_dev->next = dev; + last_dev = dev; + spin_unlock(&dev_lock); return dev; } /** - * @brief round a number up to an alignment. - * @param val the starting value - * @param roundup Alignment as a power of two - * @returns rounded up number + * Round a number up to an alignment. + * + * @param val The starting value. + * @param roundup Alignment as a power of two. + * @return Rounded up number. */ -static unsigned long round(unsigned long val, unsigned long roundup) +static resource_t round(resource_t val, unsigned long pow) { - /* ROUNDUP MUST BE A POWER OF TWO. */ - unsigned long inverse; - inverse = ~(roundup - 1); - val += (roundup - 1); - val &= inverse; + resource_t mask; + mask = (1ULL << pow) - 1ULL; + val += mask; + val &= ~mask; return val; } -static unsigned long round_down(unsigned long val, unsigned long round_down) -{ - /* ROUND_DOWN MUST BE A POWER OF TWO. */ - unsigned long inverse; - inverse = ~(round_down - 1); - val &= inverse; - return val; -} - - -/** Read the resources on all devices of a given bus. - * @param bus bus to read the resources on. +/** + * Read the resources on all devices of a given bus. + * + * @param bus Bus to read the resources on. */ static void read_resources(struct bus *bus) { struct device *curdev; - /* Walk through all of the devices and find which resources they need. */ + printk(BIOS_SPEW, "%s %s bus %x link: %d\n", dev_path(bus->dev), + __func__, bus->secondary, bus->link_num); + + /* Walk through all devices and find which resources they need. */ for (curdev = bus->children; curdev; curdev = curdev->sibling) { - unsigned links; - int i; - if (curdev->resources > 0) { + struct bus *link; + + if (!curdev->enabled) continue; - } + if (!curdev->ops || !curdev->ops->read_resources) { - printk_err("%s missing read_resources\n", - dev_path(curdev)); + printk(BIOS_ERR, "%s missing read_resources\n", + dev_path(curdev)); continue; } - if (!curdev->enabled) { - continue; - } - curdev->ops->read_resources(curdev); - /* Read in subtractive resources behind the current device */ - links = 0; - for (i = 0; i < curdev->resources; i++) { - struct resource *resource; - resource = &curdev->resource[i]; - if ((resource->flags & IORESOURCE_SUBTRACTIVE) && - (!(links & (1 << resource->index)))) - { - links |= (1 << resource->index); - read_resources(&curdev->link[resource->index]); - - } - } + /* Read in the resources behind the current device's links. */ + for (link = curdev->link_list; link; link = link->next) + read_resources(link); } + printk(BIOS_SPEW, "%s read_resources bus %d link: %d done\n", + dev_path(bus->dev), bus->secondary, bus->link_num); } struct pick_largest_state { struct resource *last; - struct device *result_dev; + struct device *result_dev; struct resource *result; int seen_last; }; -static void pick_largest_resource(struct pick_largest_state *state, - struct device *dev, struct resource *resource) +static void pick_largest_resource(void *gp, struct device *dev, + struct resource *resource) { + struct pick_largest_state *state = gp; struct resource *last; + last = state->last; - /* Be certain to pick the successor to last */ + + /* Be certain to pick the successor to last. */ if (resource == last) { state->seen_last = 1; return; } - if (last && - ((last->align < resource->align) || - ((last->align == resource->align) && - (last->size < resource->size)) || - ((last->align == resource->align) && - (last->size == resource->size) && - (!state->seen_last)))) { + if (resource->flags & IORESOURCE_FIXED) + return; /* Skip it. */ + if (last && ((last->align < resource->align) || + ((last->align == resource->align) && + (last->size < resource->size)) || + ((last->align == resource->align) && + (last->size == resource->size) && (!state->seen_last)))) { return; } if (!state->result || @@ -189,34 +188,6 @@ static void pick_largest_resource(struct pick_largest_state *state, (state->result->size < resource->size))) { state->result_dev = dev; state->result = resource; - } -} - -static void find_largest_resource(struct pick_largest_state *state, - struct bus *bus, unsigned long type_mask, - unsigned long type) -{ - struct device *curdev; - - for (curdev = bus->children; curdev; curdev = curdev->sibling) { - int i; - for (i = 0; i < curdev->resources; i++) { - struct resource *resource = &curdev->resource[i]; - /* If it isn't the right kind of resource ignore it */ - if ((resource->flags & type_mask) != type) { - continue; - } - /* If it is a subtractive resource recurse */ - if (resource->flags & IORESOURCE_SUBTRACTIVE) { - struct bus *subbus; - subbus = &curdev->link[resource->index]; - find_largest_resource(state, subbus, - type_mask, type); - continue; - } - /* See if this is the largest resource */ - pick_largest_resource(state, curdev, resource); - } } } @@ -228,348 +199,924 @@ static struct device *largest_resource(struct bus *bus, struct pick_largest_state state; state.last = *result_res; - state.result_dev = 0; - state.result = 0; + state.result_dev = NULL; + state.result = NULL; state.seen_last = 0; - find_largest_resource(&state, bus, type_mask, type); + search_bus_resources(bus, type_mask, type, pick_largest_resource, + &state); *result_res = state.result; return state.result_dev; } -/* Compute allocate resources is the guts of the resource allocator. - * +/** + * This function is the guts of the resource allocator. + * * The problem. - * - Allocate resources locations for every device. + * - Allocate resource locations for every device. * - Don't overlap, and follow the rules of bridges. * - Don't overlap with resources in fixed locations. * - Be efficient so we don't have ugly strategies. * * The strategy. * - Devices that have fixed addresses are the minority so don't - * worry about them too much. Instead only use part of the address - * space for devices with programmable addresses. This easily handles + * worry about them too much. Instead only use part of the address + * space for devices with programmable addresses. This easily handles * everything except bridges. * - * - PCI devices are required to have thier sizes and their alignments - * equal. In this case an optimal solution to the packing problem - * exists. Allocate all devices from highest alignment to least - * alignment or vice versa. Use this. + * - PCI devices are required to have their sizes and their alignments + * equal. In this case an optimal solution to the packing problem + * exists. Allocate all devices from highest alignment to least + * alignment or vice versa. Use this. * - * - So we can handle more than PCI run two allocation passes on - * bridges. The first to see how large the resources are behind - * the bridge, and what their alignment requirements are. The - * second to assign a safe address to the devices behind the - * bridge. This allows me to treat a bridge as just a device with - * a couple of resources, and not need to special case it in the - * allocator. Also this allows handling of other types of bridges. + * - So we can handle more than PCI run two allocation passes on bridges. The + * first to see how large the resources are behind the bridge, and what + * their alignment requirements are. The second to assign a safe address to + * the devices behind the bridge. This allows us to treat a bridge as just + * a device with a couple of resources, and not need to special case it in + * the allocator. Also this allows handling of other types of bridges. * + * @param bus The bus we are traversing. + * @param bridge The bridge resource which must contain the bus' resources. + * @param type_mask This value gets ANDed with the resource type. + * @param type This value must match the result of the AND. + * @return TODO */ - -void compute_allocate_resource( - struct bus *bus, - struct resource *bridge, - unsigned long type_mask, - unsigned long type) +static void compute_resources(struct bus *bus, struct resource *bridge, + unsigned long type_mask, unsigned long type) { struct device *dev; struct resource *resource; - unsigned long base; - unsigned long align, min_align; - min_align = 0; - base = bridge->base; + resource_t base; + base = round(bridge->base, bridge->align); - printk_spew("%s compute_allocate_%s: base: %08lx size: %08lx " - "align: %d gran: %d\n", - dev_path(bus->dev), - (bridge->flags & IORESOURCE_IO)? "io": - (bridge->flags & IORESOURCE_PREFETCH)? "prefmem" : "mem", - base, bridge->size, bridge->align, bridge->gran); + printk(BIOS_SPEW, "%s %s_%s: base: %llx size: %llx align: %d gran: %d" + " limit: %llx\n", dev_path(bus->dev), __func__, + (type & IORESOURCE_IO) ? "io" : (type & IORESOURCE_PREFETCH) ? + "prefmem" : "mem", base, bridge->size, bridge->align, + bridge->gran, bridge->limit); - /* We want different minimum alignments for different kinds of - * resources. These minimums are not device type specific - * but resource type specific. - */ - if (bridge->flags & IORESOURCE_IO) { - min_align = log2(DEVICE_IO_ALIGN); - } - if (bridge->flags & IORESOURCE_MEM) { - min_align = log2(DEVICE_MEM_ALIGN); - } + /* For each child which is a bridge, compute the resource needs. */ + for (dev = bus->children; dev; dev = dev->sibling) { + struct resource *child_bridge; - /* Make certain I have read in all of the resources */ - read_resources(bus); + if (!dev->link_list) + continue; - /* Remember I haven't found anything yet. */ - resource = 0; + /* Find the resources with matching type flags. */ + for (child_bridge = dev->resource_list; child_bridge; + child_bridge = child_bridge->next) { + struct bus* link; + + if (!(child_bridge->flags & IORESOURCE_BRIDGE) + || (child_bridge->flags & type_mask) != type) + continue; + + /* + * Split prefetchable memory if combined. Many domains + * use the same address space for prefetchable memory + * and non-prefetchable memory. Bridges below them need + * it separated. Add the PREFETCH flag to the type_mask + * and type. + */ + link = dev->link_list; + while (link && link->link_num != + IOINDEX_LINK(child_bridge->index)) + link = link->next; + + if (link == NULL) { + printk(BIOS_ERR, "link %ld not found on %s\n", + IOINDEX_LINK(child_bridge->index), + dev_path(dev)); + } + + compute_resources(link, child_bridge, + type_mask | IORESOURCE_PREFETCH, + type | (child_bridge->flags & + IORESOURCE_PREFETCH)); + } + } - /* Walk through all the devices on the current bus and compute the - * addresses */ + /* Remember we haven't found anything yet. */ + resource = NULL; + + /* + * Walk through all the resources on the current bus and compute the + * amount of address space taken by them. Take granularity and + * alignment into account. + */ while ((dev = largest_resource(bus, &resource, type_mask, type))) { - unsigned long size; - /* Do NOT I repeat do not ignore resources which have zero size. - * If they need to be ignored dev->read_resources should not even - * return them. Some resources must be set even when they have - * no size. PCI bridge resources are a good example of this. - */ - /* Propogate the resource alignment to the bridge register */ - if (resource->align > bridge->align) { + /* Size 0 resources can be skipped. */ + if (!resource->size) + continue; + + /* Propagate the resource alignment to the bridge resource. */ + if (resource->align > bridge->align) bridge->align = resource->align; + + /* Propagate the resource limit to the bridge register. */ + if (bridge->limit > resource->limit) + bridge->limit = resource->limit; + + /* Warn if it looks like APICs aren't declared. */ + if ((resource->limit == 0xffffffff) && + (resource->flags & IORESOURCE_ASSIGNED)) { + printk(BIOS_ERR, + "Resource limit looks wrong! (no APIC?)\n"); + printk(BIOS_ERR, "%s %02lx limit %08llx\n", + dev_path(dev), resource->index, resource->limit); } - /* Make certain we are dealing with a good minimum size */ - size = resource->size; - align = resource->align; - if (align < min_align) { - align = min_align; + if (resource->flags & IORESOURCE_IO) { + /* + * Don't allow potential aliases over the legacy PCI + * expansion card addresses. The legacy PCI decodes + * only 10 bits, uses 0x100 - 0x3ff. Therefore, only + * 0x00 - 0xff can be used out of each 0x400 block of + * I/O space. + */ + if ((base & 0x300) != 0) { + base = (base & ~0x3ff) + 0x400; + } + /* + * Don't allow allocations in the VGA I/O range. + * PCI has special cases for that. + */ + else if ((base >= 0x3b0) && (base <= 0x3df)) { + base = 0x3e0; + } } - if (resource->flags & IORESOURCE_FIXED) { + /* Base must be aligned. */ + base = round(base, resource->align); + resource->base = base; + base += resource->size; + + printk(BIOS_SPEW, "%s %02lx * [0x%llx - 0x%llx] %s\n", + dev_path(dev), resource->index, resource->base, + resource->base + resource->size - 1, + (resource->flags & IORESOURCE_IO) ? "io" : + (resource->flags & IORESOURCE_PREFETCH) ? + "prefmem" : "mem"); + } + + /* + * A PCI bridge resource does not need to be a power of two size, but + * it does have a minimum granularity. Round the size up to that + * minimum granularity so we know not to place something else at an + * address postitively decoded by the bridge. + */ + bridge->size = round(base, bridge->gran) - + round(bridge->base, bridge->align); + + printk(BIOS_SPEW, "%s %s_%s: base: %llx size: %llx align: %d gran: %d" + " limit: %llx done\n", dev_path(bus->dev), __func__, + (bridge->flags & IORESOURCE_IO) ? "io" : + (bridge->flags & IORESOURCE_PREFETCH) ? "prefmem" : "mem", + base, bridge->size, bridge->align, bridge->gran, bridge->limit); +} + +/** + * This function is the second part of the resource allocator. + * + * See the compute_resources function for a more detailed explanation. + * + * This function assigns the resources a value. + * + * @param bus The bus we are traversing. + * @param bridge The bridge resource which must contain the bus' resources. + * @param type_mask This value gets ANDed with the resource type. + * @param type This value must match the result of the AND. + * + * @see compute_resources + */ +static void allocate_resources(struct bus *bus, struct resource *bridge, + unsigned long type_mask, unsigned long type) +{ + struct device *dev; + struct resource *resource; + resource_t base; + base = bridge->base; + + printk(BIOS_SPEW, "%s %s_%s: base:%llx size:%llx align:%d gran:%d " + "limit:%llx\n", dev_path(bus->dev), __func__, + (type & IORESOURCE_IO) ? "io" : (type & IORESOURCE_PREFETCH) ? + "prefmem" : "mem", + base, bridge->size, bridge->align, bridge->gran, bridge->limit); + + /* Remember we haven't found anything yet. */ + resource = NULL; + + /* + * Walk through all the resources on the current bus and allocate them + * address space. + */ + while ((dev = largest_resource(bus, &resource, type_mask, type))) { + + /* Propagate the bridge limit to the resource register. */ + if (resource->limit > bridge->limit) + resource->limit = bridge->limit; + + /* Size 0 resources can be skipped. */ + if (!resource->size) { + /* Set the base to limit so it doesn't confuse tolm. */ + resource->base = resource->limit; + resource->flags |= IORESOURCE_ASSIGNED; continue; } + if (resource->flags & IORESOURCE_IO) { - /* Don't allow potential aliases over the - * legacy pci expansion card addresses. - * The legacy pci decodes only 10 bits, - * uses 100h - 3ffh. Therefor, only 0 - ff - * can be used out of each 400h block of io - * space. + /* + * Don't allow potential aliases over the legacy PCI + * expansion card addresses. The legacy PCI decodes + * only 10 bits, uses 0x100 - 0x3ff. Therefore, only + * 0x00 - 0xff can be used out of each 0x400 block of + * I/O space. */ if ((base & 0x300) != 0) { base = (base & ~0x3ff) + 0x400; } - /* Don't allow allocations in the VGA IO range. + /* + * Don't allow allocations in the VGA I/O range. * PCI has special cases for that. */ else if ((base >= 0x3b0) && (base <= 0x3df)) { base = 0x3e0; } } - if (((round(base, 1UL << align) + size) -1) <= resource->limit) { - /* base must be aligned to size */ - base = round(base, 1UL << align); + + if ((round(base, resource->align) + resource->size - 1) <= + resource->limit) { + /* Base must be aligned. */ + base = round(base, resource->align); resource->base = base; resource->flags |= IORESOURCE_ASSIGNED; resource->flags &= ~IORESOURCE_STORED; - base += size; - - printk_spew("%s %02x * [0x%08lx - 0x%08lx] %s\n", - dev_path(dev), - resource->index, resource->base, - resource->base + resource->size - 1, - (resource->flags & IORESOURCE_IO)? "io": - (resource->flags & IORESOURCE_PREFETCH)? "prefmem": "mem"); + base += resource->size; + } else { + printk(BIOS_ERR, "!! Resource didn't fit !!\n"); + printk(BIOS_ERR, " aligned base %llx size %llx " + "limit %llx\n", round(base, resource->align), + resource->size, resource->limit); + printk(BIOS_ERR, " %llx needs to be <= %llx " + "(limit)\n", (round(base, resource->align) + + resource->size) - 1, resource->limit); + printk(BIOS_ERR, " %s%s %02lx * [0x%llx - 0x%llx]" + " %s\n", (resource->flags & IORESOURCE_ASSIGNED) + ? "Assigned: " : "", dev_path(dev), + resource->index, resource->base, + resource->base + resource->size - 1, + (resource->flags & IORESOURCE_IO) ? "io" + : (resource->flags & IORESOURCE_PREFETCH) + ? "prefmem" : "mem"); } + + printk(BIOS_SPEW, "%s%s %02lx * [0x%llx - 0x%llx] %s\n", + (resource->flags & IORESOURCE_ASSIGNED) ? "Assigned: " + : "", dev_path(dev), resource->index, resource->base, + resource->size ? resource->base + resource->size - 1 : + resource->base, (resource->flags & IORESOURCE_IO) + ? "io" : (resource->flags & IORESOURCE_PREFETCH) + ? "prefmem" : "mem"); } - /* A pci bridge resource does not need to be a power - * of two size, but it does have a minimum granularity. - * Round the size up to that minimum granularity so we - * know not to place something else at an address postitively - * decoded by the bridge. + + /* + * A PCI bridge resource does not need to be a power of two size, but + * it does have a minimum granularity. Round the size up to that + * minimum granularity so we know not to place something else at an + * address positively decoded by the bridge. */ - bridge->size = round(base, 1UL << bridge->gran) - bridge->base; - printk_spew("%s compute_allocate_%s: base: %08lx size: %08lx align: %d gran: %d done\n", - dev_path(dev), - (bridge->flags & IORESOURCE_IO)? "io": - (bridge->flags & IORESOURCE_PREFETCH)? "prefmem" : "mem", - base, bridge->size, bridge->align, bridge->gran); + bridge->flags |= IORESOURCE_ASSIGNED; + + printk(BIOS_SPEW, "%s %s_%s: next_base: %llx size: %llx align: %d " + "gran: %d done\n", dev_path(bus->dev), __func__, + (type & IORESOURCE_IO) ? "io" : (type & IORESOURCE_PREFETCH) ? + "prefmem" : "mem", base, bridge->size, bridge->align, + bridge->gran); + + /* For each child which is a bridge, allocate_resources. */ + for (dev = bus->children; dev; dev = dev->sibling) { + struct resource *child_bridge; + + if (!dev->link_list) + continue; + + /* Find the resources with matching type flags. */ + for (child_bridge = dev->resource_list; child_bridge; + child_bridge = child_bridge->next) { + struct bus* link; + + if (!(child_bridge->flags & IORESOURCE_BRIDGE) || + (child_bridge->flags & type_mask) != type) + continue; + + /* + * Split prefetchable memory if combined. Many domains + * use the same address space for prefetchable memory + * and non-prefetchable memory. Bridges below them need + * it separated. Add the PREFETCH flag to the type_mask + * and type. + */ + link = dev->link_list; + while (link && link->link_num != + IOINDEX_LINK(child_bridge->index)) + link = link->next; + if (link == NULL) + printk(BIOS_ERR, "link %ld not found on %s\n", + IOINDEX_LINK(child_bridge->index), + dev_path(dev)); + + allocate_resources(link, child_bridge, + type_mask | IORESOURCE_PREFETCH, + type | (child_bridge->flags & + IORESOURCE_PREFETCH)); + } + } +} + +#if CONFIG_PCI_64BIT_PREF_MEM == 1 +#define MEM_MASK (IORESOURCE_PREFETCH | IORESOURCE_MEM) +#else +#define MEM_MASK (IORESOURCE_MEM) +#endif + +#define IO_MASK (IORESOURCE_IO) +#define PREF_TYPE (IORESOURCE_PREFETCH | IORESOURCE_MEM) +#define MEM_TYPE (IORESOURCE_MEM) +#define IO_TYPE (IORESOURCE_IO) + +struct constraints { + struct resource pref, io, mem; +}; + +static void constrain_resources(struct device *dev, struct constraints* limits) +{ + struct device *child; + struct resource *res; + struct resource *lim; + struct bus *link; + + printk(BIOS_SPEW, "%s: %s\n", __func__, dev_path(dev)); + + /* Constrain limits based on the fixed resources of this device. */ + for (res = dev->resource_list; res; res = res->next) { + if (!(res->flags & IORESOURCE_FIXED)) + continue; + if (!res->size) { + /* It makes no sense to have 0-sized, fixed resources.*/ + printk(BIOS_ERR, "skipping %s@%lx fixed resource, " + "size=0!\n", dev_path(dev), res->index); + continue; + } + + /* PREFETCH, MEM, or I/O - skip any others. */ + if ((res->flags & MEM_MASK) == PREF_TYPE) + lim = &limits->pref; + else if ((res->flags & MEM_MASK) == MEM_TYPE) + lim = &limits->mem; + else if ((res->flags & IO_MASK) == IO_TYPE) + lim = &limits->io; + else + continue; + + /* + * Is it a fixed resource outside the current known region? + * If so, we don't have to consider it - it will be handled + * correctly and doesn't affect current region's limits. + */ + if (((res->base + res->size -1) < lim->base) + || (res->base > lim->limit)) + continue; + /* + * Choose to be above or below fixed resources. This check is + * signed so that "negative" amounts of space are handled + * correctly. + */ + if ((signed long long)(lim->limit - (res->base + res->size -1)) + > (signed long long)(res->base - lim->base)) + lim->base = res->base + res->size; + else + lim->limit = res->base -1; + } + /* Descend into every enabled child and look for fixed resources. */ + for (link = dev->link_list; link; link = link->next) { + for (child = link->children; child; child = child->sibling) { + if (child->enabled) + constrain_resources(child, limits); + } + } } -static void allocate_vga_resource(void) +static void avoid_fixed_resources(struct device *dev) { -#warning "FIXME modify allocate_vga_resource so it is less pci centric!" -#warning "This function knows to much about PCI stuff, it should be just a ietrator/visitor." + struct constraints limits; + struct resource *res; + + printk(BIOS_SPEW, "%s: %s\n", __func__, dev_path(dev)); + + /* Initialize constraints to maximum size. */ + limits.pref.base = 0; + limits.pref.limit = 0xffffffffffffffffULL; + limits.io.base = 0; + limits.io.limit = 0xffffffffffffffffULL; + limits.mem.base = 0; + limits.mem.limit = 0xffffffffffffffffULL; + + /* Constrain the limits to dev's initial resources. */ + for (res = dev->resource_list; res; res = res->next) { + if ((res->flags & IORESOURCE_FIXED)) + continue; + printk(BIOS_SPEW, "%s:@%s %02lx limit %08llx\n", __func__, + dev_path(dev), res->index, res->limit); + if ((res->flags & MEM_MASK) == PREF_TYPE && + (res->limit < limits.pref.limit)) + limits.pref.limit = res->limit; + if ((res->flags & MEM_MASK) == MEM_TYPE && + (res->limit < limits.mem.limit)) + limits.mem.limit = res->limit; + if ((res->flags & IO_MASK) == IO_TYPE && + (res->limit < limits.io.limit)) + limits.io.limit = res->limit; + } - /* FIXME handle the VGA pallette snooping */ - struct device *dev, *vga; + /* Look through the tree for fixed resources and update the limits. */ + constrain_resources(dev, &limits); + + /* Update dev's resources with new limits. */ + for (res = dev->resource_list; res; res = res->next) { + struct resource *lim; + + if ((res->flags & IORESOURCE_FIXED)) + continue; + + /* PREFETCH, MEM, or I/O - skip any others. */ + if ((res->flags & MEM_MASK) == PREF_TYPE) + lim = &limits.pref; + else if ((res->flags & MEM_MASK) == MEM_TYPE) + lim = &limits.mem; + else if ((res->flags & IO_MASK) == IO_TYPE) + lim = &limits.io; + else + continue; + + printk(BIOS_SPEW, "%s2: %s@%02lx limit %08llx\n", __func__, + dev_path(dev), res->index, res->limit); + printk(BIOS_SPEW, "\tlim->base %08llx lim->limit %08llx\n", + lim->base, lim->limit); + + /* Is the resource outside the limits? */ + if (lim->base > res->base) + res->base = lim->base; + if (res->limit > lim->limit) + res->limit = lim->limit; + } +} + +#if CONFIG_VGA_BRIDGE_SETUP == 1 +device_t vga_pri = 0; +static void set_vga_bridge_bits(void) +{ + /* + * FIXME: Modify set_vga_bridge() so it is less PCI centric! + * This function knows too much about PCI stuff, it should be just + * an iterator/visitor. + */ + + /* FIXME: Handle the VGA palette snooping. */ + struct device *dev, *vga, *vga_onboard, *vga_first, *vga_last; struct bus *bus; + bus = 0; vga = 0; - for(dev = all_devices; dev; dev = dev->next) { - if (((dev->class >> 16) == 0x03) && - ((dev->class >> 8) != 0x380)) { - if (!vga) { - printk_debug("Allocating VGA resource\n"); - vga = dev; - } - if (vga == dev) { - /* All legacy VGA cards have MEM & I/O space registers */ - dev->command |= PCI_COMMAND_MEMORY | PCI_COMMAND_IO; + vga_onboard = 0; + vga_first = 0; + vga_last = 0; + + for (dev = all_devices; dev; dev = dev->next) { + + if (!dev->enabled) + continue; + + if (((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) && + ((dev->class >> 8) != PCI_CLASS_DISPLAY_OTHER)) { + if (!vga_first) { + if (dev->on_mainboard) + vga_onboard = dev; + else + vga_first = dev; } else { - /* It isn't safe to enable other VGA cards */ - dev->command &= ~(PCI_COMMAND_MEMORY | PCI_COMMAND_IO); + if (dev->on_mainboard) + vga_onboard = dev; + else + vga_last = dev; } + + /* It isn't safe to enable other VGA cards. */ + dev->command &= ~(PCI_COMMAND_MEMORY | PCI_COMMAND_IO); } } + + vga = vga_last; + + if (!vga) + vga = vga_first; + +#if CONFIG_ONBOARD_VGA_IS_PRIMARY == 1 + if (vga_onboard) /* Will use onboard VGA as primary. */ +#else + if (!vga) /* Will use last add-on adapter as primary. */ +#endif + { + vga = vga_onboard; + } + if (vga) { + /* VGA is first add-on card or the only onboard VGA. */ + printk(BIOS_DEBUG, "Setting up VGA for %s\n", dev_path(vga)); + /* All legacy VGA cards have MEM & I/O space registers. */ + vga->command |= (PCI_COMMAND_MEMORY | PCI_COMMAND_IO); + vga_pri = vga; bus = vga->bus; } - /* Now walk up the bridges setting the VGA enable */ - while(bus) { + + /* Now walk up the bridges setting the VGA enable. */ + while (bus) { + printk(BIOS_DEBUG, "Setting PCI_BRIDGE_CTL_VGA for bridge %s\n", + dev_path(bus->dev)); bus->bridge_ctrl |= PCI_BRIDGE_CTL_VGA; - bus = (bus == bus->dev->bus)? 0 : bus->dev->bus; - } + bus = (bus == bus->dev->bus) ? 0 : bus->dev->bus; + } } +#endif -/** Assign the computed resources to the bridges and devices on the bus. - * Recurse to any bridges found on this bus first. Then do the devices - * on this bus. - * - * @param bus Pointer to the structure for this bus - */ +/** + * Assign the computed resources to the devices on the bus. + * + * Use the device specific set_resources() method to store the computed + * resources to hardware. For bridge devices, the set_resources() method + * has to recurse into every down stream buses. + * + * Mutual recursion: + * assign_resources() -> device_operation::set_resources() + * device_operation::set_resources() -> assign_resources() + * + * @param bus Pointer to the structure for this bus. + */ void assign_resources(struct bus *bus) { struct device *curdev; - printk_debug("ASSIGN RESOURCES, bus %d\n", bus->secondary); + printk(BIOS_SPEW, "%s assign_resources, bus %d link: %d\n", + dev_path(bus->dev), bus->secondary, bus->link_num); for (curdev = bus->children; curdev; curdev = curdev->sibling) { - if (!curdev->ops || !curdev->ops->set_resources) { - printk_err("%s missing set_resources\n", - dev_path(curdev)); + if (!curdev->enabled || !curdev->resource_list) continue; - } - if (!curdev->enabled) { + + if (!curdev->ops || !curdev->ops->set_resources) { + printk(BIOS_ERR, "%s missing set_resources\n", + dev_path(curdev)); continue; } curdev->ops->set_resources(curdev); } - printk_debug("ASSIGNED RESOURCES, bus %d\n", bus->secondary); + printk(BIOS_SPEW, "%s assign_resources, bus %d link: %d\n", + dev_path(bus->dev), bus->secondary, bus->link_num); } /** - * @brief Enable the resources for a specific device - * - * @param dev the device whose resources are to be enabled + * Enable the resources for devices on a link. * * Enable resources of the device by calling the device specific * enable_resources() method. * * The parent's resources should be enabled first to avoid having enabling * order problem. This is done by calling the parent's enable_resources() - * method and let the method to call it's children's enable_resoruces() via - * enable_childrens_resources(). + * method before its childrens' enable_resources() methods. + * + * @param link The link whose devices' resources are to be enabled. */ -void enable_resources(struct device *dev) +static void enable_resources(struct bus *link) { - if (!dev->ops || !dev->ops->enable_resources) { - printk_err("%s missing enable_resources\n", dev_path(dev)); - return; + struct device *dev; + struct bus *c_link; + + for (dev = link->children; dev; dev = dev->sibling) { + if (dev->enabled && dev->ops && dev->ops->enable_resources) + dev->ops->enable_resources(dev); } - if (!dev->enabled) { - return; + + for (dev = link->children; dev; dev = dev->sibling) { + for (c_link = dev->link_list; c_link; c_link = c_link->next) + enable_resources(c_link); } - dev->ops->enable_resources(dev); } /** - * @brief Determine the existence of dynamic devices and construct dynamic + * Reset all of the devices on a bus and clear the bus's reset_needed flag. + * + * @param bus Pointer to the bus structure. + * @return 1 if the bus was successfully reset, 0 otherwise. + */ +int reset_bus(struct bus *bus) +{ + if (bus && bus->dev && bus->dev->ops && bus->dev->ops->reset_bus) { + bus->dev->ops->reset_bus(bus); + bus->reset_needed = 0; + return 1; + } + return 0; +} + +/** + * Scan for devices on a bus. + * + * If there are bridges on the bus, recursively scan the buses behind the + * bridges. If the setting up and tuning of the bus causes a reset to be + * required, reset the bus and scan it again. + * + * @param busdev Pointer to the bus device. + * @param max Current bus number. + * @return The maximum bus number found, after scanning all subordinate buses. + */ +unsigned int scan_bus(struct device *busdev, unsigned int max) +{ + unsigned int new_max; + int do_scan_bus; + + if (!busdev || !busdev->enabled || !busdev->ops || + !busdev->ops->scan_bus) { + return max; + } + + do_scan_bus = 1; + while (do_scan_bus) { + struct bus *link; + new_max = busdev->ops->scan_bus(busdev, max); + do_scan_bus = 0; + for (link = busdev->link_list; link; link = link->next) { + if (link->reset_needed) { + if (reset_bus(link)) + do_scan_bus = 1; + else + busdev->bus->reset_needed = 1; + } + } + } + return new_max; +} + +/** + * Determine the existence of devices and extend the device tree. + * + * Most of the devices in the system are listed in the mainboard devicetree.cb + * file. The device structures for these devices are generated at compile + * time by the config tool and are organized into the device tree. This + * function determines if the devices created at compile time actually exist + * in the physical system. + * + * For devices in the physical system but not listed in devicetree.cb, + * the device structures have to be created at run time and attached to the * device tree. * - * Start form the root device 'dev_root', scan the buses in the system - * recursively, build the dynamic device tree according to the result - * of the probe. + * This function starts from the root device 'dev_root', scans the buses in + * the system recursively, and modifies the device tree according to the + * result of the probe. * * This function has no idea how to scan and probe buses and devices at all. * It depends on the bus/device specific scan_bus() method to do it. The - * scan_bus() function also have to create the device structure and attach - * it to the device tree. + * scan_bus() method also has to create the device structure and attach + * it to the device tree. */ void dev_enumerate(void) { struct device *root; - unsigned subordinate; - printk_info("Enumerating buses...\n"); + printk(BIOS_INFO, "Enumerating buses...\n"); root = &dev_root; - subordinate = root->ops->scan_bus(root, 0); - printk_info("done\n"); + show_all_devs(BIOS_SPEW, "Before device enumeration."); + printk(BIOS_SPEW, "Compare with tree...\n"); + show_devs_tree(root, BIOS_SPEW, 0, 0); + + if (root->chip_ops && root->chip_ops->enable_dev) + root->chip_ops->enable_dev(root); + + if (!root->ops || !root->ops->scan_bus) { + printk(BIOS_ERR, "dev_root missing scan_bus operation"); + return; + } + scan_bus(root, 0); + printk(BIOS_INFO, "done\n"); } /** - * @brief Configure devices on the devices tree. - * - * Starting at the root of the dynamic device tree, travel recursively, - * compute resources needed by each device and allocate them. + * Configure devices on the devices tree. + * + * Starting at the root of the device tree, travel it recursively in two + * passes. In the first pass, we compute and allocate resources (ranges) + * requried by each device. In the second pass, the resources ranges are + * relocated to their final position and stored to the hardware. * - * I/O resources start at DEVICE_IO_START and grow upward. MEM resources start - * at DEVICE_MEM_START and grow downward. + * I/O resources grow upward. MEM resources grow downward. * * Since the assignment is hierarchical we set the values into the dev_root - * struct. + * struct. */ void dev_configure(void) { - struct device *root = &dev_root; + struct resource *res; + struct device *root; + struct device *child; + +#if CONFIG_VGA_BRIDGE_SETUP == 1 + set_vga_bridge_bits(); +#endif + + printk(BIOS_INFO, "Allocating resources...\n"); + + root = &dev_root; + + /* + * Each domain should create resources which contain the entire address + * space for IO, MEM, and PREFMEM resources in the domain. The + * allocation of device resources will be done from this address space. + */ - printk_info("Allocating resources..."); - printk_debug("\n"); + /* Read the resources for the entire tree. */ - root->ops->read_resources(root); + printk(BIOS_INFO, "Reading resources...\n"); + read_resources(root->link_list); + printk(BIOS_INFO, "Done reading resources.\n"); - /* Make certain the io devices are allocated somewhere safe. */ - root->resource[0].base = DEVICE_IO_START; - root->resource[0].flags |= IORESOURCE_ASSIGNED; - root->resource[0].flags &= ~IORESOURCE_STORED; + print_resource_tree(root, BIOS_SPEW, "After reading."); - /* Now reallocate the pci resources memory with the highest - * addresses I can manage.*/ - root->resource[1].base = - round_down(DEVICE_MEM_HIGH - root->resource[1].size, - 1UL << root->resource[1].align); - root->resource[1].flags |= IORESOURCE_ASSIGNED; - root->resource[1].flags &= ~IORESOURCE_STORED; + /* Compute resources for all domains. */ + for (child = root->link_list->children; child; child = child->sibling) { + if (!(child->path.type == DEVICE_PATH_PCI_DOMAIN)) + continue; + for (res = child->resource_list; res; res = res->next) { + if (res->flags & IORESOURCE_FIXED) + continue; + if (res->flags & IORESOURCE_PREFETCH) { + compute_resources(child->link_list, + res, MEM_MASK, PREF_TYPE); + continue; + } + if (res->flags & IORESOURCE_MEM) { + compute_resources(child->link_list, + res, MEM_MASK, MEM_TYPE); + continue; + } + if (res->flags & IORESOURCE_IO) { + compute_resources(child->link_list, + res, IO_MASK, IO_TYPE); + continue; + } + } + } - /* Allocate the VGA I/O resource.. */ - allocate_vga_resource(); + /* For all domains. */ + for (child = root->link_list->children; child; child=child->sibling) + if (child->path.type == DEVICE_PATH_PCI_DOMAIN) + avoid_fixed_resources(child); + + /* + * Now we need to adjust the resources. MEM resources need to start at + * the highest address managable. + */ + for (child = root->link_list->children; child; child = child->sibling) { + if (child->path.type != DEVICE_PATH_PCI_DOMAIN) + continue; + for (res = child->resource_list; res; res = res->next) { + if (!(res->flags & IORESOURCE_MEM) || + res->flags & IORESOURCE_FIXED) + continue; + res->base = resource_max(res); + } + } - /* now just set things into registers ... we hope ... */ - root->ops->set_resources(root); + /* Store the computed resource allocations into device registers ... */ + printk(BIOS_INFO, "Setting resources...\n"); + for (child = root->link_list->children; child; child = child->sibling) { + if (!(child->path.type == DEVICE_PATH_PCI_DOMAIN)) + continue; + for (res = child->resource_list; res; res = res->next) { + if (res->flags & IORESOURCE_FIXED) + continue; + if (res->flags & IORESOURCE_PREFETCH) { + allocate_resources(child->link_list, + res, MEM_MASK, PREF_TYPE); + continue; + } + if (res->flags & IORESOURCE_MEM) { + allocate_resources(child->link_list, + res, MEM_MASK, MEM_TYPE); + continue; + } + if (res->flags & IORESOURCE_IO) { + allocate_resources(child->link_list, + res, IO_MASK, IO_TYPE); + continue; + } + } + } + assign_resources(root->link_list); + printk(BIOS_INFO, "Done setting resources.\n"); + print_resource_tree(root, BIOS_SPEW, "After assigning values."); - printk_info("done.\n"); + printk(BIOS_INFO, "Done allocating resources.\n"); } /** - * @brief Enable devices on the device tree. + * Enable devices on the device tree. * * Starting at the root, walk the tree and enable all devices/bridges by * calling the device's enable_resources() method. */ void dev_enable(void) { - printk_info("Enabling resourcess...\n"); + struct bus *link; + + printk(BIOS_INFO, "Enabling resources...\n"); - /* now enable everything. */ - enable_resources(&dev_root); + /* Now enable everything. */ + for (link = dev_root.link_list; link; link = link->next) + enable_resources(link); - printk_info("done.\n"); + printk(BIOS_INFO, "done.\n"); } /** - * @brief Initialize all devices in the global device list. + * Initialize a specific device. * - * Starting at the first device on the global device link list, - * walk the list and call a driver to do device specific setup. + * The parent should be initialized first to avoid having an ordering problem. + * This is done by calling the parent's init() method before its childrens' + * init() methods. + * + * @param dev The device to be initialized. */ -void dev_initialize(void) +static void init_dev(struct device *dev) +{ + if (!dev->enabled) + return; + + if (!dev->initialized && dev->ops && dev->ops->init) { + if (dev->path.type == DEVICE_PATH_I2C) { + printk(BIOS_DEBUG, "smbus: %s[%d]->", + dev_path(dev->bus->dev), dev->bus->link_num); + } + + printk(BIOS_DEBUG, "%s init\n", dev_path(dev)); + dev->initialized = 1; + dev->ops->init(dev); + } +} + +static void init_link(struct bus *link) { struct device *dev; + struct bus *c_link; - printk_info("Initializing devices...\n"); + for (dev = link->children; dev; dev = dev->sibling) + init_dev(dev); - for (dev = all_devices; dev; dev = dev->next) { - if (dev->enabled && dev->ops && dev->ops->init) { - printk_debug("%s init\n", dev_path(dev)); - dev->ops->init(dev); - } + for (dev = link->children; dev; dev = dev->sibling) { + for (c_link = dev->link_list; c_link; c_link = c_link->next) + init_link(c_link); } +} + +/** + * Initialize all devices in the global device tree. + * + * Starting at the root device, call the device's init() method to do + * device-specific setup, then call each child's init() method. + */ +void dev_initialize(void) +{ + struct bus *link; + + printk(BIOS_INFO, "Initializing devices...\n"); + +#if CONFIG_ARCH_X86 + /* Ensure EBDA is prepared before Option ROMs. */ + setup_default_ebda(); +#endif + + /* First call the mainboard init. */ + init_dev(&dev_root); + + /* Now initialize everything. */ + for (link = dev_root.link_list; link; link = link->next) + init_link(link); - printk_info("Devices initialized\n"); + printk(BIOS_INFO, "Devices initialized\n"); + show_all_devs(BIOS_SPEW, "After init."); }