Commit 55f6ac44 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

Merge patch series "mei: connect to card in D3cold"

Alexander Usyskin <alexander.usyskin@intel.com> says:

When discrete graphic card enters D3cold th CSC engine is powered down.

On wakeup from the D3cold full HECI link reset is required.  The driver
should detect that firmware requests link reset and initiate the link
reset flow.

In the usual flow the connect IOCTL will trigger the wake from D3cold
and corresponding link reset.  The MEI driver invalidates all open
handles on link reset including the one that triggered the wake
rendering this connection unusable.  To break this loop make connect
detect that it is interrupted by link reset and retry connect attempt
after reset was completed.

Link: https://lore.kernel.org/r/20250918130435.3327400-1-alexander.usyskin@intel.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parents 68be6c43 f9deb462
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -814,7 +814,7 @@ int mei_cldev_enable(struct mei_cl_device *cldev)

	ret = mei_cl_connect(cl, cldev->me_cl, NULL);
	if (ret < 0) {
		dev_err(&cldev->dev, "cannot connect\n");
		dev_dbg(&cldev->dev, "cannot connect\n");
		mei_cl_bus_vtag_free(cldev);
	}

+17 −42
Original line number Diff line number Diff line
@@ -493,43 +493,6 @@ static int mei_me_hw_ready_wait(struct mei_device *dev)
	return 0;
}

/**
 * mei_me_check_fw_reset - check for the firmware reset error and exception conditions
 *
 * @dev: mei device
 */
static void mei_me_check_fw_reset(struct mei_device *dev)
{
	struct mei_fw_status fw_status;
	char fw_sts_str[MEI_FW_STATUS_STR_SZ] = {0};
	int ret;
	u32 fw_pm_event = 0;

	if (!dev->saved_fw_status_flag)
		goto end;

	if (dev->gsc_reset_to_pxp == MEI_DEV_RESET_TO_PXP_PERFORMED) {
		ret = mei_fw_status(dev, &fw_status);
		if (!ret) {
			fw_pm_event = fw_status.status[1] & PCI_CFG_HFS_2_PM_EVENT_MASK;
			if (fw_pm_event != PCI_CFG_HFS_2_PM_CMOFF_TO_CMX_ERROR &&
			    fw_pm_event != PCI_CFG_HFS_2_PM_CM_RESET_ERROR)
				goto end;
		} else {
			dev_err(&dev->dev, "failed to read firmware status: %d\n", ret);
		}
	}

	mei_fw_status2str(&dev->saved_fw_status, fw_sts_str, sizeof(fw_sts_str));
	dev_warn(&dev->dev, "unexpected reset: fw_pm_event = 0x%x, dev_state = %u fw status = %s\n",
		 fw_pm_event, dev->saved_dev_state, fw_sts_str);

end:
	if (dev->gsc_reset_to_pxp == MEI_DEV_RESET_TO_PXP_PERFORMED)
		dev->gsc_reset_to_pxp = MEI_DEV_RESET_TO_PXP_DONE;
	dev->saved_fw_status_flag = false;
}

/**
 * mei_me_hw_start - hw start routine
 *
@@ -540,8 +503,9 @@ static int mei_me_hw_start(struct mei_device *dev)
{
	int ret = mei_me_hw_ready_wait(dev);

	if (kind_is_gsc(dev) || kind_is_gscfi(dev))
		mei_me_check_fw_reset(dev);
	if ((kind_is_gsc(dev) || kind_is_gscfi(dev)) &&
	    dev->gsc_reset_to_pxp == MEI_DEV_RESET_TO_PXP_PERFORMED)
		dev->gsc_reset_to_pxp = MEI_DEV_RESET_TO_PXP_DONE;
	if (ret)
		return ret;
	dev_dbg(&dev->dev, "hw is ready\n");
@@ -1373,9 +1337,20 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
	/*  check if we need to start the dev */
	if (!mei_host_is_ready(dev)) {
		if (mei_hw_is_ready(dev)) {
			/* synchronized by dev mutex */
			if (waitqueue_active(&dev->wait_hw_ready)) {
				dev_dbg(&dev->dev, "we need to start the dev.\n");
				dev->recvd_hw_ready = true;
				wake_up(&dev->wait_hw_ready);
			} else if (dev->dev_state != MEI_DEV_UNINITIALIZED &&
				   dev->dev_state != MEI_DEV_POWERING_DOWN &&
				   dev->dev_state != MEI_DEV_POWER_DOWN) {
				dev_dbg(&dev->dev, "Force link reset.\n");
				schedule_work(&dev->reset_work);
			} else {
				dev_dbg(&dev->dev, "Ignore this interrupt in state = %d\n",
					dev->dev_state);
			}
		} else {
			dev_dbg(&dev->dev, "Spurious Interrupt\n");
		}
+2 −0
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@
#define MKHI_RCV_TIMEOUT 500 /* receive timeout in msec */
#define MKHI_RCV_TIMEOUT_SLOW 10000 /* receive timeout in msec, slow FW */

#define MEI_LINK_RESET_WAIT_TIMEOUT_MSEC 500  /* Max wait timeout for link reset, in msec */

/*
 * FW page size for DMA allocations
 */
+3 −18
Original line number Diff line number Diff line
@@ -89,22 +89,6 @@ void mei_cancel_work(struct mei_device *dev)
}
EXPORT_SYMBOL_GPL(mei_cancel_work);

static void mei_save_fw_status(struct mei_device *dev)
{
	struct mei_fw_status fw_status;
	int ret;

	ret = mei_fw_status(dev, &fw_status);
	if (ret) {
		dev_err(&dev->dev, "failed to read firmware status: %d\n", ret);
		return;
	}

	dev->saved_dev_state = dev->dev_state;
	dev->saved_fw_status_flag = true;
	memcpy(&dev->saved_fw_status, &fw_status, sizeof(fw_status));
}

