summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2008-10-21 10:06:29 +1100
committerJesse Barnes <jbarnes@virtuousgeek.org>2008-10-22 16:42:44 -0700
commita1c19894b786f10c76ac40e93c6b5d70c9b946d2 (patch)
tree4a03c1d7b9958c6fe328eeea13ee31a0edbb4478
parent0b8b0dca9aad94878adaf4520f3f12bf9813f329 (diff)
PCI: Workaround invalid P2P bridge bus numbers
Some firmware fail to properly configure P2P bridges, leaving them with invalid bus numbers. In some cases, this happens on some embedded 4xx boards as the result of the kernel allocating different bus space than the firmware does to host bridges while not setting pcibios_assign_all_busses() for various reasons. In other cases, it can just be bogus firmware. This adds some sanity checking to the PCI probing code. If a bridge is found whose primary bus number doesn't match the bus it's sitting on, or whose secondary bus number not strictly above it's primary bus number, then the bridge bus numbers are deconfigured in the first pass of pci_scan_bridge() to be re-assigned in the second pass. Tested-by: "Ayman El-Khashab" <AymanE@tanisys.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
-rw-r--r--drivers/pci/probe.c12
1 files changed, 10 insertions, 2 deletions
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index aaaf0a1fed22..6f1e51d77bce 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -480,19 +480,27 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max,
int is_cardbus = (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS);
u32 buses, i, j = 0;
u16 bctl;
+ int broken = 0;
pci_read_config_dword(dev, PCI_PRIMARY_BUS, &buses);
dev_dbg(&dev->dev, "scanning behind bridge, config %06x, pass %d\n",
buses & 0xffffff, pass);
+ /* Check if setup is sensible at all */
+ if (!pass &&
+ ((buses & 0xff) != bus->number || ((buses >> 8) & 0xff) <= bus->number)) {
+ dev_dbg(&dev->dev, "bus configuration invalid, reconfiguring\n");
+ broken = 1;
+ }
+
/* Disable MasterAbortMode during probing to avoid reporting
of bus errors (in some architectures) */
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &bctl);
pci_write_config_word(dev, PCI_BRIDGE_CONTROL,
bctl & ~PCI_BRIDGE_CTL_MASTER_ABORT);
- if ((buses & 0xffff00) && !pcibios_assign_all_busses() && !is_cardbus) {
+ if ((buses & 0xffff00) && !pcibios_assign_all_busses() && !is_cardbus && !broken) {
unsigned int cmax, busnr;
/*
* Bus already configured by firmware, process it in the first
@@ -530,7 +538,7 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max,
* do in the second pass.
*/
if (!pass) {
- if (pcibios_assign_all_busses())
+ if (pcibios_assign_all_busses() || broken)
/* Temporarily disable forwarding of the
configuration cycles on all bridges in
this bus segment to avoid possible