Re: [PATCH] x86/PCI: Disable D3cold on Intel BE200 Wi-Fi on Lenovo IdeaPad Pro 5 14IAH10

From: Nika Krasnova

Date: Wed Jul 29 2026 - 09:13:22 EST


On Tue, 28 Jul 2026 20:14:36 +0000, "Grumbach, Emmanuel" <emmanuel.grumbach@xxxxxxxxx> wrote:
> We try to avoid resetting the firmware during suspend / resume. Out of
> curiosity, can you try to reset it?

I applied your hunk on top of 7.1.3, with my quirk removed, and it makes
no difference. The patched and unpatched runs fail identically:

iwlwifi 0000:01:00.0: timeout waiting for FW reset ACK (inta_hw=0xffffffff, reset_done 1)
ieee80211 phy0: PM: dpm_run_callback(): wiphy_resume [cfg80211] returns -110
ieee80211 phy0: PM: failed to resume async: error -110

On Tue, 28 Jul 2026 15:05:59 -0500, Bjorn Helgaas <helgaas@xxxxxxxxxx> wrote:
> I guess this means "lspci -xs01:00.0" after resume shows all 0xff?

Yes. Before suspend:

$ lspci -xs01:00.0
01:00.0 Network controller: Intel Corporation Wi-Fi 7(802.11be) AX1775*/AX1790*/BE20*/BE401/BE1750* 2x2 (rev 1a)
00: 86 80 2b 27 06 04 10 00 1a 00 80 02 10 00 80 00
10: 04 00 20 8c 00 00 00 00 00 00 00 00 00 00 00 00
20: 00 00 00 00 00 00 00 00 00 00 00 00 86 80 f4 00
30: 00 00 00 00 40 00 00 00 00 00 00 00 ff 01 00 00

and after resume:

$ lspci -xs01:00.0
01:00.0 Network controller: Intel Corporation Wi-Fi 7(802.11be) AX1775*/AX1790*/BE20*/BE401/BE1750* 2x2 (rev 1a)
00: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
10: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
20: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
30: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff

I've noticed that the root port above the card also never brings the
link back up:

pcieport 0000:00:06.0: Data Link Layer Link Active not set in 100 msec
[...]
iwlwifi 0000:01:00.0: timeout waiting for FW reset ACK (inta_hw=0xffffffff, reset_done 1)
ieee80211 phy0: PM: failed to resume async: error -110

0000:00:06.0 is the parent of 01:00.0. pci_pm_resume_noirq() only
reaches that wait with prev_state == PCI_D3cold, so the root port itself
was in D3cold, not just the endpoint. When the wait fails, everything
below the port is marked disconnected.

$ lspci -PP -s 01:00.0
00:06.0/01:00.0 Network controller: Intel Corporation Wi-Fi 7(802.11be) AX1775*/AX1790*/BE20*/BE401/BE1750* 2x2 (rev 1a)

(The "100 msec" there is the settle delay argument rather than the wait.
pcie_wait_for_link_delay() polls DLLLA for up to a second and also tries
pcie_failed_link_retrain(), so a larger d3cold_delay would not help.)

As far as I can tell the BE200 and its firmware are not involved. By the
time iwlwifi runs, the link is already down and every read returns ones,
which is what I would expect if the card never got its power back. That
also explains why a module reload and a PCI remove/rescan cannot revive
it: a rescan needs a working link.

D3cold on this port means switching off the ACPI power resource
\_SB.PC00.RP10.PXP_. I turned on the AML interpreter's method tracing
(trace_debug_layer=0x80, trace_debug_level=0x10, trace_state=enable) and
ran a suspend/resume where the card did not come back, to see whether
the firmware's power-on sequence runs at all. Other methods interleave
in the raw log, so this is grepped for RP10:

Method Begin [\_SB.PC00.RP10.PXP._OFF] execution.
Method Begin [\_SB.PC00.RP10.GPRS] execution.
Method End [\_SB.PC00.RP10.GPRS] execution.
Method Begin [\_SB.PC00.RP10.DL23] execution.
Method End [\_SB.PC00.RP10.DL23] execution.
Method Begin [\_SB.PC00.RP10.POFF] execution.
Method Begin [\_SB.PC00.RP10.GPPR] execution.
Method End [\_SB.PC00.RP10.GPPR] execution.
Method End [\_SB.PC00.RP10.POFF] execution.
Method End [\_SB.PC00.RP10.PXP._OFF] execution.
Method Begin [\_SB.PC00.RP10.PXP._ON] execution.
Method Begin [\_SB.PC00.RP10.GPRS] execution.
Method End [\_SB.PC00.RP10.GPRS] execution.
Method Begin [\_SB.PC00.RP10.PON] execution.
Method End [\_SB.PC00.RP10.PON] execution.
Method Begin [\_SB.PC00.RP10.L23D] execution.
Method End [\_SB.PC00.RP10.L23D] execution.
Method End [\_SB.PC00.RP10.PXP._ON] execution.

