patch to support peer PCI host bridges

Steve Sweeney (smcs@ricochet.net)
Thu, 23 Apr 1998 22:35:46 -0700


Hi -

I'm new to Linux and this is my first post. I am running 2.1.97 on a Micron
Powerdigm Xsu and found that the pci_scan_bus routine in drivers/pci/pci.c
does not properly support peer host-PCI bridges (multiple bridges attached to
the host bus). This is not a common configuration but the Powerdigm at least
does implement peer host bridges. The peer host bridges must be configured
with unique bus numbers in order to distinguish configuration space accesses
(since the configuration registers overlay each other in memory in this
situation). The patch follows (apologies if it is not in the correct format):

====cut=====

--- linux/drivers/pci/pci.c Fri Apr 17 21:58:48 1998
+++ linux-modpci-2.1.97/drivers/pci/pci.c Thu Apr 23 18:05:42 1998
@@ -92,7 +92,7 @@

__initfunc(unsigned int pci_scan_bus(struct pci_bus *bus))
{
- unsigned int devfn, l, max, class;
+ unsigned int devfn, l, max, class, hostbridges = 0;
unsigned char cmd, irq, tmp, hdr_type, is_multi = 0;
struct pci_dev *dev;
struct pci_bus *child;
@@ -289,6 +289,79 @@
}
pcibios_write_config_word(bus->number, devfn, PCI_COMMAND, cr);
}
+
+ /* if it's a host bridge, keep track of how many we've seen on this bus.
+ multiple ones on the same bus need to be assigned their own bus number
+ */
+ if ( (class >> 8 == PCI_CLASS_BRIDGE_HOST) && (++hostbridges > 1) ) {
+ unsigned int buses;
+ unsigned short cr;
+
+ /*
+ * Insert it into the tree of buses.
+ */
+ child = kmalloc(sizeof(*child), GFP_ATOMIC);
+ memset(child, 0, sizeof(*child));
+ bus->next =child;
+ child->self = dev;
+
+ /*
+ * Set up the primary, secondary and subordinate
+ * bus numbers.
+ */
+ child->number = child->primary = child->secondary = ++max;
+ child->subordinate = 0xff;
+ /*
+ * Clear all status bits and turn off memory,
+ * I/O and master enables.
+ */
+ pcibios_read_config_word(bus->number, devfn, PCI_COMMAND, &cr);
+ pcibios_write_config_word(bus->number, devfn, PCI_COMMAND, 0x0000);
+ pcibios_write_config_word(bus->number, devfn, PCI_STATUS, 0xffff);
+ /*
+ * Read the existing primary/secondary/subordinate bus
+ * number configuration to determine if the PCI bridge
+ * has already been configured by the system. If so,
+ * do not modify the configuration, merely note it.
+ */
+ pcibios_read_config_dword(bus->number, devfn, PCI_PRIMARY_BUS, &buses);
+ if ((buses & 0xFFFFFF) != 0)
+ {
+ unsigned int cmax;
+
+ child->primary = buses & 0xFF;
+ child->secondary = (buses >> 8) & 0xFF;
+ child->subordinate = (buses >> 16) & 0xFF;
+ child->number = child->secondary;
+ cmax = pci_scan_bus(child);
+ if (cmax > max) max = cmax;
+ }
+ else
+ {
+ /*
+ * Configure the bus numbers for this bridge:
+ */
+ buses &= 0xff000000;
+ buses |=
+ (((unsigned int)(child->primary) << 0) |
+ ((unsigned int)(child->secondary) << 8) |
+ ((unsigned int)(child->subordinate) << 16));
+ pcibios_write_config_dword(bus->number, devfn, PCI_PRIMARY_BUS, buses);
+ /*
+ * Now we can scan all subordinate buses:
+ */
+ max = pci_scan_bus(child);
+ /*
+ * Set the subordinate bus number to its real
+ * value:
+ */
+ child->subordinate = max;
+ buses = (buses & 0xff00ffff)
+ | ((unsigned int)(child->subordinate) << 16);
+ pcibios_write_config_dword(bus->number, devfn, PCI_PRIMARY_BUS, buses);
+ }
+ pcibios_write_config_word(bus->number, devfn, PCI_COMMAND, cr);
+ }
}
/*
* We've scanned the bus and so we know all about what's on
====cut=====

- Steven M. Sweeney
<smcs@ricochet.net>

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu