Some not critical changes, and a bit more testing was done
(on ux164 - including card with an extra bridge, thanks to Michal).
Changes from bridges-2.4.0t11-rth:
- Disable devices before changing BARs
- Handle bridges not supporting IO forwarding
- Handle bridges with their own IO ports and/or memory
- Set cache line and default latency for all devices
- Updated comment for empty IO/memory ranges case
Diffs against bridges-2.4.0t11-rth and pristine 2.4.0-test11 attached.
Ivan.
diff -urp 2.4.0t11/arch/alpha/kernel/pci.c linux/arch/alpha/kernel/pci.c
--- 2.4.0t11/arch/alpha/kernel/pci.c Mon Nov 20 17:10:19 2000
+++ linux/arch/alpha/kernel/pci.c Mon Nov 20 16:41:36 2000
@@ -264,7 +264,7 @@ pcibios_fixup_bus(struct pci_bus *bus)
&dev->resource[PCI_BRIDGE_RESOURCES+i];
bus->resource[i]->name = bus->name;
}
- bus->resource[0]->flags |= IORESOURCE_IO;
+ bus->resource[0]->flags |= pci_bridge_check_io(dev);
bus->resource[1]->flags |= IORESOURCE_MEM;
/* For now, propogate hose limits to the bus;
we'll adjust them later. */
diff -urp 2.4.0t11/drivers/pci/setup-bus.c linux/drivers/pci/setup-bus.c
--- 2.4.0t11/drivers/pci/setup-bus.c Mon Nov 20 17:10:19 2000
+++ linux/drivers/pci/setup-bus.c Mon Nov 20 16:55:43 2000
@@ -45,21 +45,29 @@ pbus_assign_resources_sorted(struct pci_
head_io.next = head_mem.next = NULL;
for (ln=bus->devices.next; ln != &bus->devices; ln=ln->next) {
struct pci_dev *dev = pci_dev_b(ln);
+ u16 cmd;
- /* Skip PCI-PCI bridges */
- if (dev->class >> 8 == PCI_CLASS_BRIDGE_PCI)
- continue;
-
- /* ??? Reserve some resources for CardBus */
+ /* First, disable the device to avoid side
+ effects of possibly overlapping I/O and
+ memory ranges.
+ Except the VGA - for obvious reason. :-) */
+ if (dev->class >> 8 == PCI_CLASS_DISPLAY_VGA)
+ found_vga = 1;
+ else {
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY
+ | PCI_COMMAND_MASTER);
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+ }
+
+ /* Reserve some resources for CardBus.
+ Are these values reasonable? */
if (dev->class >> 8 == PCI_CLASS_BRIDGE_CARDBUS) {
io_reserved += 8*1024;
mem_reserved += 32*1024*1024;
continue;
}
- if (dev->class >> 8 == PCI_CLASS_DISPLAY_VGA)
- found_vga = 1;
-
pdev_sort_resources(dev, &head_io, IORESOURCE_IO);
pdev_sort_resources(dev, &head_mem, IORESOURCE_MEM);
}
@@ -88,15 +96,20 @@ pbus_assign_resources_sorted(struct pci_
ranges->io_end += io_reserved;
ranges->mem_end += mem_reserved;
- /* ??? How to turn off a bus from responding to, say, I/O at
- all if there are no I/O ports behind the bus? Turning off
- PCI_COMMAND_IO doesn't seem to do the job. So we must
- allow for at least one unit. */
+ /* 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. */
+#if 1
+ /* But assuming that some hardware designed before 1998 might
+ not support this (very unlikely - at least all DEC bridges
+ are ok and I believe that was standard de-facto. -ink), we
+ must allow for at least one unit. */
if (ranges->io_end == ranges->io_start)
ranges->io_end += 1;
if (ranges->mem_end == ranges->mem_start)
ranges->mem_end += 1;
-
+#endif
ranges->io_end = ROUND_UP(ranges->io_end, 4*1024);
ranges->mem_end = ROUND_UP(ranges->mem_end, 1024*1024);
@@ -123,10 +136,6 @@ pci_setup_bridge(struct pci_bus *bus)
DBGC((" IO window: %04lx-%04lx\n", ranges.io_start, ranges.io_end));
DBGC((" MEM window: %08lx-%08lx\n", ranges.mem_start, ranges.mem_end));
- /* Set the cache line correctly. */
- pci_write_config_byte(bridge, PCI_CACHE_LINE_SIZE,
- (L1_CACHE_BYTES / sizeof(u32)));
-
/* Set up the top and bottom of the PCI I/O segment for this bus. */
pci_read_config_dword(bridge, PCI_IO_BASE, &l);
l &= 0xffff0000;
@@ -134,14 +143,9 @@ pci_setup_bridge(struct pci_bus *bus)
l |= ranges.io_end & 0xf000;
pci_write_config_dword(bridge, PCI_IO_BASE, l);
-#if 0
- /* Set up upper 16 bits of I/O base/limit. */
- l = (ranges.io_start >> 16) & 0xffff;
- l |= ranges.io_end & 0xffff0000;
- pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, l);
-#else
+ /* Clear upper 16 bits of I/O base/limit. */
pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, 0);
-#endif
+
/* Clear out the upper 32 bits of PREF base/limit. */
pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, 0);
pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, 0);
@@ -160,15 +164,7 @@ pci_setup_bridge(struct pci_bus *bus)
/* Check if we have VGA behind the bridge.
Enable ISA in either case. */
l = (bus->resource[0]->flags & IORESOURCE_BUS_HAS_VGA) ? 0x0c : 0x04;
- pci_write_config_byte(bridge, PCI_BRIDGE_CONTROL, l);
-
- /*
- * Clear status bits,
- * turn on I/O enable (for downstream I/O),
- * turn on memory enable (for downstream memory),
- * turn on master enable (for upstream memory and I/O).
- */
- pci_write_config_dword(bridge, PCI_COMMAND, 0xffff0007);
+ pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, l);
}
static void __init
@@ -225,4 +221,25 @@ pci_assign_unassigned_resources(void)
pci_for_each_dev(dev) {
pdev_enable_device(dev);
}
+}
+
+/* Check whether the bridge supports I/O forwarding.
+ If not, its I/O base/limit register must be
+ read-only and read as 0. */
+unsigned long __init
+pci_bridge_check_io(struct pci_dev *bridge)
+{
+ u16 io;
+
+ pci_read_config_word(bridge, PCI_IO_BASE, &io);
+ if (!io) {
+ pci_write_config_word(bridge, PCI_IO_BASE, 0xf0f0);
+ pci_read_config_word(bridge, PCI_IO_BASE, &io);
+ pci_write_config_word(bridge, PCI_IO_BASE, 0x0);
+ }
+ if (io)
+ return IORESOURCE_IO;
+ printk(KERN_WARNING "PCI: bridge %s does not support I/O forwarding!\n",
+ bridge->name);
+ return 0;
}
diff -urp 2.4.0t11/drivers/pci/setup-res.c linux/drivers/pci/setup-res.c
--- 2.4.0t11/drivers/pci/setup-res.c Mon Nov 20 17:10:19 2000
+++ linux/drivers/pci/setup-res.c Mon Nov 20 16:42:05 2000
@@ -120,7 +120,8 @@ pci_assign_resource(struct pci_dev *dev,
}
}
- DBGC((" got res[%lx:%lx] for resource %d\n", res->start, res->end, i));
+ DBGC((" got res[%lx:%lx] for resource %d of %s\n", res->start,
+ res->end, i, dev->name));
return 0;
}
@@ -136,6 +137,12 @@ pdev_sort_resources(struct pci_dev *dev,
struct resource *r;
struct resource_list *list, *tmp;
+ /* PCI-PCI bridges may have I/O ports or
+ memory on the primary bus */
+ if (dev->class >> 8 == PCI_CLASS_BRIDGE_PCI &&
+ i >= PCI_BRIDGE_RESOURCES)
+ continue;
+
r = &dev->resource[i];
if (!(r->flags & type_mask) || r->parent)
continue;
@@ -201,6 +208,10 @@ pdev_enable_device(struct pci_dev *dev)
/* ??? Always turn on bus mastering. If the device doesn't support
it, the bit will go into the bucket. */
cmd |= PCI_COMMAND_MASTER;
+
+ /* Set the cache line and default latency (32). */
+ pci_write_config_word(dev, PCI_CACHE_LINE_SIZE,
+ (32 << 8) | (L1_CACHE_BYTES / sizeof(u32)));
/* Enable the appropriate bits in the PCI command register. */
pci_write_config_word(dev, PCI_COMMAND, cmd);
diff -urp 2.4.0t11/include/linux/pci.h linux/include/linux/pci.h
--- 2.4.0t11/include/linux/pci.h Mon Nov 20 17:10:31 2000
+++ linux/include/linux/pci.h Mon Nov 20 16:41:36 2000
@@ -536,6 +536,7 @@ int pci_claim_resource(struct pci_dev *,
void pci_assign_unassigned_resources(void);
void pdev_enable_device(struct pci_dev *);
void pdev_sort_resources(struct pci_dev *, struct resource_list *, u32);
+unsigned long pci_bridge_check_io(struct pci_dev *);
void pci_fixup_irqs(u8 (*)(struct pci_dev *, u8 *),
int (*)(struct pci_dev *, u8, u8));
diff vs 2.4.0-test11