From b9ce34ea1e66930fdebe76ff26d39cf4fb650bde Mon Sep 17 00:00:00 2001 From: Michael Adisumarta Date: Mon, 23 Sep 2019 17:55:53 -0700 Subject: [PATCH] msm:ipa: unvote PCIe clock only if it was voted before this fix makes sure to unvote PCIE clock only if it was voted before from the same function. Change-Id: Ie4060b7c54598ef7aa1b8b4a4a8f06d2067d2542 Signed-off-by: Michael Adisumarta --- drivers/platform/msm/ipa/ipa_v3/ipa_mpm.c | 175 ++++++++++++++++------ 1 file changed, 127 insertions(+), 48 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mpm.c b/drivers/platform/msm/ipa/ipa_v3/ipa_mpm.c index 22af3c478a13..379190850b86 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_mpm.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mpm.c @@ -71,6 +71,7 @@ #define IPA_CHANNEL_STOP_IN_PROC_TO_MSEC 5 #define IPA_CHANNEL_STOP_IN_PROC_SLEEP_USEC 200 #define IPA_MHIP_HOLB_TMO 31 /* value to match granularity on ipa HW 4.5 */ + enum mhip_re_type { MHIP_RE_XFER = 0x2, MHIP_RE_NOP = 0x4, @@ -420,7 +421,7 @@ static void ipa_mpm_change_gsi_state(int probe_id, enum ipa_mpm_gsi_state next_state); static int ipa_mpm_probe(struct platform_device *pdev); static int ipa_mpm_vote_unvote_pcie_clk(enum ipa_mpm_clk_vote_type vote, - int probe_id); + int probe_id, bool is_force, bool *is_acted); static void ipa_mpm_vote_unvote_ipa_clk(enum ipa_mpm_clk_vote_type vote, int probe_id); static enum mhip_status_type ipa_mpm_start_stop_mhip_chan( @@ -1364,12 +1365,20 @@ static void ipa_mpm_mhip_shutdown(int mhip_idx) IPA_MPM_FUNC_EXIT(); } -/* - * Turning on/OFF PCIE Clock is done once for all clients. - * Always vote for Probe_ID 0 as a standard. +/** + * @ipa_mpm_vote_unvote_pcie_clk - Vote/Unvote PCIe Clock per probe_id + * Returns if success or failure. + * @ipa_mpm_clk_vote_type - Vote or Unvote for PCIe Clock + * @probe_id - MHI probe_id per client. + * @is_force - Forcebly casts vote - should be true only in probe. + * @is_acted - Output param - This indicates the clk is actually voted or not + * The flag output is checked only when we vote for clocks. + * Return value: PCIe clock voting is success or failure. */ static int ipa_mpm_vote_unvote_pcie_clk(enum ipa_mpm_clk_vote_type vote, - int probe_id) + int probe_id, + bool is_force, + bool *is_acted) { int result = 0; @@ -1383,20 +1392,28 @@ static int ipa_mpm_vote_unvote_pcie_clk(enum ipa_mpm_clk_vote_type vote, return -EINVAL; } + if (!is_acted) { + IPA_MPM_ERR("Invalid clk_vote ptr\n"); + return -EFAULT; + } + mutex_lock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); if (ipa_mpm_ctx->md[probe_id].mhi_dev == NULL) { IPA_MPM_ERR("MHI not initialized yet\n"); + *is_acted = false; mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); return 0; } - if (!ipa_mpm_ctx->md[probe_id].init_complete) { + if (!ipa_mpm_ctx->md[probe_id].init_complete && + !is_force) { /* * SSR might be in progress, dont have to vote/unvote for * IPA clocks as it will be taken care in remove_cb/subsequent * probe. */ IPA_MPM_DBG("SSR in progress, return\n"); + *is_acted = false; mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); return 0; } @@ -1412,6 +1429,7 @@ static int ipa_mpm_vote_unvote_pcie_clk(enum ipa_mpm_clk_vote_type vote, if (result) { IPA_MPM_ERR("mhi_sync_get failed for probe_id %d\n", result, probe_id); + *is_acted = false; return result; } @@ -1425,6 +1443,7 @@ static int ipa_mpm_vote_unvote_pcie_clk(enum ipa_mpm_clk_vote_type vote, IPA_MPM_DBG("probe_id %d PCIE clock already devoted\n", probe_id); WARN_ON(1); + *is_acted = true; return 0; } mhi_device_put(ipa_mpm_ctx->md[probe_id].mhi_dev, MHI_VOTE_BUS); @@ -1432,6 +1451,7 @@ static int ipa_mpm_vote_unvote_pcie_clk(enum ipa_mpm_clk_vote_type vote, atomic_dec(&ipa_mpm_ctx->md[probe_id].clk_cnt.pcie_clk_cnt); atomic_dec(&ipa_mpm_ctx->pcie_clk_total_cnt); } + *is_acted = true; return result; } @@ -1476,16 +1496,24 @@ static int ipa_mpm_start_stop_remote_mhip_chan( int ret = 0; struct mhi_device *mhi_dev = ipa_mpm_ctx->md[probe_id].mhi_dev; - if (!mhi_dev) { - IPA_MPM_ERR("MHI not initialized yet\n"); + /* Sanity check to make sure Remote channels can be started. + * If probe in progress, mhi_prepare_for_transfer will start + * the remote channels so no need to start it from here. + */ + mutex_lock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); + if (!ipa_mpm_ctx->md[probe_id].init_complete) { + IPA_MPM_ERR("MHI not initialized yet, probe in progress\n"); + mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); return ret; } /* For error state, expect modem SSR to recover from error */ if (ipa_mpm_ctx->md[probe_id].remote_state == MPM_MHIP_REMOTE_ERR) { IPA_MPM_ERR("Remote channels in err state for %d\n", probe_id); + mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); return -EFAULT; } + mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); if (start_stop == MPM_MHIP_START) { if (ipa_mpm_ctx->md[probe_id].remote_state == @@ -1690,6 +1718,7 @@ int ipa_mpm_notify_wan_state(struct wan_ioctl_notify_wan_state *state) static enum mhip_status_type status; int ret = 0; enum ipa_mpm_mhip_client_type mhip_client = IPA_MPM_MHIP_TETH; + bool is_acted = true; if (!state) return -EPERM; @@ -1713,10 +1742,10 @@ int ipa_mpm_notify_wan_state(struct wan_ioctl_notify_wan_state *state) if (state->up) { /* Start UL MHIP channel for offloading tethering connection */ - ret = ipa_mpm_vote_unvote_pcie_clk(CLK_ON, probe_id); + ret = ipa_mpm_vote_unvote_pcie_clk(CLK_ON, probe_id, + false, &is_acted); if (ret) { - IPA_MPM_ERR("Error cloking on PCIe clk, err = %d\n", - ret); + IPA_MPM_ERR("Err %d cloking on PCIe clk %d\n", ret); return ret; } @@ -1735,6 +1764,13 @@ int ipa_mpm_notify_wan_state(struct wan_ioctl_notify_wan_state *state) * so return a failure. */ IPA_MPM_ERR("MHIP remote chan start fail = %d\n", ret); + + if (is_acted) + ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, + probe_id, + false, + &is_acted); + return ret; } IPA_MPM_DBG("MHIP remote channels are started\n"); @@ -1750,18 +1786,27 @@ int ipa_mpm_notify_wan_state(struct wan_ioctl_notify_wan_state *state) case MHIP_STATUS_NO_OP: IPA_MPM_DBG("UL chan already start, status = %d\n", status); - ret = ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id); - return ret; + if (is_acted) { + return ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, + probe_id, + false, + &is_acted); + } + break; case MHIP_STATUS_FAIL: case MHIP_STATUS_BAD_STATE: case MHIP_STATUS_EP_NOT_FOUND: IPA_MPM_ERR("UL chan start err =%d\n", status); - ret = ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id); + if (is_acted) + ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id, + false, &is_acted); ipa_assert(); return -EFAULT; default: IPA_MPM_ERR("Err not found\n"); - ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id); + if (is_acted) + ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id, + false, &is_acted); ret = -EFAULT; break; } @@ -1810,7 +1855,8 @@ int ipa_mpm_notify_wan_state(struct wan_ioctl_notify_wan_state *state) return -EFAULT; } /* Stop UL MHIP channel for offloading tethering connection */ - ret = ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id); + ret = ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id, + false, &is_acted); if (ret) { IPA_MPM_ERR("Error cloking off PCIe clk, err = %d\n", @@ -1921,6 +1967,7 @@ static int ipa_mpm_mhi_probe_cb(struct mhi_device *mhi_dev, u32 evt_ring_db_addr_low, evt_ring_db_addr_high; u32 wp_addr; int pipe_idx; + bool is_acted = true; IPA_MPM_FUNC_ENTRY(); @@ -1959,7 +2006,12 @@ static int ipa_mpm_mhi_probe_cb(struct mhi_device *mhi_dev, mutex_lock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); ipa_mpm_ctx->md[probe_id].remote_state = MPM_MHIP_REMOTE_STOP; mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); - ipa_mpm_vote_unvote_pcie_clk(CLK_ON, probe_id); + ret = ipa_mpm_vote_unvote_pcie_clk(CLK_ON, probe_id, true, &is_acted); + if (ret) { + IPA_MPM_ERR("Err %d voitng PCIe clocks\n", ret); + return -EPERM; + } + ipa_mpm_vote_unvote_ipa_clk(CLK_ON, probe_id); ipa_mpm_ctx->md[probe_id].in_lpm = false; IPA_MPM_DBG("ul chan = %d, dl_chan = %d\n", ul_prod, dl_cons); @@ -2260,7 +2312,9 @@ static int ipa_mpm_mhi_probe_cb(struct mhi_device *mhi_dev, ipa_mpm_start_stop_mhip_chan(IPA_MPM_MHIP_CHAN_UL, probe_id, MPM_MHIP_STOP); } - ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id); + if (is_acted) + ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id, true, + &is_acted); break; case IPA_MPM_TETH_INPROGRESS: case IPA_MPM_TETH_CONNECTED: @@ -2298,7 +2352,9 @@ fail_start_channel: fail_smmu: if (ipa_mpm_ctx->dev_info.ipa_smmu_enabled) IPA_MPM_DBG("SMMU failed\n"); - ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id); + if (is_acted) + ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id, true, + &is_acted); ipa_mpm_vote_unvote_ipa_clk(CLK_OFF, probe_id); ipa_assert(); return ret; @@ -2445,8 +2501,8 @@ static void ipa_mpm_mhi_status_cb(struct mhi_device *mhi_dev, case MHI_CB_PENDING_DATA: case MHI_CB_SYS_ERROR: case MHI_CB_FATAL_ERROR: - case MHI_CB_BW_REQ: case MHI_CB_EE_MISSION_MODE: + case MHI_CB_DTR_SIGNAL: IPA_MPM_ERR("unexpected event %d\n", mhi_cb); break; } @@ -2479,8 +2535,9 @@ int ipa_mpm_mhip_xdci_pipe_enable(enum ipa_usb_teth_prot xdci_teth_prot) int i; enum ipa_mpm_mhip_client_type mhip_client; enum mhip_status_type status; - int ret = 0; int pipe_idx; + bool is_acted = true; + int ret = 0; if (ipa_mpm_ctx == NULL) { IPA_MPM_ERR("MPM not platform probed yet, returning ..\n"); @@ -2506,10 +2563,11 @@ int ipa_mpm_mhip_xdci_pipe_enable(enum ipa_usb_teth_prot xdci_teth_prot) ipa_mpm_ctx->md[probe_id].mhip_client = mhip_client; - ret = ipa_mpm_vote_unvote_pcie_clk(CLK_ON, probe_id); + ret = ipa_mpm_vote_unvote_pcie_clk(CLK_ON, probe_id, + false, &is_acted); if (ret) { IPA_MPM_ERR("Error cloking on PCIe clk, err = %d\n", ret); - return ret; + return ret; } /* @@ -2524,7 +2582,7 @@ int ipa_mpm_mhip_xdci_pipe_enable(enum ipa_usb_teth_prot xdci_teth_prot) /* * This can fail only when modem is in SSR state. * Eventually there would be a remove callback, - * so return a failure. + * so return a failure. Dont have to unvote PCIE here. */ IPA_MPM_ERR("MHIP remote chan start fail = %d\n", ret); @@ -2545,11 +2603,14 @@ int ipa_mpm_mhip_xdci_pipe_enable(enum ipa_usb_teth_prot xdci_teth_prot) return 0; default: IPA_MPM_DBG("mhip_client = %d not processed\n", mhip_client); - ret = ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id); - if (ret) { - IPA_MPM_ERR("Error unvoting on PCIe clk, err = %d\n", + if (is_acted) { + ret = ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id, + false, &is_acted); + if (ret) { + IPA_MPM_ERR("Err unvoting PCIe clk, err = %d\n", ret); - return ret; + return ret; + } } return 0; } @@ -2568,26 +2629,33 @@ int ipa_mpm_mhip_xdci_pipe_enable(enum ipa_usb_teth_prot xdci_teth_prot) /* Lift the delay for rmnet USB prod pipe */ ipa3_xdci_ep_delay_rm(pipe_idx); - if (status == MHIP_STATUS_NO_OP) { + if (status == MHIP_STATUS_NO_OP && is_acted) { /* Channels already have been started, * we can devote for pcie clocks */ - ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id); + ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id, + false, &is_acted); } break; case MHIP_STATUS_EP_NOT_READY: - ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id); + if (is_acted) + ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id, + false, &is_acted); ipa_mpm_change_teth_state(probe_id, IPA_MPM_TETH_INPROGRESS); break; case MHIP_STATUS_FAIL: case MHIP_STATUS_BAD_STATE: case MHIP_STATUS_EP_NOT_FOUND: IPA_MPM_ERR("UL chan cant be started err =%d\n", status); - ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id); + if (is_acted) + ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id, + false, &is_acted); ret = -EFAULT; break; default: - ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id); + if (is_acted) + ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id, + false, &is_acted); IPA_MPM_ERR("Err not found\n"); break; } @@ -2601,6 +2669,7 @@ int ipa_mpm_mhip_xdci_pipe_disable(enum ipa_usb_teth_prot xdci_teth_prot) enum ipa_mpm_mhip_client_type mhip_client; enum mhip_status_type status; int ret = 0; + bool is_acted = true; if (ipa_mpm_ctx == NULL) { IPA_MPM_ERR("MPM not platform probed, returning ..\n"); @@ -2664,7 +2733,8 @@ int ipa_mpm_mhip_xdci_pipe_disable(enum ipa_usb_teth_prot xdci_teth_prot) ipa_mpm_ctx->md[probe_id].mhip_client = IPA_MPM_MHIP_NONE; } - ret = ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id); + ret = ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id, + false, &is_acted); if (ret) IPA_MPM_ERR("Error clking off PCIe clk err%d\n", ret); atomic_set(&ipa_mpm_ctx->adpl_over_usb_available, 0); @@ -2687,14 +2757,16 @@ int ipa_mpm_mhip_xdci_pipe_disable(enum ipa_usb_teth_prot xdci_teth_prot) case MHIP_STATUS_BAD_STATE: case MHIP_STATUS_EP_NOT_FOUND: IPA_MPM_ERR("UL chan cant be started err =%d\n", status); - ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id); + ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id, + false, &is_acted); return -EFAULT; default: IPA_MPM_ERR("Err not found\n"); break; } - ret = ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id); + ret = ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id, + false, &is_acted); if (ret) { IPA_MPM_ERR("Error cloking off PCIe clk, err = %d\n", ret); @@ -2986,6 +3058,7 @@ int ipa3_get_mhip_gsi_stats(struct ipa3_uc_dbg_ring_stats *stats) int ipa3_mpm_enable_adpl_over_odl(bool enable) { int ret; + bool is_acted = true; IPA_MPM_FUNC_ENTRY(); @@ -2999,19 +3072,21 @@ int ipa3_mpm_enable_adpl_over_odl(bool enable) IPA_MPM_DBG("mpm enabling ADPL over ODL\n"); ret = ipa_mpm_vote_unvote_pcie_clk(CLK_ON, - IPA_MPM_MHIP_CH_ID_2); + IPA_MPM_MHIP_CH_ID_2, false, &is_acted); if (ret) { - IPA_MPM_ERR("Error cloking on PCIe clk, err = %d\n", - ret); - return ret; + IPA_MPM_ERR("Err %d cloking on PCIe clk\n", ret); + return ret; } ret = ipa_mpm_set_dma_mode(IPA_CLIENT_MHI_PRIME_DPL_PROD, IPA_CLIENT_ODL_DPL_CONS, false); if (ret) { IPA_MPM_ERR("MPM failed to set dma mode to ODL\n"); - ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, - IPA_MPM_MHIP_CH_ID_2); + if (is_acted) + ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, + IPA_MPM_MHIP_CH_ID_2, + false, + &is_acted); return ret; } @@ -3020,21 +3095,25 @@ int ipa3_mpm_enable_adpl_over_odl(bool enable) } else { /* dec clk count and set DMA to USB */ IPA_MPM_DBG("mpm disabling ADPL over ODL\n"); - ret = ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, - IPA_MPM_MHIP_CH_ID_2); + IPA_MPM_MHIP_CH_ID_2, + false, + &is_acted); if (ret) { - IPA_MPM_ERR("Error cloking off PCIe clk, err = %d\n", + IPA_MPM_ERR("Err %d cloking off PCIe clk\n", ret); - return ret; + return ret; } ret = ipa_mpm_set_dma_mode(IPA_CLIENT_MHI_PRIME_DPL_PROD, IPA_CLIENT_USB_DPL_CONS, false); if (ret) { IPA_MPM_ERR("MPM failed to set dma mode to USB\n"); - ipa_mpm_vote_unvote_pcie_clk(CLK_ON, - IPA_MPM_MHIP_CH_ID_2); + if (ipa_mpm_vote_unvote_pcie_clk(CLK_ON, + IPA_MPM_MHIP_CH_ID_2, + false, + &is_acted)) + IPA_MPM_ERR("Err clocking on pcie\n"); return ret; }