diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2017-01-11 10:11:53 -0500 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2017-01-11 10:11:53 -0500 |
commit | 51ebfc92b72b4f7dac1ab45683bf56741e454b8c (patch) | |
tree | 41bcd7dcfb9fcc7bbd427b2c8e070c6f33fcd1b7 | |
parent | 89e9f7bcd8744ea25fcf0ac671b8d72c10d7d790 (diff) |
PCI: Enumerate switches below PCI-to-PCIe bridges
A PCI-to-PCIe bridge (a "reverse bridge") has a PCI or PCI-X primary
interface and a PCI Express secondary interface. The PCIe interface is a
Downstream Port that originates a Link. See the "PCI Express to PCI/PCI-X
Bridge Specification", rev 1.0, sections 1.2 and A.6.
The bug report below involves a PCI-to-PCIe bridge and a PCIe switch below
the bridge:
00:1e.0 Intel 82801 PCI Bridge to [bus 01-0a]
01:00.0 Pericom PI7C9X111SL PCIe-to-PCI Reversible Bridge to [bus 02-0a]
02:00.0 Pericom Device 8608 [PCIe Upstream Port] to [bus 03-0a]
03:01.0 Pericom Device 8608 [PCIe Downstream Port] to [bus 0a]
01:00.0 is configured as a PCI-to-PCIe bridge (despite the name printed by
lspci). As we traverse a PCIe hierarchy, device connections alternate
between PCIe Links and internal Switch logic. Previously we did not
recognize that 01:00.0 had a secondary link, so we thought the 02:00.0
Upstream Port *did* have a secondary link. In fact, it's the other way
around: 01:00.0 has a secondary link, and 02:00.0 has internal Switch logic
on its secondary side.
When we thought 02:00.0 had a secondary link, the pci_scan_slot() ->
only_one_child() path assumed 02:00.0 could have only one child, so 03:00.0
was the only possible downstream device. But 03:00.0 doesn't exist, so we
didn't look for any other devices on bus 03.
Booting with "pci=pcie_scan_all" is a workaround, but we don't want users
to have to do that.
Recognize that PCI-to-PCIe bridges originate links on their secondary
interfaces.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=189361
Fixes: d0751b98dfa3 ("PCI: Add dev->has_secondary_link to track downstream PCIe links")
Tested-by: Blake Moore <blake.moore@men.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
CC: stable@vger.kernel.org # v4.2+
-rw-r--r-- | drivers/pci/probe.c | 12 |
1 files changed, 7 insertions, 5 deletions
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index e164b5c9f0f0..204960e70333 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c | |||
@@ -1169,6 +1169,7 @@ void set_pcie_port_type(struct pci_dev *pdev) | |||
1169 | pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); | 1169 | pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); |
1170 | if (!pos) | 1170 | if (!pos) |
1171 | return; | 1171 | return; |
1172 | |||
1172 | pdev->pcie_cap = pos; | 1173 | pdev->pcie_cap = pos; |
1173 | pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, ®16); | 1174 | pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, ®16); |
1174 | pdev->pcie_flags_reg = reg16; | 1175 | pdev->pcie_flags_reg = reg16; |
@@ -1176,13 +1177,14 @@ void set_pcie_port_type(struct pci_dev *pdev) | |||
1176 | pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD; | 1177 | pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD; |
1177 | 1178 | ||
1178 | /* | 1179 | /* |
1179 | * A Root Port is always the upstream end of a Link. No PCIe | 1180 | * A Root Port or a PCI-to-PCIe bridge is always the upstream end |
1180 | * component has two Links. Two Links are connected by a Switch | 1181 | * of a Link. No PCIe component has two Links. Two Links are |
1181 | * that has a Port on each Link and internal logic to connect the | 1182 | * connected by a Switch that has a Port on each Link and internal |
1182 | * two Ports. | 1183 | * logic to connect the two Ports. |
1183 | */ | 1184 | */ |
1184 | type = pci_pcie_type(pdev); | 1185 | type = pci_pcie_type(pdev); |
1185 | if (type == PCI_EXP_TYPE_ROOT_PORT) | 1186 | if (type == PCI_EXP_TYPE_ROOT_PORT || |
1187 | type == PCI_EXP_TYPE_PCIE_BRIDGE) | ||
1186 | pdev->has_secondary_link = 1; | 1188 | pdev->has_secondary_link = 1; |
1187 | else if (type == PCI_EXP_TYPE_UPSTREAM || | 1189 | else if (type == PCI_EXP_TYPE_UPSTREAM || |
1188 | type == PCI_EXP_TYPE_DOWNSTREAM) { | 1190 | type == PCI_EXP_TYPE_DOWNSTREAM) { |