PCI: work around Stratus ftServer broken PCIe hierarchy
authorBjorn Helgaas <bhelgaas@google.com>
Mon, 30 Apr 2012 21:21:02 +0000 (15:21 -0600)
committerBjorn Helgaas <bhelgaas@google.com>
Mon, 30 Apr 2012 21:21:02 +0000 (15:21 -0600)
A PCIe downstream port is a P2P bridge.  Its secondary interface is
a link that should lead only to device 0 (unless ARI is enabled)[1], so
we don't probe for non-zero device numbers.

Some Stratus ftServer systems have a PCIe downstream port (02:00.0) that
leads to both an upstream port (03:00.0) and a downstream port (03:01.0),
and 03:01.0 has important devices below it:

  [0000:02]-+-00.0-[03-3c]--+-00.0-[04-09]--...
                            \-01.0-[0a-0d]--+-[USB]
                                            +-[NIC]
                                            +-...

Previously, we didn't enumerate device 03:01.0, so USB and the network
didn't work.  This patch adds a DMI quirk to scan all device numbers,
not just 0, below a downstream port.

Based on a patch by Prarit Bhargava.

[1] PCIe spec r3.0, sec 7.3.1

CC: Myron Stowe <mstowe@redhat.com>
CC: Don Dutile <ddutile@redhat.com>
CC: James Paradis <james.paradis@stratus.com>
CC: Matthew Wilcox <matthew.r.wilcox@intel.com>
CC: Jesse Barnes <jbarnes@virtuousgeek.org>
CC: Prarit Bhargava <prarit@redhat.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Documentation/kernel-parameters.txt
arch/x86/pci/common.c
drivers/pci/pci.c
drivers/pci/probe.c
include/asm-generic/pci-bridge.h

index c1601e5a8b71e81ae57f7d1d5a70be5449501443..f995195409fd699c0e657ac1b12d3ce59267e0b2 100644 (file)
@@ -2161,6 +2161,9 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                                on: Turn realloc on
                realloc         same as realloc=on
                noari           do not use PCIe ARI.
+               pcie_scan_all   Scan all possible PCIe devices.  Otherwise we
+                               only look for one device below a PCIe downstream
+                               port.
 
        pcie_aspm=      [PCIE] Forcibly enable or disable PCIe Active State Power
                        Management.
index 323481e06ef8cfa3f8aefa47d9b763a08f4aab92..16c5d783529525f0829d059c4f8069a7c9409438 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/dmi.h>
 #include <linux/slab.h>
 
+#include <asm-generic/pci-bridge.h>
 #include <asm/acpi.h>
 #include <asm/segment.h>
 #include <asm/io.h>
@@ -229,6 +230,14 @@ static int __devinit assign_all_busses(const struct dmi_system_id *d)
 }
 #endif
 
+static int __devinit set_scan_all(const struct dmi_system_id *d)
+{
+       printk(KERN_INFO "PCI: %s detected, enabling pci=pcie_scan_all\n",
+              d->ident);
+       pci_add_flags(PCI_SCAN_ALL_PCIE_DEVS);
+       return 0;
+}
+
 static const struct dmi_system_id __devinitconst pciprobe_dmi_table[] = {
 #ifdef __i386__
 /*
@@ -420,6 +429,13 @@ static const struct dmi_system_id __devinitconst pciprobe_dmi_table[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant DL585 G2"),
                },
        },
+       {
+               .callback = set_scan_all,
+               .ident = "Stratus/NEC ftServer",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ftServer"),
+               },
+       },
        {}
 };
 
index 111569ccab434dda8262e1ddeb3a9aab04043171..8e6c38817036f7061c4fcf66d0217f7ad906807b 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/interrupt.h>
 #include <linux/device.h>
 #include <linux/pm_runtime.h>
+#include <asm-generic/pci-bridge.h>
 #include <asm/setup.h>
 #include "pci.h"
 
@@ -3893,6 +3894,8 @@ static int __init pci_setup(char *str)
                                pcie_bus_config = PCIE_BUS_PERFORMANCE;
                        } else if (!strncmp(str, "pcie_bus_peer2peer", 18)) {
                                pcie_bus_config = PCIE_BUS_PEER2PEER;
+                       } else if (!strncmp(str, "pcie_scan_all", 13)) {
+                               pci_add_flags(PCI_SCAN_ALL_PCIE_DEVS);
                        } else {
                                printk(KERN_ERR "PCI: Unknown option `%s'\n",
                                                str);
index 5e1ca3c58a7d4502b966974ec13c55a892e9be2b..2dc8675eea1aadcb67164eb448d1d5f0cd9b7d0d 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/module.h>
 #include <linux/cpumask.h>
 #include <linux/pci-aspm.h>
+#include <asm-generic/pci-bridge.h>
 #include "pci.h"
 
 #define CARDBUS_LATENCY_TIMER  176     /* secondary latency timer */
@@ -1395,10 +1396,13 @@ static unsigned no_next_fn(struct pci_dev *dev, unsigned fn)
 static int only_one_child(struct pci_bus *bus)
 {
        struct pci_dev *parent = bus->self;
+
        if (!parent || !pci_is_pcie(parent))
                return 0;
-       if (parent->pcie_type == PCI_EXP_TYPE_ROOT_PORT ||
-           parent->pcie_type == PCI_EXP_TYPE_DOWNSTREAM)
+       if (parent->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
+               return 1;
+       if (parent->pcie_type == PCI_EXP_TYPE_DOWNSTREAM &&
+           !pci_has_flag(PCI_SCAN_ALL_PCIE_DEVS))
                return 1;
        return 0;
 }
index a5b5d5a89a4fe3fc87d2adb2de31b9ac58c74e61..20db2e5a0a699b20d6b4805de244890a96ec789e 100644 (file)
@@ -30,6 +30,12 @@ enum {
        PCI_ENABLE_PROC_DOMAINS = 0x00000010,
        /* ... except for domain 0 */
        PCI_COMPAT_DOMAIN_0     = 0x00000020,
+
+       /* PCIe downstream ports are bridges that normally lead to only a
+        * device 0, but if this is set, we scan all possible devices, not
+        * just device 0.
+        */
+       PCI_SCAN_ALL_PCIE_DEVS  = 0x00000040,
 };
 
 #ifdef CONFIG_PCI