From: Matthew Wilcox [moved from linux-scsi to linux-pci since this really isn't scsi-related] On Sun, Nov 09, 2003 at 11:51:36AM -0500, Doug Ledford wrote: > I can tell you what's going on here. This is a 450NX based > motherboard. The 450NX chipset from Intel was the first chipset to have > peer PCI busses. For backwards compatibility, some machine makers > hacked their PCI BIOS to have a fake bridge device on PCI bus 0 that > points to the same bus number as the peer bus. This way if the OS > didn't know about the peer bus registers it would still find the devices > by scanning behind the bridge. Ah, thanks Doug. That saved me a few hours of debugging and head-scratching ;-) > In this case we are scanning behind this > fake bridge and then also scanning based upon the peer bus registers in > the chipset, and as a result we are finding the device twice. In order > to fix this problem you need to change the peer bus quirk code for the > 450NX chipset to scan the list of bus 0 devices looking for a bridge > that has the same config as the peer bus registers and if so delete the > bridge from the list. That will avoid double scanning and will avoid > having the PCI code try and configure sub busses via a fake bridge when > it should do all configurations via the 450NX peer bus registers. I agree. I considered some other possibilities like having pci_scan_bridge() check for duplicate busses and return if it detects them, but that would leave some pcipci bridges in the list that really don't exist. Here's a patch that compiles ... comments? arch/i386/pci/fixup.c | 33 +++++++++++++++++++++++++++++---- 1 files changed, 29 insertions(+), 4 deletions(-) diff -puN arch/i386/pci/fixup.c~i450nx-scanning-fix arch/i386/pci/fixup.c --- 25/arch/i386/pci/fixup.c~i450nx-scanning-fix 2003-11-09 16:58:55.000000000 -0800 +++ 25-akpm/arch/i386/pci/fixup.c 2003-11-09 16:58:55.000000000 -0800 @@ -6,27 +6,52 @@ #include #include "pci.h" +static void __devinit i450nx_scan_bus(struct pci_bus *parent, u8 busnr) +{ + struct list_head *tmp; + + pci_scan_bus(busnr, &pci_root_ops, NULL); + + list_for_each(tmp, &parent->children) { + u8 childnr; + struct pci_dev *dev = pci_dev_b(tmp); + + if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) + continue; + pci_read_config_byte(dev, PCI_PRIMARY_BUS, &childnr); + if (childnr != busnr) + continue; + + printk(KERN_WARNING "PCI: Removing fake PCI bridge %s\n", + pci_name(dev)); + pci_remove_bus_device(dev); + break; + } +} static void __devinit pci_fixup_i450nx(struct pci_dev *d) { /* * i450NX -- Find and scan all secondary buses on all PXB's. + * Some manufacturers added fake PCI-PCI bridges that also point + * to the peer busses. Look for them and delete them. */ int pxb, reg; u8 busno, suba, subb; - printk(KERN_WARNING "PCI: Searching for i450NX host bridges on %s\n", pci_name(d)); + printk(KERN_NOTICE "PCI: Searching for i450NX host bridges on %s\n", pci_name(d)); reg = 0xd0; - for(pxb=0; pxb<2; pxb++) { + for (pxb = 0; pxb < 2; pxb++) { pci_read_config_byte(d, reg++, &busno); pci_read_config_byte(d, reg++, &suba); pci_read_config_byte(d, reg++, &subb); DBG("i450NX PXB %d: %02x/%02x/%02x\n", pxb, busno, suba, subb); if (busno) - pci_scan_bus(busno, &pci_root_ops, NULL); /* Bus A */ + i450nx_scan_bus(d->bus, busno); /* Bus A */ if (suba < subb) - pci_scan_bus(suba+1, &pci_root_ops, NULL); /* Bus B */ + i450nx_scan_bus(d->bus, suba+1); /* Bus B */ } + pcibios_last_bus = -1; } _