diff options
author | Eric Biederman <ebiederm@xmission.com> | 2003-09-02 03:36:25 +0000 |
---|---|---|
committer | Eric Biederman <ebiederm@xmission.com> | 2003-09-02 03:36:25 +0000 |
commit | e9a271e32c53076445ef70da8aec8201c82693ec (patch) | |
tree | af88f51ba907922157d3b97f9713a07480223372 /src/devices | |
parent | d4c14524f53d8e812cf52b57e16c53d259c44ea0 (diff) |
- Major update of the dynamic device tree so it can handle
* subtractive resources
* merging with the static device tree
* more device types than just pci
- The piece to watch out for is the new enable_resources method that was needed in all of the drivers
git-svn-id: svn://svn.coreboot.org/coreboot/trunk@1096 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
Diffstat (limited to 'src/devices')
-rw-r--r-- | src/devices/Config.lb | 1 | ||||
-rw-r--r-- | src/devices/chip.c | 85 | ||||
-rw-r--r-- | src/devices/device.c | 246 | ||||
-rw-r--r-- | src/devices/device_util.c | 83 | ||||
-rw-r--r-- | src/devices/pci_device.c | 348 | ||||
-rw-r--r-- | src/devices/root_device.c | 127 |
6 files changed, 628 insertions, 262 deletions
diff --git a/src/devices/Config.lb b/src/devices/Config.lb index 199c7da46b..063aa1db92 100644 --- a/src/devices/Config.lb +++ b/src/devices/Config.lb @@ -1,4 +1,5 @@ object device.o +object root_device.o object device_util.o object pci_device.o object chip.o diff --git a/src/devices/chip.c b/src/devices/chip.c index 0074f2bede..b7eace38f0 100644 --- a/src/devices/chip.c +++ b/src/devices/chip.c @@ -5,7 +5,9 @@ * path, etc. */ +#include <console/console.h> #include <device/chip.h> +#include <device/pci.h> void chip_configure(struct chip *root, enum chip_pass pass) @@ -22,3 +24,86 @@ chip_configure(struct chip *root, enum chip_pass pass) chip_configure(c->children, pass); } } + +/** Convert a static struct chip structure to a set of dynamic device structures. + * @param chip Static chip structure to start with. + */ + +void chip_enumerate(struct chip *chip) +{ + struct chip *child; + device_t dev; + int link; + int i; + dev = 0; + link = 0; +#if 1 + if (chip->control && chip->control->name) { + printk_debug("Enumerating: %s\n", chip->control->name); + } +#endif + for(i = 0; i < MAX_CHIP_PATHS; i++) { + int identical_paths; + identical_paths = + (i > 0) && + (path_eq(&chip->path[i - 1].path, &chip->path[i].path)) && + (chip->path[i - 1].channel == chip->path[i].channel); + if (!identical_paths) { + link = 0; + dev = 0; + switch(chip->path[i].path.type) { + case DEVICE_PATH_NONE: + break; + default: + dev = alloc_dev(chip->bus, &chip->path[i].path); + break; + } + } + else { + link += 1; + } + if (dev) { + printk_spew("path %s %s\n", dev_path(dev), identical_paths?"identical":""); + dev->enable = chip->path[i].enable; + dev->links = link + 1; + for(child = chip->children; child; child = child->next) { + if (!child->bus && + child->path[0].channel == i) { + child->bus = &dev->link[link]; + } + } + } + if (dev && !chip->dev) { + chip->dev = dev; + } + } + for(child = chip->children; child; child = child->next) { + if (!child->bus) { + child->bus = &chip->dev->link[0]; + } + } +} + +static void enumerate_static_device_chain(struct chip *root) +{ + struct chip *chip; + for(chip = root; chip; chip = chip->next) { + void (*enumerate)(struct chip *chip); + enumerate = chip_enumerate; + if (chip->control && chip->control->enumerate) { + enumerate = chip->control->enumerate; + } + enumerate(chip); + } + + for(chip = root; chip; chip = chip->next) { + if (chip->children) { + enumerate_static_device_chain(chip->children); + } + } +} + +void enumerate_static_devices(void) +{ + enumerate_static_device_chain(&static_root); +} diff --git a/src/devices/device.c b/src/devices/device.c index 8e6fcb3519..e52cc2a57e 100644 --- a/src/devices/device.c +++ b/src/devices/device.c @@ -1,6 +1,7 @@ /* * (c) 1999--2000 Martin Mares <mj@suse.cz> * (c) 2003 Eric Biederman <ebiederm@xmission.com> + * (c) 2003 Linux Networx */ /* lots of mods by ron minnich (rminnich@lanl.gov), with * the final architecture guidance from Tom Merritt (tjm@codegen.com) @@ -18,12 +19,9 @@ #include <arch/io.h> #include <device/device.h> #include <device/pci.h> +#include <stdlib.h> +#include <string.h> -/** - * This is the root of the device tree. A PCI tree always has - * one bus, bus 0. Bus 0 contains devices and bridges. - */ -struct device dev_root; /* Linked list of ALL devices */ struct device *all_devices = 0; /* pointer to the last device */ @@ -32,16 +30,46 @@ static struct device **last_dev_p = &all_devices; #define DEVICE_MEM_HIGH 0xFEC00000UL /* Reserve 20M for the system */ #define DEVICE_IO_START 0x1000 - -/* Append a new device to the global device chain. - * The chain is used to find devices once everything is set up. +/** Allocate a new device structure */ -void append_device(struct device *dev) +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; ) { + child = child->sibling; + } + dev = malloc(sizeof(*dev)); + if (dev == 0) { + die("DEV: out of memory.\n"); + } + memset(dev, 0, sizeof(*dev)); + memcpy(&dev->path, path, sizeof(*path)); + + /* Append a new device to the global device chain. + * The chain 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; + } + + /* Add the new device to the children of the bus. */ + dev->bus = parent; + if (child) { + child->sibling = dev; + } else { + parent->children = dev; + } + /* If we don't have any other information about a device enable it */ + dev->enable = 1; + return dev; +} /** round a number to an alignment. * @param val the starting value @@ -71,28 +99,79 @@ static unsigned long round_down(unsigned long val, unsigned long round_down) /** Read the resources on all devices of a given bus. * @param bus bus to read the resources on. */ -static void read_resources(struct device *bus) +static void read_resources(struct bus *bus) { struct device *curdev; - /* Walk through all of the devices and find which resources they need. */ for(curdev = bus->children; curdev; curdev = curdev->sibling) { + unsigned links; + int i; if (curdev->resources > 0) { continue; } + if (!curdev->ops || !curdev->ops->read_resources) { + printk_err("%s missing read_resources\n", + dev_path(curdev)); + 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]); + + } + } } } -static struct device *largest_resource(struct device *bus, struct resource **result_res, - unsigned long type_mask, unsigned long type) +struct pick_largest_state { + struct resource *last; + 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) +{ + struct resource *last; + last = state->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)))) { + return; + } + if (!state->result || + (state->result->align < resource->align) || + ((state->result->align == resource->align) && + (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; - struct device *result_dev = 0; - struct resource *last = *result_res; - struct resource *result = 0; - int seen_last = 0; for(curdev = bus->children; curdev; curdev = curdev->sibling) { int i; for(i = 0; i < curdev->resources; i++) { @@ -101,31 +180,33 @@ static struct device *largest_resource(struct device *bus, struct resource **res if ((resource->flags & type_mask) != type) { continue; } - /* Be certain to pick the successor to last */ - if (resource == last) { - seen_last = 1; + /* 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; } - if (last && ( - (last->align < resource->align) || - ((last->align == resource->align) && - (last->size < resource->size)) || - ((last->align == resource->align) && - (last->size == resource->size) && - (!seen_last)))) { - continue; - } - if (!result || - (result->align < resource->align) || - ((result->align == resource->align) && - (result->size < resource->size))) { - result_dev = curdev; - result = resource; - } + /* See if this is the largest resource */ + pick_largest_resource(state, curdev, resource); } } - *result_res = result; - return result_dev; +} + +static struct device *largest_resource(struct bus *bus, struct resource **result_res, + unsigned long type_mask, unsigned long type) +{ + struct pick_largest_state state; + + state.last = *result_res; + state.result_dev = 0; + state.result = 0; + state.seen_last = 0; + + find_largest_resource(&state, bus, type_mask, type); + + *result_res = state.result; + return state.result_dev; } /* Compute allocate resources is the guts of the resource allocator. @@ -158,7 +239,7 @@ static struct device *largest_resource(struct device *bus, struct resource **res */ void compute_allocate_resource( - struct device *bus, + struct bus *bus, struct resource *bridge, unsigned long type_mask, unsigned long type) @@ -181,9 +262,8 @@ void compute_allocate_resource( min_align = log2(DEVICE_MEM_ALIGN); } - printk_spew("DEV: %02x:%02x.%01x compute_allocate_%s: base: %08lx size: %08lx align: %d gran: %d\n", - bus->bus->secondary, - PCI_SLOT(bus->devfn), PCI_FUNC(bus->devfn), + printk_spew("%s compute_allocate_%s: base: %08lx size: %08lx align: %d gran: %d\n", + dev_path(dev), (bridge->flags & IORESOURCE_IO)? "io": (bridge->flags & IORESOURCE_PREFETCH)? "prefmem" : "mem", base, bridge->size, bridge->align, bridge->gran); @@ -214,6 +294,9 @@ void compute_allocate_resource( if (align < min_align) { align = min_align; } + if (resource->flags & IORESOURCE_FIXED) { + continue; + } if (resource->flags & IORESOURCE_IO) { /* Don't allow potential aliases over the * legacy pci expansion card addresses. @@ -240,9 +323,8 @@ void compute_allocate_resource( base += size; printk_spew( - "DEV: %02x:%02x.%01x %02x * [0x%08lx - 0x%08lx] %s\n", - dev->bus->secondary, - PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), + "%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": @@ -258,9 +340,8 @@ void compute_allocate_resource( */ bridge->size = round(base, 1UL << bridge->gran) - bridge->base; - printk_spew("DEV: %02x:%02x.%01x compute_allocate_%s: base: %08lx size: %08lx align: %d gran: %d done\n", - bus->bus->secondary, - PCI_SLOT(bus->devfn), PCI_FUNC(bus->devfn), + 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); @@ -270,14 +351,15 @@ void compute_allocate_resource( static void allocate_vga_resource(void) { +#warning "FIXME modify allocate_vga_resource so it is less pci centric!" /* FIXME handle the VGA pallette snooping */ - struct device *dev, *vga, *bus; - bus = vga = 0; + struct device *dev, *vga; + struct bus *bus; + bus = 0; + vga = 0; for(dev = all_devices; dev; dev = dev->next) { - uint32_t class_revision; - class_revision = pci_read_config32(dev, PCI_CLASS_REVISION); - if (((class_revision >> 24) == 0x03) && - ((class_revision >> 16) != 0x380)) { + if (((dev->class >> 16) == 0x03) && + ((dev->class >> 8) != 0x380)) { if (!vga) { printk_debug("Allocating VGA resource\n"); vga = dev; @@ -296,11 +378,8 @@ static void allocate_vga_resource(void) } /* Now walk up the bridges setting the VGA enable */ while(bus) { - uint16_t ctrl; - ctrl = pci_read_config16(bus, PCI_BRIDGE_CONTROL); - ctrl |= PCI_BRIDGE_CTL_VGA; - pci_write_config16(bus, PCI_BRIDGE_CONTROL, ctrl); - bus = (bus == bus->bus)? 0 : bus->bus; + bus->bridge_ctrl |= PCI_BRIDGE_CONTROL; + bus = (bus == bus->dev->bus)? 0 : bus->dev->bus; } } @@ -310,36 +389,35 @@ static void allocate_vga_resource(void) * on this bus. * @param bus Pointer to the structure for this bus */ -void assign_resources(struct device *bus) +void assign_resources(struct bus *bus) { struct device *curdev; printk_debug("ASSIGN RESOURCES, bus %d\n", bus->secondary); 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)); + continue; + } curdev->ops->set_resources(curdev); } printk_debug("ASSIGNED RESOURCES, bus %d\n", bus->secondary); } -static void enable_resources(struct device *bus) +void enable_resources(struct device *dev) { - struct device *curdev; - - /* Walk through the chain of all pci devices and enable them. - * This is effectively a breadth first traversal so we should - * not have enalbing ordering problems. + /* Enable the resources for a specific device. + * The parents resources should be enabled first to avoid + * having enabling ordering problems. */ - for (curdev = all_devices; curdev; curdev = curdev->next) { - uint16_t command; - command = pci_read_config16(curdev, PCI_COMMAND); - command |= curdev->command; - printk_debug("DEV: %02x:%02x.%01x cmd <- %02x\n", - curdev->bus->secondary, - PCI_SLOT(curdev->devfn), PCI_FUNC(curdev->devfn), - command); - pci_write_config16(curdev, PCI_COMMAND, command); + if (!dev->ops || !dev->ops->enable_resources) { + printk_err("%s missing enable_resources\n", + dev_path(dev)); + return; } + dev->ops->enable_resources(dev); } /** Enumerate the resources on the PCI by calling pci_init @@ -347,12 +425,10 @@ static void enable_resources(struct device *bus) void dev_enumerate(void) { struct device *root; + unsigned subordinate; printk_info("Enumerating buses..."); root = &dev_root; - if (!root->ops) { - root->ops = &default_pci_ops_root; - } - root->subordinate = root->ops->scan_bus(root, 0); + subordinate = root->ops->scan_bus(root, 0); printk_info("done\n"); } @@ -394,7 +470,7 @@ void dev_configure(void) */ void dev_enable(void) { - printk_info("Enabling resourcess..."); + printk_info("Enabling resourcess...\n"); /* now enable everything. */ enable_resources(&dev_root); @@ -410,14 +486,10 @@ void dev_initialize(void) printk_info("Initializing devices...\n"); for (dev = all_devices; dev; dev = dev->next) { - if (dev->ops->init) { - printk_debug("PCI: %02x:%02x.%01x init\n", - dev->bus->secondary, - PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + if (dev->ops && dev->ops->init) { + printk_debug("%s init\n", dev_path(dev)); dev->ops->init(dev); } } printk_info("Devices initialized\n"); } - - diff --git a/src/devices/device_util.c b/src/devices/device_util.c index a57ea1f5ae..384a3be8e0 100644 --- a/src/devices/device_util.c +++ b/src/devices/device_util.c @@ -1,5 +1,26 @@ #include <console/console.h> #include <device/device.h> +#include <device/path.h> +#include <device/pci.h> +#include <string.h> + + +/** + * See if a device structure already exists and if not allocate it + * @param bus The bus to find the device on + * @param path The relative path from the bus to the appropriate device + * @return pointer a device structure for the device on bus at path + */ +device_t alloc_find_dev(struct bus *parent, struct device_path *path) +{ + device_t child; + for(child = parent->children; child; child = child->sibling) { + if (path_eq(path, &child->path)) { + return child; + } + } + return alloc_dev(parent, path); +} /** * Given a bus and a devfn number, find the device structure @@ -11,9 +32,12 @@ struct device *dev_find_slot(unsigned int bus, unsigned int devfn) { struct device *dev; - for (dev = all_devices; dev; dev = dev->next) - if (dev->bus->secondary == bus && dev->devfn == devfn) + for (dev = all_devices; dev; dev = dev->next) { + if ((dev->bus->secondary == bus) && + (dev->path.u.pci.devfn == devfn)) { break; + } + } return dev; } @@ -54,3 +78,58 @@ struct device *dev_find_class(unsigned int class, struct device *from) return from; } + +const char *dev_path(device_t dev) +{ + static char buffer[DEVICE_PATH_MAX]; + buffer[0] = '\0'; + if (!dev) { + memcpy(buffer, "<null>", 7); + } + else { + switch(dev->path.type) { + case DEVICE_PATH_PCI: + sprintf(buffer, "PCI: %02x:%02x.%01x", + dev->bus->secondary, + PCI_SLOT(dev->path.u.pci.devfn), PCI_FUNC(dev->path.u.pci.devfn)); + break; + case DEVICE_PATH_PNP: + sprintf(buffer, "PNP: %04x.%01x", + dev->path.u.pnp.port, dev->path.u.pnp.device); + break; + case DEVICE_PATH_I2C: + sprintf(buffer, "I2C: %02x", + dev->path.u.i2c.device); + break; + default: + printk_err("Unknown device path type: %d\n", dev->path.type); + break; + } + } + return buffer; +} + +int path_eq(struct device_path *path1, struct device_path *path2) +{ + int equal = 0; + if (path1->type == path2->type) { + switch(path1->type) { + case DEVICE_PATH_NONE: + break; + case DEVICE_PATH_PCI: + equal = path1->u.pci.devfn == path2->u.pci.devfn; + break; + case DEVICE_PATH_PNP: + equal = (path1->u.pnp.port == path2->u.pnp.port) && + (path1->u.pnp.device == path2->u.pnp.device); + break; + case DEVICE_PATH_I2C: + equal = (path1->u.i2c.device == path2->u.i2c.device); + break; + default: + printk_err("Uknown device type: %d\n", path1->type); + break; + } + } + return equal; +} diff --git a/src/devices/pci_device.c b/src/devices/pci_device.c index 6af920e584..4db0102745 100644 --- a/src/devices/pci_device.c +++ b/src/devices/pci_device.c @@ -17,6 +17,7 @@ #include <device/device.h> #include <device/pci.h> #include <device/pci_ids.h> +#include <part/hard_reset.h> #include <part/fallback_boot.h> /** Given a device and register, read the size of the BAR for that register. @@ -39,8 +40,6 @@ static void pci_get_resource(struct device *dev, struct resource *resource, unsi resource->index = index; addr = pci_read_config32(dev, index); - if (addr == 0xffffffffUL) - return; /* FIXME: more consideration for 64-bit PCI devices, * we currently detect their size but otherwise @@ -71,9 +70,8 @@ static void pci_get_resource(struct device *dev, struct resource *resource, unsi if ((addr == size) && (addr == base)) { if (size != 0) { printk_debug( - "PCI: %02x:%02x.%01x register %02x(%08x), read-only ignoring it\n", - dev->bus->secondary, - PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), + "%s register %02x(%08x), read-only ignoring it\n", + dev_path(dev), index, addr); } resource->flags = 0; @@ -134,9 +132,8 @@ static void pci_get_resource(struct device *dev, struct resource *resource, unsi pci_write_config32(dev, index_hi, 0); } else { - printk_err("PCI: %02x:%02x.%01x Unable to handle 64-bit address\n", - dev->bus->secondary, - PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + printk_err("%s Unable to handle 64-bit address\n", + dev_path(dev)); resource->flags = IORESOURCE_PCI64; } } @@ -185,7 +182,7 @@ static void pci_bridge_read_bases(struct device *dev) dev->resource[reg].limit = 0xffffUL; dev->resource[reg].flags = IORESOURCE_IO | IORESOURCE_PCI_BRIDGE; dev->resource[reg].index = PCI_IO_BASE; - compute_allocate_resource(dev, &dev->resource[reg], + compute_allocate_resource(&dev->link[0], &dev->resource[reg], IORESOURCE_IO, IORESOURCE_IO); reg++; @@ -197,7 +194,7 @@ static void pci_bridge_read_bases(struct device *dev) dev->resource[reg].limit = 0xffffffffUL; dev->resource[reg].flags = IORESOURCE_MEM | IORESOURCE_PREFETCH | IORESOURCE_PCI_BRIDGE; dev->resource[reg].index = PCI_PREF_MEMORY_BASE; - compute_allocate_resource(dev, &dev->resource[reg], + compute_allocate_resource(&dev->link[0], &dev->resource[reg], IORESOURCE_MEM | IORESOURCE_PREFETCH, IORESOURCE_MEM | IORESOURCE_PREFETCH); reg++; @@ -210,7 +207,7 @@ static void pci_bridge_read_bases(struct device *dev) dev->resource[reg].limit = 0xffffffffUL; dev->resource[reg].flags = IORESOURCE_MEM | IORESOURCE_PCI_BRIDGE; dev->resource[reg].index = PCI_MEMORY_BASE; - compute_allocate_resource(dev, &dev->resource[reg], + compute_allocate_resource(&dev->link[0], &dev->resource[reg], IORESOURCE_MEM | IORESOURCE_PREFETCH, IORESOURCE_MEM); reg++; @@ -233,7 +230,7 @@ void pci_bus_read_resources(struct device *dev) { uint32_t addr; dev->resources = 0; - memset(&dev->resource[0], 0, sizeof(dev->resource)); + memset(&dev->resource, 0, sizeof(dev->resource)); pci_bridge_read_bases(dev); pci_read_bases(dev, 2); @@ -246,16 +243,14 @@ void pci_bus_read_resources(struct device *dev) static void pci_set_resource(struct device *dev, struct resource *resource) { unsigned long base, limit; - unsigned long bridge_align = PCI_MEM_BRIDGE_ALIGN; unsigned char buf[10]; - + unsigned long align; + /* Make certain the resource has actually been set */ if (!(resource->flags & IORESOURCE_SET)) { #if 1 - printk_err("ERROR: %02x:%02x.%01x %02x not allocated\n", - dev->bus->secondary, - PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), - resource->index); + printk_err("ERROR: %s %02x not allocated\n", + dev_path(dev), resource->index); #endif return; } @@ -263,23 +258,23 @@ static void pci_set_resource(struct device *dev, struct resource *resource) /* Only handle PCI memory and IO resources for now */ if (!(resource->flags & (IORESOURCE_MEM |IORESOURCE_IO))) return; - + if (resource->flags & IORESOURCE_MEM) { dev->command |= PCI_COMMAND_MEMORY; - bridge_align = PCI_MEM_BRIDGE_ALIGN; } if (resource->flags & IORESOURCE_IO) { dev->command |= PCI_COMMAND_IO; - bridge_align = PCI_IO_BRIDGE_ALIGN; } if (resource->flags & IORESOURCE_PCI_BRIDGE) { dev->command |= PCI_COMMAND_MASTER; } /* Get the base address */ base = resource->base; + /* Get the resource alignment */ + align = 1UL << resource->align; /* Get the limit (rounded up) */ - limit = base + ((resource->size + bridge_align - 1UL) & ~(bridge_align -1)) -1UL; + limit = base + ((resource->size + align - 1UL) & ~(align - 1UL)) -1UL; if (!(resource->flags & IORESOURCE_PCI_BRIDGE)) { /* @@ -300,7 +295,7 @@ static void pci_set_resource(struct device *dev, struct resource *resource) /* set the IO ranges * WARNING: we don't really do 32-bit addressing for IO yet! */ - compute_allocate_resource(dev, resource, + compute_allocate_resource(&dev->link[0], resource, IORESOURCE_IO, IORESOURCE_IO); pci_write_config8(dev, PCI_IO_BASE, base >> 8); pci_write_config8(dev, PCI_IO_LIMIT, limit >> 8); @@ -310,7 +305,7 @@ static void pci_set_resource(struct device *dev, struct resource *resource) else if (resource->index == PCI_MEMORY_BASE) { /* set the memory range */ - compute_allocate_resource(dev, resource, + compute_allocate_resource(&dev->link[0], resource, IORESOURCE_MEM | IORESOURCE_PREFETCH, IORESOURCE_MEM); pci_write_config16(dev, PCI_MEMORY_BASE, base >> 16); @@ -320,7 +315,7 @@ static void pci_set_resource(struct device *dev, struct resource *resource) /* set the prefetchable memory range * WARNING: we don't really do 64-bit addressing for prefetchable memory yet! */ - compute_allocate_resource(dev, resource, + compute_allocate_resource(&dev->link[0], resource, IORESOURCE_MEM | IORESOURCE_PREFETCH, IORESOURCE_MEM | IORESOURCE_PREFETCH); pci_write_config16(dev, PCI_PREF_MEMORY_BASE, base >> 16); @@ -334,13 +329,12 @@ static void pci_set_resource(struct device *dev, struct resource *resource) } buf[0] = '\0'; if (resource->flags & IORESOURCE_PCI_BRIDGE) { - sprintf(buf, "bus %d ", dev->secondary); + sprintf(buf, "bus %d ", dev->link[0].secondary); } printk_debug( - "PCI: %02x:%02x.%01x %02x <- [0x%08lx - 0x%08lx] %s%s\n", - dev->bus->secondary, - PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), + "%s %02x <- [0x%08lx - 0x%08lx] %s%s\n", + dev_path(dev), resource->index, resource->base, limit, buf, @@ -352,6 +346,7 @@ static void pci_set_resource(struct device *dev, struct resource *resource) void pci_dev_set_resources(struct device *dev) { struct resource *resource, *last; + unsigned link; uint8_t line; last = &dev->resource[dev->resources]; @@ -359,8 +354,12 @@ void pci_dev_set_resources(struct device *dev) for(resource = &dev->resource[0]; resource < last; resource++) { pci_set_resource(dev, resource); } - if (dev->children) { - assign_resources(dev); + for(link = 0; link < dev->links; link++) { + struct bus *bus; + bus = &dev->link[link]; + if (bus->children) { + assign_resources(bus); + } } /* set a default latency timer */ @@ -380,15 +379,39 @@ void pci_dev_set_resources(struct device *dev) pci_write_config8(dev, PCI_CACHE_LINE_SIZE, 64 >> 2); } +void pci_dev_enable_resources(struct device *dev) +{ + uint16_t command; + command = pci_read_config16(dev, PCI_COMMAND); + command |= dev->command; + printk_debug("%s cmd <- %02x\n", dev_path(dev), command); + pci_write_config16(dev, PCI_COMMAND, command); + + enable_childrens_resources(dev); +} + +void pci_bus_enable_resources(struct device *dev) +{ + uint16_t ctrl; + ctrl = pci_read_config16(dev, PCI_BRIDGE_CONTROL); + ctrl |= dev->link[0].bridge_ctrl; + printk_debug("%s bridge ctrl <- %04x\n", dev_path(dev), ctrl); + pci_write_config16(dev, PCI_BRIDGE_CONTROL, ctrl); + + pci_dev_enable_resources(dev); +} + struct device_operations default_pci_ops_dev = { - .read_resources = pci_dev_read_resources, - .set_resources = pci_dev_set_resources, + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, .init = 0, .scan_bus = 0, }; struct device_operations default_pci_ops_bus = { - .read_resources = pci_bus_read_resources, - .set_resources = pci_dev_set_resources, + .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, }; @@ -406,10 +429,10 @@ static void set_pci_ops(struct device *dev) (driver->device == dev->device)) { dev->ops = driver->ops; #if 1 - printk_debug("PCI: %02x:%02x.%01x [%04x/%04x] ops\n", - dev->bus->secondary, - PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), - driver->vendor, driver->device + printk_debug("%s [%04x/%04x] %sops\n", + dev_path(dev), + driver->vendor, driver->device, + (driver->ops->scan_bus?"bus ":"") ); #endif return; @@ -429,10 +452,9 @@ static void set_pci_ops(struct device *dev) break; default: bad: - printk_err("PCI: %02x:%02x.%01x [%04x/%04x/%06x] has unknown header " + printk_err("%s [%04x/%04x/%06x] has unknown header " "type %02x, ignoring.\n", - dev->bus->secondary, - PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), + dev_path(dev), dev->vendor, dev->device, dev->class >> 8, dev->hdr_type); } @@ -449,7 +471,7 @@ static struct device *pci_scan_get_dev(struct device **list, unsigned int devfn) { struct device *dev = 0; for(; *list; list = &(*list)->sibling) { - if ((*list)->devfn == devfn) { + if ((*list)->path.u.pci.devfn == devfn) { /* Unlink from the list */ dev = *list; *list = (*list)->sibling; @@ -457,6 +479,20 @@ static struct device *pci_scan_get_dev(struct device **list, unsigned int devfn) break; } } + if (dev) { + device_t 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 it's parent. */ + if (child) { + child->sibling = dev; + } else { + dev->bus->children = dev; + } + } + return dev; } @@ -468,7 +504,8 @@ void assign_id_set_links(device_t dev, uint8_t *pos, static const uint8_t link_width_to_pow2[]= { 3, 4, 0, 5, 1, 2, 0, 0 }; static const uint8_t pow2_to_link_width[] = { 0x7, 4, 5, 0, 1, 3 }; uint16_t flags; - struct device last, prev_bus, previous; + struct bus prev_bus; + struct device last, previous; unsigned count; uint8_t present_width_cap; uint16_t present_freq_cap; @@ -499,14 +536,17 @@ void assign_id_set_links(device_t dev, uint8_t *pos, /* calculate the previous pos for the host */ *previous_pos = 0x80; previous.bus = &prev_bus; - previous.devfn = 0x18 << 3; + previous.path.type = DEVICE_PATH_PCI; + previous.path.u.pci.devfn = 0x18 << 3; #warning "FIXME we should not hard code this!" } else { - previous.bus = bus; - previous.devfn = previous_unitid << 3; + previous.bus = bus; + previous.path.type = DEVICE_PATH_PCI; + previous.path.u.pci.devfn = previous_unitid << 3; } - last.bus = bus; - last.devfn = last_unitid << 3; + last.bus = bus; + last.path.type = DEVICE_PATH_PCI; + last.path.u.pci.devfn = last_unitid << 3; /* Set link width and frequency */ present_freq_cap = pci_read_config16(&last, (*pos) + PCI_HT_CAP_SLAVE_FREQ_CAP0); @@ -614,16 +654,20 @@ void assign_id_set_links(device_t dev, uint8_t *pos, #define HYPERTRANSPORT_SUPPORT 1 /** Scan the pci bus devices and bridges. - * @param pci_bus pointer to the bus structure + * @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 device *bus, unsigned int max) +unsigned int pci_scan_bus(struct bus *bus, + unsigned min_devfn, unsigned max_devfn, + unsigned int max) { unsigned int devfn; - struct device *dev, **bus_last; - struct device *old_devices; - struct device *child; + device_t dev; + device_t old_devices; + device_t child; #if HYPERTRANSPORT_SUPPORT unsigned next_unitid, last_unitid, previous_unitid; int reset_needed = 0; @@ -634,7 +678,6 @@ unsigned int pci_scan_bus(struct device *bus, unsigned int max) old_devices = bus->children; bus->children = 0; - bus_last = &bus->children; post_code(0x24); @@ -643,15 +686,16 @@ unsigned int pci_scan_bus(struct device *bus, unsigned int max) /* Spin through the devices and collapse any early * hypertransport enumeration. */ - for(devfn = 0; devfn <= 0xff; devfn += 8) { + for(devfn = min_devfn; devfn <= max_devfn; devfn += 8) { struct device dummy; uint32_t id; uint8_t hdr_type, pos; - dummy.bus = bus; - dummy.devfn = devfn; + dummy.bus = bus; + dummy.path.type = DEVICE_PATH_PCI; + dummy.path.u.pci.devfn = devfn; id = pci_read_config32(&dummy, PCI_VENDOR_ID); - if (id == 0xffffffff || id == 0x00000000 || - id == 0x0000ffff || id == 0xffff0000) { + if ( (id == 0xffffffff) || (id == 0x00000000) || + (id == 0x0000ffff) || (id == 0xffff0000)) { continue; } hdr_type = pci_read_config8(&dummy, PCI_HEADER_TYPE); @@ -699,8 +743,9 @@ unsigned int pci_scan_bus(struct device *bus, unsigned int max) last_unitid = next_unitid; /* Read the next unassigned device off the stack */ - dummy.bus = bus; - dummy.devfn = 0; + dummy.bus = bus; + dummy.path.type = DEVICE_PATH_PCI; + dummy.path.u.pci.devfn = 0; id = pci_read_config32(&dummy, PCI_VENDOR_ID); /* If the chain is enumerated quit */ if (id == 0xffffffff || id == 0x00000000 || @@ -745,79 +790,78 @@ unsigned int pci_scan_bus(struct device *bus, unsigned int max) /* probe all devices on this bus with some optimization for non-existance and single funcion devices */ - for (devfn = 0; devfn <= 0xff; devfn++) { - struct device dummy; + for (devfn = min_devfn; devfn <= max_devfn; devfn++) { uint32_t id, class; uint8_t hdr_type; /* First thing setup the device structure */ dev = pci_scan_get_dev(&old_devices, devfn); - dummy.bus = bus; - dummy.devfn = devfn; - id = pci_read_config32(&dummy, PCI_VENDOR_ID); - /* some broken boards return 0 if a slot is empty: */ - if (!dev && - (id == 0xffffffff || id == 0x00000000 || - id == 0x0000ffff || id == 0xffff0000)) { - printk_spew("PCI: devfn 0x%x, bad id 0x%x\n", devfn, id); - if (PCI_FUNC(devfn) == 0x00) { - /* if this is a function 0 device and it is not present, - skip to next device */ - devfn += 0x07; - } - /* multi function device, skip to next function */ - continue; - } - hdr_type = pci_read_config8(&dummy, PCI_HEADER_TYPE); - class = pci_read_config32(&dummy, PCI_CLASS_REVISION); - + /* Detect if a device is present */ if (!dev) { - if ((dev = malloc(sizeof(*dev))) == 0) { - printk_err("PCI: out of memory.\n"); + struct device dummy; + dummy.bus = bus; + dummy.path.type = DEVICE_PATH_PCI; + dummy.path.u.pci.devfn = devfn; + id = pci_read_config32(&dummy, PCI_VENDOR_ID); + /* some broken boards return 0 if a slot is empty: */ + if ( (id == 0xffffffff) || (id == 0x00000000) || + (id == 0x0000ffff) || (id == 0xffff0000)) + { + printk_spew("PCI: devfn 0x%x, bad id 0x%x\n", devfn, id); + if (PCI_FUNC(devfn) == 0x00) { + /* if this is a function 0 device and it is not present, + skip to next device */ + devfn += 0x07; + } + /* multi function device, skip to next function */ continue; } - memset(dev, 0, sizeof(*dev)); - dev->bus = bus; - dev->devfn = devfn; - 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; - - /* If we don't have prior information about this device enable it */ - dev->enable = 1; + dev = alloc_dev(bus, &dummy.path); + } + else { + /* Run the magic enable/disable sequence for the device */ + if (dev->ops && dev->ops->enable) { + dev->ops->enable(dev); + } + /* Now read the vendor and device id */ + id = pci_read_config32(dev, PCI_VENDOR_ID); } + /* 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; + /* 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. */ - set_pci_ops(dev); - /* Kill the device if we don't have some pci operations for it */ if (!dev->ops) { - free(dev); - continue; - } - - /* Now run the magic enable/disable sequence for the device */ - if (dev->ops && dev->ops->enable) { - dev->ops->enable(dev); + set_pci_ops(dev); + /* Error if we don't have some pci operations for it */ + if (!dev->ops) { + printk_err("%s No device operations\n", + dev_path(dev)); + continue; + } + /* Now run the magic enable/disable sequence for the device */ + if (dev->ops && dev->ops->enable) { + dev->ops->enable(dev); + } } - printk_debug("PCI: %02x:%02x.%01x [%04x/%04x] %s\n", - bus->secondary, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), + printk_debug("%s [%04x/%04x] %s\n", + dev_path(dev), dev->vendor, dev->device, dev->enable?"enabled": "disabled"); - /* Put it into the global device chain. */ - append_device(dev); - - /* Now insert it into the list of devices held by the parent bus. */ - *bus_last = dev; - bus_last = &dev->sibling; - if (PCI_FUNC(devfn) == 0x00 && (hdr_type & 0x80) != 0x80) { /* if this is not a multi function device, don't waste time probe another function. Skip to next device. */ @@ -827,10 +871,10 @@ unsigned int pci_scan_bus(struct device *bus, unsigned int max) post_code(0x25); for(child = bus->children; child; child = child->sibling) { - if (!child->ops->scan_bus) + if (!child->ops->scan_bus) { continue; + } max = child->ops->scan_bus(child, max); - } /* * We've scanned the bus and so we know all about what's on @@ -848,10 +892,15 @@ unsigned int pci_scan_bus(struct device *bus, unsigned int max) * @param pci_bus pointer to the bus structure * @return The maximum bus number found, after scanning all subordinate busses */ -unsigned int pci_scan_bridge(struct device *bus, unsigned int max) +unsigned int pci_scan_bridge(struct device *dev, unsigned int max) { + struct bus *bus; uint32_t buses; uint16_t cr; + + bus = &dev->link[0]; + dev->links = 1; + /* 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 @@ -860,28 +909,28 @@ unsigned int pci_scan_bridge(struct device *bus, unsigned int max) bus->subordinate = 0xff; /* Clear all status bits and turn off memory, I/O and master enables. */ - cr = pci_read_config16(bus, PCI_COMMAND); - pci_write_config16(bus, PCI_COMMAND, 0x0000); - pci_write_config16(bus, PCI_STATUS, 0xffff); + 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(bus, PCI_PRIMARY_BUS); + 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) (bus->bus->secondary) << 0) | + buses |= (((unsigned int) (dev->bus->secondary) << 0) | ((unsigned int) (bus->secondary) << 8) | ((unsigned int) (bus->subordinate) << 16)); - pci_write_config32(bus, PCI_PRIMARY_BUS, buses); + pci_write_config32(dev, PCI_PRIMARY_BUS, buses); /* Now we can scan all subordinate buses i.e. the bus hehind the bridge */ - max = pci_scan_bus(bus, max); + max = pci_scan_bus(bus, 0x00, 0xff, max); /* We know the number of buses behind this bridge. Set the subordinate * bus number to its real value @@ -889,55 +938,8 @@ unsigned int pci_scan_bridge(struct device *bus, unsigned int max) bus->subordinate = max; buses = (buses & 0xff00ffff) | ((unsigned int) (bus->subordinate) << 16); - pci_write_config32(bus, PCI_PRIMARY_BUS, buses); - pci_write_config16(bus, PCI_COMMAND, cr); + pci_write_config32(dev, PCI_PRIMARY_BUS, buses); + pci_write_config16(dev, PCI_COMMAND, cr); return max; } - - -static void pci_root_read_resources(struct device *bus) -{ - int res = 0; - /* Initialize the system wide io space constraints */ - bus->resource[res].base = 0x400; - bus->resource[res].size = 0; - bus->resource[res].align = 0; - bus->resource[res].gran = 0; - bus->resource[res].limit = 0xffffUL; - bus->resource[res].flags = IORESOURCE_IO; - bus->resource[res].index = PCI_IO_BASE; - compute_allocate_resource(bus, &bus->resource[res], - IORESOURCE_IO, IORESOURCE_IO); - res++; - - /* Initialize the system wide memory resources constraints */ - bus->resource[res].base = 0; - bus->resource[res].size = 0; - bus->resource[res].align = 0; - bus->resource[res].gran = 0; - bus->resource[res].limit = 0xffffffffUL; - bus->resource[res].flags = IORESOURCE_MEM; - bus->resource[res].index = PCI_MEMORY_BASE; - compute_allocate_resource(bus, &bus->resource[res], - IORESOURCE_MEM, IORESOURCE_MEM); - res++; - - bus->resources = res; -} -static void pci_root_set_resources(struct device *bus) -{ - compute_allocate_resource(bus, - &bus->resource[0], IORESOURCE_IO, IORESOURCE_IO); - compute_allocate_resource(bus, - &bus->resource[1], IORESOURCE_MEM, IORESOURCE_MEM); - assign_resources(bus); -} - -struct device_operations default_pci_ops_root = { - .read_resources = pci_root_read_resources, - .set_resources = pci_root_set_resources, - .init = 0, - .scan_bus = pci_scan_bus, -}; - diff --git a/src/devices/root_device.c b/src/devices/root_device.c new file mode 100644 index 0000000000..c05a2cd015 --- /dev/null +++ b/src/devices/root_device.c @@ -0,0 +1,127 @@ +#include <console/console.h> +#include <device/device.h> +#include <device/pci.h> + +/** + * Read the resources for the root device, + * that encompase the resources for the entire system. + * @param root Pointer to the device structure for the system root device + */ +void root_dev_read_resources(device_t root) +{ + int res = 0; + + /* Initialize the system wide io space constraints */ + root->resource[res].base = 0x400; + root->resource[res].size = 0; + root->resource[res].align = 0; + root->resource[res].gran = 0; + root->resource[res].limit = 0xffffUL; + root->resource[res].flags = IORESOURCE_IO; + root->resource[res].index = 0; + compute_allocate_resource(&root->link[0], &root->resource[res], + IORESOURCE_IO, IORESOURCE_IO); + res++; + + /* Initialize the system wide memory resources constraints */ + root->resource[res].base = 0; + root->resource[res].size = 0; + root->resource[res].align = 0; + root->resource[res].gran = 0; + root->resource[res].limit = 0xffffffffUL; + root->resource[res].flags = IORESOURCE_MEM; + root->resource[res].index = 1; + compute_allocate_resource(&root->link[0], &root->resource[res], + IORESOURCE_MEM, IORESOURCE_MEM); + res++; + + root->resources = res; +} + +/** + * Write the resources for the root device, + * and every device under it which are all of the devices. + * @param root Pointer to the device structure for the system root device + */ +void root_dev_set_resources(device_t root) +{ + struct bus *bus; + bus = &root->link[0]; + compute_allocate_resource(bus, + &root->resource[0], IORESOURCE_IO, IORESOURCE_IO); + compute_allocate_resource(bus, + &root->resource[1], IORESOURCE_MEM, IORESOURCE_MEM); + assign_resources(bus); +} + +/** + * Walk through devices on the motherboard and scan for devices behind + * them. + * @param root Pointer to the device structure for the system root device + * @param max Maximum bus number allowed in the system. + * @return Largest bus number used. + */ +unsigned int walk_static_devices(device_t bus, unsigned int max) +{ + device_t child; + unsigned link; + for(link = 0; link < bus->links; link++) { + for(child = bus->link[link].children; child; child = child->sibling) { + if (child->ops && child->ops->enable) { + child->ops->enable(child); + } + printk_debug("%s %s\n", + dev_path(child), + child->enable?"enabled": "disabled"); + } + } + for(link = 0; link < bus->links; link++) { + for(child = bus->link[link].children; child; child = child->sibling) { + if (!child->ops || !child->ops->scan_bus) + continue; + printk_debug("%s scanning...\n", dev_path(child)); + max = child->ops->scan_bus(child, max); + } + } + return max; +} + +void enable_childrens_resources(device_t dev) +{ + unsigned link; + for(link = 0; link < dev->links; link++) { + device_t child; + for(child = dev->link[link].children; child; child = child->sibling) { + enable_resources(child); + } + } +} + +unsigned int root_dev_scan_pci_bus(device_t root, unsigned int max) +{ + return pci_scan_bus(&root->link[0], 0, 0xff, max); +} + +struct device_operations default_dev_ops_root = { + .read_resources = root_dev_read_resources, + .set_resources = root_dev_set_resources, + .enable_resources = enable_childrens_resources, + .init = 0, + .scan_bus = root_dev_scan_pci_bus, +}; + +/** + * This is the root of the device tree. A PCI tree always has + * one bus, bus 0. Bus 0 contains devices and bridges. + */ +struct device dev_root = { + .ops = &default_dev_ops_root, + .bus = &dev_root.link[0], + .links = 1, + .link = { + [0] = { + .dev = &dev_root, + .link = 0, + }, + }, +}; |