]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
PCI: Revert "PCI: Call pci_read_bridge_bases() from core instead of arch code"
authorBjorn Helgaas <bhelgaas@google.com>
Tue, 15 Sep 2015 18:18:04 +0000 (13:18 -0500)
committerBjorn Helgaas <bhelgaas@google.com>
Tue, 15 Sep 2015 18:18:04 +0000 (13:18 -0500)
Revert dff22d2054b5 ("PCI: Call pci_read_bridge_bases() from core instead
of arch code").

Reading PCI bridge windows is not arch-specific in itself, but there is PCI
core code that doesn't work correctly if we read them too early.  For
example, Hannes found this case on an ARM Freescale i.mx6 board:

  pci_bus 0000:00: root bus resource [mem 0x01000000-0x01efffff]
  pci 0000:00:00.0: PCI bridge to [bus 01-ff]
  pci 0000:00:00.0: BAR 8: no space for [mem size 0x01000000] (mem window)
  pci 0000:01:00.0: BAR 2: failed to assign [mem size 0x00200000]
  pci 0000:01:00.0: BAR 1: failed to assign [mem size 0x00004000]
  pci 0000:01:00.0: BAR 0: failed to assign [mem size 0x00000100]

The 00:00.0 mem window needs to be at least 3MB: the 01:00.0 device needs
0x204100 of space, and mem windows are megabyte-aligned.

Bus sizing can increase a bridge window size, but never *decrease* it (see
d65245c3297a ("PCI: don't shrink bridge resources")).  Prior to
dff22d2054b5, ARM didn't read bridge windows at all, so the "original size"
was zero, and we assigned a 3MB window.

After dff22d2054b5, we read the bridge windows before sizing the bus.  The
firmware programmed a 16MB window (size 0x01000000) in 00:00.0, and since
we never decrease the size, we kept 16MB even though we only needed 3MB.
But 16MB doesn't fit in the host bridge aperture, so we failed to assign
space for the window and the downstream devices.

I think this is a defect in the PCI core: we shouldn't rely on the firmware
to assign sensible windows.

Ray reported a similar problem, also on ARM, with Broadcom iProc.

Issues like this are too hard to fix right now, so revert dff22d2054b5.

Reported-by: Hannes <oe5hpm@gmail.com>
Reported-by: Ray Jui <rjui@broadcom.com>
Link: http://lkml.kernel.org/r/CAAa04yFQEUJm7Jj1qMT57-LG7ZGtnhNDBe=PpSRa70Mj+XhW-A@mail.gmail.com
Link: http://lkml.kernel.org/r/55F75BB8.4070405@broadcom.com
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Yinghai Lu <yinghai@kernel.org>
Acked-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
12 files changed:
arch/alpha/kernel/pci.c
arch/frv/mb93090-mb00/pci-vdk.c
arch/ia64/pci/pci.c
arch/microblaze/pci/pci-common.c
arch/mips/pci/pci.c
arch/mn10300/unit-asb2305/pci.c
arch/powerpc/kernel/pci-common.c
arch/x86/pci/common.c
arch/xtensa/kernel/pci.c
drivers/parisc/dino.c
drivers/parisc/lba_pci.c
drivers/pci/probe.c

index cded02c890aacb5b32813332b61df3b4523fbd05..5f387ee5b5c5e0a69fb4817e9437cef5cf0c7cdd 100644 (file)
@@ -242,7 +242,12 @@ pci_restore_srm_config(void)
 
 void pcibios_fixup_bus(struct pci_bus *bus)
 {
-       struct pci_dev *dev;
+       struct pci_dev *dev = bus->self;
+
+       if (pci_has_flag(PCI_PROBE_ONLY) && dev &&
+           (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
+               pci_read_bridge_bases(bus);
+       }
 
        list_for_each_entry(dev, &bus->devices, bus_list) {
                pdev_save_srm_config(dev);
index f9c86c475bbdafdbd37771c6834d7376b25673ec..f211839e2cae18f4d71999ec41ad8ed2a8ff1bca 100644 (file)
@@ -294,6 +294,8 @@ void pcibios_fixup_bus(struct pci_bus *bus)
        printk("### PCIBIOS_FIXUP_BUS(%d)\n",bus->number);
 #endif
 
+       pci_read_bridge_bases(bus);
+
        if (bus->number == 0) {
                struct pci_dev *dev;
                list_for_each_entry(dev, &bus->devices, bus_list) {
index d89b6013c9412c7f2ef5d72a3a5f9710f7544d3e..7cc3be9fa7c65a0dd700dfd30c04cc7be822922d 100644 (file)
@@ -533,9 +533,10 @@ void pcibios_fixup_bus(struct pci_bus *b)
 {
        struct pci_dev *dev;
 
-       if (b->self)
+       if (b->self) {
+               pci_read_bridge_bases(b);
                pcibios_fixup_bridge_resources(b->self);
-
+       }
        list_for_each_entry(dev, &b->devices, bus_list)
                pcibios_fixup_device_resources(dev);
        platform_pci_fixup_bus(b);
index 6b8b75266801aaf25fe509985d2a5edd66ed3e8c..ae838ed5fcf2535ca5c047a2837adadc434735cd 100644 (file)
@@ -863,7 +863,14 @@ void pcibios_setup_bus_devices(struct pci_bus *bus)
 
 void pcibios_fixup_bus(struct pci_bus *bus)
 {
-       /* Fixup the bus */
+       /* When called from the generic PCI probe, read PCI<->PCI bridge
+        * bases. This is -not- called when generating the PCI tree from
+        * the OF device-tree.
+        */
+       if (bus->self != NULL)
+               pci_read_bridge_bases(bus);
+
+       /* Now fixup the bus bus */
        pcibios_setup_bus_self(bus);
 
        /* Now fixup devices on that bus */
index c6996cf67a5c83e91e465d7d03e25c3ff7d3c89d..b8a0bf5766f2efb64380ae3dedddca3b4782da03 100644 (file)
@@ -311,6 +311,12 @@ int pcibios_enable_device(struct pci_dev *dev, int mask)
 
 void pcibios_fixup_bus(struct pci_bus *bus)
 {
+       struct pci_dev *dev = bus->self;
+
+       if (pci_has_flag(PCI_PROBE_ONLY) && dev &&
+           (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
+               pci_read_bridge_bases(bus);
+       }
 }
 
 EXPORT_SYMBOL(PCIBIOS_MIN_IO);
index deaa893efba5969a92aed92e6c230f2e00658e6f..3dfe2d31c67b20971701cac89565af0531dec878 100644 (file)
@@ -324,6 +324,7 @@ void pcibios_fixup_bus(struct pci_bus *bus)
        struct pci_dev *dev;
 
        if (bus->self) {
+               pci_read_bridge_bases(bus);
                pcibios_fixup_bridge_resources(bus->self);
        }
 
index a1d0632d97c69ff5f5ad2e8ccc2a53487c5d8d14..7587b2ae5f779d6b8c97cb48e8325db0770f42b4 100644 (file)
@@ -1032,7 +1032,13 @@ void pcibios_set_master(struct pci_dev *dev)
 
 void pcibios_fixup_bus(struct pci_bus *bus)
 {
-       /* Fixup the bus */
+       /* When called from the generic PCI probe, read PCI<->PCI bridge
+        * bases. This is -not- called when generating the PCI tree from
+        * the OF device-tree.
+        */
+       pci_read_bridge_bases(bus);
+
+       /* Now fixup the bus bus */
        pcibios_setup_bus_self(bus);
 
        /* Now fixup devices on that bus */
index 09d3afc0a181c1ac44ca654f9fde35796dbe3843..dc78a4a9a46663f10600afd013d9230fe5fe222b 100644 (file)
@@ -166,6 +166,7 @@ void pcibios_fixup_bus(struct pci_bus *b)
 {
        struct pci_dev *dev;
 
+       pci_read_bridge_bases(b);
        list_for_each_entry(dev, &b->devices, bus_list)
                pcibios_fixup_device_resources(dev);
 }
index d27b4dcf221f8904e34198474d396c77b1034bb6..b848cc3dc913d8de7dc5181fc140a9f83215e6cf 100644 (file)
@@ -210,6 +210,10 @@ subsys_initcall(pcibios_init);
 
 void pcibios_fixup_bus(struct pci_bus *bus)
 {
+       if (bus->parent) {
+               /* This is a subordinate bridge */
+               pci_read_bridge_bases(bus);
+       }
 }
 
 void pcibios_set_master(struct pci_dev *dev)
index baec33c4e6981ffdb19df8d767f5aff8cb72bb77..a0580afe1713a5f58db5e96da635028856200a08 100644 (file)
@@ -560,6 +560,9 @@ dino_fixup_bus(struct pci_bus *bus)
        } else if (bus->parent) {
                int i;
 
+               pci_read_bridge_bases(bus);
+
+
                for(i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) {
                        if((bus->self->resource[i].flags & 
                            (IORESOURCE_IO | IORESOURCE_MEM)) == 0)
index 7b9e89ba0465f120b07385643b56900a61054696..a32c1f6c252cd91660b358ae8cd754d28953ce3b 100644 (file)
@@ -693,6 +693,7 @@ lba_fixup_bus(struct pci_bus *bus)
        if (bus->parent) {
                int i;
                /* PCI-PCI Bridge */
+               pci_read_bridge_bases(bus);
                for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++)
                        pci_claim_bridge_resource(bus->self, i);
        } else {
index 0b2be174d9818e9ffe86110068c9eb2e3ea19f11..c8cc0e62a7b9d25733a45253fb051dac22f53dd2 100644 (file)
@@ -855,9 +855,6 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass)
                        child->bridge_ctl = bctl;
                }
 
-               /* Read and initialize bridge resources */
-               pci_read_bridge_bases(child);
-
                cmax = pci_scan_child_bus(child);
                if (cmax > subordinate)
                        dev_warn(&dev->dev, "bridge has subordinate %02x but max busn %02x\n",
@@ -918,9 +915,6 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass)
 
                if (!is_cardbus) {
                        child->bridge_ctl = bctl;
-
-                       /* Read and initialize bridge resources */
-                       pci_read_bridge_bases(child);
                        max = pci_scan_child_bus(child);
                } else {
                        /*