msm: mhi_dev: Add support for MSI disable

PCIe endpoint device informs PCIe root complex/host about completion
or availability of new work using MSI. In some cases like UEFI, MSI
functionality is not supported by used PCIe root complex/host, and
can work in polling mode only. In MSI not supported case, endpoint
is not allowed to raise MSI. This change queries MSI functionality
support with root complex/host, and handle case where MSI
functionality is not supported by polling MHI state, and not
invoking MSIs towards host.

Change-Id: Ie9c78f40fe7e9735d3e0104f845e29ac82d76778
Signed-off-by: Mohana Basava Tejesh Reddy Mareddy <quic_mmareddy@quicinc.com>
This commit is contained in:
Mohana Basava Tejesh Reddy Mareddy 2022-12-20 20:43:32 -08:00
commit ed328e7bfc
3 changed files with 66 additions and 9 deletions

View file

@ -281,7 +281,11 @@ static int mhi_dev_schedule_msi_ipa(struct mhi_dev *mhi, struct event_req *ereq)
int rc;
rc = ep_pcie_get_msi_config(mhi->phandle, &cfg);
if (rc) {
if (rc == -EOPNOTSUPP) {
mhi_log(MHI_MSG_VERBOSE, "MSI is disabled\n");
mhi_ctx->msi_disable = true;
return 0;
} else if (rc) {
mhi_log(MHI_MSG_ERROR, "Error retrieving pcie msi logic\n");
return rc;
}
@ -335,6 +339,24 @@ static void mhi_dev_event_rd_offset_completion_cb(void *req)
if (ereq->event_rd_dma)
dma_unmap_single(&mhi_ctx->pdev->dev, ereq->event_rd_dma,
sizeof(uint64_t), DMA_TO_DEVICE);
/*
* The mhi_dev_cmd_event_msi_cb and mhi_dev_event_msi_cb APIs does
* add back the flushed events space to the event buffer and returns
* the event req back to the list. These are registered in the API
* mhi_dev_schedule_msi_ipa and get invoked when MSI triggering is
* complete.
* In the case of MSI being disabled by the host, these callbacks will
* not get invoked as triggering MSI is suppressed from device side.
* Hence, invoking these callbacks as part of this API to ensure we do
* not run out on ereq buffer space in this scenario.
*/
if (mhi_ctx->msi_disable) {
if (ereq->is_cmd_cpl)
mhi_dev_cmd_event_msi_cb(ereq);
else
mhi_dev_event_msi_cb(ereq);
}
}
static void mhi_dev_cmd_event_msi_cb(void *req)
@ -415,7 +437,11 @@ static int mhi_trigger_msi_edma(struct mhi_dev_ring *ring, u32 idx)
if (!mhi_ctx->msi_lower) {
rc = ep_pcie_get_msi_config(mhi_ctx->phandle, &cfg);
if (rc) {
if (rc == -EOPNOTSUPP) {
mhi_log(MHI_MSG_VERBOSE, "MSI is disabled\n");
mhi_ctx->msi_disable = true;
return 0;
} else if (rc) {
mhi_log(MHI_MSG_ERROR,
"Error retrieving pcie msi logic\n");
return rc;
@ -1425,7 +1451,10 @@ static int mhi_hwc_init(struct mhi_dev *mhi)
/* Call IPA HW_ACC Init with MSI Address and db routing info */
rc = ep_pcie_get_msi_config(mhi_ctx->phandle, &cfg);
if (rc) {
if (rc == -EOPNOTSUPP) {
mhi_log(MHI_MSG_VERBOSE, "MSI is disabled\n");
mhi_ctx->msi_disable = true;
} else if (rc) {
mhi_log(MHI_MSG_ERROR,
"Error retrieving pcie msi logic\n");
return rc;
@ -1450,6 +1479,7 @@ static int mhi_hwc_init(struct mhi_dev *mhi)
ipa_init_params.msi.mask = ((1 << cfg.msg_num) - 1);
ipa_init_params.first_er_idx = erdb_cfg.base;
ipa_init_params.first_ch_idx = HW_CHANNEL_BASE;
ipa_init_params.disable_msi = mhi_ctx->msi_disable;
if (mhi_ctx->config_iatu)
ipa_init_params.mmio_addr =
@ -1642,7 +1672,10 @@ int mhi_dev_send_event(struct mhi_dev *mhi, int evnt_ring,
struct mhi_addr transfer_addr;
rc = ep_pcie_get_msi_config(mhi->phandle, &cfg);
if (rc) {
if (rc == -EOPNOTSUPP) {
mhi_log(MHI_MSG_VERBOSE, "MSI is disabled\n");
mhi_ctx->msi_disable = true;
} else if (rc) {
mhi_log(MHI_MSG_ERROR, "Error retrieving pcie msi logic\n");
return rc;
}
@ -3759,16 +3792,19 @@ static int mhi_dev_recover(struct mhi_dev *mhi)
mhi_log(MHI_MSG_VERBOSE, "mhi_state = 0x%X, reset = %d\n",
state, mhi_reset);
if (mhi_ctx->msi_disable)
goto poll_for_reset;
rc = mhi_dev_mmio_read(mhi, BHI_INTVEC, &bhi_intvec);
if (rc)
return rc;
while (bhi_intvec == 0xffffffff &&
bhi_max_cnt < MHI_BHI_INTVEC_MAX_CNT) {
bhi_max_cnt < MHI_BHI_INTVEC_MAX_CNT) {
/* Wait for Host to set the bhi_intvec */
msleep(MHI_BHI_INTVEC_WAIT_MS);
mhi_log(MHI_MSG_VERBOSE,
"Wait for Host to set BHI_INTVEC\n");
"Wait for Host to set BHI_INTVEC\n");
rc = mhi_dev_mmio_read(mhi, BHI_INTVEC, &bhi_intvec);
if (rc) {
mhi_log(MHI_MSG_ERROR,
@ -3780,12 +3816,12 @@ static int mhi_dev_recover(struct mhi_dev *mhi)
if (bhi_max_cnt == MHI_BHI_INTVEC_MAX_CNT) {
mhi_log(MHI_MSG_ERROR,
"Host failed to set BHI_INTVEC\n");
"Host failed to set BHI_INTVEC\n");
return -EINVAL;
}
if (bhi_intvec != 0xffffffff) {
/* Indicate the host that the device is ready */
/* Indicate the host that device is ready */
rc = ep_pcie_trigger_msi(mhi->phandle, bhi_intvec);
if (rc) {
mhi_log(MHI_MSG_ERROR, "error sending msi\n");
@ -3793,6 +3829,7 @@ static int mhi_dev_recover(struct mhi_dev *mhi)
}
}
poll_for_reset:
/* Poll for the host to set the reset bit */
rc = mhi_dev_mmio_get_mhi_state(mhi, &state, &mhi_reset);
if (rc) {
@ -4313,6 +4350,7 @@ static void mhi_dev_reinit(struct work_struct *work)
static int mhi_dev_resume_mmio_mhi_init(struct mhi_dev *mhi_ctx)
{
struct platform_device *pdev;
struct ep_pcie_msi_config cfg;
int rc = 0;
/*
@ -4360,6 +4398,24 @@ static int mhi_dev_resume_mmio_mhi_init(struct mhi_dev *mhi_ctx)
return -EINVAL;
}
/*
* Fetching MSI config to read the MSI capability and setting the
* msi_disable flag based on it.
*/
rc = ep_pcie_get_msi_config(mhi_ctx->phandle, &cfg);
if (rc == -EOPNOTSUPP) {
mhi_log(MHI_MSG_VERBOSE, "MSI is disabled\n");
mhi_ctx->msi_disable = true;
} else if (!rc) {
mhi_ctx->msi_disable = false;
} else {
mhi_log(MHI_MSG_ERROR,
"Error retrieving pcie msi logic\n");
return rc;
}
mhi_log(MHI_MSG_VERBOSE, "msi_disable = %d\n", mhi_ctx->msi_disable);
rc = mhi_dev_recover(mhi_ctx);
if (rc) {
mhi_log(MHI_MSG_ERROR, "get mhi state failed\n");

View file

@ -527,6 +527,7 @@ struct mhi_dev {
uint32_t *mmio_backup;
struct mhi_config cfg;
bool msi_disable;
u32 msi_data;
u32 msi_lower;
spinlock_t msi_lock;

View file

@ -479,7 +479,7 @@ static int mhi_sm_prepare_resume(void)
case MHI_DEV_READY_STATE:
res = ep_pcie_get_msi_config(mhi_sm_ctx->mhi_dev->phandle,
&cfg);
if (res) {
if (res && res != -EOPNOTSUPP) {
MHI_SM_ERR("Error retrieving pcie msi logic\n");
goto exit;
}