Commit 59c752c5 authored by Jiri Kosina's avatar Jiri Kosina
Browse files

Merge branch 'for-6.19/intel-ish-v2' into for-linus

- Power management/hibernation improvements in intel-ish (Zhang Lixu)
- Switch of intel-ish to unbound workqueues (Zhang Lixu)
parents b68822a9 3644f441
Loading
Loading
Loading
Loading
+58 −43
Original line number Diff line number Diff line
@@ -481,6 +481,20 @@ static int timed_wait_for_timeout(struct ishtp_device *dev, int condition,
	return ret;
}

static void ish_send_reset_notify_ack(struct ishtp_device *dev)
{
	/* Read reset ID */
	u32 reset_id = ish_reg_read(dev, IPC_REG_ISH2HOST_MSG) & 0xFFFF;

	/*
	 * Set HOST2ISH.ILUP. Apparently we need this BEFORE sending
	 * RESET_NOTIFY_ACK - FW will be checking for it
	 */
	ish_set_host_rdy(dev);
	/* Send RESET_NOTIFY_ACK (with reset_id) */
	ipc_send_mng_msg(dev, MNG_RESET_NOTIFY_ACK, &reset_id, sizeof(u32));
}

#define TIME_SLICE_FOR_FW_RDY_MS		100
#define TIME_SLICE_FOR_INPUT_RDY_MS		100
#define TIMEOUT_FOR_FW_RDY_MS			2000
@@ -496,13 +510,9 @@ static int timed_wait_for_timeout(struct ishtp_device *dev, int condition,
 */
static int ish_fw_reset_handler(struct ishtp_device *dev)
{
	uint32_t	reset_id;
	unsigned long	flags;
	int ret;

	/* Read reset ID */
	reset_id = ish_reg_read(dev, IPC_REG_ISH2HOST_MSG) & 0xFFFF;

	/* Clear IPC output queue */
	spin_lock_irqsave(&dev->wr_processing_spinlock, flags);
	list_splice_init(&dev->wr_processing_list, &dev->wr_free_list);
@@ -521,15 +531,6 @@ static int ish_fw_reset_handler(struct ishtp_device *dev)
	/* Send clock sync at once after reset */
	ishtp_dev->prev_sync = 0;

	/*
	 * Set HOST2ISH.ILUP. Apparently we need this BEFORE sending
	 * RESET_NOTIFY_ACK - FW will be checking for it
	 */
	ish_set_host_rdy(dev);
	/* Send RESET_NOTIFY_ACK (with reset_id) */
	ipc_send_mng_msg(dev, MNG_RESET_NOTIFY_ACK, &reset_id,
			 sizeof(uint32_t));

	/* Wait for ISH FW'es ILUP and ISHTP_READY */
	ret = timed_wait_for_timeout(dev, WAIT_FOR_FW_RDY,
				     TIME_SLICE_FOR_FW_RDY_MS,
@@ -563,8 +564,6 @@ static void fw_reset_work_fn(struct work_struct *work)
	if (!rv) {
		/* ISH is ILUP & ISHTP-ready. Restart ISHTP */
		msleep_interruptible(TIMEOUT_FOR_HW_RDY_MS);
		ishtp_dev->recvd_hw_ready = 1;
		wake_up_interruptible(&ishtp_dev->wait_hw_ready);

		/* ISHTP notification in IPC_RESET sequence completion */
		if (!work_pending(work))
@@ -625,15 +624,14 @@ static void recv_ipc(struct ishtp_device *dev, uint32_t doorbell_val)
		break;

	case MNG_RESET_NOTIFY:
		if (!ishtp_dev) {
			ishtp_dev = dev;
		}
		schedule_work(&fw_reset_work);
		break;
		ish_send_reset_notify_ack(ishtp_dev);
		fallthrough;

	case MNG_RESET_NOTIFY_ACK:
		dev->recvd_hw_ready = 1;
		wake_up_interruptible(&dev->wait_hw_ready);
		if (!work_pending(&fw_reset_work))
			queue_work(dev->unbound_wq, &fw_reset_work);
		break;
	}
}
@@ -730,22 +728,28 @@ int ish_disable_dma(struct ishtp_device *dev)
 * ish_wakeup() - wakeup ishfw from waiting-for-host state
 * @dev: ishtp device pointer
 *
 * Set the dma enable bit and send a void message to FW,
 * Set the dma enable bit and send a IPC RESET message to FW,
 * it wil wakeup FW from waiting-for-host state.
 *
 * Return: 0 for success else error code.
 */
static void ish_wakeup(struct ishtp_device *dev)
static int ish_wakeup(struct ishtp_device *dev)
{
	int ret;

	/* Set dma enable bit */
	ish_reg_write(dev, IPC_REG_ISH_RMP2, IPC_RMP2_DMA_ENABLED);

	/*
	 * Send 0 IPC message so that ISH FW wakes up if it was already
	 * Send IPC RESET message so that ISH FW wakes up if it was already
	 * asleep.
	 */
	ish_reg_write(dev, IPC_REG_HOST2ISH_DRBL, IPC_DRBL_BUSY_BIT);
	ret = ish_ipc_reset(dev);

	/* Flush writes to doorbell and REMAP2 */
	ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS);

	return ret;
}

/**
@@ -794,11 +798,11 @@ static int _ish_hw_reset(struct ishtp_device *dev)
	pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, csr);

	/* Now we can enable ISH DMA operation and wakeup ISHFW */
	ish_wakeup(dev);

	return	0;
	return ish_wakeup(dev);
}