/**
 * mei_reset - resets host and fw.
 *
@@ -128,7 +112,6 @@ int mei_reset(struct mei_device *dev)
		if (kind_is_gsc(dev) || kind_is_gscfi(dev)) {
			dev_dbg(&dev->dev, "unexpected reset: dev_state = %s fw status = %s\n",
				mei_dev_state_str(state), fw_sts_str);
			mei_save_fw_status(dev);
		} else {
			dev_warn(&dev->dev, "unexpected reset: dev_state = %s fw status = %s\n",
				 mei_dev_state_str(state), fw_sts_str);
@@ -399,7 +382,8 @@ void mei_device_init(struct mei_device *dev,
	init_waitqueue_head(&dev->wait_hw_ready);
	init_waitqueue_head(&dev->wait_pg);
	init_waitqueue_head(&dev->wait_hbm_start);
	dev->dev_state = MEI_DEV_INITIALIZING;
	dev->dev_state = MEI_DEV_UNINITIALIZED;
	init_waitqueue_head(&dev->wait_dev_state);
	dev->reset_count = 0;

	INIT_LIST_HEAD(&dev->write_list);
@@ -442,5 +426,6 @@ void mei_device_init(struct mei_device *dev,
		dev->timeouts.hbm = mei_secs_to_jiffies(MEI_HBM_TIMEOUT);
		dev->timeouts.mkhi_recv = msecs_to_jiffies(MKHI_RCV_TIMEOUT);
	}
	dev->timeouts.link_reset_wait = msecs_to_jiffies(MEI_LINK_RESET_WAIT_TIMEOUT_MSEC);
}
EXPORT_SYMBOL_GPL(mei_device_init);
+34 −9
Original line number Diff line number Diff line
@@ -423,6 +423,7 @@ static int mei_ioctl_connect_client(struct file *file,
	    cl->state != MEI_FILE_DISCONNECTED)
		return  -EBUSY;

retry:
	/* find ME client we're trying to connect to */
	me_cl = mei_me_cl_by_uuid(dev, in_client_uuid);
	if (!me_cl) {
@@ -454,6 +455,28 @@ static int mei_ioctl_connect_client(struct file *file,

	rets = mei_cl_connect(cl, me_cl, file);

	if (rets && cl->status == -EFAULT &&
	    (dev->dev_state == MEI_DEV_RESETTING ||
	     dev->dev_state == MEI_DEV_INIT_CLIENTS)) {
		/* in link reset, wait for it completion */
		mutex_unlock(&dev->device_lock);
		rets = wait_event_interruptible_timeout(dev->wait_dev_state,
							dev->dev_state == MEI_DEV_ENABLED,
							dev->timeouts.link_reset_wait);
		mutex_lock(&dev->device_lock);
		if (rets < 0) {
			if (signal_pending(current))
				rets = -EINTR;
			goto end;
		}
		if (dev->dev_state != MEI_DEV_ENABLED) {
			rets = -ETIME;
			goto end;
		}
		mei_me_cl_put(me_cl);
		goto retry;
	}

end:
	mei_me_cl_put(me_cl);
	return rets;
@@ -646,7 +669,7 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data)
	struct mei_cl *cl = file->private_data;
	struct mei_connect_client_data conn;
	struct mei_connect_client_data_vtag conn_vtag;
	const uuid_le *cl_uuid;
	uuid_le cl_uuid;
	struct mei_client *props;
	u8 vtag;
	u32 notify_get, notify_req;
@@ -674,18 +697,18 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data)
			rets = -EFAULT;
			goto out;
		}
		cl_uuid = &conn.in_client_uuid;
		cl_uuid = conn.in_client_uuid;
		props = &conn.out_client_properties;
		vtag = 0;

		rets = mei_vt_support_check(dev, cl_uuid);
		rets = mei_vt_support_check(dev, &cl_uuid);
		if (rets == -ENOTTY)
			goto out;
		if (!rets)
			rets = mei_ioctl_connect_vtag(file, cl_uuid, props,
			rets = mei_ioctl_connect_vtag(file, &cl_uuid, props,
						      vtag);
		else
			rets = mei_ioctl_connect_client(file, cl_uuid, props);
			rets = mei_ioctl_connect_client(file, &cl_uuid, props);
		if (rets)
			goto out;

@@ -707,14 +730,14 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data)
			goto out;
		}

		cl_uuid = &conn_vtag.connect.in_client_uuid;
		cl_uuid = conn_vtag.connect.in_client_uuid;
		props = &conn_vtag.out_client_properties;
		vtag = conn_vtag.connect.vtag;

		rets = mei_vt_support_check(dev, cl_uuid);
		rets = mei_vt_support_check(dev, &cl_uuid);
		if (rets == -EOPNOTSUPP)
			cl_dbg(dev, cl, "FW Client %pUl does not support vtags\n",
				cl_uuid);
				&cl_uuid);
		if (rets)
			goto out;

@@ -724,7 +747,7 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data)
			goto out;
		}

		rets = mei_ioctl_connect_vtag(file, cl_uuid, props, vtag);
		rets = mei_ioctl_connect_vtag(file, &cl_uuid, props, vtag);
		if (rets)
			goto out;

@@ -1120,6 +1143,8 @@ void mei_set_devstate(struct mei_device *dev, enum mei_dev_state state)

	dev->dev_state = state;

	wake_up_interruptible_all(&dev->wait_dev_state);

	if (!dev->cdev)
		return;

Loading