PCI: Add dev->has_secondary_link to track downstream PCIe links
authorYijing Wang <wangyijing@huawei.com>
Thu, 21 May 2015 07:05:02 +0000 (15:05 +0800)
committerBjorn Helgaas <bhelgaas@google.com>
Fri, 22 May 2015 14:59:33 +0000 (09:59 -0500)
A PCIe Port is an interface to a Link.  A Root Port is a PCI-PCI bridge in
a Root Complex and has a Link on its secondary (downstream) side.  For
other Ports, the Link may be on either the upstream (closer to the Root
Complex) or downstream side of the Port.

The usual topology has a Root Port connected to an Upstream Port.  We
previously assumed this was the only possible topology, and that a
Downstream Port's Link was always on its downstream side, like this:

                  +---------------------+
  +------+        |          Downstream |
  | Root |        | Upstream       Port +--Link--
  | Port +--Link--+ Port                |
  +------+        |          Downstream |
                  |                Port +--Link--
                  +---------------------+

But systems do exist (see URL below) where the Root Port is connected to a
Downstream Port.  In this case, a Downstream Port's Link may be on either
the upstream or downstream side:

                  +---------------------+
  +------+        |            Upstream |
  | Root |        | Downstream     Port +--Link--
  | Port +--Link--+ Port                |
  +------+        |          Downstream |
                  |                Port +--Link--
                  +---------------------+

We can't use the Port type to determine which side the Link is on, so add a
bit in struct pci_dev to keep track.

A Root Port's Link is always on the Port's secondary side.  A component
(Endpoint or Port) on the other end of the Link obviously has the Link on
its upstream side.  If that component is a Port, it is part of a Switch or
a Bridge.  A Bridge has a PCI or PCI-X bus on its secondary side, not a
Link.  The internal bus of a Switch connects the Port to another Port whose
Link is on the downstream side.

[bhelgaas: changelog, comment, cache "type", use if/else]
Link: http://lkml.kernel.org/r/54EB81B2.4050904@pobox.com
Link: https://bugzilla.kernel.org/show_bug.cgi?id=94361
Suggested-by: Bjorn Helgaas <bhelgaas@google.com>
Signed-off-by: Yijing Wang <wangyijing@huawei.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
drivers/pci/probe.c
include/linux/pci.h

index 6675a7a1b9fc6a113379b9e5ac025b2a3df66ad3..96dcd7b8303b23246f4009608c459916d7f65ad2 100644 (file)
@@ -973,6 +973,8 @@ void set_pcie_port_type(struct pci_dev *pdev)
 {
        int pos;
        u16 reg16;
+       int type;
+       struct pci_dev *parent;
 
        pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
        if (!pos)
@@ -982,6 +984,22 @@ void set_pcie_port_type(struct pci_dev *pdev)
        pdev->pcie_flags_reg = reg16;
        pci_read_config_word(pdev, pos + PCI_EXP_DEVCAP, &reg16);
        pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD;
+
+       /*
+        * A Root Port is always the upstream end of a Link.  No PCIe
+        * component has two Links.  Two Links are connected by a Switch
+        * that has a Port on each Link and internal logic to connect the
+        * two Ports.
+        */
+       type = pci_pcie_type(pdev);
+       if (type == PCI_EXP_TYPE_ROOT_PORT)
+               pdev->has_secondary_link = 1;
+       else if (type == PCI_EXP_TYPE_UPSTREAM ||
+                type == PCI_EXP_TYPE_DOWNSTREAM) {
+               parent = pci_upstream_bridge(pdev);
+               if (!parent->has_secondary_link)
+                       pdev->has_secondary_link = 1;
+       }
 }
 
 void set_pcie_hotplug_bridge(struct pci_dev *pdev)
index 353db8dc4c6e0eca74751d4aabc8b0cb28fc4c7a..1989f6dc9618092e6723b884fd11cf8a48ee8e1f 100644 (file)
@@ -355,6 +355,7 @@ struct pci_dev {
        unsigned int    broken_intx_masking:1;
        unsigned int    io_window_1k:1; /* Intel P2P bridge 1K I/O windows */
        unsigned int    irq_managed:1;
+       unsigned int    has_secondary_link:1;
        pci_dev_flags_t dev_flags;
        atomic_t        enable_cnt;     /* pci_enable_device has been called */