#define RECVD_HW_READY_TIMEOUT (10 * HZ)

/**
 * _ish_ipc_reset() - IPC reset
 * @dev: ishtp device pointer
@@ -833,7 +837,8 @@ static int _ish_ipc_reset(struct ishtp_device *dev)
	}

	wait_event_interruptible_timeout(dev->wait_hw_ready,
					 dev->recvd_hw_ready, 2 * HZ);
					 dev->recvd_hw_ready,
					 RECVD_HW_READY_TIMEOUT);
	if (!dev->recvd_hw_ready) {
		dev_err(dev->devc, "Timed out waiting for HW ready\n");
		rv = -ENODEV;
@@ -857,21 +862,7 @@ int ish_hw_start(struct ishtp_device *dev)
	set_host_ready(dev);

	/* After that we can enable ISH DMA operation and wakeup ISHFW */
	ish_wakeup(dev);

	/* wait for FW-initiated reset flow */
	if (!dev->recvd_hw_ready)
		wait_event_interruptible_timeout(dev->wait_hw_ready,
						 dev->recvd_hw_ready,
						 10 * HZ);

	if (!dev->recvd_hw_ready) {
		dev_err(dev->devc,
			"[ishtp-ish]: Timed out waiting for FW-initiated reset\n");
		return	-ENODEV;
	}

	return 0;
	return ish_wakeup(dev);
}

