]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
HID: intel-ish-ipc: Reset clients state on resume from D3
authorZhang Lixu <lixu.zhang@intel.com>
Fri, 17 Oct 2025 02:22:15 +0000 (10:22 +0800)
committerJiri Kosina <jkosina@suse.com>
Fri, 17 Oct 2025 15:47:53 +0000 (17:47 +0200)
When ISH resumes from D3, the connection between ishtp clients and firmware
is lost. The ish_resume() function schedules resume_work asynchronously to
re-initiate the connection and then returns immediately. This can cause a
race where the upper-layer ishtp client driver's .resume() may execute
before the connection is fully restored, leaving the client in a stale
connected state. If the client sends messages during this window, the
firmware cannot respond.

To avoid this, reset the ishtp clients' state before returning from
ish_resume() if ISH is resuming from D3.

Signed-off-by: Zhang Lixu <lixu.zhang@intel.com>
Acked-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
drivers/hid/intel-ish-hid/ipc/pci-ish.c

index b748ac6fbfdc73c0c0b1ac224b66c6ffa6076e46..e4499c83c62efcf9fab971250054ec48d0528f98 100644 (file)
@@ -147,6 +147,12 @@ static inline bool ish_should_enter_d0i3(struct pci_dev *pdev)
 
 static inline bool ish_should_leave_d0i3(struct pci_dev *pdev)
 {
+       struct ishtp_device *dev = pci_get_drvdata(pdev);
+       u32 fwsts = dev->ops->get_fw_status(dev);
+
+       if (dev->suspend_flag || !IPC_IS_ISH_ILUP(fwsts))
+               return false;
+
        return !pm_resume_via_firmware() || pdev->device == PCI_DEVICE_ID_INTEL_ISH_CHV;
 }
 
@@ -277,10 +283,8 @@ static void __maybe_unused ish_resume_handler(struct work_struct *work)
 {
        struct pci_dev *pdev = to_pci_dev(ish_resume_device);
        struct ishtp_device *dev = pci_get_drvdata(pdev);
-       uint32_t fwsts = dev->ops->get_fw_status(dev);
 
-       if (ish_should_leave_d0i3(pdev) && !dev->suspend_flag
-                       && IPC_IS_ISH_ILUP(fwsts)) {
+       if (ish_should_leave_d0i3(pdev)) {
                if (device_may_wakeup(&pdev->dev))
                        disable_irq_wake(pdev->irq);
 
@@ -384,6 +388,10 @@ static int __maybe_unused ish_resume(struct device *device)
        ish_resume_device = device;
        dev->resume_flag = 1;
 
+       /* If ISH resume from D3, reset ishtp clients before return */
+       if (!ish_should_leave_d0i3(pdev))
+               ishtp_reset_handler(dev);
+
        queue_work(dev->unbound_wq, &resume_work);
 
        return 0;