Commit bd1b9a8d authored by Zhang Lixu's avatar Zhang Lixu Committed by Jiri Kosina
Browse files

HID: intel-ish-ipc: Reset clients state on resume from D3



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: default avatarZhang Lixu <lixu.zhang@intel.com>
Acked-by: default avatarSrinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.com>
parent 3cbf6544
Loading
Loading
Loading
Loading
+11 −3
Original line number Diff line number Diff line
@@ -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;