提交 6c35a1ac 编写于 作者: L Lukas Wunner 提交者: Bjorn Helgaas

PCI: pciehp: Tolerate initially unstable link

When a device is hotplugged, Presence Detect and Link Up events often do
not occur simultaneously, but with a lag of a few milliseconds.  Only
the first event received is relevant, the other one can be disregarded.

Moreover, Stefan Roese reports that on certain platforms, Link State and
Presence Detect may flap for up to 100 ms before stabilizing, suggesting
that such events should be disregarded for at least this long:
https://lkml.kernel.org/r/20180130084121.18653-1-sr@denx.de

On slot enablement, pciehp_check_link_status() waits for 100 ms per
PCIe r4.0, sec 6.7.3.3, then probes the hotplugged device's vendor
register for up to 1 second.

If this succeeds, the link is definitely up, so ignore any Presence
Detect or Link State events that occurred up to this point.

pciehp_check_link_status() then checks the Link Training bit in the
Link Status register.  This is the final opportunity to detect
inaccessibility of the device and abort slot enablement.  Any link
or presence change that occurs afterwards will cause the slot to be
disabled again immediately after attempting to enable it.

The astute reviewer may appreciate that achieving this behavior would be
more complicated had pciehp not just been converted to enable/disable
the slot exclusively from the IRQ thread:  When the slot is enabled via
sysfs, each link or presence flap would otherwise cause the IRQ thread
to run and it would have to sense that those events are belonging to a
concurrent slot enablement operation and disregard them.  It would be
much more difficult than this mere 3 line change.
Signed-off-by: NLukas Wunner <lukas@wunner.de>
Signed-off-by: NBjorn Helgaas <bhelgaas@google.com>
Cc: Stefan Roese <sr@denx.de>
上级 25c83b84
...@@ -270,6 +270,11 @@ int pciehp_check_link_status(struct controller *ctrl) ...@@ -270,6 +270,11 @@ int pciehp_check_link_status(struct controller *ctrl)
found = pci_bus_check_dev(ctrl->pcie->port->subordinate, found = pci_bus_check_dev(ctrl->pcie->port->subordinate,
PCI_DEVFN(0, 0)); PCI_DEVFN(0, 0));
/* ignore link or presence changes up to this point */
if (found)
atomic_and(~(PCI_EXP_SLTSTA_DLLSC | PCI_EXP_SLTSTA_PDC),
&ctrl->pending_events);
pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status); ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status);
if ((lnk_status & PCI_EXP_LNKSTA_LT) || if ((lnk_status & PCI_EXP_LNKSTA_LT) ||
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册