diff options
Diffstat (limited to 'arch/alpha/kernel/pci.c')
-rw-r--r-- | arch/alpha/kernel/pci.c | 140 |
1 files changed, 132 insertions, 8 deletions
diff --git a/arch/alpha/kernel/pci.c b/arch/alpha/kernel/pci.c index 81178976a..f9a79c1d6 100644 --- a/arch/alpha/kernel/pci.c +++ b/arch/alpha/kernel/pci.c @@ -110,10 +110,15 @@ void pcibios_align_resource(void *data, struct resource *res, unsigned long size) { struct pci_dev *dev = data; + struct pci_controler *hose = dev->sysdata; unsigned long alignto; unsigned long start = res->start; if (res->flags & IORESOURCE_IO) { + /* Make sure we start at our min on all hoses */ + if (start - hose->io_space->start < PCIBIOS_MIN_IO) + start = PCIBIOS_MIN_IO + hose->io_space->start; + /* * Aligning to 0x800 rather than the minimum base of * 0x400 is an attempt to avoid having devices in @@ -121,11 +126,18 @@ pcibios_align_resource(void *data, struct resource *res, unsigned long size) * probes for EISA cards. * * Adaptecs, especially, resent such intrusions. + * + * The de4x5 driver has the eisa probe conditionalized + * out for Alpha, so lower the minimum base back to 0x400. */ - alignto = MAX(0x800, size); + alignto = MAX(0x400, size); start = ALIGN(start, alignto); } else if (res->flags & IORESOURCE_MEM) { + /* Make sure we start at our min on all hoses */ + if (start - hose->mem_space->start < PCIBIOS_MIN_MEM) + start = PCIBIOS_MIN_MEM + hose->io_space->start; + /* * The following holds at least for the Low Cost * Alpha implementation of the PCI interface: @@ -230,7 +242,7 @@ void __init pcibios_fixup_device_resources(struct pci_dev *dev, struct pci_bus *bus) { /* Update device resources. */ - + struct pci_controler *hose = (struct pci_controler *)bus->sysdata; int i; for (i = 0; i < PCI_NUM_RESOURCES; i++) { @@ -238,10 +250,10 @@ pcibios_fixup_device_resources(struct pci_dev *dev, struct pci_bus *bus) continue; if (dev->resource[i].flags & IORESOURCE_IO) pcibios_fixup_resource(&dev->resource[i], - bus->resource[0]); + hose->io_space); else if (dev->resource[i].flags & IORESOURCE_MEM) pcibios_fixup_resource(&dev->resource[i], - bus->resource[1]); + hose->mem_space); } pcibios_assign_special(dev); } @@ -257,6 +269,12 @@ pcibios_fixup_bus(struct pci_bus *bus) bus->resource[0] = hose->io_space; bus->resource[1] = hose->mem_space; + /* If this is a bridge, get the current bases */ + if (bus->self) { + pci_read_bridge_bases(bus); + pcibios_fixup_device_resources(bus->self, bus->parent); + } + for (ln = bus->devices.next; ln != &bus->devices; ln = ln->next) { struct pci_dev *dev = pci_dev_b(ln); if ((dev->class >> 8) != PCI_CLASS_BRIDGE_PCI) @@ -268,10 +286,25 @@ void __init pcibios_update_resource(struct pci_dev *dev, struct resource *root, struct resource *res, int resource) { + struct pci_controler *hose = dev->sysdata; int where; u32 reg; + if (resource < PCI_ROM_RESOURCE) where = PCI_BASE_ADDRESS_0 + (resource * 4); + else if (resource == PCI_ROM_RESOURCE) + where = dev->rom_base_reg; + else { + /* Don't update non-standard resources here */ + return; + } + + /* Point root at the hose root */ + if (res->flags & IORESOURCE_IO) + root = hose->io_space; + if (res->flags & IORESOURCE_MEM) + root = hose->mem_space; + reg = (res->start - root->start) | (res->flags & 0xf); pci_write_config_dword(dev, where, reg); if ((res->flags & (PCI_BASE_ADDRESS_SPACE @@ -319,10 +352,12 @@ void __init pcibios_fixup_pbus_ranges(struct pci_bus * bus, struct pbus_set_ranges_data * ranges) { - ranges->io_start -= bus->resource[0]->start; - ranges->io_end -= bus->resource[0]->start; - ranges->mem_start -= bus->resource[1]->start; - ranges->mem_end -= bus->resource[1]->start; + struct pci_controler *hose = (struct pci_controler *)bus->sysdata; + + ranges->io_start -= hose->io_space->start; + ranges->io_end -= hose->io_space->start; + ranges->mem_start -= hose->mem_space->start; + ranges->mem_end -= hose->mem_space->start; } int @@ -332,6 +367,94 @@ pcibios_enable_device(struct pci_dev *dev) return 0; } +#define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1)) + +static void __init +pcibios_size_bridge(struct pci_bus *bus, struct pbus_set_ranges_data *outer) +{ + struct pbus_set_ranges_data inner; + struct pci_dev *dev; + struct pci_dev *bridge = bus->self; + struct pci_controler *hose = bus->sysdata; + struct list_head *ln; + + if (!bridge) + return; /* host bridge, nothing to do */ + + /* set reasonable default locations for pcibios_align_resource */ + inner.io_start = hose->io_space->start + PCIBIOS_MIN_IO; + inner.mem_start = hose->mem_space->start + PCIBIOS_MIN_MEM; + inner.io_end = inner.io_start; + inner.mem_end = inner.mem_start; + + /* Collect information about how our direct children are layed out. */ + for (ln=bus->devices.next; ln != &bus->devices; ln=ln->next) { + int i; + dev = pci_dev_b(ln); + + /* Skip bridges for now */ + if (dev->class >> 8 == PCI_CLASS_BRIDGE_PCI) + continue; + + for (i = 0; i < PCI_NUM_RESOURCES; i++) { + struct resource res; + unsigned long size; + + memcpy(&res, &dev->resource[i], sizeof(res)); + size = res.end - res.start + 1; + + if (res.flags & IORESOURCE_IO) { + res.start = inner.io_end; + pcibios_align_resource(dev, &res, size); + inner.io_end = res.start + size; + } else if (res.flags & IORESOURCE_MEM) { + res.start = inner.mem_end; + pcibios_align_resource(dev, &res, size); + inner.mem_end = res.start + size; + } + } + } + + /* And for all of the subordinate busses. */ + for (ln=bus->children.next; ln != &bus->children; ln=ln->next) + pcibios_size_bridge(pci_bus_b(ln), &inner); + + /* turn the ending locations into sizes (subtract start) */ + inner.io_end -= inner.io_start; + inner.mem_end -= inner.mem_start; + + /* Align the sizes up by bridge rules */ + inner.io_end = ROUND_UP(inner.io_end, 4*1024); + inner.mem_end = ROUND_UP(inner.mem_end, 1*1024*1024); + + /* Adjust the bridge's allocation requirements */ + bridge->resource[0].end = bridge->resource[0].start + inner.io_end; + bridge->resource[1].end = bridge->resource[1].start + inner.mem_end; + + /* adjust parent's resource requirements */ + if (outer) { + outer->io_end = ROUND_UP(outer->io_end, 4*1024); + outer->io_end += inner.io_end; + + outer->mem_end = ROUND_UP(outer->mem_end, 1*1024*1024); + outer->mem_end += inner.mem_end; + } +} + +#undef ROUND_UP + +static void __init +pcibios_size_bridges(void) +{ + struct list_head *ln1, *ln2; + + for(ln1=pci_root_buses.next; ln1 != &pci_root_buses; ln1=ln1->next) + for(ln2 = pci_bus_b(ln1)->children.next; + ln2 != &pci_bus_b(ln1)->children; + ln2 = ln2->next) + pcibios_size_bridge(pci_bus_b(ln2), NULL); +} + void __init common_init_pci(void) { @@ -349,6 +472,7 @@ common_init_pci(void) next_busno += 1; } + pcibios_size_bridges(); pci_assign_unassigned_resources(); pci_fixup_irqs(alpha_mv.pci_swizzle, alpha_mv.pci_map_irq); pci_set_bus_ranges(); |