ChangeSet 1.1384, 2003/10/29 15:20:38-08:00, ink@jurassic.park.msu.ru [PATCH] PCI: fix bug in pci_setup_bridge() This bug prevents Alphas with older firmware from booting if there is a card with PCI-PCI bridge that supports 32-bit IO. This has happened on AS2100 with a quad-tulip card, for example: - initially, the I/O window of 21152 bridge was 0x10000-0x10fff, as set up by firmware; - pci_setup_bridge() is going to change this, say, to 0xa000-0xafff: first, it updates PCI_IO_BASE_UPPER16 and PCI_IO_LIMIT_UPPER16 registers, so that IO window temporarily is at 0x0000-0x0fff, which effectively blocks up all legacy IO ports in the lower 4K range, such as serial, floppy, RTC an so on; does debugging printk - machine dies here with recursive machine checks as the serial console has gone. Moving (or disabling) the debugging printk is not a solution - there is possibility that timer interrupt (which might access RTC ports) occurs between writes to lower and upper parts of the base/limit registers. The patch temporarily disables the IO window of the bridge by setting PCI_IO_BASE_UPPER16 > PCI_IO_LIMIT_UPPER16 before doing an update. It's safe, as we don't have any active IO behind the bridge at this point. Also, it's a NOP for bridges with 16-bit-only IO. Similar (but simpler, as we always clear upper 32 bits) fix for 64-bit prefetchable MMIO range. drivers/pci/setup-bus.c | 30 +++++++++++++++++++++--------- 1 files changed, 21 insertions(+), 9 deletions(-) diff -Nru a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c --- a/drivers/pci/setup-bus.c Wed Oct 29 16:45:17 2003 +++ b/drivers/pci/setup-bus.c Wed Oct 29 16:45:17 2003 @@ -132,13 +132,19 @@ PCI-to-PCI Bridge Architecture Specification rev. 1.1 (1998) requires that if there is no I/O ports or memory behind the bridge, corresponding range must be turned off by writing base - value greater than limit to the bridge's base/limit registers. */ + value greater than limit to the bridge's base/limit registers. + + Note: care must be taken when updating I/O base/limit registers + of bridges which support 32-bit I/O. This update requires two + config space writes, so it's quite possible that an I/O window of + the bridge will have some undesirable address (e.g. 0) after the + first write. Ditto 64-bit prefetchable MMIO. */ static void __devinit pci_setup_bridge(struct pci_bus *bus) { struct pci_dev *bridge = bus->self; struct pci_bus_region region; - u32 l; + u32 l, io_upper16; DBGC((KERN_INFO "PCI: Bus %d, bridge: %s\n", bus->number, pci_name(bridge))); @@ -151,20 +157,22 @@ l |= (region.start >> 8) & 0x00f0; l |= region.end & 0xf000; /* Set up upper 16 bits of I/O base/limit. */ - pci_write_config_word(bridge, PCI_IO_BASE_UPPER16, - region.start >> 16); - pci_write_config_word(bridge, PCI_IO_LIMIT_UPPER16, - region.end >> 16); + io_upper16 = (region.end & 0xffff0000) | (region.start >> 16); DBGC((KERN_INFO " IO window: %04lx-%04lx\n", region.start, region.end)); } else { /* Clear upper 16 bits of I/O base/limit. */ - pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, 0); + io_upper16 = 0; l = 0x00f0; DBGC((KERN_INFO " IO window: disabled.\n")); } + /* Temporarily disable the I/O range before updating PCI_IO_BASE. */ + pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, 0x0000ffff); + /* Update lower 16 bits of I/O base/limit. */ pci_write_config_dword(bridge, PCI_IO_BASE, l); + /* Update upper 16 bits of I/O base/limit. */ + pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, io_upper16); /* Set up the top and bottom of the PCI Memory segment for this bus. */ @@ -181,8 +189,9 @@ } pci_write_config_dword(bridge, PCI_MEMORY_BASE, l); - /* Clear out the upper 32 bits of PREF base/limit. */ - pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, 0); + /* Clear out the upper 32 bits of PREF limit. + If PCI_PREF_BASE_UPPER32 was non-zero, this temporarily + disables PREF range, which is ok. */ pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, 0); /* Set up PREF base/limit. */ @@ -198,6 +207,9 @@ DBGC((KERN_INFO " PREFETCH window: disabled.\n")); } pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l); + + /* Clear out the upper 32 bits of PREF base. */ + pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, 0); /* Check if we have VGA behind the bridge. Enable ISA in either case (FIXME!). */