From: Lyude Paul On certain Lenovo Legion devices (including the 16APH8, 16AHP9, and 15AHP9), the firmware appears to enjoy firing a GPE on the parent PCIe port of the nvidia GPU very shortly after the GPU enters D3Cold. This means that every time the GPU is runtime suspended, ACPI firmware immediately wakes up its parent PCIe port, which then wakes up the GPU. Once it falls asleep again, the cycle repeats. Note, this bug doesn't require Nvidia's driver. It happens on nouveau and nova as well, and it even seems possible for this to happen without any driver loaded. This seems to simply be a bug. Luckily, there is nothing that we actually need ACPI wakeup events for on the GPU. Events such as display connector hotplug events in D3Cold come through as ACPI_VIDEO events which aren't affected by this quirk. Reported-by: Ryan Brue Link: https://github.com/NVIDIA/open-gpu-kernel-modules/issues/905 Co-developed-by: Ryan Brue Signed-off-by: Ryan Brue Signed-off-by: Lyude Paul --- Bjorn, please pick this up if it looks good. Derek J. Clark graciously sent a message to the Lenovo team on my behalf, but noted that the Legion side of the business doesn't have a fully fleshed-out Linux team, and that Mark has been helping out in their free time. I imagine this patch has been missed by them. The patch has been tested by many users in the linked issue, and I have been running it personally since the patch was made. --- Changes in v4: - Rebased onto v7.4-rc2. No functional change. - Ccing stable, as well as other addresses I missed when taking over the patchset from Lyude. - Link to v3: https://patch.msgid.link/20261005-broken-gpe-legion-v3-1-f49f465d48ec@gmail.com Changes in v3: - Add the Lenovo LOQ 15AHP9 to the quirk table (@jindalpratik on GitHub) - Also match Nvidia RTX 4070 Max-Q (0x2860), which similarly refuses to stay in D3Cold (@inigo001 on GitHub) - Link to v2: https://patch.msgid.link/20260821200607.621341-1-lyude@redhat.com Changes in v2: - Don't forget to actually use parent_adev with acpi_remove_pm_notifier() - Check if parent_adev is NULL (could happen if booted with acpi=off), just consider it a no-op if it is. - Base the patch off Linux master instead, I based it off drm-misc-next by mistake because I'm too used to working on DRM drivers :). - Link to v1: https://patch.msgid.link/20260821181203.82722-1-lyude@redhat.com --- Tested on all the modified Legion devices. The Nvidia dGPU properly goes into D3Cold with the patch, whereas without it, the dGPU will wake up shortly after going to D3Cold. See the linked open-gpu-kernel-modules issue for more details. --- drivers/pci/quirks.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 7f1f47ea2ee4..c2269c367279 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -6336,6 +6336,94 @@ DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_NVIDIA, 0x13b1, PCI_CLASS_DISPLAY_VGA, 8, quirk_reset_lenovo_thinkpad_p50_nvgpu); +#ifdef CONFIG_ACPI +#ifdef CONFIG_DMI +static const struct dmi_system_id nvidia_dgpu_broken_gpe_quirk_table[] = { + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "15AHP9"), + }, + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "16APH8"), + }, + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "16AHP9"), + }, + }, + {} +}; + +/* + * On the Lenovo Legion Slim 5 16APH8 and 16AHP9, the firmware appears to enjoy + * firing a GPE on the parent PCIe port of the nvidia GPU very shortly after the + * GPU enters D3Cold. This means that every time the GPU is runtime suspended, + * ACPI firmware immediately wakes up its parent PCIe port, which then wakes up + * the GPU. Once it falls asleep again, the cycle repeats. + * + * See: https://github.com/NVIDIA/open-gpu-kernel-modules/issues/905 + * + * Note, this bug doesn't require Nvidia's driver. It happens on nouveau and + * nova as well, and it even seems possible for this to happen without any + * driver loaded. + * + * This seems to simply be a bug. Luckily, there is nothing that we actually + * need ACPI wakeup events for on the GPU. Events such as display connector + * hotplug events in D3Cold come through as ACPI_VIDEO events which aren't + * affected by this quirk. + * + * So, workaround this by disabling wakeup events for the parent PCIe port of + * the GPU. + */ +static void quirk_nvidia_dgpu_broken_gpe(struct pci_dev *pdev) +{ + struct acpi_device *parent_adev; + struct device *parent_dev; + int ret; + + /* Just to be extra safe and make sure we don't try this on an eGPU */ + if (pci_is_thunderbolt_attached(pdev)) + return; + + ret = dmi_check_system(nvidia_dgpu_broken_gpe_quirk_table); + if (ret == 0) + return; + + pci_info(pdev, FW_BUG "GPU PCIe port has broken wakeup events, disabling\n"); + + parent_dev = pci_physfn(pdev)->dev.parent; + if (!parent_dev) { + pci_err(pdev, + "Can't find PCIe parent? Your Nvidia GPU will have broken runtime PM\n"); + return; + } + + parent_adev = ACPI_COMPANION(parent_dev); + if (!parent_adev) /* No ACPI? That means no GPE, and nothing to do here. */ + return; + + /* The spurious GPEs will be sent from the ACPI device for the PCIe port this GPU is + * connected to, so remove our PM notifier to turn them into a no-op. + */ + ret = acpi_remove_pm_notifier(parent_adev); + if (ACPI_FAILURE(ret)) + pci_err(pdev, "Removing PM notifier failed: %d\n", ret); +} +DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_NVIDIA, 0x28e0, + PCI_CLASS_DISPLAY_VGA, 8, + quirk_nvidia_dgpu_broken_gpe); +DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_NVIDIA, 0x2860, + PCI_CLASS_DISPLAY_VGA, 8, + quirk_nvidia_dgpu_broken_gpe); +#endif +#endif + /* * Device [1b21:2142] * When in D0, PME# doesn't get asserted when plugging USB 3.0 device. --- base-commit: 7475ea8b58616b33c04afb976cf0fbaad9dd63fb change-id: 20261005-broken-gpe-legion-9c9f87a5898c Best regards, -- Ryan Brue