The resume side mirrors the suspend side: PON() and L23D() going up,
DL23() and POFF() coming down. The firmware runs the whole sequence and
reports success. The link still does not train.

Two registers in the port's own config space say more, both named by the
ASL. L23R sits at 0xE2 bit 3; L23D writes it and then polls until the
hardware clears it, and DL23 does the same with L23E on the way down.
LTSM is at 0x32B, which the firmware's own debug strings call the LTSSM
state.

Before suspend:

$ setpci -s 00:06.0 0xe2.b
11

$ for i in $(seq 5); do setpci -s 00:06.0 0x32b.b; done
40
40
40
40
40

After the failed resume:

$ setpci -s 00:06.0 0xe2.b
91

$ for i in $(seq 5); do setpci -s 00:06.0 0x32b.b; done
01
01
01
01
01

L23R (0x08) is clear both times, so the request L23D makes does
complete. For comparison, the sibling root port 0000:00:06.2, whose link
is up:

$ for i in $(seq 3); do setpci -s 00:06.2 0x32b.b; done
33
40
40

I do not know how the values in 0x32B map to LTSSM states, so the only
thing I can point at is the comparison: the failing port sits in a state
it never occupies while a link is up. If anyone has the decode for that
register, I would like to know what 0x01 is.

I can send the full acpidump output, or the decompiled DSDT and SSDTs,
off-list to anyone who wants to look at PON()/POFF() and the GPRS()
helper themselves.

> Another thing.. Does Windows run the same suspend / resume sequence?
> It also uses D3cold? I'd expect Lenovo to run thorough validation on
> those flows on Windows and report possible bugs to us.

I've dualbooted Windows 11 on this machine. The results:

PS C:\Users\user> powercfg /a
The following sleep states are available on this system:
Standby (S0 Low Power Idle) Network Connected
Hibernate
Fast Startup

The following sleep states are not available on this system:
Standby (S1)
The system firmware does not support this standby state.
This standby state is disabled when S0 low power idle is supported.

Standby (S2)
The system firmware does not support this standby state.
This standby state is disabled when S0 low power idle is supported.

Standby (S3)
The system firmware does not support this standby state.
This standby state is disabled when S0 low power idle is supported.
The current Device Guard configuration has disabled this standby
state.

Hybrid Sleep
Standby (S3) is not available.
The hypervisor does not support this standby state.

PS C:\Users\user> powercfg /devicequery wake_armed
USB4 Root Router (1.0)
Intel(R) Wi-Fi 7 BE200 320MHz

Output from devmgmt.msc for our BE200:

Current power state:
D0

Power capabilities:
00000089
PDCAP_D0_SUPPORTED
PDCAP_D3_SUPPORTED
PDCAP_WAKE_FROM_D3_SUPPORTED

Power state mappings:
S0 -> D0
S1 -> D3
S2 -> D3
S3 -> D3
S4 -> D3
S5 -> D3

The device clearly uses D3, but that does not distinguish D3hot from
D3cold. Windows' DEVICE_POWER_STATE enumeration [1] bottoms out at
PowerDeviceD3, and a driver does not initiate the D3hot -> D3cold step
at all: it requests D3hot, and the device reaches D3cold only when the
bus driver or the system firmware removes the power source [2]. So what
Device Manager reports is the requested D-state, not whether the rail
was subsequently removed. This machine also sleeps via S0 Low Power Idle
rather than S3, so the Sx -> Dx table above does not even describe the
flow that matters here.

Entering D3cold while the system stays in S0 has to be enabled by the
power policy owner, through the INF or a SetD3ColdSupport() call [2].
Does the Windows BE200 driver enable D3cold transitions in S0 on
platforms like this one? If it does not, then Windows never takes the
card below D3hot here, and the D3cold->D0 path would never have been
exercised in validation.

Given that the failure is at the link layer above the device rather than
in the device itself, this should probably be a PCI quirk in
drivers/pci/quirks.c instead of arch/x86/pci/fixup.c.

[1] https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ne-wdm-_device_power_state
[2] https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/enabling-transitions-to-d3cold

--
Nika Krasnova