Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8f63744f authored by Hemant Kumar's avatar Hemant Kumar
Browse files

mhi: controller: qcom: Enable MHI register write offload support



When PCIe endpoint enters L1SS sleep and mhi client on Host tries
to queue a transfer request endpoint takes more than 6ms to come
back to L0 state. This can cause CPU stall if MHI register write
is followed by a write memory barrier. This can cause other tasks
to get blocked. Prevent this situation by offloading MHI register
write to a high priority worker thread when in AMSS. When thread gets
a chance to run it would first bring the link to L0 and then perform
register write.

Change-Id: I1b4839801df140fc50973e92e2522eb57d2897fa
Signed-off-by: default avatarHemant Kumar <hemantk@codeaurora.org>
parent 99333ce2
Loading
Loading
Loading
Loading
+31 −0
Original line number Diff line number Diff line
@@ -54,6 +54,37 @@ enum MHI_DEBUG_LEVEL mhi_ipc_log_lvl = MHI_MSG_LVL_ERROR;

#endif

void mhi_reg_write_work(struct work_struct *w)
{
	struct mhi_controller *mhi_cntrl = container_of(w,
						struct mhi_controller,
						reg_write_work);
	struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl);
	struct pci_dev *pci_dev = mhi_dev->pci_dev;
	struct reg_write_info *info =
				&mhi_cntrl->reg_write_q[mhi_cntrl->read_idx];

	if (!info->valid)
		return;

	if (mhi_is_active(mhi_cntrl->mhi_dev) && msm_pcie_prevent_l1(pci_dev))
		return;

	while (info->valid) {
		if (!mhi_is_active(mhi_cntrl->mhi_dev))
			return;

		writel_relaxed(info->val, info->reg_addr);
		info->valid = false;
		mhi_cntrl->read_idx =
				(mhi_cntrl->read_idx + 1) &
						(REG_WRITE_QUEUE_LEN - 1);
		info = &mhi_cntrl->reg_write_q[mhi_cntrl->read_idx];
	}

	msm_pcie_allow_l1(pci_dev);
}

static int mhi_arch_pm_notifier(struct notifier_block *nb,
				unsigned long event, void *unused)
{
+17 −0
Original line number Diff line number Diff line
@@ -840,11 +840,28 @@ static struct mhi_controller *mhi_register_controller(struct pci_dev *pci_dev)
	mhi_cntrl->fw_image = firmware_info->fw_image;
	mhi_cntrl->edl_image = firmware_info->edl_image;

	mhi_cntrl->offload_wq = alloc_ordered_workqueue("offload_wq",
						WQ_MEM_RECLAIM | WQ_HIGHPRI);
	if (!mhi_cntrl->offload_wq)
		goto error_register;

	INIT_WORK(&mhi_cntrl->reg_write_work, mhi_reg_write_work);

	mhi_cntrl->reg_write_q = kcalloc(REG_WRITE_QUEUE_LEN,
					sizeof(*mhi_cntrl->reg_write_q),
					GFP_KERNEL);
	if (!mhi_cntrl->reg_write_q)
		goto error_free_wq;

	atomic_set(&mhi_cntrl->write_idx, -1);

	if (sysfs_create_group(&mhi_cntrl->mhi_dev->dev.kobj, &mhi_qcom_group))
		MHI_ERR("Error while creating the sysfs group\n");

	return mhi_cntrl;

error_free_wq:
	destroy_workqueue(mhi_cntrl->offload_wq);
error_register:
	mhi_free_controller(mhi_cntrl);

+1 −0
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ struct mhi_dev {
void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl);
int mhi_pci_probe(struct pci_dev *pci_dev,
		  const struct pci_device_id *device_id);
void mhi_reg_write_work(struct work_struct *w);

#ifdef CONFIG_ARCH_QCOM