patch-2.3.99-pre6 linux/arch/alpha/kernel/pci.c

Next file: linux/arch/alpha/kernel/process.c
Previous file: linux/arch/alpha/kernel/irq.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.99-pre5/linux/arch/alpha/kernel/pci.c linux/arch/alpha/kernel/pci.c
@@ -110,10 +110,15 @@
 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 @@
 		 * 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 @@
 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 @@
 			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 @@
 	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 @@
 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 @@
 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 @@
 	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 @@
 		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();

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)