Make the device a usable wakeup source so it can raise PME while runtime suspended to D3hot The PCI core marks endpoints wakeup-capable but leaves them disabled, so pci_enable_wake() returns -EINVAL until the driver opts in using devm_device_init_wakeup(). D3cold is excluded, as the device loses power there and cannot signal at all.
Assisted-by: GitHub_Copilot:claude-opus-5 Signed-off-by: Vinod Govindapillai <[email protected]> --- drivers/gpu/drm/xe/xe_pci.c | 22 ++++++++++++++++++++-- drivers/gpu/drm/xe/xe_pm.c | 25 +++++++++++++++++++++++++ drivers/gpu/drm/xe/xe_pm.h | 1 + 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c index c22b40045f7b..42c933390d0d 100644 --- a/drivers/gpu/drm/xe/xe_pci.c +++ b/drivers/gpu/drm/xe/xe_pci.c @@ -1217,6 +1217,10 @@ static int __xe_pci_probe(struct pci_dev *pdev, const struct xe_device_desc *des pci_set_master(pdev); + err = devm_device_init_wakeup(&pdev->dev); + if (err) + return err; + err = xe_probe_info_early(xe, desc, &probed_info); if (err) return err; @@ -1381,6 +1385,7 @@ static int xe_pci_runtime_suspend(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct xe_device *xe = pdev_to_xe_device(pdev); + bool pme_armed = false; int err; /* @@ -1392,9 +1397,20 @@ static int xe_pci_runtime_suspend(struct device *dev) xe_assert(xe, !IS_SRIOV_VF(xe)); xe_assert(xe, !pci_num_vf(pdev)); - err = xe_pm_runtime_suspend(xe, false); - if (err) + if (xe_pm_pme_capable(xe)) { + pme_armed = !pci_enable_wake(pdev, PCI_D3hot, true); + if (!pme_armed) + drm_warn(&xe->drm, + "Failed to arm PME for D3hot: %d\n", err); + } + + err = xe_pm_runtime_suspend(xe, pme_armed); + if (err) { + if (pme_armed) + pci_enable_wake(pdev, PCI_D3hot, false); + return err; + } pci_save_state(pdev); @@ -1421,6 +1437,8 @@ static int xe_pci_runtime_resume(struct device *dev) if (err) return err; + pci_enable_wake(pdev, PCI_D3hot, false); + pci_restore_state(pdev); if (xe->d3cold.allowed) { diff --git a/drivers/gpu/drm/xe/xe_pm.c b/drivers/gpu/drm/xe/xe_pm.c index c3a53c5f8b33..e488bfa086ea 100644 --- a/drivers/gpu/drm/xe/xe_pm.c +++ b/drivers/gpu/drm/xe/xe_pm.c @@ -555,6 +555,31 @@ struct task_struct *xe_pm_read_callback_task(struct xe_device *xe) return READ_ONCE(xe->pm_callback_task); } +/** + * xe_pm_pme_capable - Can the device raise PME from runtime D3hot? + * @xe: xe device instance + * + * Only meaningful on the D3hot runtime suspend path: on D3cold the device + * loses power and cannot signal a hotplug. Honours the user-visible + * power/wakeup policy via device_may_wakeup(), so a user who disables + * wakeup falls back to the normal reset-and-poll behaviour rather than + * losing both mechanisms. + * + * Return: true if PME can be armed for D3hot. + */ +bool xe_pm_pme_capable(struct xe_device *xe) +{ + struct pci_dev *pdev = to_pci_dev(xe->drm.dev); + + if (xe->d3cold.allowed) + return false; + + if (!pci_pme_capable(pdev, PCI_D3hot)) + return false; + + return device_may_wakeup(&pdev->dev); +} + /** * xe_pm_runtime_suspended - Check if runtime_pm state is suspended * @xe: xe device instance diff --git a/drivers/gpu/drm/xe/xe_pm.h b/drivers/gpu/drm/xe/xe_pm.h index 27455a8d2061..876afd5bebc6 100644 --- a/drivers/gpu/drm/xe/xe_pm.h +++ b/drivers/gpu/drm/xe/xe_pm.h @@ -23,6 +23,7 @@ void xe_pm_fini(struct xe_device *xe); bool xe_pm_runtime_suspended(struct xe_device *xe); int xe_pm_runtime_suspend(struct xe_device *xe, bool pme_capable); int xe_pm_runtime_resume(struct xe_device *xe); +bool xe_pm_pme_capable(struct xe_device *xe); void xe_pm_runtime_get(struct xe_device *xe); int xe_pm_runtime_get_ioctl(struct xe_device *xe); void xe_pm_runtime_put(struct xe_device *xe); -- 2.43.0
