summaryrefslogtreecommitdiffstats
path: root/arch/alpha/kernel/pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/alpha/kernel/pci.c')
-rw-r--r--arch/alpha/kernel/pci.c140
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();