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

Reply via email to