/**
@@ -933,6 +924,25 @@ static const struct ishtp_hw_ops ish_hw_ops = {
	.dma_no_cache_snooping = _dma_no_cache_snooping
};

static void ishtp_free_workqueue(void *wq)
{
	destroy_workqueue(wq);
}

static struct workqueue_struct *devm_ishtp_alloc_workqueue(struct device *dev)
{
	struct workqueue_struct *wq;

	wq = alloc_workqueue("ishtp_unbound_%d", WQ_UNBOUND, 0, dev->id);
	if (!wq)
		return NULL;

	if (devm_add_action_or_reset(dev, ishtp_free_workqueue, wq))
		return NULL;

	return wq;
}

/**
 * ish_dev_init() -Initialize ISH devoce
 * @pdev: PCI device
@@ -953,6 +963,10 @@ struct ishtp_device *ish_dev_init(struct pci_dev *pdev)
	if (!dev)
		return NULL;

	dev->unbound_wq = devm_ishtp_alloc_workqueue(&pdev->dev);
	if (!dev->unbound_wq)
		return NULL;

	dev->devc = &pdev->dev;
	ishtp_device_init(dev);

@@ -982,6 +996,7 @@ struct ishtp_device *ish_dev_init(struct pci_dev *pdev)
		list_add_tail(&tx_buf->link, &dev->wr_free_list);
	}

	ishtp_dev = dev;
	ret = devm_work_autocancel(&pdev->dev, &fw_reset_work, fw_reset_work_fn);
	if (ret) {
		dev_err(dev->devc, "Failed to initialise FW reset work\n");
+26 −5
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,12 +388,29 @@ static int __maybe_unused ish_resume(struct device *device)
	ish_resume_device = device;
	dev->resume_flag = 1;

	schedule_work(&resume_work);
	/* 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;
}

static SIMPLE_DEV_PM_OPS(ish_pm_ops, ish_suspend, ish_resume);
static int __maybe_unused ish_freeze(struct device *device)
{
	struct pci_dev *pdev = to_pci_dev(device);

	return pci_save_state(pdev);
}

static const struct dev_pm_ops __maybe_unused ish_pm_ops = {
	.suspend = pm_sleep_ptr(ish_suspend),
	.resume = pm_sleep_ptr(ish_resume),
	.freeze = pm_sleep_ptr(ish_freeze),
	.restore = pm_sleep_ptr(ish_resume),
	.poweroff = pm_sleep_ptr(ish_suspend),
};

static ssize_t base_version_show(struct device *cdev,
				 struct device_attribute *attr, char *buf)
+11 −4
Original line number Diff line number Diff line
@@ -757,8 +757,15 @@ static void hid_ishtp_cl_resume_handler(struct work_struct *work)
	struct ishtp_cl *hid_ishtp_cl = client_data->hid_ishtp_cl;

	if (ishtp_wait_resume(ishtp_get_ishtp_device(hid_ishtp_cl))) {
		/*
		 * Clear the suspended flag only when the connection is established.
		 * If the connection is not established, the suspended flag will be cleared after
		 * the connection is made.
		 */
		if (ishtp_get_connection_state(hid_ishtp_cl) == ISHTP_CL_CONNECTED) {
			client_data->suspended = false;
			wake_up_interruptible(&client_data->ishtp_resume_wait);
		}
	} else {
		hid_ishtp_trace(client_data, "hid client: wait for resume timed out");
		dev_err(cl_data_to_dev(client_data), "wait for resume timed out");
@@ -860,7 +867,7 @@ static int hid_ishtp_cl_reset(struct ishtp_cl_device *cl_device)
	hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
			hid_ishtp_cl);

	schedule_work(&client_data->work);
	queue_work(ishtp_get_workqueue(cl_device), &client_data->work);

	return 0;
}
@@ -902,7 +909,7 @@ static int hid_ishtp_cl_resume(struct device *device)

	hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
			hid_ishtp_cl);
	schedule_work(&client_data->resume_work);
	queue_work(ishtp_get_workqueue(cl_device), &client_data->resume_work);
	return 0;
}

+17 −1
Original line number Diff line number Diff line
@@ -541,7 +541,7 @@ void ishtp_cl_bus_rx_event(struct ishtp_cl_device *device)
		return;

	if (device->event_cb)
		schedule_work(&device->event_work);
		queue_work(device->ishtp_dev->unbound_wq, &device->event_work);
}

/**
@@ -876,6 +876,22 @@ struct device *ishtp_get_pci_device(struct ishtp_cl_device *device)
}
EXPORT_SYMBOL(ishtp_get_pci_device);

/**
 * ishtp_get_workqueue - Retrieve the workqueue associated with an ISHTP device
 * @cl_device: Pointer to the ISHTP client device structure
 *
 * Returns the workqueue_struct pointer (unbound_wq) associated with the given
 * ISHTP client device. This workqueue is typically used for scheduling work
 * related to the device.
 *
 * Return: Pointer to struct workqueue_struct.
 */
struct workqueue_struct *ishtp_get_workqueue(struct ishtp_cl_device *cl_device)
{
	return cl_device->ishtp_dev->unbound_wq;
}
EXPORT_SYMBOL(ishtp_get_workqueue);

/**
 * ishtp_trace_callback() - Return trace callback
 * @cl_device: ISH-TP client device instance
+6 −0
Original line number Diff line number Diff line
@@ -1261,6 +1261,12 @@ void ishtp_set_connection_state(struct ishtp_cl *cl, int state)
}
EXPORT_SYMBOL(ishtp_set_connection_state);

int ishtp_get_connection_state(struct ishtp_cl *cl)
{
	return cl->state;
}
EXPORT_SYMBOL(ishtp_get_connection_state);

void ishtp_cl_set_fw_client_id(struct ishtp_cl *cl, int fw_client_id)
{
	cl->fw_client_id = fw_client_id;
Loading