From fc67a6f8a680bef1db4132c830c70ea8ced78b4f Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Fri, 24 Oct 2025 20:12:23 +0200 Subject: [PATCH] PCI/ASPM: Disable L0s/L1 if only enabled on one end of link by BIOS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adrià's Pixelbook Eve laptop (model C0A) features an Intel AC 7265 "Stone Peak" wifi card which has ASPM L1 Entry enabled on boot, even though the Root Port above the wifi card has both L0s and L1 disabled. The Root Port is hotplug-capable and consistently signals a spurious Presence Detect Changed event on device enumeration, plus occasionally at runtime. In addition the Root Port signals intermittent Correctable Receiver Errors. Commit 4d4c10f763d7 ("PCI: Explicitly put devices into D0 when initializing") has aggravated the situation in that the wifi card fails to retrain the link after the first spurious Presence Detect Changed event. Per PCIe r7.0 sec 7.5.3.7, "ASPM L1 must be enabled by software in the Upstream component on a Link prior to enabling ASPM L1 in the Downstream component on that Link." The ASPM Control settings made by BIOS violate this requirement, which explains the link issues. Disable L0s or L1 if BIOS enabled it only on one end of the link. Emit a message with KERN_INFO severity to alert firmware validation engineers. An alternative would be to enable L0s or L1 on the end of the link which BIOS left disabled, but that seems less safe. Users may choose the "powersave" or "powersupersave" ASPM policy to force (re-)enablement of L0s or L1. Reported-by: Adrià Vilanova Martínez Closes: https://lore.kernel.org/r/owewi3sswao4jt5qn3ntm533lvxe3jlovhjbdufq3uedbuztxt@76nft22vboxv/ Reported-by: googleg Closes: https://github.com/MrChromebox/firmware/issues/786 Reported-by: dangkhoa Closes: https://forum.chrultrabook.com/t/7727 Signed-off-by: Lukas Wunner Cc: stable@vger.kernel.org --- drivers/pci/pcie/aspm.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 7cc8281e70119b..8155de47c4deeb 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -830,8 +830,8 @@ static void pcie_aspm_override_default_link_state(struct pcie_link_state *link) static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) { struct pci_dev *child = link->downstream, *parent = link->pdev; + u16 parent_lnkctl, child_lnkctl, one_sided; u32 parent_lnkcap, child_lnkcap; - u16 parent_lnkctl, child_lnkctl; struct pci_bus *linkbus = parent->subordinate; if (blacklist) { @@ -873,6 +873,18 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) parent_lnkctl & ~PCI_EXP_LNKCTL_ASPMC); } + /* Leave L0s/L1 disabled if configured incorrectly by BIOS */ + one_sided = FIELD_GET(PCI_EXP_LNKCTL_ASPMC, child_lnkctl) ^ + FIELD_GET(PCI_EXP_LNKCTL_ASPMC, parent_lnkctl); + if (one_sided) { + pci_info(child, FW_BUG "ASPM: %s%s" + "not enabled on both ends of the link, disabling\n", + one_sided & PCI_EXP_LNKCTL_ASPM_L0S ? "L0s " : "", + one_sided & PCI_EXP_LNKCTL_ASPM_L1 ? "L1 " : ""); + parent_lnkctl &= ~one_sided; + child_lnkctl &= ~one_sided; + } + /* * Setup L0s state *