[PATCH v3] pci: Add broken-GPE quirk for some Lenovo Legions with Nvidia GPUs

From: Ryan Brue

Date: Mon Oct 05 2026 - 01:55:09 EST


From: Lyude Paul <lyude@xxxxxxxxxx>

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 <ryanbrue.dev@xxxxxxxxx>
Link: https://github.com/NVIDIA/open-gpu-kernel-modules/issues/905
Co-developed-by: Ryan Brue <ryanbrue.dev@xxxxxxxxx>
Signed-off-by: Ryan Brue <ryanbrue.dev@xxxxxxxxx>
Signed-off-by: Lyude Paul <lyude@xxxxxxxxxx>
Cc: stable@xxxxxxxxxxxxxxx
---
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)
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 :).
---
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.

Lyude gave me permission to send out the v3 with modifications, as she has
been busy.
---
drivers/pci/quirks.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 88 insertions(+)

diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index de9bbccda21f..f0ea0a722183 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -6169,6 +6169,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: a90ee4305c4a5df72c11b31dacfdc76e00fcf78a
change-id: 20261005-broken-gpe-legion-9c9f87a5898c

Best regards,
--
Ryan Brue <ryanbrue.dev@xxxxxxxxx>