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 Cc: stable@vger.kernel.org --- 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