From 574f72b01f51c8ae7269398d2a760dea67427d96 Mon Sep 17 00:00:00 2001 From: Hemant Kumar Date: Tue, 24 Sep 2019 19:42:14 -0700 Subject: [PATCH] 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: Hemant Kumar --- drivers/bus/mhi/controllers/mhi_arch_qcom.c | 31 +++++++++++++++++++++ drivers/bus/mhi/controllers/mhi_qcom.c | 17 +++++++++++ drivers/bus/mhi/controllers/mhi_qcom.h | 1 + 3 files changed, 49 insertions(+) diff --git a/drivers/bus/mhi/controllers/mhi_arch_qcom.c b/drivers/bus/mhi/controllers/mhi_arch_qcom.c index d3faadbc1bc4..dd3396b32a08 100644 --- a/drivers/bus/mhi/controllers/mhi_arch_qcom.c +++ b/drivers/bus/mhi/controllers/mhi_arch_qcom.c @@ -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) { diff --git a/drivers/bus/mhi/controllers/mhi_qcom.c b/drivers/bus/mhi/controllers/mhi_qcom.c index cb70405ac303..244aa4011f3d 100644 --- a/drivers/bus/mhi/controllers/mhi_qcom.c +++ b/drivers/bus/mhi/controllers/mhi_qcom.c @@ -754,11 +754,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); diff --git a/drivers/bus/mhi/controllers/mhi_qcom.h b/drivers/bus/mhi/controllers/mhi_qcom.h index 9c983e8fd5c4..692a4f4967fb 100644 --- a/drivers/bus/mhi/controllers/mhi_qcom.h +++ b/drivers/bus/mhi/controllers/mhi_qcom.h @@ -60,6 +60,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