Re: [PATCH v3] PCI: Skip Target Speed quirk on clamped ports with no link

Andreas Wild <[email protected]>
Newsgroups org.kernel.vger.linux-pci,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
On Mon, 11 Aug 2026, Aoxtj wrote:

> Diagnostic result: link is genuinely down, not recovering.
>
> [1.623701] diag: pre  ret=-110 DLLLA=0 sta=0x9023 ctl2=0x0023
> [3.734697] diag: post DLLLA=0 after 1010ms sta=0x9823
> [3.734702] diag: post ctl2=0x0021

So the link really isn't coming back at all rather than too late for the
bus scan.

  after the failed 8GT/s retrain (ret=-110, -ETIMEDOUT):
    LNKSTA  0x9023   CLS 8.0GT/s  width x2  LT 0  DLLLA 0
    LNKCTL2 0x0023   TLS 8.0GT/s  HASD set

  after the restore, plus 1010 ms of polling:
    LNKSTA  0x9823   CLS 8.0GT/s  width x2  LT 1  DLLLA 0
    LNKCTL2 0x0021   TLS 2.5GT/s  HASD set

The register restore works, LNKCTL2 goes back to 2.5GT/s, but LT is still
asserted a second later, with DLLLA never returning, so the port is stuck
in link training rather than merely slow to recover.

On the HASD bit:

Your LNKCTL2 has bit 5 (PCI_EXP_LNKCTL2_HASD, "SpeedDis+") set in both
samples.  So firmware clamped the Target Link Speed to 2.5GT/s and also
disabled hardware autonomous speed changes, on a link that turns out not
to work at 8GT/s.

If you would like something to try, the change below skips lifting the
restriction if HASD is set.  It applies to your tree (I checked it against
v7.1.6 and on top of v3). Mainline would need a small adaptation, since
that block no longer has the LNKCAP test.

diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -115,6 +115,11 @@ int pcie_failed_link_retrain(struct pci_dev *dev)
 	pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap);
 	if ((lnkctl2 & PCI_EXP_LNKCTL2_TLS) == PCI_EXP_LNKCTL2_TLS_2_5GT &&
 	    (lnkcap & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) {
+		if (lnkctl2 & PCI_EXP_LNKCTL2_HASD) {
+			pci_info(dev, "2.5GT/s restriction left, firmware set HASD\n");
+			return ret;
+		}
+
 		pci_info(dev, "removing 2.5GT/s downstream link speed restriction\n");
 		ret = pcie_set_target_speed(dev, PCIE_LNKCAP_SLS2SPEED(lnkcap), false);
 		if (ret)

Some things to consider:

I cannot verify the change myself - it compiles without warnings and that
is all I can say.

HASD is specified as disabling *hardware autonomous* speed changes, so it
does not strictly forbid a software-initiated retrain.  Interpreting it as
"firmware meant this" is just my guess.

As I said before, I have no background in this code.  I came to it through
one boot-time regression on my own machine, and that is the extent of it.

So please take it as "here is one thing that could help this particular
case", not as a view on how the quirk ought to work.  Maciej and Bjorn are
far better placed to judge whether the answer is this, or restoring the
device ID match, or something else entirely.

One further thought for people who know the code better than I do:

Since retraining apparently cannot recover the link once it is wedged,
would a secondary bus reset be the appropriate recovery in the error path?

Best regards,
Andreas
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.